@ipld/car 3.2.4 → 4.1.1
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 +183 -2
- package/api.ts +22 -4
- package/buffer-writer +1 -0
- package/cjs/browser-test/common.js +78 -3
- package/cjs/browser-test/node-test-large.js +8 -8
- package/cjs/browser-test/test-buffer-writer.js +330 -0
- package/cjs/browser-test/test-errors.js +57 -34
- package/cjs/browser-test/test-indexer.js +12 -0
- package/cjs/browser-test/test-reader.js +83 -0
- package/cjs/browser-test/test-writer.js +3 -3
- package/cjs/lib/buffer-writer.js +161 -0
- package/cjs/lib/decoder.js +72 -15
- package/cjs/lib/encoder.js +2 -2
- package/cjs/lib/header-validator.js +29 -0
- package/cjs/lib/reader-browser.js +7 -7
- package/cjs/lib/writer-browser.js +1 -1
- package/cjs/node-test/common.js +78 -3
- package/cjs/node-test/node-test-large.js +8 -8
- package/cjs/node-test/test-buffer-writer.js +330 -0
- package/cjs/node-test/test-errors.js +57 -34
- package/cjs/node-test/test-indexer.js +12 -0
- package/cjs/node-test/test-reader.js +83 -0
- package/cjs/node-test/test-writer.js +3 -3
- package/esm/browser-test/common.js +76 -1
- package/esm/browser-test/test-buffer-writer.js +311 -0
- package/esm/browser-test/test-errors.js +57 -33
- package/esm/browser-test/test-indexer.js +15 -0
- package/esm/browser-test/test-reader.js +90 -1
- package/esm/browser-test/test-writer.js +3 -3
- package/esm/lib/buffer-writer.js +126 -0
- package/esm/lib/decoder.js +69 -13
- package/esm/lib/header-validator.js +23 -0
- package/esm/lib/reader-browser.js +7 -8
- package/esm/lib/writer-browser.js +1 -1
- package/esm/node-test/common.js +76 -1
- package/esm/node-test/test-buffer-writer.js +311 -0
- package/esm/node-test/test-errors.js +57 -33
- package/esm/node-test/test-indexer.js +15 -0
- package/esm/node-test/test-reader.js +90 -1
- package/esm/node-test/test-writer.js +3 -3
- package/examples/car-to-fixture.js +1 -4
- package/examples/dump-index.js +24 -0
- package/examples/test-examples.js +33 -0
- package/lib/buffer-writer.js +286 -0
- package/lib/coding.ts +17 -2
- package/lib/decoder.js +130 -14
- package/lib/header-validator.js +33 -0
- package/lib/header.ipldsch +6 -0
- package/lib/reader-browser.js +11 -11
- package/lib/writer-browser.js +1 -1
- package/package.json +16 -6
- package/test/_fixtures_to_js.mjs +24 -0
- package/test/common.js +49 -3
- package/test/go.carv2 +0 -0
- package/test/test-buffer-writer.js +256 -0
- package/test/test-errors.js +52 -30
- package/test/test-indexer.js +24 -1
- package/test/test-reader.js +94 -1
- package/test/test-writer.js +3 -3
- package/tsconfig.json +3 -1
- package/types/api.d.ts +16 -0
- package/types/api.d.ts.map +1 -1
- package/types/lib/buffer-writer.d.ts +86 -0
- package/types/lib/buffer-writer.d.ts.map +1 -0
- package/types/lib/coding.d.ts +14 -4
- package/types/lib/coding.d.ts.map +1 -1
- package/types/lib/decoder.d.ts +38 -2
- package/types/lib/decoder.d.ts.map +1 -1
- package/types/lib/header-validator.d.ts +2 -0
- package/types/lib/header-validator.d.ts.map +1 -0
- package/types/lib/reader-browser.d.ts +15 -7
- package/types/lib/reader-browser.d.ts.map +1 -1
- package/types/test/_fixtures_to_js.d.mts +3 -0
- package/types/test/_fixtures_to_js.d.mts.map +1 -0
- package/types/test/common.d.ts +13 -0
- package/types/test/common.d.ts.map +1 -1
- package/types/test/fixtures-expectations.d.ts +63 -0
- package/types/test/fixtures-expectations.d.ts.map +1 -0
- package/types/test/fixtures.d.ts +3 -0
- package/types/test/fixtures.d.ts.map +1 -0
- package/types/test/test-buffer-writer.d.ts +2 -0
- package/types/test/test-buffer-writer.d.ts.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ipld/car",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.1",
|
|
4
4
|
"description": "Content Addressable aRchive format reader and writer",
|
|
5
5
|
"main": "./cjs/car.js",
|
|
6
6
|
"types": "./types/car.d.ts",
|
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
"lint": "standard",
|
|
9
9
|
"build": "npm run build:js && npm run build:types",
|
|
10
10
|
"build:js": "ipjs build --tests --main && npm run build:copy",
|
|
11
|
-
"build:copy": "mkdir -p dist/examples/ && cp -a tsconfig.json *.js *.ts lib test dist/ && cp examples/*.* dist/examples/",
|
|
11
|
+
"build:copy": "mkdir -p dist/examples/ && cp -a tsconfig.json .npmignore *.js *.ts lib test dist/ && cp examples/*.* dist/examples/ && rm -rf dist/test/fixtures/",
|
|
12
12
|
"build:types": "tsc --build && mv types dist",
|
|
13
13
|
"test:cjs": "rm -rf dist && npm run build && cp test/go.car dist/cjs/node-test/ && mocha dist/cjs/node-test/test-*.js && mocha dist/cjs/node-test/node-test-*.js && npm run test:cjs:browser",
|
|
14
14
|
"test:esm": "rm -rf dist && npm run build && cp test/go.car dist/esm/node-test/ && mocha dist/esm/node-test/test-*.js && mocha dist/esm/node-test/node-test-*.js && npm run test:esm:browser",
|
|
15
|
-
"test:node": "c8 --check-coverage --branches 100 --functions 100 --lines 100 mocha test/test-*.js test/node-test-*.js",
|
|
15
|
+
"test:node": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --exclude lib/header-validator.js --exclude test/ mocha test/test-*.js test/node-test-*.js",
|
|
16
16
|
"test:cjs:browser": "polendina --page --worker --serviceworker --cleanup dist/cjs/browser-test/test-*.js",
|
|
17
17
|
"test:esm:browser": "polendina --page --worker --serviceworker --cleanup dist/esm/browser-test/test-*.js",
|
|
18
18
|
"test": "npm run lint && npm run test:node && npm run test:cjs && npm run test --prefix examples/",
|
|
19
19
|
"test:ci": "npm run lint && npm run test:node && npm run test:esm && npm run test:cjs && npm run test --prefix examples/",
|
|
20
20
|
"coverage": "c8 --reporter=html --reporter=text mocha test/test-*.js && npx st -d coverage -p 8888",
|
|
21
|
-
"docs": "jsdoc4readme --readme --description-only lib/reader*.js lib/indexed-reader.js lib/iterator.js lib/indexer.js lib/writer*.js"
|
|
21
|
+
"docs": "jsdoc4readme --readme --description-only lib/reader*.js lib/indexed-reader.js lib/iterator.js lib/indexer.js lib/writer*.js lib/buffer-writer.js lib/decoder.js"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
24
24
|
"car",
|
|
@@ -63,10 +63,16 @@
|
|
|
63
63
|
"browser": "./esm/lib/writer-browser.js",
|
|
64
64
|
"require": "./cjs/lib/writer.js",
|
|
65
65
|
"import": "./esm/lib/writer.js"
|
|
66
|
+
},
|
|
67
|
+
"./buffer-writer": {
|
|
68
|
+
"browser": "./esm/lib/buffer-writer.js",
|
|
69
|
+
"require": "./cjs/lib/buffer-writer.js",
|
|
70
|
+
"import": "./esm/lib/buffer-writer.js"
|
|
66
71
|
}
|
|
67
72
|
},
|
|
68
73
|
"dependencies": {
|
|
69
74
|
"@ipld/dag-cbor": "^7.0.0",
|
|
75
|
+
"cborg": "^1.9.0",
|
|
70
76
|
"multiformats": "^9.5.4",
|
|
71
77
|
"varint": "^6.0.0"
|
|
72
78
|
},
|
|
@@ -87,7 +93,7 @@
|
|
|
87
93
|
"jsdoc4readme": "^1.4.0",
|
|
88
94
|
"mocha": "^9.1.3",
|
|
89
95
|
"polendina": "~2.0.1",
|
|
90
|
-
"standard": "^
|
|
96
|
+
"standard": "^17.0.0",
|
|
91
97
|
"typescript": "~4.6.2"
|
|
92
98
|
},
|
|
93
99
|
"standard": {
|
|
@@ -126,6 +132,9 @@
|
|
|
126
132
|
"writer": [
|
|
127
133
|
"types/lib/writer.d.ts"
|
|
128
134
|
],
|
|
135
|
+
"buffer-writer": [
|
|
136
|
+
"types/lib/buffer-writer.d.ts"
|
|
137
|
+
],
|
|
129
138
|
"*": [
|
|
130
139
|
"types/*"
|
|
131
140
|
],
|
|
@@ -235,6 +244,7 @@
|
|
|
235
244
|
"./iterator": "./cjs/lib/iterator.js",
|
|
236
245
|
"./writer": "./cjs/lib/writer-browser.js",
|
|
237
246
|
"./esm/lib/writer.js": "./esm/lib/writer-browser.js",
|
|
238
|
-
"./cjs/lib/writer.js": "./cjs/lib/writer-browser.js"
|
|
247
|
+
"./cjs/lib/writer.js": "./cjs/lib/writer-browser.js",
|
|
248
|
+
"./buffer-writer": "./cjs/lib/buffer-writer.js"
|
|
239
249
|
}
|
|
240
250
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readdir, readFile, writeFile } from 'fs/promises'
|
|
4
|
+
import { dirname, join } from 'path'
|
|
5
|
+
|
|
6
|
+
async function main () {
|
|
7
|
+
const thisdir = dirname(new URL(import.meta.url).pathname)
|
|
8
|
+
const outfile = join(thisdir, 'fixtures.js')
|
|
9
|
+
const fixturesdir = join(thisdir, 'fixtures')
|
|
10
|
+
const files = await readdir(fixturesdir)
|
|
11
|
+
let content = '/** @type {Record<string, string>} */\nexport const data = {\n'
|
|
12
|
+
for (const f of files) {
|
|
13
|
+
content += ` '${f}': '`
|
|
14
|
+
content += (await readFile(join(fixturesdir, f))).toString('base64')
|
|
15
|
+
content += '\',\n'
|
|
16
|
+
}
|
|
17
|
+
content += ' _: \'\'\n}\n'
|
|
18
|
+
await writeFile(join(outfile), content, 'utf8')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
main().catch((err) => {
|
|
22
|
+
console.error(err)
|
|
23
|
+
process.exit(1)
|
|
24
|
+
})
|
package/test/common.js
CHANGED
|
@@ -132,14 +132,13 @@ function makeIterable (data, chunkSize) {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
const carBytes = bytes.fromHex('63a265726f6f747382d82a58250001711220f88bc853804cf294fe417e4fa83028689fcdb1b1592c5102e1474dbc200fab8bd82a5825000171122069ea0740f9807a28f4d932c62e7c1c83be055e55072c90266ab3e79df63a365b6776657273696f6e01280155122061be55a8e2f6b4e172338bddf184d6dbee29c98853e0a0485ecee7f27b9af0b461616161280155122081cc5b17018674b401b42f35ba07bb79e211239c23bffe658da1577e3e646877626262622801551220b6fbd675f98e2abd22d4ed29fdc83150fedc48597e92dd1a7a24381d44a2745163636363511220e7dc486e97e6ebe5cdabab3e392bdad128b6e09acc94bb4e2aa2af7b986d24d0122d0a240155122061be55a8e2f6b4e172338bddf184d6dbee29c98853e0a0485ecee7f27b9af0b4120363617418048001122079a982de3c9907953d4d323cee1d0fb1ed8f45f8ef02870c0cb9e09246bd530a122d0a240155122081cc5b17018674b401b42f35ba07bb79e211239c23bffe658da1577e3e6468771203646f671804122d0a221220e7dc486e97e6ebe5cdabab3e392bdad128b6e09acc94bb4e2aa2af7b986d24d01205666972737418338301122002acecc5de2438ea4126a3010ecb1f8a599c8eff22fff1a1dcffe999b27fd3de122e0a2401551220b6fbd675f98e2abd22d4ed29fdc83150fedc48597e92dd1a7a24381d44a274511204626561721804122f0a22122079a982de3c9907953d4d323cee1d0fb1ed8f45f8ef02870c0cb9e09246bd530a12067365636f6e641895015b01711220f88bc853804cf294fe417e4fa83028689fcdb1b1592c5102e1474dbc200fab8ba2646c696e6bd82a582300122002acecc5de2438ea4126a3010ecb1f8a599c8eff22fff1a1dcffe999b27fd3de646e616d6564626c6970360171122069ea0740f9807a28f4d932c62e7c1c83be055e55072c90266ab3e79df63a365ba2646c696e6bf6646e616d65656c696d626f')
|
|
135
|
+
|
|
135
136
|
// go.car is written as a graph, not by the allBlocks ordering here, so ordering is slightly out
|
|
136
137
|
const goCarBytes = bytes.fromHex('63a265726f6f747382d82a58250001711220f88bc853804cf294fe417e4fa83028689fcdb1b1592c5102e1474dbc200fab8bd82a5825000171122069ea0740f9807a28f4d932c62e7c1c83be055e55072c90266ab3e79df63a365b6776657273696f6e015b01711220f88bc853804cf294fe417e4fa83028689fcdb1b1592c5102e1474dbc200fab8ba2646c696e6bd82a582300122002acecc5de2438ea4126a3010ecb1f8a599c8eff22fff1a1dcffe999b27fd3de646e616d6564626c69708301122002acecc5de2438ea4126a3010ecb1f8a599c8eff22fff1a1dcffe999b27fd3de122e0a2401551220b6fbd675f98e2abd22d4ed29fdc83150fedc48597e92dd1a7a24381d44a274511204626561721804122f0a22122079a982de3c9907953d4d323cee1d0fb1ed8f45f8ef02870c0cb9e09246bd530a12067365636f6e641895012801551220b6fbd675f98e2abd22d4ed29fdc83150fedc48597e92dd1a7a24381d44a27451636363638001122079a982de3c9907953d4d323cee1d0fb1ed8f45f8ef02870c0cb9e09246bd530a122d0a240155122081cc5b17018674b401b42f35ba07bb79e211239c23bffe658da1577e3e6468771203646f671804122d0a221220e7dc486e97e6ebe5cdabab3e392bdad128b6e09acc94bb4e2aa2af7b986d24d0120566697273741833280155122081cc5b17018674b401b42f35ba07bb79e211239c23bffe658da1577e3e64687762626262511220e7dc486e97e6ebe5cdabab3e392bdad128b6e09acc94bb4e2aa2af7b986d24d0122d0a240155122061be55a8e2f6b4e172338bddf184d6dbee29c98853e0a0485ecee7f27b9af0b412036361741804280155122061be55a8e2f6b4e172338bddf184d6dbee29c98853e0a0485ecee7f27b9af0b461616161360171122069ea0740f9807a28f4d932c62e7c1c83be055e55072c90266ab3e79df63a365ba2646c696e6bf6646e616d65656c696d626f')
|
|
137
|
-
|
|
138
138
|
const goCarRoots = [
|
|
139
139
|
CID.parse('bafyreihyrpefhacm6kkp4ql6j6udakdit7g3dmkzfriqfykhjw6cad5lrm'),
|
|
140
140
|
CID.parse('bafyreidj5idub6mapiupjwjsyyxhyhedxycv4vihfsicm2vt46o7morwlm')
|
|
141
141
|
]
|
|
142
|
-
|
|
143
142
|
const goCarIndex = [
|
|
144
143
|
{ cid: CID.parse('bafyreihyrpefhacm6kkp4ql6j6udakdit7g3dmkzfriqfykhjw6cad5lrm'), offset: 100, length: 92, blockOffset: 137, blockLength: 55 },
|
|
145
144
|
{ cid: CID.parse('QmNX6Tffavsya4xgBi2VJQnSuqy9GsxongxZZ9uZBqp16d'), offset: 192, length: 133, blockOffset: 228, blockLength: 97 },
|
|
@@ -151,6 +150,49 @@ const goCarIndex = [
|
|
|
151
150
|
{ cid: CID.parse('bafyreidj5idub6mapiupjwjsyyxhyhedxycv4vihfsicm2vt46o7morwlm'), offset: 660, length: 55, blockOffset: 697, blockLength: 18 }
|
|
152
151
|
]
|
|
153
152
|
|
|
153
|
+
const goCarV2Bytes = bytes.fromHex('0aa16776657273696f6e02000000000000000000000000000000003300000000000000c001000000000000f30100000000000038a265726f6f747381d82a5823001220fb16f5083412ef1371d031ed4aa239903d84efdadf1ba3cd678e6475b1a232f86776657273696f6e01511220fb16f5083412ef1371d031ed4aa239903d84efdadf1ba3cd678e6475b1a232f8122d0a221220d9c0d5376d26f1931f7ad52d7acc00fc1090d2edb0808bf61eeb0a152826f6261204f09f8da418a40185011220d9c0d5376d26f1931f7ad52d7acc00fc1090d2edb0808bf61eeb0a152826f62612310a221220d745b7757f5b4593eeab7820306c7bc64eb496a7410a0d07df7a34ffec4b97f1120962617272656c657965183a122e0a2401551220a2e1c40da1ae335d4dffe729eb4d5ca23b74b9e51fc535f4a804a261080c294d1204f09f90a11807581220d745b7757f5b4593eeab7820306c7bc64eb496a7410a0d07df7a34ffec4b97f112340a2401551220b474a99a2705e23cf905a484ec6d14ef58b56bbe62e9292783466ec363b5072d120a666973686d6f6e67657218042801551220b474a99a2705e23cf905a484ec6d14ef58b56bbe62e9292783466ec363b5072d666973682b01551220a2e1c40da1ae335d4dffe729eb4d5ca23b74b9e51fc535f4a804a261080c294d6c6f62737465720100000028000000c800000000000000a2e1c40da1ae335d4dffe729eb4d5ca23b74b9e51fc535f4a804a261080c294d9401000000000000b474a99a2705e23cf905a484ec6d14ef58b56bbe62e9292783466ec363b5072d6b01000000000000d745b7757f5b4593eeab7820306c7bc64eb496a7410a0d07df7a34ffec4b97f11201000000000000d9c0d5376d26f1931f7ad52d7acc00fc1090d2edb0808bf61eeb0a152826f6268b00000000000000fb16f5083412ef1371d031ed4aa239903d84efdadf1ba3cd678e6475b1a232f83900000000000000')
|
|
154
|
+
const goCarV2Roots = [CID.parse('QmfEoLyB5NndqeKieExd1rtJzTduQUPEV8TwAYcUiy3H5Z')]
|
|
155
|
+
const goCarV2Index = [
|
|
156
|
+
{ blockLength: 47, blockOffset: 143, cid: CID.parse('QmfEoLyB5NndqeKieExd1rtJzTduQUPEV8TwAYcUiy3H5Z'), length: 82, offset: 108 },
|
|
157
|
+
{ blockLength: 99, blockOffset: 226, cid: CID.parse('QmczfirA7VEH7YVvKPTPoU69XM3qY4DC39nnTsWd4K3SkM'), length: 135, offset: 190 },
|
|
158
|
+
{ blockLength: 54, blockOffset: 360, cid: CID.parse('Qmcpz2FHJD7VAhg1fxFXdYJKePtkx1BsHuCrAgWVnaHMTE'), length: 89, offset: 325 },
|
|
159
|
+
{ blockLength: 4, blockOffset: 451, cid: CID.parse('bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu'), length: 41, offset: 414 },
|
|
160
|
+
{ blockLength: 7, blockOffset: 492, cid: CID.parse('bafkreifc4hca3inognou377hfhvu2xfchn2ltzi7yu27jkaeujqqqdbjju'), length: 44, offset: 455 }
|
|
161
|
+
]
|
|
162
|
+
/** @type {{[k in string]: any}} */
|
|
163
|
+
const goCarV2Contents = {
|
|
164
|
+
QmfEoLyB5NndqeKieExd1rtJzTduQUPEV8TwAYcUiy3H5Z: {
|
|
165
|
+
Links: [{
|
|
166
|
+
Hash: CID.parse('QmczfirA7VEH7YVvKPTPoU69XM3qY4DC39nnTsWd4K3SkM'),
|
|
167
|
+
Name: '🍤',
|
|
168
|
+
Tsize: 164
|
|
169
|
+
}]
|
|
170
|
+
},
|
|
171
|
+
QmczfirA7VEH7YVvKPTPoU69XM3qY4DC39nnTsWd4K3SkM: {
|
|
172
|
+
Links: [
|
|
173
|
+
{
|
|
174
|
+
Hash: CID.parse('Qmcpz2FHJD7VAhg1fxFXdYJKePtkx1BsHuCrAgWVnaHMTE'),
|
|
175
|
+
Name: 'barreleye',
|
|
176
|
+
Tsize: 58
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
Hash: CID.parse('bafkreifc4hca3inognou377hfhvu2xfchn2ltzi7yu27jkaeujqqqdbjju'),
|
|
180
|
+
Name: '🐡',
|
|
181
|
+
Tsize: 7
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
Qmcpz2FHJD7VAhg1fxFXdYJKePtkx1BsHuCrAgWVnaHMTE: {
|
|
186
|
+
Links: [{
|
|
187
|
+
Hash: CID.parse('bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu'),
|
|
188
|
+
Name: 'fishmonger',
|
|
189
|
+
Tsize: 4
|
|
190
|
+
}]
|
|
191
|
+
},
|
|
192
|
+
bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu: 'fish',
|
|
193
|
+
bafkreifc4hca3inognou377hfhvu2xfchn2ltzi7yu27jkaeujqqqdbjju: 'lobster'
|
|
194
|
+
}
|
|
195
|
+
|
|
154
196
|
export {
|
|
155
197
|
toBlock,
|
|
156
198
|
assert,
|
|
@@ -160,5 +202,9 @@ export {
|
|
|
160
202
|
carBytes,
|
|
161
203
|
goCarBytes,
|
|
162
204
|
goCarRoots,
|
|
163
|
-
goCarIndex
|
|
205
|
+
goCarIndex,
|
|
206
|
+
goCarV2Bytes,
|
|
207
|
+
goCarV2Roots,
|
|
208
|
+
goCarV2Index,
|
|
209
|
+
goCarV2Contents
|
|
164
210
|
}
|
package/test/go.carv2
ADDED
|
Binary file
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
import * as CarBufferWriter from '@ipld/car/buffer-writer'
|
|
4
|
+
import { CarReader } from '@ipld/car/reader'
|
|
5
|
+
import { createHeader } from '../lib/encoder.js'
|
|
6
|
+
import { assert } from './common.js'
|
|
7
|
+
import { CID, varint } from 'multiformats'
|
|
8
|
+
import * as CBOR from '@ipld/dag-cbor'
|
|
9
|
+
import { sha256, sha512 } from 'multiformats/hashes/sha2'
|
|
10
|
+
import { identity } from 'multiformats/hashes/identity'
|
|
11
|
+
import * as Raw from 'multiformats/codecs/raw'
|
|
12
|
+
import * as Block from 'multiformats/block'
|
|
13
|
+
|
|
14
|
+
describe('CarBufferWriter', () => {
|
|
15
|
+
const cid = CID.parse('bafkreifuosuzujyf4i6psbneqtwg2fhplc2wxptc5euspa2gn3bwhnihfu')
|
|
16
|
+
describe('calculateHeaderLength', async () => {
|
|
17
|
+
for (const count of [0, 1, 10, 18, 24, 48, 124, 255, 258, 65536 - 1, 65536]) {
|
|
18
|
+
it(`calculateHeaderLength(new Array(${count}).fill(36))`, () => {
|
|
19
|
+
const roots = new Array(count).fill(cid)
|
|
20
|
+
const sizes = new Array(count).fill(cid.bytes.byteLength)
|
|
21
|
+
assert.deepEqual(
|
|
22
|
+
CarBufferWriter.calculateHeaderLength(sizes),
|
|
23
|
+
createHeader(roots).byteLength
|
|
24
|
+
)
|
|
25
|
+
})
|
|
26
|
+
it(`calculateHeaderLength(new Array(${count}).fill(36))`, () => {
|
|
27
|
+
const roots = new Array(count).fill(cid)
|
|
28
|
+
const rootLengths = roots.map((c) => c.bytes.byteLength)
|
|
29
|
+
assert.deepEqual(CarBufferWriter.calculateHeaderLength(rootLengths), createHeader(roots).byteLength)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
it('estimate on large CIDs', () => {
|
|
33
|
+
const largeCID = CID.parse(`bafkqbbac${'a'.repeat(416)}`)
|
|
34
|
+
assert.equal(
|
|
35
|
+
CarBufferWriter.calculateHeaderLength([
|
|
36
|
+
cid.bytes.byteLength,
|
|
37
|
+
largeCID.bytes.byteLength
|
|
38
|
+
]),
|
|
39
|
+
createHeader([
|
|
40
|
+
cid,
|
|
41
|
+
largeCID
|
|
42
|
+
]).byteLength
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('estimate on large CIDs 2', () => {
|
|
47
|
+
const largeCID = CID.createV1(Raw.code, identity.digest(new Uint8Array(512).fill(1)))
|
|
48
|
+
assert.equal(
|
|
49
|
+
CarBufferWriter.calculateHeaderLength([
|
|
50
|
+
cid.bytes.byteLength,
|
|
51
|
+
largeCID.bytes.byteLength
|
|
52
|
+
]),
|
|
53
|
+
createHeader([cid, largeCID]).byteLength
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('writer', () => {
|
|
59
|
+
it('estimate header and write blocks', async () => {
|
|
60
|
+
const headerSize = CarBufferWriter.estimateHeaderLength(1)
|
|
61
|
+
const dataSize = 256
|
|
62
|
+
const buffer = new ArrayBuffer(headerSize + dataSize)
|
|
63
|
+
const writer = CarBufferWriter.createWriter(buffer, { headerSize })
|
|
64
|
+
const b1 = await Block.encode({
|
|
65
|
+
value: { hello: 'world' },
|
|
66
|
+
codec: CBOR,
|
|
67
|
+
hasher: sha256
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
writer.write(b1)
|
|
71
|
+
|
|
72
|
+
const b2 = await Block.encode({
|
|
73
|
+
value: { bye: 'world' },
|
|
74
|
+
codec: CBOR,
|
|
75
|
+
hasher: sha256
|
|
76
|
+
})
|
|
77
|
+
writer.write(b2)
|
|
78
|
+
|
|
79
|
+
writer.addRoot(b1.cid)
|
|
80
|
+
const bytes = writer.close()
|
|
81
|
+
|
|
82
|
+
const reader = await CarReader.fromBytes(bytes)
|
|
83
|
+
assert.deepEqual(await reader.getRoots(), [b1.cid])
|
|
84
|
+
assert.deepEqual(reader._blocks, [{ cid: b1.cid, bytes: b1.bytes }, { cid: b2.cid, bytes: b2.bytes }])
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('overestimate header', async () => {
|
|
88
|
+
const headerSize = CarBufferWriter.estimateHeaderLength(2)
|
|
89
|
+
const dataSize = 256
|
|
90
|
+
const buffer = new ArrayBuffer(headerSize + dataSize)
|
|
91
|
+
const writer = CarBufferWriter.createWriter(buffer, { headerSize })
|
|
92
|
+
const b1 = await Block.encode({
|
|
93
|
+
value: { hello: 'world' },
|
|
94
|
+
codec: CBOR,
|
|
95
|
+
hasher: sha256
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
writer.write(b1)
|
|
99
|
+
|
|
100
|
+
const b2 = await Block.encode({
|
|
101
|
+
value: { bye: 'world' },
|
|
102
|
+
codec: CBOR,
|
|
103
|
+
hasher: sha256
|
|
104
|
+
})
|
|
105
|
+
writer.write(b2)
|
|
106
|
+
|
|
107
|
+
writer.addRoot(b1.cid)
|
|
108
|
+
assert.throws(() => writer.close(), /Header size was overestimate/)
|
|
109
|
+
const bytes = writer.close({ resize: true })
|
|
110
|
+
|
|
111
|
+
const reader = await CarReader.fromBytes(bytes)
|
|
112
|
+
assert.deepEqual(await reader.getRoots(), [b1.cid])
|
|
113
|
+
assert.deepEqual(reader._blocks, [{ cid: b1.cid, bytes: b1.bytes }, { cid: b2.cid, bytes: b2.bytes }])
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('underestimate header', async () => {
|
|
117
|
+
const headerSize = CarBufferWriter.estimateHeaderLength(2)
|
|
118
|
+
const dataSize = 300
|
|
119
|
+
const buffer = new ArrayBuffer(headerSize + dataSize)
|
|
120
|
+
const writer = CarBufferWriter.createWriter(buffer, { headerSize })
|
|
121
|
+
const b1 = await Block.encode({
|
|
122
|
+
value: { hello: 'world' },
|
|
123
|
+
codec: CBOR,
|
|
124
|
+
hasher: sha256
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
writer.write(b1)
|
|
128
|
+
writer.addRoot(b1.cid)
|
|
129
|
+
|
|
130
|
+
const b2 = await Block.encode({
|
|
131
|
+
value: { bye: 'world' },
|
|
132
|
+
codec: CBOR,
|
|
133
|
+
hasher: sha512
|
|
134
|
+
})
|
|
135
|
+
writer.write(b2)
|
|
136
|
+
assert.throws(() => writer.addRoot(b2.cid), /has no capacity/)
|
|
137
|
+
writer.addRoot(b2.cid, { resize: true })
|
|
138
|
+
|
|
139
|
+
const bytes = writer.close()
|
|
140
|
+
|
|
141
|
+
const reader = await CarReader.fromBytes(bytes)
|
|
142
|
+
assert.deepEqual(await reader.getRoots(), [b1.cid, b2.cid])
|
|
143
|
+
assert.deepEqual(reader._blocks, [{ cid: b1.cid, bytes: b1.bytes }, { cid: b2.cid, bytes: b2.bytes }])
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('has no space for the root', async () => {
|
|
148
|
+
const headerSize = CarBufferWriter.estimateHeaderLength(1)
|
|
149
|
+
const dataSize = 100
|
|
150
|
+
const buffer = new ArrayBuffer(headerSize + dataSize)
|
|
151
|
+
const writer = CarBufferWriter.createWriter(buffer, { headerSize })
|
|
152
|
+
const b1 = await Block.encode({
|
|
153
|
+
value: { hello: 'world' },
|
|
154
|
+
codec: CBOR,
|
|
155
|
+
hasher: sha256
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
writer.write(b1)
|
|
159
|
+
writer.addRoot(b1.cid)
|
|
160
|
+
|
|
161
|
+
const b2 = await Block.encode({
|
|
162
|
+
value: { bye: 'world' },
|
|
163
|
+
codec: CBOR,
|
|
164
|
+
hasher: sha256
|
|
165
|
+
})
|
|
166
|
+
writer.write(b2)
|
|
167
|
+
assert.throws(() => writer.addRoot(b2.cid), /Buffer has no capacity for a new root/)
|
|
168
|
+
assert.throws(() => writer.addRoot(b2.cid, { resize: true }), /Buffer has no capacity for a new root/)
|
|
169
|
+
|
|
170
|
+
const bytes = writer.close()
|
|
171
|
+
|
|
172
|
+
const reader = await CarReader.fromBytes(bytes)
|
|
173
|
+
assert.deepEqual(await reader.getRoots(), [b1.cid])
|
|
174
|
+
assert.deepEqual(reader._blocks, [{ cid: b1.cid, bytes: b1.bytes }, { cid: b2.cid, bytes: b2.bytes }])
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('has no space for the block', async () => {
|
|
178
|
+
const headerSize = CarBufferWriter.estimateHeaderLength(1)
|
|
179
|
+
const dataSize = 58
|
|
180
|
+
const buffer = new ArrayBuffer(headerSize + dataSize)
|
|
181
|
+
const writer = CarBufferWriter.createWriter(buffer, { headerSize })
|
|
182
|
+
const b1 = await Block.encode({
|
|
183
|
+
value: { hello: 'world' },
|
|
184
|
+
codec: CBOR,
|
|
185
|
+
hasher: sha256
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
writer.write(b1)
|
|
189
|
+
writer.addRoot(b1.cid)
|
|
190
|
+
|
|
191
|
+
const b2 = await Block.encode({
|
|
192
|
+
value: { bye: 'world' },
|
|
193
|
+
codec: CBOR,
|
|
194
|
+
hasher: sha256
|
|
195
|
+
})
|
|
196
|
+
assert.throws(() => writer.write(b2), /Buffer has no capacity for this block/)
|
|
197
|
+
|
|
198
|
+
const bytes = writer.close()
|
|
199
|
+
|
|
200
|
+
const reader = await CarReader.fromBytes(bytes)
|
|
201
|
+
assert.deepEqual(await reader.getRoots(), [b1.cid])
|
|
202
|
+
assert.deepEqual(reader._blocks, [{ cid: b1.cid, bytes: b1.bytes }])
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('provide roots', async () => {
|
|
206
|
+
const b1 = await Block.encode({
|
|
207
|
+
value: { hello: 'world' },
|
|
208
|
+
codec: CBOR,
|
|
209
|
+
hasher: sha256
|
|
210
|
+
})
|
|
211
|
+
const b2 = await Block.encode({
|
|
212
|
+
value: { bye: 'world' },
|
|
213
|
+
codec: CBOR,
|
|
214
|
+
hasher: sha512
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
const buffer = new ArrayBuffer(300)
|
|
218
|
+
const writer = CarBufferWriter.createWriter(buffer, { roots: [b1.cid, b2.cid] })
|
|
219
|
+
|
|
220
|
+
writer.write(b1)
|
|
221
|
+
writer.write(b2)
|
|
222
|
+
|
|
223
|
+
const bytes = writer.close()
|
|
224
|
+
|
|
225
|
+
const reader = await CarReader.fromBytes(bytes)
|
|
226
|
+
assert.deepEqual(await reader.getRoots(), [b1.cid, b2.cid])
|
|
227
|
+
assert.deepEqual(reader._blocks, [{ cid: b1.cid, bytes: b1.bytes }, { cid: b2.cid, bytes: b2.bytes }])
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('provide large CID root', async () => {
|
|
231
|
+
const bytes = new Uint8Array(512).fill(1)
|
|
232
|
+
const b1 = await Block.encode({
|
|
233
|
+
value: { hello: 'world' },
|
|
234
|
+
codec: CBOR,
|
|
235
|
+
hasher: sha256
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
const b2 = {
|
|
239
|
+
cid: CID.createV1(Raw.code, identity.digest(bytes)),
|
|
240
|
+
bytes
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const headerSize = CBOR.encode({ version: 1, roots: [b1.cid, b2.cid] }).byteLength
|
|
244
|
+
const bodySize = CarBufferWriter.blockLength(b1) + CarBufferWriter.blockLength(b2)
|
|
245
|
+
const varintSize = varint.encodingLength(headerSize)
|
|
246
|
+
|
|
247
|
+
const writer = CarBufferWriter.createWriter(new ArrayBuffer(varintSize + headerSize + bodySize), { roots: [b1.cid, b2.cid] })
|
|
248
|
+
|
|
249
|
+
writer.write(b1)
|
|
250
|
+
writer.write(b2)
|
|
251
|
+
const car = writer.close()
|
|
252
|
+
const reader = await CarReader.fromBytes(car)
|
|
253
|
+
assert.deepEqual(await reader.getRoots(), [b1.cid, b2.cid])
|
|
254
|
+
assert.deepEqual(reader._blocks, [{ cid: b1.cid, bytes: b1.bytes }, { cid: b2.cid, bytes: b2.bytes }])
|
|
255
|
+
})
|
|
256
|
+
})
|
package/test/test-errors.js
CHANGED
|
@@ -3,7 +3,7 @@ import { bytes } from 'multiformats'
|
|
|
3
3
|
import { encode as cbEncode } from '@ipld/dag-cbor'
|
|
4
4
|
import { encode as vEncode } from 'varint'
|
|
5
5
|
import { CarReader } from '@ipld/car/reader'
|
|
6
|
-
import { carBytes, assert } from './common.js'
|
|
6
|
+
import { carBytes, assert, goCarV2Bytes } from './common.js'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @param {any} block
|
|
@@ -34,43 +34,65 @@ describe('Misc errors', () => {
|
|
|
34
34
|
|
|
35
35
|
it('bad version', async () => {
|
|
36
36
|
// quick sanity check that makeHeader() works properly!
|
|
37
|
-
const buf2 = bytes.fromHex('
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR version:
|
|
37
|
+
const buf2 = bytes.fromHex('0aa16776657273696f6e03')
|
|
38
|
+
assert.strictEqual(bytes.toHex(makeHeader({ version: 3 })), '0aa16776657273696f6e03')
|
|
39
|
+
// {version:3}
|
|
40
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR version: 3')
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
describe('bad header', async () => {
|
|
44
|
+
it('sanity check', async () => {
|
|
45
|
+
// sanity check, this should be fine
|
|
46
|
+
const buf2 = makeHeader({ version: 1, roots: [] })
|
|
47
|
+
await assert.isFulfilled(CarReader.fromBytes(buf2))
|
|
48
|
+
})
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
it('no \'version\' array', async () => {
|
|
51
|
+
const buf2 = makeHeader({ roots: [] })
|
|
52
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR header format')
|
|
53
|
+
})
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
it('bad \'version\' type', async () => {
|
|
56
|
+
const buf2 = makeHeader({ version: '1', roots: [] })
|
|
57
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR header format')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('no \'roots\' array', async () => {
|
|
61
|
+
const buf2 = makeHeader({ version: 1 })
|
|
62
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR header format')
|
|
63
|
+
})
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
it('bad \'roots\' type', async () => {
|
|
66
|
+
const buf2 = makeHeader({ version: 1, roots: {} })
|
|
67
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR header format')
|
|
68
|
+
})
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
it('extraneous properties', async () => {
|
|
71
|
+
const buf2 = makeHeader({ version: 1, roots: [], blip: true })
|
|
72
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR header format')
|
|
73
|
+
})
|
|
63
74
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
it('not an object', async () => {
|
|
76
|
+
const buf2 = makeHeader([1, []])
|
|
77
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR header format')
|
|
78
|
+
})
|
|
67
79
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
it('not an object', async () => {
|
|
81
|
+
const buf2 = makeHeader(null)
|
|
82
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR header format')
|
|
83
|
+
})
|
|
71
84
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
85
|
+
it('recursive v2 header', async () => {
|
|
86
|
+
// first 51 bytes are the carv2 header:
|
|
87
|
+
// 11b prefix, 16b characteristics, 8b data offset, 8b data size, 8b index offset
|
|
88
|
+
const v2Header = goCarV2Bytes.slice(0, 51)
|
|
89
|
+
// parser should expect to get a carv1 header at the data offset, but it uses the same
|
|
90
|
+
// code to check the carv2 header so let's make sure it doesn't allow recursive carv2
|
|
91
|
+
// headers
|
|
92
|
+
const buf2 = new Uint8Array(51 * 2)
|
|
93
|
+
buf2.set(v2Header, 0)
|
|
94
|
+
buf2.set(v2Header, 51)
|
|
95
|
+
await assert.isRejected(CarReader.fromBytes(buf2), Error, 'Invalid CAR version: 2 (expected 1)')
|
|
96
|
+
})
|
|
75
97
|
})
|
|
76
98
|
})
|
package/test/test-indexer.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
/* eslint-env mocha */
|
|
2
2
|
|
|
3
3
|
import { CarIndexer } from '@ipld/car/indexer'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
goCarBytes,
|
|
6
|
+
goCarIndex,
|
|
7
|
+
goCarV2Bytes,
|
|
8
|
+
goCarV2Roots,
|
|
9
|
+
goCarV2Index,
|
|
10
|
+
makeIterable,
|
|
11
|
+
assert
|
|
12
|
+
} from './common.js'
|
|
5
13
|
import { verifyRoots } from './verify-store-reader.js'
|
|
6
14
|
|
|
7
15
|
describe('CarIndexer fromBytes()', () => {
|
|
@@ -18,6 +26,21 @@ describe('CarIndexer fromBytes()', () => {
|
|
|
18
26
|
assert.deepStrictEqual(indexData, goCarIndex)
|
|
19
27
|
})
|
|
20
28
|
|
|
29
|
+
it('v2 complete', async () => {
|
|
30
|
+
const indexer = await CarIndexer.fromBytes(goCarV2Bytes)
|
|
31
|
+
const roots = await indexer.getRoots()
|
|
32
|
+
assert.strictEqual(roots.length, 1)
|
|
33
|
+
assert.ok(goCarV2Roots[0].equals(roots[0]))
|
|
34
|
+
assert.strictEqual(indexer.version, 2)
|
|
35
|
+
|
|
36
|
+
const indexData = []
|
|
37
|
+
for await (const index of indexer) {
|
|
38
|
+
indexData.push(index)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
assert.deepStrictEqual(indexData, goCarV2Index)
|
|
42
|
+
})
|
|
43
|
+
|
|
21
44
|
it('bad argument', async () => {
|
|
22
45
|
for (const arg of [true, false, null, undefined, 'string', 100, { obj: 'nope' }]) {
|
|
23
46
|
// @ts-ignore
|