@atproto/lex-installer 0.0.14 → 0.0.16

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.
@@ -11,6 +11,48 @@ const fs_js_1 = require("./fs.js");
11
11
  const lexicons_manifest_js_1 = require("./lexicons-manifest.js");
12
12
  const nsid_map_js_1 = require("./nsid-map.js");
13
13
  const nsid_set_js_1 = require("./nsid-set.js");
14
+ /**
15
+ * Manages the installation of Lexicon schemas from the AT Protocol network.
16
+ *
17
+ * The `LexInstaller` class handles fetching, caching, and organizing lexicon
18
+ * documents. It tracks dependencies between lexicons and ensures all referenced
19
+ * schemas are installed. The class implements `AsyncDisposable` for proper
20
+ * resource cleanup.
21
+ *
22
+ * @example
23
+ * Basic usage with async disposal:
24
+ * ```typescript
25
+ * import { LexInstaller } from '@atproto/lex-installer'
26
+ *
27
+ * await using installer = new LexInstaller({
28
+ * lexicons: './lexicons',
29
+ * manifest: './lexicons.manifest.json',
30
+ * })
31
+ *
32
+ * await installer.install({
33
+ * additions: ['app.bsky.feed.post'],
34
+ * })
35
+ *
36
+ * await installer.save()
37
+ * // Resources automatically cleaned up when block exits
38
+ * ```
39
+ *
40
+ * @example
41
+ * Manual disposal:
42
+ * ```typescript
43
+ * const installer = new LexInstaller({
44
+ * lexicons: './lexicons',
45
+ * manifest: './lexicons.manifest.json',
46
+ * })
47
+ *
48
+ * try {
49
+ * await installer.install({ additions: ['app.bsky.actor.profile'] })
50
+ * await installer.save()
51
+ * } finally {
52
+ * await installer[Symbol.asyncDispose]()
53
+ * }
54
+ * ```
55
+ */
14
56
  class LexInstaller {
15
57
  options;
16
58
  lexiconResolver;
@@ -31,9 +73,59 @@ class LexInstaller {
31
73
  async [Symbol.asyncDispose]() {
32
74
  await this.indexer[Symbol.asyncDispose]();
33
75
  }
76
+ /**
77
+ * Compares the current manifest state with another manifest for equality.
78
+ *
79
+ * Both manifests are normalized before comparison to ensure consistent
80
+ * ordering of entries. Useful for detecting changes during CI verification.
81
+ *
82
+ * @param manifest - The manifest to compare against
83
+ * @returns `true` if the manifests are equivalent, `false` otherwise
84
+ */
34
85
  equals(manifest) {
35
86
  return (0, lex_data_1.lexEquals)((0, lexicons_manifest_js_1.normalizeLexiconsManifest)(manifest), (0, lexicons_manifest_js_1.normalizeLexiconsManifest)(this.manifest));
36
87
  }
88
+ /**
89
+ * Installs lexicons and their dependencies.
90
+ *
91
+ * This method processes explicit additions and restores entries from an
92
+ * existing manifest. It recursively resolves and installs all referenced
93
+ * lexicons to ensure complete dependency trees.
94
+ *
95
+ * @param options - Installation options
96
+ * @param options.additions - Iterable of lexicon identifiers to add.
97
+ * Can be NSID strings or AT URIs.
98
+ * @param options.manifest - Existing manifest to use as a baseline.
99
+ * Previously resolved URIs are preserved unless explicitly overridden.
100
+ *
101
+ * @example
102
+ * Install new lexicons:
103
+ * ```typescript
104
+ * await installer.install({
105
+ * additions: ['app.bsky.feed.post', 'app.bsky.actor.profile'],
106
+ * })
107
+ * ```
108
+ *
109
+ * @example
110
+ * Install with existing manifest as hint:
111
+ * ```typescript
112
+ * const existingManifest = await readJsonFile('./lexicons.manifest.json')
113
+ * await installer.install({
114
+ * additions: ['com.example.newLexicon'],
115
+ * manifest: existingManifest,
116
+ * })
117
+ * ```
118
+ *
119
+ * @example
120
+ * Install from specific AT URIs:
121
+ * ```typescript
122
+ * await installer.install({
123
+ * additions: [
124
+ * 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post',
125
+ * ],
126
+ * })
127
+ * ```
128
+ */
37
129
  async install({ additions, manifest, } = {}) {
38
130
  const roots = new nsid_map_js_1.NsidMap();
39
131
  // First, process explicit additions
@@ -122,6 +214,15 @@ class LexInstaller {
122
214
  };
123
215
  return { lexicon, uri };
124
216
  }
217
+ /**
218
+ * Fetches a lexicon document from the network and saves it locally.
219
+ *
220
+ * The lexicon is retrieved from the specified AT URI, written to the
221
+ * local lexicons directory, and its metadata is recorded for the manifest.
222
+ *
223
+ * @param uri - The AT URI pointing to the lexicon document
224
+ * @returns An object containing the fetched lexicon document and its CID
225
+ */
125
226
  async fetch(uri) {
126
227
  console.debug(`Fetching lexicon from ${uri}...`);
127
228
  const { lexicon, cid } = await this.lexiconResolver.fetch(uri, {
@@ -131,6 +232,12 @@ class LexInstaller {
131
232
  await (0, fs_js_1.writeJsonFile)(`${basePath}.json`, lexicon);
132
233
  return { lexicon, cid };
133
234
  }
235
+ /**
236
+ * Saves the current manifest to disk.
237
+ *
238
+ * The manifest is normalized before saving to ensure consistent ordering
239
+ * of entries, making it suitable for version control.
240
+ */
134
241
  async save() {
135
242
  await (0, fs_js_1.writeJsonFile)(this.options.manifest, (0, lexicons_manifest_js_1.normalizeLexiconsManifest)(this.manifest));
136
243
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lex-installer.js","sourceRoot":"","sources":["../src/lex-installer.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from 'node:path'\nimport { LexiconDirectoryIndexer } from '@atproto/lex-builder'\nimport { cidForLex } from '@atproto/lex-cbor'\nimport { Cid, lexEquals } from '@atproto/lex-data'\nimport {\n LexiconDocument,\n LexiconParameters,\n LexiconPermission,\n LexiconRef,\n LexiconRefUnion,\n LexiconUnknown,\n MainLexiconDefinition,\n NamedLexiconDefinition,\n} from '@atproto/lex-document'\nimport { LexResolver, LexResolverOptions } from '@atproto/lex-resolver'\nimport { AtUriString, NsidString } from '@atproto/lex-schema'\nimport { AtUri, NSID } from '@atproto/syntax'\nimport { isEnoentError, writeJsonFile } from './fs.js'\nimport {\n LexiconsManifest,\n normalizeLexiconsManifest,\n} from './lexicons-manifest.js'\nimport { NsidMap } from './nsid-map.js'\nimport { NsidSet } from './nsid-set.js'\n\nexport type LexInstallerOptions = LexResolverOptions & {\n lexicons: string\n manifest: string\n update?: boolean\n}\n\nexport class LexInstaller implements AsyncDisposable {\n protected readonly lexiconResolver: LexResolver\n protected readonly indexer: LexiconDirectoryIndexer\n protected readonly documents = new NsidMap<LexiconDocument>()\n protected readonly manifest: LexiconsManifest = {\n version: 1,\n lexicons: [],\n resolutions: {},\n }\n\n constructor(protected readonly options: LexInstallerOptions) {\n this.lexiconResolver = new LexResolver(options)\n this.indexer = new LexiconDirectoryIndexer({\n lexicons: options.lexicons,\n })\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.indexer[Symbol.asyncDispose]()\n }\n\n equals(manifest: LexiconsManifest): boolean {\n return lexEquals(\n normalizeLexiconsManifest(manifest),\n normalizeLexiconsManifest(this.manifest),\n )\n }\n\n async install({\n additions,\n manifest,\n }: {\n additions?: Iterable<string>\n manifest?: LexiconsManifest\n } = {}): Promise<void> {\n const roots = new NsidMap<AtUri | null>()\n\n // First, process explicit additions\n for (const lexicon of new Set(additions)) {\n const [nsid, uri]: [NSID, AtUri | null] = lexicon.startsWith('at://')\n ? ((uri) => [NSID.from(uri.rkey), uri])(new AtUri(lexicon))\n : [NSID.from(lexicon), null]\n\n if (roots.has(nsid)) {\n throw new Error(\n `Duplicate lexicon addition: ${nsid} (${roots.get(nsid) ?? lexicon})`,\n )\n }\n\n roots.set(nsid, uri)\n console.debug(`Adding new lexicon: ${nsid} (${uri ?? 'from NSID'})`)\n }\n\n // Next, restore previously existing manifest entries\n if (manifest) {\n for (const lexicon of manifest.lexicons) {\n const nsid = NSID.from(lexicon)\n\n // Skip entries already added explicitly\n if (!roots.has(nsid)) {\n const uri = manifest.resolutions[lexicon]\n ? new AtUri(manifest.resolutions[lexicon].uri)\n : null\n\n roots.set(nsid, uri)\n\n console.debug(\n `Adding lexicon from manifest: ${nsid} (${uri ?? 'from NSID'})`,\n )\n }\n }\n }\n\n // Install all root lexicons (and store them in the manifest)\n await Promise.all(\n Array.from(roots, async ([nsid, sourceUri]) => {\n console.debug(`Installing lexicon: ${nsid}`)\n\n const { lexicon: document } = sourceUri\n ? await this.installFromUri(sourceUri)\n : await this.installFromNsid(nsid)\n\n // Store the direct reference in the new manifest\n this.manifest.lexicons.push(document.id)\n }),\n )\n\n // Then recursively install all referenced lexicons\n let results: unknown[]\n do {\n results = await Promise.all(\n Array.from(this.getMissingIds(), async (nsid) => {\n console.debug(`Resolving dependency lexicon: ${nsid}`)\n\n const nsidStr = nsid.toString() as NsidString\n const resolvedUri = manifest?.resolutions[nsidStr]?.uri\n ? new AtUri(manifest.resolutions[nsidStr].uri)\n : null\n if (resolvedUri) {\n await this.installFromUri(resolvedUri)\n } else {\n await this.installFromNsid(nsid)\n }\n }),\n )\n } while (results.length > 0)\n }\n\n protected getMissingIds(): NsidSet {\n const missing = new NsidSet()\n\n for (const document of this.documents.values()) {\n for (const nsid of listDocumentNsidRefs(document)) {\n if (!this.documents.has(nsid)) {\n missing.add(nsid)\n }\n }\n }\n\n return missing\n }\n\n protected async installFromNsid(nsid: NSID) {\n const uri = await this.lexiconResolver.resolve(nsid)\n return this.installFromUri(uri)\n }\n\n protected async installFromUri(uri: AtUri): Promise<{\n lexicon: LexiconDocument\n uri: AtUri\n }> {\n const { lexicon, cid } = this.options.update\n ? await this.fetch(uri)\n : await this.indexer.get(uri.rkey).then(\n async (lexicon) => {\n console.debug(`Re-using existing lexicon ${uri.rkey} from indexer`)\n const cid = await cidForLex(lexicon)\n return { cid, lexicon }\n },\n (err) => {\n if (isEnoentError(err)) return this.fetch(uri)\n throw err\n },\n )\n\n this.documents.set(NSID.from(lexicon.id), lexicon)\n this.manifest.resolutions[lexicon.id] = {\n cid: cid.toString(),\n uri: uri.toString() as AtUriString,\n }\n\n return { lexicon, uri }\n }\n\n async fetch(uri: AtUri): Promise<{ lexicon: LexiconDocument; cid: Cid }> {\n console.debug(`Fetching lexicon from ${uri}...`)\n\n const { lexicon, cid } = await this.lexiconResolver.fetch(uri, {\n noCache: this.options.update,\n })\n\n const basePath = join(this.options.lexicons, ...lexicon.id.split('.'))\n await writeJsonFile(`${basePath}.json`, lexicon)\n\n return { lexicon, cid }\n }\n\n async save(): Promise<void> {\n await writeJsonFile(\n this.options.manifest,\n normalizeLexiconsManifest(this.manifest),\n )\n }\n}\n\nfunction* listDocumentNsidRefs(doc: LexiconDocument): Iterable<NSID> {\n try {\n for (const def of Object.values(doc.defs)) {\n if (def) {\n for (const ref of defRefs(def)) {\n const [nsid] = ref.split('#', 1)\n if (nsid) yield NSID.from(nsid)\n }\n }\n }\n } catch (cause) {\n throw new Error(`Failed to extract refs from lexicon ${doc.id}`, { cause })\n }\n}\n\nfunction* defRefs(\n def:\n | MainLexiconDefinition\n | NamedLexiconDefinition\n | LexiconPermission\n | LexiconUnknown\n | LexiconParameters\n | LexiconRef\n | LexiconRefUnion,\n): Iterable<string> {\n switch (def.type) {\n case 'string':\n if (def.knownValues) {\n for (const val of def.knownValues) {\n // Tokens ?\n const { length, 0: nsid, 1: hash } = val.split('#')\n if (length === 2 && hash) {\n try {\n NSID.from(nsid)\n yield val\n } catch {\n // ignore invalid nsid\n }\n }\n }\n }\n return\n case 'array':\n return yield* defRefs(def.items)\n case 'params':\n case 'object':\n for (const prop of Object.values(def.properties)) {\n yield* defRefs(prop)\n }\n return\n case 'union':\n yield* def.refs\n return\n case 'ref': {\n yield def.ref\n return\n }\n case 'record':\n yield* defRefs(def.record)\n return\n case 'procedure':\n if (def.input?.schema) {\n yield* defRefs(def.input.schema)\n }\n // fallthrough\n case 'query':\n if (def.output?.schema) {\n yield* defRefs(def.output.schema)\n }\n // fallthrough\n case 'subscription':\n if (def.parameters) {\n yield* defRefs(def.parameters)\n }\n if ('message' in def && def.message?.schema) {\n yield* defRefs(def.message.schema)\n }\n return\n case 'permission-set':\n for (const permission of def.permissions) {\n yield* defRefs(permission)\n }\n return\n case 'permission':\n if (def.resource === 'rpc') {\n if (Array.isArray(def.lxm)) {\n for (const lxm of def.lxm) {\n if (typeof lxm === 'string') {\n yield lxm\n }\n }\n }\n } else if (def.resource === 'repo') {\n if (Array.isArray(def.collection)) {\n for (const lxm of def.collection) {\n if (typeof lxm === 'string') {\n yield lxm\n }\n }\n }\n }\n return\n case 'boolean':\n case 'cid-link':\n case 'token':\n case 'bytes':\n case 'blob':\n case 'integer':\n case 'unknown':\n // @NOTE We explicitly list all types here to ensure exhaustiveness\n // causing TS to error if a new type is added without updating this switch\n return\n default:\n // @ts-expect-error\n throw new Error(`Unknown lexicon def type: ${def.type}`)\n }\n}\n"]}
1
+ {"version":3,"file":"lex-installer.js","sourceRoot":"","sources":["../src/lex-installer.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from 'node:path'\nimport { LexiconDirectoryIndexer } from '@atproto/lex-builder'\nimport { cidForLex } from '@atproto/lex-cbor'\nimport { Cid, lexEquals } from '@atproto/lex-data'\nimport {\n LexiconDocument,\n LexiconParameters,\n LexiconPermission,\n LexiconRef,\n LexiconRefUnion,\n LexiconUnknown,\n MainLexiconDefinition,\n NamedLexiconDefinition,\n} from '@atproto/lex-document'\nimport { LexResolver, LexResolverOptions } from '@atproto/lex-resolver'\nimport { AtUriString, NsidString } from '@atproto/lex-schema'\nimport { AtUri, NSID } from '@atproto/syntax'\nimport { isEnoentError, writeJsonFile } from './fs.js'\nimport {\n LexiconsManifest,\n normalizeLexiconsManifest,\n} from './lexicons-manifest.js'\nimport { NsidMap } from './nsid-map.js'\nimport { NsidSet } from './nsid-set.js'\n\n/**\n * Configuration options for the {@link LexInstaller} class.\n *\n * Extends {@link LexResolverOptions} with paths for lexicon storage\n * and manifest management.\n *\n * @example\n * ```typescript\n * const options: LexInstallerOptions = {\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * update: false,\n * }\n * ```\n */\nexport type LexInstallerOptions = LexResolverOptions & {\n /**\n * Path to the directory where lexicon JSON files will be stored.\n * The directory structure mirrors the NSID hierarchy\n * (e.g., 'app.bsky.feed.post' becomes 'app/bsky/feed/post.json').\n */\n lexicons: string\n\n /**\n * Path to the manifest file that tracks installed lexicons and their resolutions.\n */\n manifest: string\n\n /**\n * When `true`, forces re-fetching of lexicons from the network even if they\n * already exist locally. Useful for updating to newer versions.\n * @default false\n */\n update?: boolean\n}\n\n/**\n * Manages the installation of Lexicon schemas from the AT Protocol network.\n *\n * The `LexInstaller` class handles fetching, caching, and organizing lexicon\n * documents. It tracks dependencies between lexicons and ensures all referenced\n * schemas are installed. The class implements `AsyncDisposable` for proper\n * resource cleanup.\n *\n * @example\n * Basic usage with async disposal:\n * ```typescript\n * import { LexInstaller } from '@atproto/lex-installer'\n *\n * await using installer = new LexInstaller({\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * })\n *\n * await installer.install({\n * additions: ['app.bsky.feed.post'],\n * })\n *\n * await installer.save()\n * // Resources automatically cleaned up when block exits\n * ```\n *\n * @example\n * Manual disposal:\n * ```typescript\n * const installer = new LexInstaller({\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * })\n *\n * try {\n * await installer.install({ additions: ['app.bsky.actor.profile'] })\n * await installer.save()\n * } finally {\n * await installer[Symbol.asyncDispose]()\n * }\n * ```\n */\nexport class LexInstaller implements AsyncDisposable {\n protected readonly lexiconResolver: LexResolver\n protected readonly indexer: LexiconDirectoryIndexer\n protected readonly documents = new NsidMap<LexiconDocument>()\n protected readonly manifest: LexiconsManifest = {\n version: 1,\n lexicons: [],\n resolutions: {},\n }\n\n constructor(protected readonly options: LexInstallerOptions) {\n this.lexiconResolver = new LexResolver(options)\n this.indexer = new LexiconDirectoryIndexer({\n lexicons: options.lexicons,\n })\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.indexer[Symbol.asyncDispose]()\n }\n\n /**\n * Compares the current manifest state with another manifest for equality.\n *\n * Both manifests are normalized before comparison to ensure consistent\n * ordering of entries. Useful for detecting changes during CI verification.\n *\n * @param manifest - The manifest to compare against\n * @returns `true` if the manifests are equivalent, `false` otherwise\n */\n equals(manifest: LexiconsManifest): boolean {\n return lexEquals(\n normalizeLexiconsManifest(manifest),\n normalizeLexiconsManifest(this.manifest),\n )\n }\n\n /**\n * Installs lexicons and their dependencies.\n *\n * This method processes explicit additions and restores entries from an\n * existing manifest. It recursively resolves and installs all referenced\n * lexicons to ensure complete dependency trees.\n *\n * @param options - Installation options\n * @param options.additions - Iterable of lexicon identifiers to add.\n * Can be NSID strings or AT URIs.\n * @param options.manifest - Existing manifest to use as a baseline.\n * Previously resolved URIs are preserved unless explicitly overridden.\n *\n * @example\n * Install new lexicons:\n * ```typescript\n * await installer.install({\n * additions: ['app.bsky.feed.post', 'app.bsky.actor.profile'],\n * })\n * ```\n *\n * @example\n * Install with existing manifest as hint:\n * ```typescript\n * const existingManifest = await readJsonFile('./lexicons.manifest.json')\n * await installer.install({\n * additions: ['com.example.newLexicon'],\n * manifest: existingManifest,\n * })\n * ```\n *\n * @example\n * Install from specific AT URIs:\n * ```typescript\n * await installer.install({\n * additions: [\n * 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post',\n * ],\n * })\n * ```\n */\n async install({\n additions,\n manifest,\n }: {\n additions?: Iterable<string>\n manifest?: LexiconsManifest\n } = {}): Promise<void> {\n const roots = new NsidMap<AtUri | null>()\n\n // First, process explicit additions\n for (const lexicon of new Set(additions)) {\n const [nsid, uri]: [NSID, AtUri | null] = lexicon.startsWith('at://')\n ? ((uri) => [NSID.from(uri.rkey), uri])(new AtUri(lexicon))\n : [NSID.from(lexicon), null]\n\n if (roots.has(nsid)) {\n throw new Error(\n `Duplicate lexicon addition: ${nsid} (${roots.get(nsid) ?? lexicon})`,\n )\n }\n\n roots.set(nsid, uri)\n console.debug(`Adding new lexicon: ${nsid} (${uri ?? 'from NSID'})`)\n }\n\n // Next, restore previously existing manifest entries\n if (manifest) {\n for (const lexicon of manifest.lexicons) {\n const nsid = NSID.from(lexicon)\n\n // Skip entries already added explicitly\n if (!roots.has(nsid)) {\n const uri = manifest.resolutions[lexicon]\n ? new AtUri(manifest.resolutions[lexicon].uri)\n : null\n\n roots.set(nsid, uri)\n\n console.debug(\n `Adding lexicon from manifest: ${nsid} (${uri ?? 'from NSID'})`,\n )\n }\n }\n }\n\n // Install all root lexicons (and store them in the manifest)\n await Promise.all(\n Array.from(roots, async ([nsid, sourceUri]) => {\n console.debug(`Installing lexicon: ${nsid}`)\n\n const { lexicon: document } = sourceUri\n ? await this.installFromUri(sourceUri)\n : await this.installFromNsid(nsid)\n\n // Store the direct reference in the new manifest\n this.manifest.lexicons.push(document.id)\n }),\n )\n\n // Then recursively install all referenced lexicons\n let results: unknown[]\n do {\n results = await Promise.all(\n Array.from(this.getMissingIds(), async (nsid) => {\n console.debug(`Resolving dependency lexicon: ${nsid}`)\n\n const nsidStr = nsid.toString() as NsidString\n const resolvedUri = manifest?.resolutions[nsidStr]?.uri\n ? new AtUri(manifest.resolutions[nsidStr].uri)\n : null\n if (resolvedUri) {\n await this.installFromUri(resolvedUri)\n } else {\n await this.installFromNsid(nsid)\n }\n }),\n )\n } while (results.length > 0)\n }\n\n protected getMissingIds(): NsidSet {\n const missing = new NsidSet()\n\n for (const document of this.documents.values()) {\n for (const nsid of listDocumentNsidRefs(document)) {\n if (!this.documents.has(nsid)) {\n missing.add(nsid)\n }\n }\n }\n\n return missing\n }\n\n protected async installFromNsid(nsid: NSID) {\n const uri = await this.lexiconResolver.resolve(nsid)\n return this.installFromUri(uri)\n }\n\n protected async installFromUri(uri: AtUri): Promise<{\n lexicon: LexiconDocument\n uri: AtUri\n }> {\n const { lexicon, cid } = this.options.update\n ? await this.fetch(uri)\n : await this.indexer.get(uri.rkey).then(\n async (lexicon) => {\n console.debug(`Re-using existing lexicon ${uri.rkey} from indexer`)\n const cid = await cidForLex(lexicon)\n return { cid, lexicon }\n },\n (err) => {\n if (isEnoentError(err)) return this.fetch(uri)\n throw err\n },\n )\n\n this.documents.set(NSID.from(lexicon.id), lexicon)\n this.manifest.resolutions[lexicon.id] = {\n cid: cid.toString(),\n uri: uri.toString() as AtUriString,\n }\n\n return { lexicon, uri }\n }\n\n /**\n * Fetches a lexicon document from the network and saves it locally.\n *\n * The lexicon is retrieved from the specified AT URI, written to the\n * local lexicons directory, and its metadata is recorded for the manifest.\n *\n * @param uri - The AT URI pointing to the lexicon document\n * @returns An object containing the fetched lexicon document and its CID\n */\n async fetch(uri: AtUri): Promise<{ lexicon: LexiconDocument; cid: Cid }> {\n console.debug(`Fetching lexicon from ${uri}...`)\n\n const { lexicon, cid } = await this.lexiconResolver.fetch(uri, {\n noCache: this.options.update,\n })\n\n const basePath = join(this.options.lexicons, ...lexicon.id.split('.'))\n await writeJsonFile(`${basePath}.json`, lexicon)\n\n return { lexicon, cid }\n }\n\n /**\n * Saves the current manifest to disk.\n *\n * The manifest is normalized before saving to ensure consistent ordering\n * of entries, making it suitable for version control.\n */\n async save(): Promise<void> {\n await writeJsonFile(\n this.options.manifest,\n normalizeLexiconsManifest(this.manifest),\n )\n }\n}\n\nfunction* listDocumentNsidRefs(doc: LexiconDocument): Iterable<NSID> {\n try {\n for (const def of Object.values(doc.defs)) {\n if (def) {\n for (const ref of defRefs(def)) {\n const [nsid] = ref.split('#', 1)\n if (nsid) yield NSID.from(nsid)\n }\n }\n }\n } catch (cause) {\n throw new Error(`Failed to extract refs from lexicon ${doc.id}`, { cause })\n }\n}\n\nfunction* defRefs(\n def:\n | MainLexiconDefinition\n | NamedLexiconDefinition\n | LexiconPermission\n | LexiconUnknown\n | LexiconParameters\n | LexiconRef\n | LexiconRefUnion,\n): Iterable<string> {\n switch (def.type) {\n case 'string':\n if (def.knownValues) {\n for (const val of def.knownValues) {\n // Tokens ?\n const { length, 0: nsid, 1: hash } = val.split('#')\n if (length === 2 && hash) {\n try {\n NSID.from(nsid)\n yield val\n } catch {\n // ignore invalid nsid\n }\n }\n }\n }\n return\n case 'array':\n return yield* defRefs(def.items)\n case 'params':\n case 'object':\n for (const prop of Object.values(def.properties)) {\n yield* defRefs(prop)\n }\n return\n case 'union':\n yield* def.refs\n return\n case 'ref': {\n yield def.ref\n return\n }\n case 'record':\n yield* defRefs(def.record)\n return\n case 'procedure':\n if (def.input?.schema) {\n yield* defRefs(def.input.schema)\n }\n // fallthrough\n case 'query':\n if (def.output?.schema) {\n yield* defRefs(def.output.schema)\n }\n // fallthrough\n case 'subscription':\n if (def.parameters) {\n yield* defRefs(def.parameters)\n }\n if ('message' in def && def.message?.schema) {\n yield* defRefs(def.message.schema)\n }\n return\n case 'permission-set':\n for (const permission of def.permissions) {\n yield* defRefs(permission)\n }\n return\n case 'permission':\n if (def.resource === 'rpc') {\n if (Array.isArray(def.lxm)) {\n for (const lxm of def.lxm) {\n if (typeof lxm === 'string') {\n yield lxm\n }\n }\n }\n } else if (def.resource === 'repo') {\n if (Array.isArray(def.collection)) {\n for (const lxm of def.collection) {\n if (typeof lxm === 'string') {\n yield lxm\n }\n }\n }\n }\n return\n case 'boolean':\n case 'cid-link':\n case 'token':\n case 'bytes':\n case 'blob':\n case 'integer':\n case 'unknown':\n // @NOTE We explicitly list all types here to ensure exhaustiveness\n // causing TS to error if a new type is added without updating this switch\n return\n default:\n // @ts-expect-error\n throw new Error(`Unknown lexicon def type: ${def.type}`)\n }\n}\n"]}
@@ -1,20 +1,48 @@
1
1
  import { l } from '@atproto/lex-schema';
2
+ /**
3
+ * Schema for validating and parsing lexicons manifest files.
4
+ *
5
+ * The manifest tracks which lexicons are installed and how they were resolved.
6
+ * This schema ensures the manifest file conforms to the expected structure.
7
+ */
2
8
  export declare const lexiconsManifestSchema: l.ObjectSchema<{
9
+ /** Schema version, currently always 1 */
3
10
  readonly version: l.LiteralSchema<1>;
11
+ /** Array of NSID strings for directly requested lexicons */
4
12
  readonly lexicons: l.ArraySchema<l.StringSchema<{
5
13
  readonly format: "nsid";
6
14
  }>>;
15
+ /** Map of NSID to resolution info (AT URI and CID) for all installed lexicons */
7
16
  readonly resolutions: l.DictSchema<l.StringSchema<{
8
17
  readonly format: "nsid";
9
18
  }>, l.ObjectSchema<{
19
+ /** AT URI where the lexicon was fetched from */
10
20
  readonly uri: l.StringSchema<{
11
21
  readonly format: "at-uri";
12
22
  }>;
23
+ /** Content identifier (CID) of the lexicon document */
13
24
  readonly cid: l.StringSchema<{
14
25
  readonly format: "cid";
15
26
  }>;
16
27
  }>>;
17
28
  }>;
29
+ /**
30
+ * Type representing a parsed lexicons manifest.
31
+ */
18
32
  export type LexiconsManifest = l.Infer<typeof lexiconsManifestSchema>;
33
+ /**
34
+ * Normalizes a lexicons manifest for consistent storage and comparison.
35
+ *
36
+ * This function:
37
+ * - Sorts the `lexicons` array alphabetically
38
+ * - Sorts the `resolutions` object entries by key
39
+ * - Validates the result against the schema
40
+ *
41
+ * Normalization ensures that manifests with the same content produce identical
42
+ * JSON output, making them suitable for version control and comparison.
43
+ *
44
+ * @param manifest - The manifest to normalize
45
+ * @returns A new normalized manifest object
46
+ */
19
47
  export declare function normalizeLexiconsManifest(manifest: LexiconsManifest): LexiconsManifest;
20
48
  //# sourceMappingURL=lexicons-manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lexicons-manifest.d.ts","sourceRoot":"","sources":["../src/lexicons-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AAEvC,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;EAUjC,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAErE,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,gBAAgB,GACzB,gBAAgB,CAYlB"}
1
+ {"version":3,"file":"lexicons-manifest.d.ts","sourceRoot":"","sources":["../src/lexicons-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;IACjC,yCAAyC;;IAEzC,4DAA4D;;;;IAE5D,iFAAiF;;;;QAI7E,gDAAgD;;;;QAEhD,uDAAuD;;;;;EAI3D,CAAA;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAErE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,gBAAgB,GACzB,gBAAgB,CAYlB"}
@@ -3,14 +3,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.lexiconsManifestSchema = void 0;
4
4
  exports.normalizeLexiconsManifest = normalizeLexiconsManifest;
5
5
  const lex_schema_1 = require("@atproto/lex-schema");
6
+ /**
7
+ * Schema for validating and parsing lexicons manifest files.
8
+ *
9
+ * The manifest tracks which lexicons are installed and how they were resolved.
10
+ * This schema ensures the manifest file conforms to the expected structure.
11
+ */
6
12
  exports.lexiconsManifestSchema = lex_schema_1.l.object({
13
+ /** Schema version, currently always 1 */
7
14
  version: lex_schema_1.l.literal(1),
15
+ /** Array of NSID strings for directly requested lexicons */
8
16
  lexicons: lex_schema_1.l.array(lex_schema_1.l.string({ format: 'nsid' })),
17
+ /** Map of NSID to resolution info (AT URI and CID) for all installed lexicons */
9
18
  resolutions: lex_schema_1.l.dict(lex_schema_1.l.string({ format: 'nsid' }), lex_schema_1.l.object({
19
+ /** AT URI where the lexicon was fetched from */
10
20
  uri: lex_schema_1.l.string({ format: 'at-uri' }),
21
+ /** Content identifier (CID) of the lexicon document */
11
22
  cid: lex_schema_1.l.string({ format: 'cid' }),
12
23
  })),
13
24
  });
25
+ /**
26
+ * Normalizes a lexicons manifest for consistent storage and comparison.
27
+ *
28
+ * This function:
29
+ * - Sorts the `lexicons` array alphabetically
30
+ * - Sorts the `resolutions` object entries by key
31
+ * - Validates the result against the schema
32
+ *
33
+ * Normalization ensures that manifests with the same content produce identical
34
+ * JSON output, making them suitable for version control and comparison.
35
+ *
36
+ * @param manifest - The manifest to normalize
37
+ * @returns A new normalized manifest object
38
+ */
14
39
  function normalizeLexiconsManifest(manifest) {
15
40
  const normalized = {
16
41
  version: manifest.version,
@@ -1 +1 @@
1
- {"version":3,"file":"lexicons-manifest.js","sourceRoot":"","sources":["../src/lexicons-manifest.ts"],"names":[],"mappings":";;;AAgBA,8DAcC;AA9BD,oDAAuC;AAE1B,QAAA,sBAAsB,GAAG,cAAC,CAAC,MAAM,CAAC;IAC7C,OAAO,EAAE,cAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,QAAQ,EAAE,cAAC,CAAC,KAAK,CAAC,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/C,WAAW,EAAE,cAAC,CAAC,IAAI,CACjB,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAC5B,cAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACnC,GAAG,EAAE,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;KACjC,CAAC,CACH;CACF,CAAC,CAAA;AAIF,SAAgB,yBAAyB,CACvC,QAA0B;IAE1B,MAAM,UAAU,GAAqB;QACnC,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE;QACvC,WAAW,EAAE,MAAM,CAAC,WAAW,CAC7B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;aACjC,IAAI,CAAC,sBAAsB,CAAC;aAC5B,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CACjD;KACF,CAAA;IACD,oBAAoB;IACpB,OAAO,8BAAsB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;AACjD,CAAC;AAED,SAAS,sBAAsB,CAC7B,CAAoB,EACpB,CAAoB;IAEpB,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC/C,CAAC","sourcesContent":["import { l } from '@atproto/lex-schema'\n\nexport const lexiconsManifestSchema = l.object({\n version: l.literal(1),\n lexicons: l.array(l.string({ format: 'nsid' })),\n resolutions: l.dict(\n l.string({ format: 'nsid' }),\n l.object({\n uri: l.string({ format: 'at-uri' }),\n cid: l.string({ format: 'cid' }),\n }),\n ),\n})\n\nexport type LexiconsManifest = l.Infer<typeof lexiconsManifestSchema>\n\nexport function normalizeLexiconsManifest(\n manifest: LexiconsManifest,\n): LexiconsManifest {\n const normalized: LexiconsManifest = {\n version: manifest.version,\n lexicons: [...manifest.lexicons].sort(),\n resolutions: Object.fromEntries(\n Object.entries(manifest.resolutions)\n .sort(compareObjectEntriesFn)\n .map(([k, { uri, cid }]) => [k, { uri, cid }]),\n ),\n }\n // For good measure:\n return lexiconsManifestSchema.parse(normalized)\n}\n\nfunction compareObjectEntriesFn(\n a: [string, unknown],\n b: [string, unknown],\n): number {\n return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0\n}\n"]}
1
+ {"version":3,"file":"lexicons-manifest.js","sourceRoot":"","sources":["../src/lexicons-manifest.ts"],"names":[],"mappings":";;;AA4CA,8DAcC;AA1DD,oDAAuC;AAEvC;;;;;GAKG;AACU,QAAA,sBAAsB,GAAG,cAAC,CAAC,MAAM,CAAC;IAC7C,yCAAyC;IACzC,OAAO,EAAE,cAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,EAAE,cAAC,CAAC,KAAK,CAAC,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/C,iFAAiF;IACjF,WAAW,EAAE,cAAC,CAAC,IAAI,CACjB,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAC5B,cAAC,CAAC,MAAM,CAAC;QACP,gDAAgD;QAChD,GAAG,EAAE,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACnC,uDAAuD;QACvD,GAAG,EAAE,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;KACjC,CAAC,CACH;CACF,CAAC,CAAA;AAOF;;;;;;;;;;;;;GAaG;AACH,SAAgB,yBAAyB,CACvC,QAA0B;IAE1B,MAAM,UAAU,GAAqB;QACnC,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE;QACvC,WAAW,EAAE,MAAM,CAAC,WAAW,CAC7B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;aACjC,IAAI,CAAC,sBAAsB,CAAC;aAC5B,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CACjD;KACF,CAAA;IACD,oBAAoB;IACpB,OAAO,8BAAsB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;AACjD,CAAC;AAED,SAAS,sBAAsB,CAC7B,CAAoB,EACpB,CAAoB;IAEpB,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC/C,CAAC","sourcesContent":["import { l } from '@atproto/lex-schema'\n\n/**\n * Schema for validating and parsing lexicons manifest files.\n *\n * The manifest tracks which lexicons are installed and how they were resolved.\n * This schema ensures the manifest file conforms to the expected structure.\n */\nexport const lexiconsManifestSchema = l.object({\n /** Schema version, currently always 1 */\n version: l.literal(1),\n /** Array of NSID strings for directly requested lexicons */\n lexicons: l.array(l.string({ format: 'nsid' })),\n /** Map of NSID to resolution info (AT URI and CID) for all installed lexicons */\n resolutions: l.dict(\n l.string({ format: 'nsid' }),\n l.object({\n /** AT URI where the lexicon was fetched from */\n uri: l.string({ format: 'at-uri' }),\n /** Content identifier (CID) of the lexicon document */\n cid: l.string({ format: 'cid' }),\n }),\n ),\n})\n\n/**\n * Type representing a parsed lexicons manifest.\n */\nexport type LexiconsManifest = l.Infer<typeof lexiconsManifestSchema>\n\n/**\n * Normalizes a lexicons manifest for consistent storage and comparison.\n *\n * This function:\n * - Sorts the `lexicons` array alphabetically\n * - Sorts the `resolutions` object entries by key\n * - Validates the result against the schema\n *\n * Normalization ensures that manifests with the same content produce identical\n * JSON output, making them suitable for version control and comparison.\n *\n * @param manifest - The manifest to normalize\n * @returns A new normalized manifest object\n */\nexport function normalizeLexiconsManifest(\n manifest: LexiconsManifest,\n): LexiconsManifest {\n const normalized: LexiconsManifest = {\n version: manifest.version,\n lexicons: [...manifest.lexicons].sort(),\n resolutions: Object.fromEntries(\n Object.entries(manifest.resolutions)\n .sort(compareObjectEntriesFn)\n .map(([k, { uri, cid }]) => [k, { uri, cid }]),\n ),\n }\n // For good measure:\n return lexiconsManifestSchema.parse(normalized)\n}\n\nfunction compareObjectEntriesFn(\n a: [string, unknown],\n b: [string, unknown],\n): number {\n return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0\n}\n"]}
@@ -1,16 +1,25 @@
1
1
  import { NSID } from '@atproto/syntax';
2
2
  /**
3
- * A Map implementation that maps keys of type K to an internal representation
4
- * I. Key identity is determined by the {@link Object.is} comparison of the
5
- * encoded keys.
3
+ * A Map implementation that maps keys of type K to an internal representation I.
6
4
  *
7
- * This is typically useful for values that can be serialized to a unique string
8
- * representation.
5
+ * Key identity is determined by the {@link Object.is} comparison of the
6
+ * encoded keys. This is useful for complex key types that can be serialized
7
+ * to a unique primitive representation (typically strings).
8
+ *
9
+ * @typeParam K - The external key type
10
+ * @typeParam V - The value type stored in the map
11
+ * @typeParam I - The internal encoded key type used for identity comparison
9
12
  */
10
13
  declare class MappedMap<K, V, I = any> implements Map<K, V> {
11
14
  private readonly encodeKey;
12
15
  private readonly decodeKey;
13
16
  private map;
17
+ /**
18
+ * Creates a new MappedMap with custom key encoding/decoding functions.
19
+ *
20
+ * @param encodeKey - Function to convert external keys to internal representation
21
+ * @param decodeKey - Function to convert internal representation back to external keys
22
+ */
14
23
  constructor(encodeKey: (key: K) => I, decodeKey: (enc: I) => K);
15
24
  get size(): number;
16
25
  clear(): void;
@@ -25,7 +34,39 @@ declare class MappedMap<K, V, I = any> implements Map<K, V> {
25
34
  [Symbol.iterator](): IterableIterator<[K, V]>;
26
35
  [Symbol.toStringTag]: string;
27
36
  }
37
+ /**
38
+ * A Map specialized for using NSID (Namespaced Identifier) values as keys.
39
+ *
40
+ * NSIDs are compared by their string representation, allowing different
41
+ * NSID object instances with the same value to be treated as the same key.
42
+ *
43
+ * @typeParam T - The value type stored in the map
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { NsidMap } from '@atproto/lex-installer'
48
+ * import { NSID } from '@atproto/syntax'
49
+ * import { LexiconDocument } from '@atproto/lex-document'
50
+ *
51
+ * const lexicons = new NsidMap<LexiconDocument>()
52
+ *
53
+ * // Store lexicon documents by NSID
54
+ * lexicons.set(NSID.from('app.bsky.feed.post'), postLexicon)
55
+ * lexicons.set(NSID.from('app.bsky.actor.profile'), profileLexicon)
56
+ *
57
+ * // Retrieve by NSID (different object instance works)
58
+ * const doc = lexicons.get(NSID.from('app.bsky.feed.post'))
59
+ *
60
+ * // Iterate over entries
61
+ * for (const [nsid, lexicon] of lexicons) {
62
+ * console.log(`${nsid}: ${lexicon.description}`)
63
+ * }
64
+ * ```
65
+ */
28
66
  export declare class NsidMap<T> extends MappedMap<NSID, T, string> {
67
+ /**
68
+ * Creates a new empty NsidMap.
69
+ */
29
70
  constructor();
30
71
  }
31
72
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"nsid-map.d.ts","sourceRoot":"","sources":["../src/nsid-map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC;;;;;;;GAOG;AACH,cAAM,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,CAAE,YAAW,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAI/C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,GAAG,CAAkB;gBAGV,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EACxB,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;IAG3C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,IAAI;IAIb,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAK3B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAI1B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIvB,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI5B,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAM3B,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAMpC,OAAO,CACL,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,EAC5D,OAAO,CAAC,EAAE,GAAG,GACZ,IAAI;IAMP,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAI7C,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAc;CAC3C;AAED,qBAAa,OAAO,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC;;CAOzD"}
1
+ {"version":3,"file":"nsid-map.d.ts","sourceRoot":"","sources":["../src/nsid-map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC;;;;;;;;;;GAUG;AACH,cAAM,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,CAAE,YAAW,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAU/C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAV5B,OAAO,CAAC,GAAG,CAAkB;IAE7B;;;;;OAKG;gBAEgB,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EACxB,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;IAG3C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,IAAI;IAIb,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAK3B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAI1B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIvB,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI5B,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAM3B,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAMpC,OAAO,CACL,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,EAC5D,OAAO,CAAC,EAAE,GAAG,GACZ,IAAI;IAMP,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAI7C,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAc;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,OAAO,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC;IACxD;;OAEG;;CAOJ"}
package/dist/nsid-map.js CHANGED
@@ -3,17 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NsidMap = void 0;
4
4
  const syntax_1 = require("@atproto/syntax");
5
5
  /**
6
- * A Map implementation that maps keys of type K to an internal representation
7
- * I. Key identity is determined by the {@link Object.is} comparison of the
8
- * encoded keys.
6
+ * A Map implementation that maps keys of type K to an internal representation I.
9
7
  *
10
- * This is typically useful for values that can be serialized to a unique string
11
- * representation.
8
+ * Key identity is determined by the {@link Object.is} comparison of the
9
+ * encoded keys. This is useful for complex key types that can be serialized
10
+ * to a unique primitive representation (typically strings).
11
+ *
12
+ * @typeParam K - The external key type
13
+ * @typeParam V - The value type stored in the map
14
+ * @typeParam I - The internal encoded key type used for identity comparison
12
15
  */
13
16
  class MappedMap {
14
17
  encodeKey;
15
18
  decodeKey;
16
19
  map = new Map();
20
+ /**
21
+ * Creates a new MappedMap with custom key encoding/decoding functions.
22
+ *
23
+ * @param encodeKey - Function to convert external keys to internal representation
24
+ * @param decodeKey - Function to convert internal representation back to external keys
25
+ */
17
26
  constructor(encodeKey, decodeKey) {
18
27
  this.encodeKey = encodeKey;
19
28
  this.decodeKey = decodeKey;
@@ -60,7 +69,39 @@ class MappedMap {
60
69
  }
61
70
  [Symbol.toStringTag] = 'MappedMap';
62
71
  }
72
+ /**
73
+ * A Map specialized for using NSID (Namespaced Identifier) values as keys.
74
+ *
75
+ * NSIDs are compared by their string representation, allowing different
76
+ * NSID object instances with the same value to be treated as the same key.
77
+ *
78
+ * @typeParam T - The value type stored in the map
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { NsidMap } from '@atproto/lex-installer'
83
+ * import { NSID } from '@atproto/syntax'
84
+ * import { LexiconDocument } from '@atproto/lex-document'
85
+ *
86
+ * const lexicons = new NsidMap<LexiconDocument>()
87
+ *
88
+ * // Store lexicon documents by NSID
89
+ * lexicons.set(NSID.from('app.bsky.feed.post'), postLexicon)
90
+ * lexicons.set(NSID.from('app.bsky.actor.profile'), profileLexicon)
91
+ *
92
+ * // Retrieve by NSID (different object instance works)
93
+ * const doc = lexicons.get(NSID.from('app.bsky.feed.post'))
94
+ *
95
+ * // Iterate over entries
96
+ * for (const [nsid, lexicon] of lexicons) {
97
+ * console.log(`${nsid}: ${lexicon.description}`)
98
+ * }
99
+ * ```
100
+ */
63
101
  class NsidMap extends MappedMap {
102
+ /**
103
+ * Creates a new empty NsidMap.
104
+ */
64
105
  constructor() {
65
106
  super((key) => key.toString(), (enc) => syntax_1.NSID.from(enc));
66
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"nsid-map.js","sourceRoot":"","sources":["../src/nsid-map.ts"],"names":[],"mappings":";;;AAAA,4CAAsC;AAEtC;;;;;;;GAOG;AACH,MAAM,SAAS;IAIM;IACA;IAJX,GAAG,GAAG,IAAI,GAAG,EAAQ,CAAA;IAE7B,YACmB,SAAwB,EACxB,SAAwB;QADxB,cAAS,GAAT,SAAS,CAAe;QACxB,cAAS,GAAT,SAAS,CAAe;IACxC,CAAC;IAEJ,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAA;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA;IAClB,CAAC;IAED,GAAG,CAAC,GAAM,EAAE,KAAQ;QAClB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;QACxC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,GAAG,CAAC,GAAM;QACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1C,CAAC;IAED,GAAG,CAAC,GAAM;QACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1C,CAAC;IAED,MAAM,CAAC,GAAM;QACX,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IAC7C,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAA;IAC1B,CAAC;IAED,CAAC,IAAI;QACH,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YAClC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;IAED,CAAC,OAAO;QACN,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;QACpC,CAAC;IACH,CAAC;IAED,OAAO,CACL,UAA4D,EAC5D,OAAa;QAEb,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;YAChC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;QAC5C,CAAC;IACH,CAAC;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC;QACf,OAAO,IAAI,CAAC,OAAO,EAAE,CAAA;IACvB,CAAC;IAED,CAAC,MAAM,CAAC,WAAW,CAAC,GAAW,WAAW,CAAA;CAC3C;AAED,MAAa,OAAW,SAAQ,SAA0B;IACxD;QACE,KAAK,CACH,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,EACvB,CAAC,GAAG,EAAE,EAAE,CAAC,aAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CACxB,CAAA;IACH,CAAC;CACF;AAPD,0BAOC","sourcesContent":["import { NSID } from '@atproto/syntax'\n\n/**\n * A Map implementation that maps keys of type K to an internal representation\n * I. Key identity is determined by the {@link Object.is} comparison of the\n * encoded keys.\n *\n * This is typically useful for values that can be serialized to a unique string\n * representation.\n */\nclass MappedMap<K, V, I = any> implements Map<K, V> {\n private map = new Map<I, V>()\n\n constructor(\n private readonly encodeKey: (key: K) => I,\n private readonly decodeKey: (enc: I) => K,\n ) {}\n\n get size(): number {\n return this.map.size\n }\n\n clear(): void {\n this.map.clear()\n }\n\n set(key: K, value: V): this {\n this.map.set(this.encodeKey(key), value)\n return this\n }\n\n get(key: K): V | undefined {\n return this.map.get(this.encodeKey(key))\n }\n\n has(key: K): boolean {\n return this.map.has(this.encodeKey(key))\n }\n\n delete(key: K): boolean {\n return this.map.delete(this.encodeKey(key))\n }\n\n values(): IterableIterator<V> {\n return this.map.values()\n }\n\n *keys(): IterableIterator<K> {\n for (const key of this.map.keys()) {\n yield this.decodeKey(key)\n }\n }\n\n *entries(): IterableIterator<[K, V]> {\n for (const [key, value] of this.map.entries()) {\n yield [this.decodeKey(key), value]\n }\n }\n\n forEach(\n callbackfn: (value: V, key: K, map: MappedMap<K, V>) => void,\n thisArg?: any,\n ): void {\n for (const [key, value] of this) {\n callbackfn.call(thisArg, value, key, this)\n }\n }\n\n [Symbol.iterator](): IterableIterator<[K, V]> {\n return this.entries()\n }\n\n [Symbol.toStringTag]: string = 'MappedMap'\n}\n\nexport class NsidMap<T> extends MappedMap<NSID, T, string> {\n constructor() {\n super(\n (key) => key.toString(),\n (enc) => NSID.from(enc),\n )\n }\n}\n"]}
1
+ {"version":3,"file":"nsid-map.js","sourceRoot":"","sources":["../src/nsid-map.ts"],"names":[],"mappings":";;;AAAA,4CAAsC;AAEtC;;;;;;;;;;GAUG;AACH,MAAM,SAAS;IAUM;IACA;IAVX,GAAG,GAAG,IAAI,GAAG,EAAQ,CAAA;IAE7B;;;;;OAKG;IACH,YACmB,SAAwB,EACxB,SAAwB;QADxB,cAAS,GAAT,SAAS,CAAe;QACxB,cAAS,GAAT,SAAS,CAAe;IACxC,CAAC;IAEJ,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAA;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA;IAClB,CAAC;IAED,GAAG,CAAC,GAAM,EAAE,KAAQ;QAClB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;QACxC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,GAAG,CAAC,GAAM;QACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1C,CAAC;IAED,GAAG,CAAC,GAAM;QACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1C,CAAC;IAED,MAAM,CAAC,GAAM;QACX,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IAC7C,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAA;IAC1B,CAAC;IAED,CAAC,IAAI;QACH,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YAClC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;IAED,CAAC,OAAO;QACN,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;QACpC,CAAC;IACH,CAAC;IAED,OAAO,CACL,UAA4D,EAC5D,OAAa;QAEb,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;YAChC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;QAC5C,CAAC;IACH,CAAC;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC;QACf,OAAO,IAAI,CAAC,OAAO,EAAE,CAAA;IACvB,CAAC;IAED,CAAC,MAAM,CAAC,WAAW,CAAC,GAAW,WAAW,CAAA;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAa,OAAW,SAAQ,SAA0B;IACxD;;OAEG;IACH;QACE,KAAK,CACH,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,EACvB,CAAC,GAAG,EAAE,EAAE,CAAC,aAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CACxB,CAAA;IACH,CAAC;CACF;AAVD,0BAUC","sourcesContent":["import { NSID } from '@atproto/syntax'\n\n/**\n * A Map implementation that maps keys of type K to an internal representation I.\n *\n * Key identity is determined by the {@link Object.is} comparison of the\n * encoded keys. This is useful for complex key types that can be serialized\n * to a unique primitive representation (typically strings).\n *\n * @typeParam K - The external key type\n * @typeParam V - The value type stored in the map\n * @typeParam I - The internal encoded key type used for identity comparison\n */\nclass MappedMap<K, V, I = any> implements Map<K, V> {\n private map = new Map<I, V>()\n\n /**\n * Creates a new MappedMap with custom key encoding/decoding functions.\n *\n * @param encodeKey - Function to convert external keys to internal representation\n * @param decodeKey - Function to convert internal representation back to external keys\n */\n constructor(\n private readonly encodeKey: (key: K) => I,\n private readonly decodeKey: (enc: I) => K,\n ) {}\n\n get size(): number {\n return this.map.size\n }\n\n clear(): void {\n this.map.clear()\n }\n\n set(key: K, value: V): this {\n this.map.set(this.encodeKey(key), value)\n return this\n }\n\n get(key: K): V | undefined {\n return this.map.get(this.encodeKey(key))\n }\n\n has(key: K): boolean {\n return this.map.has(this.encodeKey(key))\n }\n\n delete(key: K): boolean {\n return this.map.delete(this.encodeKey(key))\n }\n\n values(): IterableIterator<V> {\n return this.map.values()\n }\n\n *keys(): IterableIterator<K> {\n for (const key of this.map.keys()) {\n yield this.decodeKey(key)\n }\n }\n\n *entries(): IterableIterator<[K, V]> {\n for (const [key, value] of this.map.entries()) {\n yield [this.decodeKey(key), value]\n }\n }\n\n forEach(\n callbackfn: (value: V, key: K, map: MappedMap<K, V>) => void,\n thisArg?: any,\n ): void {\n for (const [key, value] of this) {\n callbackfn.call(thisArg, value, key, this)\n }\n }\n\n [Symbol.iterator](): IterableIterator<[K, V]> {\n return this.entries()\n }\n\n [Symbol.toStringTag]: string = 'MappedMap'\n}\n\n/**\n * A Map specialized for using NSID (Namespaced Identifier) values as keys.\n *\n * NSIDs are compared by their string representation, allowing different\n * NSID object instances with the same value to be treated as the same key.\n *\n * @typeParam T - The value type stored in the map\n *\n * @example\n * ```typescript\n * import { NsidMap } from '@atproto/lex-installer'\n * import { NSID } from '@atproto/syntax'\n * import { LexiconDocument } from '@atproto/lex-document'\n *\n * const lexicons = new NsidMap<LexiconDocument>()\n *\n * // Store lexicon documents by NSID\n * lexicons.set(NSID.from('app.bsky.feed.post'), postLexicon)\n * lexicons.set(NSID.from('app.bsky.actor.profile'), profileLexicon)\n *\n * // Retrieve by NSID (different object instance works)\n * const doc = lexicons.get(NSID.from('app.bsky.feed.post'))\n *\n * // Iterate over entries\n * for (const [nsid, lexicon] of lexicons) {\n * console.log(`${nsid}: ${lexicon.description}`)\n * }\n * ```\n */\nexport class NsidMap<T> extends MappedMap<NSID, T, string> {\n /**\n * Creates a new empty NsidMap.\n */\n constructor() {\n super(\n (key) => key.toString(),\n (enc) => NSID.from(enc),\n )\n }\n}\n"]}
@@ -1,16 +1,24 @@
1
1
  import { NSID } from '@atproto/syntax';
2
2
  /**
3
- * A Set implementation that maps values of type K to an internal representation
4
- * I. Value identity is determined by the {@link Object.is} comparison of the
5
- * encoded values.
3
+ * A Set implementation that maps values of type K to an internal representation I.
6
4
  *
7
- * This is typically useful for values that can be serialized to a unique string
8
- * representation.
5
+ * Value identity is determined by the {@link Object.is} comparison of the
6
+ * encoded values. This is useful for complex types that can be serialized
7
+ * to a unique primitive representation (typically strings).
8
+ *
9
+ * @typeParam K - The external value type stored in the set
10
+ * @typeParam I - The internal encoded type used for identity comparison
9
11
  */
10
12
  export declare class MappedSet<K, I = any> implements Set<K> {
11
13
  private readonly encodeValue;
12
14
  private readonly decodeValue;
13
15
  private set;
16
+ /**
17
+ * Creates a new MappedSet with custom encoding/decoding functions.
18
+ *
19
+ * @param encodeValue - Function to convert external values to internal representation
20
+ * @param decodeValue - Function to convert internal representation back to external values
21
+ */
14
22
  constructor(encodeValue: (val: K) => I, decodeValue: (enc: I) => K);
15
23
  get size(): number;
16
24
  clear(): void;
@@ -24,7 +32,35 @@ export declare class MappedSet<K, I = any> implements Set<K> {
24
32
  [Symbol.iterator](): IterableIterator<K>;
25
33
  [Symbol.toStringTag]: string;
26
34
  }
35
+ /**
36
+ * A Set specialized for storing NSID (Namespaced Identifier) values.
37
+ *
38
+ * NSIDs are compared by their string representation, allowing different
39
+ * NSID object instances with the same value to be treated as equal.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * import { NsidSet } from '@atproto/lex-installer'
44
+ * import { NSID } from '@atproto/syntax'
45
+ *
46
+ * const nsidSet = new NsidSet()
47
+ *
48
+ * nsidSet.add(NSID.from('app.bsky.feed.post'))
49
+ * nsidSet.add(NSID.from('app.bsky.actor.profile'))
50
+ *
51
+ * // Check membership
52
+ * nsidSet.has(NSID.from('app.bsky.feed.post')) // true
53
+ *
54
+ * // Iterate over NSIDs
55
+ * for (const nsid of nsidSet) {
56
+ * console.log(nsid.toString())
57
+ * }
58
+ * ```
59
+ */
27
60
  export declare class NsidSet extends MappedSet<NSID, string> {
61
+ /**
62
+ * Creates a new empty NsidSet.
63
+ */
28
64
  constructor();
29
65
  }
30
66
  //# sourceMappingURL=nsid-set.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nsid-set.d.ts","sourceRoot":"","sources":["../src/nsid-set.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC;;;;;;;GAOG;AACH,qBAAa,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAE,YAAW,GAAG,CAAC,CAAC,CAAC;IAIhD,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAJ9B,OAAO,CAAC,GAAG,CAAe;gBAGP,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAC1B,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;IAG7C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,IAAI;IAIb,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IAKjB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAItB,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAM9B,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC;IAIrB,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAIpC,OAAO,CACL,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,EACtD,OAAO,CAAC,EAAE,GAAG,GACZ,IAAI;IAMP,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAIxC,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAc;CAC3C;AAED,qBAAa,OAAQ,SAAQ,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC;;CAOnD"}
1
+ {"version":3,"file":"nsid-set.d.ts","sourceRoot":"","sources":["../src/nsid-set.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC;;;;;;;;;GASG;AACH,qBAAa,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAE,YAAW,GAAG,CAAC,CAAC,CAAC;IAUhD,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAV9B,OAAO,CAAC,GAAG,CAAe;IAE1B;;;;;OAKG;gBAEgB,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAC1B,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;IAG7C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,IAAI;IAIb,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IAKjB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAItB,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAM9B,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC;IAIrB,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAIpC,OAAO,CACL,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,EACtD,OAAO,CAAC,EAAE,GAAG,GACZ,IAAI;IAMP,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAIxC,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAc;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,OAAQ,SAAQ,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC;IAClD;;OAEG;;CAOJ"}