@atproto/lex-installer 0.0.25 → 0.1.0-next.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # @atproto/lex-installer
2
2
 
3
+ ## 0.1.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#4929](https://github.com/bluesky-social/atproto/pull/4929) [`bb7491c`](https://github.com/bluesky-social/atproto/commit/bb7491c29e06181e1d2f8cf6eb454f9bb8ab961b) Thanks [@devinivy](https://github.com/devinivy)! - **BREAKING:** Drop support for Node.js 18 and 20. Node.js 22 is now the minimum supported version. Docker images now use Node.js 24.
8
+
9
+ - [#4943](https://github.com/bluesky-social/atproto/pull/4943) [`07ae5d4`](https://github.com/bluesky-social/atproto/commit/07ae5d4452df51e045e0239da7a04cf0bc154028) Thanks [@devinivy](https://github.com/devinivy)! - **BREAKING:** Convert to pure ESM. All packages now ship `"type": "module"` with ES module output and Node16 module resolution.
10
+
11
+ Node.js 22's `require()` compatibility layer can still load these packages in CommonJS code.
12
+
13
+ - [#4930](https://github.com/bluesky-social/atproto/pull/4930) [`042df15`](https://github.com/bluesky-social/atproto/commit/042df15087c0e62cd1e715fcbf58852fab875af9) Thanks [@devinivy](https://github.com/devinivy)! - Build with TypeScript 6.0. Emitted `.d.ts` files now use TypeScript 6's stricter `Uint8Array<ArrayBuffer>` typing in places where Web/Node APIs require buffer-backed (not shared-memory) byte arrays. Consumers compiling against these types on older TypeScript should see no runtime impact, but may need to widen or cast in spots that previously relied on `Uint8Array` defaulting to `<ArrayBufferLike>`.
14
+
15
+ Internal: tsconfig `moduleResolution: "node"` is silenced via `ignoreDeprecations: "6.0"` for now; the proper migration to `node16`/`bundler` resolution is deferred.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [[`bb7491c`](https://github.com/bluesky-social/atproto/commit/bb7491c29e06181e1d2f8cf6eb454f9bb8ab961b), [`07ae5d4`](https://github.com/bluesky-social/atproto/commit/07ae5d4452df51e045e0239da7a04cf0bc154028), [`042df15`](https://github.com/bluesky-social/atproto/commit/042df15087c0e62cd1e715fcbf58852fab875af9)]:
20
+ - @atproto/lex-builder@0.1.0-next.0
21
+ - @atproto/lex-cbor@0.1.0-next.0
22
+ - @atproto/lex-data@0.1.0-next.0
23
+ - @atproto/lex-document@0.1.0-next.0
24
+ - @atproto/lex-resolver@0.1.0-next.0
25
+ - @atproto/lex-schema@0.1.0-next.0
26
+ - @atproto/syntax@0.6.0-next.0
27
+
28
+ ## 0.0.26
29
+
30
+ ### Patch Changes
31
+
32
+ - Updated dependencies [[`2fd8d62`](https://github.com/bluesky-social/atproto/commit/2fd8d62708dc23de6ed21cbcccfebab68b19f588)]:
33
+ - @atproto/lex-schema@0.0.20
34
+ - @atproto/lex-builder@0.0.23
35
+ - @atproto/lex-document@0.0.21
36
+ - @atproto/lex-resolver@0.0.23
37
+
3
38
  ## 0.0.25
4
39
 
5
40
  ### Patch Changes
package/dist/fs.js CHANGED
@@ -1,10 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readJsonFile = readJsonFile;
4
- exports.writeJsonFile = writeJsonFile;
5
- exports.isEnoentError = isEnoentError;
6
- const promises_1 = require("node:fs/promises");
7
- const node_path_1 = require("node:path");
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
8
3
  /**
9
4
  * Reads and parses a JSON file from the filesystem.
10
5
  *
@@ -36,8 +31,8 @@ const node_path_1 = require("node:path");
36
31
  * }
37
32
  * ```
38
33
  */
39
- async function readJsonFile(path) {
40
- const contents = await (0, promises_1.readFile)(path, 'utf8');
34
+ export async function readJsonFile(path) {
35
+ const contents = await readFile(path, 'utf8');
41
36
  return JSON.parse(contents);
42
37
  }
43
38
  /**
@@ -71,10 +66,10 @@ async function readJsonFile(path) {
71
66
  * await writeJsonFile('./lexicons/app/bsky/feed/post.json', lexiconDocument)
72
67
  * ```
73
68
  */
74
- async function writeJsonFile(path, data) {
75
- await (0, promises_1.mkdir)((0, node_path_1.dirname)(path), { recursive: true });
69
+ export async function writeJsonFile(path, data) {
70
+ await mkdir(dirname(path), { recursive: true });
76
71
  const contents = JSON.stringify(data, null, 2);
77
- await (0, promises_1.writeFile)(path, contents, {
72
+ await writeFile(path, contents, {
78
73
  encoding: 'utf8',
79
74
  mode: 0o644,
80
75
  flag: 'w', // override
@@ -116,7 +111,7 @@ async function writeJsonFile(path, data) {
116
111
  * }
117
112
  * ```
118
113
  */
119
- function isEnoentError(err) {
114
+ export function isEnoentError(err) {
120
115
  return err instanceof Error && 'code' in err && err.code === 'ENOENT';
121
116
  }
122
117
  //# sourceMappingURL=fs.js.map
package/dist/fs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":";;AAkCA,oCAGC;AAiCD,sCAWC;AAsCD,sCAEC;AAzHD,+CAA6D;AAC7D,yCAAmC;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACI,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAQ,EAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACI,KAAK,UAAU,aAAa,CACjC,IAAY,EACZ,IAAa;IAEb,MAAM,IAAA,gBAAK,EAAC,IAAA,mBAAO,EAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAC9C,MAAM,IAAA,oBAAS,EAAC,IAAI,EAAE,QAAQ,EAAE;QAC9B,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,GAAG,EAAE,WAAW;KACvB,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,SAAgB,aAAa,CAAC,GAAY;IACxC,OAAO,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAA;AACvE,CAAC","sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\n/**\n * Reads and parses a JSON file from the filesystem.\n *\n * @param path - Absolute or relative path to the JSON file\n * @returns The parsed JSON content\n * @throws {Error} When the file cannot be read (e.g., ENOENT, EACCES)\n * @throws {SyntaxError} When the file contains invalid JSON\n *\n * @example\n * ```typescript\n * import { readJsonFile } from '@atproto/lex-installer'\n *\n * const manifest = await readJsonFile('./lexicons.manifest.json')\n * ```\n *\n * @example\n * Handle missing file:\n * ```typescript\n * import { readJsonFile, isEnoentError } from '@atproto/lex-installer'\n *\n * try {\n * const data = await readJsonFile('./config.json')\n * } catch (err) {\n * if (isEnoentError(err)) {\n * console.log('File does not exist, using defaults')\n * } else {\n * throw err\n * }\n * }\n * ```\n */\nexport async function readJsonFile(path: string): Promise<unknown> {\n const contents = await readFile(path, 'utf8')\n return JSON.parse(contents)\n}\n\n/**\n * Writes data as formatted JSON to a file.\n *\n * The function:\n * - Creates parent directories if they don't exist\n * - Formats JSON with 2-space indentation\n * - Overwrites existing files\n * - Sets file permissions to 0o644 (rw-r--r--)\n *\n * @param path - Absolute or relative path for the output file\n * @param data - Data to serialize as JSON\n * @throws {Error} When the file cannot be written\n *\n * @example\n * ```typescript\n * import { writeJsonFile } from '@atproto/lex-installer'\n *\n * await writeJsonFile('./output/data.json', {\n * name: 'example',\n * values: [1, 2, 3],\n * })\n * ```\n *\n * @example\n * Write a lexicon document:\n * ```typescript\n * import { writeJsonFile } from '@atproto/lex-installer'\n *\n * await writeJsonFile('./lexicons/app/bsky/feed/post.json', lexiconDocument)\n * ```\n */\nexport async function writeJsonFile(\n path: string,\n data: unknown,\n): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n const contents = JSON.stringify(data, null, 2)\n await writeFile(path, contents, {\n encoding: 'utf8',\n mode: 0o644,\n flag: 'w', // override\n })\n}\n\n/**\n * Checks if an error is an ENOENT (file not found) error.\n *\n * Useful for handling cases where a file may or may not exist,\n * such as reading an optional configuration file.\n *\n * @param err - The error to check\n * @returns `true` if the error is an ENOENT error, `false` otherwise\n *\n * @example\n * ```typescript\n * import { readFile } from 'node:fs/promises'\n * import { isEnoentError } from '@atproto/lex-installer'\n *\n * const config = await readFile('./config.json').catch((err) => {\n * if (isEnoentError(err)) {\n * return { defaults: true }\n * }\n * throw err\n * })\n * ```\n *\n * @example\n * In try/catch:\n * ```typescript\n * try {\n * const manifest = await readFile('./lexicons.manifest.json', 'utf8')\n * } catch (err) {\n * if (isEnoentError(err)) {\n * // File doesn't exist, create a new manifest\n * return { version: 1, lexicons: [], resolutions: {} }\n * }\n * throw err\n * }\n * ```\n */\nexport function isEnoentError(err: unknown): boolean {\n return err instanceof Error && 'code' in err && err.code === 'ENOENT'\n}\n"]}
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAY,EACZ,IAAa;IAEb,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAC9C,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE;QAC9B,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,GAAG,EAAE,WAAW;KACvB,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,OAAO,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAA;AACvE,CAAC","sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\n/**\n * Reads and parses a JSON file from the filesystem.\n *\n * @param path - Absolute or relative path to the JSON file\n * @returns The parsed JSON content\n * @throws {Error} When the file cannot be read (e.g., ENOENT, EACCES)\n * @throws {SyntaxError} When the file contains invalid JSON\n *\n * @example\n * ```typescript\n * import { readJsonFile } from '@atproto/lex-installer'\n *\n * const manifest = await readJsonFile('./lexicons.manifest.json')\n * ```\n *\n * @example\n * Handle missing file:\n * ```typescript\n * import { readJsonFile, isEnoentError } from '@atproto/lex-installer'\n *\n * try {\n * const data = await readJsonFile('./config.json')\n * } catch (err) {\n * if (isEnoentError(err)) {\n * console.log('File does not exist, using defaults')\n * } else {\n * throw err\n * }\n * }\n * ```\n */\nexport async function readJsonFile(path: string): Promise<unknown> {\n const contents = await readFile(path, 'utf8')\n return JSON.parse(contents)\n}\n\n/**\n * Writes data as formatted JSON to a file.\n *\n * The function:\n * - Creates parent directories if they don't exist\n * - Formats JSON with 2-space indentation\n * - Overwrites existing files\n * - Sets file permissions to 0o644 (rw-r--r--)\n *\n * @param path - Absolute or relative path for the output file\n * @param data - Data to serialize as JSON\n * @throws {Error} When the file cannot be written\n *\n * @example\n * ```typescript\n * import { writeJsonFile } from '@atproto/lex-installer'\n *\n * await writeJsonFile('./output/data.json', {\n * name: 'example',\n * values: [1, 2, 3],\n * })\n * ```\n *\n * @example\n * Write a lexicon document:\n * ```typescript\n * import { writeJsonFile } from '@atproto/lex-installer'\n *\n * await writeJsonFile('./lexicons/app/bsky/feed/post.json', lexiconDocument)\n * ```\n */\nexport async function writeJsonFile(\n path: string,\n data: unknown,\n): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n const contents = JSON.stringify(data, null, 2)\n await writeFile(path, contents, {\n encoding: 'utf8',\n mode: 0o644,\n flag: 'w', // override\n })\n}\n\n/**\n * Checks if an error is an ENOENT (file not found) error.\n *\n * Useful for handling cases where a file may or may not exist,\n * such as reading an optional configuration file.\n *\n * @param err - The error to check\n * @returns `true` if the error is an ENOENT error, `false` otherwise\n *\n * @example\n * ```typescript\n * import { readFile } from 'node:fs/promises'\n * import { isEnoentError } from '@atproto/lex-installer'\n *\n * const config = await readFile('./config.json').catch((err) => {\n * if (isEnoentError(err)) {\n * return { defaults: true }\n * }\n * throw err\n * })\n * ```\n *\n * @example\n * In try/catch:\n * ```typescript\n * try {\n * const manifest = await readFile('./lexicons.manifest.json', 'utf8')\n * } catch (err) {\n * if (isEnoentError(err)) {\n * // File doesn't exist, create a new manifest\n * return { version: 1, lexicons: [], resolutions: {} }\n * }\n * throw err\n * }\n * ```\n */\nexport function isEnoentError(err: unknown): boolean {\n return err instanceof Error && 'code' in err && err.code === 'ENOENT'\n}\n"]}
package/dist/index.js CHANGED
@@ -1,10 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.install = install;
4
- const tslib_1 = require("tslib");
5
- const fs_js_1 = require("./fs.js");
6
- const lex_installer_js_1 = require("./lex-installer.js");
7
- const lexicons_manifest_js_1 = require("./lexicons-manifest.js");
1
+ import { __addDisposableResource, __disposeResources } from "tslib";
2
+ import { isEnoentError, readJsonFile } from './fs.js';
3
+ import { LexInstaller } from './lex-installer.js';
4
+ import { lexiconsManifestSchema, } from './lexicons-manifest.js';
8
5
  /**
9
6
  * Installs lexicons from the network based on the provided options.
10
7
  *
@@ -55,17 +52,17 @@ const lexicons_manifest_js_1 = require("./lexicons-manifest.js");
55
52
  * })
56
53
  * ```
57
54
  */
58
- async function install(options) {
55
+ export async function install(options) {
59
56
  const env_1 = { stack: [], error: void 0, hasError: false };
60
57
  try {
61
- const manifest = await (0, fs_js_1.readJsonFile)(options.manifest).then((json) => lexicons_manifest_js_1.lexiconsManifestSchema.parse(json), (cause) => {
62
- if ((0, fs_js_1.isEnoentError)(cause))
58
+ const manifest = await readJsonFile(options.manifest).then((json) => lexiconsManifestSchema.parse(json), (cause) => {
59
+ if (isEnoentError(cause))
63
60
  return undefined;
64
61
  throw new Error('Failed to read lexicons manifest', { cause });
65
62
  });
66
63
  const additions = new Set(options.add);
67
64
  // Perform the installation using the existing manifest as "hint"
68
- const installer = tslib_1.__addDisposableResource(env_1, new lex_installer_js_1.LexInstaller(options), true);
65
+ const installer = __addDisposableResource(env_1, new LexInstaller(options), true);
69
66
  await installer.install({ additions, manifest });
70
67
  // Verify lockfile
71
68
  if (options.ci && (!manifest || !installer.equals(manifest))) {
@@ -81,7 +78,7 @@ async function install(options) {
81
78
  env_1.hasError = true;
82
79
  }
83
80
  finally {
84
- const result_1 = tslib_1.__disposeResources(env_1);
81
+ const result_1 = __disposeResources(env_1);
85
82
  if (result_1)
86
83
  await result_1;
87
84
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAkGA,0BA2BC;;AA7HD,mCAAqD;AACrD,yDAAsE;AACtE,iEAG+B;AA2C/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACI,KAAK,UAAU,OAAO,CAAC,OAA0B;;;QACtD,MAAM,QAAQ,GAAiC,MAAM,IAAA,oBAAY,EAC/D,OAAO,CAAC,QAAQ,CACjB,CAAC,IAAI,CACJ,CAAC,IAAI,EAAE,EAAE,CAAC,6CAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,EAC5C,CAAC,KAAc,EAAE,EAAE;YACjB,IAAI,IAAA,qBAAa,EAAC,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAA;YAC1C,MAAM,IAAI,KAAK,CAAC,kCAAkC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;QAChE,CAAC,CACF,CAAA;QAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAEtC,iEAAiE;QACjE,MAAY,SAAS,0CAAG,IAAI,+BAAY,CAAC,OAAO,CAAC,OAAA,CAAA;QAEjD,MAAM,SAAS,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;QAEhD,kBAAkB;QAClB,IAAI,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;YAC7D,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;QACrD,CAAC;QAED,4BAA4B;QAC5B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,SAAS,CAAC,IAAI,EAAE,CAAA;QACxB,CAAC;;;;;;;;;;;CACF","sourcesContent":["import { isEnoentError, readJsonFile } from './fs.js'\nimport { LexInstaller, LexInstallerOptions } from './lex-installer.js'\nimport {\n LexiconsManifest,\n lexiconsManifestSchema,\n} from './lexicons-manifest.js'\n\n/**\n * Options for the {@link install} function.\n *\n * Extends {@link LexInstallerOptions} with additional options for controlling\n * the installation behavior.\n *\n * @example\n * ```typescript\n * const options: LexInstallOptions = {\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * add: ['com.example.myLexicon', 'at://did:plc:xyz/com.example.otherLexicon'],\n * save: true,\n * ci: false,\n * }\n * ```\n */\nexport type LexInstallOptions = LexInstallerOptions & {\n /**\n * Array of lexicons to add to the installation. Can be NSID strings\n * (e.g., 'com.example.myLexicon') or AT URIs\n * (e.g., 'at://did:plc:xyz/com.example.myLexicon').\n */\n add?: string[]\n\n /**\n * Whether to save the updated manifest after installation.\n * When `true`, the manifest file will be written with any new lexicons.\n * @default false\n */\n save?: boolean\n\n /**\n * Enable CI mode for strict manifest verification.\n * When `true`, throws an error if the manifest is out of date,\n * useful for continuous integration pipelines.\n * @default false\n */\n ci?: boolean\n}\n\n/**\n * Installs lexicons from the network based on the provided options.\n *\n * This is the main entry point for programmatic lexicon installation.\n * It reads an existing manifest (if present), installs any new lexicons,\n * and optionally saves the updated manifest.\n *\n * @param options - Configuration options for the installation\n * @throws {Error} When the manifest file cannot be read (unless it doesn't exist)\n * @throws {Error} When in CI mode and the manifest is out of date\n *\n * @example\n * Install lexicons and save the manifest:\n * ```typescript\n * import { install } from '@atproto/lex-installer'\n *\n * await install({\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * add: ['app.bsky.feed.post', 'app.bsky.actor.profile'],\n * save: true,\n * })\n * ```\n *\n * @example\n * Verify manifest in CI pipeline:\n * ```typescript\n * import { install } from '@atproto/lex-installer'\n *\n * // Throws if manifest is out of date\n * await install({\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * ci: true,\n * })\n * ```\n *\n * @example\n * Install from specific AT URIs:\n * ```typescript\n * import { install } from '@atproto/lex-installer'\n *\n * await install({\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * add: ['at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post'],\n * save: true,\n * })\n * ```\n */\nexport async function install(options: LexInstallOptions) {\n const manifest: LexiconsManifest | undefined = await readJsonFile(\n options.manifest,\n ).then(\n (json) => lexiconsManifestSchema.parse(json),\n (cause: unknown) => {\n if (isEnoentError(cause)) return undefined\n throw new Error('Failed to read lexicons manifest', { cause })\n },\n )\n\n const additions = new Set(options.add)\n\n // Perform the installation using the existing manifest as \"hint\"\n await using installer = new LexInstaller(options)\n\n await installer.install({ additions, manifest })\n\n // Verify lockfile\n if (options.ci && (!manifest || !installer.equals(manifest))) {\n throw new Error('Lexicons manifest is out of date')\n }\n\n // Save changes if requested\n if (options.save) {\n await installer.save()\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACrD,OAAO,EAAE,YAAY,EAAuB,MAAM,oBAAoB,CAAA;AACtE,OAAO,EAEL,sBAAsB,GACvB,MAAM,wBAAwB,CAAA;AA2C/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAA0B;;;QACtD,MAAM,QAAQ,GAAiC,MAAM,YAAY,CAC/D,OAAO,CAAC,QAAQ,CACjB,CAAC,IAAI,CACJ,CAAC,IAAI,EAAE,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,EAC5C,CAAC,KAAc,EAAE,EAAE;YACjB,IAAI,aAAa,CAAC,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAA;YAC1C,MAAM,IAAI,KAAK,CAAC,kCAAkC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;QAChE,CAAC,CACF,CAAA;QAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAEtC,iEAAiE;QACjE,MAAY,SAAS,kCAAG,IAAI,YAAY,CAAC,OAAO,CAAC,OAAA,CAAA;QAEjD,MAAM,SAAS,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;QAEhD,kBAAkB;QAClB,IAAI,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;YAC7D,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;QACrD,CAAC;QAED,4BAA4B;QAC5B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,SAAS,CAAC,IAAI,EAAE,CAAA;QACxB,CAAC;;;;;;;;;;;CACF","sourcesContent":["import { isEnoentError, readJsonFile } from './fs.js'\nimport { LexInstaller, LexInstallerOptions } from './lex-installer.js'\nimport {\n LexiconsManifest,\n lexiconsManifestSchema,\n} from './lexicons-manifest.js'\n\n/**\n * Options for the {@link install} function.\n *\n * Extends {@link LexInstallerOptions} with additional options for controlling\n * the installation behavior.\n *\n * @example\n * ```typescript\n * const options: LexInstallOptions = {\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * add: ['com.example.myLexicon', 'at://did:plc:xyz/com.example.otherLexicon'],\n * save: true,\n * ci: false,\n * }\n * ```\n */\nexport type LexInstallOptions = LexInstallerOptions & {\n /**\n * Array of lexicons to add to the installation. Can be NSID strings\n * (e.g., 'com.example.myLexicon') or AT URIs\n * (e.g., 'at://did:plc:xyz/com.example.myLexicon').\n */\n add?: string[]\n\n /**\n * Whether to save the updated manifest after installation.\n * When `true`, the manifest file will be written with any new lexicons.\n * @default false\n */\n save?: boolean\n\n /**\n * Enable CI mode for strict manifest verification.\n * When `true`, throws an error if the manifest is out of date,\n * useful for continuous integration pipelines.\n * @default false\n */\n ci?: boolean\n}\n\n/**\n * Installs lexicons from the network based on the provided options.\n *\n * This is the main entry point for programmatic lexicon installation.\n * It reads an existing manifest (if present), installs any new lexicons,\n * and optionally saves the updated manifest.\n *\n * @param options - Configuration options for the installation\n * @throws {Error} When the manifest file cannot be read (unless it doesn't exist)\n * @throws {Error} When in CI mode and the manifest is out of date\n *\n * @example\n * Install lexicons and save the manifest:\n * ```typescript\n * import { install } from '@atproto/lex-installer'\n *\n * await install({\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * add: ['app.bsky.feed.post', 'app.bsky.actor.profile'],\n * save: true,\n * })\n * ```\n *\n * @example\n * Verify manifest in CI pipeline:\n * ```typescript\n * import { install } from '@atproto/lex-installer'\n *\n * // Throws if manifest is out of date\n * await install({\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * ci: true,\n * })\n * ```\n *\n * @example\n * Install from specific AT URIs:\n * ```typescript\n * import { install } from '@atproto/lex-installer'\n *\n * await install({\n * lexicons: './lexicons',\n * manifest: './lexicons.manifest.json',\n * add: ['at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post'],\n * save: true,\n * })\n * ```\n */\nexport async function install(options: LexInstallOptions) {\n const manifest: LexiconsManifest | undefined = await readJsonFile(\n options.manifest,\n ).then(\n (json) => lexiconsManifestSchema.parse(json),\n (cause: unknown) => {\n if (isEnoentError(cause)) return undefined\n throw new Error('Failed to read lexicons manifest', { cause })\n },\n )\n\n const additions = new Set(options.add)\n\n // Perform the installation using the existing manifest as \"hint\"\n await using installer = new LexInstaller(options)\n\n await installer.install({ additions, manifest })\n\n // Verify lockfile\n if (options.ci && (!manifest || !installer.equals(manifest))) {\n throw new Error('Lexicons manifest is out of date')\n }\n\n // Save changes if requested\n if (options.save) {\n await installer.save()\n }\n}\n"]}
@@ -1,16 +1,13 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LexInstaller = void 0;
4
- const node_path_1 = require("node:path");
5
- const lex_builder_1 = require("@atproto/lex-builder");
6
- const lex_cbor_1 = require("@atproto/lex-cbor");
7
- const lex_data_1 = require("@atproto/lex-data");
8
- const lex_resolver_1 = require("@atproto/lex-resolver");
9
- const syntax_1 = require("@atproto/syntax");
10
- const fs_js_1 = require("./fs.js");
11
- const lexicons_manifest_js_1 = require("./lexicons-manifest.js");
12
- const nsid_map_js_1 = require("./nsid-map.js");
13
- const nsid_set_js_1 = require("./nsid-set.js");
1
+ import { join } from 'node:path';
2
+ import { LexiconDirectoryIndexer } from '@atproto/lex-builder';
3
+ import { cidForLex } from '@atproto/lex-cbor';
4
+ import { lexEquals } from '@atproto/lex-data';
5
+ import { LexResolver } from '@atproto/lex-resolver';
6
+ import { AtUri, NSID } from '@atproto/syntax';
7
+ import { isEnoentError, writeJsonFile } from './fs.js';
8
+ import { normalizeLexiconsManifest, } from './lexicons-manifest.js';
9
+ import { NsidMap } from './nsid-map.js';
10
+ import { NsidSet } from './nsid-set.js';
14
11
  /**
15
12
  * Manages the installation of Lexicon schemas from the AT Protocol network.
16
13
  *
@@ -53,20 +50,17 @@ const nsid_set_js_1 = require("./nsid-set.js");
53
50
  * }
54
51
  * ```
55
52
  */
56
- class LexInstaller {
57
- options;
58
- lexiconResolver;
59
- indexer;
60
- documents = new nsid_map_js_1.NsidMap();
61
- manifest = {
62
- version: 1,
63
- lexicons: [],
64
- resolutions: {},
65
- };
53
+ export class LexInstaller {
66
54
  constructor(options) {
67
55
  this.options = options;
68
- this.lexiconResolver = new lex_resolver_1.LexResolver(options);
69
- this.indexer = new lex_builder_1.LexiconDirectoryIndexer({
56
+ this.documents = new NsidMap();
57
+ this.manifest = {
58
+ version: 1,
59
+ lexicons: [],
60
+ resolutions: {},
61
+ };
62
+ this.lexiconResolver = new LexResolver(options);
63
+ this.indexer = new LexiconDirectoryIndexer({
70
64
  lexicons: options.lexicons,
71
65
  });
72
66
  }
@@ -83,7 +77,7 @@ class LexInstaller {
83
77
  * @returns `true` if the manifests are equivalent, `false` otherwise
84
78
  */
85
79
  equals(manifest) {
86
- return (0, lex_data_1.lexEquals)((0, lexicons_manifest_js_1.normalizeLexiconsManifest)(manifest), (0, lexicons_manifest_js_1.normalizeLexiconsManifest)(this.manifest));
80
+ return lexEquals(normalizeLexiconsManifest(manifest), normalizeLexiconsManifest(this.manifest));
87
81
  }
88
82
  /**
89
83
  * Installs lexicons and their dependencies.
@@ -127,12 +121,12 @@ class LexInstaller {
127
121
  * ```
128
122
  */
129
123
  async install({ additions, manifest, } = {}) {
130
- const roots = new nsid_map_js_1.NsidMap();
124
+ const roots = new NsidMap();
131
125
  // First, process explicit additions
132
126
  for (const lexicon of new Set(additions)) {
133
127
  const [nsid, uri] = lexicon.startsWith('at://')
134
- ? ((uri) => [syntax_1.NSID.from(uri.rkey), uri])(new syntax_1.AtUri(lexicon))
135
- : [syntax_1.NSID.from(lexicon), null];
128
+ ? ((uri) => [NSID.from(uri.rkey), uri])(new AtUri(lexicon))
129
+ : [NSID.from(lexicon), null];
136
130
  if (roots.has(nsid)) {
137
131
  throw new Error(`Duplicate lexicon addition: ${nsid} (${roots.get(nsid) ?? lexicon})`);
138
132
  }
@@ -142,11 +136,11 @@ class LexInstaller {
142
136
  // Next, restore previously existing manifest entries
143
137
  if (manifest) {
144
138
  for (const lexicon of manifest.lexicons) {
145
- const nsid = syntax_1.NSID.from(lexicon);
139
+ const nsid = NSID.from(lexicon);
146
140
  // Skip entries already added explicitly
147
141
  if (!roots.has(nsid)) {
148
142
  const uri = manifest.resolutions[lexicon]
149
- ? new syntax_1.AtUri(manifest.resolutions[lexicon].uri)
143
+ ? new AtUri(manifest.resolutions[lexicon].uri)
150
144
  : null;
151
145
  roots.set(nsid, uri);
152
146
  console.debug(`Adding lexicon from manifest: ${nsid} (${uri ?? 'from NSID'})`);
@@ -169,7 +163,7 @@ class LexInstaller {
169
163
  console.debug(`Resolving dependency lexicon: ${nsid}`);
170
164
  const nsidStr = nsid.toString();
171
165
  const resolvedUri = manifest?.resolutions[nsidStr]?.uri
172
- ? new syntax_1.AtUri(manifest.resolutions[nsidStr].uri)
166
+ ? new AtUri(manifest.resolutions[nsidStr].uri)
173
167
  : null;
174
168
  if (resolvedUri) {
175
169
  await this.installFromUri(resolvedUri);
@@ -181,7 +175,7 @@ class LexInstaller {
181
175
  } while (results.length > 0);
182
176
  }
183
177
  getMissingIds() {
184
- const missing = new nsid_set_js_1.NsidSet();
178
+ const missing = new NsidSet();
185
179
  for (const document of this.documents.values()) {
186
180
  for (const nsid of listDocumentNsidRefs(document)) {
187
181
  if (!this.documents.has(nsid)) {
@@ -200,14 +194,14 @@ class LexInstaller {
200
194
  ? await this.fetch(uri)
201
195
  : await this.indexer.get(uri.rkey).then(async (lexicon) => {
202
196
  console.debug(`Re-using existing lexicon ${uri.rkey} from indexer`);
203
- const cid = await (0, lex_cbor_1.cidForLex)(lexicon);
197
+ const cid = await cidForLex(lexicon);
204
198
  return { cid, lexicon };
205
199
  }, (err) => {
206
- if ((0, fs_js_1.isEnoentError)(err))
200
+ if (isEnoentError(err))
207
201
  return this.fetch(uri);
208
202
  throw err;
209
203
  });
210
- this.documents.set(syntax_1.NSID.from(lexicon.id), lexicon);
204
+ this.documents.set(NSID.from(lexicon.id), lexicon);
211
205
  this.manifest.resolutions[lexicon.id] = {
212
206
  cid: cid.toString(),
213
207
  uri: uri.toString(),
@@ -228,8 +222,8 @@ class LexInstaller {
228
222
  const { lexicon, cid } = await this.lexiconResolver.fetch(uri, {
229
223
  noCache: this.options.update,
230
224
  });
231
- const basePath = (0, node_path_1.join)(this.options.lexicons, ...lexicon.id.split('.'));
232
- await (0, fs_js_1.writeJsonFile)(`${basePath}.json`, lexicon);
225
+ const basePath = join(this.options.lexicons, ...lexicon.id.split('.'));
226
+ await writeJsonFile(`${basePath}.json`, lexicon);
233
227
  return { lexicon, cid };
234
228
  }
235
229
  /**
@@ -239,10 +233,9 @@ class LexInstaller {
239
233
  * of entries, making it suitable for version control.
240
234
  */
241
235
  async save() {
242
- await (0, fs_js_1.writeJsonFile)(this.options.manifest, (0, lexicons_manifest_js_1.normalizeLexiconsManifest)(this.manifest));
236
+ await writeJsonFile(this.options.manifest, normalizeLexiconsManifest(this.manifest));
243
237
  }
244
238
  }
245
- exports.LexInstaller = LexInstaller;
246
239
  function* listDocumentNsidRefs(doc) {
247
240
  try {
248
241
  for (const def of Object.values(doc.defs)) {
@@ -250,7 +243,7 @@ function* listDocumentNsidRefs(doc) {
250
243
  for (const ref of defRefs(def)) {
251
244
  const [nsid] = ref.split('#', 1);
252
245
  if (nsid)
253
- yield syntax_1.NSID.from(nsid);
246
+ yield NSID.from(nsid);
254
247
  }
255
248
  }
256
249
  }
@@ -268,7 +261,7 @@ function* defRefs(def) {
268
261
  const { length, 0: nsid, 1: hash } = val.split('#');
269
262
  if (length === 2 && hash) {
270
263
  try {
271
- syntax_1.NSID.from(nsid);
264
+ NSID.from(nsid);
272
265
  yield val;
273
266
  }
274
267
  catch {
@@ -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\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
+ {"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,25 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.lexiconsManifestSchema = void 0;
4
- exports.normalizeLexiconsManifest = normalizeLexiconsManifest;
5
- const lex_schema_1 = require("@atproto/lex-schema");
1
+ import { l } from '@atproto/lex-schema';
6
2
  /**
7
3
  * Schema for validating and parsing lexicons manifest files.
8
4
  *
9
5
  * The manifest tracks which lexicons are installed and how they were resolved.
10
6
  * This schema ensures the manifest file conforms to the expected structure.
11
7
  */
12
- exports.lexiconsManifestSchema = lex_schema_1.l.object({
8
+ export const lexiconsManifestSchema = l.object({
13
9
  /** Schema version, currently always 1 */
14
- version: lex_schema_1.l.literal(1),
10
+ version: l.literal(1),
15
11
  /** Array of NSID strings for directly requested lexicons */
16
- lexicons: lex_schema_1.l.array(lex_schema_1.l.string({ format: 'nsid' })),
12
+ lexicons: l.array(l.string({ format: 'nsid' })),
17
13
  /** Map of NSID to resolution info (AT URI and CID) for all installed lexicons */
18
- resolutions: lex_schema_1.l.dict(lex_schema_1.l.string({ format: 'nsid' }), lex_schema_1.l.object({
14
+ resolutions: l.dict(l.string({ format: 'nsid' }), l.object({
19
15
  /** AT URI where the lexicon was fetched from */
20
- uri: lex_schema_1.l.string({ format: 'at-uri' }),
16
+ uri: l.string({ format: 'at-uri' }),
21
17
  /** Content identifier (CID) of the lexicon document */
22
- cid: lex_schema_1.l.string({ format: 'cid' }),
18
+ cid: l.string({ format: 'cid' }),
23
19
  })),
24
20
  });
25
21
  /**
@@ -36,7 +32,7 @@ exports.lexiconsManifestSchema = lex_schema_1.l.object({
36
32
  * @param manifest - The manifest to normalize
37
33
  * @returns A new normalized manifest object
38
34
  */
39
- function normalizeLexiconsManifest(manifest) {
35
+ export function normalizeLexiconsManifest(manifest) {
40
36
  const normalized = {
41
37
  version: manifest.version,
42
38
  lexicons: [...manifest.lexicons].sort(),
@@ -45,7 +41,7 @@ function normalizeLexiconsManifest(manifest) {
45
41
  .map(([k, { uri, cid }]) => [k, { uri, cid }])),
46
42
  };
47
43
  // For good measure:
48
- return exports.lexiconsManifestSchema.parse(normalized);
44
+ return lexiconsManifestSchema.parse(normalized);
49
45
  }
50
46
  function compareObjectEntriesFn(a, b) {
51
47
  return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0;
@@ -1 +1 @@
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
+ {"version":3,"file":"lexicons-manifest.js","sourceRoot":"","sources":["../src/lexicons-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,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,MAAM,UAAU,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,sBAAsB,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"]}
package/dist/nsid-map.js CHANGED
@@ -1,7 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NsidMap = void 0;
4
- const syntax_1 = require("@atproto/syntax");
1
+ var _a;
2
+ import { NSID } from '@atproto/syntax';
5
3
  /**
6
4
  * A Map implementation that maps keys of type K to an internal representation I.
7
5
  *
@@ -14,9 +12,7 @@ const syntax_1 = require("@atproto/syntax");
14
12
  * @typeParam I - The internal encoded key type used for identity comparison
15
13
  */
16
14
  class MappedMap {
17
- encodeKey;
18
- decodeKey;
19
- map = new Map();
15
+ static { _a = Symbol.toStringTag; }
20
16
  /**
21
17
  * Creates a new MappedMap with custom key encoding/decoding functions.
22
18
  *
@@ -26,6 +22,8 @@ class MappedMap {
26
22
  constructor(encodeKey, decodeKey) {
27
23
  this.encodeKey = encodeKey;
28
24
  this.decodeKey = decodeKey;
25
+ this.map = new Map();
26
+ this[_a] = 'MappedMap';
29
27
  }
30
28
  get size() {
31
29
  return this.map.size;
@@ -67,7 +65,6 @@ class MappedMap {
67
65
  [Symbol.iterator]() {
68
66
  return this.entries();
69
67
  }
70
- [Symbol.toStringTag] = 'MappedMap';
71
68
  }
72
69
  /**
73
70
  * A Map specialized for using NSID (Namespaced Identifier) values as keys.
@@ -98,13 +95,12 @@ class MappedMap {
98
95
  * }
99
96
  * ```
100
97
  */
101
- class NsidMap extends MappedMap {
98
+ export class NsidMap extends MappedMap {
102
99
  /**
103
100
  * Creates a new empty NsidMap.
104
101
  */
105
102
  constructor() {
106
- super((key) => key.toString(), (enc) => syntax_1.NSID.from(enc));
103
+ super((key) => key.toString(), (enc) => NSID.from(enc));
107
104
  }
108
105
  }
109
- exports.NsidMap = NsidMap;
110
106
  //# sourceMappingURL=nsid-map.js.map
@@ -1 +1 @@
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
+ {"version":3,"file":"nsid-map.js","sourceRoot":"","sources":["../src/nsid-map.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC;;;;;;;;;;GAUG;AACH,MAAM,SAAS;kBAoEZ,MAAM,CAAC,WAAW;IAjEnB;;;;;OAKG;IACH,YACmB,SAAwB,EACxB,SAAwB;QADxB,cAAS,GAAT,SAAS,CAAe;QACxB,cAAS,GAAT,SAAS,CAAe;QAVnC,QAAG,GAAG,IAAI,GAAG,EAAQ,CAAA;QAmE7B,QAAoB,GAAW,WAAW,CAAA;IAxDvC,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;CAGF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,OAAO,OAAW,SAAQ,SAA0B;IACxD;;OAEG;IACH;QACE,KAAK,CACH,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,EACvB,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CACxB,CAAA;IACH,CAAC;CACF","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"]}
package/dist/nsid-set.js CHANGED
@@ -1,7 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NsidSet = exports.MappedSet = void 0;
4
- const syntax_1 = require("@atproto/syntax");
1
+ var _a;
2
+ import { NSID } from '@atproto/syntax';
5
3
  /**
6
4
  * A Set implementation that maps values of type K to an internal representation I.
7
5
  *
@@ -12,10 +10,8 @@ const syntax_1 = require("@atproto/syntax");
12
10
  * @typeParam K - The external value type stored in the set
13
11
  * @typeParam I - The internal encoded type used for identity comparison
14
12
  */
15
- class MappedSet {
16
- encodeValue;
17
- decodeValue;
18
- set = new Set();
13
+ export class MappedSet {
14
+ static { _a = Symbol.toStringTag; }
19
15
  /**
20
16
  * Creates a new MappedSet with custom encoding/decoding functions.
21
17
  *
@@ -25,6 +21,8 @@ class MappedSet {
25
21
  constructor(encodeValue, decodeValue) {
26
22
  this.encodeValue = encodeValue;
27
23
  this.decodeValue = decodeValue;
24
+ this.set = new Set();
25
+ this[_a] = 'MappedSet';
28
26
  }
29
27
  get size() {
30
28
  return this.set.size;
@@ -62,9 +60,7 @@ class MappedSet {
62
60
  [Symbol.iterator]() {
63
61
  return this.values();
64
62
  }
65
- [Symbol.toStringTag] = 'MappedSet';
66
63
  }
67
- exports.MappedSet = MappedSet;
68
64
  /**
69
65
  * A Set specialized for storing NSID (Namespaced Identifier) values.
70
66
  *
@@ -90,13 +86,12 @@ exports.MappedSet = MappedSet;
90
86
  * }
91
87
  * ```
92
88
  */
93
- class NsidSet extends MappedSet {
89
+ export class NsidSet extends MappedSet {
94
90
  /**
95
91
  * Creates a new empty NsidSet.
96
92
  */
97
93
  constructor() {
98
- super((val) => val.toString(), (enc) => syntax_1.NSID.from(enc));
94
+ super((val) => val.toString(), (enc) => NSID.from(enc));
99
95
  }
100
96
  }
101
- exports.NsidSet = NsidSet;
102
97
  //# sourceMappingURL=nsid-set.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nsid-set.js","sourceRoot":"","sources":["../src/nsid-set.ts"],"names":[],"mappings":";;;AAAA,4CAAsC;AAEtC;;;;;;;;;GASG;AACH,MAAa,SAAS;IAUD;IACA;IAVX,GAAG,GAAG,IAAI,GAAG,EAAK,CAAA;IAE1B;;;;;OAKG;IACH,YACmB,WAA0B,EAC1B,WAA0B;QAD1B,gBAAW,GAAX,WAAW,CAAe;QAC1B,gBAAW,GAAX,WAAW,CAAe;IAC1C,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;QACR,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;QACnC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,GAAG,CAAC,GAAM;QACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,CAAC,GAAM;QACX,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,CAAC,MAAM;QACL,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;IAED,CAAC,OAAO;QACN,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IAC1C,CAAC;IAED,OAAO,CACL,UAAsD,EACtD,OAAa;QAEb,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;QAC1C,CAAC;IACH,CAAC;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC;QACf,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;IAED,CAAC,MAAM,CAAC,WAAW,CAAC,GAAW,WAAW,CAAA;CAC3C;AA/DD,8BA+DC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAa,OAAQ,SAAQ,SAAuB;IAClD;;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 Set implementation that maps values of type K to an internal representation I.\n *\n * Value identity is determined by the {@link Object.is} comparison of the\n * encoded values. This is useful for complex types that can be serialized\n * to a unique primitive representation (typically strings).\n *\n * @typeParam K - The external value type stored in the set\n * @typeParam I - The internal encoded type used for identity comparison\n */\nexport class MappedSet<K, I = any> implements Set<K> {\n private set = new Set<I>()\n\n /**\n * Creates a new MappedSet with custom encoding/decoding functions.\n *\n * @param encodeValue - Function to convert external values to internal representation\n * @param decodeValue - Function to convert internal representation back to external values\n */\n constructor(\n private readonly encodeValue: (val: K) => I,\n private readonly decodeValue: (enc: I) => K,\n ) {}\n\n get size(): number {\n return this.set.size\n }\n\n clear(): void {\n this.set.clear()\n }\n\n add(val: K): this {\n this.set.add(this.encodeValue(val))\n return this\n }\n\n has(val: K): boolean {\n return this.set.has(this.encodeValue(val))\n }\n\n delete(val: K): boolean {\n return this.set.delete(this.encodeValue(val))\n }\n\n *values(): IterableIterator<K> {\n for (const val of this.set.values()) {\n yield this.decodeValue(val)\n }\n }\n\n keys(): SetIterator<K> {\n return this.values()\n }\n\n *entries(): IterableIterator<[K, K]> {\n for (const val of this) yield [val, val]\n }\n\n forEach(\n callbackfn: (value: K, value2: K, set: Set<K>) => void,\n thisArg?: any,\n ): void {\n for (const val of this) {\n callbackfn.call(thisArg, val, val, this)\n }\n }\n\n [Symbol.iterator](): IterableIterator<K> {\n return this.values()\n }\n\n [Symbol.toStringTag]: string = 'MappedSet'\n}\n\n/**\n * A Set specialized for storing NSID (Namespaced Identifier) values.\n *\n * NSIDs are compared by their string representation, allowing different\n * NSID object instances with the same value to be treated as equal.\n *\n * @example\n * ```typescript\n * import { NsidSet } from '@atproto/lex-installer'\n * import { NSID } from '@atproto/syntax'\n *\n * const nsidSet = new NsidSet()\n *\n * nsidSet.add(NSID.from('app.bsky.feed.post'))\n * nsidSet.add(NSID.from('app.bsky.actor.profile'))\n *\n * // Check membership\n * nsidSet.has(NSID.from('app.bsky.feed.post')) // true\n *\n * // Iterate over NSIDs\n * for (const nsid of nsidSet) {\n * console.log(nsid.toString())\n * }\n * ```\n */\nexport class NsidSet extends MappedSet<NSID, string> {\n /**\n * Creates a new empty NsidSet.\n */\n constructor() {\n super(\n (val) => val.toString(),\n (enc) => NSID.from(enc),\n )\n }\n}\n"]}
1
+ {"version":3,"file":"nsid-set.js","sourceRoot":"","sources":["../src/nsid-set.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC;;;;;;;;;GASG;AACH,MAAM,OAAO,SAAS;kBA8DnB,MAAM,CAAC,WAAW;IA3DnB;;;;;OAKG;IACH,YACmB,WAA0B,EAC1B,WAA0B;QAD1B,gBAAW,GAAX,WAAW,CAAe;QAC1B,gBAAW,GAAX,WAAW,CAAe;QAVrC,QAAG,GAAG,IAAI,GAAG,EAAK,CAAA;QA6D1B,QAAoB,GAAW,WAAW,CAAA;IAlDvC,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;QACR,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;QACnC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,GAAG,CAAC,GAAM;QACR,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,CAAC,GAAM;QACX,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,CAAC,MAAM;QACL,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;IAED,CAAC,OAAO;QACN,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IAC1C,CAAC;IAED,OAAO,CACL,UAAsD,EACtD,OAAa;QAEb,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;QAC1C,CAAC;IACH,CAAC;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC;QACf,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;CAGF;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,OAAQ,SAAQ,SAAuB;IAClD;;OAEG;IACH;QACE,KAAK,CACH,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,EACvB,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CACxB,CAAA;IACH,CAAC;CACF","sourcesContent":["import { NSID } from '@atproto/syntax'\n\n/**\n * A Set implementation that maps values of type K to an internal representation I.\n *\n * Value identity is determined by the {@link Object.is} comparison of the\n * encoded values. This is useful for complex types that can be serialized\n * to a unique primitive representation (typically strings).\n *\n * @typeParam K - The external value type stored in the set\n * @typeParam I - The internal encoded type used for identity comparison\n */\nexport class MappedSet<K, I = any> implements Set<K> {\n private set = new Set<I>()\n\n /**\n * Creates a new MappedSet with custom encoding/decoding functions.\n *\n * @param encodeValue - Function to convert external values to internal representation\n * @param decodeValue - Function to convert internal representation back to external values\n */\n constructor(\n private readonly encodeValue: (val: K) => I,\n private readonly decodeValue: (enc: I) => K,\n ) {}\n\n get size(): number {\n return this.set.size\n }\n\n clear(): void {\n this.set.clear()\n }\n\n add(val: K): this {\n this.set.add(this.encodeValue(val))\n return this\n }\n\n has(val: K): boolean {\n return this.set.has(this.encodeValue(val))\n }\n\n delete(val: K): boolean {\n return this.set.delete(this.encodeValue(val))\n }\n\n *values(): IterableIterator<K> {\n for (const val of this.set.values()) {\n yield this.decodeValue(val)\n }\n }\n\n keys(): SetIterator<K> {\n return this.values()\n }\n\n *entries(): IterableIterator<[K, K]> {\n for (const val of this) yield [val, val]\n }\n\n forEach(\n callbackfn: (value: K, value2: K, set: Set<K>) => void,\n thisArg?: any,\n ): void {\n for (const val of this) {\n callbackfn.call(thisArg, val, val, this)\n }\n }\n\n [Symbol.iterator](): IterableIterator<K> {\n return this.values()\n }\n\n [Symbol.toStringTag]: string = 'MappedSet'\n}\n\n/**\n * A Set specialized for storing NSID (Namespaced Identifier) values.\n *\n * NSIDs are compared by their string representation, allowing different\n * NSID object instances with the same value to be treated as equal.\n *\n * @example\n * ```typescript\n * import { NsidSet } from '@atproto/lex-installer'\n * import { NSID } from '@atproto/syntax'\n *\n * const nsidSet = new NsidSet()\n *\n * nsidSet.add(NSID.from('app.bsky.feed.post'))\n * nsidSet.add(NSID.from('app.bsky.actor.profile'))\n *\n * // Check membership\n * nsidSet.has(NSID.from('app.bsky.feed.post')) // true\n *\n * // Iterate over NSIDs\n * for (const nsid of nsidSet) {\n * console.log(nsid.toString())\n * }\n * ```\n */\nexport class NsidSet extends MappedSet<NSID, string> {\n /**\n * Creates a new empty NsidSet.\n */\n constructor() {\n super(\n (val) => val.toString(),\n (enc) => NSID.from(enc),\n )\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "@atproto/lex-installer",
3
- "version": "0.0.25",
3
+ "version": "0.1.0-next.0",
4
+ "engines": {
5
+ "node": ">=22"
6
+ },
4
7
  "license": "MIT",
5
8
  "description": "Lexicon document packet manager for AT Lexicons",
6
9
  "keywords": [
@@ -24,26 +27,22 @@
24
27
  "./CHANGELOG.md"
25
28
  ],
26
29
  "sideEffects": false,
27
- "type": "commonjs",
28
- "main": "./dist/index.js",
29
- "types": "./dist/index.d.ts",
30
+ "type": "module",
30
31
  "exports": {
31
32
  ".": {
32
33
  "types": "./dist/index.d.ts",
33
- "browser": "./dist/index.js",
34
- "import": "./dist/index.js",
35
34
  "default": "./dist/index.js"
36
35
  }
37
36
  },
38
37
  "dependencies": {
39
38
  "tslib": "^2.8.1",
40
- "@atproto/lex-builder": "^0.0.22",
41
- "@atproto/lex-cbor": "^0.0.16",
42
- "@atproto/lex-data": "^0.0.15",
43
- "@atproto/lex-document": "^0.0.20",
44
- "@atproto/lex-resolver": "^0.0.22",
45
- "@atproto/lex-schema": "^0.0.19",
46
- "@atproto/syntax": "^0.5.4"
39
+ "@atproto/lex-builder": "^0.1.0-next.0",
40
+ "@atproto/lex-cbor": "^0.1.0-next.0",
41
+ "@atproto/lex-data": "^0.1.0-next.0",
42
+ "@atproto/lex-document": "^0.1.0-next.0",
43
+ "@atproto/lex-schema": "^0.1.0-next.0",
44
+ "@atproto/lex-resolver": "^0.1.0-next.0",
45
+ "@atproto/syntax": "^0.6.0-next.0"
47
46
  },
48
47
  "devDependencies": {
49
48
  "vitest": "^4.0.16"