@optimystic/quereus-plugin-crypto 0.22.0 → 0.24.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/CHANGELOG.md +91 -91
- package/dist/index.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/package.json +2 -2
- package/src/cid.ts +200 -200
- package/src/crypto.ts +547 -547
- package/src/sd.ts +250 -250
package/src/cid.ts
CHANGED
|
@@ -1,200 +1,200 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Self-describing content identifiers (CIDv1) for Quereus.
|
|
3
|
-
*
|
|
4
|
-
* Where {@link ./crypto.ts | digest} emits a *bare* hash (raw digest bytes in
|
|
5
|
-
* some text encoding), this module emits an interoperable, self-describing
|
|
6
|
-
* CIDv1:
|
|
7
|
-
*
|
|
8
|
-
* ```
|
|
9
|
-
* CIDv1 = multibase( version ‖ multicodec(content-type) ‖ multihash )
|
|
10
|
-
* multihash = hashFnCode ‖ digestLength ‖ digestBytes
|
|
11
|
-
* ```
|
|
12
|
-
*
|
|
13
|
-
* The value carries its own multibase, multicodec (content type), and multihash
|
|
14
|
-
* (hash algorithm + length), so a consumer can decode it without out-of-band
|
|
15
|
-
* knowledge, and an algorithm migration (e.g. sha2-256 → another hash) is
|
|
16
|
-
* unambiguous because the hash code is recorded *in the value*.
|
|
17
|
-
*
|
|
18
|
-
* All framing/parsing comes from the audited `multiformats` library — there is
|
|
19
|
-
* no bespoke byte-pushing here. The actual hashing reuses the same synchronous,
|
|
20
|
-
* cross-platform `@noble/hashes` functions the rest of the plugin uses (via
|
|
21
|
-
* {@link resolveHasher}), so the output is byte-identical to the CID an external
|
|
22
|
-
* content-addressed store (IPFS/IPLD) computes for the same bytes:
|
|
23
|
-
* `cid(utf8('hello world'))` === `bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e`.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { CID } from 'multiformats/cid';
|
|
27
|
-
import * as Digest from 'multiformats/hashes/digest';
|
|
28
|
-
import { base32 } from 'multiformats/bases/base32';
|
|
29
|
-
import { base58btc } from 'multiformats/bases/base58';
|
|
30
|
-
import { base64url } from 'multiformats/bases/base64';
|
|
31
|
-
import { base16 } from 'multiformats/bases/base16';
|
|
32
|
-
import type { MultibaseEncoder, MultibaseDecoder } from 'multiformats/bases/interface';
|
|
33
|
-
import { resolveHasher, type HashAlgorithm } from './crypto.js';
|
|
34
|
-
|
|
35
|
-
/** Content-type multicodec selectable for the CID. Extensible. */
|
|
36
|
-
export type Multicodec = 'raw' | 'dag-cbor';
|
|
37
|
-
/** Hash-algorithm multihash code selectable for the CID. */
|
|
38
|
-
export type MultihashCode = 'sha2-256' | 'sha2-512' | 'blake3';
|
|
39
|
-
/** Multibase the CID string is rendered in. */
|
|
40
|
-
export type Multibase = 'base32' | 'base58btc' | 'base64url' | 'base16';
|
|
41
|
-
|
|
42
|
-
/** Parsed parts of a CIDv1 (or CIDv0), as returned by {@link cidDecode}. */
|
|
43
|
-
export interface CidParts {
|
|
44
|
-
/** CID version (1 for the values this module produces; 0 for legacy CIDv0). */
|
|
45
|
-
readonly version: number;
|
|
46
|
-
/** Content-type codec name when recognized, else the raw multicodec number. */
|
|
47
|
-
readonly codec: Multicodec | number;
|
|
48
|
-
/** Hash-algorithm code name when recognized, else the raw multihash number. */
|
|
49
|
-
readonly hashCode: MultihashCode | number;
|
|
50
|
-
/** Raw digest bytes (without the multihash code/length prefix). */
|
|
51
|
-
readonly digest: Uint8Array;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// --- Multiformats code tables (see multiformats/multicodec table.csv) --- //
|
|
55
|
-
|
|
56
|
-
/** Content-type name → multicodec code. */
|
|
57
|
-
const MULTICODEC_CODES: Record<Multicodec, number> = {
|
|
58
|
-
'raw': 0x55,
|
|
59
|
-
'dag-cbor': 0x71,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/** Hash name → multihash code. */
|
|
63
|
-
const MULTIHASH_CODES: Record<MultihashCode, number> = {
|
|
64
|
-
'sha2-256': 0x12,
|
|
65
|
-
'sha2-512': 0x13,
|
|
66
|
-
'blake3': 0x1e,
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/** Multihash code → the synchronous `@noble/hashes` algorithm that produces it. */
|
|
70
|
-
const MULTIHASH_TO_ALGORITHM: Record<MultihashCode, HashAlgorithm> = {
|
|
71
|
-
'sha2-256': 'sha256',
|
|
72
|
-
'sha2-512': 'sha512',
|
|
73
|
-
'blake3': 'blake3',
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Multihash code → exact digest length in bytes. A CID is replicable only if its
|
|
78
|
-
* digest length is fixed, so blake3 (which is variable-length in general) is
|
|
79
|
-
* pinned to 32 bytes here, matching the plugin's blake3 output and sha2-256.
|
|
80
|
-
*/
|
|
81
|
-
const MULTIHASH_DIGEST_LENGTHS: Record<MultihashCode, number> = {
|
|
82
|
-
'sha2-256': 32,
|
|
83
|
-
'sha2-512': 64,
|
|
84
|
-
'blake3': 32,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
/** Reverse lookups for {@link cidDecode}: code number → friendly name. */
|
|
88
|
-
const MULTICODEC_NAMES: ReadonlyMap<number, Multicodec> = new Map(
|
|
89
|
-
(Object.entries(MULTICODEC_CODES) as [Multicodec, number][]).map(([name, code]) => [code, name])
|
|
90
|
-
);
|
|
91
|
-
const MULTIHASH_NAMES: ReadonlyMap<number, MultihashCode> = new Map(
|
|
92
|
-
(Object.entries(MULTIHASH_CODES) as [MultihashCode, number][]).map(([name, code]) => [code, name])
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
/** Multibase name → its multiformats encoder. */
|
|
96
|
-
const MULTIBASE_ENCODERS: Record<Multibase, MultibaseEncoder<string>> = {
|
|
97
|
-
'base32': base32,
|
|
98
|
-
'base58btc': base58btc,
|
|
99
|
-
'base64url': base64url,
|
|
100
|
-
'base16': base16,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Combined decoder that dispatches on the multibase prefix character, so
|
|
105
|
-
* {@link cidDecode} accepts a CID in any of the supported bases without the
|
|
106
|
-
* caller having to declare which.
|
|
107
|
-
*/
|
|
108
|
-
const MULTIBASE_DECODER: MultibaseDecoder<string> = base32.decoder
|
|
109
|
-
.or(base58btc.decoder)
|
|
110
|
-
.or(base64url.decoder)
|
|
111
|
-
.or(base16.decoder);
|
|
112
|
-
|
|
113
|
-
function resolveCodecCode(codec: Multicodec): number {
|
|
114
|
-
const code = MULTICODEC_CODES[codec];
|
|
115
|
-
if (code === undefined) {
|
|
116
|
-
throw new Error(`cid: unsupported multicodec '${codec}' (expected one of ${Object.keys(MULTICODEC_CODES).join(', ')})`);
|
|
117
|
-
}
|
|
118
|
-
return code;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function resolveBaseEncoder(base: Multibase): MultibaseEncoder<string> {
|
|
122
|
-
const encoder = MULTIBASE_ENCODERS[base];
|
|
123
|
-
if (!encoder) {
|
|
124
|
-
throw new Error(`cid: unsupported multibase '${base}' (expected one of ${Object.keys(MULTIBASE_ENCODERS).join(', ')})`);
|
|
125
|
-
}
|
|
126
|
-
return encoder;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Frame an **already-computed** digest as a CIDv1 string. The caller asserts
|
|
131
|
-
* which `hash` produced the digest; the digest length is validated against that
|
|
132
|
-
* hash so a mismatched assertion is rejected rather than silently mis-framed.
|
|
133
|
-
*
|
|
134
|
-
* Use this to turn an existing field-tuple digest into a CID without re-hashing,
|
|
135
|
-
* e.g. `cidV1(digest(fields, 'sha256', 'bytes'), 'sha2-256')`.
|
|
136
|
-
*
|
|
137
|
-
* @param digest - Raw digest bytes (no multihash prefix).
|
|
138
|
-
* @param hash - The multihash code asserting which algorithm produced `digest`.
|
|
139
|
-
* @param codec - Content-type multicodec (default `'raw'`).
|
|
140
|
-
* @param base - Multibase to render in (default `'base32'`, the IPFS canonical).
|
|
141
|
-
*/
|
|
142
|
-
export function cidV1(
|
|
143
|
-
digest: Uint8Array,
|
|
144
|
-
hash: MultihashCode,
|
|
145
|
-
codec: Multicodec = 'raw',
|
|
146
|
-
base: Multibase = 'base32'
|
|
147
|
-
): string {
|
|
148
|
-
const hashCode = MULTIHASH_CODES[hash];
|
|
149
|
-
if (hashCode === undefined) {
|
|
150
|
-
throw new Error(`cid: unsupported multihash code '${hash}' (expected one of ${Object.keys(MULTIHASH_CODES).join(', ')})`);
|
|
151
|
-
}
|
|
152
|
-
const expectedLength = MULTIHASH_DIGEST_LENGTHS[hash];
|
|
153
|
-
if (digest.length !== expectedLength) {
|
|
154
|
-
throw new Error(`cid: digest length ${digest.length} does not match asserted hash '${hash}' (expected ${expectedLength} bytes)`);
|
|
155
|
-
}
|
|
156
|
-
const codecCode = resolveCodecCode(codec);
|
|
157
|
-
const encoder = resolveBaseEncoder(base);
|
|
158
|
-
const multihash = Digest.create(hashCode, digest);
|
|
159
|
-
return CID.createV1(codecCode, multihash).toString(encoder);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Hash `data`, wrap the digest as a multihash, frame it as a CIDv1, and encode
|
|
164
|
-
* in `base`. The result is the same interoperable address an IPFS/IPLD store
|
|
165
|
-
* computes for the same bytes (for the matching codec/hash).
|
|
166
|
-
*
|
|
167
|
-
* @param data - The content bytes to address.
|
|
168
|
-
* @param codec - Content-type multicodec (default `'raw'`).
|
|
169
|
-
* @param hash - Hash algorithm (default `'sha2-256'`).
|
|
170
|
-
* @param base - Multibase to render in (default `'base32'`, the IPFS canonical).
|
|
171
|
-
*/
|
|
172
|
-
export function cid(
|
|
173
|
-
data: Uint8Array,
|
|
174
|
-
codec: Multicodec = 'raw',
|
|
175
|
-
hash: MultihashCode = 'sha2-256',
|
|
176
|
-
base: Multibase = 'base32'
|
|
177
|
-
): string {
|
|
178
|
-
const algorithm = MULTIHASH_TO_ALGORITHM[hash];
|
|
179
|
-
if (!algorithm) {
|
|
180
|
-
throw new Error(`cid: unsupported multihash code '${hash}' (expected one of ${Object.keys(MULTIHASH_CODES).join(', ')})`);
|
|
181
|
-
}
|
|
182
|
-
const digest = resolveHasher(algorithm)(data);
|
|
183
|
-
return cidV1(digest, hash, codec, base);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Parse a CID string back into its parts, for schema validation and migration.
|
|
188
|
-
* Recognized codec/hash codes are returned as friendly names; unrecognized ones
|
|
189
|
-
* as their raw numbers. Throws cleanly on malformed input (delegated to
|
|
190
|
-
* `multiformats`), never silently mis-framing.
|
|
191
|
-
*/
|
|
192
|
-
export function cidDecode(value: string): CidParts {
|
|
193
|
-
const parsed = CID.parse(value, MULTIBASE_DECODER);
|
|
194
|
-
return {
|
|
195
|
-
version: parsed.version,
|
|
196
|
-
codec: MULTICODEC_NAMES.get(parsed.code) ?? parsed.code,
|
|
197
|
-
hashCode: MULTIHASH_NAMES.get(parsed.multihash.code) ?? parsed.multihash.code,
|
|
198
|
-
digest: parsed.multihash.digest,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Self-describing content identifiers (CIDv1) for Quereus.
|
|
3
|
+
*
|
|
4
|
+
* Where {@link ./crypto.ts | digest} emits a *bare* hash (raw digest bytes in
|
|
5
|
+
* some text encoding), this module emits an interoperable, self-describing
|
|
6
|
+
* CIDv1:
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* CIDv1 = multibase( version ‖ multicodec(content-type) ‖ multihash )
|
|
10
|
+
* multihash = hashFnCode ‖ digestLength ‖ digestBytes
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The value carries its own multibase, multicodec (content type), and multihash
|
|
14
|
+
* (hash algorithm + length), so a consumer can decode it without out-of-band
|
|
15
|
+
* knowledge, and an algorithm migration (e.g. sha2-256 → another hash) is
|
|
16
|
+
* unambiguous because the hash code is recorded *in the value*.
|
|
17
|
+
*
|
|
18
|
+
* All framing/parsing comes from the audited `multiformats` library — there is
|
|
19
|
+
* no bespoke byte-pushing here. The actual hashing reuses the same synchronous,
|
|
20
|
+
* cross-platform `@noble/hashes` functions the rest of the plugin uses (via
|
|
21
|
+
* {@link resolveHasher}), so the output is byte-identical to the CID an external
|
|
22
|
+
* content-addressed store (IPFS/IPLD) computes for the same bytes:
|
|
23
|
+
* `cid(utf8('hello world'))` === `bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e`.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { CID } from 'multiformats/cid';
|
|
27
|
+
import * as Digest from 'multiformats/hashes/digest';
|
|
28
|
+
import { base32 } from 'multiformats/bases/base32';
|
|
29
|
+
import { base58btc } from 'multiformats/bases/base58';
|
|
30
|
+
import { base64url } from 'multiformats/bases/base64';
|
|
31
|
+
import { base16 } from 'multiformats/bases/base16';
|
|
32
|
+
import type { MultibaseEncoder, MultibaseDecoder } from 'multiformats/bases/interface';
|
|
33
|
+
import { resolveHasher, type HashAlgorithm } from './crypto.js';
|
|
34
|
+
|
|
35
|
+
/** Content-type multicodec selectable for the CID. Extensible. */
|
|
36
|
+
export type Multicodec = 'raw' | 'dag-cbor';
|
|
37
|
+
/** Hash-algorithm multihash code selectable for the CID. */
|
|
38
|
+
export type MultihashCode = 'sha2-256' | 'sha2-512' | 'blake3';
|
|
39
|
+
/** Multibase the CID string is rendered in. */
|
|
40
|
+
export type Multibase = 'base32' | 'base58btc' | 'base64url' | 'base16';
|
|
41
|
+
|
|
42
|
+
/** Parsed parts of a CIDv1 (or CIDv0), as returned by {@link cidDecode}. */
|
|
43
|
+
export interface CidParts {
|
|
44
|
+
/** CID version (1 for the values this module produces; 0 for legacy CIDv0). */
|
|
45
|
+
readonly version: number;
|
|
46
|
+
/** Content-type codec name when recognized, else the raw multicodec number. */
|
|
47
|
+
readonly codec: Multicodec | number;
|
|
48
|
+
/** Hash-algorithm code name when recognized, else the raw multihash number. */
|
|
49
|
+
readonly hashCode: MultihashCode | number;
|
|
50
|
+
/** Raw digest bytes (without the multihash code/length prefix). */
|
|
51
|
+
readonly digest: Uint8Array;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// --- Multiformats code tables (see multiformats/multicodec table.csv) --- //
|
|
55
|
+
|
|
56
|
+
/** Content-type name → multicodec code. */
|
|
57
|
+
const MULTICODEC_CODES: Record<Multicodec, number> = {
|
|
58
|
+
'raw': 0x55,
|
|
59
|
+
'dag-cbor': 0x71,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Hash name → multihash code. */
|
|
63
|
+
const MULTIHASH_CODES: Record<MultihashCode, number> = {
|
|
64
|
+
'sha2-256': 0x12,
|
|
65
|
+
'sha2-512': 0x13,
|
|
66
|
+
'blake3': 0x1e,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/** Multihash code → the synchronous `@noble/hashes` algorithm that produces it. */
|
|
70
|
+
const MULTIHASH_TO_ALGORITHM: Record<MultihashCode, HashAlgorithm> = {
|
|
71
|
+
'sha2-256': 'sha256',
|
|
72
|
+
'sha2-512': 'sha512',
|
|
73
|
+
'blake3': 'blake3',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Multihash code → exact digest length in bytes. A CID is replicable only if its
|
|
78
|
+
* digest length is fixed, so blake3 (which is variable-length in general) is
|
|
79
|
+
* pinned to 32 bytes here, matching the plugin's blake3 output and sha2-256.
|
|
80
|
+
*/
|
|
81
|
+
const MULTIHASH_DIGEST_LENGTHS: Record<MultihashCode, number> = {
|
|
82
|
+
'sha2-256': 32,
|
|
83
|
+
'sha2-512': 64,
|
|
84
|
+
'blake3': 32,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/** Reverse lookups for {@link cidDecode}: code number → friendly name. */
|
|
88
|
+
const MULTICODEC_NAMES: ReadonlyMap<number, Multicodec> = new Map(
|
|
89
|
+
(Object.entries(MULTICODEC_CODES) as [Multicodec, number][]).map(([name, code]) => [code, name])
|
|
90
|
+
);
|
|
91
|
+
const MULTIHASH_NAMES: ReadonlyMap<number, MultihashCode> = new Map(
|
|
92
|
+
(Object.entries(MULTIHASH_CODES) as [MultihashCode, number][]).map(([name, code]) => [code, name])
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
/** Multibase name → its multiformats encoder. */
|
|
96
|
+
const MULTIBASE_ENCODERS: Record<Multibase, MultibaseEncoder<string>> = {
|
|
97
|
+
'base32': base32,
|
|
98
|
+
'base58btc': base58btc,
|
|
99
|
+
'base64url': base64url,
|
|
100
|
+
'base16': base16,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Combined decoder that dispatches on the multibase prefix character, so
|
|
105
|
+
* {@link cidDecode} accepts a CID in any of the supported bases without the
|
|
106
|
+
* caller having to declare which.
|
|
107
|
+
*/
|
|
108
|
+
const MULTIBASE_DECODER: MultibaseDecoder<string> = base32.decoder
|
|
109
|
+
.or(base58btc.decoder)
|
|
110
|
+
.or(base64url.decoder)
|
|
111
|
+
.or(base16.decoder);
|
|
112
|
+
|
|
113
|
+
function resolveCodecCode(codec: Multicodec): number {
|
|
114
|
+
const code = MULTICODEC_CODES[codec];
|
|
115
|
+
if (code === undefined) {
|
|
116
|
+
throw new Error(`cid: unsupported multicodec '${codec}' (expected one of ${Object.keys(MULTICODEC_CODES).join(', ')})`);
|
|
117
|
+
}
|
|
118
|
+
return code;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function resolveBaseEncoder(base: Multibase): MultibaseEncoder<string> {
|
|
122
|
+
const encoder = MULTIBASE_ENCODERS[base];
|
|
123
|
+
if (!encoder) {
|
|
124
|
+
throw new Error(`cid: unsupported multibase '${base}' (expected one of ${Object.keys(MULTIBASE_ENCODERS).join(', ')})`);
|
|
125
|
+
}
|
|
126
|
+
return encoder;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Frame an **already-computed** digest as a CIDv1 string. The caller asserts
|
|
131
|
+
* which `hash` produced the digest; the digest length is validated against that
|
|
132
|
+
* hash so a mismatched assertion is rejected rather than silently mis-framed.
|
|
133
|
+
*
|
|
134
|
+
* Use this to turn an existing field-tuple digest into a CID without re-hashing,
|
|
135
|
+
* e.g. `cidV1(digest(fields, 'sha256', 'bytes'), 'sha2-256')`.
|
|
136
|
+
*
|
|
137
|
+
* @param digest - Raw digest bytes (no multihash prefix).
|
|
138
|
+
* @param hash - The multihash code asserting which algorithm produced `digest`.
|
|
139
|
+
* @param codec - Content-type multicodec (default `'raw'`).
|
|
140
|
+
* @param base - Multibase to render in (default `'base32'`, the IPFS canonical).
|
|
141
|
+
*/
|
|
142
|
+
export function cidV1(
|
|
143
|
+
digest: Uint8Array,
|
|
144
|
+
hash: MultihashCode,
|
|
145
|
+
codec: Multicodec = 'raw',
|
|
146
|
+
base: Multibase = 'base32'
|
|
147
|
+
): string {
|
|
148
|
+
const hashCode = MULTIHASH_CODES[hash];
|
|
149
|
+
if (hashCode === undefined) {
|
|
150
|
+
throw new Error(`cid: unsupported multihash code '${hash}' (expected one of ${Object.keys(MULTIHASH_CODES).join(', ')})`);
|
|
151
|
+
}
|
|
152
|
+
const expectedLength = MULTIHASH_DIGEST_LENGTHS[hash];
|
|
153
|
+
if (digest.length !== expectedLength) {
|
|
154
|
+
throw new Error(`cid: digest length ${digest.length} does not match asserted hash '${hash}' (expected ${expectedLength} bytes)`);
|
|
155
|
+
}
|
|
156
|
+
const codecCode = resolveCodecCode(codec);
|
|
157
|
+
const encoder = resolveBaseEncoder(base);
|
|
158
|
+
const multihash = Digest.create(hashCode, digest);
|
|
159
|
+
return CID.createV1(codecCode, multihash).toString(encoder);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Hash `data`, wrap the digest as a multihash, frame it as a CIDv1, and encode
|
|
164
|
+
* in `base`. The result is the same interoperable address an IPFS/IPLD store
|
|
165
|
+
* computes for the same bytes (for the matching codec/hash).
|
|
166
|
+
*
|
|
167
|
+
* @param data - The content bytes to address.
|
|
168
|
+
* @param codec - Content-type multicodec (default `'raw'`).
|
|
169
|
+
* @param hash - Hash algorithm (default `'sha2-256'`).
|
|
170
|
+
* @param base - Multibase to render in (default `'base32'`, the IPFS canonical).
|
|
171
|
+
*/
|
|
172
|
+
export function cid(
|
|
173
|
+
data: Uint8Array,
|
|
174
|
+
codec: Multicodec = 'raw',
|
|
175
|
+
hash: MultihashCode = 'sha2-256',
|
|
176
|
+
base: Multibase = 'base32'
|
|
177
|
+
): string {
|
|
178
|
+
const algorithm = MULTIHASH_TO_ALGORITHM[hash];
|
|
179
|
+
if (!algorithm) {
|
|
180
|
+
throw new Error(`cid: unsupported multihash code '${hash}' (expected one of ${Object.keys(MULTIHASH_CODES).join(', ')})`);
|
|
181
|
+
}
|
|
182
|
+
const digest = resolveHasher(algorithm)(data);
|
|
183
|
+
return cidV1(digest, hash, codec, base);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Parse a CID string back into its parts, for schema validation and migration.
|
|
188
|
+
* Recognized codec/hash codes are returned as friendly names; unrecognized ones
|
|
189
|
+
* as their raw numbers. Throws cleanly on malformed input (delegated to
|
|
190
|
+
* `multiformats`), never silently mis-framing.
|
|
191
|
+
*/
|
|
192
|
+
export function cidDecode(value: string): CidParts {
|
|
193
|
+
const parsed = CID.parse(value, MULTIBASE_DECODER);
|
|
194
|
+
return {
|
|
195
|
+
version: parsed.version,
|
|
196
|
+
codec: MULTICODEC_NAMES.get(parsed.code) ?? parsed.code,
|
|
197
|
+
hashCode: MULTIHASH_NAMES.get(parsed.multihash.code) ?? parsed.multihash.code,
|
|
198
|
+
digest: parsed.multihash.digest,
|
|
199
|
+
};
|
|
200
|
+
}
|