@atproto/lex-document 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,62 @@
1
1
  import { LexiconDocument } from './lexicon-document.js';
2
2
  import { LexiconIndexer } from './lexicon-indexer.js';
3
3
  /**
4
- * (Lazily) indexes lexicon documents from an iterable source.
4
+ * Lazily indexes Lexicon documents from an iterable source.
5
+ *
6
+ * This class implements `LexiconIndexer` by consuming documents from an
7
+ * iterable (sync or async) and caching them for efficient retrieval.
8
+ * Documents are indexed on-demand as they are requested or iterated over.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // From an array of documents
13
+ * const docs = [lexiconDoc1, lexiconDoc2, lexiconDoc3]
14
+ * const indexer = new LexiconIterableIndexer(docs)
15
+ *
16
+ * // Documents are indexed lazily as requested
17
+ * const doc = await indexer.get('com.example.post')
18
+ * ```
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * // From an async generator (e.g., reading from files)
23
+ * async function* loadLexicons() {
24
+ * for (const file of lexiconFiles) {
25
+ * yield JSON.parse(await fs.readFile(file, 'utf8'))
26
+ * }
27
+ * }
28
+ *
29
+ * await using indexer = new LexiconIterableIndexer(loadLexicons())
30
+ * const schemas = await LexiconSchemaBuilder.buildAll(indexer)
31
+ * ```
5
32
  */
6
33
  export declare class LexiconIterableIndexer implements LexiconIndexer, AsyncDisposable {
7
34
  #private;
8
35
  readonly source: AsyncIterable<LexiconDocument> | Iterable<LexiconDocument>;
36
+ /**
37
+ * Creates a new {@link LexiconIterableIndexer} from an iterable source.
38
+ *
39
+ * @param source - An iterable or async iterable of Lexicon documents.
40
+ * The iterator is consumed lazily as documents are requested.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * // Sync iterable (array, Set, Map.values(), etc.)
45
+ * const indexer = new LexiconIterableIndexer(lexiconDocuments)
46
+ *
47
+ * // Async iterable (async generator, ReadableStream, etc.)
48
+ * const indexer = new LexiconIterableIndexer(asyncLexiconStream)
49
+ * ```
50
+ */
9
51
  constructor(source: AsyncIterable<LexiconDocument> | Iterable<LexiconDocument>);
52
+ /**
53
+ * Retrieves a Lexicon document by its NSID.
54
+ *
55
+ * If the document has already been indexed, it is returned from cache.
56
+ * Otherwise, the source iterator is consumed until the document is found.
57
+ *
58
+ * @see {@link LexiconIndexer.get}
59
+ */
10
60
  get(id: string): Promise<LexiconDocument>;
11
61
  [Symbol.asyncIterator](): AsyncIterator<LexiconDocument, void, undefined>;
12
62
  [Symbol.asyncDispose](): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"lexicon-iterable-indexer.d.ts","sourceRoot":"","sources":["../src/lexicon-iterable-indexer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD;;GAEG;AACH,qBAAa,sBAAuB,YAAW,cAAc,EAAE,eAAe;;IAO1E,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC;gBAAlE,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC;IAQvE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAaxC,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAC5C,eAAe,EACf,IAAI,EACJ,SAAS,CACV;IAwCK,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7C"}
1
+ {"version":3,"file":"lexicon-iterable-indexer.d.ts","sourceRoot":"","sources":["../src/lexicon-iterable-indexer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,sBAAuB,YAAW,cAAc,EAAE,eAAe;;IAsB1E,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC;IAhB7E;;;;;;;;;;;;;;OAcG;gBAEQ,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC;IAQ7E;;;;;;;OAOG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAaxC,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAC5C,eAAe,EACf,IAAI,EACJ,SAAS,CACV;IAwCK,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7C"}
@@ -2,12 +2,54 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LexiconIterableIndexer = void 0;
4
4
  /**
5
- * (Lazily) indexes lexicon documents from an iterable source.
5
+ * Lazily indexes Lexicon documents from an iterable source.
6
+ *
7
+ * This class implements `LexiconIndexer` by consuming documents from an
8
+ * iterable (sync or async) and caching them for efficient retrieval.
9
+ * Documents are indexed on-demand as they are requested or iterated over.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // From an array of documents
14
+ * const docs = [lexiconDoc1, lexiconDoc2, lexiconDoc3]
15
+ * const indexer = new LexiconIterableIndexer(docs)
16
+ *
17
+ * // Documents are indexed lazily as requested
18
+ * const doc = await indexer.get('com.example.post')
19
+ * ```
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * // From an async generator (e.g., reading from files)
24
+ * async function* loadLexicons() {
25
+ * for (const file of lexiconFiles) {
26
+ * yield JSON.parse(await fs.readFile(file, 'utf8'))
27
+ * }
28
+ * }
29
+ *
30
+ * await using indexer = new LexiconIterableIndexer(loadLexicons())
31
+ * const schemas = await LexiconSchemaBuilder.buildAll(indexer)
32
+ * ```
6
33
  */
7
34
  class LexiconIterableIndexer {
8
35
  source;
9
36
  #lexicons = new Map();
10
37
  #iterator;
38
+ /**
39
+ * Creates a new {@link LexiconIterableIndexer} from an iterable source.
40
+ *
41
+ * @param source - An iterable or async iterable of Lexicon documents.
42
+ * The iterator is consumed lazily as documents are requested.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * // Sync iterable (array, Set, Map.values(), etc.)
47
+ * const indexer = new LexiconIterableIndexer(lexiconDocuments)
48
+ *
49
+ * // Async iterable (async generator, ReadableStream, etc.)
50
+ * const indexer = new LexiconIterableIndexer(asyncLexiconStream)
51
+ * ```
52
+ */
11
53
  constructor(source) {
12
54
  this.source = source;
13
55
  this.#iterator =
@@ -15,6 +57,14 @@ class LexiconIterableIndexer {
15
57
  ? source[Symbol.asyncIterator]()
16
58
  : source[Symbol.iterator]();
17
59
  }
60
+ /**
61
+ * Retrieves a Lexicon document by its NSID.
62
+ *
63
+ * If the document has already been indexed, it is returned from cache.
64
+ * Otherwise, the source iterator is consumed until the document is found.
65
+ *
66
+ * @see {@link LexiconIndexer.get}
67
+ */
18
68
  async get(id) {
19
69
  const cached = this.#lexicons.get(id);
20
70
  if (cached)
@@ -1 +1 @@
1
- {"version":3,"file":"lexicon-iterable-indexer.js","sourceRoot":"","sources":["../src/lexicon-iterable-indexer.ts"],"names":[],"mappings":";;;AAGA;;GAEG;AACH,MAAa,sBAAsB;IAOtB;IANF,SAAS,GAAiC,IAAI,GAAG,EAAE,CAAA;IACnD,SAAS,CAE0B;IAE5C,YACW,MAAkE;QAAlE,WAAM,GAAN,MAAM,CAA4D;QAE3E,IAAI,CAAC,SAAS;YACZ,MAAM,CAAC,aAAa,IAAI,MAAM;gBAC5B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE;gBAChC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAA;IACjC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACrC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAA;QAEzB,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE;gBAAE,OAAO,GAAG,CAAA;QAC/B,CAAC;QAED,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE;YACxD,IAAI,EAAE,QAAQ;SACf,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;QAK3B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;QAElC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACpB,MAAM,GAAG,CAAA;QACX,CAAC;QAED,GAAG,CAAC;YACF,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;YAEnD,IAAI,IAAI;gBAAE,MAAK;YAEf,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC3B,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,kCAAkC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAA;gBACnE,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAA;gBACjC,MAAM,GAAG,CAAA,CAAC,0DAA0D;YACtE,CAAC;YAED,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;YACnC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YACtB,MAAM,KAAK,CAAA;QACb,CAAC,QAAQ,IAAI,EAAC;QAEd,0EAA0E;QAC1E,yEAAyE;QACzE,4EAA4E;QAC5E,2EAA2E;QAC3E,4EAA4E;QAC5E,uEAAuE;QACvE,2CAA2C;QAE3C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC1B,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBACpB,MAAM,GAAG,CAAA;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QACzB,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAA;IACjC,CAAC;CACF;AA3ED,wDA2EC","sourcesContent":["import { LexiconDocument } from './lexicon-document.js'\nimport { LexiconIndexer } from './lexicon-indexer.js'\n\n/**\n * (Lazily) indexes lexicon documents from an iterable source.\n */\nexport class LexiconIterableIndexer implements LexiconIndexer, AsyncDisposable {\n readonly #lexicons: Map<string, LexiconDocument> = new Map()\n readonly #iterator:\n | AsyncIterator<LexiconDocument, void, unknown>\n | Iterator<LexiconDocument, void, unknown>\n\n constructor(\n readonly source: AsyncIterable<LexiconDocument> | Iterable<LexiconDocument>,\n ) {\n this.#iterator =\n Symbol.asyncIterator in source\n ? source[Symbol.asyncIterator]()\n : source[Symbol.iterator]()\n }\n\n async get(id: string): Promise<LexiconDocument> {\n const cached = this.#lexicons.get(id)\n if (cached) return cached\n\n for await (const doc of this) {\n if (doc.id === id) return doc\n }\n\n throw Object.assign(new Error(`Lexicon ${id} not found`), {\n code: 'ENOENT',\n })\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<\n LexiconDocument,\n void,\n undefined\n > {\n const returned = new Set<string>()\n\n for (const doc of this.#lexicons.values()) {\n returned.add(doc.id)\n yield doc\n }\n\n do {\n const { value, done } = await this.#iterator.next()\n\n if (done) break\n\n if (returned.has(value.id)) {\n const err = new Error(`Duplicate lexicon document id: ${value.id}`)\n await this.#iterator.throw?.(err)\n throw err // In case iterator.throw does not exist or does not throw\n }\n\n this.#lexicons.set(value.id, value)\n returned.add(value.id)\n yield value\n } while (true)\n\n // At this point, the underlying iterator is done. However, there may have\n // been requests (.get()) for documents that caused the iterator to yield\n // those documents during concurrent execution of this loop. If that was the\n // case, new documents may have been added to `#lexicons` that have not yet\n // been yielded. We need to yield those as well. Since we yield control back\n // to the caller, we need to repeat this process until no new documents\n // appear sunce we don't know what happens.\n\n for (const doc of this.#lexicons.values()) {\n if (!returned.has(doc.id)) {\n returned.add(doc.id)\n yield doc\n }\n }\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.#iterator.return?.()\n }\n}\n"]}
1
+ {"version":3,"file":"lexicon-iterable-indexer.js","sourceRoot":"","sources":["../src/lexicon-iterable-indexer.ts"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAa,sBAAsB;IAsBtB;IArBF,SAAS,GAAiC,IAAI,GAAG,EAAE,CAAA;IACnD,SAAS,CAE0B;IAE5C;;;;;;;;;;;;;;OAcG;IACH,YACW,MAAkE;QAAlE,WAAM,GAAN,MAAM,CAA4D;QAE3E,IAAI,CAAC,SAAS;YACZ,MAAM,CAAC,aAAa,IAAI,MAAM;gBAC5B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE;gBAChC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAA;IACjC,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACrC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAA;QAEzB,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE;gBAAE,OAAO,GAAG,CAAA;QAC/B,CAAC;QAED,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE;YACxD,IAAI,EAAE,QAAQ;SACf,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;QAK3B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;QAElC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACpB,MAAM,GAAG,CAAA;QACX,CAAC;QAED,GAAG,CAAC;YACF,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;YAEnD,IAAI,IAAI;gBAAE,MAAK;YAEf,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC3B,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,kCAAkC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAA;gBACnE,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAA;gBACjC,MAAM,GAAG,CAAA,CAAC,0DAA0D;YACtE,CAAC;YAED,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;YACnC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YACtB,MAAM,KAAK,CAAA;QACb,CAAC,QAAQ,IAAI,EAAC;QAEd,0EAA0E;QAC1E,yEAAyE;QACzE,4EAA4E;QAC5E,2EAA2E;QAC3E,4EAA4E;QAC5E,uEAAuE;QACvE,2CAA2C;QAE3C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC1B,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBACpB,MAAM,GAAG,CAAA;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QACzB,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAA;IACjC,CAAC;CACF;AAlGD,wDAkGC","sourcesContent":["import { LexiconDocument } from './lexicon-document.js'\nimport { LexiconIndexer } from './lexicon-indexer.js'\n\n/**\n * Lazily indexes Lexicon documents from an iterable source.\n *\n * This class implements `LexiconIndexer` by consuming documents from an\n * iterable (sync or async) and caching them for efficient retrieval.\n * Documents are indexed on-demand as they are requested or iterated over.\n *\n * @example\n * ```ts\n * // From an array of documents\n * const docs = [lexiconDoc1, lexiconDoc2, lexiconDoc3]\n * const indexer = new LexiconIterableIndexer(docs)\n *\n * // Documents are indexed lazily as requested\n * const doc = await indexer.get('com.example.post')\n * ```\n *\n * @example\n * ```ts\n * // From an async generator (e.g., reading from files)\n * async function* loadLexicons() {\n * for (const file of lexiconFiles) {\n * yield JSON.parse(await fs.readFile(file, 'utf8'))\n * }\n * }\n *\n * await using indexer = new LexiconIterableIndexer(loadLexicons())\n * const schemas = await LexiconSchemaBuilder.buildAll(indexer)\n * ```\n */\nexport class LexiconIterableIndexer implements LexiconIndexer, AsyncDisposable {\n readonly #lexicons: Map<string, LexiconDocument> = new Map()\n readonly #iterator:\n | AsyncIterator<LexiconDocument, void, unknown>\n | Iterator<LexiconDocument, void, unknown>\n\n /**\n * Creates a new {@link LexiconIterableIndexer} from an iterable source.\n *\n * @param source - An iterable or async iterable of Lexicon documents.\n * The iterator is consumed lazily as documents are requested.\n *\n * @example\n * ```ts\n * // Sync iterable (array, Set, Map.values(), etc.)\n * const indexer = new LexiconIterableIndexer(lexiconDocuments)\n *\n * // Async iterable (async generator, ReadableStream, etc.)\n * const indexer = new LexiconIterableIndexer(asyncLexiconStream)\n * ```\n */\n constructor(\n readonly source: AsyncIterable<LexiconDocument> | Iterable<LexiconDocument>,\n ) {\n this.#iterator =\n Symbol.asyncIterator in source\n ? source[Symbol.asyncIterator]()\n : source[Symbol.iterator]()\n }\n\n /**\n * Retrieves a Lexicon document by its NSID.\n *\n * If the document has already been indexed, it is returned from cache.\n * Otherwise, the source iterator is consumed until the document is found.\n *\n * @see {@link LexiconIndexer.get}\n */\n async get(id: string): Promise<LexiconDocument> {\n const cached = this.#lexicons.get(id)\n if (cached) return cached\n\n for await (const doc of this) {\n if (doc.id === id) return doc\n }\n\n throw Object.assign(new Error(`Lexicon ${id} not found`), {\n code: 'ENOENT',\n })\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<\n LexiconDocument,\n void,\n undefined\n > {\n const returned = new Set<string>()\n\n for (const doc of this.#lexicons.values()) {\n returned.add(doc.id)\n yield doc\n }\n\n do {\n const { value, done } = await this.#iterator.next()\n\n if (done) break\n\n if (returned.has(value.id)) {\n const err = new Error(`Duplicate lexicon document id: ${value.id}`)\n await this.#iterator.throw?.(err)\n throw err // In case iterator.throw does not exist or does not throw\n }\n\n this.#lexicons.set(value.id, value)\n returned.add(value.id)\n yield value\n } while (true)\n\n // At this point, the underlying iterator is done. However, there may have\n // been requests (.get()) for documents that caused the iterator to yield\n // those documents during concurrent execution of this loop. If that was the\n // case, new documents may have been added to `#lexicons` that have not yet\n // been yielded. We need to yield those as well. Since we yield control back\n // to the caller, we need to repeat this process until no new documents\n // appear sunce we don't know what happens.\n\n for (const doc of this.#lexicons.values()) {\n if (!returned.has(doc.id)) {\n returned.add(doc.id)\n yield doc\n }\n }\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.#iterator.return?.()\n }\n}\n"]}
@@ -1,46 +1,133 @@
1
+ import { LexValue } from '@atproto/lex-data';
1
2
  import { l } from '@atproto/lex-schema';
2
3
  import { LexiconArray, LexiconArrayItems, LexiconDocument, LexiconError, LexiconObject, LexiconParameters, LexiconPayload, LexiconRef, LexiconRefUnion } from './lexicon-document.js';
3
4
  import { LexiconIndexer } from './lexicon-indexer.js';
4
5
  /**
5
- * Builds a validator for a given lexicon "ref" from a lexicon indexer.
6
+ * Builds validators for Lexicon documents.
7
+ *
8
+ * This class converts Lexicon type definitions into runtime validators
9
+ * that can validate data against the schema. It handles reference resolution,
10
+ * supporting both local (`#defName`) and cross-document (`nsid#defName`) refs.
6
11
  *
7
12
  * @example
13
+ * ```ts
14
+ * import { LexiconSchemaBuilder, LexiconIterableIndexer } from '@atproto/lex-document'
15
+ *
16
+ * // Build a single validator
17
+ * const indexer = new LexiconIterableIndexer(lexiconDocs)
18
+ * const validator = await LexiconSchemaBuilder.build(indexer, 'com.example.post#main')
8
19
  *
20
+ * // Validate data
21
+ * const result = validator.safeParse(myPostData)
22
+ * if (result.success) {
23
+ * console.log('Valid:', result.value)
24
+ * } else {
25
+ * console.log('Invalid:', result.error)
26
+ * }
27
+ * ```
28
+ *
29
+ * @example
9
30
  * ```ts
10
- * import { LexiconSchemaBuilder } from '@atproto/lex/doc'
11
- * import { LexiconStreamIndexer } from '@atproto/lex/doc'
31
+ * // Build all validators from an iterable indexer
32
+ * const indexer = new LexiconIterableIndexer(lexiconDocs)
33
+ * const allSchemas = await LexiconSchemaBuilder.buildAll(indexer)
12
34
  *
13
- * const indexer = new LexiconStreamIndexer(lexiconDocs)
14
- * const validator = await LexiconSchemaBuilder.build(indexer, 'com.example.foo#bar')
35
+ * for (const [ref, schema] of allSchemas) {
36
+ * console.log(`Built validator for ${ref}`)
37
+ * }
15
38
  * ```
16
39
  */
17
40
  export declare class LexiconSchemaBuilder {
18
41
  #private;
19
42
  protected indexer: LexiconIndexer;
20
- static build(indexer: LexiconIndexer, fullRef: string): Promise<l.Validator<unknown>>;
21
- static buildAll(indexer: LexiconIndexer): Promise<Map<string, l.Validator<unknown, unknown> | l.Query<`${string}.${string}.${string}`, l.ParamsSchema<{
22
- [x: string]: l.Validator<string | number | boolean | (string | number | boolean)[] | undefined, string | number | boolean | (string | number | boolean)[] | undefined>;
23
- }>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, readonly string[] | undefined> | l.Subscription<`${string}.${string}.${string}`, l.ParamsSchema<{
24
- [x: string]: l.Validator<string | number | boolean | (string | number | boolean)[] | undefined, string | number | boolean | (string | number | boolean)[] | undefined>;
25
- }>, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>>, readonly string[] | undefined> | l.Procedure<`${string}.${string}.${string}`, l.ParamsSchema<{
26
- [x: string]: l.Validator<string | number | boolean | (string | number | boolean)[] | undefined, string | number | boolean | (string | number | boolean)[] | undefined>;
27
- }>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, readonly string[] | undefined> | l.PermissionSet<any, any>>>;
43
+ /**
44
+ * Builds a validator for a single Lexicon definition reference.
45
+ *
46
+ * @param indexer - The Lexicon indexer to resolve documents from
47
+ * @param fullRef - The full reference to build, in format "nsid#defName"
48
+ * @returns A promise resolving to a validator for the referenced definition
49
+ * @throws Error if the reference does not point to a schema type
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const validator = await LexiconSchemaBuilder.build(
54
+ * indexer,
55
+ * 'app.bsky.feed.post#main'
56
+ * )
57
+ *
58
+ * validator.parse(postRecord) // Throws if invalid
59
+ * ```
60
+ */
61
+ static build(indexer: LexiconIndexer, fullRef: string): Promise<l.Schema<LexValue>>;
62
+ /**
63
+ * Builds validators for all definitions in all documents from an iterable indexer.
64
+ *
65
+ * This method iterates over all Lexicon documents available in the indexer
66
+ * and builds validators for every definition in each document.
67
+ *
68
+ * @param indexer - An iterable Lexicon indexer (must implement `Symbol.asyncIterator`)
69
+ * @returns A promise resolving to a Map of full references to their validators.
70
+ * The map values can be validators, Query, Subscription, Procedure, or PermissionSet.
71
+ * @throws Error if the indexer does not support iteration
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const indexer = new LexiconIterableIndexer(allLexiconDocs)
76
+ * const schemas = await LexiconSchemaBuilder.buildAll(indexer)
77
+ *
78
+ * // Access a specific schema
79
+ * const postSchema = schemas.get('app.bsky.feed.post#main')
80
+ *
81
+ * // Iterate all schemas
82
+ * for (const [ref, schema] of schemas) {
83
+ * console.log(ref, schema)
84
+ * }
85
+ * ```
86
+ */
87
+ static buildAll(indexer: LexiconIndexer): Promise<Map<string, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>> | l.Query<`${string}.${string}.${string}`, l.ParamsSchema<l.ParamsShape>, l.Payload<string | undefined, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>> | undefined>, readonly string[] | undefined> | l.Subscription<`${string}.${string}.${string}`, l.ParamsSchema<l.ParamsShape>, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>, readonly string[] | undefined> | l.Procedure<`${string}.${string}.${string}`, l.ParamsSchema<l.ParamsShape>, l.Payload<string | undefined, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>> | undefined>, l.Payload<string | undefined, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>> | undefined>, readonly string[] | undefined> | l.PermissionSet<any, any>>>;
88
+ /**
89
+ * Creates a new LexiconSchemaBuilder instance.
90
+ *
91
+ * Note: For most use cases, prefer using the static `build()` or `buildAll()`
92
+ * methods instead of instantiating directly.
93
+ *
94
+ * @param indexer - The Lexicon indexer to resolve documents from
95
+ */
28
96
  constructor(indexer: LexiconIndexer);
97
+ /**
98
+ * Waits for all pending reference resolution tasks to complete.
99
+ *
100
+ * When building schemas with cross-references, the builder schedules
101
+ * async tasks to resolve those references. This method must be called
102
+ * to ensure all references are fully resolved before using the validators.
103
+ *
104
+ * @returns A promise that resolves when all pending tasks are complete
105
+ * @throws Rethrows any errors from failed reference resolution
106
+ */
29
107
  done(): Promise<void>;
30
- buildFullRef: (fullRef: string) => Promise<l.Validator<unknown, unknown> | l.PermissionSet<`${string}.${string}.${string}`, l.Permission<string, {
31
- [x: string]: string | number | boolean | (string | number | boolean)[];
32
- }>[]> | l.Procedure<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, string[]> | l.Query<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, string[]> | l.Subscription<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.ObjectSchema<any> | l.RefSchema<l.Validator<unknown, unknown>> | l.TypedUnionSchema<l.TypedRefSchema<l.TypedObjectSchema<`${string}.${string}.${string}` | `${string}.${string}.${string}#${string}`, any> | l.RecordSchema<any, any, any>>[], boolean>, string[]>>;
33
- protected buildRefGetter(fullRef: string): () => l.Validator<unknown>;
108
+ /**
109
+ * Builds a validator for a full reference (memoized).
110
+ *
111
+ * Results are cached, so calling with the same reference returns
112
+ * the same promise/result.
113
+ *
114
+ * @param fullRef - The full reference in format "nsid#defName"
115
+ * @returns A promise resolving to the built schema or method definition
116
+ */
117
+ buildFullRef: (fullRef: string) => Promise<l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>> | l.PermissionSet<`${string}.${string}.${string}`, l.Permission<string, {
118
+ [x: string]: string | number | boolean | number[] | string[] | boolean[];
119
+ }>[]> | l.Procedure<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Payload<string, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>>, l.Payload<string, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>>, string[]> | l.Query<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Payload<string, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>>, string[]> | l.Subscription<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>, string[]>>;
120
+ protected buildRefGetter(fullRef: string): () => l.Schema<LexValue>;
34
121
  protected buildTypedRefGetter(fullRef: string): () => l.TypedObjectSchema | l.RecordSchema;
35
- protected compileDef(doc: LexiconDocument, hash: string): l.Validator<unknown, unknown> | l.PermissionSet<`${string}.${string}.${string}`, l.Permission<string, {
36
- [x: string]: string | number | boolean | (string | number | boolean)[];
37
- }>[]> | l.Procedure<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, string[]> | l.Query<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Payload<string | undefined, l.Schema<unknown, unknown, l.SchemaInternals<unknown, unknown>> | undefined>, string[]> | l.Subscription<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.ObjectSchema<any> | l.RefSchema<l.Validator<unknown, unknown>> | l.TypedUnionSchema<l.TypedRefSchema<l.TypedObjectSchema<`${string}.${string}.${string}` | `${string}.${string}.${string}#${string}`, any> | l.RecordSchema<any, any, any>>[], boolean>, string[]>;
38
- protected compileLeaf(doc: LexiconDocument, def: LexiconArray | LexiconArrayItems): l.Validator<unknown>;
39
- protected compileRef(doc: LexiconDocument, def: LexiconRef | LexiconRefUnion): l.RefSchema<l.Validator<unknown, unknown>> | l.TypedUnionSchema<l.TypedRefSchema<l.TypedObjectSchema<`${string}.${string}.${string}` | `${string}.${string}.${string}#${string}`, any> | l.RecordSchema<any, any, any>>[], boolean>;
40
- protected compileObject(doc: LexiconDocument, def: LexiconObject): l.ObjectSchema;
41
- protected compilePayload(doc: LexiconDocument, def: LexiconPayload | undefined): l.Payload;
122
+ protected compileDef(doc: LexiconDocument, hash: string): l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>> | l.PermissionSet<`${string}.${string}.${string}`, l.Permission<string, {
123
+ [x: string]: string | number | boolean | number[] | string[] | boolean[];
124
+ }>[]> | l.Procedure<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Payload<string, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>>, l.Payload<string, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>>, string[]> | l.Query<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Payload<string, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>>, string[]> | l.Subscription<`${string}.${string}.${string}`, l.ParamsSchema<{}>, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>, string[]>;
125
+ protected compileLeaf(doc: LexiconDocument, def: LexiconArray | LexiconArrayItems): l.Schema<LexValue>;
126
+ protected compileRef(doc: LexiconDocument, def: LexiconRef | LexiconRefUnion): l.Schema<LexValue>;
127
+ protected compileObject(doc: LexiconDocument, def: LexiconObject): l.ObjectSchema<Record<string, l.Schema<LexValue | undefined, LexValue | undefined, l.SchemaInternals<LexValue | undefined, LexValue | undefined>>>>;
128
+ protected compilePayload(doc: LexiconDocument, def: LexiconPayload | undefined): l.Payload<string, l.Schema<LexValue, LexValue, l.SchemaInternals<LexValue, LexValue>>>;
42
129
  protected compileErrors(_doc: LexiconDocument, errors?: readonly LexiconError[]): undefined | string[];
43
- protected compilePayloadSchema(doc: LexiconDocument, def: LexiconObject | LexiconRef | LexiconRefUnion): l.ObjectSchema<any> | l.RefSchema<l.Validator<unknown, unknown>> | l.TypedUnionSchema<l.TypedRefSchema<l.TypedObjectSchema<`${string}.${string}.${string}` | `${string}.${string}.${string}#${string}`, any> | l.RecordSchema<any, any, any>>[], boolean>;
130
+ protected compilePayloadSchema(doc: LexiconDocument, def: LexiconObject | LexiconRef | LexiconRefUnion): l.Schema<LexValue, LexValue>;
44
131
  protected compileParams(doc: LexiconDocument, def?: LexiconParameters): l.ParamsSchema<{}>;
45
132
  }
46
133
  export declare function memoize<Fn extends (arg: string) => unknown>(fn: Fn): Fn;
@@ -1 +1 @@
1
- {"version":3,"file":"lexicon-schema-builder.d.ts","sourceRoot":"","sources":["../src/lexicon-schema-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AACvC,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,eAAe,EAChB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD;;;;;;;;;;;;GAYG;AACH,qBAAa,oBAAoB;;IA8CnB,SAAS,CAAC,OAAO,EAAE,cAAc;WA7ChC,KAAK,CAChB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;WAanB,QAAQ,CAAC,OAAO,EAAE,cAAc;;;;;;;gBA6BvB,OAAO,EAAE,cAAc;IAEvC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,YAAY,YAA2B,MAAM;;gzBAM3C;IAEF,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC;IAkBrE,SAAS,CAAC,mBAAmB,CAC3B,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,CAAC,iBAAiB,GAAG,CAAC,CAAC,YAAY;IAqB7C,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM;;;IAiDvD,SAAS,CAAC,WAAW,CACnB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,YAAY,GAAG,iBAAiB,GACpC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC;IA8DvB,SAAS,CAAC,UAAU,CAClB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,UAAU,GAAG,eAAe;IAkBnC,SAAS,CAAC,aAAa,CACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,aAAa,GACjB,CAAC,CAAC,YAAY;IAuBjB,SAAS,CAAC,cAAc,CACtB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,GAAG,SAAS,GAC9B,CAAC,CAAC,OAAO;IAOZ,SAAS,CAAC,aAAa,CACrB,IAAI,EAAE,eAAe,EACrB,MAAM,CAAC,EAAE,SAAS,YAAY,EAAE,GAC/B,SAAS,GAAG,MAAM,EAAE;IAIvB,SAAS,CAAC,oBAAoB,CAC5B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,aAAa,GAAG,UAAU,GAAG,eAAe;IAUnD,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,iBAAiB;CAgBtE;AA6CD,wBAAgB,OAAO,CAAC,EAAE,SAAS,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAQvE"}
1
+ {"version":3,"file":"lexicon-schema-builder.d.ts","sourceRoot":"","sources":["../src/lexicon-schema-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AACvC,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,eAAe,EAChB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,oBAAoB;;IAiGnB,SAAS,CAAC,OAAO,EAAE,cAAc;IAhG7C;;;;;;;;;;;;;;;;;OAiBG;WACU,KAAK,CAChB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAa9B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;WACU,QAAQ,CAAC,OAAO,EAAE,cAAc;IA6B7C;;;;;;;OAOG;gBACmB,OAAO,EAAE,cAAc;IAE7C;;;;;;;;;OASG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;;;;;OAQG;IACH,YAAY,YAA2B,MAAM;;8jBAM3C;IAEF,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IAkBnE,SAAS,CAAC,mBAAmB,CAC3B,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,CAAC,iBAAiB,GAAG,CAAC,CAAC,YAAY;IAqB7C,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM;;;IAiDvD,SAAS,CAAC,WAAW,CACnB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,YAAY,GAAG,iBAAiB,GACpC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IA8DrB,SAAS,CAAC,UAAU,CAClB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,UAAU,GAAG,eAAe,GAChC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IAiBrB,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,aAAa;IA0BhE,SAAS,CAAC,cAAc,CACtB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,GAAG,SAAS;IAQjC,SAAS,CAAC,aAAa,CACrB,IAAI,EAAE,eAAe,EACrB,MAAM,CAAC,EAAE,SAAS,YAAY,EAAE,GAC/B,SAAS,GAAG,MAAM,EAAE;IAIvB,SAAS,CAAC,oBAAoB,CAC5B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,aAAa,GAAG,UAAU,GAAG,eAAe,GAChD,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAS/B,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,iBAAiB;CAsBtE;AA6CD,wBAAgB,OAAO,CAAC,EAAE,SAAS,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAQvE"}
@@ -4,20 +4,60 @@ exports.LexiconSchemaBuilder = void 0;
4
4
  exports.memoize = memoize;
5
5
  const lex_schema_1 = require("@atproto/lex-schema");
6
6
  /**
7
- * Builds a validator for a given lexicon "ref" from a lexicon indexer.
7
+ * Builds validators for Lexicon documents.
8
+ *
9
+ * This class converts Lexicon type definitions into runtime validators
10
+ * that can validate data against the schema. It handles reference resolution,
11
+ * supporting both local (`#defName`) and cross-document (`nsid#defName`) refs.
8
12
  *
9
13
  * @example
14
+ * ```ts
15
+ * import { LexiconSchemaBuilder, LexiconIterableIndexer } from '@atproto/lex-document'
16
+ *
17
+ * // Build a single validator
18
+ * const indexer = new LexiconIterableIndexer(lexiconDocs)
19
+ * const validator = await LexiconSchemaBuilder.build(indexer, 'com.example.post#main')
20
+ *
21
+ * // Validate data
22
+ * const result = validator.safeParse(myPostData)
23
+ * if (result.success) {
24
+ * console.log('Valid:', result.value)
25
+ * } else {
26
+ * console.log('Invalid:', result.error)
27
+ * }
28
+ * ```
10
29
  *
30
+ * @example
11
31
  * ```ts
12
- * import { LexiconSchemaBuilder } from '@atproto/lex/doc'
13
- * import { LexiconStreamIndexer } from '@atproto/lex/doc'
32
+ * // Build all validators from an iterable indexer
33
+ * const indexer = new LexiconIterableIndexer(lexiconDocs)
34
+ * const allSchemas = await LexiconSchemaBuilder.buildAll(indexer)
14
35
  *
15
- * const indexer = new LexiconStreamIndexer(lexiconDocs)
16
- * const validator = await LexiconSchemaBuilder.build(indexer, 'com.example.foo#bar')
36
+ * for (const [ref, schema] of allSchemas) {
37
+ * console.log(`Built validator for ${ref}`)
38
+ * }
17
39
  * ```
18
40
  */
19
41
  class LexiconSchemaBuilder {
20
42
  indexer;
43
+ /**
44
+ * Builds a validator for a single Lexicon definition reference.
45
+ *
46
+ * @param indexer - The Lexicon indexer to resolve documents from
47
+ * @param fullRef - The full reference to build, in format "nsid#defName"
48
+ * @returns A promise resolving to a validator for the referenced definition
49
+ * @throws Error if the reference does not point to a schema type
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const validator = await LexiconSchemaBuilder.build(
54
+ * indexer,
55
+ * 'app.bsky.feed.post#main'
56
+ * )
57
+ *
58
+ * validator.parse(postRecord) // Throws if invalid
59
+ * ```
60
+ */
21
61
  static async build(indexer, fullRef) {
22
62
  const ctx = new LexiconSchemaBuilder(indexer);
23
63
  try {
@@ -31,6 +71,31 @@ class LexiconSchemaBuilder {
31
71
  await ctx.done();
32
72
  }
33
73
  }
74
+ /**
75
+ * Builds validators for all definitions in all documents from an iterable indexer.
76
+ *
77
+ * This method iterates over all Lexicon documents available in the indexer
78
+ * and builds validators for every definition in each document.
79
+ *
80
+ * @param indexer - An iterable Lexicon indexer (must implement `Symbol.asyncIterator`)
81
+ * @returns A promise resolving to a Map of full references to their validators.
82
+ * The map values can be validators, Query, Subscription, Procedure, or PermissionSet.
83
+ * @throws Error if the indexer does not support iteration
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const indexer = new LexiconIterableIndexer(allLexiconDocs)
88
+ * const schemas = await LexiconSchemaBuilder.buildAll(indexer)
89
+ *
90
+ * // Access a specific schema
91
+ * const postSchema = schemas.get('app.bsky.feed.post#main')
92
+ *
93
+ * // Iterate all schemas
94
+ * for (const [ref, schema] of schemas) {
95
+ * console.log(ref, schema)
96
+ * }
97
+ * ```
98
+ */
34
99
  static async buildAll(indexer) {
35
100
  const builder = new LexiconSchemaBuilder(indexer);
36
101
  const schemas = new Map();
@@ -52,28 +117,55 @@ class LexiconSchemaBuilder {
52
117
  }
53
118
  }
54
119
  #asyncTasks = new AsyncTasks();
120
+ /**
121
+ * Creates a new LexiconSchemaBuilder instance.
122
+ *
123
+ * Note: For most use cases, prefer using the static `build()` or `buildAll()`
124
+ * methods instead of instantiating directly.
125
+ *
126
+ * @param indexer - The Lexicon indexer to resolve documents from
127
+ */
55
128
  constructor(indexer) {
56
129
  this.indexer = indexer;
57
130
  }
131
+ /**
132
+ * Waits for all pending reference resolution tasks to complete.
133
+ *
134
+ * When building schemas with cross-references, the builder schedules
135
+ * async tasks to resolve those references. This method must be called
136
+ * to ensure all references are fully resolved before using the validators.
137
+ *
138
+ * @returns A promise that resolves when all pending tasks are complete
139
+ * @throws Rethrows any errors from failed reference resolution
140
+ */
58
141
  async done() {
59
142
  await this.#asyncTasks.done();
60
143
  }
144
+ /**
145
+ * Builds a validator for a full reference (memoized).
146
+ *
147
+ * Results are cached, so calling with the same reference returns
148
+ * the same promise/result.
149
+ *
150
+ * @param fullRef - The full reference in format "nsid#defName"
151
+ * @returns A promise resolving to the built schema or method definition
152
+ */
61
153
  buildFullRef = memoize(async (fullRef) => {
62
154
  const { nsid, hash } = parseRef(fullRef);
63
155
  const doc = await this.indexer.get(nsid);
64
156
  return this.compileDef(doc, hash);
65
157
  });
66
158
  buildRefGetter(fullRef) {
67
- let validator;
159
+ let schema;
68
160
  this.#asyncTasks.add(this.buildFullRef(fullRef).then((v) => {
69
161
  if (!(v instanceof lex_schema_1.l.Schema)) {
70
162
  throw new Error(`Only refs to schema types are allowed`);
71
163
  }
72
- validator = v;
164
+ schema = v;
73
165
  }));
74
166
  return () => {
75
- if (validator)
76
- return validator;
167
+ if (schema)
168
+ return schema;
77
169
  throw new Error('Validator not yet built. Did you await done()?');
78
170
  };
79
171
  }
@@ -169,7 +261,7 @@ class LexiconSchemaBuilder {
169
261
  case 'bytes':
170
262
  return lex_schema_1.l.bytes(def);
171
263
  case 'unknown':
172
- return lex_schema_1.l.unknown();
264
+ return lex_schema_1.l.lexMap();
173
265
  case 'array':
174
266
  return lex_schema_1.l.array(this.compileLeaf(doc, def.items), def);
175
267
  default:
@@ -1 +1 @@
1
- {"version":3,"file":"lexicon-schema-builder.js","sourceRoot":"","sources":["../src/lexicon-schema-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { l } from '@atproto/lex-schema'\nimport {\n LexiconArray,\n LexiconArrayItems,\n LexiconDocument,\n LexiconError,\n LexiconObject,\n LexiconParameters,\n LexiconPayload,\n LexiconRef,\n LexiconRefUnion,\n} from './lexicon-document.js'\nimport { LexiconIndexer } from './lexicon-indexer.js'\n\n/**\n * Builds a validator for a given lexicon \"ref\" from a lexicon indexer.\n *\n * @example\n *\n * ```ts\n * import { LexiconSchemaBuilder } from '@atproto/lex/doc'\n * import { LexiconStreamIndexer } from '@atproto/lex/doc'\n *\n * const indexer = new LexiconStreamIndexer(lexiconDocs)\n * const validator = await LexiconSchemaBuilder.build(indexer, 'com.example.foo#bar')\n * ```\n */\nexport class LexiconSchemaBuilder {\n static async build(\n indexer: LexiconIndexer,\n fullRef: string,\n ): Promise<l.Validator<unknown>> {\n const ctx = new LexiconSchemaBuilder(indexer)\n try {\n const result = await ctx.buildFullRef(fullRef)\n if (!(result instanceof l.Schema)) {\n throw new Error(`Ref ${fullRef} is not a schema type`)\n }\n return result\n } finally {\n await ctx.done()\n }\n }\n\n static async buildAll(indexer: LexiconIndexer) {\n const builder = new LexiconSchemaBuilder(indexer)\n const schemas = new Map<\n string,\n | l.Validator<unknown>\n | l.Query\n | l.Subscription\n | l.Procedure\n | l.PermissionSet\n >()\n if (!isAsyncIterableObject(indexer)) {\n throw new Error('An iterable indexer is required to build all schemas')\n }\n try {\n for await (const doc of indexer) {\n for (const hash of Object.keys(doc.defs)) {\n const fullRef = `${doc.id}#${hash}`\n const schema = await builder.buildFullRef(fullRef)\n schemas.set(fullRef, schema)\n }\n }\n return schemas\n } finally {\n await builder.done()\n }\n }\n\n #asyncTasks = new AsyncTasks()\n\n constructor(protected indexer: LexiconIndexer) {}\n\n async done(): Promise<void> {\n await this.#asyncTasks.done()\n }\n\n buildFullRef = memoize(async (fullRef: string) => {\n const { nsid, hash } = parseRef(fullRef)\n\n const doc = await this.indexer.get(nsid)\n\n return this.compileDef(doc, hash)\n })\n\n protected buildRefGetter(fullRef: string): () => l.Validator<unknown> {\n let validator: l.Validator<unknown>\n\n this.#asyncTasks.add(\n this.buildFullRef(fullRef).then((v) => {\n if (!(v instanceof l.Schema)) {\n throw new Error(`Only refs to schema types are allowed`)\n }\n validator = v\n }),\n )\n\n return () => {\n if (validator) return validator\n throw new Error('Validator not yet built. Did you await done()?')\n }\n }\n\n protected buildTypedRefGetter(\n fullRef: string,\n ): () => l.TypedObjectSchema | l.RecordSchema {\n let validator: l.TypedObjectSchema | l.RecordSchema\n\n this.#asyncTasks.add(\n this.buildFullRef(fullRef).then((v) => {\n if (v instanceof l.TypedObjectSchema || v instanceof l.RecordSchema) {\n validator = v\n } else {\n throw new Error(\n 'Only refs to records and object definitions are allowed',\n )\n }\n }),\n )\n\n return () => {\n if (validator) return validator\n throw new Error('Validator not yet built. Did you await done()?')\n }\n }\n\n protected compileDef(doc: LexiconDocument, hash: string) {\n const def = Object.hasOwn(doc.defs, hash) ? doc.defs[hash] : null\n if (!def) {\n throw new Error(\n `No definition found for hash \"${JSON.stringify(hash)}\" in ${doc.id}`,\n )\n }\n switch (def.type) {\n case 'permission-set':\n return l.permissionSet(\n doc.id,\n def.permissions.map(({ resource, type, ...p }) =>\n l.permission(resource, p),\n ),\n def,\n )\n case 'procedure':\n return l.procedure(\n doc.id,\n this.compileParams(doc, def.parameters),\n this.compilePayload(doc, def.input),\n this.compilePayload(doc, def.output),\n this.compileErrors(doc, def.errors),\n )\n case 'query':\n return l.query(\n doc.id,\n this.compileParams(doc, def.parameters),\n this.compilePayload(doc, def.output),\n this.compileErrors(doc, def.errors),\n )\n case 'subscription':\n return l.subscription(\n doc.id,\n this.compileParams(doc, def.parameters),\n this.compilePayloadSchema(doc, def.message.schema),\n this.compileErrors(doc, def.errors),\n )\n case 'token':\n return l.token(doc.id, hash)\n case 'record':\n return l.record(def.key, doc.id, this.compileObject(doc, def.record))\n case 'object':\n return l.typedObject(doc.id, hash, this.compileObject(doc, def))\n default:\n return this.compileLeaf(doc, def)\n }\n }\n\n protected compileLeaf(\n doc: LexiconDocument,\n def: LexiconArray | LexiconArrayItems,\n ): l.Validator<unknown> {\n if (\n 'const' in def &&\n 'enum' in def &&\n def.enum != null &&\n def.const !== undefined &&\n !(def.enum as readonly unknown[]).includes(def.const)\n ) {\n return l.never()\n }\n\n switch (def.type) {\n case 'string': {\n const schema = l.string(def)\n if (def.default != null) schema.check(def.default)\n if (def.const != null) schema.check(def.const)\n if (def.enum != null) for (const v of def.enum) schema.check(v)\n\n const result =\n def.const != null\n ? l.literal(def.const)\n : def.enum != null\n ? l.enum(def.enum)\n : schema\n\n return def.default != null ? l.withDefault(result, def.default) : result\n }\n case 'integer': {\n const schema = l.integer(def)\n if (def.default != null) schema.check(def.default)\n if (def.const != null) schema.check(def.const)\n if (def.enum != null) for (const v of def.enum) schema.check(v)\n\n const result =\n def.const != null\n ? l.literal(def.const)\n : def.enum != null\n ? l.enum(def.enum)\n : schema\n\n return def.default != null ? l.withDefault(result, def.default) : result\n }\n case 'boolean': {\n const result = def.const != null ? l.literal(def.const) : l.boolean()\n\n return def.default != null ? l.withDefault(result, def.default) : result\n }\n case 'blob':\n return l.blob(def)\n case 'cid-link':\n return l.cid()\n case 'bytes':\n return l.bytes(def)\n case 'unknown':\n return l.unknown()\n case 'array':\n return l.array(this.compileLeaf(doc, def.items), def)\n default:\n return this.compileRef(doc, def)\n }\n }\n\n protected compileRef(\n doc: LexiconDocument,\n def: LexiconRef | LexiconRefUnion,\n ) {\n switch (def.type) {\n case 'ref':\n return l.ref(this.buildRefGetter(buildFullRef(doc, def.ref)))\n case 'union':\n return l.typedUnion(\n def.refs.map((r) =>\n l.typedRef(this.buildTypedRefGetter(buildFullRef(doc, r))),\n ),\n def.closed ?? false,\n )\n default:\n // @ts-expect-error\n throw new Error(`Unknown lexicon type: ${def.type}`)\n }\n }\n\n protected compileObject(\n doc: LexiconDocument,\n def: LexiconObject,\n ): l.ObjectSchema {\n const props: Record<string, l.Validator> = {}\n for (const [key, propDef] of Object.entries(def.properties)) {\n if (propDef === undefined) continue\n\n const isNullable = def.nullable?.includes(key)\n const isRequired = def.required?.includes(key)\n\n let schema = this.compileLeaf(doc, propDef)\n\n if (isNullable) {\n schema = l.nullable(schema)\n }\n\n if (!isRequired) {\n schema = l.optional(schema)\n }\n\n props[key] = schema\n }\n return l.object(props)\n }\n\n protected compilePayload(\n doc: LexiconDocument,\n def: LexiconPayload | undefined,\n ): l.Payload {\n return l.payload(\n def?.encoding,\n def?.schema ? this.compilePayloadSchema(doc, def.schema) : undefined,\n )\n }\n\n protected compileErrors(\n _doc: LexiconDocument,\n errors?: readonly LexiconError[],\n ): undefined | string[] {\n return errors?.map((e) => e.name)\n }\n\n protected compilePayloadSchema(\n doc: LexiconDocument,\n def: LexiconObject | LexiconRef | LexiconRefUnion,\n ) {\n switch (def.type) {\n case 'object':\n return this.compileObject(doc, def)\n default:\n return this.compileRef(doc, def)\n }\n }\n\n protected compileParams(doc: LexiconDocument, def?: LexiconParameters) {\n if (!def) return l.params()\n\n const shape: l.ParamsSchemaShape = {}\n for (const [paramName, paramDef] of Object.entries(def.properties)) {\n if (paramDef === undefined) continue\n\n const isRequired = def.required?.includes(paramName)\n\n const propSchema = this.compileLeaf(doc, paramDef) as l.Validator<l.Param>\n\n shape[paramName] = isRequired ? propSchema : l.optional(propSchema)\n }\n\n return l.params(shape)\n }\n}\n\nclass AsyncTasks {\n /**\n * A set that, eventually, contains only rejected promises.\n */\n #promises = new Set<Promise<void>>()\n\n async done(): Promise<void> {\n do {\n // @NOTE this is going to throw on the first rejected promise (which is\n // what we want)\n for (const p of this.#promises) await p\n // At this point, all settled promises should have been removed. If\n // this.#promises is not empty, it means new promises were added during\n // the awaiting process, so we loop again.\n } while (this.#promises.size > 0)\n }\n\n add(p: Promise<void>) {\n const promise = Promise.resolve(p).then(() => {\n // No need to keep the promise any longer\n this.#promises.delete(promise)\n })\n\n void promise.catch((_err) => {\n // ignore errors here, they should be caught though done()\n })\n\n this.#promises.add(promise)\n }\n}\n\nfunction parseRef(fullRef: string) {\n const { length, 0: nsid, 1: hash } = fullRef.split('#')\n if (length !== 2) throw new Error('Uri can only have one hash segment')\n if (!nsid || !hash) throw new Error('Invalid ref, missing hash')\n return { nsid, hash }\n}\n\nfunction buildFullRef(from: LexiconDocument, ref: string) {\n if (ref.startsWith('#')) return `${from.id}${ref}`\n return ref\n}\n\nexport function memoize<Fn extends (arg: string) => unknown>(fn: Fn): Fn {\n const cache = new Map<string, ReturnType<Fn>>()\n return ((arg: string) => {\n if (cache.has(arg)) return cache.get(arg)!\n const result = fn(arg) as ReturnType<Fn>\n cache.set(arg, result)\n return result\n }) as Fn\n}\n\nfunction isAsyncIterableObject<T>(\n obj: T,\n): obj is T & object & AsyncIterable<unknown> {\n return (\n obj != null &&\n typeof obj === 'object' &&\n Symbol.asyncIterator in obj &&\n typeof obj[Symbol.asyncIterator] === 'function'\n )\n}\n"]}
1
+ {"version":3,"file":"lexicon-schema-builder.js","sourceRoot":"","sources":["../src/lexicon-schema-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexValue } from '@atproto/lex-data'\nimport { l } from '@atproto/lex-schema'\nimport {\n LexiconArray,\n LexiconArrayItems,\n LexiconDocument,\n LexiconError,\n LexiconObject,\n LexiconParameters,\n LexiconPayload,\n LexiconRef,\n LexiconRefUnion,\n} from './lexicon-document.js'\nimport { LexiconIndexer } from './lexicon-indexer.js'\n\n/**\n * Builds validators for Lexicon documents.\n *\n * This class converts Lexicon type definitions into runtime validators\n * that can validate data against the schema. It handles reference resolution,\n * supporting both local (`#defName`) and cross-document (`nsid#defName`) refs.\n *\n * @example\n * ```ts\n * import { LexiconSchemaBuilder, LexiconIterableIndexer } from '@atproto/lex-document'\n *\n * // Build a single validator\n * const indexer = new LexiconIterableIndexer(lexiconDocs)\n * const validator = await LexiconSchemaBuilder.build(indexer, 'com.example.post#main')\n *\n * // Validate data\n * const result = validator.safeParse(myPostData)\n * if (result.success) {\n * console.log('Valid:', result.value)\n * } else {\n * console.log('Invalid:', result.error)\n * }\n * ```\n *\n * @example\n * ```ts\n * // Build all validators from an iterable indexer\n * const indexer = new LexiconIterableIndexer(lexiconDocs)\n * const allSchemas = await LexiconSchemaBuilder.buildAll(indexer)\n *\n * for (const [ref, schema] of allSchemas) {\n * console.log(`Built validator for ${ref}`)\n * }\n * ```\n */\nexport class LexiconSchemaBuilder {\n /**\n * Builds a validator for a single Lexicon definition reference.\n *\n * @param indexer - The Lexicon indexer to resolve documents from\n * @param fullRef - The full reference to build, in format \"nsid#defName\"\n * @returns A promise resolving to a validator for the referenced definition\n * @throws Error if the reference does not point to a schema type\n *\n * @example\n * ```ts\n * const validator = await LexiconSchemaBuilder.build(\n * indexer,\n * 'app.bsky.feed.post#main'\n * )\n *\n * validator.parse(postRecord) // Throws if invalid\n * ```\n */\n static async build(\n indexer: LexiconIndexer,\n fullRef: string,\n ): Promise<l.Schema<LexValue>> {\n const ctx = new LexiconSchemaBuilder(indexer)\n try {\n const result = await ctx.buildFullRef(fullRef)\n if (!(result instanceof l.Schema)) {\n throw new Error(`Ref ${fullRef} is not a schema type`)\n }\n return result\n } finally {\n await ctx.done()\n }\n }\n\n /**\n * Builds validators for all definitions in all documents from an iterable indexer.\n *\n * This method iterates over all Lexicon documents available in the indexer\n * and builds validators for every definition in each document.\n *\n * @param indexer - An iterable Lexicon indexer (must implement `Symbol.asyncIterator`)\n * @returns A promise resolving to a Map of full references to their validators.\n * The map values can be validators, Query, Subscription, Procedure, or PermissionSet.\n * @throws Error if the indexer does not support iteration\n *\n * @example\n * ```ts\n * const indexer = new LexiconIterableIndexer(allLexiconDocs)\n * const schemas = await LexiconSchemaBuilder.buildAll(indexer)\n *\n * // Access a specific schema\n * const postSchema = schemas.get('app.bsky.feed.post#main')\n *\n * // Iterate all schemas\n * for (const [ref, schema] of schemas) {\n * console.log(ref, schema)\n * }\n * ```\n */\n static async buildAll(indexer: LexiconIndexer) {\n const builder = new LexiconSchemaBuilder(indexer)\n const schemas = new Map<\n string,\n | l.Schema<LexValue>\n | l.Query\n | l.Subscription\n | l.Procedure\n | l.PermissionSet\n >()\n if (!isAsyncIterableObject(indexer)) {\n throw new Error('An iterable indexer is required to build all schemas')\n }\n try {\n for await (const doc of indexer) {\n for (const hash of Object.keys(doc.defs)) {\n const fullRef = `${doc.id}#${hash}`\n const schema = await builder.buildFullRef(fullRef)\n schemas.set(fullRef, schema)\n }\n }\n return schemas\n } finally {\n await builder.done()\n }\n }\n\n #asyncTasks = new AsyncTasks()\n\n /**\n * Creates a new LexiconSchemaBuilder instance.\n *\n * Note: For most use cases, prefer using the static `build()` or `buildAll()`\n * methods instead of instantiating directly.\n *\n * @param indexer - The Lexicon indexer to resolve documents from\n */\n constructor(protected indexer: LexiconIndexer) {}\n\n /**\n * Waits for all pending reference resolution tasks to complete.\n *\n * When building schemas with cross-references, the builder schedules\n * async tasks to resolve those references. This method must be called\n * to ensure all references are fully resolved before using the validators.\n *\n * @returns A promise that resolves when all pending tasks are complete\n * @throws Rethrows any errors from failed reference resolution\n */\n async done(): Promise<void> {\n await this.#asyncTasks.done()\n }\n\n /**\n * Builds a validator for a full reference (memoized).\n *\n * Results are cached, so calling with the same reference returns\n * the same promise/result.\n *\n * @param fullRef - The full reference in format \"nsid#defName\"\n * @returns A promise resolving to the built schema or method definition\n */\n buildFullRef = memoize(async (fullRef: string) => {\n const { nsid, hash } = parseRef(fullRef)\n\n const doc = await this.indexer.get(nsid)\n\n return this.compileDef(doc, hash)\n })\n\n protected buildRefGetter(fullRef: string): () => l.Schema<LexValue> {\n let schema: l.Schema<LexValue>\n\n this.#asyncTasks.add(\n this.buildFullRef(fullRef).then((v) => {\n if (!(v instanceof l.Schema)) {\n throw new Error(`Only refs to schema types are allowed`)\n }\n schema = v\n }),\n )\n\n return () => {\n if (schema) return schema\n throw new Error('Validator not yet built. Did you await done()?')\n }\n }\n\n protected buildTypedRefGetter(\n fullRef: string,\n ): () => l.TypedObjectSchema | l.RecordSchema {\n let validator: l.TypedObjectSchema | l.RecordSchema\n\n this.#asyncTasks.add(\n this.buildFullRef(fullRef).then((v) => {\n if (v instanceof l.TypedObjectSchema || v instanceof l.RecordSchema) {\n validator = v\n } else {\n throw new Error(\n 'Only refs to records and object definitions are allowed',\n )\n }\n }),\n )\n\n return () => {\n if (validator) return validator\n throw new Error('Validator not yet built. Did you await done()?')\n }\n }\n\n protected compileDef(doc: LexiconDocument, hash: string) {\n const def = Object.hasOwn(doc.defs, hash) ? doc.defs[hash] : null\n if (!def) {\n throw new Error(\n `No definition found for hash \"${JSON.stringify(hash)}\" in ${doc.id}`,\n )\n }\n switch (def.type) {\n case 'permission-set':\n return l.permissionSet(\n doc.id,\n def.permissions.map(({ resource, type, ...p }) =>\n l.permission(resource, p),\n ),\n def,\n )\n case 'procedure':\n return l.procedure(\n doc.id,\n this.compileParams(doc, def.parameters),\n this.compilePayload(doc, def.input),\n this.compilePayload(doc, def.output),\n this.compileErrors(doc, def.errors),\n )\n case 'query':\n return l.query(\n doc.id,\n this.compileParams(doc, def.parameters),\n this.compilePayload(doc, def.output),\n this.compileErrors(doc, def.errors),\n )\n case 'subscription':\n return l.subscription(\n doc.id,\n this.compileParams(doc, def.parameters),\n this.compilePayloadSchema(doc, def.message.schema),\n this.compileErrors(doc, def.errors),\n )\n case 'token':\n return l.token(doc.id, hash)\n case 'record':\n return l.record(def.key, doc.id, this.compileObject(doc, def.record))\n case 'object':\n return l.typedObject(doc.id, hash, this.compileObject(doc, def))\n default:\n return this.compileLeaf(doc, def)\n }\n }\n\n protected compileLeaf(\n doc: LexiconDocument,\n def: LexiconArray | LexiconArrayItems,\n ): l.Schema<LexValue> {\n if (\n 'const' in def &&\n 'enum' in def &&\n def.enum != null &&\n def.const !== undefined &&\n !(def.enum as readonly unknown[]).includes(def.const)\n ) {\n return l.never()\n }\n\n switch (def.type) {\n case 'string': {\n const schema = l.string(def)\n if (def.default != null) schema.check(def.default)\n if (def.const != null) schema.check(def.const)\n if (def.enum != null) for (const v of def.enum) schema.check(v)\n\n const result =\n def.const != null\n ? l.literal(def.const)\n : def.enum != null\n ? l.enum(def.enum)\n : schema\n\n return def.default != null ? l.withDefault(result, def.default) : result\n }\n case 'integer': {\n const schema = l.integer(def)\n if (def.default != null) schema.check(def.default)\n if (def.const != null) schema.check(def.const)\n if (def.enum != null) for (const v of def.enum) schema.check(v)\n\n const result =\n def.const != null\n ? l.literal(def.const)\n : def.enum != null\n ? l.enum(def.enum)\n : schema\n\n return def.default != null ? l.withDefault(result, def.default) : result\n }\n case 'boolean': {\n const result = def.const != null ? l.literal(def.const) : l.boolean()\n\n return def.default != null ? l.withDefault(result, def.default) : result\n }\n case 'blob':\n return l.blob(def)\n case 'cid-link':\n return l.cid()\n case 'bytes':\n return l.bytes(def)\n case 'unknown':\n return l.lexMap()\n case 'array':\n return l.array(this.compileLeaf(doc, def.items), def)\n default:\n return this.compileRef(doc, def)\n }\n }\n\n protected compileRef(\n doc: LexiconDocument,\n def: LexiconRef | LexiconRefUnion,\n ): l.Schema<LexValue> {\n switch (def.type) {\n case 'ref':\n return l.ref(this.buildRefGetter(buildFullRef(doc, def.ref)))\n case 'union':\n return l.typedUnion(\n def.refs.map((r) =>\n l.typedRef(this.buildTypedRefGetter(buildFullRef(doc, r))),\n ),\n def.closed ?? false,\n )\n default:\n // @ts-expect-error\n throw new Error(`Unknown lexicon type: ${def.type}`)\n }\n }\n\n protected compileObject(doc: LexiconDocument, def: LexiconObject) {\n const props: Record<string, l.Schema<undefined | LexValue>> = {}\n for (const [key, propDef] of Object.entries(def.properties)) {\n if (propDef === undefined) continue\n\n const isNullable = def.nullable?.includes(key)\n const isRequired = def.required?.includes(key)\n\n let schema: l.Schema<undefined | LexValue> = this.compileLeaf(\n doc,\n propDef,\n )\n\n if (isNullable) {\n schema = l.nullable(schema)\n }\n\n if (!isRequired) {\n schema = l.optional(schema)\n }\n\n props[key] = schema\n }\n return l.object(props)\n }\n\n protected compilePayload(\n doc: LexiconDocument,\n def: LexiconPayload | undefined,\n ) {\n return l.payload(\n def?.encoding,\n def?.schema ? this.compilePayloadSchema(doc, def.schema) : undefined,\n )\n }\n\n protected compileErrors(\n _doc: LexiconDocument,\n errors?: readonly LexiconError[],\n ): undefined | string[] {\n return errors?.map((e) => e.name)\n }\n\n protected compilePayloadSchema(\n doc: LexiconDocument,\n def: LexiconObject | LexiconRef | LexiconRefUnion,\n ): l.Schema<LexValue, LexValue> {\n switch (def.type) {\n case 'object':\n return this.compileObject(doc, def)\n default:\n return this.compileRef(doc, def)\n }\n }\n\n protected compileParams(doc: LexiconDocument, def?: LexiconParameters) {\n if (!def) return l.params()\n\n const shape: l.ParamsShape = {}\n for (const [paramName, paramDef] of Object.entries(def.properties)) {\n if (paramDef === undefined) continue\n\n const isRequired = def.required?.includes(paramName)\n\n const propSchema = this.compileLeaf(doc, paramDef) as\n | l.StringSchema\n | l.BooleanSchema\n | l.IntegerSchema\n | l.ArraySchema<l.StringSchema>\n | l.ArraySchema<l.BooleanSchema>\n | l.ArraySchema<l.IntegerSchema>\n\n shape[paramName] = isRequired ? propSchema : l.optional(propSchema)\n }\n\n return l.params(shape)\n }\n}\n\nclass AsyncTasks {\n /**\n * A set that, eventually, contains only rejected promises.\n */\n #promises = new Set<Promise<void>>()\n\n async done(): Promise<void> {\n do {\n // @NOTE this is going to throw on the first rejected promise (which is\n // what we want)\n for (const p of this.#promises) await p\n // At this point, all settled promises should have been removed. If\n // this.#promises is not empty, it means new promises were added during\n // the awaiting process, so we loop again.\n } while (this.#promises.size > 0)\n }\n\n add(p: Promise<void>) {\n const promise = Promise.resolve(p).then(() => {\n // No need to keep the promise any longer\n this.#promises.delete(promise)\n })\n\n void promise.catch((_err) => {\n // ignore errors here, they should be caught though done()\n })\n\n this.#promises.add(promise)\n }\n}\n\nfunction parseRef(fullRef: string) {\n const { length, 0: nsid, 1: hash } = fullRef.split('#')\n if (length !== 2) throw new Error('Uri can only have one hash segment')\n if (!nsid || !hash) throw new Error('Invalid ref, missing hash')\n return { nsid, hash }\n}\n\nfunction buildFullRef(from: LexiconDocument, ref: string) {\n if (ref.startsWith('#')) return `${from.id}${ref}`\n return ref\n}\n\nexport function memoize<Fn extends (arg: string) => unknown>(fn: Fn): Fn {\n const cache = new Map<string, ReturnType<Fn>>()\n return ((arg: string) => {\n if (cache.has(arg)) return cache.get(arg)!\n const result = fn(arg) as ReturnType<Fn>\n cache.set(arg, result)\n return result\n }) as Fn\n}\n\nfunction isAsyncIterableObject<T>(\n obj: T,\n): obj is T & object & AsyncIterable<unknown> {\n return (\n obj != null &&\n typeof obj === 'object' &&\n Symbol.asyncIterator in obj &&\n typeof obj[Symbol.asyncIterator] === 'function'\n )\n}\n"]}