···11+# This workflow uses actions that are not certified by GitHub.
22+# They are provided by a third-party and are governed by
33+# separate terms of service, privacy policy, and support
44+# documentation.
55+66+# This workflow will install Deno then run `deno lint` and `deno test`.
77+# For more information see: https://github.com/denoland/setup-deno
88+99+name: Deno
1010+1111+on:
1212+ push:
1313+ branches: ["main"]
1414+ pull_request:
1515+ branches: ["main"]
1616+1717+permissions:
1818+ contents: read
1919+2020+jobs:
2121+ test:
2222+ runs-on: ubuntu-latest
2323+2424+ steps:
2525+ - name: Setup repo
2626+ uses: actions/checkout@v4
2727+2828+ - name: Setup Deno
2929+ uses: denoland/setup-deno@v1
3030+ with:
3131+ deno-version: v2.x
3232+3333+ - name: Verify formatting
3434+ run: deno fmt --check
3535+3636+ - name: Run linter
3737+ run: deno lint
3838+3939+ - name: Run type checker
4040+ run: deno check
4141+4242+ - name: Run tests
4343+ run: deno test -A
+16-6
README.md
···11# typed-lexicon
2233> [!WARNING]
44-> this project is in the middle of active initial development and not ready for use. there will be updates posted [here](https://bsky.app/profile/tylur.dev) if you'd like to follow along! or checkout the [todo.md](./todo.md)
44+> this project is in the middle of active initial development and not ready for
55+> use. there will be updates posted [here](https://bsky.app/profile/tylur.dev)
66+> if you'd like to follow along! or checkout the [todo.md](./todo.md)
5766-this will be a toolkit for writing lexicon json schema's in typescript and providing types for lexicon data shape. it will:
88+this will be a toolkit for writing lexicon json schema's in typescript and
99+providing types for lexicon data shape. it will:
710811- remove boilerplate and improve ergonomics
99-- type hint for [atproto type parameters](https://atproto.com/specs/lexicon#overview-of-types)
1010-- infer the typescript type definitions for the data shape to avoid duplication and skew
1212+- type hint for
1313+ [atproto type parameters](https://atproto.com/specs/lexicon#overview-of-types)
1414+- infer the typescript type definitions for the data shape to avoid duplication
1515+ and skew
1116- methods and a cli for generating json
12171313-With each of the above finished, i'll plan to write a `validate` method that will be published alongside this that takes any lexicon json definition and validates payloads off that.
1818+With each of the above finished, i'll plan to write a `validate` method that
1919+will be published alongside this that takes any lexicon json definition and
2020+validates payloads off that.
14211515-My working hypothesis: it will be easier to write lexicons in typescript with a single api, then validate based off the json definition, than it would be to start with validation library types (standard-schema style) and attempt to use those as the authoring and validation tools.
2222+My working hypothesis: it will be easier to write lexicons in typescript with a
2323+single api, then validate based off the json definition, than it would be to
2424+start with validation library types (standard-schema style) and attempt to use
2525+those as the authoring and validation tools.
16261727**what you'd write:**
1828
···197197 post: lx.ref("#postView", { required: true }),
198198 parent: lx.union(["#threadViewPost", "#notFoundPost", "#blockedPost"]),
199199 replies: lx.array(
200200- lx.union(["#threadViewPost", "#notFoundPost", "#blockedPost"])
200200+ lx.union(["#threadViewPost", "#notFoundPost", "#blockedPost"]),
201201 ),
202202 threadContext: lx.ref("#threadContext"),
203203 });
···465465466466Deno.test("app.bsky.feed.defs - requestLess token", () => {
467467 const requestLess = lx.token(
468468- "Request that less content like the given feed item be shown in the feed"
468468+ "Request that less content like the given feed item be shown in the feed",
469469 );
470470471471 assertEquals(requestLess, {
···477477478478Deno.test("app.bsky.feed.defs - requestMore token", () => {
479479 const requestMore = lx.token(
480480- "Request that more content like the given feed item be shown in the feed"
480480+ "Request that more content like the given feed item be shown in the feed",
481481 );
482482483483 assertEquals(requestMore, {
···498498499499Deno.test("app.bsky.feed.defs - clickthroughAuthor token", () => {
500500 const clickthroughAuthor = lx.token(
501501- "User clicked through to the author of the feed item"
501501+ "User clicked through to the author of the feed item",
502502 );
503503504504 assertEquals(clickthroughAuthor, {
···509509510510Deno.test("app.bsky.feed.defs - clickthroughReposter token", () => {
511511 const clickthroughReposter = lx.token(
512512- "User clicked through to the reposter of the feed item"
512512+ "User clicked through to the reposter of the feed item",
513513 );
514514515515 assertEquals(clickthroughReposter, {
···520520521521Deno.test("app.bsky.feed.defs - clickthroughEmbed token", () => {
522522 const clickthroughEmbed = lx.token(
523523- "User clicked through to the embedded content of the feed item"
523523+ "User clicked through to the embedded content of the feed item",
524524 );
525525526526 assertEquals(clickthroughEmbed, {
···532532533533Deno.test("app.bsky.feed.defs - contentModeUnspecified token", () => {
534534 const contentModeUnspecified = lx.token(
535535- "Declares the feed generator returns any types of posts."
535535+ "Declares the feed generator returns any types of posts.",
536536 );
537537538538 assertEquals(contentModeUnspecified, {
···543543544544Deno.test("app.bsky.feed.defs - contentModeVideo token", () => {
545545 const contentModeVideo = lx.token(
546546- "Declares the feed generator returns posts containing app.bsky.embed.video embeds."
546546+ "Declares the feed generator returns posts containing app.bsky.embed.video embeds.",
547547 );
548548549549 assertEquals(contentModeVideo, {
···643643 pinned: lx.boolean(),
644644 }),
645645 requestLess: lx.token(
646646- "Request that less content like the given feed item be shown in the feed"
646646+ "Request that less content like the given feed item be shown in the feed",
647647 ),
648648 requestMore: lx.token(
649649- "Request that more content like the given feed item be shown in the feed"
649649+ "Request that more content like the given feed item be shown in the feed",
650650 ),
651651 clickthroughItem: lx.token("User clicked through to the feed item"),
652652 clickthroughAuthor: lx.token(
653653- "User clicked through to the author of the feed item"
653653+ "User clicked through to the author of the feed item",
654654 ),
655655 clickthroughReposter: lx.token(
656656- "User clicked through to the reposter of the feed item"
656656+ "User clicked through to the reposter of the feed item",
657657 ),
658658 clickthroughEmbed: lx.token(
659659- "User clicked through to the embedded content of the feed item"
659659+ "User clicked through to the embedded content of the feed item",
660660 ),
661661 contentModeUnspecified: lx.token(
662662- "Declares the feed generator returns any types of posts."
662662+ "Declares the feed generator returns any types of posts.",
663663 ),
664664 contentModeVideo: lx.token(
665665- "Declares the feed generator returns posts containing app.bsky.embed.video embeds."
665665+ "Declares the feed generator returns posts containing app.bsky.embed.video embeds.",
666666 ),
667667 interactionSeen: lx.token("Feed item was seen by user"),
668668 interactionLike: lx.token("User liked the feed item"),
+3-3
tests/primitives.test.ts
···98989999Deno.test("lx.cidLink()", () => {
100100 const result = lx.cidLink(
101101- "bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a"
101101+ "bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a",
102102 );
103103 assertEquals(result, {
104104 type: "cid-link",
···182182183183Deno.test("lx.token() with interaction event", () => {
184184 const result = lx.token(
185185- "Request that less content like the given feed item be shown in the feed"
185185+ "Request that less content like the given feed item be shown in the feed",
186186 );
187187 assertEquals(result, {
188188 type: "token",
···193193194194Deno.test("lx.token() with content mode", () => {
195195 const result = lx.token(
196196- "Declares the feed generator returns posts containing app.bsky.embed.video embeds"
196196+ "Declares the feed generator returns posts containing app.bsky.embed.video embeds",
197197 );
198198 assertEquals(result, {
199199 type: "token",
+10-5
todo.md
···55Build a toolkit for writing ATProto lexicon JSON schemas in TypeScript that:
6677- Removes boilerplate and improves ergonomics
88-- Provides type hints for [atproto type parameters](https://atproto.com/specs/lexicon#overview-of-types)
99-- Infers TypeScript type definitions for data shapes to avoid duplication and skew
88+- Provides type hints for
99+ [atproto type parameters](https://atproto.com/specs/lexicon#overview-of-types)
1010+- Infers TypeScript type definitions for data shapes to avoid duplication and
1111+ skew
1012- Includes methods and a CLI for generating JSON
11131214## Files to Read
···2527- **Main spec**: https://atproto.com/specs/lexicon#overview-of-types
2628- **Data model**: https://atproto.com/specs/data-model
2729- **ATProto lexicon examples**:
2828- - https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/defs.json (for `ref` examples)
2929- - https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/defs.json (for `token` examples)
3030+ - https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/defs.json
3131+ (for `ref` examples)
3232+ - https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/defs.json
3333+ (for `token` examples)
30343135## Implementation Status
3236···34383539## todo
36403737-write two new test files, one for bsky actor and another for bsky feed. i want to see these fully implemented and tested in separate files. then wait.
4141+write two new test files, one for bsky actor and another for bsky feed. i want
4242+to see these fully implemented and tested in separate files. then wait.