@atproto/lex-resolver 0.1.3 → 0.1.4

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,25 @@
1
1
  # @atproto/lex-resolver
2
2
 
3
+ ## 0.1.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#5099](https://github.com/bluesky-social/atproto/pull/5099) [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update TypeScript build to rely on references to composite internal projects
8
+
9
+ - [#5099](https://github.com/bluesky-social/atproto/pull/5099) [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Build using `verbatimModuleSyntax`, reducing runtime import graph
10
+
11
+ - [#5099](https://github.com/bluesky-social/atproto/pull/5099) [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Bundle only necessary files in the NPM tarball, including the `CHANGELOG.md` and `README.md` files (if present).
12
+
13
+ - Updated dependencies [[`28a0b58`](https://github.com/bluesky-social/atproto/commit/28a0b588147863eaef948cd2bb8fc0f19d08cda9), [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07), [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07), [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07), [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07)]:
14
+ - @atproto/syntax@0.6.4
15
+ - @atproto-labs/did-resolver@0.3.4
16
+ - @atproto/lex-document@0.1.3
17
+ - @atproto/lex-client@0.2.1
18
+ - @atproto/lex-schema@0.1.6
19
+ - @atproto/lex-data@0.1.4
20
+ - @atproto/crypto@0.5.3
21
+ - @atproto/repo@0.10.3
22
+
3
23
  ## 0.1.3
4
24
 
5
25
  ### Patch Changes
@@ -1,7 +1,7 @@
1
- import { Cid } from '@atproto/lex-data';
2
- import { LexiconDocument } from '@atproto/lex-document';
1
+ import type { Cid } from '@atproto/lex-data';
2
+ import type { LexiconDocument } from '@atproto/lex-document';
3
3
  import { AtUri, NSID } from '@atproto/syntax';
4
- import { CreateDidResolverOptions, Did, DidResolver, ResolveDidOptions } from '@atproto-labs/did-resolver';
4
+ import type { CreateDidResolverOptions, Did, DidResolver, ResolveDidOptions } from '@atproto-labs/did-resolver';
5
5
  /**
6
6
  * Result returned when successfully resolving a lexicon document.
7
7
  *
@@ -1 +1 @@
1
- {"version":3,"file":"lex-resolver.d.ts","sourceRoot":"","sources":["../src/lex-resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lex-resolver.d.ts","sourceRoot":"","sources":["../src/lex-resolver.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"lex-resolver.js","sourceRoot":"","sources":["../src/lex-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveTxt } from 'node:dns/promises'\nimport * as crypto from '@atproto/crypto'\nimport { buildAgent, xrpc } from '@atproto/lex-client'\nimport { Cid } from '@atproto/lex-data'\nimport { LexiconDocument, lexiconDocumentSchema } from '@atproto/lex-document'\nimport {\n MST,\n MemoryBlockstore,\n def as repoDef,\n readCarWithRoot,\n verifyCommitSig,\n} from '@atproto/repo'\nimport { AtUri, NSID, NsidString } from '@atproto/syntax'\nimport {\n AtprotoVerificationMethod,\n CreateDidResolverOptions,\n Did,\n DidResolver,\n ResolveDidOptions,\n assertDid,\n createDidResolver,\n extractAtprotoData,\n} from '@atproto-labs/did-resolver'\nimport { LexResolverError } from './lex-resolver-error.js'\nimport { com } from './lexicons/index.js'\n\n/**\n * Result returned when successfully resolving a lexicon document.\n *\n * Contains the full AT URI where the lexicon was found, the content-addressed\n * identifier (CID) for integrity verification, and the parsed lexicon document.\n */\nexport type LexResolverResult = {\n /** The AT URI where the lexicon document was found */\n uri: AtUri\n /** Content identifier (CID) of the lexicon record for integrity verification */\n cid: Cid\n /** The parsed and validated lexicon document */\n lexicon: LexiconDocument\n}\n\n/**\n * Result returned when fetching a lexicon document from a specific URI.\n *\n * This is a subset of {@link LexResolverResult} used internally and by hooks,\n * containing only the CID and lexicon document (without the URI, which is\n * already known from the fetch request).\n */\nexport type LexResolverFetchResult = {\n /** Content identifier (CID) of the lexicon record */\n cid: Cid\n /** The parsed and validated lexicon document */\n lexicon: LexiconDocument\n}\n\nexport type Awaitable<T> = T | Promise<T>\n\n/**\n * Callback hooks for customizing the lexicon resolution process.\n *\n * Hooks allow you to intercept, cache, or override the default resolution\n * behavior at various stages. Each hook can be synchronous or asynchronous.\n *\n * @example Implementing a cache with hooks\n * ```typescript\n * import { LexResolver, LexResolverHooks, LexResolverFetchResult } from '@atproto/lex-resolver'\n * import { AtUri } from '@atproto/syntax'\n *\n * const cache = new Map<string, LexResolverFetchResult>()\n *\n * const hooks: LexResolverHooks = {\n * // Return cached result if available, bypassing network fetch\n * onFetch({ uri }) {\n * return cache.get(uri.toString())\n * },\n * // Cache successful fetches\n * onFetchResult({ uri, cid, lexicon }) {\n * cache.set(uri.toString(), { cid, lexicon })\n * },\n * // Log errors for monitoring\n * onFetchError({ uri, err }) {\n * console.error(`Failed to fetch ${uri}:`, err)\n * }\n * }\n *\n * const resolver = new LexResolver({ hooks })\n * ```\n *\n * @example Overriding authority resolution for testing\n * ```typescript\n * const hooks: LexResolverHooks = {\n * // Always resolve to a test DID\n * onResolveAuthority({ nsid }) {\n * if (nsid.authority === 'test.example') {\n * return 'did:plc:test123'\n * }\n * // Return undefined to use default resolution\n * }\n * }\n * ```\n */\nexport type LexResolverHooks = {\n /**\n * Hook called before resolving a lexicon authority DID. If a DID is returned,\n * it will be used instead of performing the default resolution. In that case,\n * the `onResolveAuthorityResult` and `onResolveAuthorityError` hooks will\n * not be called.\n *\n * @param data - Object containing the NSID being resolved\n * @returns A DID to use instead of default resolution, or void/undefined to proceed normally\n */\n onResolveAuthority?(data: { nsid: NSID }): Awaitable<void | Did>\n\n /**\n * Hook called after successfully resolving a lexicon authority DID.\n *\n * @param data - Object containing the NSID and resolved DID\n */\n onResolveAuthorityResult?(data: { nsid: NSID; did: Did }): Awaitable<void>\n\n /**\n * Hook called when authority resolution fails.\n *\n * @param data - Object containing the NSID and error that occurred\n */\n onResolveAuthorityError?(data: { nsid: NSID; err: unknown }): Awaitable<void>\n\n /**\n * Hook called before fetching a lexicon URI. If a result is returned, it will\n * be used instead of performing the default fetch. In that case, the\n * `onFetchResult` and `onFetchError` hooks will not be called.\n *\n * @param data - Object containing the URI being fetched\n * @returns A fetch result to use instead of default fetch, or void/undefined to proceed normally\n */\n onFetch?(data: { uri: AtUri }): Awaitable<void | LexResolverFetchResult>\n\n /**\n * Hook called after successfully fetching a lexicon document.\n *\n * @param data - Object containing the URI, CID, and parsed lexicon document\n */\n onFetchResult?(data: {\n uri: AtUri\n cid: Cid\n lexicon: LexiconDocument\n }): Awaitable<void>\n\n /**\n * Hook called when fetching fails.\n *\n * @param data - Object containing the URI and error that occurred\n */\n onFetchError?(data: { uri: AtUri; err: unknown }): Awaitable<void>\n}\n\n/**\n * Configuration options for the {@link LexResolver}.\n *\n * Extends DID resolver options with lexicon-specific hooks for customizing\n * the resolution process.\n *\n * @see {@link CreateDidResolverOptions} for DID resolver configuration\n */\nexport type LexResolverOptions = CreateDidResolverOptions & {\n /**\n * Optional hooks for customizing the resolution process.\n * See {@link LexResolverHooks} for available callbacks.\n */\n hooks?: LexResolverHooks\n}\n\nexport { AtUri, type Cid, NSID }\nexport type { LexiconDocument, ResolveDidOptions }\n\n/**\n * Resolves Lexicon documents from the AT Protocol network.\n *\n * The {@link LexResolver} handles the complete process of resolving a lexicon\n * by NSID:\n * 1. **Authority Resolution**: Looks up the `_lexicon.<authority>` DNS TXT record\n * to find the DID that controls lexicons for that namespace\n * 2. **DID Resolution**: Resolves the DID document to find the PDS endpoint and\n * signing key\n * 3. **Record Fetch**: Fetches the lexicon record from the PDS with cryptographic\n * proof verification\n * 4. **Validation**: Validates the lexicon document structure\n *\n * @example Basic usage - resolve a lexicon by NSID\n * ```typescript\n * import { LexResolver } from '@atproto/lex-resolver'\n *\n * const resolver = new LexResolver({})\n *\n * // Get a lexicon document by its NSID\n * const result = await resolver.get('app.bsky.feed.post')\n * console.log(result.lexicon) // The parsed lexicon document\n * console.log(result.uri) // AT URI where it was found\n * console.log(result.cid) // Content identifier for verification\n * ```\n *\n * @example Two-step resolution for more control\n * ```typescript\n * import { LexResolver } from '@atproto/lex-resolver'\n *\n * const resolver = new LexResolver({})\n *\n * // Step 1: Resolve the authority to get the AT URI\n * const uri = await resolver.resolve('app.bsky.feed.post')\n * console.log(uri.toString()) // 'at://did:plc:xxx/com.atproto.lexicon.schema/app.bsky.feed.post'\n *\n * // Step 2: Fetch the lexicon from the URI\n * const result = await resolver.fetch(uri)\n * console.log(result.lexicon)\n * ```\n *\n * @example Using hooks for caching\n * ```typescript\n * import { LexResolver, LexResolverFetchResult } from '@atproto/lex-resolver'\n *\n * const cache = new Map<string, LexResolverFetchResult>()\n *\n * const resolver = new LexResolver({\n * hooks: {\n * onFetch({ uri }) {\n * return cache.get(uri.toString())\n * },\n * onFetchResult({ uri, cid, lexicon }) {\n * cache.set(uri.toString(), { cid, lexicon })\n * }\n * }\n * })\n * ```\n *\n * @example Error handling\n * ```typescript\n * import { LexResolver, LexResolverError } from '@atproto/lex-resolver'\n *\n * const resolver = new LexResolver({})\n *\n * try {\n * const result = await resolver.get('com.example.unknown')\n * } catch (error) {\n * if (error instanceof LexResolverError) {\n * console.error(`Failed to resolve ${error.nsid}: ${error.description}`)\n * }\n * }\n * ```\n */\nexport class LexResolver {\n protected readonly didResolver: DidResolver<'plc' | 'web'>\n\n constructor(protected readonly options: LexResolverOptions) {\n this.didResolver = createDidResolver(options)\n }\n\n /**\n * Gets a lexicon document by its NSID.\n *\n * This is the primary method for resolving lexicons. It combines\n * {@link resolve} and {@link fetch} into a single operation, handling\n * authority resolution, DID lookup, and record fetching.\n *\n * @param nsidStr - The NSID to resolve, either as a string or NSID object\n * @param options - Optional DID resolution options (e.g., signal for cancellation)\n * @returns The resolved lexicon result containing URI, CID, and lexicon document\n * @throws {LexResolverError} If resolution fails at any stage\n *\n * @example\n * ```typescript\n * // Resolve using string NSID\n * const result = await resolver.get('app.bsky.feed.post')\n *\n * // Resolve using NSID object\n * import { NSID } from '@atproto/syntax'\n * const nsid = NSID.from('app.bsky.feed.post')\n * const result = await resolver.get(nsid)\n *\n * // With abort signal for cancellation\n * const controller = new AbortController()\n * const result = await resolver.get('app.bsky.feed.post', {\n * signal: controller.signal\n * })\n * ```\n */\n async get(\n nsidStr: NSID | string,\n options?: ResolveDidOptions,\n ): Promise<LexResolverResult> {\n const uri = await this.resolve(nsidStr)\n return this.fetch(uri, options)\n }\n\n /**\n * Resolves the authority for an NSID and returns the AT URI for the lexicon.\n *\n * This method performs the first stage of lexicon resolution:\n * 1. Parses the NSID to extract the authority domain\n * 2. Looks up the `_lexicon.<authority>` DNS TXT record\n * 3. Extracts the DID from the TXT record (format: `did=<did>`)\n * 4. Constructs the AT URI for the lexicon record\n *\n * Use this when you need the URI without fetching the actual document,\n * or when you want to implement custom fetching logic.\n *\n * @param nsidStr - The NSID to resolve, either as a string or NSID object\n * @returns The AT URI pointing to the lexicon record\n * @throws {LexResolverError} If authority resolution fails (e.g., DNS lookup fails)\n *\n * @example\n * ```typescript\n * // Resolve to get the AT URI\n * const uri = await resolver.resolve('app.bsky.feed.post')\n * console.log(uri.toString())\n * // Output: 'at://did:plc:z72i7hdynmk6r22z27h6tvur/com.atproto.lexicon.schema/app.bsky.feed.post'\n *\n * // The URI can then be used with fetch() or stored for later use\n * const result = await resolver.fetch(uri)\n * ```\n */\n async resolve(nsidStr: NSID | string): Promise<AtUri> {\n const nsid = NSID.from(nsidStr)\n\n const did =\n (await this.options.hooks?.onResolveAuthority?.({ nsid })) ??\n (await this.resolveLexiconAuthority(nsid).then(\n async (did) => {\n await this.options.hooks?.onResolveAuthorityResult?.({ nsid, did })\n return did\n },\n async (err) => {\n await this.options.hooks?.onResolveAuthorityError?.({ nsid, err })\n throw err\n },\n ))\n\n return AtUri.make(did, 'com.atproto.lexicon.schema', nsid.toString())\n }\n\n // @TODO This class could be made compatible with browsers by making the\n // following method abstract and/or by allowing the caller to inject a DNS\n // resolver implementation (based on DNS-over-HTTPS or similar), instead of\n // using the Node.js built-in resolver.\n protected async resolveLexiconAuthority(nsid: NSID): Promise<Did> {\n try {\n return await getDomainTxtDid(`_lexicon.${nsid.authority}`)\n } catch (cause) {\n throw new LexResolverError(\n nsid,\n `Failed to resolve lexicon DID authority for ${nsid}`,\n { cause },\n )\n }\n }\n\n /**\n * Fetches a lexicon document from a specific AT URI.\n *\n * This method performs the second stage of lexicon resolution:\n * 1. Resolves the DID from the URI to find the PDS endpoint\n * 2. Fetches the record from the PDS using `com.atproto.sync.getRecord`\n * 3. Verifies the cryptographic proof (commit signature)\n * 4. Validates the lexicon document structure\n * 5. Ensures the document ID matches the URI rkey\n *\n * Use this when you already have an AT URI (e.g., from {@link resolve})\n * and want to fetch the lexicon document.\n *\n * @param uriStr - The AT URI to fetch, either as a string or AtUri object\n * @param options - Optional DID resolution options (e.g., signal for cancellation, noCache)\n * @returns The resolved lexicon result containing URI, CID, and lexicon document\n * @throws {LexResolverError} If fetching or validation fails\n *\n * @example\n * ```typescript\n * // Fetch from a known URI\n * const result = await resolver.fetch(\n * 'at://did:plc:xyz/com.atproto.lexicon.schema/app.bsky.feed.post'\n * )\n *\n * // Fetch with no-cache to bypass any upstream caching\n * const result = await resolver.fetch(uri, { noCache: true })\n *\n * // Fetch with abort signal\n * const controller = new AbortController()\n * const result = await resolver.fetch(uri, { signal: controller.signal })\n * ```\n */\n async fetch(\n uriStr: AtUri | string,\n options?: ResolveDidOptions,\n ): Promise<LexResolverResult> {\n const uri = typeof uriStr === 'string' ? new AtUri(uriStr) : uriStr\n\n const { lexicon, cid } =\n (await this.options.hooks?.onFetch?.({ uri })) ??\n (await this.fetchLexiconUri(uri, options).then(\n async (res) => {\n await this.options.hooks?.onFetchResult?.({ uri, ...res })\n return res\n },\n async (err) => {\n await this.options.hooks?.onFetchError?.({ uri, err })\n throw err\n },\n ))\n\n return { uri, cid, lexicon }\n }\n\n protected async fetchLexiconUri(\n uri: AtUri,\n options?: ResolveDidOptions,\n ): Promise<LexResolverFetchResult> {\n const { did, nsid } = parseLexiconUri(uri)\n\n const { pds, key } = await this.didResolver\n .resolve(did, options)\n .then(extractAtprotoData)\n .catch((cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to resolve DID document for ${did}`,\n { cause },\n )\n })\n\n if (!key || !pds || !URL.canParse(pds.serviceEndpoint)) {\n throw new LexResolverError(\n nsid,\n `No atproto PDS service endpoint or signing key found in ${did} DID document`,\n )\n }\n\n const agent = buildAgent({\n service: pds.serviceEndpoint,\n fetch: this.options.fetch,\n })\n\n const collection = 'com.atproto.lexicon.schema'\n const rkey = nsid.toString()\n\n const { cid, record } = await xrpc(agent, com.atproto.sync.getRecord, {\n signal: options?.signal,\n headers: options?.noCache ? { 'Cache-Control': 'no-cache' } : undefined,\n params: { did, collection, rkey },\n }).then(\n ({ body }) => {\n return verifyRecordProof(body, did, key, collection, rkey).catch(\n (cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to verify Lexicon record proof at ${uri}`,\n { cause },\n )\n },\n )\n },\n (cause) => {\n throw new LexResolverError(nsid, `Failed to fetch Record ${uri}`, {\n cause,\n })\n },\n )\n\n const validationResult = lexiconDocumentSchema.safeParse(record)\n if (!validationResult.success) {\n throw new LexResolverError(nsid, `Invalid Lexicon document at ${uri}`, {\n cause: validationResult.reason,\n })\n }\n\n const lexicon = validationResult.value\n if (lexicon.id !== uri.rkey) {\n throw new LexResolverError(\n nsid,\n `Invalid document id \"${lexicon.id}\" at ${uri}`,\n )\n }\n\n return { lexicon, cid }\n }\n}\n\nfunction parseLexiconUri(uri: AtUri): {\n did: Did\n nsid: NSID\n} {\n // Validate input URI\n const nsid = NSID.from(uri.rkey)\n try {\n const did = uri.host\n assertDid(did)\n return { did, nsid }\n } catch (cause) {\n throw new LexResolverError(nsid, `URI host is not a DID ${uri}`, { cause })\n }\n}\n\nasync function getDomainTxtDid(domain: string): Promise<Did> {\n const didLines = (await resolveTxt(domain))\n .map((chunks) => chunks.join(''))\n .filter((i) => i.startsWith('did='))\n\n if (didLines.length === 1) {\n const did = didLines[0].slice(4)\n assertDid(did)\n return did\n }\n\n throw didLines.length > 1\n ? new Error('Multiple DIDs found in DNS TXT records')\n : new Error('No DID found in DNS TXT records')\n}\n\nasync function verifyRecordProof(\n car: Uint8Array,\n did: Did,\n key: AtprotoVerificationMethod,\n collection: NsidString,\n rkey: string,\n) {\n const { root, blocks } = await readCarWithRoot(car)\n const blockstore = new MemoryBlockstore(blocks)\n\n const commit = await blockstore.readObj(root, repoDef.commit)\n if (commit.did !== did) {\n throw new Error(`Invalid repo did: ${commit.did}`)\n }\n\n const signingKey = getDidKeyFromMultibase(key)\n const validSig = await verifyCommitSig(commit, signingKey)\n if (!validSig) {\n throw new Error(`Invalid signature on commit: ${root.toString()}`)\n }\n\n const mst = MST.load(blockstore, commit.data)\n\n const cid = await mst.get(`${collection}/${rkey}`)\n if (!cid) throw new Error('Record not found in proof')\n\n const record = await blockstore.readRecord(cid)\n if (record?.$type !== collection) {\n throw new Error(\n `Invalid record type: expected ${collection}, got ${record?.$type}`,\n )\n }\n\n return { cid, record }\n}\n\nfunction getDidKeyFromMultibase(key: AtprotoVerificationMethod) {\n switch (key.type) {\n case 'EcdsaSecp256r1VerificationKey2019': {\n const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)\n return crypto.formatDidKey(crypto.P256_JWT_ALG, keyBytes)\n }\n case 'EcdsaSecp256k1VerificationKey2019': {\n const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)\n return crypto.formatDidKey(crypto.SECP256K1_JWT_ALG, keyBytes)\n }\n case 'Multikey': {\n const { jwtAlg, keyBytes } = crypto.parseMultikey(key.publicKeyMultibase)\n return crypto.formatDidKey(jwtAlg, keyBytes)\n }\n default: {\n // Should never happen\n throw new Error(`Unsupported verification method type: ${key.type}`)\n }\n }\n}\n"]}
1
+ {"version":3,"file":"lex-resolver.js","sourceRoot":"","sources":["../src/lex-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveTxt } from 'node:dns/promises'\nimport * as crypto from '@atproto/crypto'\nimport { buildAgent, xrpc } from '@atproto/lex-client'\nimport type { Cid } from '@atproto/lex-data'\nimport type { LexiconDocument } from '@atproto/lex-document'\nimport { lexiconDocumentSchema } from '@atproto/lex-document'\nimport {\n MST,\n MemoryBlockstore,\n def as repoDef,\n readCarWithRoot,\n verifyCommitSig,\n} from '@atproto/repo'\nimport type { NsidString } from '@atproto/syntax'\nimport { AtUri, NSID } from '@atproto/syntax'\nimport type {\n AtprotoVerificationMethod,\n CreateDidResolverOptions,\n Did,\n DidResolver,\n ResolveDidOptions,\n} from '@atproto-labs/did-resolver'\nimport {\n assertDid,\n createDidResolver,\n extractAtprotoData,\n} from '@atproto-labs/did-resolver'\nimport { LexResolverError } from './lex-resolver-error.js'\nimport { com } from './lexicons/index.js'\n\n/**\n * Result returned when successfully resolving a lexicon document.\n *\n * Contains the full AT URI where the lexicon was found, the content-addressed\n * identifier (CID) for integrity verification, and the parsed lexicon document.\n */\nexport type LexResolverResult = {\n /** The AT URI where the lexicon document was found */\n uri: AtUri\n /** Content identifier (CID) of the lexicon record for integrity verification */\n cid: Cid\n /** The parsed and validated lexicon document */\n lexicon: LexiconDocument\n}\n\n/**\n * Result returned when fetching a lexicon document from a specific URI.\n *\n * This is a subset of {@link LexResolverResult} used internally and by hooks,\n * containing only the CID and lexicon document (without the URI, which is\n * already known from the fetch request).\n */\nexport type LexResolverFetchResult = {\n /** Content identifier (CID) of the lexicon record */\n cid: Cid\n /** The parsed and validated lexicon document */\n lexicon: LexiconDocument\n}\n\nexport type Awaitable<T> = T | Promise<T>\n\n/**\n * Callback hooks for customizing the lexicon resolution process.\n *\n * Hooks allow you to intercept, cache, or override the default resolution\n * behavior at various stages. Each hook can be synchronous or asynchronous.\n *\n * @example Implementing a cache with hooks\n * ```typescript\n * import { LexResolver, LexResolverHooks, LexResolverFetchResult } from '@atproto/lex-resolver'\n * import { AtUri } from '@atproto/syntax'\n *\n * const cache = new Map<string, LexResolverFetchResult>()\n *\n * const hooks: LexResolverHooks = {\n * // Return cached result if available, bypassing network fetch\n * onFetch({ uri }) {\n * return cache.get(uri.toString())\n * },\n * // Cache successful fetches\n * onFetchResult({ uri, cid, lexicon }) {\n * cache.set(uri.toString(), { cid, lexicon })\n * },\n * // Log errors for monitoring\n * onFetchError({ uri, err }) {\n * console.error(`Failed to fetch ${uri}:`, err)\n * }\n * }\n *\n * const resolver = new LexResolver({ hooks })\n * ```\n *\n * @example Overriding authority resolution for testing\n * ```typescript\n * const hooks: LexResolverHooks = {\n * // Always resolve to a test DID\n * onResolveAuthority({ nsid }) {\n * if (nsid.authority === 'test.example') {\n * return 'did:plc:test123'\n * }\n * // Return undefined to use default resolution\n * }\n * }\n * ```\n */\nexport type LexResolverHooks = {\n /**\n * Hook called before resolving a lexicon authority DID. If a DID is returned,\n * it will be used instead of performing the default resolution. In that case,\n * the `onResolveAuthorityResult` and `onResolveAuthorityError` hooks will\n * not be called.\n *\n * @param data - Object containing the NSID being resolved\n * @returns A DID to use instead of default resolution, or void/undefined to proceed normally\n */\n onResolveAuthority?(data: { nsid: NSID }): Awaitable<void | Did>\n\n /**\n * Hook called after successfully resolving a lexicon authority DID.\n *\n * @param data - Object containing the NSID and resolved DID\n */\n onResolveAuthorityResult?(data: { nsid: NSID; did: Did }): Awaitable<void>\n\n /**\n * Hook called when authority resolution fails.\n *\n * @param data - Object containing the NSID and error that occurred\n */\n onResolveAuthorityError?(data: { nsid: NSID; err: unknown }): Awaitable<void>\n\n /**\n * Hook called before fetching a lexicon URI. If a result is returned, it will\n * be used instead of performing the default fetch. In that case, the\n * `onFetchResult` and `onFetchError` hooks will not be called.\n *\n * @param data - Object containing the URI being fetched\n * @returns A fetch result to use instead of default fetch, or void/undefined to proceed normally\n */\n onFetch?(data: { uri: AtUri }): Awaitable<void | LexResolverFetchResult>\n\n /**\n * Hook called after successfully fetching a lexicon document.\n *\n * @param data - Object containing the URI, CID, and parsed lexicon document\n */\n onFetchResult?(data: {\n uri: AtUri\n cid: Cid\n lexicon: LexiconDocument\n }): Awaitable<void>\n\n /**\n * Hook called when fetching fails.\n *\n * @param data - Object containing the URI and error that occurred\n */\n onFetchError?(data: { uri: AtUri; err: unknown }): Awaitable<void>\n}\n\n/**\n * Configuration options for the {@link LexResolver}.\n *\n * Extends DID resolver options with lexicon-specific hooks for customizing\n * the resolution process.\n *\n * @see {@link CreateDidResolverOptions} for DID resolver configuration\n */\nexport type LexResolverOptions = CreateDidResolverOptions & {\n /**\n * Optional hooks for customizing the resolution process.\n * See {@link LexResolverHooks} for available callbacks.\n */\n hooks?: LexResolverHooks\n}\n\nexport { AtUri, type Cid, NSID }\nexport type { LexiconDocument, ResolveDidOptions }\n\n/**\n * Resolves Lexicon documents from the AT Protocol network.\n *\n * The {@link LexResolver} handles the complete process of resolving a lexicon\n * by NSID:\n * 1. **Authority Resolution**: Looks up the `_lexicon.<authority>` DNS TXT record\n * to find the DID that controls lexicons for that namespace\n * 2. **DID Resolution**: Resolves the DID document to find the PDS endpoint and\n * signing key\n * 3. **Record Fetch**: Fetches the lexicon record from the PDS with cryptographic\n * proof verification\n * 4. **Validation**: Validates the lexicon document structure\n *\n * @example Basic usage - resolve a lexicon by NSID\n * ```typescript\n * import { LexResolver } from '@atproto/lex-resolver'\n *\n * const resolver = new LexResolver({})\n *\n * // Get a lexicon document by its NSID\n * const result = await resolver.get('app.bsky.feed.post')\n * console.log(result.lexicon) // The parsed lexicon document\n * console.log(result.uri) // AT URI where it was found\n * console.log(result.cid) // Content identifier for verification\n * ```\n *\n * @example Two-step resolution for more control\n * ```typescript\n * import { LexResolver } from '@atproto/lex-resolver'\n *\n * const resolver = new LexResolver({})\n *\n * // Step 1: Resolve the authority to get the AT URI\n * const uri = await resolver.resolve('app.bsky.feed.post')\n * console.log(uri.toString()) // 'at://did:plc:xxx/com.atproto.lexicon.schema/app.bsky.feed.post'\n *\n * // Step 2: Fetch the lexicon from the URI\n * const result = await resolver.fetch(uri)\n * console.log(result.lexicon)\n * ```\n *\n * @example Using hooks for caching\n * ```typescript\n * import { LexResolver, LexResolverFetchResult } from '@atproto/lex-resolver'\n *\n * const cache = new Map<string, LexResolverFetchResult>()\n *\n * const resolver = new LexResolver({\n * hooks: {\n * onFetch({ uri }) {\n * return cache.get(uri.toString())\n * },\n * onFetchResult({ uri, cid, lexicon }) {\n * cache.set(uri.toString(), { cid, lexicon })\n * }\n * }\n * })\n * ```\n *\n * @example Error handling\n * ```typescript\n * import { LexResolver, LexResolverError } from '@atproto/lex-resolver'\n *\n * const resolver = new LexResolver({})\n *\n * try {\n * const result = await resolver.get('com.example.unknown')\n * } catch (error) {\n * if (error instanceof LexResolverError) {\n * console.error(`Failed to resolve ${error.nsid}: ${error.description}`)\n * }\n * }\n * ```\n */\nexport class LexResolver {\n protected readonly options: LexResolverOptions\n protected readonly didResolver: DidResolver<'plc' | 'web'>\n\n constructor(options: LexResolverOptions) {\n this.options = options\n this.didResolver = createDidResolver(options)\n }\n\n /**\n * Gets a lexicon document by its NSID.\n *\n * This is the primary method for resolving lexicons. It combines\n * {@link resolve} and {@link fetch} into a single operation, handling\n * authority resolution, DID lookup, and record fetching.\n *\n * @param nsidStr - The NSID to resolve, either as a string or NSID object\n * @param options - Optional DID resolution options (e.g., signal for cancellation)\n * @returns The resolved lexicon result containing URI, CID, and lexicon document\n * @throws {LexResolverError} If resolution fails at any stage\n *\n * @example\n * ```typescript\n * // Resolve using string NSID\n * const result = await resolver.get('app.bsky.feed.post')\n *\n * // Resolve using NSID object\n * import { NSID } from '@atproto/syntax'\n * const nsid = NSID.from('app.bsky.feed.post')\n * const result = await resolver.get(nsid)\n *\n * // With abort signal for cancellation\n * const controller = new AbortController()\n * const result = await resolver.get('app.bsky.feed.post', {\n * signal: controller.signal\n * })\n * ```\n */\n async get(\n nsidStr: NSID | string,\n options?: ResolveDidOptions,\n ): Promise<LexResolverResult> {\n const uri = await this.resolve(nsidStr)\n return this.fetch(uri, options)\n }\n\n /**\n * Resolves the authority for an NSID and returns the AT URI for the lexicon.\n *\n * This method performs the first stage of lexicon resolution:\n * 1. Parses the NSID to extract the authority domain\n * 2. Looks up the `_lexicon.<authority>` DNS TXT record\n * 3. Extracts the DID from the TXT record (format: `did=<did>`)\n * 4. Constructs the AT URI for the lexicon record\n *\n * Use this when you need the URI without fetching the actual document,\n * or when you want to implement custom fetching logic.\n *\n * @param nsidStr - The NSID to resolve, either as a string or NSID object\n * @returns The AT URI pointing to the lexicon record\n * @throws {LexResolverError} If authority resolution fails (e.g., DNS lookup fails)\n *\n * @example\n * ```typescript\n * // Resolve to get the AT URI\n * const uri = await resolver.resolve('app.bsky.feed.post')\n * console.log(uri.toString())\n * // Output: 'at://did:plc:z72i7hdynmk6r22z27h6tvur/com.atproto.lexicon.schema/app.bsky.feed.post'\n *\n * // The URI can then be used with fetch() or stored for later use\n * const result = await resolver.fetch(uri)\n * ```\n */\n async resolve(nsidStr: NSID | string): Promise<AtUri> {\n const nsid = NSID.from(nsidStr)\n\n const did =\n (await this.options.hooks?.onResolveAuthority?.({ nsid })) ??\n (await this.resolveLexiconAuthority(nsid).then(\n async (did) => {\n await this.options.hooks?.onResolveAuthorityResult?.({ nsid, did })\n return did\n },\n async (err) => {\n await this.options.hooks?.onResolveAuthorityError?.({ nsid, err })\n throw err\n },\n ))\n\n return AtUri.make(did, 'com.atproto.lexicon.schema', nsid.toString())\n }\n\n // @TODO This class could be made compatible with browsers by making the\n // following method abstract and/or by allowing the caller to inject a DNS\n // resolver implementation (based on DNS-over-HTTPS or similar), instead of\n // using the Node.js built-in resolver.\n protected async resolveLexiconAuthority(nsid: NSID): Promise<Did> {\n try {\n return await getDomainTxtDid(`_lexicon.${nsid.authority}`)\n } catch (cause) {\n throw new LexResolverError(\n nsid,\n `Failed to resolve lexicon DID authority for ${nsid}`,\n { cause },\n )\n }\n }\n\n /**\n * Fetches a lexicon document from a specific AT URI.\n *\n * This method performs the second stage of lexicon resolution:\n * 1. Resolves the DID from the URI to find the PDS endpoint\n * 2. Fetches the record from the PDS using `com.atproto.sync.getRecord`\n * 3. Verifies the cryptographic proof (commit signature)\n * 4. Validates the lexicon document structure\n * 5. Ensures the document ID matches the URI rkey\n *\n * Use this when you already have an AT URI (e.g., from {@link resolve})\n * and want to fetch the lexicon document.\n *\n * @param uriStr - The AT URI to fetch, either as a string or AtUri object\n * @param options - Optional DID resolution options (e.g., signal for cancellation, noCache)\n * @returns The resolved lexicon result containing URI, CID, and lexicon document\n * @throws {LexResolverError} If fetching or validation fails\n *\n * @example\n * ```typescript\n * // Fetch from a known URI\n * const result = await resolver.fetch(\n * 'at://did:plc:xyz/com.atproto.lexicon.schema/app.bsky.feed.post'\n * )\n *\n * // Fetch with no-cache to bypass any upstream caching\n * const result = await resolver.fetch(uri, { noCache: true })\n *\n * // Fetch with abort signal\n * const controller = new AbortController()\n * const result = await resolver.fetch(uri, { signal: controller.signal })\n * ```\n */\n async fetch(\n uriStr: AtUri | string,\n options?: ResolveDidOptions,\n ): Promise<LexResolverResult> {\n const uri = typeof uriStr === 'string' ? new AtUri(uriStr) : uriStr\n\n const { lexicon, cid } =\n (await this.options.hooks?.onFetch?.({ uri })) ??\n (await this.fetchLexiconUri(uri, options).then(\n async (res) => {\n await this.options.hooks?.onFetchResult?.({ uri, ...res })\n return res\n },\n async (err) => {\n await this.options.hooks?.onFetchError?.({ uri, err })\n throw err\n },\n ))\n\n return { uri, cid, lexicon }\n }\n\n protected async fetchLexiconUri(\n uri: AtUri,\n options?: ResolveDidOptions,\n ): Promise<LexResolverFetchResult> {\n const { did, nsid } = parseLexiconUri(uri)\n\n const { pds, key } = await this.didResolver\n .resolve(did, options)\n .then(extractAtprotoData)\n .catch((cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to resolve DID document for ${did}`,\n { cause },\n )\n })\n\n if (!key || !pds || !URL.canParse(pds.serviceEndpoint)) {\n throw new LexResolverError(\n nsid,\n `No atproto PDS service endpoint or signing key found in ${did} DID document`,\n )\n }\n\n const agent = buildAgent({\n service: pds.serviceEndpoint,\n fetch: this.options.fetch,\n })\n\n const collection = 'com.atproto.lexicon.schema'\n const rkey = nsid.toString()\n\n const { cid, record } = await xrpc(agent, com.atproto.sync.getRecord, {\n signal: options?.signal,\n headers: options?.noCache ? { 'Cache-Control': 'no-cache' } : undefined,\n params: { did, collection, rkey },\n }).then(\n ({ body }) => {\n return verifyRecordProof(body, did, key, collection, rkey).catch(\n (cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to verify Lexicon record proof at ${uri}`,\n { cause },\n )\n },\n )\n },\n (cause) => {\n throw new LexResolverError(nsid, `Failed to fetch Record ${uri}`, {\n cause,\n })\n },\n )\n\n const validationResult = lexiconDocumentSchema.safeParse(record)\n if (!validationResult.success) {\n throw new LexResolverError(nsid, `Invalid Lexicon document at ${uri}`, {\n cause: validationResult.reason,\n })\n }\n\n const lexicon = validationResult.value\n if (lexicon.id !== uri.rkey) {\n throw new LexResolverError(\n nsid,\n `Invalid document id \"${lexicon.id}\" at ${uri}`,\n )\n }\n\n return { lexicon, cid }\n }\n}\n\nfunction parseLexiconUri(uri: AtUri): {\n did: Did\n nsid: NSID\n} {\n // Validate input URI\n const nsid = NSID.from(uri.rkey)\n try {\n const did = uri.host\n assertDid(did)\n return { did, nsid }\n } catch (cause) {\n throw new LexResolverError(nsid, `URI host is not a DID ${uri}`, { cause })\n }\n}\n\nasync function getDomainTxtDid(domain: string): Promise<Did> {\n const didLines = (await resolveTxt(domain))\n .map((chunks) => chunks.join(''))\n .filter((i) => i.startsWith('did='))\n\n if (didLines.length === 1) {\n const did = didLines[0].slice(4)\n assertDid(did)\n return did\n }\n\n throw didLines.length > 1\n ? new Error('Multiple DIDs found in DNS TXT records')\n : new Error('No DID found in DNS TXT records')\n}\n\nasync function verifyRecordProof(\n car: Uint8Array,\n did: Did,\n key: AtprotoVerificationMethod,\n collection: NsidString,\n rkey: string,\n) {\n const { root, blocks } = await readCarWithRoot(car)\n const blockstore = new MemoryBlockstore(blocks)\n\n const commit = await blockstore.readObj(root, repoDef.commit)\n if (commit.did !== did) {\n throw new Error(`Invalid repo did: ${commit.did}`)\n }\n\n const signingKey = getDidKeyFromMultibase(key)\n const validSig = await verifyCommitSig(commit, signingKey)\n if (!validSig) {\n throw new Error(`Invalid signature on commit: ${root.toString()}`)\n }\n\n const mst = MST.load(blockstore, commit.data)\n\n const cid = await mst.get(`${collection}/${rkey}`)\n if (!cid) throw new Error('Record not found in proof')\n\n const record = await blockstore.readRecord(cid)\n if (record?.$type !== collection) {\n throw new Error(\n `Invalid record type: expected ${collection}, got ${record?.$type}`,\n )\n }\n\n return { cid, record }\n}\n\nfunction getDidKeyFromMultibase(key: AtprotoVerificationMethod) {\n switch (key.type) {\n case 'EcdsaSecp256r1VerificationKey2019': {\n const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)\n return crypto.formatDidKey(crypto.P256_JWT_ALG, keyBytes)\n }\n case 'EcdsaSecp256k1VerificationKey2019': {\n const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)\n return crypto.formatDidKey(crypto.SECP256K1_JWT_ALG, keyBytes)\n }\n case 'Multikey': {\n const { jwtAlg, keyBytes } = crypto.parseMultikey(key.publicKeyMultibase)\n return crypto.formatDidKey(jwtAlg, keyBytes)\n }\n default: {\n // Should never happen\n throw new Error(`Unsupported verification method type: ${key.type}`)\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,9 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-resolver",
3
- "version": "0.1.3",
4
- "engines": {
5
- "node": ">=22"
6
- },
3
+ "version": "0.1.4",
7
4
  "license": "MIT",
8
5
  "description": "Lexicon document resolver utility for AT Lexicons",
9
6
  "keywords": [
@@ -19,10 +16,6 @@
19
16
  "directory": "packages/lex/lex-resolver"
20
17
  },
21
18
  "files": [
22
- "./src",
23
- "./tsconfig.build.json",
24
- "./tsconfig.tests.json",
25
- "./tsconfig.json",
26
19
  "./dist",
27
20
  "./CHANGELOG.md"
28
21
  ],
@@ -34,23 +27,27 @@
34
27
  "default": "./dist/index.js"
35
28
  }
36
29
  },
30
+ "engines": {
31
+ "node": ">=22"
32
+ },
37
33
  "dependencies": {
38
34
  "tslib": "^2.8.1",
39
- "@atproto-labs/did-resolver": "^0.3.3",
40
- "@atproto/lex-data": "^0.1.3",
41
- "@atproto/lex-schema": "^0.1.5",
42
- "@atproto/lex-document": "^0.1.2",
43
- "@atproto/crypto": "^0.5.2",
44
- "@atproto/lex-client": "^0.2.0",
45
- "@atproto/syntax": "^0.6.3",
46
- "@atproto/repo": "^0.10.2"
35
+ "@atproto-labs/did-resolver": "^0.3.4",
36
+ "@atproto/crypto": "^0.5.3",
37
+ "@atproto/lex-client": "^0.2.1",
38
+ "@atproto/lex-data": "^0.1.4",
39
+ "@atproto/lex-document": "^0.1.3",
40
+ "@atproto/lex-schema": "^0.1.6",
41
+ "@atproto/repo": "^0.10.3",
42
+ "@atproto/syntax": "^0.6.4"
47
43
  },
48
44
  "devDependencies": {
49
45
  "vitest": "^4.0.16",
50
- "@atproto/lex-builder": "^0.1.4"
46
+ "@atproto/lex-builder": "^0.1.5"
51
47
  },
52
48
  "scripts": {
53
- "prebuild": "node ./scripts/lex-build.mjs",
49
+ "codegen:lex": "node ./scripts/lex-build.mjs",
50
+ "prebuild": "pnpm run '/^(codegen:.+)$/'",
54
51
  "build": "tsgo --build tsconfig.build.json",
55
52
  "test": "vitest run"
56
53
  }
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './lex-resolver.js'
2
- export * from './lex-resolver-error.js'
@@ -1,109 +0,0 @@
1
- import { LexError } from '@atproto/lex-data'
2
- import { NSID } from '@atproto/syntax'
3
-
4
- /**
5
- * Error class for lexicon resolution failures.
6
- *
7
- * This error is thrown when the {@link LexResolver} encounters issues during
8
- * the resolution process, such as DNS lookup failures, DID resolution errors,
9
- * invalid lexicon documents, or network failures.
10
- *
11
- * @example Catching resolution errors
12
- * ```typescript
13
- * import { LexResolver, LexResolverError } from '@atproto/lex-resolver'
14
- *
15
- * const resolver = new LexResolver({})
16
- *
17
- * try {
18
- * const result = await resolver.get('com.example.myLexicon')
19
- * } catch (error) {
20
- * if (error instanceof LexResolverError) {
21
- * console.error(`Failed to resolve ${error.nsid}: ${error.description}`)
22
- * // Access the original cause if available
23
- * if (error.cause) {
24
- * console.error('Caused by:', error.cause)
25
- * }
26
- * }
27
- * }
28
- * ```
29
- *
30
- * @example Creating errors with the factory method
31
- * ```typescript
32
- * import { LexResolverError } from '@atproto/lex-resolver'
33
- *
34
- * // Create from string NSID
35
- * const error = LexResolverError.from(
36
- * 'com.example.myLexicon',
37
- * 'Custom error description'
38
- * )
39
- * ```
40
- */
41
- export class LexResolverError extends LexError {
42
- name = 'LexResolverError'
43
-
44
- /**
45
- * Creates a new LexResolverError instance.
46
- *
47
- * @param nsid - The NSID that failed to resolve
48
- * @param description - Human-readable description of the error. Defaults to
49
- * a generic message if not provided.
50
- * @param options - Standard error options including `cause` for error chaining
51
- *
52
- * @example
53
- * ```typescript
54
- * import { NSID } from '@atproto/syntax'
55
- * import { LexResolverError } from '@atproto/lex-resolver'
56
- *
57
- * const nsid = NSID.from('com.example.myLexicon')
58
- * const error = new LexResolverError(
59
- * nsid,
60
- * 'DNS lookup failed',
61
- * { cause: originalError }
62
- * )
63
- * ```
64
- */
65
- constructor(
66
- /**
67
- * The NSID that failed to resolve.
68
- */
69
- public readonly nsid: NSID,
70
- /**
71
- * Human-readable description of what went wrong during resolution.
72
- */
73
- public readonly description = `Could not resolve Lexicon for NSID`,
74
- options?: ErrorOptions,
75
- ) {
76
- super('LexiconResolutionFailure', `${description} (${nsid})`, options)
77
- }
78
-
79
- /**
80
- * Factory method to create a LexResolverError from a string or NSID.
81
- *
82
- * This is a convenience method that handles the conversion of string NSIDs
83
- * to NSID objects automatically.
84
- *
85
- * @param nsid - The NSID as a string or NSID object
86
- * @param description - Optional human-readable description of the error
87
- * @returns A new LexResolverError instance
88
- *
89
- * @example
90
- * ```typescript
91
- * import { LexResolverError } from '@atproto/lex-resolver'
92
- *
93
- * // Create from string
94
- * const error1 = LexResolverError.from('com.example.myLexicon')
95
- *
96
- * // Create with description
97
- * const error2 = LexResolverError.from(
98
- * 'com.example.myLexicon',
99
- * 'Authority not found in DNS'
100
- * )
101
- * ```
102
- */
103
- static from(nsid: NSID | string, description?: string) {
104
- return new LexResolverError(
105
- typeof nsid === 'string' ? NSID.from(nsid) : nsid,
106
- description,
107
- )
108
- }
109
- }
@@ -1,571 +0,0 @@
1
- import { resolveTxt } from 'node:dns/promises'
2
- import * as crypto from '@atproto/crypto'
3
- import { buildAgent, xrpc } from '@atproto/lex-client'
4
- import { Cid } from '@atproto/lex-data'
5
- import { LexiconDocument, lexiconDocumentSchema } from '@atproto/lex-document'
6
- import {
7
- MST,
8
- MemoryBlockstore,
9
- def as repoDef,
10
- readCarWithRoot,
11
- verifyCommitSig,
12
- } from '@atproto/repo'
13
- import { AtUri, NSID, NsidString } from '@atproto/syntax'
14
- import {
15
- AtprotoVerificationMethod,
16
- CreateDidResolverOptions,
17
- Did,
18
- DidResolver,
19
- ResolveDidOptions,
20
- assertDid,
21
- createDidResolver,
22
- extractAtprotoData,
23
- } from '@atproto-labs/did-resolver'
24
- import { LexResolverError } from './lex-resolver-error.js'
25
- import { com } from './lexicons/index.js'
26
-
27
- /**
28
- * Result returned when successfully resolving a lexicon document.
29
- *
30
- * Contains the full AT URI where the lexicon was found, the content-addressed
31
- * identifier (CID) for integrity verification, and the parsed lexicon document.
32
- */
33
- export type LexResolverResult = {
34
- /** The AT URI where the lexicon document was found */
35
- uri: AtUri
36
- /** Content identifier (CID) of the lexicon record for integrity verification */
37
- cid: Cid
38
- /** The parsed and validated lexicon document */
39
- lexicon: LexiconDocument
40
- }
41
-
42
- /**
43
- * Result returned when fetching a lexicon document from a specific URI.
44
- *
45
- * This is a subset of {@link LexResolverResult} used internally and by hooks,
46
- * containing only the CID and lexicon document (without the URI, which is
47
- * already known from the fetch request).
48
- */
49
- export type LexResolverFetchResult = {
50
- /** Content identifier (CID) of the lexicon record */
51
- cid: Cid
52
- /** The parsed and validated lexicon document */
53
- lexicon: LexiconDocument
54
- }
55
-
56
- export type Awaitable<T> = T | Promise<T>
57
-
58
- /**
59
- * Callback hooks for customizing the lexicon resolution process.
60
- *
61
- * Hooks allow you to intercept, cache, or override the default resolution
62
- * behavior at various stages. Each hook can be synchronous or asynchronous.
63
- *
64
- * @example Implementing a cache with hooks
65
- * ```typescript
66
- * import { LexResolver, LexResolverHooks, LexResolverFetchResult } from '@atproto/lex-resolver'
67
- * import { AtUri } from '@atproto/syntax'
68
- *
69
- * const cache = new Map<string, LexResolverFetchResult>()
70
- *
71
- * const hooks: LexResolverHooks = {
72
- * // Return cached result if available, bypassing network fetch
73
- * onFetch({ uri }) {
74
- * return cache.get(uri.toString())
75
- * },
76
- * // Cache successful fetches
77
- * onFetchResult({ uri, cid, lexicon }) {
78
- * cache.set(uri.toString(), { cid, lexicon })
79
- * },
80
- * // Log errors for monitoring
81
- * onFetchError({ uri, err }) {
82
- * console.error(`Failed to fetch ${uri}:`, err)
83
- * }
84
- * }
85
- *
86
- * const resolver = new LexResolver({ hooks })
87
- * ```
88
- *
89
- * @example Overriding authority resolution for testing
90
- * ```typescript
91
- * const hooks: LexResolverHooks = {
92
- * // Always resolve to a test DID
93
- * onResolveAuthority({ nsid }) {
94
- * if (nsid.authority === 'test.example') {
95
- * return 'did:plc:test123'
96
- * }
97
- * // Return undefined to use default resolution
98
- * }
99
- * }
100
- * ```
101
- */
102
- export type LexResolverHooks = {
103
- /**
104
- * Hook called before resolving a lexicon authority DID. If a DID is returned,
105
- * it will be used instead of performing the default resolution. In that case,
106
- * the `onResolveAuthorityResult` and `onResolveAuthorityError` hooks will
107
- * not be called.
108
- *
109
- * @param data - Object containing the NSID being resolved
110
- * @returns A DID to use instead of default resolution, or void/undefined to proceed normally
111
- */
112
- onResolveAuthority?(data: { nsid: NSID }): Awaitable<void | Did>
113
-
114
- /**
115
- * Hook called after successfully resolving a lexicon authority DID.
116
- *
117
- * @param data - Object containing the NSID and resolved DID
118
- */
119
- onResolveAuthorityResult?(data: { nsid: NSID; did: Did }): Awaitable<void>
120
-
121
- /**
122
- * Hook called when authority resolution fails.
123
- *
124
- * @param data - Object containing the NSID and error that occurred
125
- */
126
- onResolveAuthorityError?(data: { nsid: NSID; err: unknown }): Awaitable<void>
127
-
128
- /**
129
- * Hook called before fetching a lexicon URI. If a result is returned, it will
130
- * be used instead of performing the default fetch. In that case, the
131
- * `onFetchResult` and `onFetchError` hooks will not be called.
132
- *
133
- * @param data - Object containing the URI being fetched
134
- * @returns A fetch result to use instead of default fetch, or void/undefined to proceed normally
135
- */
136
- onFetch?(data: { uri: AtUri }): Awaitable<void | LexResolverFetchResult>
137
-
138
- /**
139
- * Hook called after successfully fetching a lexicon document.
140
- *
141
- * @param data - Object containing the URI, CID, and parsed lexicon document
142
- */
143
- onFetchResult?(data: {
144
- uri: AtUri
145
- cid: Cid
146
- lexicon: LexiconDocument
147
- }): Awaitable<void>
148
-
149
- /**
150
- * Hook called when fetching fails.
151
- *
152
- * @param data - Object containing the URI and error that occurred
153
- */
154
- onFetchError?(data: { uri: AtUri; err: unknown }): Awaitable<void>
155
- }
156
-
157
- /**
158
- * Configuration options for the {@link LexResolver}.
159
- *
160
- * Extends DID resolver options with lexicon-specific hooks for customizing
161
- * the resolution process.
162
- *
163
- * @see {@link CreateDidResolverOptions} for DID resolver configuration
164
- */
165
- export type LexResolverOptions = CreateDidResolverOptions & {
166
- /**
167
- * Optional hooks for customizing the resolution process.
168
- * See {@link LexResolverHooks} for available callbacks.
169
- */
170
- hooks?: LexResolverHooks
171
- }
172
-
173
- export { AtUri, type Cid, NSID }
174
- export type { LexiconDocument, ResolveDidOptions }
175
-
176
- /**
177
- * Resolves Lexicon documents from the AT Protocol network.
178
- *
179
- * The {@link LexResolver} handles the complete process of resolving a lexicon
180
- * by NSID:
181
- * 1. **Authority Resolution**: Looks up the `_lexicon.<authority>` DNS TXT record
182
- * to find the DID that controls lexicons for that namespace
183
- * 2. **DID Resolution**: Resolves the DID document to find the PDS endpoint and
184
- * signing key
185
- * 3. **Record Fetch**: Fetches the lexicon record from the PDS with cryptographic
186
- * proof verification
187
- * 4. **Validation**: Validates the lexicon document structure
188
- *
189
- * @example Basic usage - resolve a lexicon by NSID
190
- * ```typescript
191
- * import { LexResolver } from '@atproto/lex-resolver'
192
- *
193
- * const resolver = new LexResolver({})
194
- *
195
- * // Get a lexicon document by its NSID
196
- * const result = await resolver.get('app.bsky.feed.post')
197
- * console.log(result.lexicon) // The parsed lexicon document
198
- * console.log(result.uri) // AT URI where it was found
199
- * console.log(result.cid) // Content identifier for verification
200
- * ```
201
- *
202
- * @example Two-step resolution for more control
203
- * ```typescript
204
- * import { LexResolver } from '@atproto/lex-resolver'
205
- *
206
- * const resolver = new LexResolver({})
207
- *
208
- * // Step 1: Resolve the authority to get the AT URI
209
- * const uri = await resolver.resolve('app.bsky.feed.post')
210
- * console.log(uri.toString()) // 'at://did:plc:xxx/com.atproto.lexicon.schema/app.bsky.feed.post'
211
- *
212
- * // Step 2: Fetch the lexicon from the URI
213
- * const result = await resolver.fetch(uri)
214
- * console.log(result.lexicon)
215
- * ```
216
- *
217
- * @example Using hooks for caching
218
- * ```typescript
219
- * import { LexResolver, LexResolverFetchResult } from '@atproto/lex-resolver'
220
- *
221
- * const cache = new Map<string, LexResolverFetchResult>()
222
- *
223
- * const resolver = new LexResolver({
224
- * hooks: {
225
- * onFetch({ uri }) {
226
- * return cache.get(uri.toString())
227
- * },
228
- * onFetchResult({ uri, cid, lexicon }) {
229
- * cache.set(uri.toString(), { cid, lexicon })
230
- * }
231
- * }
232
- * })
233
- * ```
234
- *
235
- * @example Error handling
236
- * ```typescript
237
- * import { LexResolver, LexResolverError } from '@atproto/lex-resolver'
238
- *
239
- * const resolver = new LexResolver({})
240
- *
241
- * try {
242
- * const result = await resolver.get('com.example.unknown')
243
- * } catch (error) {
244
- * if (error instanceof LexResolverError) {
245
- * console.error(`Failed to resolve ${error.nsid}: ${error.description}`)
246
- * }
247
- * }
248
- * ```
249
- */
250
- export class LexResolver {
251
- protected readonly didResolver: DidResolver<'plc' | 'web'>
252
-
253
- constructor(protected readonly options: LexResolverOptions) {
254
- this.didResolver = createDidResolver(options)
255
- }
256
-
257
- /**
258
- * Gets a lexicon document by its NSID.
259
- *
260
- * This is the primary method for resolving lexicons. It combines
261
- * {@link resolve} and {@link fetch} into a single operation, handling
262
- * authority resolution, DID lookup, and record fetching.
263
- *
264
- * @param nsidStr - The NSID to resolve, either as a string or NSID object
265
- * @param options - Optional DID resolution options (e.g., signal for cancellation)
266
- * @returns The resolved lexicon result containing URI, CID, and lexicon document
267
- * @throws {LexResolverError} If resolution fails at any stage
268
- *
269
- * @example
270
- * ```typescript
271
- * // Resolve using string NSID
272
- * const result = await resolver.get('app.bsky.feed.post')
273
- *
274
- * // Resolve using NSID object
275
- * import { NSID } from '@atproto/syntax'
276
- * const nsid = NSID.from('app.bsky.feed.post')
277
- * const result = await resolver.get(nsid)
278
- *
279
- * // With abort signal for cancellation
280
- * const controller = new AbortController()
281
- * const result = await resolver.get('app.bsky.feed.post', {
282
- * signal: controller.signal
283
- * })
284
- * ```
285
- */
286
- async get(
287
- nsidStr: NSID | string,
288
- options?: ResolveDidOptions,
289
- ): Promise<LexResolverResult> {
290
- const uri = await this.resolve(nsidStr)
291
- return this.fetch(uri, options)
292
- }
293
-
294
- /**
295
- * Resolves the authority for an NSID and returns the AT URI for the lexicon.
296
- *
297
- * This method performs the first stage of lexicon resolution:
298
- * 1. Parses the NSID to extract the authority domain
299
- * 2. Looks up the `_lexicon.<authority>` DNS TXT record
300
- * 3. Extracts the DID from the TXT record (format: `did=<did>`)
301
- * 4. Constructs the AT URI for the lexicon record
302
- *
303
- * Use this when you need the URI without fetching the actual document,
304
- * or when you want to implement custom fetching logic.
305
- *
306
- * @param nsidStr - The NSID to resolve, either as a string or NSID object
307
- * @returns The AT URI pointing to the lexicon record
308
- * @throws {LexResolverError} If authority resolution fails (e.g., DNS lookup fails)
309
- *
310
- * @example
311
- * ```typescript
312
- * // Resolve to get the AT URI
313
- * const uri = await resolver.resolve('app.bsky.feed.post')
314
- * console.log(uri.toString())
315
- * // Output: 'at://did:plc:z72i7hdynmk6r22z27h6tvur/com.atproto.lexicon.schema/app.bsky.feed.post'
316
- *
317
- * // The URI can then be used with fetch() or stored for later use
318
- * const result = await resolver.fetch(uri)
319
- * ```
320
- */
321
- async resolve(nsidStr: NSID | string): Promise<AtUri> {
322
- const nsid = NSID.from(nsidStr)
323
-
324
- const did =
325
- (await this.options.hooks?.onResolveAuthority?.({ nsid })) ??
326
- (await this.resolveLexiconAuthority(nsid).then(
327
- async (did) => {
328
- await this.options.hooks?.onResolveAuthorityResult?.({ nsid, did })
329
- return did
330
- },
331
- async (err) => {
332
- await this.options.hooks?.onResolveAuthorityError?.({ nsid, err })
333
- throw err
334
- },
335
- ))
336
-
337
- return AtUri.make(did, 'com.atproto.lexicon.schema', nsid.toString())
338
- }
339
-
340
- // @TODO This class could be made compatible with browsers by making the
341
- // following method abstract and/or by allowing the caller to inject a DNS
342
- // resolver implementation (based on DNS-over-HTTPS or similar), instead of
343
- // using the Node.js built-in resolver.
344
- protected async resolveLexiconAuthority(nsid: NSID): Promise<Did> {
345
- try {
346
- return await getDomainTxtDid(`_lexicon.${nsid.authority}`)
347
- } catch (cause) {
348
- throw new LexResolverError(
349
- nsid,
350
- `Failed to resolve lexicon DID authority for ${nsid}`,
351
- { cause },
352
- )
353
- }
354
- }
355
-
356
- /**
357
- * Fetches a lexicon document from a specific AT URI.
358
- *
359
- * This method performs the second stage of lexicon resolution:
360
- * 1. Resolves the DID from the URI to find the PDS endpoint
361
- * 2. Fetches the record from the PDS using `com.atproto.sync.getRecord`
362
- * 3. Verifies the cryptographic proof (commit signature)
363
- * 4. Validates the lexicon document structure
364
- * 5. Ensures the document ID matches the URI rkey
365
- *
366
- * Use this when you already have an AT URI (e.g., from {@link resolve})
367
- * and want to fetch the lexicon document.
368
- *
369
- * @param uriStr - The AT URI to fetch, either as a string or AtUri object
370
- * @param options - Optional DID resolution options (e.g., signal for cancellation, noCache)
371
- * @returns The resolved lexicon result containing URI, CID, and lexicon document
372
- * @throws {LexResolverError} If fetching or validation fails
373
- *
374
- * @example
375
- * ```typescript
376
- * // Fetch from a known URI
377
- * const result = await resolver.fetch(
378
- * 'at://did:plc:xyz/com.atproto.lexicon.schema/app.bsky.feed.post'
379
- * )
380
- *
381
- * // Fetch with no-cache to bypass any upstream caching
382
- * const result = await resolver.fetch(uri, { noCache: true })
383
- *
384
- * // Fetch with abort signal
385
- * const controller = new AbortController()
386
- * const result = await resolver.fetch(uri, { signal: controller.signal })
387
- * ```
388
- */
389
- async fetch(
390
- uriStr: AtUri | string,
391
- options?: ResolveDidOptions,
392
- ): Promise<LexResolverResult> {
393
- const uri = typeof uriStr === 'string' ? new AtUri(uriStr) : uriStr
394
-
395
- const { lexicon, cid } =
396
- (await this.options.hooks?.onFetch?.({ uri })) ??
397
- (await this.fetchLexiconUri(uri, options).then(
398
- async (res) => {
399
- await this.options.hooks?.onFetchResult?.({ uri, ...res })
400
- return res
401
- },
402
- async (err) => {
403
- await this.options.hooks?.onFetchError?.({ uri, err })
404
- throw err
405
- },
406
- ))
407
-
408
- return { uri, cid, lexicon }
409
- }
410
-
411
- protected async fetchLexiconUri(
412
- uri: AtUri,
413
- options?: ResolveDidOptions,
414
- ): Promise<LexResolverFetchResult> {
415
- const { did, nsid } = parseLexiconUri(uri)
416
-
417
- const { pds, key } = await this.didResolver
418
- .resolve(did, options)
419
- .then(extractAtprotoData)
420
- .catch((cause) => {
421
- throw new LexResolverError(
422
- nsid,
423
- `Failed to resolve DID document for ${did}`,
424
- { cause },
425
- )
426
- })
427
-
428
- if (!key || !pds || !URL.canParse(pds.serviceEndpoint)) {
429
- throw new LexResolverError(
430
- nsid,
431
- `No atproto PDS service endpoint or signing key found in ${did} DID document`,
432
- )
433
- }
434
-
435
- const agent = buildAgent({
436
- service: pds.serviceEndpoint,
437
- fetch: this.options.fetch,
438
- })
439
-
440
- const collection = 'com.atproto.lexicon.schema'
441
- const rkey = nsid.toString()
442
-
443
- const { cid, record } = await xrpc(agent, com.atproto.sync.getRecord, {
444
- signal: options?.signal,
445
- headers: options?.noCache ? { 'Cache-Control': 'no-cache' } : undefined,
446
- params: { did, collection, rkey },
447
- }).then(
448
- ({ body }) => {
449
- return verifyRecordProof(body, did, key, collection, rkey).catch(
450
- (cause) => {
451
- throw new LexResolverError(
452
- nsid,
453
- `Failed to verify Lexicon record proof at ${uri}`,
454
- { cause },
455
- )
456
- },
457
- )
458
- },
459
- (cause) => {
460
- throw new LexResolverError(nsid, `Failed to fetch Record ${uri}`, {
461
- cause,
462
- })
463
- },
464
- )
465
-
466
- const validationResult = lexiconDocumentSchema.safeParse(record)
467
- if (!validationResult.success) {
468
- throw new LexResolverError(nsid, `Invalid Lexicon document at ${uri}`, {
469
- cause: validationResult.reason,
470
- })
471
- }
472
-
473
- const lexicon = validationResult.value
474
- if (lexicon.id !== uri.rkey) {
475
- throw new LexResolverError(
476
- nsid,
477
- `Invalid document id "${lexicon.id}" at ${uri}`,
478
- )
479
- }
480
-
481
- return { lexicon, cid }
482
- }
483
- }
484
-
485
- function parseLexiconUri(uri: AtUri): {
486
- did: Did
487
- nsid: NSID
488
- } {
489
- // Validate input URI
490
- const nsid = NSID.from(uri.rkey)
491
- try {
492
- const did = uri.host
493
- assertDid(did)
494
- return { did, nsid }
495
- } catch (cause) {
496
- throw new LexResolverError(nsid, `URI host is not a DID ${uri}`, { cause })
497
- }
498
- }
499
-
500
- async function getDomainTxtDid(domain: string): Promise<Did> {
501
- const didLines = (await resolveTxt(domain))
502
- .map((chunks) => chunks.join(''))
503
- .filter((i) => i.startsWith('did='))
504
-
505
- if (didLines.length === 1) {
506
- const did = didLines[0].slice(4)
507
- assertDid(did)
508
- return did
509
- }
510
-
511
- throw didLines.length > 1
512
- ? new Error('Multiple DIDs found in DNS TXT records')
513
- : new Error('No DID found in DNS TXT records')
514
- }
515
-
516
- async function verifyRecordProof(
517
- car: Uint8Array,
518
- did: Did,
519
- key: AtprotoVerificationMethod,
520
- collection: NsidString,
521
- rkey: string,
522
- ) {
523
- const { root, blocks } = await readCarWithRoot(car)
524
- const blockstore = new MemoryBlockstore(blocks)
525
-
526
- const commit = await blockstore.readObj(root, repoDef.commit)
527
- if (commit.did !== did) {
528
- throw new Error(`Invalid repo did: ${commit.did}`)
529
- }
530
-
531
- const signingKey = getDidKeyFromMultibase(key)
532
- const validSig = await verifyCommitSig(commit, signingKey)
533
- if (!validSig) {
534
- throw new Error(`Invalid signature on commit: ${root.toString()}`)
535
- }
536
-
537
- const mst = MST.load(blockstore, commit.data)
538
-
539
- const cid = await mst.get(`${collection}/${rkey}`)
540
- if (!cid) throw new Error('Record not found in proof')
541
-
542
- const record = await blockstore.readRecord(cid)
543
- if (record?.$type !== collection) {
544
- throw new Error(
545
- `Invalid record type: expected ${collection}, got ${record?.$type}`,
546
- )
547
- }
548
-
549
- return { cid, record }
550
- }
551
-
552
- function getDidKeyFromMultibase(key: AtprotoVerificationMethod) {
553
- switch (key.type) {
554
- case 'EcdsaSecp256r1VerificationKey2019': {
555
- const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)
556
- return crypto.formatDidKey(crypto.P256_JWT_ALG, keyBytes)
557
- }
558
- case 'EcdsaSecp256k1VerificationKey2019': {
559
- const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)
560
- return crypto.formatDidKey(crypto.SECP256K1_JWT_ALG, keyBytes)
561
- }
562
- case 'Multikey': {
563
- const { jwtAlg, keyBytes } = crypto.parseMultikey(key.publicKeyMultibase)
564
- return crypto.formatDidKey(jwtAlg, keyBytes)
565
- }
566
- default: {
567
- // Should never happen
568
- throw new Error(`Unsupported verification method type: ${key.type}`)
569
- }
570
- }
571
- }
@@ -1,38 +0,0 @@
1
- /*
2
- * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
- */
4
-
5
- import { l } from '@atproto/lex-schema'
6
-
7
- const $nsid = 'com.atproto.sync.getRecord'
8
-
9
- export { $nsid }
10
-
11
- export const $params = /*#__PURE__*/ l.params({
12
- did: /*#__PURE__*/ l.string({ format: 'did' }),
13
- collection: /*#__PURE__*/ l.string({ format: 'nsid' }),
14
- rkey: /*#__PURE__*/ l.string({ format: 'record-key' }),
15
- })
16
-
17
- export type $Params = l.InferOutput<typeof $params>
18
-
19
- export const $output = /*#__PURE__*/ l.payload('application/vnd.ipld.car')
20
-
21
- export type $Output<B = l.BinaryData> = l.InferPayload<typeof $output, B>
22
- export type $OutputBody<B = l.BinaryData> = l.InferPayloadBody<
23
- typeof $output,
24
- B
25
- >
26
-
27
- /** Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth. */
28
- const main = /*#__PURE__*/ l.query($nsid, $params, $output, [
29
- 'RecordNotFound',
30
- 'RepoNotFound',
31
- 'RepoTakendown',
32
- 'RepoSuspended',
33
- 'RepoDeactivated',
34
- ])
35
-
36
- export { main }
37
-
38
- export const $lxm = $nsid
@@ -1,5 +0,0 @@
1
- /*
2
- * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
- */
4
-
5
- export * from './getRecord.defs.js'
@@ -1,5 +0,0 @@
1
- /*
2
- * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
- */
4
-
5
- export * as getRecord from './sync/getRecord.js'
@@ -1,5 +0,0 @@
1
- /*
2
- * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
- */
4
-
5
- export * as sync from './atproto/sync.js'
@@ -1,5 +0,0 @@
1
- /*
2
- * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
- */
4
-
5
- export * as atproto from './com/atproto.js'
@@ -1,5 +0,0 @@
1
- /*
2
- * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
- */
4
-
5
- export * as com from './com.js'
@@ -1,13 +0,0 @@
1
- {
2
- "extends": ["../../../tsconfig/node.json"],
3
- "include": ["./src"],
4
- "exclude": ["**/*.test.ts"],
5
- "compilerOptions": {
6
- "noImplicitAny": true,
7
- "importHelpers": true,
8
- "target": "ES2023",
9
- "rootDir": "./src",
10
- "outDir": "./dist",
11
- "types": ["node"],
12
- },
13
- }
package/tsconfig.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "include": [],
3
- "references": [
4
- { "path": "./tsconfig.build.json" },
5
- { "path": "./tsconfig.tests.json" },
6
- ],
7
- }
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig/vitest.json",
3
- "include": ["./tests", "./src/**/*.test.ts"],
4
- "compilerOptions": {
5
- "noImplicitAny": true,
6
- "rootDir": "./",
7
- },
8
- }