@atproto/lex-data 0.0.6 → 0.0.7

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,15 @@
1
1
  # @atproto/lex-data
2
2
 
3
+ ## 0.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [#4512](https://github.com/bluesky-social/atproto/pull/4512) [`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe) Thanks [@matthieusieben](https://github.com/matthieusieben)! - **breaking**: replace `strict` options with `flavor` checking when parsing/decoding/validating `Cid` values
8
+
9
+ - [#4512](https://github.com/bluesky-social/atproto/pull/4512) [`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Expose CID creation and validation utilities
10
+
11
+ - [#4512](https://github.com/bluesky-social/atproto/pull/4512) [`d78484f`](https://github.com/bluesky-social/atproto/commit/d78484f94d8ba1352ec66030115000d515c9dafe) Thanks [@matthieusieben](https://github.com/matthieusieben)! - **breaking**: `asCid` now throws if the input cannot be cast into a `Cid`
12
+
3
13
  ## 0.0.6
4
14
 
5
15
  ### Patch Changes
package/dist/blob.d.ts CHANGED
@@ -1,15 +1,15 @@
1
- import { Cid } from './cid.js';
1
+ import { Cid, RawCid } from './cid.js';
2
2
  import { LexValue } from './lex.js';
3
3
  /**
4
4
  * @note {@link BlobRef} is just a {@link LexMap} with a specific shape.
5
5
  */
6
- export type BlobRef = {
6
+ export type BlobRef<Ref extends Cid = Cid> = {
7
7
  $type: 'blob';
8
8
  mimeType: string;
9
- ref: Cid;
9
+ ref: Ref;
10
10
  size: number;
11
11
  };
12
- export type BlobRefValidationOptions = {
12
+ export type BlobRefCheckOptions = {
13
13
  /**
14
14
  * If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing
15
15
  * any valid CID. Otherwise, validates that the CID is v1, uses the raw
@@ -19,7 +19,14 @@ export type BlobRefValidationOptions = {
19
19
  */
20
20
  strict?: boolean;
21
21
  };
22
- export declare function isBlobRef(input: unknown, options?: BlobRefValidationOptions): input is BlobRef;
22
+ export type InferCheckedBlobRef<TOptions extends BlobRefCheckOptions> = TOptions extends {
23
+ strict: false;
24
+ } ? BlobRef : {
25
+ strict: boolean;
26
+ } extends TOptions ? BlobRef : BlobRef<RawCid>;
27
+ export declare function isBlobRef(input: unknown): input is BlobRef<RawCid>;
28
+ export declare function isBlobRef<TOptions extends BlobRefCheckOptions>(input: unknown, options: TOptions): input is InferCheckedBlobRef<TOptions>;
29
+ export declare function isBlobRef(input: unknown, options?: BlobRefCheckOptions): input is BlobRef;
23
30
  /**
24
31
  * @note {@link LegacyBlobRef} is just a {@link LexMap} with a specific shape.
25
32
  */
@@ -28,21 +35,22 @@ export type LegacyBlobRef = {
28
35
  mimeType: string;
29
36
  };
30
37
  export declare function isLegacyBlobRef(input: unknown): input is LegacyBlobRef;
31
- export type EnumBlobRefsOptions = BlobRefValidationOptions & {
38
+ export type EnumBlobRefsOptions = BlobRefCheckOptions & {
32
39
  /**
33
40
  * @defaults to `false`
34
41
  */
35
42
  allowLegacy?: boolean;
36
43
  };
44
+ export type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> = TOptions extends {
45
+ allowLegacy: true;
46
+ } ? InferCheckedBlobRef<TOptions> | LegacyBlobRef : {
47
+ allowLegacy: boolean;
48
+ } extends TOptions ? InferCheckedBlobRef<TOptions> | LegacyBlobRef : InferCheckedBlobRef<TOptions>;
37
49
  /**
38
50
  * Enumerates all {@link BlobRef}s (and, optionally, {@link LegacyBlobRef}s)
39
51
  * found within a {@link LexValue}.
40
52
  */
41
- export declare function enumBlobRefs(input: LexValue, options: EnumBlobRefsOptions & {
42
- allowLegacy: true;
43
- }): Generator<BlobRef | LegacyBlobRef, void, unknown>;
44
- export declare function enumBlobRefs(input: LexValue, options?: EnumBlobRefsOptions & {
45
- allowLegacy?: false;
46
- }): Generator<BlobRef, void, unknown>;
53
+ export declare function enumBlobRefs(input: LexValue): Generator<BlobRef<RawCid>, void, unknown>;
54
+ export declare function enumBlobRefs<TOptions extends EnumBlobRefsOptions>(input: LexValue, options: TOptions): Generator<InferEnumBlobRefs<TOptions>, void, unknown>;
47
55
  export declare function enumBlobRefs(input: LexValue, options?: EnumBlobRefsOptions): Generator<BlobRef | LegacyBlobRef, void, unknown>;
48
56
  //# sourceMappingURL=blob.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EAKJ,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGnC;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,GAAG,CAAA;IACR,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,wBAAwB,GACjC,KAAK,IAAI,OAAO,CAoDlB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CA2BtE;AAED,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GAAG;IAC3D;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,mBAAmB,GAAG;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GACnD,SAAS,CAAC,OAAO,GAAG,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AACpD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,mBAAmB,GAAG;IAAE,WAAW,CAAC,EAAE,KAAK,CAAA;CAAE,GACtD,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;AACpC,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;AAGnC;;GAEG;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,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,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,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;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAyBtE;AAED,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IACtD;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,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;;;GAGG;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
@@ -31,21 +31,12 @@ function isBlobRef(input, options) {
31
31
  return false;
32
32
  }
33
33
  }
34
- const cid = (0, cid_js_1.asCid)(ref);
34
+ const cid = (0, cid_js_1.ifCid)(ref,
35
+ // Strict unless explicitly disabled
36
+ options?.strict === false ? undefined : { flavor: 'raw' });
35
37
  if (!cid) {
36
38
  return false;
37
39
  }
38
- if (options?.strict !== false) {
39
- if (cid.version !== 1) {
40
- return false;
41
- }
42
- if (cid.code !== cid_js_1.RAW_BIN_MULTICODEC) {
43
- return false;
44
- }
45
- if (cid.multihash.code !== cid_js_1.SHA2_256_MULTIHASH_CODE) {
46
- return false;
47
- }
48
- }
49
40
  return true;
50
41
  }
51
42
  function isLegacyBlobRef(input) {
@@ -64,15 +55,13 @@ function isLegacyBlobRef(input) {
64
55
  return false;
65
56
  }
66
57
  }
67
- try {
68
- (0, cid_js_1.parseCid)(cid);
69
- }
70
- catch {
58
+ if (!(0, cid_js_1.validateCidString)(cid)) {
71
59
  return false;
72
60
  }
73
61
  return true;
74
62
  }
75
63
  function* enumBlobRefs(input, options) {
64
+ // LegacyBlobRef not included by default
76
65
  const includeLegacy = options?.allowLegacy === true;
77
66
  // Using a stack to avoid recursion depth issues.
78
67
  const stack = [input];
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 {\n Cid,\n RAW_BIN_MULTICODEC,\n SHA2_256_MULTIHASH_CODE,\n asCid,\n parseCid,\n} from './cid.js'\nimport { LexValue } from './lex.js'\nimport { isPlainObject, isPlainProto } from './object.js'\n\n/**\n * @note {@link BlobRef} is just a {@link LexMap} with a specific shape.\n */\nexport type BlobRef = {\n $type: 'blob'\n mimeType: string\n ref: Cid\n size: number\n}\n\nexport type BlobRefValidationOptions = {\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 * @defaults to `true`\n */\n strict?: boolean\n}\n\nexport function isBlobRef(\n input: unknown,\n options?: BlobRefValidationOptions,\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 = asCid(ref)\n if (!cid) {\n return false\n }\n\n if (options?.strict !== false) {\n if (cid.version !== 1) {\n return false\n }\n if (cid.code !== RAW_BIN_MULTICODEC) {\n return false\n }\n if (cid.multihash.code !== SHA2_256_MULTIHASH_CODE) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * @note {@link LegacyBlobRef} is just a {@link LexMap} with a specific shape.\n */\nexport type LegacyBlobRef = {\n cid: string\n mimeType: string\n}\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 try {\n parseCid(cid)\n } catch {\n return false\n }\n\n return true\n}\n\nexport type EnumBlobRefsOptions = BlobRefValidationOptions & {\n /**\n * @defaults to `false`\n */\n allowLegacy?: boolean\n}\n\n/**\n * Enumerates all {@link BlobRef}s (and, optionally, {@link LegacyBlobRef}s)\n * found within a {@link LexValue}.\n */\nexport function enumBlobRefs(\n input: LexValue,\n options: EnumBlobRefsOptions & { allowLegacy: true },\n): Generator<BlobRef | LegacyBlobRef, void, unknown>\nexport function enumBlobRefs(\n input: LexValue,\n options?: EnumBlobRefsOptions & { allowLegacy?: false },\n): Generator<BlobRef, 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 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/**\n * @note {@link BlobRef} is just a {@link LexMap} with a specific shape.\n */\nexport type BlobRef<Ref extends Cid = Cid> = {\n $type: 'blob'\n mimeType: string\n ref: Ref\n size: number\n}\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 * @defaults to `true`\n */\n strict?: boolean\n}\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\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 * @note {@link LegacyBlobRef} is just a {@link LexMap} with a specific shape.\n */\nexport type LegacyBlobRef = {\n cid: string\n mimeType: string\n}\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\nexport type EnumBlobRefsOptions = BlobRefCheckOptions & {\n /**\n * @defaults to `false`\n */\n allowLegacy?: boolean\n}\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 * Enumerates all {@link BlobRef}s (and, optionally, {@link LegacyBlobRef}s)\n * found within a {@link LexValue}.\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
@@ -1,7 +1,10 @@
1
1
  import { CID } from 'multiformats/cid';
2
2
  export declare const DAG_CBOR_MULTICODEC = 113;
3
- export declare const RAW_BIN_MULTICODEC = 85;
4
- export declare const SHA2_256_MULTIHASH_CODE = 18;
3
+ export type DAG_CBOR_MULTICODEC = typeof DAG_CBOR_MULTICODEC;
4
+ export declare const RAW_MULTICODEC = 85;
5
+ export type RAW_MULTICODEC = typeof RAW_MULTICODEC;
6
+ export declare const SHA256_MULTIHASH: 18;
7
+ export type SHA256_MULTIHASH = typeof SHA256_MULTIHASH;
5
8
  export type MultihashDigest<Code extends number = number> = {
6
9
  code: Code;
7
10
  digest: Uint8Array;
@@ -11,8 +14,8 @@ export type MultihashDigest<Code extends number = number> = {
11
14
  declare module 'multiformats/cid' {
12
15
  /**
13
16
  * @deprecated use the {@link Cid} interface from `@atproto/lex-data`, and
14
- * related helpers ({@link asCid}, {@link parseCid}, {@link decodeCid},
15
- * {@link createCid}, {@link isCid}), instead.
17
+ * related helpers ({@link isCid}, {@link ifCid}, {@link asCid},
18
+ * {@link parseCid}, {@link decodeCid}), instead.
16
19
  *
17
20
  * This is marked as deprecated because we want to discourage direct usage of
18
21
  * `multiformats/cid` in dependent packages, and instead have them rely on the
@@ -44,14 +47,80 @@ export interface Cid {
44
47
  equals(other: unknown): boolean;
45
48
  toString(): string;
46
49
  }
47
- export declare function asCid(value: unknown): Cid | null;
48
- export declare function parseCid(input: string): Cid;
49
- export declare function decodeCid(bytes: Uint8Array): Cid;
50
- export declare function createCid(code: number, digest: MultihashDigest): Cid;
51
- export declare function isCid(value: unknown, options?: {
52
- strict?: boolean;
53
- }): value is Cid;
54
- export declare function validateCidString(input: string): boolean;
55
- export declare function parseCidString(input: string): Cid | undefined;
56
- export declare function ensureValidCidString(input: string): void;
50
+ /**
51
+ * Represents the cid of raw binary data (like media blobs).
52
+ * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats ATproto Data Model - Link and CID Formats}
53
+ */
54
+ export interface RawCid extends Cid {
55
+ version: 1;
56
+ code: RAW_MULTICODEC;
57
+ }
58
+ export declare function isRawCid(cid: Cid): cid is RawCid;
59
+ /**
60
+ * Represents a DASL compliant CID.
61
+ * @see {@link https://dasl.ing/cid.html DASL-CIDs}
62
+ */
63
+ export interface DaslCid extends Cid {
64
+ version: 1;
65
+ code: RAW_MULTICODEC | DAG_CBOR_MULTICODEC;
66
+ multihash: MultihashDigest<SHA256_MULTIHASH>;
67
+ }
68
+ export declare function isDaslCid(cid: Cid): cid is DaslCid;
69
+ /**
70
+ * Represents the cid of ATProto DAG-CBOR data (like repository MST nodes).
71
+ * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats ATproto Data Model - Link and CID Formats}
72
+ */
73
+ export interface CborCid extends DaslCid {
74
+ code: DAG_CBOR_MULTICODEC;
75
+ }
76
+ export declare function isCborCid(cid: Cid): cid is CborCid;
77
+ export type CidCheckOptions = {
78
+ flavor?: 'raw' | 'cbor' | 'dasl';
79
+ };
80
+ export type InferCheckedCid<TOptions> = TOptions extends {
81
+ flavor: 'raw';
82
+ } ? RawCid : TOptions extends {
83
+ flavor: 'cbor';
84
+ } ? CborCid : Cid;
85
+ /**
86
+ * Coerces the input value to a Cid, or returns null if not possible.
87
+ */
88
+ export declare function ifCid<TOptions extends CidCheckOptions>(value: unknown, options: TOptions): InferCheckedCid<TOptions> | null;
89
+ export declare function ifCid(value: unknown, options?: CidCheckOptions): Cid | null;
90
+ export declare function isCid<TOptions extends CidCheckOptions>(value: unknown, options: TOptions): value is InferCheckedCid<TOptions>;
91
+ export declare function isCid(value: unknown, options?: CidCheckOptions): value is Cid;
92
+ /**
93
+ * Coerces the input value to a Cid, or throws if not possible.
94
+ */
95
+ export declare function asCid<TOptions extends CidCheckOptions>(value: unknown, options: TOptions): InferCheckedCid<TOptions>;
96
+ export declare function asCid(value: unknown, options?: CidCheckOptions): Cid;
97
+ /**
98
+ * Parses a CID string into a Cid object.
99
+ *
100
+ * @throws if the input is not a valid CID string.
101
+ */
102
+ export declare function parseCid<TOptions extends CidCheckOptions>(input: string, options: TOptions): InferCheckedCid<TOptions>;
103
+ export declare function parseCid(input: string, options?: CidCheckOptions): Cid;
104
+ /**
105
+ * Decodes a CID from its binary representation.
106
+ *
107
+ * @see {@link https://dasl.ing/cid.html DASL-CIDs}
108
+ * @throws if the input do not represent a valid DASL {@link Cid}
109
+ */
110
+ export declare function decodeCid<TOptions extends CidCheckOptions>(cidBytes: Uint8Array, options: TOptions): InferCheckedCid<TOptions>;
111
+ export declare function decodeCid(cidBytes: Uint8Array, options?: CidCheckOptions): Cid;
112
+ export declare function validateCidString(input: string, options?: CidCheckOptions): boolean;
113
+ export declare function parseCidString<TOptions extends CidCheckOptions>(input: string, options: TOptions): InferCheckedCid<TOptions> | undefined;
114
+ export declare function parseCidString(input: string, options?: CidCheckOptions): Cid | undefined;
115
+ export declare function ensureValidCidString(input: string, options?: CidCheckOptions): void;
116
+ /**
117
+ * Verifies whether the multihash of a given {@link cid} matches the hash of the provided {@link bytes}.
118
+ * @params cid The CID to match against the bytes.
119
+ * @params bytes The bytes to verify.
120
+ * @returns true if the CID matches the bytes, false otherwise.
121
+ */
122
+ export declare function isCidForBytes(cid: Cid, bytes: Uint8Array): Promise<boolean>;
123
+ export declare function cidForCbor(bytes: Uint8Array): Promise<CborCid>;
124
+ export declare function cidForRawBytes(bytes: Uint8Array): Promise<RawCid>;
125
+ export declare function cidForRawHash(hash: Uint8Array): RawCid;
57
126
  //# 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":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAEtC,eAAO,MAAM,mBAAmB,MAAO,CAAA;AACvC,eAAO,MAAM,kBAAkB,KAAO,CAAA;AAEtC,eAAO,MAAM,uBAAuB,KAAO,CAAA;AAE3C,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI;IAC1D,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,UAAU,CAAA;CAClB,CAAA;AAED,OAAO,QAAQ,kBAAkB,CAAC;IAChC;;;;;;;;;;;;;;;;;;OAkBG;IACH,UAAU,GAAG;KAAG;CACjB;AAqBD,OAAO,EAAE,GAAG,EAAE,CAAA;AAEd;;;GAGG;AACH,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,CAAC,GAAG,CAAC,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,eAAe,CAAA;IAC1B,KAAK,EAAE,UAAU,CAAA;IACjB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAA;IAC/B,QAAQ,IAAI,MAAM,CAAA;CACnB;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,GAAG,IAAI,CAEhD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAE3C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAEhD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,GAAG,CAEpE;AAED,wBAAgB,KAAK,CACnB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,KAAK,IAAI,GAAG,CAmBd;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAM7D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIxD"}
1
+ {"version":3,"file":"cid.d.ts","sourceRoot":"","sources":["../src/cid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAOtC,eAAO,MAAM,mBAAmB,MAAO,CAAA;AACvC,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAA;AAE5D,eAAO,MAAM,cAAc,KAAO,CAAA;AAClC,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAA;AAElD,eAAO,MAAM,gBAAgB,IAAc,CAAA;AAC3C,MAAM,MAAM,gBAAgB,GAAG,OAAO,gBAAgB,CAAA;AAEtD,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI;IAC1D,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,UAAU,CAAA;CAClB,CAAA;AAED,OAAO,QAAQ,kBAAkB,CAAC;IAChC;;;;;;;;;;;;;;;;;;OAkBG;IACH,UAAU,GAAG;KAAG;CACjB;AAqBD,OAAO,EAAE,GAAG,EAAE,CAAA;AAEd;;;GAGG;AACH,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,CAAC,GAAG,CAAC,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,eAAe,CAAA;IAC1B,KAAK,EAAE,UAAU,CAAA;IACjB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAA;IAC/B,QAAQ,IAAI,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,MAAO,SAAQ,GAAG;IACjC,OAAO,EAAE,CAAC,CAAA;IACV,IAAI,EAAE,cAAc,CAAA;CACrB;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAEhD;AAED;;;GAGG;AACH,MAAM,WAAW,OAAQ,SAAQ,GAAG;IAClC,OAAO,EAAE,CAAC,CAAA;IACV,IAAI,EAAE,cAAc,GAAG,mBAAmB,CAAA;IAC1C,SAAS,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAA;CAC7C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,OAAO,CAOlD;AAED;;;GAGG;AACH,MAAM,WAAW,OAAQ,SAAQ,OAAO;IACtC,IAAI,EAAE,mBAAmB,CAAA;CAC1B;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,OAAO,CAElD;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;CACjC,CAAA;AACD,MAAM,MAAM,eAAe,CAAC,QAAQ,IAAI,QAAQ,SAAS;IAAE,MAAM,EAAE,KAAK,CAAA;CAAE,GACtE,MAAM,GACN,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACjC,OAAO,GACP,GAAG,CAAA;AAET;;GAEG;AACH,wBAAgB,KAAK,CAAC,QAAQ,SAAS,eAAe,EACpD,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,GAChB,eAAe,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAA;AACnC,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,GAAG,GAAG,IAAI,CAAA;AAmB5E,wBAAgB,KAAK,CAAC,QAAQ,SAAS,eAAe,EACpD,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,GAChB,KAAK,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAA;AACrC,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,KAAK,IAAI,GAAG,CAAA;AAK9E;;GAEG;AACH,wBAAgB,KAAK,CAAC,QAAQ,SAAS,eAAe,EACpD,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,QAAQ,GAChB,eAAe,CAAC,QAAQ,CAAC,CAAA;AAC5B,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,GAAG,CAAA;AAOrE;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,SAAS,eAAe,EACvD,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,GAChB,eAAe,CAAC,QAAQ,CAAC,CAAA;AAC5B,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,GAAG,CAAA;AAMvE;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,QAAQ,SAAS,eAAe,EACxD,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,QAAQ,GAChB,eAAe,CAAC,QAAQ,CAAC,CAAA;AAC5B,wBAAgB,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,GAAG,CAAA;AAS/E,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAET;AAED,wBAAgB,cAAc,CAAC,QAAQ,SAAS,eAAe,EAC7D,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,GAChB,eAAe,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAA;AACxC,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,eAAe,GACxB,GAAG,GAAG,SAAS,CAAA;AAYlB,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,eAAe,GACxB,IAAI,CAIN;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAGpE;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAGvE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAOtD"}
package/dist/cid.js CHANGED
@@ -1,63 +1,122 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CID = exports.SHA2_256_MULTIHASH_CODE = exports.RAW_BIN_MULTICODEC = exports.DAG_CBOR_MULTICODEC = void 0;
3
+ exports.CID = exports.SHA256_MULTIHASH = exports.RAW_MULTICODEC = exports.DAG_CBOR_MULTICODEC = void 0;
4
+ exports.isRawCid = isRawCid;
5
+ exports.isDaslCid = isDaslCid;
6
+ exports.isCborCid = isCborCid;
7
+ exports.ifCid = ifCid;
8
+ exports.isCid = isCid;
4
9
  exports.asCid = asCid;
5
10
  exports.parseCid = parseCid;
6
11
  exports.decodeCid = decodeCid;
7
- exports.createCid = createCid;
8
- exports.isCid = isCid;
9
12
  exports.validateCidString = validateCidString;
10
13
  exports.parseCidString = parseCidString;
11
14
  exports.ensureValidCidString = ensureValidCidString;
15
+ exports.isCidForBytes = isCidForBytes;
16
+ exports.cidForCbor = cidForCbor;
17
+ exports.cidForRawBytes = cidForRawBytes;
18
+ exports.cidForRawHash = cidForRawHash;
12
19
  const cid_1 = require("multiformats/cid");
13
20
  Object.defineProperty(exports, "CID", { enumerable: true, get: function () { return cid_1.CID; } });
14
- exports.DAG_CBOR_MULTICODEC = 0x71;
15
- exports.RAW_BIN_MULTICODEC = 0x55;
16
- exports.SHA2_256_MULTIHASH_CODE = 0x12;
17
- function asCid(value) {
18
- return cid_1.CID.asCID(value);
21
+ const digest_1 = require("multiformats/hashes/digest");
22
+ const sha2_1 = require("multiformats/hashes/sha2");
23
+ exports.DAG_CBOR_MULTICODEC = 0x71; // DRISL conformant DAG-CBOR
24
+ exports.RAW_MULTICODEC = 0x55; // raw binary codec used in DASL CIDs
25
+ exports.SHA256_MULTIHASH = sha2_1.sha256.code;
26
+ function isRawCid(cid) {
27
+ return cid.version === 1 && cid.code === exports.RAW_MULTICODEC;
19
28
  }
20
- function parseCid(input) {
21
- return cid_1.CID.parse(input);
29
+ function isDaslCid(cid) {
30
+ return (cid.version === 1 &&
31
+ (cid.code === exports.RAW_MULTICODEC || cid.code === exports.DAG_CBOR_MULTICODEC) &&
32
+ cid.multihash.code === exports.SHA256_MULTIHASH &&
33
+ cid.multihash.size === 32 // Should always be 32 bytes (256 bits) for SHA-256
34
+ );
22
35
  }
23
- function decodeCid(bytes) {
24
- return cid_1.CID.decode(bytes);
36
+ function isCborCid(cid) {
37
+ return cid.code === exports.DAG_CBOR_MULTICODEC && isDaslCid(cid);
25
38
  }
26
- function createCid(code, digest) {
27
- return cid_1.CID.createV1(code, digest);
28
- }
29
- function isCid(value, options) {
30
- const cid = asCid(value);
39
+ function ifCid(value, options) {
40
+ const cid = cid_1.CID.asCID(value);
31
41
  if (!cid) {
32
- return false;
42
+ return null;
33
43
  }
34
- if (options?.strict) {
35
- if (cid.version !== 1) {
36
- return false;
37
- }
38
- if (cid.code !== exports.RAW_BIN_MULTICODEC && cid.code !== exports.DAG_CBOR_MULTICODEC) {
39
- return false;
40
- }
41
- if (cid.multihash.code !== exports.SHA2_256_MULTIHASH_CODE) {
42
- return false;
43
- }
44
+ switch (options?.flavor) {
45
+ case 'cbor':
46
+ return isCborCid(cid) ? cid : null;
47
+ case 'raw':
48
+ return isRawCid(cid) ? cid : null;
49
+ case 'dasl':
50
+ return isDaslCid(cid) ? cid : null;
51
+ default:
52
+ return cid;
44
53
  }
45
- return true;
46
54
  }
47
- function validateCidString(input) {
48
- return parseCidString(input)?.toString() === input;
55
+ function isCid(value, options) {
56
+ return ifCid(value, options) !== null;
57
+ }
58
+ function asCid(value, options) {
59
+ const cid = ifCid(value, options);
60
+ if (cid)
61
+ return cid;
62
+ throw new Error('Not a valid CID');
63
+ }
64
+ function parseCid(input, options) {
65
+ const cid = cid_1.CID.parse(input);
66
+ return asCid(cid, options);
67
+ }
68
+ function decodeCid(cidBytes, options) {
69
+ const cid = cid_1.CID.decode(cidBytes);
70
+ return asCid(cid, options);
49
71
  }
50
- function parseCidString(input) {
72
+ function validateCidString(input, options) {
73
+ return parseCidString(input, options)?.toString() === input;
74
+ }
75
+ function parseCidString(input, options) {
51
76
  try {
52
- return parseCid(input);
77
+ return parseCid(input, options);
53
78
  }
54
79
  catch {
55
80
  return undefined;
56
81
  }
57
82
  }
58
- function ensureValidCidString(input) {
59
- if (!validateCidString(input)) {
83
+ function ensureValidCidString(input, options) {
84
+ if (!validateCidString(input, options)) {
60
85
  throw new Error(`Invalid CID string`);
61
86
  }
62
87
  }
88
+ /**
89
+ * Verifies whether the multihash of a given {@link cid} matches the hash of the provided {@link bytes}.
90
+ * @params cid The CID to match against the bytes.
91
+ * @params bytes The bytes to verify.
92
+ * @returns true if the CID matches the bytes, false otherwise.
93
+ */
94
+ async function isCidForBytes(cid, bytes) {
95
+ if (cid.multihash.code === sha2_1.sha256.code) {
96
+ const digest = await sha2_1.sha256.digest(bytes);
97
+ return (0, digest_1.equals)(cid.multihash, digest);
98
+ }
99
+ if (cid.multihash.code === sha2_1.sha512.code) {
100
+ const digest = await sha2_1.sha512.digest(bytes);
101
+ return (0, digest_1.equals)(cid.multihash, digest);
102
+ }
103
+ // Don't know how to verify other multihash codes
104
+ throw new Error('Unsupported CID multihash');
105
+ }
106
+ async function cidForCbor(bytes) {
107
+ const digest = await sha2_1.sha256.digest(bytes);
108
+ return cid_1.CID.createV1(exports.DAG_CBOR_MULTICODEC, digest);
109
+ }
110
+ async function cidForRawBytes(bytes) {
111
+ const digest = await sha2_1.sha256.digest(bytes);
112
+ return cid_1.CID.createV1(exports.RAW_MULTICODEC, digest);
113
+ }
114
+ function cidForRawHash(hash) {
115
+ // Fool-proofing
116
+ if (hash.length !== 32) {
117
+ throw new Error(`Invalid SHA-256 hash length: ${hash.length}`);
118
+ }
119
+ const digest = (0, digest_1.create)(sha2_1.sha256.code, hash);
120
+ return cid_1.CID.createV1(exports.RAW_MULTICODEC, digest);
121
+ }
63
122
  //# 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":";;;AAuEA,sBAEC;AAED,4BAEC;AAED,8BAEC;AAED,8BAEC;AAED,sBAsBC;AAED,8CAEC;AAED,wCAMC;AAED,oDAIC;AA/HD,0CAAsC;AAwD7B,oFAxDA,SAAG,OAwDA;AAtDC,QAAA,mBAAmB,GAAG,IAAI,CAAA;AAC1B,QAAA,kBAAkB,GAAG,IAAI,CAAA;AAEzB,QAAA,uBAAuB,GAAG,IAAI,CAAA;AAkE3C,SAAgB,KAAK,CAAC,KAAc;IAClC,OAAO,SAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AACzB,CAAC;AAED,SAAgB,QAAQ,CAAC,KAAa;IACpC,OAAO,SAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AACzB,CAAC;AAED,SAAgB,SAAS,CAAC,KAAiB;IACzC,OAAO,SAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC1B,CAAC;AAED,SAAgB,SAAS,CAAC,IAAY,EAAE,MAAuB;IAC7D,OAAO,SAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AACnC,CAAC;AAED,SAAgB,KAAK,CACnB,KAAc,EACd,OAA8B;IAE9B,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAA;IACxB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;QACpB,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,0BAAkB,IAAI,GAAG,CAAC,IAAI,KAAK,2BAAmB,EAAE,CAAC;YACxE,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,+BAAuB,EAAE,CAAC;YACnD,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAgB,iBAAiB,CAAC,KAAa;IAC7C,OAAO,cAAc,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,KAAK,CAAA;AACpD,CAAC;AAED,SAAgB,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAA;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,SAAgB,oBAAoB,CAAC,KAAa;IAChD,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;IACvC,CAAC;AACH,CAAC","sourcesContent":["import { CID } from 'multiformats/cid'\n\nexport const DAG_CBOR_MULTICODEC = 0x71\nexport const RAW_BIN_MULTICODEC = 0x55\n\nexport const SHA2_256_MULTIHASH_CODE = 0x12\n\nexport type MultihashDigest<Code extends number = number> = {\n code: Code\n digest: Uint8Array\n size: number\n bytes: Uint8Array\n}\n\ndeclare module 'multiformats/cid' {\n /**\n * @deprecated use the {@link Cid} interface from `@atproto/lex-data`, and\n * related helpers ({@link asCid}, {@link parseCid}, {@link decodeCid},\n * {@link createCid}, {@link isCid}), 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`, we provide our own stable {@link Cid}\n * 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 { CID }\n\n/**\n * Interface for working with decoded CID string, compatible with\n * {@link CID} implementation.\n */\nexport interface Cid {\n version: 0 | 1\n code: number\n multihash: MultihashDigest\n bytes: Uint8Array\n equals(other: unknown): boolean\n toString(): string\n}\n\nexport function asCid(value: unknown): Cid | null {\n return CID.asCID(value)\n}\n\nexport function parseCid(input: string): Cid {\n return CID.parse(input)\n}\n\nexport function decodeCid(bytes: Uint8Array): Cid {\n return CID.decode(bytes)\n}\n\nexport function createCid(code: number, digest: MultihashDigest): Cid {\n return CID.createV1(code, digest)\n}\n\nexport function isCid(\n value: unknown,\n options?: { strict?: boolean },\n): value is Cid {\n const cid = asCid(value)\n if (!cid) {\n return false\n }\n\n if (options?.strict) {\n if (cid.version !== 1) {\n return false\n }\n if (cid.code !== RAW_BIN_MULTICODEC && cid.code !== DAG_CBOR_MULTICODEC) {\n return false\n }\n if (cid.multihash.code !== SHA2_256_MULTIHASH_CODE) {\n return false\n }\n }\n\n return true\n}\n\nexport function validateCidString(input: string): boolean {\n return parseCidString(input)?.toString() === input\n}\n\nexport function parseCidString(input: string): Cid | undefined {\n try {\n return parseCid(input)\n } catch {\n return undefined\n }\n}\n\nexport function ensureValidCidString(input: string): void {\n if (!validateCidString(input)) {\n throw new Error(`Invalid CID string`)\n }\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 {\n create as createDigest,\n equals as digestEquals,\n} from 'multiformats/hashes/digest'\nimport { sha256, sha512 } from 'multiformats/hashes/sha2'\n\nexport const DAG_CBOR_MULTICODEC = 0x71 // DRISL conformant DAG-CBOR\nexport type DAG_CBOR_MULTICODEC = typeof DAG_CBOR_MULTICODEC\n\nexport const RAW_MULTICODEC = 0x55 // raw binary codec used in DASL CIDs\nexport type RAW_MULTICODEC = typeof RAW_MULTICODEC\n\nexport const SHA256_MULTIHASH = sha256.code\nexport type SHA256_MULTIHASH = typeof SHA256_MULTIHASH\n\nexport type MultihashDigest<Code extends number = number> = {\n code: Code\n digest: Uint8Array\n size: number\n bytes: Uint8Array\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`, we provide our own stable {@link Cid}\n * 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 { CID }\n\n/**\n * Interface for working with decoded CID string, compatible with\n * {@link CID} implementation.\n */\nexport interface Cid {\n version: 0 | 1\n code: number\n multihash: MultihashDigest\n bytes: Uint8Array\n equals(other: unknown): boolean\n toString(): string\n}\n\n/**\n * Represents the cid of raw binary data (like media blobs).\n * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats ATproto Data Model - Link and CID Formats}\n */\nexport interface RawCid extends Cid {\n version: 1\n code: RAW_MULTICODEC\n}\n\nexport function isRawCid(cid: Cid): cid is RawCid {\n return cid.version === 1 && cid.code === RAW_MULTICODEC\n}\n\n/**\n * Represents a DASL compliant CID.\n * @see {@link https://dasl.ing/cid.html DASL-CIDs}\n */\nexport interface DaslCid extends Cid {\n version: 1\n code: RAW_MULTICODEC | DAG_CBOR_MULTICODEC\n multihash: MultihashDigest<SHA256_MULTIHASH>\n}\n\nexport function isDaslCid(cid: Cid): cid is DaslCid {\n return (\n cid.version === 1 &&\n (cid.code === RAW_MULTICODEC || cid.code === DAG_CBOR_MULTICODEC) &&\n cid.multihash.code === SHA256_MULTIHASH &&\n cid.multihash.size === 32 // Should always be 32 bytes (256 bits) for SHA-256\n )\n}\n\n/**\n * Represents the cid of ATProto DAG-CBOR data (like repository MST nodes).\n * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats ATproto Data Model - Link and CID Formats}\n */\nexport interface CborCid extends DaslCid {\n code: DAG_CBOR_MULTICODEC\n}\n\nexport function isCborCid(cid: Cid): cid is CborCid {\n return cid.code === DAG_CBOR_MULTICODEC && isDaslCid(cid)\n}\n\nexport type CidCheckOptions = {\n flavor?: 'raw' | 'cbor' | 'dasl'\n}\nexport type InferCheckedCid<TOptions> = TOptions extends { flavor: 'raw' }\n ? RawCid\n : TOptions extends { flavor: 'cbor' }\n ? CborCid\n : Cid\n\n/**\n * Coerces the input value to a Cid, or returns null if not possible.\n */\nexport function ifCid<TOptions extends CidCheckOptions>(\n value: unknown,\n options: TOptions,\n): InferCheckedCid<TOptions> | null\nexport function ifCid(value: unknown, options?: CidCheckOptions): Cid | null\nexport function ifCid(value: unknown, options?: CidCheckOptions): Cid | null {\n const cid = CID.asCID(value)\n if (!cid) {\n return null\n }\n\n switch (options?.flavor) {\n case 'cbor':\n return isCborCid(cid) ? cid : null\n case 'raw':\n return isRawCid(cid) ? cid : null\n case 'dasl':\n return isDaslCid(cid) ? cid : null\n default:\n return cid\n }\n}\n\nexport function isCid<TOptions extends CidCheckOptions>(\n value: unknown,\n options: TOptions,\n): value is InferCheckedCid<TOptions>\nexport function isCid(value: unknown, options?: CidCheckOptions): value is Cid\nexport function isCid(value: unknown, options?: CidCheckOptions): value is Cid {\n return ifCid(value, options) !== null\n}\n\n/**\n * Coerces the input value to a Cid, or throws if not possible.\n */\nexport function asCid<TOptions extends CidCheckOptions>(\n value: unknown,\n options: TOptions,\n): InferCheckedCid<TOptions>\nexport function asCid(value: unknown, options?: CidCheckOptions): Cid\nexport function asCid(value: unknown, options?: CidCheckOptions): Cid {\n const cid = ifCid(value, options)\n if (cid) return cid\n throw new Error('Not a valid CID')\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 CidCheckOptions>(\n input: string,\n options: TOptions,\n): InferCheckedCid<TOptions>\nexport function parseCid(input: string, options?: CidCheckOptions): Cid\nexport function parseCid(input: string, options?: CidCheckOptions): Cid {\n const cid = CID.parse(input)\n return asCid(cid, options)\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 CidCheckOptions>(\n cidBytes: Uint8Array,\n options: TOptions,\n): InferCheckedCid<TOptions>\nexport function decodeCid(cidBytes: Uint8Array, options?: CidCheckOptions): Cid\nexport function decodeCid(\n cidBytes: Uint8Array,\n options?: CidCheckOptions,\n): Cid {\n const cid = CID.decode(cidBytes)\n return asCid(cid, options)\n}\n\nexport function validateCidString(\n input: string,\n options?: CidCheckOptions,\n): boolean {\n return parseCidString(input, options)?.toString() === input\n}\n\nexport function parseCidString<TOptions extends CidCheckOptions>(\n input: string,\n options: TOptions,\n): InferCheckedCid<TOptions> | undefined\nexport function parseCidString(\n input: string,\n options?: CidCheckOptions,\n): Cid | undefined\nexport function parseCidString(\n input: string,\n options?: CidCheckOptions,\n): Cid | undefined {\n try {\n return parseCid(input, options)\n } catch {\n return undefined\n }\n}\n\nexport function ensureValidCidString(\n input: string,\n options?: CidCheckOptions,\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 digest = await sha256.digest(bytes)\n return digestEquals(cid.multihash, digest)\n }\n\n if (cid.multihash.code === sha512.code) {\n const digest = await sha512.digest(bytes)\n return digestEquals(cid.multihash, digest)\n }\n\n // Don't know how to verify other multihash codes\n throw new Error('Unsupported CID multihash')\n}\n\nexport async function cidForCbor(bytes: Uint8Array): Promise<CborCid> {\n const digest = await sha256.digest(bytes)\n return CID.createV1(DAG_CBOR_MULTICODEC, digest) as CborCid\n}\n\nexport async function cidForRawBytes(bytes: Uint8Array): Promise<RawCid> {\n const digest = await sha256.digest(bytes)\n return CID.createV1(RAW_MULTICODEC, digest) as RawCid\n}\n\nexport function cidForRawHash(hash: Uint8Array): RawCid {\n // Fool-proofing\n if (hash.length !== 32) {\n throw new Error(`Invalid SHA-256 hash length: ${hash.length}`)\n }\n const digest = createDigest(sha256.code, hash)\n return CID.createV1(RAW_MULTICODEC, digest) as RawCid\n}\n"]}
@@ -42,7 +42,7 @@ function lexEquals(a, b) {
42
42
  if ((0, cid_js_1.isCid)(a)) {
43
43
  // @NOTE CID.equals returns its argument when it is falsy (e.g. null or
44
44
  // undefined) so we need to explicitly check that the output is "true".
45
- return (0, cid_js_1.asCid)(a).equals((0, cid_js_1.asCid)(b)) === true;
45
+ return (0, cid_js_1.ifCid)(b)?.equals(a) === true;
46
46
  }
47
47
  else if ((0, cid_js_1.isCid)(b)) {
48
48
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"lex-equals.js","sourceRoot":"","sources":["../src/lex-equals.ts"],"names":[],"mappings":"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","sourcesContent":["import { asCid, isCid } from './cid.js'\nimport { LexValue } from './lex.js'\nimport { isPlainObject } from './object.js'\nimport { ui8Equals } from './uint8array.js'\n\nexport function lexEquals(a: LexValue, b: LexValue): boolean {\n if (Object.is(a, b)) {\n return true\n }\n\n if (\n a == null ||\n b == null ||\n typeof a !== 'object' ||\n typeof b !== 'object'\n ) {\n return false\n }\n\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) {\n return false\n }\n if (a.length !== b.length) {\n return false\n }\n for (let i = 0; i < a.length; i++) {\n if (!lexEquals(a[i], b[i])) {\n return false\n }\n }\n return true\n } else if (Array.isArray(b)) {\n return false\n }\n\n if (ArrayBuffer.isView(a)) {\n if (!ArrayBuffer.isView(b)) return false\n return ui8Equals(a, b)\n } else if (ArrayBuffer.isView(b)) {\n return false\n }\n\n if (isCid(a)) {\n // @NOTE CID.equals returns its argument when it is falsy (e.g. null or\n // undefined) so we need to explicitly check that the output is \"true\".\n return asCid(a)!.equals(asCid(b)) === true\n } else if (isCid(b)) {\n return false\n }\n\n if (!isPlainObject(a) || !isPlainObject(b)) {\n // Foolproof (should never happen)\n throw new TypeError(\n 'Invalid LexValue (expected CID, Uint8Array, or LexMap)',\n )\n }\n\n const aKeys = Object.keys(a)\n const bKeys = Object.keys(b)\n\n if (aKeys.length !== bKeys.length) {\n return false\n }\n\n for (const key of aKeys) {\n const aVal = a[key]\n const bVal = b[key]\n\n // Needed because of the optional index signature in the Lex object type\n // though, in practice, aVal should never be undefined here.\n if (aVal === undefined) {\n if (bVal === undefined && bKeys.includes(key)) continue\n return false\n } else if (bVal === undefined) {\n return false\n }\n\n if (!lexEquals(aVal, bVal)) {\n return false\n }\n }\n\n return true\n}\n"]}
1
+ {"version":3,"file":"lex-equals.js","sourceRoot":"","sources":["../src/lex-equals.ts"],"names":[],"mappings":"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","sourcesContent":["import { ifCid, isCid } from './cid.js'\nimport { LexValue } from './lex.js'\nimport { isPlainObject } from './object.js'\nimport { ui8Equals } from './uint8array.js'\n\nexport function lexEquals(a: LexValue, b: LexValue): boolean {\n if (Object.is(a, b)) {\n return true\n }\n\n if (\n a == null ||\n b == null ||\n typeof a !== 'object' ||\n typeof b !== 'object'\n ) {\n return false\n }\n\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) {\n return false\n }\n if (a.length !== b.length) {\n return false\n }\n for (let i = 0; i < a.length; i++) {\n if (!lexEquals(a[i], b[i])) {\n return false\n }\n }\n return true\n } else if (Array.isArray(b)) {\n return false\n }\n\n if (ArrayBuffer.isView(a)) {\n if (!ArrayBuffer.isView(b)) return false\n return ui8Equals(a, b)\n } else if (ArrayBuffer.isView(b)) {\n return false\n }\n\n if (isCid(a)) {\n // @NOTE CID.equals returns its argument when it is falsy (e.g. null or\n // undefined) so we need to explicitly check that the output is \"true\".\n return ifCid(b)?.equals(a) === true\n } else if (isCid(b)) {\n return false\n }\n\n if (!isPlainObject(a) || !isPlainObject(b)) {\n // Foolproof (should never happen)\n throw new TypeError(\n 'Invalid LexValue (expected CID, Uint8Array, or LexMap)',\n )\n }\n\n const aKeys = Object.keys(a)\n const bKeys = Object.keys(b)\n\n if (aKeys.length !== bKeys.length) {\n return false\n }\n\n for (const key of aKeys) {\n const aVal = a[key]\n const bVal = b[key]\n\n // Needed because of the optional index signature in the Lex object type\n // though, in practice, aVal should never be undefined here.\n if (aVal === undefined) {\n if (bVal === undefined && bKeys.includes(key)) continue\n return false\n } else if (bVal === undefined) {\n return false\n }\n\n if (!lexEquals(aVal, bVal)) {\n return false\n }\n }\n\n return true\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-data",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "license": "MIT",
5
5
  "description": "Core utilities for AT Lexicons",
6
6
  "keywords": [
package/src/blob.test.ts CHANGED
@@ -6,16 +6,19 @@ import {
6
6
  isBlobRef,
7
7
  isLegacyBlobRef,
8
8
  } from './blob.js'
9
- import { parseCid } from './cid.js'
9
+ import { RawCid, parseCid } from './cid.js'
10
10
  import { LexArray, LexMap, LexValue } from './lex.js'
11
11
 
12
12
  // await cidForRawBytes(Buffer.from('Hello, World!'))
13
13
  const validBlobCid = parseCid(
14
14
  'bafkreig77vqcdozl2wyk6z3cscaj5q5fggi53aoh64fewkdiri3cdauyn4',
15
+ { flavor: 'raw' },
15
16
  )
17
+
16
18
  // await cidForLex(Buffer.from('Hello, World!'))
17
19
  const invalidBlobCid = parseCid(
18
20
  'bafyreic52vzks7wdklat4evp3vimohl55i2unzqpshz2ytka5omzr7exdy',
21
+ { flavor: 'cbor' },
19
22
  )
20
23
 
21
24
  describe(isBlobRef, () => {
@@ -285,14 +288,14 @@ describe(isLegacyBlobRef, () => {
285
288
  })
286
289
 
287
290
  describe(enumBlobRefs, () => {
288
- const valid1: BlobRef = {
291
+ const valid1: BlobRef<RawCid> = {
289
292
  $type: 'blob',
290
293
  ref: validBlobCid,
291
294
  mimeType: 'image/png',
292
295
  size: 2048,
293
296
  }
294
297
 
295
- const valid2: BlobRef = {
298
+ const valid2: BlobRef<RawCid> = {
296
299
  $type: 'blob',
297
300
  ref: validBlobCid,
298
301
  mimeType: 'image/jpeg',
package/src/blob.ts CHANGED
@@ -1,24 +1,18 @@
1
- import {
2
- Cid,
3
- RAW_BIN_MULTICODEC,
4
- SHA2_256_MULTIHASH_CODE,
5
- asCid,
6
- parseCid,
7
- } from './cid.js'
1
+ import { Cid, RawCid, ifCid, validateCidString } from './cid.js'
8
2
  import { LexValue } from './lex.js'
9
3
  import { isPlainObject, isPlainProto } from './object.js'
10
4
 
11
5
  /**
12
6
  * @note {@link BlobRef} is just a {@link LexMap} with a specific shape.
13
7
  */
14
- export type BlobRef = {
8
+ export type BlobRef<Ref extends Cid = Cid> = {
15
9
  $type: 'blob'
16
10
  mimeType: string
17
- ref: Cid
11
+ ref: Ref
18
12
  size: number
19
13
  }
20
14
 
21
- export type BlobRefValidationOptions = {
15
+ export type BlobRefCheckOptions = {
22
16
  /**
23
17
  * If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing
24
18
  * any valid CID. Otherwise, validates that the CID is v1, uses the raw
@@ -29,9 +23,25 @@ export type BlobRefValidationOptions = {
29
23
  strict?: boolean
30
24
  }
31
25
 
26
+ export type InferCheckedBlobRef<TOptions extends BlobRefCheckOptions> =
27
+ TOptions extends { strict: false }
28
+ ? BlobRef
29
+ : { strict: boolean } extends TOptions
30
+ ? BlobRef
31
+ : BlobRef<RawCid>
32
+
33
+ export function isBlobRef(input: unknown): input is BlobRef<RawCid>
34
+ export function isBlobRef<TOptions extends BlobRefCheckOptions>(
35
+ input: unknown,
36
+ options: TOptions,
37
+ ): input is InferCheckedBlobRef<TOptions>
38
+ export function isBlobRef(
39
+ input: unknown,
40
+ options?: BlobRefCheckOptions,
41
+ ): input is BlobRef
32
42
  export function isBlobRef(
33
43
  input: unknown,
34
- options?: BlobRefValidationOptions,
44
+ options?: BlobRefCheckOptions,
35
45
  ): input is BlobRef {
36
46
  if (!isPlainObject(input)) {
37
47
  return false
@@ -66,23 +76,15 @@ export function isBlobRef(
66
76
  }
67
77
  }
68
78
 
69
- const cid = asCid(ref)
79
+ const cid = ifCid(
80
+ ref,
81
+ // Strict unless explicitly disabled
82
+ options?.strict === false ? undefined : { flavor: 'raw' },
83
+ )
70
84
  if (!cid) {
71
85
  return false
72
86
  }
73
87
 
74
- if (options?.strict !== false) {
75
- if (cid.version !== 1) {
76
- return false
77
- }
78
- if (cid.code !== RAW_BIN_MULTICODEC) {
79
- return false
80
- }
81
- if (cid.multihash.code !== SHA2_256_MULTIHASH_CODE) {
82
- return false
83
- }
84
- }
85
-
86
88
  return true
87
89
  }
88
90
 
@@ -114,34 +116,38 @@ export function isLegacyBlobRef(input: unknown): input is LegacyBlobRef {
114
116
  }
115
117
  }
116
118
 
117
- try {
118
- parseCid(cid)
119
- } catch {
119
+ if (!validateCidString(cid)) {
120
120
  return false
121
121
  }
122
122
 
123
123
  return true
124
124
  }
125
125
 
126
- export type EnumBlobRefsOptions = BlobRefValidationOptions & {
126
+ export type EnumBlobRefsOptions = BlobRefCheckOptions & {
127
127
  /**
128
128
  * @defaults to `false`
129
129
  */
130
130
  allowLegacy?: boolean
131
131
  }
132
132
 
133
+ export type InferEnumBlobRefs<TOptions extends EnumBlobRefsOptions> =
134
+ TOptions extends { allowLegacy: true }
135
+ ? InferCheckedBlobRef<TOptions> | LegacyBlobRef
136
+ : { allowLegacy: boolean } extends TOptions
137
+ ? InferCheckedBlobRef<TOptions> | LegacyBlobRef
138
+ : InferCheckedBlobRef<TOptions>
139
+
133
140
  /**
134
141
  * Enumerates all {@link BlobRef}s (and, optionally, {@link LegacyBlobRef}s)
135
142
  * found within a {@link LexValue}.
136
143
  */
137
144
  export function enumBlobRefs(
138
145
  input: LexValue,
139
- options: EnumBlobRefsOptions & { allowLegacy: true },
140
- ): Generator<BlobRef | LegacyBlobRef, void, unknown>
141
- export function enumBlobRefs(
146
+ ): Generator<BlobRef<RawCid>, void, unknown>
147
+ export function enumBlobRefs<TOptions extends EnumBlobRefsOptions>(
142
148
  input: LexValue,
143
- options?: EnumBlobRefsOptions & { allowLegacy?: false },
144
- ): Generator<BlobRef, void, unknown>
149
+ options: TOptions,
150
+ ): Generator<InferEnumBlobRefs<TOptions>, void, unknown>
145
151
  export function enumBlobRefs(
146
152
  input: LexValue,
147
153
  options?: EnumBlobRefsOptions,
@@ -150,6 +156,7 @@ export function* enumBlobRefs(
150
156
  input: LexValue,
151
157
  options?: EnumBlobRefsOptions,
152
158
  ): Generator<BlobRef | LegacyBlobRef, void, unknown> {
159
+ // LegacyBlobRef not included by default
153
160
  const includeLegacy = options?.allowLegacy === true
154
161
 
155
162
  // Using a stack to avoid recursion depth issues.
package/src/cid.test.ts CHANGED
@@ -2,8 +2,8 @@ import { CID } from 'multiformats/cid'
2
2
  import { sha256, sha512 } from 'multiformats/hashes/sha2'
3
3
  import { describe, expect, it } from 'vitest'
4
4
  import {
5
- RAW_BIN_MULTICODEC,
6
- createCid,
5
+ DAG_CBOR_MULTICODEC,
6
+ RAW_MULTICODEC,
7
7
  decodeCid,
8
8
  ensureValidCidString,
9
9
  isCid,
@@ -23,7 +23,7 @@ describe(isCid, () => {
23
23
  it('returns true for CID v0 and v1', async () => {
24
24
  const digest = await sha256.digest(Buffer.from('hello world'))
25
25
  const cidV0 = CID.createV0(digest)
26
- const cidV1 = CID.createV1(RAW_BIN_MULTICODEC, digest)
26
+ const cidV1 = CID.createV1(RAW_MULTICODEC, digest)
27
27
  expect(isCid(cidV0)).toBe(true)
28
28
  expect(isCid(cidV1)).toBe(true)
29
29
  })
@@ -35,41 +35,58 @@ describe(isCid, () => {
35
35
  })
36
36
  })
37
37
 
38
- describe('strict mode', () => {
39
- it('returns true for valid CIDs in strict mode', async () => {
40
- const digest = await sha256.digest(Buffer.from('hello world'))
41
- const cid = CID.createV1(RAW_BIN_MULTICODEC, digest)
42
- expect(isCid(cid, { strict: true })).toBe(true)
43
- })
38
+ describe('flavors', () => {
39
+ describe('raw', () => {
40
+ it('validated "raw" cids', async () => {
41
+ const digest = await sha256.digest(Buffer.from('hello world'))
42
+ const cid = CID.createV1(RAW_MULTICODEC, digest)
43
+ expect(isCid(cid, { flavor: 'raw' })).toBe(true)
44
+ })
44
45
 
45
- it('rejects CID v0 when strict option is set', async () => {
46
- const digest = await sha256.digest(Buffer.from('hello world'))
47
- const cid = CID.createV0(digest)
48
- expect(isCid(cid, { strict: true })).toBe(false)
49
- })
46
+ it('allows other hash algorithms', async () => {
47
+ const digest = await sha512.digest(Buffer.from('hello world'))
48
+ const cid = CID.createV1(RAW_MULTICODEC, digest)
49
+ expect(isCid(cid, { flavor: 'raw' })).toBe(true)
50
+ })
50
51
 
51
- it('rejects CIDs with invalid hash algorithm', async () => {
52
- const digest = await sha512.digest(Buffer.from('hello world'))
53
- const cid = CID.createV1(RAW_BIN_MULTICODEC, digest)
54
- expect(isCid(cid, { strict: true })).toBe(false)
55
- })
52
+ it('rejects CID v0 when strict option is set', async () => {
53
+ const digest = await sha256.digest(Buffer.from('hello world'))
54
+ const cid = CID.createV0(digest)
55
+ expect(isCid(cid, { flavor: 'raw' })).toBe(false)
56
+ })
56
57
 
57
- it('rejects CIDs with invalid code', async () => {
58
- const digest = await sha256.digest(Buffer.from('hello world'))
59
- const cid = CID.createV1(3333, digest)
60
- expect(isCid(cid, { strict: true })).toBe(false)
58
+ it('rejects CIDs with invalid code', async () => {
59
+ const digest = await sha256.digest(Buffer.from('hello world'))
60
+ const cid = CID.createV1(3333, digest)
61
+ expect(isCid(cid, { flavor: 'raw' })).toBe(false)
62
+ })
61
63
  })
62
- })
63
- })
64
64
 
65
- describe(createCid, () => {
66
- it('creates a valid CID v1', async () => {
67
- const digest = await sha256.digest(Buffer.from('hello world'))
68
- const cid = createCid(RAW_BIN_MULTICODEC, digest)
69
- expect(cid.version).toBe(1)
70
- expect(cid.code).toBe(RAW_BIN_MULTICODEC)
71
- expect(cid.multihash.code).toBe(sha256.code)
72
- expect(cid.multihash.digest).toEqual(digest.digest)
65
+ describe('cbor', () => {
66
+ it('validated "cbor" cids', async () => {
67
+ const digest = await sha256.digest(Buffer.from('hello world'))
68
+ const cid = CID.createV1(DAG_CBOR_MULTICODEC, digest)
69
+ expect(isCid(cid, { flavor: 'cbor' })).toBe(true)
70
+ })
71
+
72
+ it('rejects CIDs with invalid hash algorithm', async () => {
73
+ const digest = await sha512.digest(Buffer.from('hello world'))
74
+ const cid = CID.createV1(RAW_MULTICODEC, digest)
75
+ expect(isCid(cid, { flavor: 'cbor' })).toBe(false)
76
+ })
77
+
78
+ it('rejects CID v0 when strict option is set', async () => {
79
+ const digest = await sha256.digest(Buffer.from('hello world'))
80
+ const cid = CID.createV0(digest)
81
+ expect(isCid(cid, { flavor: 'cbor' })).toBe(false)
82
+ })
83
+
84
+ it('rejects CIDs with invalid code', async () => {
85
+ const digest = await sha256.digest(Buffer.from('hello world'))
86
+ const cid = CID.createV1(3333, digest)
87
+ expect(isCid(cid, { flavor: 'cbor' })).toBe(false)
88
+ })
89
+ })
73
90
  })
74
91
  })
75
92
 
package/src/cid.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  import { CID } from 'multiformats/cid'
2
+ import {
3
+ create as createDigest,
4
+ equals as digestEquals,
5
+ } from 'multiformats/hashes/digest'
6
+ import { sha256, sha512 } from 'multiformats/hashes/sha2'
2
7
 
3
- export const DAG_CBOR_MULTICODEC = 0x71
4
- export const RAW_BIN_MULTICODEC = 0x55
8
+ export const DAG_CBOR_MULTICODEC = 0x71 // DRISL conformant DAG-CBOR
9
+ export type DAG_CBOR_MULTICODEC = typeof DAG_CBOR_MULTICODEC
5
10
 
6
- export const SHA2_256_MULTIHASH_CODE = 0x12
11
+ export const RAW_MULTICODEC = 0x55 // raw binary codec used in DASL CIDs
12
+ export type RAW_MULTICODEC = typeof RAW_MULTICODEC
13
+
14
+ export const SHA256_MULTIHASH = sha256.code
15
+ export type SHA256_MULTIHASH = typeof SHA256_MULTIHASH
7
16
 
8
17
  export type MultihashDigest<Code extends number = number> = {
9
18
  code: Code
@@ -15,8 +24,8 @@ export type MultihashDigest<Code extends number = number> = {
15
24
  declare module 'multiformats/cid' {
16
25
  /**
17
26
  * @deprecated use the {@link Cid} interface from `@atproto/lex-data`, and
18
- * related helpers ({@link asCid}, {@link parseCid}, {@link decodeCid},
19
- * {@link createCid}, {@link isCid}), instead.
27
+ * related helpers ({@link isCid}, {@link ifCid}, {@link asCid},
28
+ * {@link parseCid}, {@link decodeCid}), instead.
20
29
  *
21
30
  * This is marked as deprecated because we want to discourage direct usage of
22
31
  * `multiformats/cid` in dependent packages, and instead have them rely on the
@@ -69,60 +78,216 @@ export interface Cid {
69
78
  toString(): string
70
79
  }
71
80
 
72
- export function asCid(value: unknown): Cid | null {
73
- return CID.asCID(value)
81
+ /**
82
+ * Represents the cid of raw binary data (like media blobs).
83
+ * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats ATproto Data Model - Link and CID Formats}
84
+ */
85
+ export interface RawCid extends Cid {
86
+ version: 1
87
+ code: RAW_MULTICODEC
88
+ }
89
+
90
+ export function isRawCid(cid: Cid): cid is RawCid {
91
+ return cid.version === 1 && cid.code === RAW_MULTICODEC
92
+ }
93
+
94
+ /**
95
+ * Represents a DASL compliant CID.
96
+ * @see {@link https://dasl.ing/cid.html DASL-CIDs}
97
+ */
98
+ export interface DaslCid extends Cid {
99
+ version: 1
100
+ code: RAW_MULTICODEC | DAG_CBOR_MULTICODEC
101
+ multihash: MultihashDigest<SHA256_MULTIHASH>
74
102
  }
75
103
 
76
- export function parseCid(input: string): Cid {
77
- return CID.parse(input)
104
+ export function isDaslCid(cid: Cid): cid is DaslCid {
105
+ return (
106
+ cid.version === 1 &&
107
+ (cid.code === RAW_MULTICODEC || cid.code === DAG_CBOR_MULTICODEC) &&
108
+ cid.multihash.code === SHA256_MULTIHASH &&
109
+ cid.multihash.size === 32 // Should always be 32 bytes (256 bits) for SHA-256
110
+ )
78
111
  }
79
112
 
80
- export function decodeCid(bytes: Uint8Array): Cid {
81
- return CID.decode(bytes)
113
+ /**
114
+ * Represents the cid of ATProto DAG-CBOR data (like repository MST nodes).
115
+ * @see {@link https://atproto.com/specs/data-model#link-and-cid-formats ATproto Data Model - Link and CID Formats}
116
+ */
117
+ export interface CborCid extends DaslCid {
118
+ code: DAG_CBOR_MULTICODEC
82
119
  }
83
120
 
84
- export function createCid(code: number, digest: MultihashDigest): Cid {
85
- return CID.createV1(code, digest)
121
+ export function isCborCid(cid: Cid): cid is CborCid {
122
+ return cid.code === DAG_CBOR_MULTICODEC && isDaslCid(cid)
86
123
  }
87
124
 
88
- export function isCid(
125
+ export type CidCheckOptions = {
126
+ flavor?: 'raw' | 'cbor' | 'dasl'
127
+ }
128
+ export type InferCheckedCid<TOptions> = TOptions extends { flavor: 'raw' }
129
+ ? RawCid
130
+ : TOptions extends { flavor: 'cbor' }
131
+ ? CborCid
132
+ : Cid
133
+
134
+ /**
135
+ * Coerces the input value to a Cid, or returns null if not possible.
136
+ */
137
+ export function ifCid<TOptions extends CidCheckOptions>(
89
138
  value: unknown,
90
- options?: { strict?: boolean },
91
- ): value is Cid {
92
- const cid = asCid(value)
139
+ options: TOptions,
140
+ ): InferCheckedCid<TOptions> | null
141
+ export function ifCid(value: unknown, options?: CidCheckOptions): Cid | null
142
+ export function ifCid(value: unknown, options?: CidCheckOptions): Cid | null {
143
+ const cid = CID.asCID(value)
93
144
  if (!cid) {
94
- return false
145
+ return null
95
146
  }
96
147
 
97
- if (options?.strict) {
98
- if (cid.version !== 1) {
99
- return false
100
- }
101
- if (cid.code !== RAW_BIN_MULTICODEC && cid.code !== DAG_CBOR_MULTICODEC) {
102
- return false
103
- }
104
- if (cid.multihash.code !== SHA2_256_MULTIHASH_CODE) {
105
- return false
106
- }
148
+ switch (options?.flavor) {
149
+ case 'cbor':
150
+ return isCborCid(cid) ? cid : null
151
+ case 'raw':
152
+ return isRawCid(cid) ? cid : null
153
+ case 'dasl':
154
+ return isDaslCid(cid) ? cid : null
155
+ default:
156
+ return cid
107
157
  }
158
+ }
108
159
 
109
- return true
160
+ export function isCid<TOptions extends CidCheckOptions>(
161
+ value: unknown,
162
+ options: TOptions,
163
+ ): value is InferCheckedCid<TOptions>
164
+ export function isCid(value: unknown, options?: CidCheckOptions): value is Cid
165
+ export function isCid(value: unknown, options?: CidCheckOptions): value is Cid {
166
+ return ifCid(value, options) !== null
110
167
  }
111
168
 
112
- export function validateCidString(input: string): boolean {
113
- return parseCidString(input)?.toString() === input
169
+ /**
170
+ * Coerces the input value to a Cid, or throws if not possible.
171
+ */
172
+ export function asCid<TOptions extends CidCheckOptions>(
173
+ value: unknown,
174
+ options: TOptions,
175
+ ): InferCheckedCid<TOptions>
176
+ export function asCid(value: unknown, options?: CidCheckOptions): Cid
177
+ export function asCid(value: unknown, options?: CidCheckOptions): Cid {
178
+ const cid = ifCid(value, options)
179
+ if (cid) return cid
180
+ throw new Error('Not a valid CID')
114
181
  }
115
182
 
116
- export function parseCidString(input: string): Cid | undefined {
183
+ /**
184
+ * Parses a CID string into a Cid object.
185
+ *
186
+ * @throws if the input is not a valid CID string.
187
+ */
188
+ export function parseCid<TOptions extends CidCheckOptions>(
189
+ input: string,
190
+ options: TOptions,
191
+ ): InferCheckedCid<TOptions>
192
+ export function parseCid(input: string, options?: CidCheckOptions): Cid
193
+ export function parseCid(input: string, options?: CidCheckOptions): Cid {
194
+ const cid = CID.parse(input)
195
+ return asCid(cid, options)
196
+ }
197
+
198
+ /**
199
+ * Decodes a CID from its binary representation.
200
+ *
201
+ * @see {@link https://dasl.ing/cid.html DASL-CIDs}
202
+ * @throws if the input do not represent a valid DASL {@link Cid}
203
+ */
204
+ export function decodeCid<TOptions extends CidCheckOptions>(
205
+ cidBytes: Uint8Array,
206
+ options: TOptions,
207
+ ): InferCheckedCid<TOptions>
208
+ export function decodeCid(cidBytes: Uint8Array, options?: CidCheckOptions): Cid
209
+ export function decodeCid(
210
+ cidBytes: Uint8Array,
211
+ options?: CidCheckOptions,
212
+ ): Cid {
213
+ const cid = CID.decode(cidBytes)
214
+ return asCid(cid, options)
215
+ }
216
+
217
+ export function validateCidString(
218
+ input: string,
219
+ options?: CidCheckOptions,
220
+ ): boolean {
221
+ return parseCidString(input, options)?.toString() === input
222
+ }
223
+
224
+ export function parseCidString<TOptions extends CidCheckOptions>(
225
+ input: string,
226
+ options: TOptions,
227
+ ): InferCheckedCid<TOptions> | undefined
228
+ export function parseCidString(
229
+ input: string,
230
+ options?: CidCheckOptions,
231
+ ): Cid | undefined
232
+ export function parseCidString(
233
+ input: string,
234
+ options?: CidCheckOptions,
235
+ ): Cid | undefined {
117
236
  try {
118
- return parseCid(input)
237
+ return parseCid(input, options)
119
238
  } catch {
120
239
  return undefined
121
240
  }
122
241
  }
123
242
 
124
- export function ensureValidCidString(input: string): void {
125
- if (!validateCidString(input)) {
243
+ export function ensureValidCidString(
244
+ input: string,
245
+ options?: CidCheckOptions,
246
+ ): void {
247
+ if (!validateCidString(input, options)) {
126
248
  throw new Error(`Invalid CID string`)
127
249
  }
128
250
  }
251
+
252
+ /**
253
+ * Verifies whether the multihash of a given {@link cid} matches the hash of the provided {@link bytes}.
254
+ * @params cid The CID to match against the bytes.
255
+ * @params bytes The bytes to verify.
256
+ * @returns true if the CID matches the bytes, false otherwise.
257
+ */
258
+ export async function isCidForBytes(
259
+ cid: Cid,
260
+ bytes: Uint8Array,
261
+ ): Promise<boolean> {
262
+ if (cid.multihash.code === sha256.code) {
263
+ const digest = await sha256.digest(bytes)
264
+ return digestEquals(cid.multihash, digest)
265
+ }
266
+
267
+ if (cid.multihash.code === sha512.code) {
268
+ const digest = await sha512.digest(bytes)
269
+ return digestEquals(cid.multihash, digest)
270
+ }
271
+
272
+ // Don't know how to verify other multihash codes
273
+ throw new Error('Unsupported CID multihash')
274
+ }
275
+
276
+ export async function cidForCbor(bytes: Uint8Array): Promise<CborCid> {
277
+ const digest = await sha256.digest(bytes)
278
+ return CID.createV1(DAG_CBOR_MULTICODEC, digest) as CborCid
279
+ }
280
+
281
+ export async function cidForRawBytes(bytes: Uint8Array): Promise<RawCid> {
282
+ const digest = await sha256.digest(bytes)
283
+ return CID.createV1(RAW_MULTICODEC, digest) as RawCid
284
+ }
285
+
286
+ export function cidForRawHash(hash: Uint8Array): RawCid {
287
+ // Fool-proofing
288
+ if (hash.length !== 32) {
289
+ throw new Error(`Invalid SHA-256 hash length: ${hash.length}`)
290
+ }
291
+ const digest = createDigest(sha256.code, hash)
292
+ return CID.createV1(RAW_MULTICODEC, digest) as RawCid
293
+ }
package/src/lex-equals.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { asCid, isCid } from './cid.js'
1
+ import { ifCid, isCid } from './cid.js'
2
2
  import { LexValue } from './lex.js'
3
3
  import { isPlainObject } from './object.js'
4
4
  import { ui8Equals } from './uint8array.js'
@@ -44,7 +44,7 @@ export function lexEquals(a: LexValue, b: LexValue): boolean {
44
44
  if (isCid(a)) {
45
45
  // @NOTE CID.equals returns its argument when it is falsy (e.g. null or
46
46
  // undefined) so we need to explicitly check that the output is "true".
47
- return asCid(a)!.equals(asCid(b)) === true
47
+ return ifCid(b)?.equals(a) === true
48
48
  } else if (isCid(b)) {
49
49
  return false
50
50
  }