@atcute/cid 1.0.1 → 1.0.2
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 +1 -1
- package/lib/index.ts +189 -0
- package/package.json +10 -6
package/README.md
CHANGED
package/lib/index.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Bare minimum implementation for creating, parsing, and formatting
|
|
4
|
+
* AT Protocol-blessed CIDv1 format.
|
|
5
|
+
*
|
|
6
|
+
* As specified by AT Protocol, the blessed format is:
|
|
7
|
+
* - Multibase: `base32` (b)
|
|
8
|
+
* - Multicodec: `dag-cbor` (0x71) for record, `raw` (0x55) for blobs
|
|
9
|
+
* - Multihash: `sha-256` (0x12)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as base32 from '@atcute/base32';
|
|
13
|
+
import * as varint from '@atcute/varint';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Raw digest information
|
|
17
|
+
*/
|
|
18
|
+
export interface Digest {
|
|
19
|
+
code: number;
|
|
20
|
+
size: number;
|
|
21
|
+
digest: Uint8Array;
|
|
22
|
+
bytes: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* CID information
|
|
27
|
+
*/
|
|
28
|
+
export interface CID {
|
|
29
|
+
version: number;
|
|
30
|
+
code: number;
|
|
31
|
+
digest: Digest;
|
|
32
|
+
bytes: Uint8Array;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Information regarding CID buffer being inspected
|
|
37
|
+
*/
|
|
38
|
+
export interface InspectedCID {
|
|
39
|
+
version: number;
|
|
40
|
+
codec: number;
|
|
41
|
+
multihashCode: number;
|
|
42
|
+
digestSize: number;
|
|
43
|
+
multihashSize: number;
|
|
44
|
+
size: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Parse a CID string
|
|
49
|
+
*/
|
|
50
|
+
export const parse = (cid: string): CID => {
|
|
51
|
+
if (cid[0] !== 'b') {
|
|
52
|
+
throw new Error(`only base32 cidv1 is supported`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const bytes = base32.decode(cid.slice(1));
|
|
56
|
+
return decode(bytes);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Provides information regarding the CID buffer
|
|
61
|
+
*/
|
|
62
|
+
export const inspect = (initialBytes: Uint8Array): InspectedCID => {
|
|
63
|
+
let offset = 0;
|
|
64
|
+
const next = (): number => {
|
|
65
|
+
const [i, length] = varint.decode(initialBytes.subarray(offset));
|
|
66
|
+
offset += length;
|
|
67
|
+
return i;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
let version = next();
|
|
71
|
+
let codec = 0x70; // dag-pb
|
|
72
|
+
if ((version as number) === 18) {
|
|
73
|
+
// CIDv0
|
|
74
|
+
version = 0;
|
|
75
|
+
offset = 0;
|
|
76
|
+
} else {
|
|
77
|
+
codec = next();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (version !== 1) {
|
|
81
|
+
throw new RangeError(`only cidv1 is supported`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const prefixSize = offset;
|
|
85
|
+
const multihashCode = next();
|
|
86
|
+
const digestSize = next();
|
|
87
|
+
const size = offset + digestSize;
|
|
88
|
+
const multihashSize = size - prefixSize;
|
|
89
|
+
|
|
90
|
+
return { version, codec, multihashCode, digestSize, multihashSize, size };
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Decode the first CID contained, and return the remainder.
|
|
95
|
+
* @param bytes Buffer to decode
|
|
96
|
+
* @returns A tuple containing the first CID in the buffer, and the remainder
|
|
97
|
+
*/
|
|
98
|
+
export const decodeFirst = (bytes: Uint8Array): [cid: CID, remainder: Uint8Array] => {
|
|
99
|
+
const specs = inspect(bytes);
|
|
100
|
+
const prefixSize = specs.size - specs.multihashSize;
|
|
101
|
+
const multihashBytes = bytes.subarray(prefixSize, prefixSize + specs.multihashSize);
|
|
102
|
+
|
|
103
|
+
if (multihashBytes.byteLength !== specs.multihashSize) {
|
|
104
|
+
throw new RangeError('incorrect cid length');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const digestBytes = multihashBytes.subarray(specs.multihashSize - specs.digestSize);
|
|
108
|
+
|
|
109
|
+
const digest: Digest = {
|
|
110
|
+
code: specs.multihashCode,
|
|
111
|
+
size: specs.multihashSize,
|
|
112
|
+
digest: digestBytes,
|
|
113
|
+
bytes: multihashBytes,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const cid: CID = {
|
|
117
|
+
version: 1,
|
|
118
|
+
code: specs.codec,
|
|
119
|
+
digest: digest,
|
|
120
|
+
bytes: bytes.subarray(0, specs.size),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return [cid, bytes.subarray(specs.size)];
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Decodes a CID buffer
|
|
128
|
+
*/
|
|
129
|
+
export const decode = (bytes: Uint8Array): CID => {
|
|
130
|
+
const [cid, remainder] = decodeFirst(bytes);
|
|
131
|
+
|
|
132
|
+
if (remainder.length !== 0) {
|
|
133
|
+
throw new Error(`incorrect cid length`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return cid;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Creates a CID
|
|
141
|
+
*/
|
|
142
|
+
export const create = async (code: number, input: Uint8Array): Promise<CID> => {
|
|
143
|
+
const digest = createDigest(0x12, new Uint8Array(await crypto.subtle.digest('sha-256', input)));
|
|
144
|
+
const bytes = encode(1, code, digest.bytes);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
version: 1,
|
|
148
|
+
code: code,
|
|
149
|
+
digest: digest,
|
|
150
|
+
bytes: bytes,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Serialize CID into a string
|
|
156
|
+
*/
|
|
157
|
+
export const format = (cid: CID): string => {
|
|
158
|
+
return 'b' + base32.encode(cid.bytes);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const createDigest = (code: number, digest: Uint8Array): Digest => {
|
|
162
|
+
const size = digest.byteLength;
|
|
163
|
+
const sizeOffset = varint.encodingLength(code);
|
|
164
|
+
const digestOffset = sizeOffset + varint.encodingLength(size);
|
|
165
|
+
|
|
166
|
+
const bytes = new Uint8Array(digestOffset + size);
|
|
167
|
+
varint.encode(code, bytes, 0);
|
|
168
|
+
varint.encode(size, bytes, sizeOffset);
|
|
169
|
+
bytes.set(digest, digestOffset);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
code: code,
|
|
173
|
+
size: size,
|
|
174
|
+
digest: digest,
|
|
175
|
+
bytes: bytes,
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const encode = (version: number, code: number, multihash: Uint8Array): Uint8Array => {
|
|
180
|
+
const codeOffset = varint.encodingLength(version);
|
|
181
|
+
const hashOffset = codeOffset + varint.encodingLength(code);
|
|
182
|
+
|
|
183
|
+
const bytes = new Uint8Array(hashOffset + multihash.byteLength);
|
|
184
|
+
varint.encode(version, bytes, 0);
|
|
185
|
+
varint.encode(code, bytes, codeOffset);
|
|
186
|
+
bytes.set(multihash, hashOffset);
|
|
187
|
+
|
|
188
|
+
return bytes;
|
|
189
|
+
};
|
package/package.json
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@atcute/cid",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"description": "create and parse AT Protocol-blessed CIDv1 format",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
|
-
"url": "https://
|
|
8
|
+
"url": "https://github.com/mary-ext/atcute",
|
|
9
|
+
"directory": "packages/utilities/cid"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
|
-
"dist/"
|
|
12
|
+
"dist/",
|
|
13
|
+
"lib/",
|
|
14
|
+
"!lib/**/*.bench.ts",
|
|
15
|
+
"!lib/**/*.test.ts"
|
|
12
16
|
],
|
|
13
17
|
"exports": {
|
|
14
18
|
".": "./dist/index.js"
|
|
15
19
|
},
|
|
16
20
|
"sideEffects": false,
|
|
17
21
|
"devDependencies": {
|
|
18
|
-
"@types/bun": "^1.1.
|
|
22
|
+
"@types/bun": "^1.1.12"
|
|
19
23
|
},
|
|
20
24
|
"dependencies": {
|
|
21
|
-
"@atcute/base32": "^1.0.
|
|
22
|
-
"@atcute/varint": "^1.0.
|
|
25
|
+
"@atcute/base32": "^1.0.1",
|
|
26
|
+
"@atcute/varint": "^1.0.1"
|
|
23
27
|
},
|
|
24
28
|
"scripts": {
|
|
25
29
|
"build": "tsc --project tsconfig.build.json",
|