@atcute/cid 2.2.5 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,87 @@
1
1
  # @atcute/cid
2
2
 
3
- lightweight [DASL CID][dasl-cid] codec library for AT Protocol.
3
+ content identifier (CID) codec for AT Protocol.
4
+
5
+ ```sh
6
+ npm install @atcute/cid
7
+ ```
8
+
9
+ this library implements DASL's [CID][dasl-cid] format used by AT Protocol to address resources by
10
+ their contents.
4
11
 
5
12
  [dasl-cid]: https://dasl.ing/cid.html
6
13
 
14
+ ## usage
15
+
16
+ ### creating CIDs
17
+
7
18
  ```ts
8
19
  import * as CID from '@atcute/cid';
9
20
 
21
+ // create a CID from DAG-CBOR data
22
+ const cid = await CID.create(0x71, cborBytes);
23
+ // -> { version: 1, codec: 113, digest: { ... }, bytes: Uint8Array(36) }
24
+
25
+ // create from raw data
26
+ const rawCid = await CID.create(0x55, rawBytes);
27
+
28
+ // create an empty CID (zero-length digest)
29
+ const empty = CID.createEmpty(0x71);
30
+ ```
31
+
32
+ ### parsing CIDs
33
+
34
+ ```ts
35
+ import * as CID from '@atcute/cid';
36
+
37
+ // parse from base32 string
10
38
  const cid = CID.fromString('bafyreihffx5a2e7k5uwrmmgofbvzujc5cmw5h4espouwuxt3liqoflx3ee');
11
- // ^? { version: 1, codec: 113, digest: { ... }, bytes: Uint8Array(36) }
12
39
 
13
- // Creating a CID containing CBOR data
14
- const cid = await CID.create(0x71, buffer);
40
+ // parse from binary (with 0x00 prefix)
41
+ const cid = CID.fromBinary(bytes);
42
+
43
+ // parse from raw CID bytes
44
+ const cid = CID.decode(cidBytes);
45
+ ```
46
+
47
+ ### serializing CIDs
48
+
49
+ ```ts
50
+ import * as CID from '@atcute/cid';
51
+
52
+ // to base32 string
53
+ CID.toString(cid);
54
+ // -> "bafyreihffx5a2e7k5uwrmmgofbvzujc5cmw5h4espouwuxt3liqoflx3ee"
55
+
56
+ // to binary (with 0x00 prefix)
57
+ CID.toBinary(cid);
58
+ // -> Uint8Array(37)
59
+ ```
60
+
61
+ ### comparing CIDs
62
+
63
+ ```ts
64
+ import * as CID from '@atcute/cid';
65
+
66
+ CID.equals(cidA, cidB); // true if same content hash
67
+ ```
68
+
69
+ ### CidLink wrapper
70
+
71
+ for JSON serialization compatible with atproto's data model:
72
+
73
+ ```ts
74
+ import { toCidLink, fromCidLink, isCidLink } from '@atcute/cid';
75
+
76
+ // convert Cid to CidLink (has $link property)
77
+ const link = toCidLink(cid);
78
+ // -> { $link: "bafyrei..." }
79
+
80
+ // convert back to Cid
81
+ const cid = fromCidLink(link);
15
82
 
16
- // Serializing CID into string
17
- CID.toString(cid); // -> bafyrei...
83
+ // type guard
84
+ if (isCidLink(value)) {
85
+ console.log(value.$link);
86
+ }
18
87
  ```
@@ -4,7 +4,7 @@ export interface CidLink {
4
4
  }
5
5
  export declare class CidLinkWrapper implements CidLink {
6
6
  readonly bytes: Uint8Array;
7
- constructor(bytes: Uint8Array, str?: string);
7
+ constructor(bytes: Uint8Array);
8
8
  get $link(): string;
9
9
  toJSON(): CidLink;
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cid-link.d.ts","sourceRoot":"","sources":["../lib/cid-link.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAI1D,MAAM,WAAW,OAAO;IACvB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,cAAe,YAAW,OAAO;IAM7C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;gBAEf,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,MAAM;IAK3C,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,MAAM,IAAI,OAAO;CAGjB;AAED,eAAO,MAAM,SAAS,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,OAOnD,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,KAAK,GAAG,KAAG,OAEpC,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,MAAM,OAAO,KAAG,GAM3C,CAAC"}
1
+ {"version":3,"file":"cid-link.d.ts","sourceRoot":"","sources":["../lib/cid-link.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2C,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAO/E,MAAM,WAAW,OAAO;IACvB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,cAAe,YAAW,OAAO;IAI7C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAE3B,YAAY,KAAK,EAAE,UAAU,EAE5B;IAED,IAAI,KAAK,IAAI,MAAM,CASlB;IAED,MAAM,IAAI,OAAO,CAEhB;CACD;AAED,eAAO,MAAM,SAAS,sCAOrB,CAAC;AAEF,eAAO,MAAM,SAAS,uBASrB,CAAC;AAEF,eAAO,MAAM,WAAW,wBAMvB,CAAC"}
package/dist/cid-link.js CHANGED
@@ -1,18 +1,22 @@
1
1
  import { toBase32 } from '@atcute/multibase';
2
- import { decode, fromString } from './codec.js';
2
+ import { CID_STRINGIFY_CACHE, decode, fromString } from './codec.js';
3
3
  const CID_LINK_SYMBOL = Symbol.for('@atcute/cid-link-wrapper');
4
+ /** @internal */
5
+ export const CIDLINK_STRINGIFY_CACHE = new WeakMap();
4
6
  export class CidLinkWrapper {
5
7
  /** @internal */
6
8
  [CID_LINK_SYMBOL] = true;
7
- /** @internal */
8
- _str;
9
9
  bytes;
10
- constructor(bytes, str) {
10
+ constructor(bytes) {
11
11
  this.bytes = bytes;
12
- this._str = str;
13
12
  }
14
13
  get $link() {
15
- return (this._str ??= `b${toBase32(this.bytes)}`);
14
+ let str = CIDLINK_STRINGIFY_CACHE.get(this);
15
+ if (str === undefined) {
16
+ str = `b${toBase32(this.bytes)}`;
17
+ CIDLINK_STRINGIFY_CACHE.set(this, str);
18
+ }
19
+ return str;
16
20
  }
17
21
  toJSON() {
18
22
  return { $link: this.$link };
@@ -24,7 +28,12 @@ export const isCidLink = (value) => {
24
28
  (val !== null && typeof val === 'object' && typeof val.$link === 'string'));
25
29
  };
26
30
  export const toCidLink = (cid) => {
27
- return new CidLinkWrapper(cid.bytes, cid._str);
31
+ const inst = new CidLinkWrapper(cid.bytes);
32
+ const str = CID_STRINGIFY_CACHE.get(cid);
33
+ if (str !== undefined) {
34
+ CIDLINK_STRINGIFY_CACHE.set(inst, str);
35
+ }
36
+ return inst;
28
37
  };
29
38
  export const fromCidLink = (link) => {
30
39
  if (link instanceof CidLinkWrapper) {
@@ -1 +1 @@
1
- {"version":3,"file":"cid-link.js","sourceRoot":"","sources":["../lib/cid-link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAY,MAAM,YAAY,CAAC;AAE1D,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAM/D,MAAM,OAAO,cAAc;IAC1B,gBAAgB;IACP,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC;IAClC,gBAAgB;IAChB,IAAI,CAAqB;IAEhB,KAAK,CAAa;IAE3B,YAAY,KAAiB,EAAE,GAAY;QAC1C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,IAAI,KAAK;QACR,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,MAAM;QACL,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;CACD;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAc,EAAoB,EAAE;IAC7D,MAAM,GAAG,GAAG,KAAY,CAAC;IAEzB,OAAO,CACN,GAAG,YAAY,cAAc;QAC7B,CAAC,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAC1E,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAQ,EAAW,EAAE;IAC9C,OAAO,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAa,EAAO,EAAE;IACjD,IAAI,IAAI,YAAY,cAAc,EAAE,CAAC;QACpC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC,CAAC"}
1
+ {"version":3,"file":"cid-link.js","sourceRoot":"","sources":["../lib/cid-link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,UAAU,EAAY,MAAM,YAAY,CAAC;AAE/E,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAE/D,gBAAgB;AAChB,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,OAAO,EAA0B,CAAC;AAM7E,MAAM,OAAO,cAAc;IAC1B,gBAAgB;IACP,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC;IAEzB,KAAK,CAAa;IAE3B,YAAY,KAAiB,EAAE;QAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAAA,CACnB;IAED,IAAI,KAAK,GAAW;QACnB,IAAI,GAAG,GAAG,uBAAuB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACvB,GAAG,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAEjC,uBAAuB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACxC,CAAC;QAED,OAAO,GAAG,CAAC;IAAA,CACX;IAED,MAAM,GAAY;QACjB,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAAA,CAC7B;CACD;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAc,EAAoB,EAAE,CAAC;IAC9D,MAAM,GAAG,GAAG,KAAY,CAAC;IAEzB,OAAO,CACN,GAAG,YAAY,cAAc;QAC7B,CAAC,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAC1E,CAAC;AAAA,CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAQ,EAAW,EAAE,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAEzC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACvB,uBAAuB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,IAAI,CAAC;AAAA,CACZ,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAa,EAAO,EAAE,CAAC;IAClD,IAAI,IAAI,YAAY,cAAc,EAAE,CAAC;QACpC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAAA,CAC9B,CAAC"}
package/dist/codec.d.ts CHANGED
@@ -1,6 +1,10 @@
1
+ /** CID version, always `1` for CIDv1 */
1
2
  export declare const CID_VERSION = 1;
3
+ /** multicodec for SHA-256 hash */
2
4
  export declare const HASH_SHA256 = 18;
5
+ /** multicodec for raw binary data */
3
6
  export declare const CODEC_RAW = 85;
7
+ /** multicodec for DAG-CBOR encoded data */
4
8
  export declare const CODEC_DCBOR = 113;
5
9
  /**
6
10
  * Represents a Content Identifier (CID), in particular, a limited subset of
@@ -22,13 +26,73 @@ export interface Cid {
22
26
  /** Raw CID bytes */
23
27
  readonly bytes: Uint8Array;
24
28
  }
25
- export declare const create: (codec: 85 | 113, data: Uint8Array) => Promise<Cid>;
29
+ /**
30
+ * creates a CID from a pre-computed SHA-256 digest
31
+ * @param codec multicodec type for the data
32
+ * @param digest raw SHA-256 hash bytes (must be 32 bytes)
33
+ * @returns CID object
34
+ */
35
+ export declare const fromDigest: (codec: 85 | 113, digest: Uint8Array<ArrayBufferLike>) => Cid;
36
+ /**
37
+ * creates a CID by hashing the provided data with SHA-256
38
+ * @param codec multicodec type for the data
39
+ * @param data raw data to hash
40
+ * @returns CID object
41
+ */
42
+ export declare const create: (codec: 85 | 113, data: Uint8Array<ArrayBufferLike>) => Promise<Cid>;
43
+ /**
44
+ * creates an empty CID with a zero-length digest
45
+ * @param codec multicodec type for the data
46
+ * @returns CID object with empty digest
47
+ */
26
48
  export declare const createEmpty: (codec: 85 | 113) => Cid;
27
- export declare const decodeFirst: (bytes: Uint8Array) => [decoded: Cid, remainder: Uint8Array];
28
- export declare const decode: (bytes: Uint8Array) => Cid;
49
+ /**
50
+ * decodes a CID from bytes, returning the CID and any remaining bytes
51
+ * @param bytes raw CID bytes
52
+ * @returns tuple of decoded CID and remainder bytes
53
+ * @throws {RangeError} if the bytes are too short or contain invalid values
54
+ */
55
+ export declare const decodeFirst: (bytes: Uint8Array<ArrayBufferLike>) => [decoded: Cid, remainder: Uint8Array<ArrayBufferLike>];
56
+ /**
57
+ * decodes a CID from bytes, expecting no remainder
58
+ * @param bytes raw CID bytes
59
+ * @returns decoded CID
60
+ * @throws {RangeError} if the bytes are invalid or contain extra data
61
+ */
62
+ export declare const decode: (bytes: Uint8Array<ArrayBufferLike>) => Cid;
63
+ /**
64
+ * parses a CID from a multibase base32 string
65
+ * @param input base32-encoded CID string (with 'b' prefix)
66
+ * @returns decoded CID
67
+ * @throws {SyntaxError} if the string is not a valid multibase base32 string
68
+ * @throws {RangeError} if the string length is invalid
69
+ */
29
70
  export declare const fromString: (input: string) => Cid;
71
+ /**
72
+ * encodes a CID to a multibase base32 string
73
+ * @param cid CID to encode
74
+ * @returns base32-encoded string with 'b' prefix
75
+ */
30
76
  export declare const toString: (cid: Cid) => string;
31
- export declare const fromBinary: (input: Uint8Array) => Cid;
32
- export declare const toBinary: (cid: Cid) => Uint8Array;
77
+ /**
78
+ * parses a CID from binary format (with 0x00 prefix)
79
+ * @param input binary CID bytes with 0x00 prefix
80
+ * @returns decoded CID
81
+ * @throws {RangeError} if the byte length is invalid
82
+ * @throws {SyntaxError} if the prefix byte is not 0x00
83
+ */
84
+ export declare const fromBinary: (input: Uint8Array<ArrayBufferLike>) => Cid;
85
+ /**
86
+ * encodes a CID to binary format (with 0x00 prefix)
87
+ * @param cid CID to encode
88
+ * @returns binary CID bytes with 0x00 prefix
89
+ */
90
+ export declare const toBinary: (cid: Cid) => Uint8Array<ArrayBufferLike>;
91
+ /**
92
+ * checks if two CIDs are equal
93
+ * @param a first CID
94
+ * @param b second CID
95
+ * @returns true if the CIDs have identical bytes
96
+ */
33
97
  export declare const equals: (a: Cid, b: Cid) => boolean;
34
98
  //# sourceMappingURL=codec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../lib/codec.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,WAAW,KAAO,CAAC;AAEhC,eAAO,MAAM,SAAS,KAAO,CAAC;AAC9B,eAAO,MAAM,WAAW,MAAO,CAAC;AAEhC;;;;GAIG;AACH,MAAM,WAAW,GAAG;IACnB,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,QAAQ,CAAC,MAAM,EAAE;QAChB,wEAAwE;QACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,qBAAqB;QACrB,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;KAC9B,CAAC;IACF,oBAAoB;IACpB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAI3B;AAKD,eAAO,MAAM,MAAM,GAAU,OAAO,EAAI,GAAG,GAAI,EAAE,MAAM,UAAU,KAAG,OAAO,CAAC,GAAG,CA2B9E,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,EAAI,GAAG,GAAI,KAAG,GAgBhD,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,UAAU,KAAG,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,CA4CnF,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,OAAO,UAAU,KAAG,GAQ1C,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,GAgB1C,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,GAAG,KAAG,MAEnC,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,OAAO,UAAU,KAAG,GAa9C,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,GAAG,KAAG,UAMnC,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,GAAG,GAAG,EAAE,GAAG,GAAG,KAAG,OAMvC,CAAC"}
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../lib/codec.ts"],"names":[],"mappings":"AAGA,wCAAwC;AACxC,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,kCAAkC;AAClC,eAAO,MAAM,WAAW,KAAO,CAAC;AAEhC,qCAAqC;AACrC,eAAO,MAAM,SAAS,KAAO,CAAC;AAC9B,2CAA2C;AAC3C,eAAO,MAAM,WAAW,MAAO,CAAC;AAKhC;;;;GAIG;AACH,MAAM,WAAW,GAAG;IACnB,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,QAAQ,CAAC,MAAM,EAAE;QAChB,wEAAwE;QACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,qBAAqB;QACrB,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;KAC9B,CAAC;IACF,oBAAoB;IACpB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC3B;AAKD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,+DAuBtB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,MAAM,sEAGlB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,0BAevB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,gGA2CvB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,MAAM,6CAQlB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,wBAgBtB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,QAAQ,sBASpB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,6CAatB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,QAAQ,2CAMpB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,MAAM,6BAElB,CAAC"}
package/dist/codec.js CHANGED
@@ -1,13 +1,24 @@
1
1
  import { fromBase32, toBase32 } from '@atcute/multibase';
2
2
  import { allocUnsafe, toSha256, equals as isBufferEqual } from '@atcute/uint8array';
3
+ /** CID version, always `1` for CIDv1 */
3
4
  export const CID_VERSION = 1;
5
+ /** multicodec for SHA-256 hash */
4
6
  export const HASH_SHA256 = 0x12;
7
+ /** multicodec for raw binary data */
5
8
  export const CODEC_RAW = 0x55;
9
+ /** multicodec for DAG-CBOR encoded data */
6
10
  export const CODEC_DCBOR = 0x71;
11
+ /** @internal */
12
+ export const CID_STRINGIFY_CACHE = new WeakMap();
7
13
  // a SHA-256 CIDv1 is always going to be 36 bytes, that's 4 bytes for the
8
14
  // header, and 32 bytes for the digest itself.
9
- export const create = async (codec, data) => {
10
- const digest = await toSha256(data);
15
+ /**
16
+ * creates a CID from a pre-computed SHA-256 digest
17
+ * @param codec multicodec type for the data
18
+ * @param digest raw SHA-256 hash bytes (must be 32 bytes)
19
+ * @returns CID object
20
+ */
21
+ export const fromDigest = (codec, digest) => {
11
22
  if (digest.length !== 32) {
12
23
  throw new RangeError(`invalid digest length`);
13
24
  }
@@ -17,7 +28,7 @@ export const create = async (codec, data) => {
17
28
  bytes[2] = HASH_SHA256;
18
29
  bytes[3] = 32;
19
30
  bytes.set(digest, 4);
20
- const cid = {
31
+ return {
21
32
  version: CID_VERSION,
22
33
  codec: codec,
23
34
  digest: {
@@ -25,10 +36,23 @@ export const create = async (codec, data) => {
25
36
  contents: bytes.subarray(4, 36),
26
37
  },
27
38
  bytes: bytes,
28
- _str: undefined,
29
39
  };
30
- return cid;
31
40
  };
41
+ /**
42
+ * creates a CID by hashing the provided data with SHA-256
43
+ * @param codec multicodec type for the data
44
+ * @param data raw data to hash
45
+ * @returns CID object
46
+ */
47
+ export const create = async (codec, data) => {
48
+ const digest = await toSha256(data);
49
+ return fromDigest(codec, digest);
50
+ };
51
+ /**
52
+ * creates an empty CID with a zero-length digest
53
+ * @param codec multicodec type for the data
54
+ * @returns CID object with empty digest
55
+ */
32
56
  export const createEmpty = (codec) => {
33
57
  const bytes = Uint8Array.from([CID_VERSION, codec, HASH_SHA256, 0]);
34
58
  const digest = bytes.subarray(4);
@@ -40,10 +64,15 @@ export const createEmpty = (codec) => {
40
64
  contents: digest,
41
65
  },
42
66
  bytes: bytes,
43
- _str: undefined,
44
67
  };
45
68
  return cid;
46
69
  };
70
+ /**
71
+ * decodes a CID from bytes, returning the CID and any remaining bytes
72
+ * @param bytes raw CID bytes
73
+ * @returns tuple of decoded CID and remainder bytes
74
+ * @throws {RangeError} if the bytes are too short or contain invalid values
75
+ */
47
76
  export const decodeFirst = (bytes) => {
48
77
  const length = bytes.length;
49
78
  if (length < 4) {
@@ -76,10 +105,15 @@ export const decodeFirst = (bytes) => {
76
105
  contents: bytes.subarray(4, 4 + digestSize),
77
106
  },
78
107
  bytes: bytes.subarray(0, 4 + digestSize),
79
- _str: undefined,
80
108
  };
81
109
  return [cid, bytes.subarray(4 + digestSize)];
82
110
  };
111
+ /**
112
+ * decodes a CID from bytes, expecting no remainder
113
+ * @param bytes raw CID bytes
114
+ * @returns decoded CID
115
+ * @throws {RangeError} if the bytes are invalid or contain extra data
116
+ */
83
117
  export const decode = (bytes) => {
84
118
  const [cid, remainder] = decodeFirst(bytes);
85
119
  if (remainder.length !== 0) {
@@ -87,6 +121,13 @@ export const decode = (bytes) => {
87
121
  }
88
122
  return cid;
89
123
  };
124
+ /**
125
+ * parses a CID from a multibase base32 string
126
+ * @param input base32-encoded CID string (with 'b' prefix)
127
+ * @returns decoded CID
128
+ * @throws {SyntaxError} if the string is not a valid multibase base32 string
129
+ * @throws {RangeError} if the string length is invalid
130
+ */
90
131
  export const fromString = (input) => {
91
132
  if (input.length < 2 || input[0] !== 'b') {
92
133
  throw new SyntaxError(`not a multibase base32 string`);
@@ -98,12 +139,29 @@ export const fromString = (input) => {
98
139
  }
99
140
  const bytes = fromBase32(input.slice(1));
100
141
  const cid = decode(bytes);
101
- cid._str = input;
142
+ CID_STRINGIFY_CACHE.set(cid, input);
102
143
  return cid;
103
144
  };
145
+ /**
146
+ * encodes a CID to a multibase base32 string
147
+ * @param cid CID to encode
148
+ * @returns base32-encoded string with 'b' prefix
149
+ */
104
150
  export const toString = (cid) => {
105
- return (cid._str ??= `b${toBase32(cid.bytes)}`);
151
+ let str = CID_STRINGIFY_CACHE.get(cid);
152
+ if (str === undefined) {
153
+ str = `b${toBase32(cid.bytes)}`;
154
+ CID_STRINGIFY_CACHE.set(cid, str);
155
+ }
156
+ return str;
106
157
  };
158
+ /**
159
+ * parses a CID from binary format (with 0x00 prefix)
160
+ * @param input binary CID bytes with 0x00 prefix
161
+ * @returns decoded CID
162
+ * @throws {RangeError} if the byte length is invalid
163
+ * @throws {SyntaxError} if the prefix byte is not 0x00
164
+ */
107
165
  export const fromBinary = (input) => {
108
166
  // 4 bytes + 1 byte for the 0x00 prefix
109
167
  // 36 bytes + 1 byte for the 0x00 prefix
@@ -116,16 +174,24 @@ export const fromBinary = (input) => {
116
174
  const bytes = input.subarray(1);
117
175
  return decode(bytes);
118
176
  };
177
+ /**
178
+ * encodes a CID to binary format (with 0x00 prefix)
179
+ * @param cid CID to encode
180
+ * @returns binary CID bytes with 0x00 prefix
181
+ */
119
182
  export const toBinary = (cid) => {
120
183
  const bytes = allocUnsafe(1 + cid.bytes.length);
121
184
  bytes[0] = 0;
122
185
  bytes.set(cid.bytes, 1);
123
186
  return bytes;
124
187
  };
188
+ /**
189
+ * checks if two CIDs are equal
190
+ * @param a first CID
191
+ * @param b second CID
192
+ * @returns true if the CIDs have identical bytes
193
+ */
125
194
  export const equals = (a, b) => {
126
- if (a._str !== undefined && b._str !== undefined) {
127
- return a._str === b._str;
128
- }
129
195
  return isBufferEqual(a.bytes, b.bytes);
130
196
  };
131
197
  //# sourceMappingURL=codec.js.map
package/dist/codec.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"codec.js","sourceRoot":"","sources":["../lib/codec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../lib/codec.ts"],"names":[],"mappings":"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"}
package/lib/cid-link.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import { toBase32 } from '@atcute/multibase';
2
2
 
3
- import { decode, fromString, type Cid } from './codec.js';
3
+ import { CID_STRINGIFY_CACHE, decode, fromString, type Cid } from './codec.js';
4
4
 
5
5
  const CID_LINK_SYMBOL = Symbol.for('@atcute/cid-link-wrapper');
6
6
 
7
+ /** @internal */
8
+ export const CIDLINK_STRINGIFY_CACHE = new WeakMap<CidLinkWrapper, string>();
9
+
7
10
  export interface CidLink {
8
11
  $link: string;
9
12
  }
@@ -11,18 +14,22 @@ export interface CidLink {
11
14
  export class CidLinkWrapper implements CidLink {
12
15
  /** @internal */
13
16
  readonly [CID_LINK_SYMBOL] = true;
14
- /** @internal */
15
- _str: string | undefined;
16
17
 
17
18
  readonly bytes: Uint8Array;
18
19
 
19
- constructor(bytes: Uint8Array, str?: string) {
20
+ constructor(bytes: Uint8Array) {
20
21
  this.bytes = bytes;
21
- this._str = str;
22
22
  }
23
23
 
24
24
  get $link(): string {
25
- return (this._str ??= `b${toBase32(this.bytes)}`);
25
+ let str = CIDLINK_STRINGIFY_CACHE.get(this);
26
+ if (str === undefined) {
27
+ str = `b${toBase32(this.bytes)}`;
28
+
29
+ CIDLINK_STRINGIFY_CACHE.set(this, str);
30
+ }
31
+
32
+ return str;
26
33
  }
27
34
 
28
35
  toJSON(): CidLink {
@@ -40,7 +47,14 @@ export const isCidLink = (value: unknown): value is CidLink => {
40
47
  };
41
48
 
42
49
  export const toCidLink = (cid: Cid): CidLink => {
43
- return new CidLinkWrapper(cid.bytes, cid._str);
50
+ const inst = new CidLinkWrapper(cid.bytes);
51
+ const str = CID_STRINGIFY_CACHE.get(cid);
52
+
53
+ if (str !== undefined) {
54
+ CIDLINK_STRINGIFY_CACHE.set(inst, str);
55
+ }
56
+
57
+ return inst;
44
58
  };
45
59
 
46
60
  export const fromCidLink = (link: CidLink): Cid => {
package/lib/codec.ts CHANGED
@@ -1,12 +1,19 @@
1
1
  import { fromBase32, toBase32 } from '@atcute/multibase';
2
2
  import { allocUnsafe, toSha256, equals as isBufferEqual } from '@atcute/uint8array';
3
3
 
4
+ /** CID version, always `1` for CIDv1 */
4
5
  export const CID_VERSION = 1;
6
+ /** multicodec for SHA-256 hash */
5
7
  export const HASH_SHA256 = 0x12;
6
8
 
9
+ /** multicodec for raw binary data */
7
10
  export const CODEC_RAW = 0x55;
11
+ /** multicodec for DAG-CBOR encoded data */
8
12
  export const CODEC_DCBOR = 0x71;
9
13
 
14
+ /** @internal */
15
+ export const CID_STRINGIFY_CACHE = new WeakMap<Cid, string>();
16
+
10
17
  /**
11
18
  * Represents a Content Identifier (CID), in particular, a limited subset of
12
19
  * CIDv1 as described by DASL specifications.
@@ -26,16 +33,18 @@ export interface Cid {
26
33
  };
27
34
  /** Raw CID bytes */
28
35
  readonly bytes: Uint8Array;
29
-
30
- /** @internal */
31
- _str: string | undefined;
32
36
  }
33
37
 
34
38
  // a SHA-256 CIDv1 is always going to be 36 bytes, that's 4 bytes for the
35
39
  // header, and 32 bytes for the digest itself.
36
40
 
37
- export const create = async (codec: 0x55 | 0x71, data: Uint8Array): Promise<Cid> => {
38
- const digest = await toSha256(data);
41
+ /**
42
+ * creates a CID from a pre-computed SHA-256 digest
43
+ * @param codec multicodec type for the data
44
+ * @param digest raw SHA-256 hash bytes (must be 32 bytes)
45
+ * @returns CID object
46
+ */
47
+ export const fromDigest = (codec: 0x55 | 0x71, digest: Uint8Array): Cid => {
39
48
  if (digest.length !== 32) {
40
49
  throw new RangeError(`invalid digest length`);
41
50
  }
@@ -49,7 +58,7 @@ export const create = async (codec: 0x55 | 0x71, data: Uint8Array): Promise<Cid>
49
58
 
50
59
  bytes.set(digest, 4);
51
60
 
52
- const cid: Cid = {
61
+ return {
53
62
  version: CID_VERSION,
54
63
  codec: codec,
55
64
  digest: {
@@ -57,12 +66,25 @@ export const create = async (codec: 0x55 | 0x71, data: Uint8Array): Promise<Cid>
57
66
  contents: bytes.subarray(4, 36),
58
67
  },
59
68
  bytes: bytes,
60
- _str: undefined,
61
69
  };
70
+ };
62
71
 
63
- return cid;
72
+ /**
73
+ * creates a CID by hashing the provided data with SHA-256
74
+ * @param codec multicodec type for the data
75
+ * @param data raw data to hash
76
+ * @returns CID object
77
+ */
78
+ export const create = async (codec: 0x55 | 0x71, data: Uint8Array): Promise<Cid> => {
79
+ const digest = await toSha256(data);
80
+ return fromDigest(codec, digest);
64
81
  };
65
82
 
83
+ /**
84
+ * creates an empty CID with a zero-length digest
85
+ * @param codec multicodec type for the data
86
+ * @returns CID object with empty digest
87
+ */
66
88
  export const createEmpty = (codec: 0x55 | 0x71): Cid => {
67
89
  const bytes = Uint8Array.from([CID_VERSION, codec, HASH_SHA256, 0]);
68
90
  const digest = bytes.subarray(4);
@@ -75,12 +97,17 @@ export const createEmpty = (codec: 0x55 | 0x71): Cid => {
75
97
  contents: digest,
76
98
  },
77
99
  bytes: bytes,
78
- _str: undefined,
79
100
  };
80
101
 
81
102
  return cid;
82
103
  };
83
104
 
105
+ /**
106
+ * decodes a CID from bytes, returning the CID and any remaining bytes
107
+ * @param bytes raw CID bytes
108
+ * @returns tuple of decoded CID and remainder bytes
109
+ * @throws {RangeError} if the bytes are too short or contain invalid values
110
+ */
84
111
  export const decodeFirst = (bytes: Uint8Array): [decoded: Cid, remainder: Uint8Array] => {
85
112
  const length = bytes.length;
86
113
 
@@ -121,12 +148,17 @@ export const decodeFirst = (bytes: Uint8Array): [decoded: Cid, remainder: Uint8A
121
148
  contents: bytes.subarray(4, 4 + digestSize),
122
149
  },
123
150
  bytes: bytes.subarray(0, 4 + digestSize),
124
- _str: undefined,
125
151
  };
126
152
 
127
153
  return [cid, bytes.subarray(4 + digestSize)];
128
154
  };
129
155
 
156
+ /**
157
+ * decodes a CID from bytes, expecting no remainder
158
+ * @param bytes raw CID bytes
159
+ * @returns decoded CID
160
+ * @throws {RangeError} if the bytes are invalid or contain extra data
161
+ */
130
162
  export const decode = (bytes: Uint8Array): Cid => {
131
163
  const [cid, remainder] = decodeFirst(bytes);
132
164
 
@@ -137,6 +169,13 @@ export const decode = (bytes: Uint8Array): Cid => {
137
169
  return cid;
138
170
  };
139
171
 
172
+ /**
173
+ * parses a CID from a multibase base32 string
174
+ * @param input base32-encoded CID string (with 'b' prefix)
175
+ * @returns decoded CID
176
+ * @throws {SyntaxError} if the string is not a valid multibase base32 string
177
+ * @throws {RangeError} if the string length is invalid
178
+ */
140
179
  export const fromString = (input: string): Cid => {
141
180
  if (input.length < 2 || input[0] !== 'b') {
142
181
  throw new SyntaxError(`not a multibase base32 string`);
@@ -151,14 +190,33 @@ export const fromString = (input: string): Cid => {
151
190
  const bytes = fromBase32(input.slice(1));
152
191
  const cid = decode(bytes);
153
192
 
154
- cid._str = input;
193
+ CID_STRINGIFY_CACHE.set(cid, input);
155
194
  return cid;
156
195
  };
157
196
 
197
+ /**
198
+ * encodes a CID to a multibase base32 string
199
+ * @param cid CID to encode
200
+ * @returns base32-encoded string with 'b' prefix
201
+ */
158
202
  export const toString = (cid: Cid): string => {
159
- return (cid._str ??= `b${toBase32(cid.bytes)}`);
203
+ let str = CID_STRINGIFY_CACHE.get(cid);
204
+ if (str === undefined) {
205
+ str = `b${toBase32(cid.bytes)}`;
206
+
207
+ CID_STRINGIFY_CACHE.set(cid, str);
208
+ }
209
+
210
+ return str;
160
211
  };
161
212
 
213
+ /**
214
+ * parses a CID from binary format (with 0x00 prefix)
215
+ * @param input binary CID bytes with 0x00 prefix
216
+ * @returns decoded CID
217
+ * @throws {RangeError} if the byte length is invalid
218
+ * @throws {SyntaxError} if the prefix byte is not 0x00
219
+ */
162
220
  export const fromBinary = (input: Uint8Array): Cid => {
163
221
  // 4 bytes + 1 byte for the 0x00 prefix
164
222
  // 36 bytes + 1 byte for the 0x00 prefix
@@ -174,6 +232,11 @@ export const fromBinary = (input: Uint8Array): Cid => {
174
232
  return decode(bytes);
175
233
  };
176
234
 
235
+ /**
236
+ * encodes a CID to binary format (with 0x00 prefix)
237
+ * @param cid CID to encode
238
+ * @returns binary CID bytes with 0x00 prefix
239
+ */
177
240
  export const toBinary = (cid: Cid): Uint8Array => {
178
241
  const bytes = allocUnsafe(1 + cid.bytes.length);
179
242
  bytes[0] = 0;
@@ -182,10 +245,12 @@ export const toBinary = (cid: Cid): Uint8Array => {
182
245
  return bytes;
183
246
  };
184
247
 
248
+ /**
249
+ * checks if two CIDs are equal
250
+ * @param a first CID
251
+ * @param b second CID
252
+ * @returns true if the CIDs have identical bytes
253
+ */
185
254
  export const equals = (a: Cid, b: Cid): boolean => {
186
- if (a._str !== undefined && b._str !== undefined) {
187
- return a._str === b._str;
188
- }
189
-
190
255
  return isBufferEqual(a.bytes, b.bytes);
191
256
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@atcute/cid",
4
- "version": "2.2.5",
4
+ "version": "2.3.0",
5
5
  "description": "lightweight DASL CID codec library for AT Protocol",
6
6
  "keywords": [
7
7
  "atproto",
@@ -13,6 +13,9 @@
13
13
  "url": "https://github.com/mary-ext/atcute",
14
14
  "directory": "packages/utilities/cid"
15
15
  },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
16
19
  "files": [
17
20
  "dist/",
18
21
  "lib/",
@@ -25,15 +28,16 @@
25
28
  },
26
29
  "sideEffects": false,
27
30
  "devDependencies": {
28
- "@types/bun": "^1.2.21"
31
+ "@vitest/coverage-v8": "^4.0.16",
32
+ "vitest": "^4.0.16"
29
33
  },
30
34
  "dependencies": {
31
35
  "@atcute/multibase": "^1.1.6",
32
- "@atcute/uint8array": "^1.0.5"
36
+ "@atcute/uint8array": "^1.0.6"
33
37
  },
34
38
  "scripts": {
35
- "build": "tsc --project tsconfig.build.json",
36
- "test": "bun test --coverage",
39
+ "build": "tsgo --project tsconfig.build.json",
40
+ "test": "vitest",
37
41
  "prepublish": "rm -rf dist; pnpm run build"
38
42
  }
39
43
  }