@atproto/lex-data 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @atproto/lex-data
2
2
 
3
+ ## 0.0.14
4
+
5
+ ### Patch Changes
6
+
7
+ - [#4761](https://github.com/bluesky-social/atproto/pull/4761) [`6a88461`](https://github.com/bluesky-social/atproto/commit/6a88461c5aa9486269f0769b7a3d52f384581786) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Allow `-1` BlobRef size in non-strict mode
8
+
9
+ ## 0.0.13
10
+
11
+ ### Patch Changes
12
+
13
+ - [#4688](https://github.com/bluesky-social/atproto/pull/4688) [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove concept of "downstream error response" (`toResponse()` method) from the `LexError` class
14
+
15
+ - [#4688](https://github.com/bluesky-social/atproto/pull/4688) [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Improve messaging of various errors by making them more precisely describe what happened.
16
+
3
17
  ## 0.0.12
4
18
 
5
19
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,EAA4B,MAAM,UAAU,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGnC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,OAAO,CAAC,GAAG,SAAS,GAAG,GAAG,GAAG,IAAI;IAC3C,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,CAAC,QAAQ,SAAS,mBAAmB,IAClE,QAAQ,SAAS;IAAE,MAAM,EAAE,KAAK,CAAA;CAAE,GAC9B,OAAO,GACP;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,SAAS,QAAQ,GAClC,OAAO,GACP,OAAO,CAAC,MAAM,CAAC,CAAA;AAEvB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;AACnE,wBAAgB,SAAS,CAAC,QAAQ,SAAS,mBAAmB,EAC5D,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,GAChB,KAAK,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAA;AACzC,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,KAAK,IAAI,OAAO,CAAA;AAkDnB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAyBtE;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IACtD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAAC,QAAQ,SAAS,mBAAmB,IAChE,QAAQ,SAAS;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAClC,mBAAmB,CAAC,QAAQ,CAAC,GAAG,aAAa,GAC7C;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,SAAS,QAAQ,GACvC,mBAAmB,CAAC,QAAQ,CAAC,GAAG,aAAa,GAC7C,mBAAmB,CAAC,QAAQ,CAAC,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,GACd,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AAC5C,wBAAgB,YAAY,CAAC,QAAQ,SAAS,mBAAmB,EAC/D,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,QAAQ,GAChB,SAAS,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AACxD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,mBAAmB,GAC5B,SAAS,CAAC,OAAO,GAAG,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA"}
1
+ {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,EAA4B,MAAM,UAAU,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAOnC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,OAAO,CAAC,GAAG,SAAS,GAAG,GAAG,GAAG,IAAI;IAC3C,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,CAAC,QAAQ,SAAS,mBAAmB,IAClE,QAAQ,SAAS;IAAE,MAAM,EAAE,KAAK,CAAA;CAAE,GAC9B,OAAO,GACP;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,SAAS,QAAQ,GAClC,OAAO,GACP,OAAO,CAAC,MAAM,CAAC,CAAA;AAEvB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;AACnE,wBAAgB,SAAS,CAAC,QAAQ,SAAS,mBAAmB,EAC5D,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,GAChB,KAAK,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAA;AACzC,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,KAAK,IAAI,OAAO,CAAA;AAqDnB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAyBtE;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IACtD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAAC,QAAQ,SAAS,mBAAmB,IAChE,QAAQ,SAAS;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAClC,mBAAmB,CAAC,QAAQ,CAAC,GAAG,aAAa,GAC7C;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,SAAS,QAAQ,GACvC,mBAAmB,CAAC,QAAQ,CAAC,GAAG,aAAa,GAC7C,mBAAmB,CAAC,QAAQ,CAAC,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,GACd,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AAC5C,wBAAgB,YAAY,CAAC,QAAQ,SAAS,mBAAmB,EAC/D,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,QAAQ,GAChB,SAAS,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AACxD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,mBAAmB,GAC5B,SAAS,CAAC,OAAO,GAAG,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA"}
package/dist/blob.js CHANGED
@@ -5,6 +5,9 @@ exports.isLegacyBlobRef = isLegacyBlobRef;
5
5
  exports.enumBlobRefs = enumBlobRefs;
6
6
  const cid_js_1 = require("./cid.js");
7
7
  const object_js_1 = require("./object.js");
8
+ // Number.isSafeInteger is actually safe to use with non-number values, so we
9
+ // can use it as a type guard.
10
+ const isSafeInteger = Number.isSafeInteger;
8
11
  function isBlobRef(input, options) {
9
12
  if (!(0, object_js_1.isPlainObject)(input)) {
10
13
  return false;
@@ -17,7 +20,11 @@ function isBlobRef(input, options) {
17
20
  if (typeof mimeType !== 'string' || !mimeType.includes('/')) {
18
21
  return false;
19
22
  }
20
- if (typeof size !== 'number' || size < 0 || !Number.isSafeInteger(size)) {
23
+ if (size === -1 && options?.strict === false) {
24
+ // In non-strict mode, allow size to be -1 to accommodate legacy blob refs
25
+ // that don't include size information.
26
+ }
27
+ else if (!isSafeInteger(size) || size < 0) {
21
28
  return false;
22
29
  }
23
30
  if (typeof ref !== 'object' || ref === null) {
package/dist/blob.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"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","sourcesContent":["import { Cid, RawCid, ifCid, validateCidString } from './cid.js'\nimport { LexValue } from './lex.js'\nimport { isPlainObject, isPlainProto } from './object.js'\n\n/**\n * Reference to binary data (like images, videos, etc.) in the AT Protocol data model.\n *\n * A BlobRef is a {@link LexMap} with a specific structure that identifies binary\n * content by its content hash (CID), along with metadata about the content type\n * and size.\n *\n * @typeParam Ref - The type of CID reference, defaults to any {@link Cid}\n *\n * @example\n * ```typescript\n * import type { BlobRef } from '@atproto/lex-data'\n *\n * const imageRef: BlobRef = {\n * $type: 'blob',\n * mimeType: 'image/jpeg',\n * ref: cid, // CID of the blob content\n * size: 12345\n * }\n * ```\n *\n * @see {@link isBlobRef} to check if a value is a valid BlobRef\n * @see {@link LegacyBlobRef} for the older blob reference format\n */\nexport type BlobRef<Ref extends Cid = Cid> = {\n $type: 'blob'\n mimeType: string\n ref: Ref\n size: number\n}\n\n/**\n * Options for validating a {@link BlobRef}.\n */\nexport type BlobRefCheckOptions = {\n /**\n * If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing\n * any valid CID. Otherwise, validates that the CID is v1, uses the raw\n * multicodec, and has a sha256 multihash.\n *\n * @default true\n */\n strict?: boolean\n}\n\n/**\n * Infers the BlobRef type based on the check options.\n *\n * @typeParam TOptions - The options used for checking\n */\nexport type InferCheckedBlobRef<TOptions extends BlobRefCheckOptions> =\n TOptions extends { strict: false }\n ? BlobRef\n : { strict: boolean } extends TOptions\n ? BlobRef\n : BlobRef<RawCid>\n\n/**\n * Type guard to check if a value is a valid {@link BlobRef}.\n *\n * Validates the structure of the input including:\n * - `$type` must be `'blob'`\n * - `mimeType` must be a valid MIME type string (containing '/')\n * - `size` must be a non-negative safe integer\n * - `ref` must be a valid CID (strict validation by default)\n *\n * @param input - The value to check\n * @param options - Optional validation options\n * @returns `true` if the input is a valid BlobRef\n *\n * @example\n * ```typescript\n * import { isBlobRef } from '@atproto/lex-data'\n *\n * if (isBlobRef(data)) {\n * console.log(data.mimeType) // e.g., 'image/jpeg'\n * console.log(data.size) // e.g., 12345\n * }\n *\n * // Allow any valid CID (not just raw CIDs)\n * if (isBlobRef(data, { strict: false })) {\n * // ...\n * }\n * ```\n */\nexport function isBlobRef(input: unknown): input is BlobRef<RawCid>\nexport function isBlobRef<TOptions extends BlobRefCheckOptions>(\n input: unknown,\n options: TOptions,\n): input is InferCheckedBlobRef<TOptions>\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is BlobRef\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is BlobRef {\n if (!isPlainObject(input)) {\n return false\n }\n\n if (input?.$type !== 'blob') {\n return false\n }\n\n const { mimeType, size, ref } = input\n // @NOTE Very basic mime validation\n if (typeof mimeType !== 'string' || !mimeType.includes('/')) {\n return false\n }\n\n if (typeof size !== 'number' || size < 0 || !Number.isSafeInteger(size)) {\n return false\n }\n\n if (typeof ref !== 'object' || ref === null) {\n return false\n }\n\n for (const key in input) {\n if (\n key !== '$type' &&\n key !== 'mimeType' &&\n key !== 'ref' &&\n key !== 'size'\n ) {\n return false\n }\n }\n\n const cid = ifCid(\n ref,\n // Strict unless explicitly disabled\n options?.strict === false ? undefined : { flavor: 'raw' },\n )\n if (!cid) {\n return false\n }\n\n return true\n}\n\n/**\n * Legacy format for blob references used in older AT Protocol data.\n *\n * This is the older format that stores the CID as a string rather than\n * as a structured CID object. New code should use {@link BlobRef} instead.\n *\n * @example\n * ```typescript\n * import type { LegacyBlobRef } from '@atproto/lex-data'\n *\n * const legacyRef: LegacyBlobRef = {\n * cid: 'bafyreib...',\n * mimeType: 'image/jpeg'\n * }\n * ```\n *\n * @see {@link isLegacyBlobRef} to check if a value is a LegacyBlobRef\n * @see {@link BlobRef} for the current blob reference format\n * @deprecated Use {@link BlobRef} for new code\n */\nexport type LegacyBlobRef = {\n cid: string\n mimeType: string\n}\n\n/**\n * Type guard to check if a value is a valid {@link LegacyBlobRef}.\n *\n * Validates the structure of the input:\n * - `cid` must be a valid CID string\n * - `mimeType` must be a non-empty string\n * - No additional properties allowed\n *\n * @param input - The value to check\n * @returns `true` if the input is a valid LegacyBlobRef\n *\n * @example\n * ```typescript\n * import { isLegacyBlobRef } from '@atproto/lex-data'\n *\n * if (isLegacyBlobRef(data)) {\n * console.log(data.cid) // CID as string\n * console.log(data.mimeType) // e.g., 'image/jpeg'\n * }\n * ```\n *\n * @see {@link isBlobRef} for checking the current blob reference format\n */\nexport function isLegacyBlobRef(input: unknown): input is LegacyBlobRef {\n if (!isPlainObject(input)) {\n return false\n }\n\n const { cid, mimeType } = input\n if (typeof cid !== 'string') {\n return false\n }\n\n if (typeof mimeType !== 'string' || mimeType.length === 0) {\n return false\n }\n\n for (const key in input) {\n if (key !== 'cid' && key !== 'mimeType') {\n return false\n }\n }\n\n if (!validateCidString(cid)) {\n return false\n }\n\n return true\n}\n\n/**\n * Options for enumerating blob references within a {@link LexValue}.\n */\nexport type EnumBlobRefsOptions = BlobRefCheckOptions & {\n /**\n * If `true`, also yields {@link LegacyBlobRef} objects in addition to\n * {@link BlobRef} objects.\n *\n * @default false\n */\n allowLegacy?: boolean\n}\n\n/**\n * Infers the yielded type of {@link enumBlobRefs} based on options.\n *\n * @typeParam TOptions - The options used for enumeration\n */\nexport type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> =\n TOptions extends { allowLegacy: true }\n ? InferCheckedBlobRef<TOptions> | LegacyBlobRef\n : { allowLegacy: boolean } extends TOptions\n ? InferCheckedBlobRef<TOptions> | LegacyBlobRef\n : InferCheckedBlobRef<TOptions>\n\n/**\n * Generator that enumerates all {@link BlobRef}s (and, optionally,\n * {@link LegacyBlobRef}s) found within a {@link LexValue}.\n *\n * Performs a deep traversal of the input value, yielding any blob references\n * found. This is useful for extracting all media references from a record.\n *\n * @param input - The LexValue to search for blob references\n * @param options - Optional configuration for the enumeration\n * @yields Each blob reference found in the input\n *\n * @example\n * ```typescript\n * import { enumBlobRefs } from '@atproto/lex-data'\n *\n * const record = {\n * text: 'Hello',\n * images: [\n * { $type: 'blob', mimeType: 'image/jpeg', ref: cid1, size: 1000 },\n * { $type: 'blob', mimeType: 'image/png', ref: cid2, size: 2000 }\n * ]\n * }\n *\n * for (const blobRef of enumBlobRefs(record)) {\n * console.log(blobRef.mimeType, blobRef.size)\n * }\n *\n * // Include legacy blob references\n * for (const ref of enumBlobRefs(record, { allowLegacy: true })) {\n * // ref may be BlobRef or LegacyBlobRef\n * }\n * ```\n */\nexport function enumBlobRefs(\n input: LexValue,\n): Generator<BlobRef<RawCid>, void, unknown>\nexport function enumBlobRefs<TOptions extends EnumBlobRefsOptions>(\n input: LexValue,\n options: TOptions,\n): Generator<InferEnumBlobRefs<TOptions>, void, unknown>\nexport function enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions,\n): Generator<BlobRef | LegacyBlobRef, void, unknown>\nexport function* enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions,\n): Generator<BlobRef | LegacyBlobRef, void, unknown> {\n // LegacyBlobRef not included by default\n const includeLegacy = options?.allowLegacy === true\n\n // Using a stack to avoid recursion depth issues.\n const stack: LexValue[] = [input]\n\n // Since we are using a stack, we could end-up in an infinite loop with cyclic\n // structures. Cyclic structures are not valid LexValues and should, thus,\n // never occur, but let's be safe.\n const visited = new Set<object>()\n\n do {\n const value = stack.pop()!\n\n if (value != null && typeof value === 'object') {\n if (Array.isArray(value)) {\n if (visited.has(value)) continue\n visited.add(value)\n stack.push(...value)\n } else if (isPlainProto(value)) {\n if (visited.has(value)) continue\n visited.add(value)\n if (isBlobRef(value, options)) {\n yield value\n } else if (includeLegacy && isLegacyBlobRef(value)) {\n yield value\n } else {\n for (const v of Object.values(value)) {\n if (v != null) stack.push(v)\n }\n }\n }\n }\n } while (stack.length > 0)\n\n // Optimization: ease GC's work\n visited.clear()\n}\n"]}
1
+ {"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"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","sourcesContent":["import { Cid, RawCid, ifCid, validateCidString } from './cid.js'\nimport { LexValue } from './lex.js'\nimport { isPlainObject, isPlainProto } from './object.js'\n\n// Number.isSafeInteger is actually safe to use with non-number values, so we\n// can use it as a type guard.\nconst isSafeInteger = Number.isSafeInteger as (v: unknown) => v is number\n\n/**\n * Reference to binary data (like images, videos, etc.) in the AT Protocol data model.\n *\n * A BlobRef is a {@link LexMap} with a specific structure that identifies binary\n * content by its content hash (CID), along with metadata about the content type\n * and size.\n *\n * @typeParam Ref - The type of CID reference, defaults to any {@link Cid}\n *\n * @example\n * ```typescript\n * import type { BlobRef } from '@atproto/lex-data'\n *\n * const imageRef: BlobRef = {\n * $type: 'blob',\n * mimeType: 'image/jpeg',\n * ref: cid, // CID of the blob content\n * size: 12345\n * }\n * ```\n *\n * @see {@link isBlobRef} to check if a value is a valid BlobRef\n * @see {@link LegacyBlobRef} for the older blob reference format\n */\nexport type BlobRef<Ref extends Cid = Cid> = {\n $type: 'blob'\n mimeType: string\n ref: Ref\n size: number\n}\n\n/**\n * Options for validating a {@link BlobRef}.\n */\nexport type BlobRefCheckOptions = {\n /**\n * If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing\n * any valid CID. Otherwise, validates that the CID is v1, uses the raw\n * multicodec, and has a sha256 multihash.\n *\n * @default true\n */\n strict?: boolean\n}\n\n/**\n * Infers the BlobRef type based on the check options.\n *\n * @typeParam TOptions - The options used for checking\n */\nexport type InferCheckedBlobRef<TOptions extends BlobRefCheckOptions> =\n TOptions extends { strict: false }\n ? BlobRef\n : { strict: boolean } extends TOptions\n ? BlobRef\n : BlobRef<RawCid>\n\n/**\n * Type guard to check if a value is a valid {@link BlobRef}.\n *\n * Validates the structure of the input including:\n * - `$type` must be `'blob'`\n * - `mimeType` must be a valid MIME type string (containing '/')\n * - `size` must be a non-negative safe integer\n * - `ref` must be a valid CID (strict validation by default)\n *\n * @param input - The value to check\n * @param options - Optional validation options\n * @returns `true` if the input is a valid BlobRef\n *\n * @example\n * ```typescript\n * import { isBlobRef } from '@atproto/lex-data'\n *\n * if (isBlobRef(data)) {\n * console.log(data.mimeType) // e.g., 'image/jpeg'\n * console.log(data.size) // e.g., 12345\n * }\n *\n * // Allow any valid CID (not just raw CIDs)\n * if (isBlobRef(data, { strict: false })) {\n * // ...\n * }\n * ```\n */\nexport function isBlobRef(input: unknown): input is BlobRef<RawCid>\nexport function isBlobRef<TOptions extends BlobRefCheckOptions>(\n input: unknown,\n options: TOptions,\n): input is InferCheckedBlobRef<TOptions>\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is BlobRef\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefCheckOptions,\n): input is BlobRef {\n if (!isPlainObject(input)) {\n return false\n }\n\n if (input?.$type !== 'blob') {\n return false\n }\n\n const { mimeType, size, ref } = input\n // @NOTE Very basic mime validation\n if (typeof mimeType !== 'string' || !mimeType.includes('/')) {\n return false\n }\n\n if (size === -1 && options?.strict === false) {\n // In non-strict mode, allow size to be -1 to accommodate legacy blob refs\n // that don't include size information.\n } else if (!isSafeInteger(size) || size < 0) {\n return false\n }\n\n if (typeof ref !== 'object' || ref === null) {\n return false\n }\n\n for (const key in input) {\n if (\n key !== '$type' &&\n key !== 'mimeType' &&\n key !== 'ref' &&\n key !== 'size'\n ) {\n return false\n }\n }\n\n const cid = ifCid(\n ref,\n // Strict unless explicitly disabled\n options?.strict === false ? undefined : { flavor: 'raw' },\n )\n if (!cid) {\n return false\n }\n\n return true\n}\n\n/**\n * Legacy format for blob references used in older AT Protocol data.\n *\n * This is the older format that stores the CID as a string rather than\n * as a structured CID object. New code should use {@link BlobRef} instead.\n *\n * @example\n * ```typescript\n * import type { LegacyBlobRef } from '@atproto/lex-data'\n *\n * const legacyRef: LegacyBlobRef = {\n * cid: 'bafyreib...',\n * mimeType: 'image/jpeg'\n * }\n * ```\n *\n * @see {@link isLegacyBlobRef} to check if a value is a LegacyBlobRef\n * @see {@link BlobRef} for the current blob reference format\n * @deprecated Use {@link BlobRef} for new code\n */\nexport type LegacyBlobRef = {\n cid: string\n mimeType: string\n}\n\n/**\n * Type guard to check if a value is a valid {@link LegacyBlobRef}.\n *\n * Validates the structure of the input:\n * - `cid` must be a valid CID string\n * - `mimeType` must be a non-empty string\n * - No additional properties allowed\n *\n * @param input - The value to check\n * @returns `true` if the input is a valid LegacyBlobRef\n *\n * @example\n * ```typescript\n * import { isLegacyBlobRef } from '@atproto/lex-data'\n *\n * if (isLegacyBlobRef(data)) {\n * console.log(data.cid) // CID as string\n * console.log(data.mimeType) // e.g., 'image/jpeg'\n * }\n * ```\n *\n * @see {@link isBlobRef} for checking the current blob reference format\n */\nexport function isLegacyBlobRef(input: unknown): input is LegacyBlobRef {\n if (!isPlainObject(input)) {\n return false\n }\n\n const { cid, mimeType } = input\n if (typeof cid !== 'string') {\n return false\n }\n\n if (typeof mimeType !== 'string' || mimeType.length === 0) {\n return false\n }\n\n for (const key in input) {\n if (key !== 'cid' && key !== 'mimeType') {\n return false\n }\n }\n\n if (!validateCidString(cid)) {\n return false\n }\n\n return true\n}\n\n/**\n * Options for enumerating blob references within a {@link LexValue}.\n */\nexport type EnumBlobRefsOptions = BlobRefCheckOptions & {\n /**\n * If `true`, also yields {@link LegacyBlobRef} objects in addition to\n * {@link BlobRef} objects.\n *\n * @default false\n */\n allowLegacy?: boolean\n}\n\n/**\n * Infers the yielded type of {@link enumBlobRefs} based on options.\n *\n * @typeParam TOptions - The options used for enumeration\n */\nexport type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> =\n TOptions extends { allowLegacy: true }\n ? InferCheckedBlobRef<TOptions> | LegacyBlobRef\n : { allowLegacy: boolean } extends TOptions\n ? InferCheckedBlobRef<TOptions> | LegacyBlobRef\n : InferCheckedBlobRef<TOptions>\n\n/**\n * Generator that enumerates all {@link BlobRef}s (and, optionally,\n * {@link LegacyBlobRef}s) found within a {@link LexValue}.\n *\n * Performs a deep traversal of the input value, yielding any blob references\n * found. This is useful for extracting all media references from a record.\n *\n * @param input - The LexValue to search for blob references\n * @param options - Optional configuration for the enumeration\n * @yields Each blob reference found in the input\n *\n * @example\n * ```typescript\n * import { enumBlobRefs } from '@atproto/lex-data'\n *\n * const record = {\n * text: 'Hello',\n * images: [\n * { $type: 'blob', mimeType: 'image/jpeg', ref: cid1, size: 1000 },\n * { $type: 'blob', mimeType: 'image/png', ref: cid2, size: 2000 }\n * ]\n * }\n *\n * for (const blobRef of enumBlobRefs(record)) {\n * console.log(blobRef.mimeType, blobRef.size)\n * }\n *\n * // Include legacy blob references\n * for (const ref of enumBlobRefs(record, { allowLegacy: true })) {\n * // ref may be BlobRef or LegacyBlobRef\n * }\n * ```\n */\nexport function enumBlobRefs(\n input: LexValue,\n): Generator<BlobRef<RawCid>, void, unknown>\nexport function enumBlobRefs<TOptions extends EnumBlobRefsOptions>(\n input: LexValue,\n options: TOptions,\n): Generator<InferEnumBlobRefs<TOptions>, void, unknown>\nexport function enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions,\n): Generator<BlobRef | LegacyBlobRef, void, unknown>\nexport function* enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions,\n): Generator<BlobRef | LegacyBlobRef, void, unknown> {\n // LegacyBlobRef not included by default\n const includeLegacy = options?.allowLegacy === true\n\n // Using a stack to avoid recursion depth issues.\n const stack: LexValue[] = [input]\n\n // Since we are using a stack, we could end-up in an infinite loop with cyclic\n // structures. Cyclic structures are not valid LexValues and should, thus,\n // never occur, but let's be safe.\n const visited = new Set<object>()\n\n do {\n const value = stack.pop()!\n\n if (value != null && typeof value === 'object') {\n if (Array.isArray(value)) {\n if (visited.has(value)) continue\n visited.add(value)\n stack.push(...value)\n } else if (isPlainProto(value)) {\n if (visited.has(value)) continue\n visited.add(value)\n if (isBlobRef(value, options)) {\n yield value\n } else if (includeLegacy && isLegacyBlobRef(value)) {\n yield value\n } else {\n for (const v of Object.values(value)) {\n if (v != null) stack.push(v)\n }\n }\n }\n }\n } while (stack.length > 0)\n\n // Optimization: ease GC's work\n visited.clear()\n}\n"]}
package/dist/cid.d.ts CHANGED
@@ -201,7 +201,7 @@ export type CheckCidOptions = {
201
201
  *
202
202
  * @typeParam TOptions - The options used for checking
203
203
  */
204
- export type InferCheckedCid<TOptions> = TOptions extends {
204
+ export type InferCheckedCid<TOptions extends CheckCidOptions> = TOptions extends {
205
205
  flavor: 'raw';
206
206
  } ? RawCid : TOptions extends {
207
207
  flavor: 'cbor';
@@ -229,7 +229,7 @@ export declare function ifCid<TValue>(value: TValue, options?: CheckCidOptions):
229
229
  * @throws if the input is not a valid {@link Cid}.
230
230
  */
231
231
  export declare function asCid<TValue, TOptions extends CheckCidOptions>(value: TValue, options: TOptions): TValue & InferCheckedCid<TOptions>;
232
- export declare function asCid<TValue>(value: TValue, options?: CheckCidOptions): Cid & TValue;
232
+ export declare function asCid<TValue>(value: TValue, options?: CheckCidOptions): TValue & Cid;
233
233
  /**
234
234
  * Decodes a CID from its binary representation.
235
235
  *
@@ -329,7 +329,7 @@ export declare function cidForRawBytes(bytes: Uint8Array): Promise<RawCid>;
329
329
  *
330
330
  * @param digest - The SHA-256 hash digest (must be 32 bytes)
331
331
  * @returns A RawCid with the given digest
332
- * @throws If the digest length is not 32 bytes
332
+ * @throws If the digest is not a valid SHA-256 hash (not 32 bytes)
333
333
  */
334
334
  export declare function cidForRawHash(digest: Uint8Array): RawCid;
335
335
  //# sourceMappingURL=cid.d.ts.map
package/dist/cid.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cid.d.ts","sourceRoot":"","sources":["../src/cid.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cid.d.ts","sourceRoot":"","sources":["../src/cid.ts"],"names":[],"mappings":"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"}
package/dist/cid.js CHANGED
@@ -24,6 +24,7 @@ const cid_1 = require("multiformats/cid");
24
24
  Object.defineProperty(exports, "CID", { enumerable: true, get: function () { return cid_1.CID; } });
25
25
  const digest_1 = require("multiformats/hashes/digest");
26
26
  const sha2_1 = require("multiformats/hashes/sha2");
27
+ const util_js_1 = require("./lib/util.js");
27
28
  const object_js_1 = require("./object.js");
28
29
  const uint8array_js_1 = require("./uint8array.js");
29
30
  /**
@@ -131,14 +132,14 @@ function isCid(value, options) {
131
132
  return isCidImplementation(value) && checkCid(value, options);
132
133
  }
133
134
  function ifCid(value, options) {
134
- if (isCidImplementation(value) && checkCid(value, options))
135
+ if (isCid(value, options))
135
136
  return value;
136
137
  return null;
137
138
  }
138
139
  function asCid(value, options) {
139
- if (isCidImplementation(value) && checkCid(value, options))
140
+ if (isCid(value, options))
140
141
  return value;
141
- throw new Error('Not a valid CID');
142
+ throw new Error(`Invalid ${options?.flavor ? `${options.flavor} CID` : 'CID'} "${value}"`);
142
143
  }
143
144
  function decodeCid(cidBytes, options) {
144
145
  const cid = cid_1.CID.decode(cidBytes);
@@ -178,7 +179,7 @@ function parseCidSafe(input, options) {
178
179
  */
179
180
  function ensureValidCidString(input, options) {
180
181
  if (!validateCidString(input, options)) {
181
- throw new Error(`Invalid CID string`);
182
+ throw new Error(`Invalid CID string "${input}"`);
182
183
  }
183
184
  }
184
185
  /**
@@ -197,7 +198,7 @@ async function isCidForBytes(cid, bytes) {
197
198
  return multihashEquals(multihash, cid.multihash);
198
199
  }
199
200
  // Don't know how to verify other multihash codes
200
- throw new Error('Unsupported CID multihash');
201
+ throw new Error(`Unsupported CID multihash code: ${(0, util_js_1.toHexString)(cid.multihash.code)}`);
201
202
  }
202
203
  /**
203
204
  * Creates a CID from a multicodec, multihash code, and digest.
@@ -247,12 +248,12 @@ async function cidForRawBytes(bytes) {
247
248
  *
248
249
  * @param digest - The SHA-256 hash digest (must be 32 bytes)
249
250
  * @returns A RawCid with the given digest
250
- * @throws If the digest length is not 32 bytes
251
+ * @throws If the digest is not a valid SHA-256 hash (not 32 bytes)
251
252
  */
252
253
  function cidForRawHash(digest) {
253
254
  // Fool-proofing
254
- if (digest.length !== 32) {
255
- throw new Error(`Invalid SHA-256 hash length: ${digest.length}`);
255
+ if (digest.length !== 0x20) {
256
+ throw new Error(`Invalid SHA-256 hash length: ${(0, util_js_1.toHexString)(digest.length)}`);
256
257
  }
257
258
  return createCid(exports.RAW_DATA_CODEC, sha2_1.sha256.code, digest);
258
259
  }
@@ -270,12 +271,12 @@ function isCidImplementation(value) {
270
271
  const val = value;
271
272
  if (val.version !== 0 && val.version !== 1)
272
273
  return false;
273
- if (!isUint8(val.code))
274
+ if (!(0, util_js_1.isUint8)(val.code))
274
275
  return false;
275
276
  if (!(0, object_js_1.isObject)(val.multihash))
276
277
  return false;
277
278
  const mh = val.multihash;
278
- if (!isUint8(mh.code))
279
+ if (!(0, util_js_1.isUint8)(mh.code))
279
280
  return false;
280
281
  if (!(mh.digest instanceof Uint8Array))
281
282
  return false;
@@ -305,7 +306,4 @@ function isCidImplementation(value) {
305
306
  }
306
307
  }
307
308
  }
308
- function isUint8(val) {
309
- return Number.isInteger(val) && val >= 0 && val < 256;
310
- }
311
309
  //# sourceMappingURL=cid.js.map
package/dist/cid.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cid.js","sourceRoot":"","sources":["../src/cid.ts"],"names":[],"mappings":"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","sourcesContent":["import { CID } from 'multiformats/cid'\nimport { create as createDigest } from 'multiformats/hashes/digest'\nimport { sha256, sha512 } from 'multiformats/hashes/sha2'\nimport { isObject } from './object.js'\nimport { ui8Equals } from './uint8array.js'\n\n/**\n * Codec code that indicates the CID references a CBOR-encoded data structure.\n *\n * Used when encoding structured data in AT Protocol repositories.\n *\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport const CBOR_DATA_CODEC = 0x71\nexport type CBOR_DATA_CODEC = typeof CBOR_DATA_CODEC\n\n/**\n * Codec code that indicates the CID references raw binary data (like media blobs).\n *\n * Used in DASL CIDs for binary blobs like images and media.\n *\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport const RAW_DATA_CODEC = 0x55\nexport type RAW_DATA_CODEC = typeof RAW_DATA_CODEC\n\n/**\n * Hash code that indicates that a CID uses SHA-256.\n */\nexport const SHA256_HASH_CODE = sha256.code\nexport type SHA256_HASH_CODE = typeof SHA256_HASH_CODE\n\n/**\n * Hash code that indicates that a CID uses SHA-512.\n */\nexport const SHA512_HASH_CODE = sha512.code\nexport type SHA512_HASH_CODE = typeof SHA512_HASH_CODE\n\n/**\n * Represent the hash part of a CID, which includes the hash algorithm code and\n * the raw digest bytes.\n *\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport interface Multihash<THashCode extends number = number> {\n /**\n * Code of the hash algorithm (e.g., SHA256_HASH_CODE).\n */\n code: THashCode\n\n /**\n * Raw digest bytes.\n */\n digest: Uint8Array\n}\n\n/**\n * Compares two {@link Multihash} for equality.\n *\n * @param a - First {@link Multihash}\n * @param b - Second {@link Multihash}\n * @returns `true` if both multihashes have the same code and digest\n */\nexport function multihashEquals(a: Multihash, b: Multihash): boolean {\n if (a === b) return true\n return a.code === b.code && ui8Equals(a.digest, b.digest)\n}\n\ndeclare module 'multiformats/cid' {\n /**\n * @deprecated use the {@link Cid} interface from `@atproto/lex-data`, and\n * related helpers ({@link isCid}, {@link ifCid}, {@link asCid},\n * {@link parseCid}, {@link decodeCid}), instead.\n *\n * This is marked as deprecated because we want to discourage direct usage of\n * `multiformats/cid` in dependent packages, and instead have them rely on the\n * {@link Cid} interface from `@atproto/lex-data`. The {@link CID} class from\n * `multiformats` version <10 has compatibility issues with certain TypeScript\n * configuration, which can lead to type errors in dependent packages.\n *\n * We are stuck with version 9 because `@atproto` packages did not drop\n * CommonJS support yet, and multiformats version 10 only supports ES modules.\n *\n * In order to avoid compatibility issues, while preparing for future breaking\n * changes (CID in multiformats v10+ has a slightly different interface), as\n * we update or swap out `multiformats`, `@atproto/lex-data` provides its own\n * stable {@link Cid} interface.\n */\n interface CID {}\n}\n\n// multiformats' CID class is not very portable because:\n//\n// - In dependent packages that use \"moduleResolution\" set to \"node16\",\n// \"nodenext\" or \"bundler\", TypeScript fails to properly resolve the\n// multiformats package when importing CID from @atproto/lex-data. This causes\n// type errors in those packages. This is caused by the fact that the\n// multiformats version <10 (which is the last version that supports CommonJS)\n// uses \"exports\" field in package.json, which do not contain \"types\"\n// entrypoints.\n// https://www.npmjs.com/package/multiformats/v/9.9.0?activeTab=code\n// - By defining our own interface and helper functions, we can have more\n// control over the public API exposed by this package.\n// - It allow us to have a stable interface in case we need to swap out, or\n// eventually update multiformats (should we choose to drop CommonJS support)\n// in the future.\n\n// @NOTE Even though it is not portable, we still re-export CID here so that\n// dependent packages where it can be used, have access to it (instead of\n// importing directly from \"multiformats\" or \"multiformats/cid\").\nexport { /** @deprecated */ CID }\n\n/**\n * Converts a {@link Cid} to a multiformats {@link CID} instance.\n *\n * @deprecated Packages depending on `@atproto/lex-data` should use the\n * {@link Cid} interface instead of relying on `multiformats`'s {@link CID}\n * implementation directly. This is to avoid compatibility issues, and in order\n * to allow better portability, compatibility and future updates.\n */\nexport function asMultiformatsCID<\n TVersion extends 0 | 1 = 0 | 1,\n TCodec extends number = number,\n THashCode extends number = number,\n>(input: Cid<TVersion, TCodec, THashCode>) {\n const cid =\n // Already a multiformats CID instance\n CID.asCID(input) ??\n // Create a new multiformats CID instance\n CID.create(\n input.version,\n input.code,\n createDigest(input.multihash.code, input.multihash.digest),\n )\n\n // @NOTE: the \"satisfies\" operator is used here to ensure that the Cid\n // interface is indeed compatible with multiformats' CID implementation, which\n // allows us to safely rely on multiformats' CID implementation where Cid are\n // needed.\n return cid satisfies Cid as CID & Cid<TVersion, TCodec, THashCode>\n}\n\n/**\n * Content Identifier (CID) for addressing content by its hash.\n *\n * CIDs are self-describing content addresses used throughout AT Protocol for\n * linking to data by its cryptographic hash. This interface provides a\n * stable API that is compatible with the `multiformats` library but avoids\n * direct dependency issues.\n *\n * @typeParam TVersion - CID version (0 or 1)\n * @typeParam TCodec - Multicodec content type code\n * @typeParam THashCode - Multihash algorithm code\n *\n * @example\n * ```typescript\n * import type { Cid } from '@atproto/lex-data'\n * import { parseCid, isCid } from '@atproto/lex-data'\n *\n * // Parse a CID from a string\n * const cid: Cid = parseCid('bafyreib...')\n *\n * // Check if a value is a CID\n * if (isCid(value)) {\n * console.log(cid.toString())\n * }\n * ```\n *\n * @see {@link isCid} to check if a value is a valid CID\n * @see {@link parseCid} to parse a CID from a string\n * @see {@link decodeCid} to decode a CID from bytes\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport interface Cid<\n TVersion extends 0 | 1 = 0 | 1,\n TCodec extends number = number,\n THashCode extends number = number,\n> {\n // @NOTE This interface is compatible with multiformats' CID implementation\n // which we are using under the hood.\n\n /** CID version (0 or 1). AT Protocol uses CIDv1. */\n readonly version: TVersion\n /** Coded (e.g., {@link CBOR_DATA_CODEC}, {@link RAW_DATA_CODEC}). */\n readonly code: TCodec\n /** The multihash containing the hash algorithm and digest. */\n readonly multihash: Multihash<THashCode>\n\n /**\n * Binary representation of the whole CID.\n */\n readonly bytes: Uint8Array\n\n /**\n * Compares this CID with another for equality.\n *\n * @param other - The CID to compare with\n * @returns `true` if the CIDs are equal\n */\n equals(other: Cid): boolean\n\n /**\n * Returns the string representation of this CID (base32 for v1, base58btc for v0).\n */\n toString(): string\n}\n\n/**\n * Represents the CID of raw binary data (like media blobs).\n *\n * The use of {@link SHA256_HASH_CODE} is recommended but not required for raw CIDs.\n *\n * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats AT Protocol Data Model - Link and CID Formats}\n */\nexport type RawCid = Cid<1, RAW_DATA_CODEC>\n\n/**\n * Type guard to check if a CID is a raw binary CID.\n *\n * @param cid - The CID to check\n * @returns `true` if the CID is a version 1 CID with raw multicodec\n */\nexport function isRawCid(cid: Cid): cid is RawCid {\n return cid.version === 1 && cid.code === RAW_DATA_CODEC\n}\n\n/**\n * Represents a DASL compliant CID.\n *\n * DASL CIDs are version 1 CIDs using either raw or DAG-CBOR multicodec\n * with SHA-256 multihash.\n *\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport type DaslCid = Cid<1, RAW_DATA_CODEC | CBOR_DATA_CODEC, SHA256_HASH_CODE>\n\n/**\n * Type guard to check if a CID is DASL compliant.\n *\n * @param cid - The CID to check\n * @returns `true` if the CID is DASL compliant (v1, raw/dag-cbor, sha256)\n */\nexport function isDaslCid(cid: Cid): cid is DaslCid {\n return (\n cid.version === 1 &&\n (cid.code === RAW_DATA_CODEC || cid.code === CBOR_DATA_CODEC) &&\n cid.multihash.code === SHA256_HASH_CODE &&\n cid.multihash.digest.byteLength === 0x20 // Should always be 32 bytes (256 bits) for SHA-256, but double-checking anyways\n )\n}\n\n/**\n * Represents the CID of AT Protocol DAG-CBOR data (like repository MST nodes).\n *\n * CBOR CIDs are version 1 CIDs using DAG-CBOR multicodec with SHA-256 multihash.\n *\n * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats AT Protocol Data Model - Link and CID Formats}\n */\nexport type CborCid = Cid<1, CBOR_DATA_CODEC, SHA256_HASH_CODE>\n\n/**\n * Type guard to check if a CID is a DAG-CBOR CID.\n *\n * @param cid - The CID to check\n * @returns `true` if the CID is a DAG-CBOR CID (v1, dag-cbor, sha256)\n */\nexport function isCborCid(cid: Cid): cid is CborCid {\n return cid.code === CBOR_DATA_CODEC && isDaslCid(cid)\n}\n\n/**\n * Options for checking CID flavor constraints.\n */\nexport type CheckCidOptions = {\n /**\n * The CID flavor to check for.\n * - `'raw'` - Raw binary CID ({@link RawCid})\n * - `'cbor'` - DAG-CBOR CID ({@link CborCid})\n * - `'dasl'` - DASL compliant CID ({@link DaslCid})\n */\n flavor?: 'raw' | 'cbor' | 'dasl'\n}\n\n/**\n * Infers the CID type based on check options.\n *\n * @typeParam TOptions - The options used for checking\n */\nexport type InferCheckedCid<TOptions> = TOptions extends { flavor: 'raw' }\n ? RawCid\n : TOptions extends { flavor: 'cbor' }\n ? CborCid\n : Cid\n\n/**\n * Type guard to check whether a {@link Cid} instance meets specific flavor\n * constraints.\n */\nexport function checkCid<TOptions extends CheckCidOptions>(\n cid: Cid,\n options: TOptions,\n): cid is InferCheckedCid<TOptions>\nexport function checkCid(cid: Cid, options?: CheckCidOptions): boolean\nexport function checkCid(cid: Cid, options?: CheckCidOptions): boolean {\n switch (options?.flavor) {\n case undefined:\n return true\n case 'cbor':\n return isCborCid(cid)\n case 'dasl':\n return isDaslCid(cid)\n case 'raw':\n return isRawCid(cid)\n default:\n throw new TypeError(`Unknown CID flavor: ${options?.flavor}`)\n }\n}\n\n/**\n * Type guard to check whether a value is a valid {@link Cid} instance,\n * optionally checking for specific flavor constraints.\n */\nexport function isCid<TOptions extends CheckCidOptions>(\n value: unknown,\n options: TOptions,\n): value is InferCheckedCid<TOptions>\nexport function isCid(value: unknown, options?: CheckCidOptions): value is Cid\nexport function isCid(value: unknown, options?: CheckCidOptions): value is Cid {\n return isCidImplementation(value) && checkCid(value, options)\n}\n\n/**\n * Returns the input value as a {@link Cid} if it is valid, or `null` otherwise.\n */\nexport function ifCid<TValue, TOptions extends CheckCidOptions>(\n value: unknown,\n options: TOptions,\n): (TValue & InferCheckedCid<TOptions>) | null\nexport function ifCid<TValue>(\n value: TValue,\n options?: CheckCidOptions,\n): (TValue & Cid) | null\nexport function ifCid(value: unknown, options?: CheckCidOptions): Cid | null {\n if (isCidImplementation(value) && checkCid(value, options)) return value\n return null\n}\n\n/**\n * Returns the input value as a {@link Cid} if it is valid.\n *\n * @throws if the input is not a valid {@link Cid}.\n */\nexport function asCid<TValue, TOptions extends CheckCidOptions>(\n value: TValue,\n options: TOptions,\n): TValue & InferCheckedCid<TOptions>\nexport function asCid<TValue>(\n value: TValue,\n options?: CheckCidOptions,\n): Cid & TValue\nexport function asCid(value: unknown, options?: CheckCidOptions): Cid {\n if (isCidImplementation(value) && checkCid(value, options)) return value\n throw new Error('Not a valid CID')\n}\n\n/**\n * Decodes a CID from its binary representation.\n *\n * @see {@link https://dasl.ing/cid.html DASL-CIDs}\n * @throws if the input do not represent a valid DASL {@link Cid}\n */\nexport function decodeCid<TOptions extends CheckCidOptions>(\n cidBytes: Uint8Array,\n options: TOptions,\n): InferCheckedCid<TOptions>\nexport function decodeCid(cidBytes: Uint8Array, options?: CheckCidOptions): Cid\nexport function decodeCid(\n cidBytes: Uint8Array,\n options?: CheckCidOptions,\n): Cid {\n const cid = CID.decode(cidBytes)\n return asCid(cid, options)\n}\n\n/**\n * Parses a CID string into a Cid object.\n *\n * @throws if the input is not a valid CID string.\n */\nexport function parseCid<TOptions extends CheckCidOptions>(\n input: string,\n options: TOptions,\n): InferCheckedCid<TOptions>\nexport function parseCid(input: string, options?: CheckCidOptions): Cid\nexport function parseCid(input: string, options?: CheckCidOptions): Cid {\n const cid = CID.parse(input)\n return asCid(cid, options)\n}\n\n/**\n * Validates that a string is a valid CID representation.\n *\n * Unlike {@link parseCid}, this function returns a boolean instead of throwing.\n * It also verifies that the string is the canonical representation of the CID.\n *\n * @param input - The string to validate\n * @param options - Optional flavor constraints\n * @returns `true` if the string is a valid CID\n */\nexport function validateCidString(\n input: string,\n options?: CheckCidOptions,\n): boolean {\n return parseCidSafe(input, options)?.toString() === input\n}\n\n/**\n * Safely parses a CID string, returning `null` on failure instead of throwing.\n *\n * @param input - The string to parse\n * @param options - Optional flavor constraints\n * @returns The parsed CID, or `null` if parsing fails\n *\n * @example\n * ```typescript\n * import { parseCidSafe } from '@atproto/lex-data'\n *\n * const cid = parseCidSafe('bafyreib...')\n * if (cid) {\n * console.log(cid.toString())\n * }\n * ```\n */\nexport function parseCidSafe<TOptions extends CheckCidOptions>(\n input: string,\n options: TOptions,\n): InferCheckedCid<TOptions> | null\nexport function parseCidSafe(\n input: string,\n options?: CheckCidOptions,\n): Cid | null\nexport function parseCidSafe(\n input: string,\n options?: CheckCidOptions,\n): Cid | null {\n try {\n return parseCid(input, options)\n } catch {\n return null\n }\n}\n\n/**\n * Ensures that a string is a valid CID representation.\n *\n * @param input - The string to validate\n * @param options - Optional flavor constraints\n * @throws If the string is not a valid CID\n */\nexport function ensureValidCidString(\n input: string,\n options?: CheckCidOptions,\n): void {\n if (!validateCidString(input, options)) {\n throw new Error(`Invalid CID string`)\n }\n}\n\n/**\n * Verifies whether the multihash of a given {@link cid} matches the hash of the provided {@link bytes}.\n * @params cid The CID to match against the bytes.\n * @params bytes The bytes to verify.\n * @returns true if the CID matches the bytes, false otherwise.\n */\nexport async function isCidForBytes(\n cid: Cid,\n bytes: Uint8Array,\n): Promise<boolean> {\n if (cid.multihash.code === sha256.code) {\n const multihash = await sha256.digest(bytes)\n return multihashEquals(multihash, cid.multihash)\n }\n\n if (cid.multihash.code === sha512.code) {\n const multihash = await sha512.digest(bytes)\n return multihashEquals(multihash, cid.multihash)\n }\n\n // Don't know how to verify other multihash codes\n throw new Error('Unsupported CID multihash')\n}\n\n/**\n * Creates a CID from a multicodec, multihash code, and digest.\n *\n * @param code - The multicodec content type code\n * @param multihashCode - The multihash algorithm code\n * @param digest - The raw hash digest bytes\n * @returns A new CIDv1 instance\n *\n * @example\n * ```typescript\n * import { createCid, RAW_DATA_CODEC, SHA256_HASH_CODE } from '@atproto/lex-data'\n *\n * const cid = createCid(RAW_DATA_CODEC, SHA256_HASH_CODE, hashDigest)\n * ```\n */\nexport function createCid<TCodec extends number, THashCode extends number>(\n code: TCodec,\n multihashCode: THashCode,\n digest: Uint8Array,\n) {\n const cid: Cid = CID.createV1(code, createDigest(multihashCode, digest))\n return cid as Cid<1, TCodec, THashCode>\n}\n\n/**\n * Creates a DAG-CBOR CID for the given CBOR bytes.\n *\n * Computes the SHA-256 hash of the bytes and creates a CIDv1 with DAG-CBOR multicodec.\n *\n * @param bytes - The CBOR-encoded bytes to hash\n * @returns A promise that resolves to the CborCid\n */\nexport async function cidForCbor(bytes: Uint8Array): Promise<CborCid> {\n const multihash = await sha256.digest(bytes)\n return CID.createV1(CBOR_DATA_CODEC, multihash) as CborCid\n}\n\n/**\n * Creates a raw CID for the given binary bytes.\n *\n * Computes the SHA-256 hash of the bytes and creates a CIDv1 with raw multicodec.\n *\n * @param bytes - The raw binary bytes to hash\n * @returns A promise that resolves to the RawCid\n */\nexport async function cidForRawBytes(bytes: Uint8Array): Promise<RawCid> {\n const multihash = await sha256.digest(bytes)\n return CID.createV1(RAW_DATA_CODEC, multihash) as RawCid\n}\n\n/**\n * Creates a raw CID from an existing SHA-256 hash digest.\n *\n * @param digest - The SHA-256 hash digest (must be 32 bytes)\n * @returns A RawCid with the given digest\n * @throws If the digest length is not 32 bytes\n */\nexport function cidForRawHash(digest: Uint8Array): RawCid {\n // Fool-proofing\n if (digest.length !== 32) {\n throw new Error(`Invalid SHA-256 hash length: ${digest.length}`)\n }\n return createCid(RAW_DATA_CODEC, sha256.code, digest)\n}\n\nfunction isCidImplementation(value: unknown): value is Cid {\n if (CID.asCID(value)) {\n // CIDs created using older multiformats versions did not have a \"bytes\"\n // property.\n return (value as { bytes?: Uint8Array }).bytes != null\n } else {\n // Unknown implementation, do a structural check\n try {\n if (!isObject(value)) return false\n\n const val = value as Record<string, unknown>\n if (val.version !== 0 && val.version !== 1) return false\n if (!isUint8(val.code)) return false\n\n if (!isObject(val.multihash)) return false\n const mh = val.multihash as Record<string, unknown>\n if (!isUint8(mh.code)) return false\n if (!(mh.digest instanceof Uint8Array)) return false\n\n // Ensure that the bytes array is consistent with other properties\n if (!(val.bytes instanceof Uint8Array)) return false\n if (val.bytes[0] !== val.version) return false\n if (val.bytes[1] !== val.code) return false\n if (val.bytes[2] !== mh.code) return false\n if (val.bytes[3] !== mh.digest.length) return false\n if (val.bytes.length !== 4 + mh.digest.length) return false\n if (!ui8Equals(val.bytes.subarray(4), mh.digest)) return false\n\n if (typeof val.equals !== 'function') return false\n if (val.equals(val) !== true) return false\n\n return true\n } catch {\n return false\n }\n }\n}\n\nfunction isUint8(val: unknown): val is number {\n return Number.isInteger(val) && (val as number) >= 0 && (val as number) < 256\n}\n"]}
1
+ {"version":3,"file":"cid.js","sourceRoot":"","sources":["../src/cid.ts"],"names":[],"mappings":"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","sourcesContent":["import { CID } from 'multiformats/cid'\nimport { create as createDigest } from 'multiformats/hashes/digest'\nimport { sha256, sha512 } from 'multiformats/hashes/sha2'\nimport { isUint8, toHexString } from './lib/util.js'\nimport { isObject } from './object.js'\nimport { ui8Equals } from './uint8array.js'\n\n/**\n * Codec code that indicates the CID references a CBOR-encoded data structure.\n *\n * Used when encoding structured data in AT Protocol repositories.\n *\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport const CBOR_DATA_CODEC = 0x71\nexport type CBOR_DATA_CODEC = typeof CBOR_DATA_CODEC\n\n/**\n * Codec code that indicates the CID references raw binary data (like media blobs).\n *\n * Used in DASL CIDs for binary blobs like images and media.\n *\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport const RAW_DATA_CODEC = 0x55\nexport type RAW_DATA_CODEC = typeof RAW_DATA_CODEC\n\n/**\n * Hash code that indicates that a CID uses SHA-256.\n */\nexport const SHA256_HASH_CODE = sha256.code\nexport type SHA256_HASH_CODE = typeof SHA256_HASH_CODE\n\n/**\n * Hash code that indicates that a CID uses SHA-512.\n */\nexport const SHA512_HASH_CODE = sha512.code\nexport type SHA512_HASH_CODE = typeof SHA512_HASH_CODE\n\n/**\n * Represent the hash part of a CID, which includes the hash algorithm code and\n * the raw digest bytes.\n *\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport interface Multihash<THashCode extends number = number> {\n /**\n * Code of the hash algorithm (e.g., SHA256_HASH_CODE).\n */\n code: THashCode\n\n /**\n * Raw digest bytes.\n */\n digest: Uint8Array\n}\n\n/**\n * Compares two {@link Multihash} for equality.\n *\n * @param a - First {@link Multihash}\n * @param b - Second {@link Multihash}\n * @returns `true` if both multihashes have the same code and digest\n */\nexport function multihashEquals(a: Multihash, b: Multihash): boolean {\n if (a === b) return true\n return a.code === b.code && ui8Equals(a.digest, b.digest)\n}\n\ndeclare module 'multiformats/cid' {\n /**\n * @deprecated use the {@link Cid} interface from `@atproto/lex-data`, and\n * related helpers ({@link isCid}, {@link ifCid}, {@link asCid},\n * {@link parseCid}, {@link decodeCid}), instead.\n *\n * This is marked as deprecated because we want to discourage direct usage of\n * `multiformats/cid` in dependent packages, and instead have them rely on the\n * {@link Cid} interface from `@atproto/lex-data`. The {@link CID} class from\n * `multiformats` version <10 has compatibility issues with certain TypeScript\n * configuration, which can lead to type errors in dependent packages.\n *\n * We are stuck with version 9 because `@atproto` packages did not drop\n * CommonJS support yet, and multiformats version 10 only supports ES modules.\n *\n * In order to avoid compatibility issues, while preparing for future breaking\n * changes (CID in multiformats v10+ has a slightly different interface), as\n * we update or swap out `multiformats`, `@atproto/lex-data` provides its own\n * stable {@link Cid} interface.\n */\n interface CID {}\n}\n\n// multiformats' CID class is not very portable because:\n//\n// - In dependent packages that use \"moduleResolution\" set to \"node16\",\n// \"nodenext\" or \"bundler\", TypeScript fails to properly resolve the\n// multiformats package when importing CID from @atproto/lex-data. This causes\n// type errors in those packages. This is caused by the fact that the\n// multiformats version <10 (which is the last version that supports CommonJS)\n// uses \"exports\" field in package.json, which do not contain \"types\"\n// entrypoints.\n// https://www.npmjs.com/package/multiformats/v/9.9.0?activeTab=code\n// - By defining our own interface and helper functions, we can have more\n// control over the public API exposed by this package.\n// - It allow us to have a stable interface in case we need to swap out, or\n// eventually update multiformats (should we choose to drop CommonJS support)\n// in the future.\n\n// @NOTE Even though it is not portable, we still re-export CID here so that\n// dependent packages where it can be used, have access to it (instead of\n// importing directly from \"multiformats\" or \"multiformats/cid\").\nexport { /** @deprecated */ CID }\n\n/**\n * Converts a {@link Cid} to a multiformats {@link CID} instance.\n *\n * @deprecated Packages depending on `@atproto/lex-data` should use the\n * {@link Cid} interface instead of relying on `multiformats`'s {@link CID}\n * implementation directly. This is to avoid compatibility issues, and in order\n * to allow better portability, compatibility and future updates.\n */\nexport function asMultiformatsCID<\n TVersion extends 0 | 1 = 0 | 1,\n TCodec extends number = number,\n THashCode extends number = number,\n>(input: Cid<TVersion, TCodec, THashCode>) {\n const cid =\n // Already a multiformats CID instance\n CID.asCID(input) ??\n // Create a new multiformats CID instance\n CID.create(\n input.version,\n input.code,\n createDigest(input.multihash.code, input.multihash.digest),\n )\n\n // @NOTE: the \"satisfies\" operator is used here to ensure that the Cid\n // interface is indeed compatible with multiformats' CID implementation, which\n // allows us to safely rely on multiformats' CID implementation where Cid are\n // needed.\n return cid satisfies Cid as CID & Cid<TVersion, TCodec, THashCode>\n}\n\n/**\n * Content Identifier (CID) for addressing content by its hash.\n *\n * CIDs are self-describing content addresses used throughout AT Protocol for\n * linking to data by its cryptographic hash. This interface provides a\n * stable API that is compatible with the `multiformats` library but avoids\n * direct dependency issues.\n *\n * @typeParam TVersion - CID version (0 or 1)\n * @typeParam TCodec - Multicodec content type code\n * @typeParam THashCode - Multihash algorithm code\n *\n * @example\n * ```typescript\n * import type { Cid } from '@atproto/lex-data'\n * import { parseCid, isCid } from '@atproto/lex-data'\n *\n * // Parse a CID from a string\n * const cid: Cid = parseCid('bafyreib...')\n *\n * // Check if a value is a CID\n * if (isCid(value)) {\n * console.log(cid.toString())\n * }\n * ```\n *\n * @see {@link isCid} to check if a value is a valid CID\n * @see {@link parseCid} to parse a CID from a string\n * @see {@link decodeCid} to decode a CID from bytes\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport interface Cid<\n TVersion extends 0 | 1 = 0 | 1,\n TCodec extends number = number,\n THashCode extends number = number,\n> {\n // @NOTE This interface is compatible with multiformats' CID implementation\n // which we are using under the hood.\n\n /** CID version (0 or 1). AT Protocol uses CIDv1. */\n readonly version: TVersion\n /** Coded (e.g., {@link CBOR_DATA_CODEC}, {@link RAW_DATA_CODEC}). */\n readonly code: TCodec\n /** The multihash containing the hash algorithm and digest. */\n readonly multihash: Multihash<THashCode>\n\n /**\n * Binary representation of the whole CID.\n */\n readonly bytes: Uint8Array\n\n /**\n * Compares this CID with another for equality.\n *\n * @param other - The CID to compare with\n * @returns `true` if the CIDs are equal\n */\n equals(other: Cid): boolean\n\n /**\n * Returns the string representation of this CID (base32 for v1, base58btc for v0).\n */\n toString(): string\n}\n\n/**\n * Represents the CID of raw binary data (like media blobs).\n *\n * The use of {@link SHA256_HASH_CODE} is recommended but not required for raw CIDs.\n *\n * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats AT Protocol Data Model - Link and CID Formats}\n */\nexport type RawCid = Cid<1, RAW_DATA_CODEC>\n\n/**\n * Type guard to check if a CID is a raw binary CID.\n *\n * @param cid - The CID to check\n * @returns `true` if the CID is a version 1 CID with raw multicodec\n */\nexport function isRawCid(cid: Cid): cid is RawCid {\n return cid.version === 1 && cid.code === RAW_DATA_CODEC\n}\n\n/**\n * Represents a DASL compliant CID.\n *\n * DASL CIDs are version 1 CIDs using either raw or DAG-CBOR multicodec\n * with SHA-256 multihash.\n *\n * @see {@link https://dasl.ing/cid.html Content IDs (DASL)}\n */\nexport type DaslCid = Cid<1, RAW_DATA_CODEC | CBOR_DATA_CODEC, SHA256_HASH_CODE>\n\n/**\n * Type guard to check if a CID is DASL compliant.\n *\n * @param cid - The CID to check\n * @returns `true` if the CID is DASL compliant (v1, raw/dag-cbor, sha256)\n */\nexport function isDaslCid(cid: Cid): cid is DaslCid {\n return (\n cid.version === 1 &&\n (cid.code === RAW_DATA_CODEC || cid.code === CBOR_DATA_CODEC) &&\n cid.multihash.code === SHA256_HASH_CODE &&\n cid.multihash.digest.byteLength === 0x20 // Should always be 32 bytes (256 bits) for SHA-256, but double-checking anyways\n )\n}\n\n/**\n * Represents the CID of AT Protocol DAG-CBOR data (like repository MST nodes).\n *\n * CBOR CIDs are version 1 CIDs using DAG-CBOR multicodec with SHA-256 multihash.\n *\n * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats AT Protocol Data Model - Link and CID Formats}\n */\nexport type CborCid = Cid<1, CBOR_DATA_CODEC, SHA256_HASH_CODE>\n\n/**\n * Type guard to check if a CID is a DAG-CBOR CID.\n *\n * @param cid - The CID to check\n * @returns `true` if the CID is a DAG-CBOR CID (v1, dag-cbor, sha256)\n */\nexport function isCborCid(cid: Cid): cid is CborCid {\n return cid.code === CBOR_DATA_CODEC && isDaslCid(cid)\n}\n\n/**\n * Options for checking CID flavor constraints.\n */\nexport type CheckCidOptions = {\n /**\n * The CID flavor to check for.\n * - `'raw'` - Raw binary CID ({@link RawCid})\n * - `'cbor'` - DAG-CBOR CID ({@link CborCid})\n * - `'dasl'` - DASL compliant CID ({@link DaslCid})\n */\n flavor?: 'raw' | 'cbor' | 'dasl'\n}\n\n/**\n * Infers the CID type based on check options.\n *\n * @typeParam TOptions - The options used for checking\n */\nexport type InferCheckedCid<TOptions extends CheckCidOptions> =\n TOptions extends { flavor: 'raw' }\n ? RawCid\n : TOptions extends { flavor: 'cbor' }\n ? CborCid\n : Cid\n\n/**\n * Type guard to check whether a {@link Cid} instance meets specific flavor\n * constraints.\n */\nexport function checkCid<TOptions extends CheckCidOptions>(\n cid: Cid,\n options: TOptions,\n): cid is InferCheckedCid<TOptions>\nexport function checkCid(cid: Cid, options?: CheckCidOptions): boolean\nexport function checkCid(cid: Cid, options?: CheckCidOptions): boolean {\n switch (options?.flavor) {\n case undefined:\n return true\n case 'cbor':\n return isCborCid(cid)\n case 'dasl':\n return isDaslCid(cid)\n case 'raw':\n return isRawCid(cid)\n default:\n throw new TypeError(`Unknown CID flavor: ${options?.flavor}`)\n }\n}\n\n/**\n * Type guard to check whether a value is a valid {@link Cid} instance,\n * optionally checking for specific flavor constraints.\n */\nexport function isCid<TOptions extends CheckCidOptions>(\n value: unknown,\n options: TOptions,\n): value is InferCheckedCid<TOptions>\nexport function isCid(value: unknown, options?: CheckCidOptions): value is Cid\nexport function isCid(value: unknown, options?: CheckCidOptions): value is Cid {\n return isCidImplementation(value) && checkCid(value, options)\n}\n\n/**\n * Returns the input value as a {@link Cid} if it is valid, or `null` otherwise.\n */\nexport function ifCid<TValue, TOptions extends CheckCidOptions>(\n value: unknown,\n options: TOptions,\n): (TValue & InferCheckedCid<TOptions>) | null\nexport function ifCid<TValue>(\n value: TValue,\n options?: CheckCidOptions,\n): (TValue & Cid) | null\nexport function ifCid(value: unknown, options?: CheckCidOptions): Cid | null {\n if (isCid(value, options)) return value\n return null\n}\n\n/**\n * Returns the input value as a {@link Cid} if it is valid.\n *\n * @throws if the input is not a valid {@link Cid}.\n */\nexport function asCid<TValue, TOptions extends CheckCidOptions>(\n value: TValue,\n options: TOptions,\n): TValue & InferCheckedCid<TOptions>\nexport function asCid<TValue>(\n value: TValue,\n options?: CheckCidOptions,\n): TValue & Cid\nexport function asCid(value: unknown, options?: CheckCidOptions): Cid {\n if (isCid(value, options)) return value\n throw new Error(\n `Invalid ${options?.flavor ? `${options.flavor} CID` : 'CID'} \"${value}\"`,\n )\n}\n\n/**\n * Decodes a CID from its binary representation.\n *\n * @see {@link https://dasl.ing/cid.html DASL-CIDs}\n * @throws if the input do not represent a valid DASL {@link Cid}\n */\nexport function decodeCid<TOptions extends CheckCidOptions>(\n cidBytes: Uint8Array,\n options: TOptions,\n): InferCheckedCid<TOptions>\nexport function decodeCid(cidBytes: Uint8Array, options?: CheckCidOptions): Cid\nexport function decodeCid(\n cidBytes: Uint8Array,\n options?: CheckCidOptions,\n): Cid {\n const cid = CID.decode(cidBytes)\n return asCid(cid, options)\n}\n\n/**\n * Parses a CID string into a Cid object.\n *\n * @throws if the input is not a valid CID string.\n */\nexport function parseCid<TOptions extends CheckCidOptions>(\n input: string,\n options: TOptions,\n): InferCheckedCid<TOptions>\nexport function parseCid(input: string, options?: CheckCidOptions): Cid\nexport function parseCid(input: string, options?: CheckCidOptions): Cid {\n const cid = CID.parse(input)\n return asCid(cid, options)\n}\n\n/**\n * Validates that a string is a valid CID representation.\n *\n * Unlike {@link parseCid}, this function returns a boolean instead of throwing.\n * It also verifies that the string is the canonical representation of the CID.\n *\n * @param input - The string to validate\n * @param options - Optional flavor constraints\n * @returns `true` if the string is a valid CID\n */\nexport function validateCidString(\n input: string,\n options?: CheckCidOptions,\n): boolean {\n return parseCidSafe(input, options)?.toString() === input\n}\n\n/**\n * Safely parses a CID string, returning `null` on failure instead of throwing.\n *\n * @param input - The string to parse\n * @param options - Optional flavor constraints\n * @returns The parsed CID, or `null` if parsing fails\n *\n * @example\n * ```typescript\n * import { parseCidSafe } from '@atproto/lex-data'\n *\n * const cid = parseCidSafe('bafyreib...')\n * if (cid) {\n * console.log(cid.toString())\n * }\n * ```\n */\nexport function parseCidSafe<TOptions extends CheckCidOptions>(\n input: string,\n options: TOptions,\n): InferCheckedCid<TOptions> | null\nexport function parseCidSafe(\n input: string,\n options?: CheckCidOptions,\n): Cid | null\nexport function parseCidSafe(\n input: string,\n options?: CheckCidOptions,\n): Cid | null {\n try {\n return parseCid(input, options)\n } catch {\n return null\n }\n}\n\n/**\n * Ensures that a string is a valid CID representation.\n *\n * @param input - The string to validate\n * @param options - Optional flavor constraints\n * @throws If the string is not a valid CID\n */\nexport function ensureValidCidString(\n input: string,\n options?: CheckCidOptions,\n): void {\n if (!validateCidString(input, options)) {\n throw new Error(`Invalid CID string \"${input}\"`)\n }\n}\n\n/**\n * Verifies whether the multihash of a given {@link cid} matches the hash of the provided {@link bytes}.\n * @params cid The CID to match against the bytes.\n * @params bytes The bytes to verify.\n * @returns true if the CID matches the bytes, false otherwise.\n */\nexport async function isCidForBytes(\n cid: Cid,\n bytes: Uint8Array,\n): Promise<boolean> {\n if (cid.multihash.code === sha256.code) {\n const multihash = await sha256.digest(bytes)\n return multihashEquals(multihash, cid.multihash)\n }\n\n if (cid.multihash.code === sha512.code) {\n const multihash = await sha512.digest(bytes)\n return multihashEquals(multihash, cid.multihash)\n }\n\n // Don't know how to verify other multihash codes\n throw new Error(\n `Unsupported CID multihash code: ${toHexString(cid.multihash.code)}`,\n )\n}\n\n/**\n * Creates a CID from a multicodec, multihash code, and digest.\n *\n * @param code - The multicodec content type code\n * @param multihashCode - The multihash algorithm code\n * @param digest - The raw hash digest bytes\n * @returns A new CIDv1 instance\n *\n * @example\n * ```typescript\n * import { createCid, RAW_DATA_CODEC, SHA256_HASH_CODE } from '@atproto/lex-data'\n *\n * const cid = createCid(RAW_DATA_CODEC, SHA256_HASH_CODE, hashDigest)\n * ```\n */\nexport function createCid<TCodec extends number, THashCode extends number>(\n code: TCodec,\n multihashCode: THashCode,\n digest: Uint8Array,\n) {\n const cid: Cid = CID.createV1(code, createDigest(multihashCode, digest))\n return cid as Cid<1, TCodec, THashCode>\n}\n\n/**\n * Creates a DAG-CBOR CID for the given CBOR bytes.\n *\n * Computes the SHA-256 hash of the bytes and creates a CIDv1 with DAG-CBOR multicodec.\n *\n * @param bytes - The CBOR-encoded bytes to hash\n * @returns A promise that resolves to the CborCid\n */\nexport async function cidForCbor(bytes: Uint8Array): Promise<CborCid> {\n const multihash = await sha256.digest(bytes)\n return CID.createV1(CBOR_DATA_CODEC, multihash) as CborCid\n}\n\n/**\n * Creates a raw CID for the given binary bytes.\n *\n * Computes the SHA-256 hash of the bytes and creates a CIDv1 with raw multicodec.\n *\n * @param bytes - The raw binary bytes to hash\n * @returns A promise that resolves to the RawCid\n */\nexport async function cidForRawBytes(bytes: Uint8Array): Promise<RawCid> {\n const multihash = await sha256.digest(bytes)\n return CID.createV1(RAW_DATA_CODEC, multihash) as RawCid\n}\n\n/**\n * Creates a raw CID from an existing SHA-256 hash digest.\n *\n * @param digest - The SHA-256 hash digest (must be 32 bytes)\n * @returns A RawCid with the given digest\n * @throws If the digest is not a valid SHA-256 hash (not 32 bytes)\n */\nexport function cidForRawHash(digest: Uint8Array): RawCid {\n // Fool-proofing\n if (digest.length !== 0x20) {\n throw new Error(\n `Invalid SHA-256 hash length: ${toHexString(digest.length)}`,\n )\n }\n return createCid(RAW_DATA_CODEC, sha256.code, digest)\n}\n\nfunction isCidImplementation(value: unknown): value is Cid {\n if (CID.asCID(value)) {\n // CIDs created using older multiformats versions did not have a \"bytes\"\n // property.\n return (value as { bytes?: Uint8Array }).bytes != null\n } else {\n // Unknown implementation, do a structural check\n try {\n if (!isObject(value)) return false\n\n const val = value as Record<string, unknown>\n if (val.version !== 0 && val.version !== 1) return false\n if (!isUint8(val.code)) return false\n\n if (!isObject(val.multihash)) return false\n const mh = val.multihash as Record<string, unknown>\n if (!isUint8(mh.code)) return false\n if (!(mh.digest instanceof Uint8Array)) return false\n\n // Ensure that the bytes array is consistent with other properties\n if (!(val.bytes instanceof Uint8Array)) return false\n if (val.bytes[0] !== val.version) return false\n if (val.bytes[1] !== val.code) return false\n if (val.bytes[2] !== mh.code) return false\n if (val.bytes[3] !== mh.digest.length) return false\n if (val.bytes.length !== 4 + mh.digest.length) return false\n if (!ui8Equals(val.bytes.subarray(4), mh.digest)) return false\n\n if (typeof val.equals !== 'function') return false\n if (val.equals(val) !== true) return false\n\n return true\n } catch {\n return false\n }\n }\n}\n"]}
@@ -37,37 +37,17 @@ export type LexErrorData<N extends LexErrorCode = LexErrorCode> = {
37
37
  /**
38
38
  * Error class for Lexicon-related errors.
39
39
  *
40
- * LexError extends the standard JavaScript {@link Error} with AT Protocol-specific
41
- * functionality including:
42
- * - An error code for programmatic error handling
43
- * - JSON serialization for API responses
44
- * - HTTP Response generation
40
+ * LexError extends the standard JavaScript {@link Error} with AT
41
+ * Protocol-specific functionality including an `error` code property and
42
+ * methods for representation as (XRPC) error responses payloads.
45
43
  *
46
44
  * @typeParam N - The specific error code type
47
- *
48
- * @example
49
- * ```typescript
50
- * import { LexError } from '@atproto/lex-data'
51
- *
52
- * // Throw a Lexicon error
53
- * throw new LexError('InvalidRequest', 'Missing required field')
54
- *
55
- * // Create and serialize
56
- * const error = new LexError('NotFound', 'Record not found')
57
- * console.log(error.toJSON())
58
- * // { error: 'NotFound', message: 'Record not found' }
59
- *
60
- * // Return as HTTP response
61
- * return error.toResponse() // 400 Bad Request with JSON body
62
- * ```
63
45
  */
64
46
  export declare class LexError<N extends LexErrorCode = LexErrorCode> extends Error {
65
47
  readonly error: N;
66
48
  name: string;
67
49
  /**
68
- * Creates a new LexError.
69
- *
70
- * @param error - The error code identifying the type of error
50
+ * @param error - The error code identifying the type of error, typically used in XRPC error payloads
71
51
  * @param message - Optional human-readable error message
72
52
  * @param options - Standard Error options (e.g., cause)
73
53
  */
@@ -76,22 +56,15 @@ export declare class LexError<N extends LexErrorCode = LexErrorCode> extends Err
76
56
  /**
77
57
  * Returns a string representation of this error.
78
58
  *
79
- * @returns A formatted string: "LexError: [ERROR_CODE] message"
59
+ * @returns A formatted string: "LexErrorClass: [MyErrorCode] My message"
80
60
  */
81
61
  toString(): string;
82
62
  /**
83
63
  * Converts this error to a JSON-serializable object.
84
64
  *
85
65
  * @returns The error data suitable for JSON serialization
66
+ * @note The `error` generic is *not* constrained to {@link N} to allow subclasses to override the error code type.
86
67
  */
87
- toJSON(): LexErrorData<N>;
88
- /**
89
- * Converts this error to an HTTP Response for downstream clients.
90
- *
91
- * Returns a 400 Bad Request response with the JSON-serialized error body.
92
- *
93
- * @returns A Response object with status 400 and JSON body
94
- */
95
- toResponse(): Response;
68
+ toJSON(): LexErrorData<LexErrorCode>;
96
69
  }
97
70
  //# sourceMappingURL=lex-error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lex-error.d.ts","sourceRoot":"","sources":["../src/lex-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;AAExD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,IAAI;IAChE,oDAAoD;IACpD,KAAK,EAAE,CAAC,CAAA;IACR,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,QAAQ,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,CAAE,SAAQ,KAAK;IAWtE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAVnB,IAAI,SAAa;IAEjB;;;;;;OAMG;gBAEQ,KAAK,EAAE,CAAC,EACjB,OAAO,CAAC,EAAE,MAAM,EAAE,gDAAgD;IAClE,OAAO,CAAC,EAAE,YAAY;IAKxB;;;;OAIG;IACH,QAAQ,IAAI,MAAM;IAIlB;;;;OAIG;IACH,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC;IAKzB;;;;;;OAMG;IACH,UAAU,IAAI,QAAQ;CAGvB"}
1
+ {"version":3,"file":"lex-error.d.ts","sourceRoot":"","sources":["../src/lex-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;AAExD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,IAAI;IAChE,oDAAoD;IACpD,KAAK,EAAE,CAAC,CAAA;IACR,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,qBAAa,QAAQ,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,CAAE,SAAQ,KAAK;IAStE,QAAQ,CAAC,KAAK,EAAE,CAAC;IARnB,IAAI,SAAa;IAEjB;;;;OAIG;gBAEQ,KAAK,EAAE,CAAC,EACjB,OAAO,CAAC,EAAE,MAAM,EAAE,gDAAgD;IAClE,OAAO,CAAC,EAAE,YAAY;IAKxB;;;;OAIG;IACH,QAAQ,IAAI,MAAM;IAIlB;;;;;OAKG;IACH,MAAM,IAAI,YAAY,CAAC,YAAY,CAAC;CAIrC"}
package/dist/lex-error.js CHANGED
@@ -4,37 +4,17 @@ exports.LexError = void 0;
4
4
  /**
5
5
  * Error class for Lexicon-related errors.
6
6
  *
7
- * LexError extends the standard JavaScript {@link Error} with AT Protocol-specific
8
- * functionality including:
9
- * - An error code for programmatic error handling
10
- * - JSON serialization for API responses
11
- * - HTTP Response generation
7
+ * LexError extends the standard JavaScript {@link Error} with AT
8
+ * Protocol-specific functionality including an `error` code property and
9
+ * methods for representation as (XRPC) error responses payloads.
12
10
  *
13
11
  * @typeParam N - The specific error code type
14
- *
15
- * @example
16
- * ```typescript
17
- * import { LexError } from '@atproto/lex-data'
18
- *
19
- * // Throw a Lexicon error
20
- * throw new LexError('InvalidRequest', 'Missing required field')
21
- *
22
- * // Create and serialize
23
- * const error = new LexError('NotFound', 'Record not found')
24
- * console.log(error.toJSON())
25
- * // { error: 'NotFound', message: 'Record not found' }
26
- *
27
- * // Return as HTTP response
28
- * return error.toResponse() // 400 Bad Request with JSON body
29
- * ```
30
12
  */
31
13
  class LexError extends Error {
32
14
  error;
33
15
  name = 'LexError';
34
16
  /**
35
- * Creates a new LexError.
36
- *
37
- * @param error - The error code identifying the type of error
17
+ * @param error - The error code identifying the type of error, typically used in XRPC error payloads
38
18
  * @param message - Optional human-readable error message
39
19
  * @param options - Standard Error options (e.g., cause)
40
20
  */
@@ -46,7 +26,7 @@ class LexError extends Error {
46
26
  /**
47
27
  * Returns a string representation of this error.
48
28
  *
49
- * @returns A formatted string: "LexError: [ERROR_CODE] message"
29
+ * @returns A formatted string: "LexErrorClass: [MyErrorCode] My message"
50
30
  */
51
31
  toString() {
52
32
  return `${this.name}: [${this.error}] ${this.message}`;
@@ -55,21 +35,12 @@ class LexError extends Error {
55
35
  * Converts this error to a JSON-serializable object.
56
36
  *
57
37
  * @returns The error data suitable for JSON serialization
38
+ * @note The `error` generic is *not* constrained to {@link N} to allow subclasses to override the error code type.
58
39
  */
59
40
  toJSON() {
60
41
  const { error, message } = this;
61
42
  return { error, message: message || undefined };
62
43
  }
63
- /**
64
- * Converts this error to an HTTP Response for downstream clients.
65
- *
66
- * Returns a 400 Bad Request response with the JSON-serialized error body.
67
- *
68
- * @returns A Response object with status 400 and JSON body
69
- */
70
- toResponse() {
71
- return Response.json(this.toJSON(), { status: 400 });
72
- }
73
44
  }
74
45
  exports.LexError = LexError;
75
46
  //# sourceMappingURL=lex-error.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lex-error.js","sourceRoot":"","sources":["../src/lex-error.ts"],"names":[],"mappings":";;;AAsCA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAa,QAAgD,SAAQ,KAAK;IAW7D;IAVX,IAAI,GAAG,UAAU,CAAA;IAEjB;;;;;;OAMG;IACH,YACW,KAAQ,EACjB,OAAgB,EAAE,gDAAgD;IAClE,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAJd,UAAK,GAAL,KAAK,CAAG;IAKnB,CAAC;IAED;;;;OAIG;IACH,QAAQ;QACN,OAAO,GAAG,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE,CAAA;IACxD,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QAC/B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,CAAA;IACjD,CAAC;IAED;;;;;;OAMG;IACH,UAAU;QACR,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACtD,CAAC;CACF;AA/CD,4BA+CC","sourcesContent":["/**\n * Error code type for Lexicon errors.\n *\n * Error codes identify the type of error that occurred (e.g., 'InvalidRequest').\n *\n * @example\n * ```typescript\n * import type { LexErrorCode } from '@atproto/lex-data'\n *\n * const errorCode: LexErrorCode = 'InvalidRequest'\n * ```\n */\nexport type LexErrorCode = string & NonNullable<unknown>\n\n/**\n * JSON-serializable error data structure.\n *\n * This is the standard format for error responses in the AT Protocol XRPC protocol.\n *\n * @typeParam N - The specific error code type\n *\n * @example\n * ```typescript\n * import type { LexErrorData } from '@atproto/lex-data'\n *\n * const errorData: LexErrorData = {\n * error: 'InvalidRequest',\n * message: 'Missing required field: handle'\n * }\n * ```\n */\nexport type LexErrorData<N extends LexErrorCode = LexErrorCode> = {\n /** The error code identifying the type of error. */\n error: N\n /** Optional human-readable error message. */\n message?: string\n}\n\n/**\n * Error class for Lexicon-related errors.\n *\n * LexError extends the standard JavaScript {@link Error} with AT Protocol-specific\n * functionality including:\n * - An error code for programmatic error handling\n * - JSON serialization for API responses\n * - HTTP Response generation\n *\n * @typeParam N - The specific error code type\n *\n * @example\n * ```typescript\n * import { LexError } from '@atproto/lex-data'\n *\n * // Throw a Lexicon error\n * throw new LexError('InvalidRequest', 'Missing required field')\n *\n * // Create and serialize\n * const error = new LexError('NotFound', 'Record not found')\n * console.log(error.toJSON())\n * // { error: 'NotFound', message: 'Record not found' }\n *\n * // Return as HTTP response\n * return error.toResponse() // 400 Bad Request with JSON body\n * ```\n */\nexport class LexError<N extends LexErrorCode = LexErrorCode> extends Error {\n name = 'LexError'\n\n /**\n * Creates a new LexError.\n *\n * @param error - The error code identifying the type of error\n * @param message - Optional human-readable error message\n * @param options - Standard Error options (e.g., cause)\n */\n constructor(\n readonly error: N,\n message?: string, // Defaults to empty string in Error constructor\n options?: ErrorOptions,\n ) {\n super(message, options)\n }\n\n /**\n * Returns a string representation of this error.\n *\n * @returns A formatted string: \"LexError: [ERROR_CODE] message\"\n */\n toString(): string {\n return `${this.name}: [${this.error}] ${this.message}`\n }\n\n /**\n * Converts this error to a JSON-serializable object.\n *\n * @returns The error data suitable for JSON serialization\n */\n toJSON(): LexErrorData<N> {\n const { error, message } = this\n return { error, message: message || undefined }\n }\n\n /**\n * Converts this error to an HTTP Response for downstream clients.\n *\n * Returns a 400 Bad Request response with the JSON-serialized error body.\n *\n * @returns A Response object with status 400 and JSON body\n */\n toResponse(): Response {\n return Response.json(this.toJSON(), { status: 400 })\n }\n}\n"]}
1
+ {"version":3,"file":"lex-error.js","sourceRoot":"","sources":["../src/lex-error.ts"],"names":[],"mappings":";;;AAsCA;;;;;;;;GAQG;AACH,MAAa,QAAgD,SAAQ,KAAK;IAS7D;IARX,IAAI,GAAG,UAAU,CAAA;IAEjB;;;;OAIG;IACH,YACW,KAAQ,EACjB,OAAgB,EAAE,gDAAgD;IAClE,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAJd,UAAK,GAAL,KAAK,CAAG;IAKnB,CAAC;IAED;;;;OAIG;IACH,QAAQ;QACN,OAAO,GAAG,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE,CAAA;IACxD,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QAC/B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,CAAA;IACjD,CAAC;CACF;AAnCD,4BAmCC","sourcesContent":["/**\n * Error code type for Lexicon errors.\n *\n * Error codes identify the type of error that occurred (e.g., 'InvalidRequest').\n *\n * @example\n * ```typescript\n * import type { LexErrorCode } from '@atproto/lex-data'\n *\n * const errorCode: LexErrorCode = 'InvalidRequest'\n * ```\n */\nexport type LexErrorCode = string & NonNullable<unknown>\n\n/**\n * JSON-serializable error data structure.\n *\n * This is the standard format for error responses in the AT Protocol XRPC protocol.\n *\n * @typeParam N - The specific error code type\n *\n * @example\n * ```typescript\n * import type { LexErrorData } from '@atproto/lex-data'\n *\n * const errorData: LexErrorData = {\n * error: 'InvalidRequest',\n * message: 'Missing required field: handle'\n * }\n * ```\n */\nexport type LexErrorData<N extends LexErrorCode = LexErrorCode> = {\n /** The error code identifying the type of error. */\n error: N\n /** Optional human-readable error message. */\n message?: string\n}\n\n/**\n * Error class for Lexicon-related errors.\n *\n * LexError extends the standard JavaScript {@link Error} with AT\n * Protocol-specific functionality including an `error` code property and\n * methods for representation as (XRPC) error responses payloads.\n *\n * @typeParam N - The specific error code type\n */\nexport class LexError<N extends LexErrorCode = LexErrorCode> extends Error {\n name = 'LexError'\n\n /**\n * @param error - The error code identifying the type of error, typically used in XRPC error payloads\n * @param message - Optional human-readable error message\n * @param options - Standard Error options (e.g., cause)\n */\n constructor(\n readonly error: N,\n message?: string, // Defaults to empty string in Error constructor\n options?: ErrorOptions,\n ) {\n super(message, options)\n }\n\n /**\n * Returns a string representation of this error.\n *\n * @returns A formatted string: \"LexErrorClass: [MyErrorCode] My message\"\n */\n toString(): string {\n return `${this.name}: [${this.error}] ${this.message}`\n }\n\n /**\n * Converts this error to a JSON-serializable object.\n *\n * @returns The error data suitable for JSON serialization\n * @note The `error` generic is *not* constrained to {@link N} to allow subclasses to override the error code type.\n */\n toJSON(): LexErrorData<LexErrorCode> {\n const { error, message } = this\n return { error, message: message || undefined }\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function toHexString(number: number): string;
2
+ export declare function isUint8(val: unknown): val is number;
3
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/lib/util.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,MAAM,CAEnD"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toHexString = toHexString;
4
+ exports.isUint8 = isUint8;
5
+ function toHexString(number) {
6
+ return `0x${number.toString(16).padStart(2, '0')}`;
7
+ }
8
+ function isUint8(val) {
9
+ return Number.isInteger(val) && val >= 0 && val < 256;
10
+ }
11
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/lib/util.ts"],"names":[],"mappings":";;AAAA,kCAEC;AAED,0BAEC;AAND,SAAgB,WAAW,CAAC,MAAc;IACxC,OAAO,KAAK,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;AACpD,CAAC;AAED,SAAgB,OAAO,CAAC,GAAY;IAClC,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAK,GAAc,IAAI,CAAC,IAAK,GAAc,GAAG,GAAG,CAAA;AAC/E,CAAC","sourcesContent":["export function toHexString(number: number): string {\n return `0x${number.toString(16).padStart(2, '0')}`\n}\n\nexport function isUint8(val: unknown): val is number {\n return Number.isInteger(val) && (val as number) >= 0 && (val as number) < 256\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-data",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "license": "MIT",
5
5
  "description": "Core utilities for AT Lexicons",
6
6
  "keywords": [
package/src/blob.ts CHANGED
@@ -2,6 +2,10 @@ import { Cid, RawCid, ifCid, validateCidString } from './cid.js'
2
2
  import { LexValue } from './lex.js'
3
3
  import { isPlainObject, isPlainProto } from './object.js'
4
4
 
5
+ // Number.isSafeInteger is actually safe to use with non-number values, so we
6
+ // can use it as a type guard.
7
+ const isSafeInteger = Number.isSafeInteger as (v: unknown) => v is number
8
+
5
9
  /**
6
10
  * Reference to binary data (like images, videos, etc.) in the AT Protocol data model.
7
11
  *
@@ -114,7 +118,10 @@ export function isBlobRef(
114
118
  return false
115
119
  }
116
120
 
117
- if (typeof size !== 'number' || size < 0 || !Number.isSafeInteger(size)) {
121
+ if (size === -1 && options?.strict === false) {
122
+ // In non-strict mode, allow size to be -1 to accommodate legacy blob refs
123
+ // that don't include size information.
124
+ } else if (!isSafeInteger(size) || size < 0) {
118
125
  return false
119
126
  }
120
127
 
package/src/cid.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { CID } from 'multiformats/cid'
2
2
  import { create as createDigest } from 'multiformats/hashes/digest'
3
3
  import { sha256, sha512 } from 'multiformats/hashes/sha2'
4
+ import { isUint8, toHexString } from './lib/util.js'
4
5
  import { isObject } from './object.js'
5
6
  import { ui8Equals } from './uint8array.js'
6
7
 
@@ -286,11 +287,12 @@ export type CheckCidOptions = {
286
287
  *
287
288
  * @typeParam TOptions - The options used for checking
288
289
  */
289
- export type InferCheckedCid<TOptions> = TOptions extends { flavor: 'raw' }
290
- ? RawCid
291
- : TOptions extends { flavor: 'cbor' }
292
- ? CborCid
293
- : Cid
290
+ export type InferCheckedCid<TOptions extends CheckCidOptions> =
291
+ TOptions extends { flavor: 'raw' }
292
+ ? RawCid
293
+ : TOptions extends { flavor: 'cbor' }
294
+ ? CborCid
295
+ : Cid
294
296
 
295
297
  /**
296
298
  * Type guard to check whether a {@link Cid} instance meets specific flavor
@@ -341,7 +343,7 @@ export function ifCid<TValue>(
341
343
  options?: CheckCidOptions,
342
344
  ): (TValue & Cid) | null
343
345
  export function ifCid(value: unknown, options?: CheckCidOptions): Cid | null {
344
- if (isCidImplementation(value) && checkCid(value, options)) return value
346
+ if (isCid(value, options)) return value
345
347
  return null
346
348
  }
347
349
 
@@ -357,10 +359,12 @@ export function asCid<TValue, TOptions extends CheckCidOptions>(
357
359
  export function asCid<TValue>(
358
360
  value: TValue,
359
361
  options?: CheckCidOptions,
360
- ): Cid & TValue
362
+ ): TValue & Cid
361
363
  export function asCid(value: unknown, options?: CheckCidOptions): Cid {
362
- if (isCidImplementation(value) && checkCid(value, options)) return value
363
- throw new Error('Not a valid CID')
364
+ if (isCid(value, options)) return value
365
+ throw new Error(
366
+ `Invalid ${options?.flavor ? `${options.flavor} CID` : 'CID'} "${value}"`,
367
+ )
364
368
  }
365
369
 
366
370
  /**
@@ -462,7 +466,7 @@ export function ensureValidCidString(
462
466
  options?: CheckCidOptions,
463
467
  ): void {
464
468
  if (!validateCidString(input, options)) {
465
- throw new Error(`Invalid CID string`)
469
+ throw new Error(`Invalid CID string "${input}"`)
466
470
  }
467
471
  }
468
472
 
@@ -487,7 +491,9 @@ export async function isCidForBytes(
487
491
  }
488
492
 
489
493
  // Don't know how to verify other multihash codes
490
- throw new Error('Unsupported CID multihash')
494
+ throw new Error(
495
+ `Unsupported CID multihash code: ${toHexString(cid.multihash.code)}`,
496
+ )
491
497
  }
492
498
 
493
499
  /**
@@ -545,12 +551,14 @@ export async function cidForRawBytes(bytes: Uint8Array): Promise<RawCid> {
545
551
  *
546
552
  * @param digest - The SHA-256 hash digest (must be 32 bytes)
547
553
  * @returns A RawCid with the given digest
548
- * @throws If the digest length is not 32 bytes
554
+ * @throws If the digest is not a valid SHA-256 hash (not 32 bytes)
549
555
  */
550
556
  export function cidForRawHash(digest: Uint8Array): RawCid {
551
557
  // Fool-proofing
552
- if (digest.length !== 32) {
553
- throw new Error(`Invalid SHA-256 hash length: ${digest.length}`)
558
+ if (digest.length !== 0x20) {
559
+ throw new Error(
560
+ `Invalid SHA-256 hash length: ${toHexString(digest.length)}`,
561
+ )
554
562
  }
555
563
  return createCid(RAW_DATA_CODEC, sha256.code, digest)
556
564
  }
@@ -592,7 +600,3 @@ function isCidImplementation(value: unknown): value is Cid {
592
600
  }
593
601
  }
594
602
  }
595
-
596
- function isUint8(val: unknown): val is number {
597
- return Number.isInteger(val) && (val as number) >= 0 && (val as number) < 256
598
- }
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { LexError } from './lex-error.js'
3
+
4
+ describe(LexError, () => {
5
+ it('stores error code and message', () => {
6
+ const err = new LexError('TestError', 'This is a test error')
7
+ expect(err.error).toBe('TestError')
8
+ expect(err.message).toBe('This is a test error')
9
+ })
10
+
11
+ it('strips empty message in toJSON output', () => {
12
+ const err = new LexError('TestError')
13
+ expect(err.toJSON()).toEqual({ error: 'TestError' })
14
+ })
15
+
16
+ it('includes message in toJSON when present', () => {
17
+ const err = new LexError('TestError', 'details here')
18
+ expect(err.toJSON()).toEqual({
19
+ error: 'TestError',
20
+ message: 'details here',
21
+ })
22
+ })
23
+
24
+ it('formats string output correctly', () => {
25
+ const err = new LexError('TestError', 'This is a test error')
26
+ expect(err.toString()).toBe('LexError: [TestError] This is a test error')
27
+ })
28
+
29
+ it('uses constructor name for the name property', () => {
30
+ const err = new LexError('TestError')
31
+ expect(err.name).toBe('LexError')
32
+ })
33
+
34
+ it('subclasses use their own constructor name', () => {
35
+ class MyCustomError extends LexError {
36
+ name = 'MyCustomError'
37
+ }
38
+ const err = new MyCustomError('CustomCode', 'custom message')
39
+ expect(err.name).toBe('MyCustomError')
40
+ expect(err.toString()).toBe('MyCustomError: [CustomCode] custom message')
41
+ })
42
+
43
+ it('preserves cause option', () => {
44
+ const cause = new Error('original')
45
+ const err = new LexError('TestError', 'wrapped', { cause })
46
+ expect(err.cause).toBe(cause)
47
+ })
48
+
49
+ it('is an instance of Error', () => {
50
+ const err = new LexError('TestError')
51
+ expect(err).toBeInstanceOf(Error)
52
+ expect(err).toBeInstanceOf(LexError)
53
+ })
54
+ })
package/src/lex-error.ts CHANGED
@@ -39,37 +39,17 @@ export type LexErrorData<N extends LexErrorCode = LexErrorCode> = {
39
39
  /**
40
40
  * Error class for Lexicon-related errors.
41
41
  *
42
- * LexError extends the standard JavaScript {@link Error} with AT Protocol-specific
43
- * functionality including:
44
- * - An error code for programmatic error handling
45
- * - JSON serialization for API responses
46
- * - HTTP Response generation
42
+ * LexError extends the standard JavaScript {@link Error} with AT
43
+ * Protocol-specific functionality including an `error` code property and
44
+ * methods for representation as (XRPC) error responses payloads.
47
45
  *
48
46
  * @typeParam N - The specific error code type
49
- *
50
- * @example
51
- * ```typescript
52
- * import { LexError } from '@atproto/lex-data'
53
- *
54
- * // Throw a Lexicon error
55
- * throw new LexError('InvalidRequest', 'Missing required field')
56
- *
57
- * // Create and serialize
58
- * const error = new LexError('NotFound', 'Record not found')
59
- * console.log(error.toJSON())
60
- * // { error: 'NotFound', message: 'Record not found' }
61
- *
62
- * // Return as HTTP response
63
- * return error.toResponse() // 400 Bad Request with JSON body
64
- * ```
65
47
  */
66
48
  export class LexError<N extends LexErrorCode = LexErrorCode> extends Error {
67
49
  name = 'LexError'
68
50
 
69
51
  /**
70
- * Creates a new LexError.
71
- *
72
- * @param error - The error code identifying the type of error
52
+ * @param error - The error code identifying the type of error, typically used in XRPC error payloads
73
53
  * @param message - Optional human-readable error message
74
54
  * @param options - Standard Error options (e.g., cause)
75
55
  */
@@ -84,7 +64,7 @@ export class LexError<N extends LexErrorCode = LexErrorCode> extends Error {
84
64
  /**
85
65
  * Returns a string representation of this error.
86
66
  *
87
- * @returns A formatted string: "LexError: [ERROR_CODE] message"
67
+ * @returns A formatted string: "LexErrorClass: [MyErrorCode] My message"
88
68
  */
89
69
  toString(): string {
90
70
  return `${this.name}: [${this.error}] ${this.message}`
@@ -94,20 +74,10 @@ export class LexError<N extends LexErrorCode = LexErrorCode> extends Error {
94
74
  * Converts this error to a JSON-serializable object.
95
75
  *
96
76
  * @returns The error data suitable for JSON serialization
77
+ * @note The `error` generic is *not* constrained to {@link N} to allow subclasses to override the error code type.
97
78
  */
98
- toJSON(): LexErrorData<N> {
79
+ toJSON(): LexErrorData<LexErrorCode> {
99
80
  const { error, message } = this
100
81
  return { error, message: message || undefined }
101
82
  }
102
-
103
- /**
104
- * Converts this error to an HTTP Response for downstream clients.
105
- *
106
- * Returns a 400 Bad Request response with the JSON-serialized error body.
107
- *
108
- * @returns A Response object with status 400 and JSON body
109
- */
110
- toResponse(): Response {
111
- return Response.json(this.toJSON(), { status: 400 })
112
- }
113
83
  }
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { isUint8, toHexString } from './util.js'
3
+
4
+ describe(toHexString, () => {
5
+ it('converts 0 to 0x00', () => {
6
+ expect(toHexString(0)).toBe('0x00')
7
+ })
8
+
9
+ it('converts single-digit hex values with padding', () => {
10
+ expect(toHexString(1)).toBe('0x01')
11
+ expect(toHexString(15)).toBe('0x0f')
12
+ })
13
+
14
+ it('converts two-digit hex values without extra padding', () => {
15
+ expect(toHexString(16)).toBe('0x10')
16
+ expect(toHexString(255)).toBe('0xff')
17
+ })
18
+
19
+ it('converts larger numbers', () => {
20
+ expect(toHexString(256)).toBe('0x100')
21
+ expect(toHexString(4096)).toBe('0x1000')
22
+ })
23
+ })
24
+
25
+ describe(isUint8, () => {
26
+ it('returns true for valid uint8 values', () => {
27
+ expect(isUint8(0)).toBe(true)
28
+ expect(isUint8(1)).toBe(true)
29
+ expect(isUint8(127)).toBe(true)
30
+ expect(isUint8(255)).toBe(true)
31
+ })
32
+
33
+ it('returns false for values outside uint8 range', () => {
34
+ expect(isUint8(-1)).toBe(false)
35
+ expect(isUint8(256)).toBe(false)
36
+ })
37
+
38
+ it('returns false for non-integer numbers', () => {
39
+ expect(isUint8(1.5)).toBe(false)
40
+ expect(isUint8(0.1)).toBe(false)
41
+ })
42
+
43
+ it('returns false for non-number types', () => {
44
+ expect(isUint8('0')).toBe(false)
45
+ expect(isUint8(null)).toBe(false)
46
+ expect(isUint8(undefined)).toBe(false)
47
+ expect(isUint8(true)).toBe(false)
48
+ })
49
+ })
@@ -0,0 +1,7 @@
1
+ export function toHexString(number: number): string {
2
+ return `0x${number.toString(16).padStart(2, '0')}`
3
+ }
4
+
5
+ export function isUint8(val: unknown): val is number {
6
+ return Number.isInteger(val) && (val as number) >= 0 && (val as number) < 256
7
+ }