@atproto/lex-data 0.0.5 → 0.0.6

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.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [#4501](https://github.com/bluesky-social/atproto/pull/4501) [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `enumBlobRefs` utility function
8
+
9
+ - [#4501](https://github.com/bluesky-social/atproto/pull/4501) [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Perform strict `BlobRef` validation by default
10
+
11
+ - [#4501](https://github.com/bluesky-social/atproto/pull/4501) [`2f78893`](https://github.com/bluesky-social/atproto/commit/2f78893ace3bbf14d4bac36837820ddb46658c98) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `base64ToUtf8` and `utf8ToBase64` utilities
12
+
3
13
  ## 0.0.5
4
14
 
5
15
  ### Patch Changes
package/dist/blob.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Cid } from './cid.js';
2
+ import { LexValue } from './lex.js';
2
3
  /**
3
4
  * @note {@link BlobRef} is just a {@link LexMap} with a specific shape.
4
5
  */
@@ -8,9 +9,17 @@ export type BlobRef = {
8
9
  ref: Cid;
9
10
  size: number;
10
11
  };
11
- export declare function isBlobRef(input: unknown, options?: {
12
+ export type BlobRefValidationOptions = {
13
+ /**
14
+ * If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing
15
+ * any valid CID. Otherwise, validates that the CID is v1, uses the raw
16
+ * multicodec, and has a sha256 multihash.
17
+ *
18
+ * @defaults to `true`
19
+ */
12
20
  strict?: boolean;
13
- }): input is BlobRef;
21
+ };
22
+ export declare function isBlobRef(input: unknown, options?: BlobRefValidationOptions): input is BlobRef;
14
23
  /**
15
24
  * @note {@link LegacyBlobRef} is just a {@link LexMap} with a specific shape.
16
25
  */
@@ -19,4 +28,21 @@ export type LegacyBlobRef = {
19
28
  mimeType: string;
20
29
  };
21
30
  export declare function isLegacyBlobRef(input: unknown): input is LegacyBlobRef;
31
+ export type EnumBlobRefsOptions = BlobRefValidationOptions & {
32
+ /**
33
+ * @defaults to `false`
34
+ */
35
+ allowLegacy?: boolean;
36
+ };
37
+ /**
38
+ * Enumerates all {@link BlobRef}s (and, optionally, {@link LegacyBlobRef}s)
39
+ * found within a {@link LexValue}.
40
+ */
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>;
47
+ export declare function enumBlobRefs(input: LexValue, options?: EnumBlobRefsOptions): Generator<BlobRef | LegacyBlobRef, void, unknown>;
22
48
  //# 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;AAGjB;;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,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,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"}
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"}
package/dist/blob.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isBlobRef = isBlobRef;
4
4
  exports.isLegacyBlobRef = isLegacyBlobRef;
5
+ exports.enumBlobRefs = enumBlobRefs;
5
6
  const cid_js_1 = require("./cid.js");
6
7
  const object_js_1 = require("./object.js");
7
8
  function isBlobRef(input, options) {
@@ -34,7 +35,7 @@ function isBlobRef(input, options) {
34
35
  if (!cid) {
35
36
  return false;
36
37
  }
37
- if (options?.strict) {
38
+ if (options?.strict !== false) {
38
39
  if (cid.version !== 1) {
39
40
  return false;
40
41
  }
@@ -71,4 +72,43 @@ function isLegacyBlobRef(input) {
71
72
  }
72
73
  return true;
73
74
  }
75
+ function* enumBlobRefs(input, options) {
76
+ const includeLegacy = options?.allowLegacy === true;
77
+ // Using a stack to avoid recursion depth issues.
78
+ const stack = [input];
79
+ // Since we are using a stack, we could end-up in an infinite loop with cyclic
80
+ // structures. Cyclic structures are not valid LexValues and should, thus,
81
+ // never occur, but let's be safe.
82
+ const visited = new Set();
83
+ do {
84
+ const value = stack.pop();
85
+ if (value != null && typeof value === 'object') {
86
+ if (Array.isArray(value)) {
87
+ if (visited.has(value))
88
+ continue;
89
+ visited.add(value);
90
+ stack.push(...value);
91
+ }
92
+ else if ((0, object_js_1.isPlainProto)(value)) {
93
+ if (visited.has(value))
94
+ continue;
95
+ visited.add(value);
96
+ if (isBlobRef(value, options)) {
97
+ yield value;
98
+ }
99
+ else if (includeLegacy && isLegacyBlobRef(value)) {
100
+ yield value;
101
+ }
102
+ else {
103
+ for (const v of Object.values(value)) {
104
+ if (v != null)
105
+ stack.push(v);
106
+ }
107
+ }
108
+ }
109
+ }
110
+ } while (stack.length > 0);
111
+ // Optimization: ease GC's work
112
+ visited.clear();
113
+ }
74
114
  //# sourceMappingURL=blob.js.map
package/dist/blob.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":";;AAmBA,8BAuDC;AAUD,0CA2BC;AA/GD,qCAMiB;AACjB,2CAA2C;AAY3C,SAAgB,SAAS,CACvB,KAAc,EACd,OAA8B;IAE9B,IAAI,CAAC,IAAA,yBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,KAAK,EAAE,KAAK,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,KAAK,CAAA;IACrC,mCAAmC;IACnC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5D,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IACE,GAAG,KAAK,OAAO;YACf,GAAG,KAAK,UAAU;YAClB,GAAG,KAAK,KAAK;YACb,GAAG,KAAK,MAAM,EACd,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAA,cAAK,EAAC,GAAG,CAAC,CAAA;IACtB,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,2BAAkB,EAAE,CAAC;YACpC,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,gCAAuB,EAAE,CAAC;YACnD,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAUD,SAAgB,eAAe,CAAC,KAAc;IAC5C,IAAI,CAAC,IAAA,yBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IAC/B,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACxC,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,IAAA,iBAAQ,EAAC,GAAG,CAAC,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import {\n Cid,\n RAW_BIN_MULTICODEC,\n SHA2_256_MULTIHASH_CODE,\n asCid,\n parseCid,\n} from './cid.js'\nimport { isPlainObject } 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 function isBlobRef(\n input: unknown,\n options?: { strict?: boolean },\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) {\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"]}
1
+ {"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":";;AA+BA,8BAuDC;AAUD,0CA2BC;AAyBD,oCAwCC;AA5LD,qCAMiB;AAEjB,2CAAyD;AAuBzD,SAAgB,SAAS,CACvB,KAAc,EACd,OAAkC;IAElC,IAAI,CAAC,IAAA,yBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,KAAK,EAAE,KAAK,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,KAAK,CAAA;IACrC,mCAAmC;IACnC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5D,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IACE,GAAG,KAAK,OAAO;YACf,GAAG,KAAK,UAAU;YAClB,GAAG,KAAK,KAAK;YACb,GAAG,KAAK,MAAM,EACd,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAA,cAAK,EAAC,GAAG,CAAC,CAAA;IACtB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,EAAE,MAAM,KAAK,KAAK,EAAE,CAAC;QAC9B,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,2BAAkB,EAAE,CAAC;YACpC,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,gCAAuB,EAAE,CAAC;YACnD,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAUD,SAAgB,eAAe,CAAC,KAAc;IAC5C,IAAI,CAAC,IAAA,yBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IAC/B,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACxC,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,IAAA,iBAAQ,EAAC,GAAG,CAAC,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAyBD,QAAe,CAAC,CAAC,YAAY,CAC3B,KAAe,EACf,OAA6B;IAE7B,MAAM,aAAa,GAAG,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IAEnD,iDAAiD;IACjD,MAAM,KAAK,GAAe,CAAC,KAAK,CAAC,CAAA;IAEjC,8EAA8E;IAC9E,0EAA0E;IAC1E,kCAAkC;IAClC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IAEjC,GAAG,CAAC;QACF,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAG,CAAA;QAE1B,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,SAAQ;gBAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBAClB,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAA;YACtB,CAAC;iBAAM,IAAI,IAAA,wBAAY,EAAC,KAAK,CAAC,EAAE,CAAC;gBAC/B,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,SAAQ;gBAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBAClB,IAAI,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;oBAC9B,MAAM,KAAK,CAAA;gBACb,CAAC;qBAAM,IAAI,aAAa,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;oBACnD,MAAM,KAAK,CAAA;gBACb,CAAC;qBAAM,CAAC;oBACN,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;wBACrC,IAAI,CAAC,IAAI,IAAI;4BAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;oBAC9B,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,QAAQ,KAAK,CAAC,MAAM,GAAG,CAAC,EAAC;IAE1B,+BAA+B;IAC/B,OAAO,CAAC,KAAK,EAAE,CAAA;AACjB,CAAC","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 +1 @@
1
- {"version":3,"file":"uint8array-from-base64.d.ts","sourceRoot":"","sources":["../src/uint8array-from-base64.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAIvD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B;;WAEG;QACH,UAAU,CAAC,EAAE,CACX,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;YACR,wBAAwB;YACxB,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;YACjC,iBAAiB,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,qBAAqB,CAAA;SAC/D,KACE,UAAU,CAAA;KAChB;CACF;AAED,eAAO,MAAM,gBAAgB,SAGhB,MAAM,aACD,cAAc,KACvB,UAAU,QAMT,CAAA;AAEV,eAAO,MAAM,cAAc,SAEhB,MAAM,aACD,cAAc,KACvB,UAAU,QAQyB,CAAA;AAE1C,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,cAAyB,GAClC,UAAU,CAIZ"}
1
+ {"version":3,"file":"uint8array-from-base64.d.ts","sourceRoot":"","sources":["../src/uint8array-from-base64.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAIvD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B;;WAEG;QACH,UAAU,CAAC,EAAE,CACX,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;YACR,wBAAwB;YACxB,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;YACjC,iBAAiB,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,qBAAqB,CAAA;SAC/D,KACE,UAAU,CAAA;KAChB;CACF;AAED,eAAO,MAAM,gBAAgB,SAGhB,MAAM,aACD,cAAc,KACvB,UAAU,QAMyB,CAAA;AAE5C,eAAO,MAAM,cAAc,SAEhB,MAAM,aACD,cAAc,KACvB,UAAU,QAQyB,CAAA;AAE1C,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,cAAyB,GAClC,UAAU,CAIZ"}
@@ -12,7 +12,7 @@ exports.fromBase64Native = typeof Uint8Array.fromBase64 === 'function'
12
12
  lastChunkHandling: 'loose',
13
13
  });
14
14
  }
15
- : null;
15
+ : /* v8 ignore next -- @preserve */ null;
16
16
  exports.fromBase64Node = Buffer
17
17
  ? function fromBase64Node(b64, alphabet = 'base64') {
18
18
  const bytes = Buffer.from(b64, alphabet);
@@ -1 +1 @@
1
- {"version":3,"file":"uint8array-from-base64.js","sourceRoot":"","sources":["../src/uint8array-from-base64.ts"],"names":[],"mappings":"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","sourcesContent":["import { fromString } from 'uint8arrays/from-string'\nimport { NodeJSBuffer } from './lib/nodejs-buffer.js'\nimport { Base64Alphabet } from './uint8array-base64.js'\n\nconst Buffer = NodeJSBuffer\n\ndeclare global {\n interface Uint8ArrayConstructor {\n /**\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64 Uint8Array.fromBase64()}\n */\n fromBase64?: (\n b64: string,\n options?: {\n /** @default 'base64' */\n alphabet?: 'base64' | 'base64url'\n lastChunkHandling?: 'loose' | 'strict' | 'stop-before-partial'\n },\n ) => Uint8Array\n }\n}\n\nexport const fromBase64Native =\n typeof Uint8Array.fromBase64 === 'function'\n ? function fromBase64Native(\n b64: string,\n alphabet: Base64Alphabet = 'base64',\n ): Uint8Array {\n return Uint8Array.fromBase64!(b64, {\n alphabet,\n lastChunkHandling: 'loose',\n })\n }\n : null\n\nexport const fromBase64Node = Buffer\n ? function fromBase64Node(\n b64: string,\n alphabet: Base64Alphabet = 'base64',\n ): Uint8Array {\n const bytes = Buffer.from(b64, alphabet)\n verifyBase64ForBytes(b64, bytes)\n // Convert to Uint8Array because even though Buffer is a sub class of\n // Uint8Array, it serializes differently to Uint8Array (e.g. in JSON) and\n // results in unexpected behavior downstream (e.g. in tests)\n return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n }\n : /* v8 ignore next -- @preserve */ null\n\nexport function fromBase64Ponyfill(\n b64: string,\n alphabet: Base64Alphabet = 'base64',\n): Uint8Array {\n const bytes = fromString(b64, b64.endsWith('=') ? `${alphabet}pad` : alphabet)\n verifyBase64ForBytes(b64, bytes)\n return bytes\n}\n\n// @NOTE NodeJS will silently stop decoding at the first invalid character,\n// while \"uint8arrays/from-string\" will not validate that the padding is\n// correct. The following function performs basic validation to ensure that the\n// input was a valid base64 string. The availability of the \"bytes\" allows\n// to perform checks with O[1] complexity.\nfunction verifyBase64ForBytes(b64: string, bytes: Uint8Array): void {\n const paddingCount = b64.endsWith('==') ? 2 : b64.endsWith('=') ? 1 : 0\n const trimmedLength = b64.length - paddingCount\n const expectedByteLength = Math.floor((trimmedLength * 3) / 4)\n if (bytes.length !== expectedByteLength) {\n throw new Error('Invalid base64 string')\n }\n\n const expectedB64Length = (bytes.length / 3) * 4\n const expectedPaddingCount =\n expectedB64Length % 4 === 0 ? 0 : 4 - (expectedB64Length % 4)\n const expectedFullB64Length = expectedB64Length + expectedPaddingCount\n if (b64.length > expectedFullB64Length) {\n throw new Error('Invalid base64 string')\n }\n\n // The previous might still allow false positive if only the last few\n // chars are invalid.\n for (\n let i = Math.ceil(expectedB64Length);\n i < b64.length - paddingCount;\n i++\n ) {\n const code = b64.charCodeAt(i)\n if (\n !(code >= 65 && code <= 90) && // A-Z\n !(code >= 97 && code <= 122) && // a-z\n !(code >= 48 && code <= 57) && // 0-9\n code !== 43 && // +\n code !== 47 // /\n ) {\n throw new Error('Invalid base64 string')\n }\n }\n}\n"]}
1
+ {"version":3,"file":"uint8array-from-base64.js","sourceRoot":"","sources":["../src/uint8array-from-base64.ts"],"names":[],"mappings":"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","sourcesContent":["import { fromString } from 'uint8arrays/from-string'\nimport { NodeJSBuffer } from './lib/nodejs-buffer.js'\nimport { Base64Alphabet } from './uint8array-base64.js'\n\nconst Buffer = NodeJSBuffer\n\ndeclare global {\n interface Uint8ArrayConstructor {\n /**\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64 Uint8Array.fromBase64()}\n */\n fromBase64?: (\n b64: string,\n options?: {\n /** @default 'base64' */\n alphabet?: 'base64' | 'base64url'\n lastChunkHandling?: 'loose' | 'strict' | 'stop-before-partial'\n },\n ) => Uint8Array\n }\n}\n\nexport const fromBase64Native =\n typeof Uint8Array.fromBase64 === 'function'\n ? function fromBase64Native(\n b64: string,\n alphabet: Base64Alphabet = 'base64',\n ): Uint8Array {\n return Uint8Array.fromBase64!(b64, {\n alphabet,\n lastChunkHandling: 'loose',\n })\n }\n : /* v8 ignore next -- @preserve */ null\n\nexport const fromBase64Node = Buffer\n ? function fromBase64Node(\n b64: string,\n alphabet: Base64Alphabet = 'base64',\n ): Uint8Array {\n const bytes = Buffer.from(b64, alphabet)\n verifyBase64ForBytes(b64, bytes)\n // Convert to Uint8Array because even though Buffer is a sub class of\n // Uint8Array, it serializes differently to Uint8Array (e.g. in JSON) and\n // results in unexpected behavior downstream (e.g. in tests)\n return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n }\n : /* v8 ignore next -- @preserve */ null\n\nexport function fromBase64Ponyfill(\n b64: string,\n alphabet: Base64Alphabet = 'base64',\n): Uint8Array {\n const bytes = fromString(b64, b64.endsWith('=') ? `${alphabet}pad` : alphabet)\n verifyBase64ForBytes(b64, bytes)\n return bytes\n}\n\n// @NOTE NodeJS will silently stop decoding at the first invalid character,\n// while \"uint8arrays/from-string\" will not validate that the padding is\n// correct. The following function performs basic validation to ensure that the\n// input was a valid base64 string. The availability of the \"bytes\" allows\n// to perform checks with O[1] complexity.\nfunction verifyBase64ForBytes(b64: string, bytes: Uint8Array): void {\n const paddingCount = b64.endsWith('==') ? 2 : b64.endsWith('=') ? 1 : 0\n const trimmedLength = b64.length - paddingCount\n const expectedByteLength = Math.floor((trimmedLength * 3) / 4)\n if (bytes.length !== expectedByteLength) {\n throw new Error('Invalid base64 string')\n }\n\n const expectedB64Length = (bytes.length / 3) * 4\n const expectedPaddingCount =\n expectedB64Length % 4 === 0 ? 0 : 4 - (expectedB64Length % 4)\n const expectedFullB64Length = expectedB64Length + expectedPaddingCount\n if (b64.length > expectedFullB64Length) {\n throw new Error('Invalid base64 string')\n }\n\n // The previous might still allow false positive if only the last few\n // chars are invalid.\n for (\n let i = Math.ceil(expectedB64Length);\n i < b64.length - paddingCount;\n i++\n ) {\n const code = b64.charCodeAt(i)\n if (\n !(code >= 65 && code <= 90) && // A-Z\n !(code >= 97 && code <= 122) && // a-z\n !(code >= 48 && code <= 57) && // 0-9\n code !== 43 && // +\n code !== 47 // /\n ) {\n throw new Error('Invalid base64 string')\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"uint8array-to-base64.d.ts","sourceRoot":"","sources":["../src/uint8array-to-base64.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAIvD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,UAAU;QAClB;;WAEG;QACH,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE;YACpB,wBAAwB;YACxB,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;YACjC,WAAW,CAAC,EAAE,OAAO,CAAA;SACtB,KAAK,MAAM,CAAA;KACb;CACF;AAED,eAAO,MAAM,cAAc,WAGZ,UAAU,aACP,cAAc,KACvB,MAAM,QAGL,CAAA;AAEV,eAAO,MAAM,YAAY,WAEZ,UAAU,aACP,cAAc,KACvB,MAAM,QAcL,CAAA;AAER,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,EACjB,QAAQ,GAAE,cAAyB,GAClC,MAAM,CAER"}
1
+ {"version":3,"file":"uint8array-to-base64.d.ts","sourceRoot":"","sources":["../src/uint8array-to-base64.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAIvD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,UAAU;QAClB;;WAEG;QACH,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE;YACpB,wBAAwB;YACxB,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;YACjC,WAAW,CAAC,EAAE,OAAO,CAAA;SACtB,KAAK,MAAM,CAAA;KACb;CACF;AAED,eAAO,MAAM,cAAc,WAGZ,UAAU,aACP,cAAc,KACvB,MAAM,QAG6B,CAAA;AAE5C,eAAO,MAAM,YAAY,WAEZ,UAAU,aACP,cAAc,KACvB,MAAM,QAc6B,CAAA;AAE1C,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,EACjB,QAAQ,GAAE,cAAyB,GAClC,MAAM,CAER"}
@@ -9,7 +9,7 @@ exports.toBase64Native = typeof Uint8Array.prototype.toBase64 === 'function'
9
9
  ? function toBase64Native(bytes, alphabet = 'base64') {
10
10
  return bytes.toBase64({ alphabet, omitPadding: true });
11
11
  }
12
- : null;
12
+ : /* v8 ignore next -- @preserve */ null;
13
13
  exports.toBase64Node = Buffer
14
14
  ? function toBase64Node(bytes, alphabet = 'base64') {
15
15
  const buffer = bytes instanceof Buffer ? bytes : Buffer.from(bytes);
@@ -24,7 +24,7 @@ exports.toBase64Node = Buffer
24
24
  : b64.slice(0, -1) // '='
25
25
  : b64;
26
26
  }
27
- : null;
27
+ : /* v8 ignore next -- @preserve */ null;
28
28
  function toBase64Ponyfill(bytes, alphabet = 'base64') {
29
29
  return (0, to_string_1.toString)(bytes, alphabet);
30
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"uint8array-to-base64.js","sourceRoot":"","sources":["../src/uint8array-to-base64.ts"],"names":[],"mappings":";;;AAiDA,4CAKC;AAtDD,qDAAgD;AAChD,6DAAqD;AAGrD,MAAM,MAAM,GAAG,+BAAY,CAAA;AAed,QAAA,cAAc,GACzB,OAAO,UAAU,CAAC,SAAS,CAAC,QAAQ,KAAK,UAAU;IACjD,CAAC,CAAC,SAAS,cAAc,CACrB,KAAiB,EACjB,WAA2B,QAAQ;QAEnC,OAAO,KAAK,CAAC,QAAS,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;IACzD,CAAC;IACH,CAAC,CAAC,IAAI,CAAA;AAEG,QAAA,YAAY,GAAG,MAAM;IAChC,CAAC,CAAC,SAAS,YAAY,CACnB,KAAiB,EACjB,WAA2B,QAAQ;QAEnC,MAAM,MAAM,GAAG,KAAK,YAAY,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnE,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAErC,uDAAuD;QACvD,0EAA0E;QAC1E,sEAAsE;QACtE,6DAA6D;QAC7D,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI;YACtD,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI;gBACjD,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO;gBAC1B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM;YAC3B,CAAC,CAAC,GAAG,CAAA;IACT,CAAC;IACH,CAAC,CAAC,IAAI,CAAA;AAER,SAAgB,gBAAgB,CAC9B,KAAiB,EACjB,WAA2B,QAAQ;IAEnC,OAAO,IAAA,oBAAQ,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { toString } from 'uint8arrays/to-string'\nimport { NodeJSBuffer } from './lib/nodejs-buffer.js'\nimport { Base64Alphabet } from './uint8array-base64.js'\n\nconst Buffer = NodeJSBuffer\n\ndeclare global {\n interface Uint8Array {\n /**\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64 Uint8Array.prototype.toBase64()}\n */\n toBase64?: (options?: {\n /** @default 'base64' */\n alphabet?: 'base64' | 'base64url'\n omitPadding?: boolean\n }) => string\n }\n}\n\nexport const toBase64Native =\n typeof Uint8Array.prototype.toBase64 === 'function'\n ? function toBase64Native(\n bytes: Uint8Array,\n alphabet: Base64Alphabet = 'base64',\n ): string {\n return bytes.toBase64!({ alphabet, omitPadding: true })\n }\n : null\n\nexport const toBase64Node = Buffer\n ? function toBase64Node(\n bytes: Uint8Array,\n alphabet: Base64Alphabet = 'base64',\n ): string {\n const buffer = bytes instanceof Buffer ? bytes : Buffer.from(bytes)\n const b64 = buffer.toString(alphabet)\n\n // @NOTE We strip padding for strict compatibility with\n // uint8arrays.toString behavior. Tests failing because of the presence of\n // padding are not really synonymous with an actual error and we might\n // (should?) actually want to keep the padding at some point.\n return b64.charCodeAt(b64.length - 1) === /* '=' */ 0x3d\n ? b64.charCodeAt(b64.length - 2) === /* '=' */ 0x3d\n ? b64.slice(0, -2) // '=='\n : b64.slice(0, -1) // '='\n : b64\n }\n : null\n\nexport function toBase64Ponyfill(\n bytes: Uint8Array,\n alphabet: Base64Alphabet = 'base64',\n): string {\n return toString(bytes, alphabet)\n}\n"]}
1
+ {"version":3,"file":"uint8array-to-base64.js","sourceRoot":"","sources":["../src/uint8array-to-base64.ts"],"names":[],"mappings":";;;AAiDA,4CAKC;AAtDD,qDAAgD;AAChD,6DAAqD;AAGrD,MAAM,MAAM,GAAG,+BAAY,CAAA;AAed,QAAA,cAAc,GACzB,OAAO,UAAU,CAAC,SAAS,CAAC,QAAQ,KAAK,UAAU;IACjD,CAAC,CAAC,SAAS,cAAc,CACrB,KAAiB,EACjB,WAA2B,QAAQ;QAEnC,OAAO,KAAK,CAAC,QAAS,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;IACzD,CAAC;IACH,CAAC,CAAC,iCAAiC,CAAC,IAAI,CAAA;AAE/B,QAAA,YAAY,GAAG,MAAM;IAChC,CAAC,CAAC,SAAS,YAAY,CACnB,KAAiB,EACjB,WAA2B,QAAQ;QAEnC,MAAM,MAAM,GAAG,KAAK,YAAY,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnE,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAErC,uDAAuD;QACvD,0EAA0E;QAC1E,sEAAsE;QACtE,6DAA6D;QAC7D,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI;YACtD,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI;gBACjD,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO;gBAC1B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM;YAC3B,CAAC,CAAC,GAAG,CAAA;IACT,CAAC;IACH,CAAC,CAAC,iCAAiC,CAAC,IAAI,CAAA;AAE1C,SAAgB,gBAAgB,CAC9B,KAAiB,EACjB,WAA2B,QAAQ;IAEnC,OAAO,IAAA,oBAAQ,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { toString } from 'uint8arrays/to-string'\nimport { NodeJSBuffer } from './lib/nodejs-buffer.js'\nimport { Base64Alphabet } from './uint8array-base64.js'\n\nconst Buffer = NodeJSBuffer\n\ndeclare global {\n interface Uint8Array {\n /**\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64 Uint8Array.prototype.toBase64()}\n */\n toBase64?: (options?: {\n /** @default 'base64' */\n alphabet?: 'base64' | 'base64url'\n omitPadding?: boolean\n }) => string\n }\n}\n\nexport const toBase64Native =\n typeof Uint8Array.prototype.toBase64 === 'function'\n ? function toBase64Native(\n bytes: Uint8Array,\n alphabet: Base64Alphabet = 'base64',\n ): string {\n return bytes.toBase64!({ alphabet, omitPadding: true })\n }\n : /* v8 ignore next -- @preserve */ null\n\nexport const toBase64Node = Buffer\n ? function toBase64Node(\n bytes: Uint8Array,\n alphabet: Base64Alphabet = 'base64',\n ): string {\n const buffer = bytes instanceof Buffer ? bytes : Buffer.from(bytes)\n const b64 = buffer.toString(alphabet)\n\n // @NOTE We strip padding for strict compatibility with\n // uint8arrays.toString behavior. Tests failing because of the presence of\n // padding are not really synonymous with an actual error and we might\n // (should?) actually want to keep the padding at some point.\n return b64.charCodeAt(b64.length - 1) === /* '=' */ 0x3d\n ? b64.charCodeAt(b64.length - 2) === /* '=' */ 0x3d\n ? b64.slice(0, -2) // '=='\n : b64.slice(0, -1) // '='\n : b64\n }\n : /* v8 ignore next -- @preserve */ null\n\nexport function toBase64Ponyfill(\n bytes: Uint8Array,\n alphabet: Base64Alphabet = 'base64',\n): string {\n return toString(bytes, alphabet)\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import { Base64Alphabet } from './uint8array-base64.js';
2
+ export declare const utf8FromBase64Node: ((b64: string, alphabet?: Base64Alphabet) => string) | null;
3
+ export declare function utf8FromBase64Ponyfill(b64: string, alphabet?: Base64Alphabet): string;
4
+ //# sourceMappingURL=utf8-from-base64.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utf8-from-base64.d.ts","sourceRoot":"","sources":["../src/utf8-from-base64.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAIvD,eAAO,MAAM,kBAAkB,SAEpB,MAAM,aACD,cAAc,KACvB,MAAM,QAG6B,CAAA;AAG1C,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,cAAc,GACxB,MAAM,CAGR"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.utf8FromBase64Node = void 0;
4
+ exports.utf8FromBase64Ponyfill = utf8FromBase64Ponyfill;
5
+ const from_string_1 = require("uint8arrays/from-string");
6
+ const nodejs_buffer_js_1 = require("./lib/nodejs-buffer.js");
7
+ const Buffer = nodejs_buffer_js_1.NodeJSBuffer;
8
+ exports.utf8FromBase64Node = Buffer
9
+ ? function utf8FromBase64Node(b64, alphabet = 'base64') {
10
+ return Buffer.from(b64, alphabet).toString('utf8');
11
+ }
12
+ : /* v8 ignore next -- @preserve */ null;
13
+ const textDecoder = /*#__PURE__*/ new TextDecoder();
14
+ function utf8FromBase64Ponyfill(b64, alphabet) {
15
+ const bytes = (0, from_string_1.fromString)(b64, alphabet);
16
+ return textDecoder.decode(bytes);
17
+ }
18
+ //# sourceMappingURL=utf8-from-base64.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utf8-from-base64.js","sourceRoot":"","sources":["../src/utf8-from-base64.ts"],"names":[],"mappings":";;;AAgBA,wDAMC;AAtBD,yDAAoD;AACpD,6DAAqD;AAGrD,MAAM,MAAM,GAAG,+BAAY,CAAA;AAEd,QAAA,kBAAkB,GAAG,MAAM;IACtC,CAAC,CAAC,SAAS,kBAAkB,CACzB,GAAW,EACX,WAA2B,QAAQ;QAEnC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IACpD,CAAC;IACH,CAAC,CAAC,iCAAiC,CAAC,IAAI,CAAA;AAE1C,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,WAAW,EAAE,CAAA;AACnD,SAAgB,sBAAsB,CACpC,GAAW,EACX,QAAyB;IAEzB,MAAM,KAAK,GAAG,IAAA,wBAAU,EAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IACvC,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { fromString } from 'uint8arrays/from-string'\nimport { NodeJSBuffer } from './lib/nodejs-buffer.js'\nimport { Base64Alphabet } from './uint8array-base64.js'\n\nconst Buffer = NodeJSBuffer\n\nexport const utf8FromBase64Node = Buffer\n ? function utf8FromBase64Node(\n b64: string,\n alphabet: Base64Alphabet = 'base64',\n ): string {\n return Buffer.from(b64, alphabet).toString('utf8')\n }\n : /* v8 ignore next -- @preserve */ null\n\nconst textDecoder = /*#__PURE__*/ new TextDecoder()\nexport function utf8FromBase64Ponyfill(\n b64: string,\n alphabet?: Base64Alphabet,\n): string {\n const bytes = fromString(b64, alphabet)\n return textDecoder.decode(bytes)\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import { Base64Alphabet } from './uint8array-base64.js';
2
+ export declare const utf8ToBase64Node: ((text: string, alphabet?: Base64Alphabet) => string) | null;
3
+ export declare function utf8ToBase64Ponyfill(text: string, alphabet?: Base64Alphabet): string;
4
+ //# sourceMappingURL=utf8-to-base64.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utf8-to-base64.d.ts","sourceRoot":"","sources":["../src/utf8-to-base64.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAKvD,eAAO,MAAM,gBAAgB,UACO,MAAM,aAAa,cAAc,KAAG,MAAM,QAIpC,CAAA;AAG1C,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,cAAc,GACxB,MAAM,CAGR"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.utf8ToBase64Node = void 0;
4
+ exports.utf8ToBase64Ponyfill = utf8ToBase64Ponyfill;
5
+ const to_string_1 = require("uint8arrays/to-string");
6
+ const nodejs_buffer_js_1 = require("./lib/nodejs-buffer.js");
7
+ const uint8array_to_base64_js_1 = require("./uint8array-to-base64.js");
8
+ const Buffer = nodejs_buffer_js_1.NodeJSBuffer;
9
+ exports.utf8ToBase64Node = Buffer
10
+ ? function utf8ToBase64Node(text, alphabet) {
11
+ const buffer = Buffer.from(text, 'utf8');
12
+ return uint8array_to_base64_js_1.toBase64Node(buffer, alphabet);
13
+ }
14
+ : /* v8 ignore next -- @preserve */ null;
15
+ const textEncoder = /*#__PURE__*/ new TextEncoder();
16
+ function utf8ToBase64Ponyfill(text, alphabet) {
17
+ const bytes = textEncoder.encode(text);
18
+ return (0, to_string_1.toString)(bytes, alphabet);
19
+ }
20
+ //# sourceMappingURL=utf8-to-base64.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utf8-to-base64.js","sourceRoot":"","sources":["../src/utf8-to-base64.ts"],"names":[],"mappings":";;;AAeA,oDAMC;AArBD,qDAAgD;AAChD,6DAAqD;AAErD,uEAAwD;AAExD,MAAM,MAAM,GAAG,+BAAY,CAAA;AAEd,QAAA,gBAAgB,GAAG,MAAM;IACpC,CAAC,CAAC,SAAS,gBAAgB,CAAC,IAAY,EAAE,QAAyB;QAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACxC,OAAO,sCAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACxC,CAAC;IACH,CAAC,CAAC,iCAAiC,CAAC,IAAI,CAAA;AAE1C,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,WAAW,EAAE,CAAA;AACnD,SAAgB,oBAAoB,CAClC,IAAY,EACZ,QAAyB;IAEzB,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACtC,OAAO,IAAA,oBAAQ,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { toString } from 'uint8arrays/to-string'\nimport { NodeJSBuffer } from './lib/nodejs-buffer.js'\nimport { Base64Alphabet } from './uint8array-base64.js'\nimport { toBase64Node } from './uint8array-to-base64.js'\n\nconst Buffer = NodeJSBuffer\n\nexport const utf8ToBase64Node = Buffer\n ? function utf8ToBase64Node(text: string, alphabet?: Base64Alphabet): string {\n const buffer = Buffer.from(text, 'utf8')\n return toBase64Node!(buffer, alphabet)\n }\n : /* v8 ignore next -- @preserve */ null\n\nconst textEncoder = /*#__PURE__*/ new TextEncoder()\nexport function utf8ToBase64Ponyfill(\n text: string,\n alphabet?: Base64Alphabet,\n): string {\n const bytes = textEncoder.encode(text)\n return toString(bytes, alphabet)\n}\n"]}
package/dist/utf8.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { Base64Alphabet } from './uint8array.js';
1
2
  export declare const graphemeLen: (str: string) => number;
2
3
  export declare const utf8Len: (string: string) => number;
4
+ export declare const utf8ToBase64: (str: string, alphabet?: Base64Alphabet) => string;
5
+ export declare const utf8FromBase64: (b64: string, alphabet?: Base64Alphabet) => string;
3
6
  //# sourceMappingURL=utf8.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utf8.d.ts","sourceRoot":"","sources":["../src/utf8.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MACD,CAAA;AAS1C,eAAO,MAAM,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAsC,CAAA"}
1
+ {"version":3,"file":"utf8.d.ts","sourceRoot":"","sources":["../src/utf8.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAShD,eAAO,MAAM,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAEY,CAAA;AAUvD,eAAO,MAAM,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAEQ,CAAA;AAElD,eAAO,MAAM,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,KAAK,MAEf,CAAA;AAExD,eAAO,MAAM,cAAc,EAAE,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,cAAc,KACtB,MAEqD,CAAA"}
package/dist/utf8.js CHANGED
@@ -1,12 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.utf8Len = exports.graphemeLen = void 0;
3
+ exports.utf8FromBase64 = exports.utf8ToBase64 = exports.utf8Len = exports.graphemeLen = void 0;
4
+ const utf8_from_base64_js_1 = require("./utf8-from-base64.js");
4
5
  const utf8_grapheme_len_js_1 = require("./utf8-grapheme-len.js");
5
6
  const utf8_len_js_1 = require("./utf8-len.js");
6
- exports.graphemeLen = utf8_grapheme_len_js_1.graphemeLenNative ?? utf8_grapheme_len_js_1.graphemeLenPonyfill;
7
+ const utf8_to_base64_js_1 = require("./utf8-to-base64.js");
8
+ exports.graphemeLen =
9
+ /* v8 ignore next -- @preserve */ utf8_grapheme_len_js_1.graphemeLenNative ??
10
+ /* v8 ignore next -- @preserve */ utf8_grapheme_len_js_1.graphemeLenPonyfill;
11
+ /* v8 ignore next -- @preserve */
7
12
  if (exports.graphemeLen === utf8_grapheme_len_js_1.graphemeLenPonyfill) {
8
13
  /*#__PURE__*/
9
14
  console.warn('[@atproto/lex-data]: Intl.Segmenter is not available in this environment. Falling back to ponyfill implementation.');
10
15
  }
11
- exports.utf8Len = utf8_len_js_1.utf8LenNode ?? utf8_len_js_1.utf8LenCompute;
16
+ exports.utf8Len =
17
+ /* v8 ignore next -- @preserve */ utf8_len_js_1.utf8LenNode ??
18
+ /* v8 ignore next -- @preserve */ utf8_len_js_1.utf8LenCompute;
19
+ exports.utf8ToBase64 =
20
+ /* v8 ignore next -- @preserve */ utf8_to_base64_js_1.utf8ToBase64Node ??
21
+ /* v8 ignore next -- @preserve */ utf8_to_base64_js_1.utf8ToBase64Ponyfill;
22
+ exports.utf8FromBase64 =
23
+ /* v8 ignore next -- @preserve */ utf8_from_base64_js_1.utf8FromBase64Node ??
24
+ /* v8 ignore next -- @preserve */ utf8_from_base64_js_1.utf8FromBase64Ponyfill;
12
25
  //# sourceMappingURL=utf8.js.map
package/dist/utf8.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utf8.js","sourceRoot":"","sources":["../src/utf8.ts"],"names":[],"mappings":";;;AAAA,iEAA+E;AAC/E,+CAA2D;AAE9C,QAAA,WAAW,GACtB,wCAAiB,IAAI,0CAAmB,CAAA;AAE1C,IAAI,mBAAW,KAAK,0CAAmB,EAAE,CAAC;IACxC,aAAa;IACb,OAAO,CAAC,IAAI,CACV,oHAAoH,CACrH,CAAA;AACH,CAAC;AAEY,QAAA,OAAO,GAA+B,yBAAW,IAAI,4BAAc,CAAA","sourcesContent":["import { graphemeLenNative, graphemeLenPonyfill } from './utf8-grapheme-len.js'\nimport { utf8LenCompute, utf8LenNode } from './utf8-len.js'\n\nexport const graphemeLen: (str: string) => number =\n graphemeLenNative ?? graphemeLenPonyfill\n\nif (graphemeLen === graphemeLenPonyfill) {\n /*#__PURE__*/\n console.warn(\n '[@atproto/lex-data]: Intl.Segmenter is not available in this environment. Falling back to ponyfill implementation.',\n )\n}\n\nexport const utf8Len: (string: string) => number = utf8LenNode ?? utf8LenCompute\n"]}
1
+ {"version":3,"file":"utf8.js","sourceRoot":"","sources":["../src/utf8.ts"],"names":[],"mappings":";;;AACA,+DAG8B;AAC9B,iEAA+E;AAC/E,+CAA2D;AAC3D,2DAA4E;AAE/D,QAAA,WAAW;AACtB,iCAAiC,CAAC,wCAAiB;IACnD,iCAAiC,CAAC,0CAAmB,CAAA;AAEvD,iCAAiC;AACjC,IAAI,mBAAW,KAAK,0CAAmB,EAAE,CAAC;IACxC,aAAa;IACb,OAAO,CAAC,IAAI,CACV,oHAAoH,CACrH,CAAA;AACH,CAAC;AAEY,QAAA,OAAO;AAClB,iCAAiC,CAAC,yBAAW;IAC7C,iCAAiC,CAAC,4BAAc,CAAA;AAErC,QAAA,YAAY;AACvB,iCAAiC,CAAC,oCAAgB;IAClD,iCAAiC,CAAC,wCAAoB,CAAA;AAE3C,QAAA,cAAc;AAIzB,iCAAiC,CAAC,wCAAkB;IACpD,iCAAiC,CAAC,4CAAsB,CAAA","sourcesContent":["import { Base64Alphabet } from './uint8array.js'\nimport {\n utf8FromBase64Node,\n utf8FromBase64Ponyfill,\n} from './utf8-from-base64.js'\nimport { graphemeLenNative, graphemeLenPonyfill } from './utf8-grapheme-len.js'\nimport { utf8LenCompute, utf8LenNode } from './utf8-len.js'\nimport { utf8ToBase64Node, utf8ToBase64Ponyfill } from './utf8-to-base64.js'\n\nexport const graphemeLen: (str: string) => number =\n /* v8 ignore next -- @preserve */ graphemeLenNative ??\n /* v8 ignore next -- @preserve */ graphemeLenPonyfill\n\n/* v8 ignore next -- @preserve */\nif (graphemeLen === graphemeLenPonyfill) {\n /*#__PURE__*/\n console.warn(\n '[@atproto/lex-data]: Intl.Segmenter is not available in this environment. Falling back to ponyfill implementation.',\n )\n}\n\nexport const utf8Len: (string: string) => number =\n /* v8 ignore next -- @preserve */ utf8LenNode ??\n /* v8 ignore next -- @preserve */ utf8LenCompute\n\nexport const utf8ToBase64: (str: string, alphabet?: Base64Alphabet) => string =\n /* v8 ignore next -- @preserve */ utf8ToBase64Node ??\n /* v8 ignore next -- @preserve */ utf8ToBase64Ponyfill\n\nexport const utf8FromBase64: (\n b64: string,\n alphabet?: Base64Alphabet,\n) => string =\n /* v8 ignore next -- @preserve */ utf8FromBase64Node ??\n /* v8 ignore next -- @preserve */ utf8FromBase64Ponyfill\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-data",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "license": "MIT",
5
5
  "description": "Core utilities for AT Lexicons",
6
6
  "keywords": [
package/src/blob.test.ts CHANGED
@@ -1,29 +1,36 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { isBlobRef, isLegacyBlobRef } from './blob.js'
2
+ import {
3
+ BlobRef,
4
+ LegacyBlobRef,
5
+ enumBlobRefs,
6
+ isBlobRef,
7
+ isLegacyBlobRef,
8
+ } from './blob.js'
3
9
  import { parseCid } from './cid.js'
10
+ import { LexArray, LexMap, LexValue } from './lex.js'
4
11
 
5
12
  // await cidForRawBytes(Buffer.from('Hello, World!'))
6
- const blobCid = parseCid(
13
+ const validBlobCid = parseCid(
7
14
  'bafkreig77vqcdozl2wyk6z3cscaj5q5fggi53aoh64fewkdiri3cdauyn4',
8
15
  )
9
16
  // await cidForLex(Buffer.from('Hello, World!'))
10
- const lexCid = parseCid(
17
+ const invalidBlobCid = parseCid(
11
18
  'bafyreic52vzks7wdklat4evp3vimohl55i2unzqpshz2ytka5omzr7exdy',
12
19
  )
13
20
 
14
21
  describe(isBlobRef, () => {
15
22
  it('tests valid blobCid and lexCid', () => {
16
- expect(blobCid.code).toBe(0x55) // raw
17
- expect(blobCid.multihash.code).toBe(0x12) // sha2-256
18
- expect(lexCid.code).toBe(0x71) // dag-cbor
19
- expect(lexCid.multihash.code).toBe(0x12) // sha2-256
23
+ expect(validBlobCid.code).toBe(0x55) // raw
24
+ expect(validBlobCid.multihash.code).toBe(0x12) // sha2-256
25
+ expect(invalidBlobCid.code).toBe(0x71) // dag-cbor
26
+ expect(invalidBlobCid.multihash.code).toBe(0x12) // sha2-256
20
27
  })
21
28
 
22
29
  it('parses valid blob', () => {
23
30
  expect(
24
31
  isBlobRef({
25
32
  $type: 'blob',
26
- ref: blobCid,
33
+ ref: validBlobCid,
27
34
  mimeType: 'image/jpeg',
28
35
  size: 10000,
29
36
  }),
@@ -33,7 +40,7 @@ describe(isBlobRef, () => {
33
40
  isBlobRef(
34
41
  {
35
42
  $type: 'blob',
36
- ref: lexCid,
43
+ ref: invalidBlobCid,
37
44
  mimeType: 'image/jpeg',
38
45
  size: 10000,
39
46
  },
@@ -43,11 +50,22 @@ describe(isBlobRef, () => {
43
50
  ).toBe(true)
44
51
  })
45
52
 
53
+ it('performs strict validation by default', () => {
54
+ expect(
55
+ isBlobRef({
56
+ $type: 'blob',
57
+ ref: invalidBlobCid,
58
+ mimeType: 'image/jpeg',
59
+ size: 10000,
60
+ }),
61
+ ).toBe(false)
62
+ })
63
+
46
64
  it('rejects invalid inputs', () => {
47
65
  expect(
48
66
  isBlobRef({
49
67
  $type: 'blob',
50
- ref: { $link: blobCid.toString() },
68
+ ref: { $link: validBlobCid.toString() },
51
69
  mimeType: 'image/jpeg',
52
70
  size: '10000',
53
71
  }),
@@ -56,7 +74,7 @@ describe(isBlobRef, () => {
56
74
  expect(
57
75
  isBlobRef({
58
76
  // $type: 'blob',
59
- ref: blobCid,
77
+ ref: validBlobCid,
60
78
  mimeType: 'image/jpeg',
61
79
  size: 10000,
62
80
  }),
@@ -65,7 +83,7 @@ describe(isBlobRef, () => {
65
83
  expect(
66
84
  isBlobRef({
67
85
  $type: 'blob',
68
- ref: blobCid,
86
+ ref: validBlobCid,
69
87
  mimeType: { toString: () => 'image/jpeg' },
70
88
  size: 10000,
71
89
  }),
@@ -75,7 +93,7 @@ describe(isBlobRef, () => {
75
93
  isBlobRef(
76
94
  {
77
95
  $type: 'blob',
78
- ref: { $link: blobCid.toString() },
96
+ ref: { $link: validBlobCid.toString() },
79
97
  mimeType: 'image/jpeg',
80
98
  size: '10000',
81
99
  },
@@ -112,7 +130,7 @@ describe(isBlobRef, () => {
112
130
  expect(
113
131
  isBlobRef({
114
132
  $type: 'blob',
115
- ref: blobCid,
133
+ ref: validBlobCid,
116
134
  mimeType: 'image/jpeg',
117
135
  size: 10000.5,
118
136
  }),
@@ -124,7 +142,7 @@ describe(isBlobRef, () => {
124
142
  isBlobRef(
125
143
  {
126
144
  $type: 'blob',
127
- ref: blobCid,
145
+ ref: validBlobCid,
128
146
  mimeType: 'image/jpeg',
129
147
  size: 10000,
130
148
  },
@@ -136,7 +154,7 @@ describe(isBlobRef, () => {
136
154
  isBlobRef(
137
155
  {
138
156
  $type: 'blob',
139
- ref: lexCid,
157
+ ref: invalidBlobCid,
140
158
  mimeType: 'image/jpeg',
141
159
  size: 10000,
142
160
  },
@@ -149,7 +167,7 @@ describe(isBlobRef, () => {
149
167
  expect(
150
168
  isBlobRef({
151
169
  $type: 'blob',
152
- ref: blobCid,
170
+ ref: validBlobCid,
153
171
  mimeType: 'image/jpeg',
154
172
  size: 10000,
155
173
  extra: 'not allowed',
@@ -160,7 +178,7 @@ describe(isBlobRef, () => {
160
178
  isBlobRef(
161
179
  {
162
180
  $type: 'blob',
163
- ref: blobCid,
181
+ ref: validBlobCid,
164
182
  mimeType: 'image/jpeg',
165
183
  size: 10000,
166
184
  extra: 'not allowed',
@@ -194,14 +212,14 @@ describe(isLegacyBlobRef, () => {
194
212
  it('parses valid legacy blob', () => {
195
213
  expect(
196
214
  isLegacyBlobRef({
197
- cid: blobCid.toString(),
215
+ cid: validBlobCid.toString(),
198
216
  mimeType: 'image/jpeg',
199
217
  }),
200
218
  ).toBe(true)
201
219
 
202
220
  expect(
203
221
  isLegacyBlobRef({
204
- cid: lexCid.toString(),
222
+ cid: invalidBlobCid.toString(),
205
223
  mimeType: 'image/jpeg',
206
224
  }),
207
225
  ).toBe(true)
@@ -230,21 +248,21 @@ describe(isLegacyBlobRef, () => {
230
248
 
231
249
  expect(
232
250
  isLegacyBlobRef({
233
- cid: lexCid.toString(),
251
+ cid: invalidBlobCid.toString(),
234
252
  mimeType: { toString: () => 'image/jpeg' },
235
253
  }),
236
254
  ).toBe(false)
237
255
 
238
256
  expect(
239
257
  isLegacyBlobRef({
240
- cid: lexCid.toString(),
258
+ cid: invalidBlobCid.toString(),
241
259
  mimeType: 3,
242
260
  }),
243
261
  ).toBe(false)
244
262
 
245
263
  expect(
246
264
  isLegacyBlobRef({
247
- cid: lexCid.toString(),
265
+ cid: invalidBlobCid.toString(),
248
266
  mimeType: '',
249
267
  }),
250
268
  ).toBe(false)
@@ -258,10 +276,114 @@ describe(isLegacyBlobRef, () => {
258
276
  it('rejects extra keys', () => {
259
277
  expect(
260
278
  isLegacyBlobRef({
261
- cid: blobCid.toString(),
279
+ cid: validBlobCid.toString(),
262
280
  mimeType: 'image/jpeg',
263
281
  extra: 'not allowed',
264
282
  }),
265
283
  ).toBe(false)
266
284
  })
267
285
  })
286
+
287
+ describe(enumBlobRefs, () => {
288
+ const valid1: BlobRef = {
289
+ $type: 'blob',
290
+ ref: validBlobCid,
291
+ mimeType: 'image/png',
292
+ size: 2048,
293
+ }
294
+
295
+ const valid2: BlobRef = {
296
+ $type: 'blob',
297
+ ref: validBlobCid,
298
+ mimeType: 'image/jpeg',
299
+ size: 1024,
300
+ }
301
+
302
+ const invalid: BlobRef = {
303
+ $type: 'blob',
304
+ ref: invalidBlobCid,
305
+ mimeType: 'image/jpeg',
306
+ size: 1024,
307
+ }
308
+
309
+ const legacy: LegacyBlobRef = {
310
+ cid: validBlobCid.toString(),
311
+ mimeType: 'image/gif',
312
+ }
313
+
314
+ const data: LexValue = {
315
+ name: 'example',
316
+ file: { deeply: { nested: { in: { object: { valid1 } } } } },
317
+ attachments: [valid2, invalid, legacy, { description: 'not a blob' }],
318
+ }
319
+
320
+ it('enumerates valid BlobRefs by default', () => {
321
+ const refs = Array.from(enumBlobRefs(data))
322
+ expect(refs).toHaveLength(2)
323
+ expect(refs.includes(valid1)).toBe(true)
324
+ expect(refs.includes(valid2)).toBe(true)
325
+ })
326
+
327
+ describe('strict support', () => {
328
+ it('enumerates valid BlobRefs in strict mode', () => {
329
+ const refs = Array.from(enumBlobRefs(data, { strict: true }))
330
+ expect(refs).toHaveLength(2)
331
+ expect(refs.includes(valid1)).toBe(true)
332
+ expect(refs.includes(valid2)).toBe(true)
333
+ })
334
+
335
+ it('enumerates all BlobRefs in non-strict mode', () => {
336
+ const refs = Array.from(enumBlobRefs(data, { strict: false }))
337
+ expect(refs).toHaveLength(3)
338
+ expect(refs.includes(valid1)).toBe(true)
339
+ expect(refs.includes(valid2)).toBe(true)
340
+ expect(refs.includes(invalid)).toBe(true)
341
+ })
342
+ })
343
+
344
+ describe('legacy support', () => {
345
+ it('returns LegacyBlobRefs when legacy option is enabled', () => {
346
+ const refs = Array.from(enumBlobRefs(data, { allowLegacy: true }))
347
+ expect(refs).toHaveLength(3)
348
+ expect(refs.includes(valid1)).toBe(true)
349
+ expect(refs.includes(valid2)).toBe(true)
350
+ expect(refs.includes(legacy)).toBe(true)
351
+ })
352
+ })
353
+
354
+ describe('safety', () => {
355
+ it('handles cyclic structures without infinite loops', () => {
356
+ const cyclicArray: LexArray = [valid2]
357
+ const cyclicObject: LexMap = {
358
+ name: 'cyclic',
359
+ blob: valid1,
360
+ }
361
+
362
+ // Creating a cycle
363
+ cyclicArray.push(cyclicArray)
364
+ cyclicObject.self = cyclicObject
365
+
366
+ const refs = Array.from(
367
+ enumBlobRefs({
368
+ cyclicObject,
369
+ cyclicArray,
370
+ }),
371
+ )
372
+ expect(refs).toHaveLength(2)
373
+ expect(refs.includes(valid1)).toBe(true)
374
+ expect(refs.includes(valid2)).toBe(true)
375
+ })
376
+
377
+ it('handles deep structures without exceeding call stack', () => {
378
+ // Creating a deep nested structure
379
+ let deepData: LexMap = { blob: valid1 }
380
+ for (let i = 0; i < 100_000; i++) {
381
+ deepData = { nested: deepData }
382
+ }
383
+
384
+ const refs = Array.from(enumBlobRefs(deepData))
385
+ expect(refs).toHaveLength(1)
386
+ expect(refs[0]).toBe(valid1)
387
+ })
388
+ })
389
+ })
package/src/blob.ts CHANGED
@@ -5,7 +5,8 @@ import {
5
5
  asCid,
6
6
  parseCid,
7
7
  } from './cid.js'
8
- import { isPlainObject } from './object.js'
8
+ import { LexValue } from './lex.js'
9
+ import { isPlainObject, isPlainProto } from './object.js'
9
10
 
10
11
  /**
11
12
  * @note {@link BlobRef} is just a {@link LexMap} with a specific shape.
@@ -17,9 +18,20 @@ export type BlobRef = {
17
18
  size: number
18
19
  }
19
20
 
21
+ export type BlobRefValidationOptions = {
22
+ /**
23
+ * If `false`, skips strict CID validation of {@link BlobRef.ref}, allowing
24
+ * any valid CID. Otherwise, validates that the CID is v1, uses the raw
25
+ * multicodec, and has a sha256 multihash.
26
+ *
27
+ * @defaults to `true`
28
+ */
29
+ strict?: boolean
30
+ }
31
+
20
32
  export function isBlobRef(
21
33
  input: unknown,
22
- options?: { strict?: boolean },
34
+ options?: BlobRefValidationOptions,
23
35
  ): input is BlobRef {
24
36
  if (!isPlainObject(input)) {
25
37
  return false
@@ -59,7 +71,7 @@ export function isBlobRef(
59
71
  return false
60
72
  }
61
73
 
62
- if (options?.strict) {
74
+ if (options?.strict !== false) {
63
75
  if (cid.version !== 1) {
64
76
  return false
65
77
  }
@@ -110,3 +122,68 @@ export function isLegacyBlobRef(input: unknown): input is LegacyBlobRef {
110
122
 
111
123
  return true
112
124
  }
125
+
126
+ export type EnumBlobRefsOptions = BlobRefValidationOptions & {
127
+ /**
128
+ * @defaults to `false`
129
+ */
130
+ allowLegacy?: boolean
131
+ }
132
+
133
+ /**
134
+ * Enumerates all {@link BlobRef}s (and, optionally, {@link LegacyBlobRef}s)
135
+ * found within a {@link LexValue}.
136
+ */
137
+ export function enumBlobRefs(
138
+ input: LexValue,
139
+ options: EnumBlobRefsOptions & { allowLegacy: true },
140
+ ): Generator<BlobRef | LegacyBlobRef, void, unknown>
141
+ export function enumBlobRefs(
142
+ input: LexValue,
143
+ options?: EnumBlobRefsOptions & { allowLegacy?: false },
144
+ ): Generator<BlobRef, void, unknown>
145
+ export function enumBlobRefs(
146
+ input: LexValue,
147
+ options?: EnumBlobRefsOptions,
148
+ ): Generator<BlobRef | LegacyBlobRef, void, unknown>
149
+ export function* enumBlobRefs(
150
+ input: LexValue,
151
+ options?: EnumBlobRefsOptions,
152
+ ): Generator<BlobRef | LegacyBlobRef, void, unknown> {
153
+ const includeLegacy = options?.allowLegacy === true
154
+
155
+ // Using a stack to avoid recursion depth issues.
156
+ const stack: LexValue[] = [input]
157
+
158
+ // Since we are using a stack, we could end-up in an infinite loop with cyclic
159
+ // structures. Cyclic structures are not valid LexValues and should, thus,
160
+ // never occur, but let's be safe.
161
+ const visited = new Set<object>()
162
+
163
+ do {
164
+ const value = stack.pop()!
165
+
166
+ if (value != null && typeof value === 'object') {
167
+ if (Array.isArray(value)) {
168
+ if (visited.has(value)) continue
169
+ visited.add(value)
170
+ stack.push(...value)
171
+ } else if (isPlainProto(value)) {
172
+ if (visited.has(value)) continue
173
+ visited.add(value)
174
+ if (isBlobRef(value, options)) {
175
+ yield value
176
+ } else if (includeLegacy && isLegacyBlobRef(value)) {
177
+ yield value
178
+ } else {
179
+ for (const v of Object.values(value)) {
180
+ if (v != null) stack.push(v)
181
+ }
182
+ }
183
+ }
184
+ }
185
+ } while (stack.length > 0)
186
+
187
+ // Optimization: ease GC's work
188
+ visited.clear()
189
+ }
@@ -31,7 +31,7 @@ export const fromBase64Native =
31
31
  lastChunkHandling: 'loose',
32
32
  })
33
33
  }
34
- : null
34
+ : /* v8 ignore next -- @preserve */ null
35
35
 
36
36
  export const fromBase64Node = Buffer
37
37
  ? function fromBase64Node(
@@ -14,9 +14,9 @@ for (const toBase64 of [
14
14
  ] as const) {
15
15
  // Tests should run in NodeJS where implementations are either available or
16
16
  // polyfilled (see core-js imports above).
17
- assert(toBase64 !== null, 'toBase64 implementation should not be null')
17
+ assert(toBase64, 'toBase64 implementation should not be null')
18
18
 
19
- describe(toBase64.name, () => {
19
+ describe(toBase64, () => {
20
20
  describe('basic encoding', () => {
21
21
  it('encodes empty Uint8Array', () => {
22
22
  const encoded = toBase64(new Uint8Array(0))
@@ -25,7 +25,7 @@ export const toBase64Native =
25
25
  ): string {
26
26
  return bytes.toBase64!({ alphabet, omitPadding: true })
27
27
  }
28
- : null
28
+ : /* v8 ignore next -- @preserve */ null
29
29
 
30
30
  export const toBase64Node = Buffer
31
31
  ? function toBase64Node(
@@ -45,7 +45,7 @@ export const toBase64Node = Buffer
45
45
  : b64.slice(0, -1) // '='
46
46
  : b64
47
47
  }
48
- : null
48
+ : /* v8 ignore next -- @preserve */ null
49
49
 
50
50
  export function toBase64Ponyfill(
51
51
  bytes: Uint8Array,
@@ -0,0 +1,39 @@
1
+ import { assert, describe, expect, it } from 'vitest'
2
+ import {
3
+ utf8FromBase64Node,
4
+ utf8FromBase64Ponyfill,
5
+ } from './utf8-from-base64.js'
6
+
7
+ const strings = [
8
+ 'Hello, World!',
9
+ '¡Hola, Mundo!',
10
+ 'こんにちは世界',
11
+ '😀👩‍💻🌍',
12
+ '',
13
+ '𓀀𓁐𓂀𓃰𓄿𓅱𓆑𓇋𓈖𓉔𓊃𓋴𓌳𓍿𓎛𓏏',
14
+ ]
15
+
16
+ for (const utf8FromBase64 of [
17
+ utf8FromBase64Node,
18
+ utf8FromBase64Ponyfill,
19
+ ] as const) {
20
+ assert(utf8FromBase64, 'implementation should not be null')
21
+
22
+ describe(utf8FromBase64, () => {
23
+ it('decodes base64 to utf8 string', () => {
24
+ for (const text of strings) {
25
+ const b64 = Buffer.from(text, 'utf8').toString('base64')
26
+ const decoded = utf8FromBase64(b64, 'base64')
27
+ expect(decoded).toBe(text)
28
+ }
29
+ })
30
+
31
+ it('decodes base64url to utf8 string', () => {
32
+ for (const text of strings) {
33
+ const b64u = Buffer.from(text, 'utf8').toString('base64url')
34
+ const decoded = utf8FromBase64(b64u, 'base64url')
35
+ expect(decoded).toBe(text)
36
+ }
37
+ })
38
+ })
39
+ }
@@ -0,0 +1,23 @@
1
+ import { fromString } from 'uint8arrays/from-string'
2
+ import { NodeJSBuffer } from './lib/nodejs-buffer.js'
3
+ import { Base64Alphabet } from './uint8array-base64.js'
4
+
5
+ const Buffer = NodeJSBuffer
6
+
7
+ export const utf8FromBase64Node = Buffer
8
+ ? function utf8FromBase64Node(
9
+ b64: string,
10
+ alphabet: Base64Alphabet = 'base64',
11
+ ): string {
12
+ return Buffer.from(b64, alphabet).toString('utf8')
13
+ }
14
+ : /* v8 ignore next -- @preserve */ null
15
+
16
+ const textDecoder = /*#__PURE__*/ new TextDecoder()
17
+ export function utf8FromBase64Ponyfill(
18
+ b64: string,
19
+ alphabet?: Base64Alphabet,
20
+ ): string {
21
+ const bytes = fromString(b64, alphabet)
22
+ return textDecoder.decode(bytes)
23
+ }
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import { graphemeLenNative, graphemeLenPonyfill } from './utf8-grapheme-len.js'
3
3
 
4
- describe('graphemeLenSegmenter', () => {
4
+ describe(graphemeLenNative!, () => {
5
5
  it('computes grapheme length', () => {
6
6
  expect(graphemeLenNative!('a')).toBe(1)
7
7
  expect(graphemeLenNative!('~')).toBe(1)
@@ -19,7 +19,7 @@ describe('graphemeLenSegmenter', () => {
19
19
  })
20
20
  })
21
21
 
22
- describe('graphemeLenInternal', () => {
22
+ describe(graphemeLenPonyfill, () => {
23
23
  it('computes grapheme length', () => {
24
24
  expect(graphemeLenPonyfill('a')).toBe(1)
25
25
  expect(graphemeLenPonyfill('~')).toBe(1)
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import { utf8LenCompute, utf8LenNode } from './utf8-len.js'
3
3
 
4
- describe('utf8LenNode', () => {
4
+ describe(utf8LenNode!, () => {
5
5
  it('computes utf8 string length', () => {
6
6
  expect(utf8LenNode!('a')).toBe(1)
7
7
  expect(utf8LenNode!('~')).toBe(1)
@@ -16,7 +16,7 @@ describe('utf8LenNode', () => {
16
16
  })
17
17
  })
18
18
 
19
- describe('utf8LenInternal', () => {
19
+ describe(utf8LenCompute, () => {
20
20
  it('computes utf8 string length', () => {
21
21
  expect(utf8LenCompute('a')).toBe(1)
22
22
  expect(utf8LenCompute('~')).toBe(1)
@@ -0,0 +1,35 @@
1
+ import { assert, describe, expect, it } from 'vitest'
2
+ import { utf8ToBase64Node, utf8ToBase64Ponyfill } from './utf8-to-base64.js'
3
+
4
+ const strings = [
5
+ 'Hello, World!',
6
+ '¡Hola, Mundo!',
7
+ 'こんにちは世界',
8
+ '😀👩‍💻🌍',
9
+ '',
10
+ '𓀀𓁐𓂀𓃰𓄿𓅱𓆑𓇋𓈖𓉔𓊃𓋴𓌳𓍿𓎛𓏏',
11
+ ]
12
+
13
+ for (const utf8ToBase64 of [utf8ToBase64Node, utf8ToBase64Ponyfill] as const) {
14
+ assert(utf8ToBase64, 'implementation should not be null')
15
+
16
+ describe(utf8ToBase64, () => {
17
+ it('encodes utf8 string to base64', () => {
18
+ for (const text of strings) {
19
+ const b64 = Buffer.from(text, 'utf8')
20
+ .toString('base64')
21
+ .replaceAll('=', '') // utf8ToBase64 omits padding
22
+ const encoded = utf8ToBase64(text, 'base64')
23
+ expect(encoded).toBe(b64)
24
+ }
25
+ })
26
+
27
+ it('encodes utf8 string to base64url', () => {
28
+ for (const text of strings) {
29
+ const b64u = Buffer.from(text, 'utf8').toString('base64url')
30
+ const encoded = utf8ToBase64(text, 'base64url')
31
+ expect(encoded).toBe(b64u)
32
+ }
33
+ })
34
+ })
35
+ }
@@ -0,0 +1,22 @@
1
+ import { toString } from 'uint8arrays/to-string'
2
+ import { NodeJSBuffer } from './lib/nodejs-buffer.js'
3
+ import { Base64Alphabet } from './uint8array-base64.js'
4
+ import { toBase64Node } from './uint8array-to-base64.js'
5
+
6
+ const Buffer = NodeJSBuffer
7
+
8
+ export const utf8ToBase64Node = Buffer
9
+ ? function utf8ToBase64Node(text: string, alphabet?: Base64Alphabet): string {
10
+ const buffer = Buffer.from(text, 'utf8')
11
+ return toBase64Node!(buffer, alphabet)
12
+ }
13
+ : /* v8 ignore next -- @preserve */ null
14
+
15
+ const textEncoder = /*#__PURE__*/ new TextEncoder()
16
+ export function utf8ToBase64Ponyfill(
17
+ text: string,
18
+ alphabet?: Base64Alphabet,
19
+ ): string {
20
+ const bytes = textEncoder.encode(text)
21
+ return toString(bytes, alphabet)
22
+ }
package/src/utf8.ts CHANGED
@@ -1,9 +1,17 @@
1
+ import { Base64Alphabet } from './uint8array.js'
2
+ import {
3
+ utf8FromBase64Node,
4
+ utf8FromBase64Ponyfill,
5
+ } from './utf8-from-base64.js'
1
6
  import { graphemeLenNative, graphemeLenPonyfill } from './utf8-grapheme-len.js'
2
7
  import { utf8LenCompute, utf8LenNode } from './utf8-len.js'
8
+ import { utf8ToBase64Node, utf8ToBase64Ponyfill } from './utf8-to-base64.js'
3
9
 
4
10
  export const graphemeLen: (str: string) => number =
5
- graphemeLenNative ?? graphemeLenPonyfill
11
+ /* v8 ignore next -- @preserve */ graphemeLenNative ??
12
+ /* v8 ignore next -- @preserve */ graphemeLenPonyfill
6
13
 
14
+ /* v8 ignore next -- @preserve */
7
15
  if (graphemeLen === graphemeLenPonyfill) {
8
16
  /*#__PURE__*/
9
17
  console.warn(
@@ -11,4 +19,17 @@ if (graphemeLen === graphemeLenPonyfill) {
11
19
  )
12
20
  }
13
21
 
14
- export const utf8Len: (string: string) => number = utf8LenNode ?? utf8LenCompute
22
+ export const utf8Len: (string: string) => number =
23
+ /* v8 ignore next -- @preserve */ utf8LenNode ??
24
+ /* v8 ignore next -- @preserve */ utf8LenCompute
25
+
26
+ export const utf8ToBase64: (str: string, alphabet?: Base64Alphabet) => string =
27
+ /* v8 ignore next -- @preserve */ utf8ToBase64Node ??
28
+ /* v8 ignore next -- @preserve */ utf8ToBase64Ponyfill
29
+
30
+ export const utf8FromBase64: (
31
+ b64: string,
32
+ alphabet?: Base64Alphabet,
33
+ ) => string =
34
+ /* v8 ignore next -- @preserve */ utf8FromBase64Node ??
35
+ /* v8 ignore next -- @preserve */ utf8FromBase64Ponyfill