cerulea/lexicon
define atproto schemas in TypeScript
git clone https://git.t4t.associates/cerulea/lexicon
62f5f17
main
@cerulea/lexicon
define AT Protocol lexicon schemas in TypeScript
- no deps :D
- runtime validation via
schema.validate(value)- returns
{ success: true, value }or{ success: false, issues }with a path per issue
- returns
- emit lexicon JSON documents via
toLexicons(...roots)- easily generate a
lexicons/dir of json from typescript defs; it's like a three-liner
- easily generate a
- simple record builder via
schema.build(...)- will auto-fill
$typefor records - will expand short
#fragmentstrings for unions
- will auto-fill
usage
import * as l from "@cerulea/lexicon" ; const facetRange = { start :l . integer , end :l . integer }; const Facet = l . union ( "example.myapp.facet" , { "#mention" :l . object ({ ...facetRange , did :l . did }), "#link" :l . object ({ ...facetRange , uri :l . uri }), }); const Post = l . record ( "example.myapp.post" , { key :"tid" }, { text :l . stringWith ({ maxGraphemes :300 }), facets :l . optional ( l . array ( Facet )), createdAt :l . datetime , }); type Post = l . Infer < typeof Post >; const getPost = l . query ( "example.myapp.getPost" , { parameters :l . params ({ uri :l . atUri }), output :Post , errors :[{ name :"NotFound" }], }); const post :Post = Post . build ({ text :"hi @alice" , facets :[{ $type :"#mention" , start :3 , end :9 , did :"did:example:alice" }], createdAt :new Date (). toISOString (), }); const untrusted :unknown = { $type :"example.myapp.post" , text :"hello" , createdAt :"my evil invalid date" , }; const result = Post . validate ( untrusted ); if ( ! result . success ) console . error ( result . issues ); const lexicons = l . toLexicons ( Post , getPost ); /* [ { lexicon: 1, id: "example.myapp.facet", … }, { lexicon: 1, id: "example.myapp.getPost", … }, … ] */