@helia/car 4.0.3 → 4.0.4-bb2ab74
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 +34 -17
- package/dist/index.min.js +2 -1
- package/dist/index.min.js.map +7 -0
- package/dist/src/car.d.ts +17 -0
- package/dist/src/car.d.ts.map +1 -0
- package/dist/src/car.js +162 -0
- package/dist/src/car.js.map +1 -0
- package/dist/src/constants.d.ts +10 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +10 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/errors.d.ts +9 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +9 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/export-strategies/block-exporter.d.ts +10 -0
- package/dist/src/export-strategies/block-exporter.d.ts.map +1 -0
- package/dist/src/export-strategies/block-exporter.js +12 -0
- package/dist/src/export-strategies/block-exporter.js.map +1 -0
- package/dist/src/export-strategies/index.d.ts +4 -0
- package/dist/src/export-strategies/index.d.ts.map +1 -0
- package/dist/src/export-strategies/index.js +4 -0
- package/dist/src/export-strategies/index.js.map +1 -0
- package/dist/src/export-strategies/subgraph-exporter.d.ts +14 -0
- package/dist/src/export-strategies/subgraph-exporter.d.ts.map +1 -0
- package/dist/src/export-strategies/subgraph-exporter.js +17 -0
- package/dist/src/export-strategies/subgraph-exporter.js.map +1 -0
- package/dist/src/export-strategies/unixfs-exporter.d.ts +11 -0
- package/dist/src/export-strategies/unixfs-exporter.d.ts.map +1 -0
- package/dist/src/export-strategies/unixfs-exporter.js +18 -0
- package/dist/src/export-strategies/unixfs-exporter.js.map +1 -0
- package/dist/src/index.d.ts +89 -24
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +38 -95
- package/dist/src/index.js.map +1 -1
- package/dist/src/traversal-strategies/cid-path.d.ts +16 -0
- package/dist/src/traversal-strategies/cid-path.d.ts.map +1 -0
- package/dist/src/traversal-strategies/cid-path.js +22 -0
- package/dist/src/traversal-strategies/cid-path.js.map +1 -0
- package/dist/src/traversal-strategies/graph-search.d.ts +14 -0
- package/dist/src/traversal-strategies/graph-search.d.ts.map +1 -0
- package/dist/src/traversal-strategies/graph-search.js +19 -0
- package/dist/src/traversal-strategies/graph-search.js.map +1 -0
- package/dist/src/traversal-strategies/index.d.ts +4 -0
- package/dist/src/traversal-strategies/index.d.ts.map +1 -0
- package/dist/src/traversal-strategies/index.js +4 -0
- package/dist/src/traversal-strategies/index.js.map +1 -0
- package/dist/src/traversal-strategies/unixfs-path.d.ts +13 -0
- package/dist/src/traversal-strategies/unixfs-path.d.ts.map +1 -0
- package/dist/src/traversal-strategies/unixfs-path.js +42 -0
- package/dist/src/traversal-strategies/unixfs-path.js.map +1 -0
- package/package.json +12 -6
- package/src/car.ts +218 -0
- package/src/constants.ts +11 -0
- package/src/errors.ts +8 -0
- package/src/export-strategies/block-exporter.ts +13 -0
- package/src/export-strategies/index.ts +3 -0
- package/src/export-strategies/subgraph-exporter.ts +18 -0
- package/src/export-strategies/unixfs-exporter.ts +22 -0
- package/src/index.ts +99 -119
- package/src/traversal-strategies/cid-path.ts +29 -0
- package/src/traversal-strategies/graph-search.ts +25 -0
- package/src/traversal-strategies/index.ts +3 -0
- package/src/traversal-strategies/unixfs-path.ts +58 -0
- package/dist/typedoc-urls.json +0 -10
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type CID } from 'multiformats/cid'
|
|
2
|
+
import { type ExportStrategy } from '../index.js'
|
|
3
|
+
import type { BlockView } from 'multiformats/block/interface'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Traverses the DAG breadth-first starting at the target CID and yields all
|
|
7
|
+
* encountered blocks.
|
|
8
|
+
*
|
|
9
|
+
* Blocks linked to from the target block are traversed using codecs defined in
|
|
10
|
+
* the helia config.
|
|
11
|
+
*/
|
|
12
|
+
export class SubgraphExporter implements ExportStrategy {
|
|
13
|
+
async * export (_cid: CID, block: BlockView<any, any, any, 0 | 1>): AsyncGenerator<CID, void, undefined> {
|
|
14
|
+
for await (const [, linkedCid] of block.links()) {
|
|
15
|
+
yield linkedCid
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DAG_PB_CODEC_CODE, RAW_PB_CODEC_CODE } from '../constants.js'
|
|
2
|
+
import { NotUnixFSError } from '../errors.js'
|
|
3
|
+
import type { ExportStrategy } from '../index.js'
|
|
4
|
+
import type { BlockView } from 'multiformats/block/interface'
|
|
5
|
+
import type { CID } from 'multiformats/cid'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This exporter is used when you want to generate a car file that contains a
|
|
9
|
+
* single UnixFS file or directory
|
|
10
|
+
*/
|
|
11
|
+
export class UnixFSExporter implements ExportStrategy {
|
|
12
|
+
async * export (cid: CID, block: BlockView<any, any, any, 0 | 1>): AsyncGenerator<CID, void, undefined> {
|
|
13
|
+
if (cid.code !== DAG_PB_CODEC_CODE && cid.code !== RAW_PB_CODEC_CODE) {
|
|
14
|
+
throw new NotUnixFSError('Target CID was not UnixFS - use the SubGraphExporter to export arbitrary graphs')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// yield all the blocks that make up the file or directory
|
|
18
|
+
for await (const [, linkedCid] of block.links()) {
|
|
19
|
+
yield linkedCid
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,39 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
*
|
|
4
|
-
* `@helia/car` provides `import` and `export` methods to read/write Car files
|
|
4
|
+
* `@helia/car` provides `import` and `export` methods to read/write Car files
|
|
5
|
+
* to {@link https://github.com/ipfs/helia Helia}'s blockstore.
|
|
5
6
|
*
|
|
6
7
|
* See the {@link Car} interface for all available operations.
|
|
7
8
|
*
|
|
8
|
-
* By default it supports `dag-pb`, `dag-cbor`, `dag-json` and `raw` CIDs, more
|
|
9
|
+
* By default it supports `dag-pb`, `dag-cbor`, `dag-json` and `raw` CIDs, more
|
|
10
|
+
* esoteric DAG walkers can be passed as an init option.
|
|
9
11
|
*
|
|
10
12
|
* @example Exporting a DAG as a CAR file
|
|
11
13
|
*
|
|
12
14
|
* ```typescript
|
|
13
15
|
* import { createHelia } from 'helia'
|
|
14
|
-
* import { unixfs } from '@helia/unixfs'
|
|
15
16
|
* import { car } from '@helia/car'
|
|
16
|
-
* import {
|
|
17
|
-
* import { Readable } from 'node:stream'
|
|
17
|
+
* import { CID } from 'multiformats/cid'
|
|
18
18
|
* import nodeFs from 'node:fs'
|
|
19
19
|
*
|
|
20
|
-
* const helia = await createHelia(
|
|
21
|
-
*
|
|
22
|
-
* })
|
|
23
|
-
* const fs = unixfs(helia)
|
|
20
|
+
* const helia = await createHelia()
|
|
21
|
+
* const cid = CID.parse('QmFoo...')
|
|
24
22
|
*
|
|
25
|
-
*
|
|
26
|
-
* const
|
|
23
|
+
* const c = car(helia)
|
|
24
|
+
* const out = nodeFs.createWriteStream('example.car')
|
|
25
|
+
*
|
|
26
|
+
* for await (const buf of c.stream(cid)) {
|
|
27
|
+
* out.write(buf)
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* out.end()
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example Exporting a part of a UnixFS DAG as a CAR file
|
|
34
|
+
*
|
|
35
|
+
* ```typescript
|
|
36
|
+
* import { createHelia } from 'helia'
|
|
37
|
+
* import { car, UnixFSPath } from '@helia/car'
|
|
38
|
+
* import { CID } from 'multiformats/cid'
|
|
39
|
+
* import nodeFs from 'node:fs'
|
|
40
|
+
*
|
|
41
|
+
* const helia = await createHelia()
|
|
42
|
+
* const cid = CID.parse('QmFoo...')
|
|
27
43
|
*
|
|
28
|
-
* // export it as a Car
|
|
29
44
|
* const c = car(helia)
|
|
30
|
-
* const
|
|
45
|
+
* const out = nodeFs.createWriteStream('example.car')
|
|
31
46
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
47
|
+
* for await (const buf of c.stream(cid, {
|
|
48
|
+
* traversal: new UnixFSPath('/foo/bar/baz.txt')
|
|
49
|
+
* })) {
|
|
50
|
+
* out.write(buf)
|
|
51
|
+
* }
|
|
34
52
|
*
|
|
35
|
-
*
|
|
36
|
-
* await c.export(cid, writer)
|
|
53
|
+
* out.end()
|
|
37
54
|
* ```
|
|
38
55
|
*
|
|
39
56
|
* @example Importing all blocks from a CAR file
|
|
@@ -59,31 +76,83 @@
|
|
|
59
76
|
* ```
|
|
60
77
|
*/
|
|
61
78
|
|
|
62
|
-
import {
|
|
63
|
-
import drain from 'it-drain'
|
|
64
|
-
import map from 'it-map'
|
|
65
|
-
import { createUnsafe } from 'multiformats/block'
|
|
66
|
-
import defer from 'p-defer'
|
|
67
|
-
import PQueue from 'p-queue'
|
|
79
|
+
import { Car as CarClass } from './car.js'
|
|
68
80
|
import type { CodecLoader } from '@helia/interface'
|
|
69
|
-
import type {
|
|
70
|
-
import type { CarReader } from '@ipld/car'
|
|
71
|
-
import type { AbortOptions } from '@libp2p/interface'
|
|
81
|
+
import type { PutManyBlocksProgressEvents, GetBlockProgressEvents } from '@helia/interface/blocks'
|
|
82
|
+
import type { CarWriter, CarReader } from '@ipld/car'
|
|
83
|
+
import type { AbortOptions, ComponentLogger } from '@libp2p/interface'
|
|
72
84
|
import type { Filter } from '@libp2p/utils/filters'
|
|
73
85
|
import type { Blockstore } from 'interface-blockstore'
|
|
86
|
+
import type { BlockView } from 'multiformats/block/interface'
|
|
74
87
|
import type { CID } from 'multiformats/cid'
|
|
75
88
|
import type { ProgressOptions } from 'progress-events'
|
|
76
89
|
|
|
77
90
|
export interface CarComponents {
|
|
91
|
+
logger: ComponentLogger
|
|
78
92
|
blockstore: Blockstore
|
|
79
93
|
getCodec: CodecLoader
|
|
80
94
|
}
|
|
81
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Interface for different traversal strategies.
|
|
98
|
+
*
|
|
99
|
+
* While traversing the DAG, it will yield blocks that it has traversed.
|
|
100
|
+
*/
|
|
101
|
+
export interface TraversalStrategy {
|
|
102
|
+
/**
|
|
103
|
+
* Traverse the DAG and yield the next CID to traverse
|
|
104
|
+
*/
|
|
105
|
+
traverse<T extends BlockView<any, any, any, 0 | 1>>(cid: CID, block: T): AsyncGenerator<CID, void, undefined>
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Returns true if the current CID is the target and we should switch to the
|
|
109
|
+
* export strategy
|
|
110
|
+
*/
|
|
111
|
+
isTarget(cid: CID): boolean
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Interface for different export strategies.
|
|
116
|
+
*
|
|
117
|
+
* When traversal has ended the export begins starting at the target CID, and
|
|
118
|
+
* the export strategy may do further traversal and writing to the car file.
|
|
119
|
+
*/
|
|
120
|
+
export interface ExportStrategy {
|
|
121
|
+
/**
|
|
122
|
+
* Export the DAG and yield the next CID to traverse
|
|
123
|
+
*/
|
|
124
|
+
export<T extends BlockView<any, any, any, 0 | 1>>(cid: CID, block: T): AsyncGenerator<CID, void, undefined>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export * from './export-strategies/index.js'
|
|
128
|
+
export * from './traversal-strategies/index.js'
|
|
129
|
+
|
|
82
130
|
export interface ExportCarOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents> {
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* If true, the blockstore will not do any network requests.
|
|
134
|
+
*
|
|
135
|
+
* @default false
|
|
136
|
+
*/
|
|
137
|
+
offline?: boolean
|
|
138
|
+
|
|
83
139
|
/**
|
|
84
|
-
* If a filter is passed it will be used to deduplicate blocks exported in the
|
|
140
|
+
* If a filter is passed it will be used to deduplicate blocks exported in the
|
|
141
|
+
* car file
|
|
85
142
|
*/
|
|
86
143
|
blockFilter?: Filter
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The traversal strategy to use for the export. This determines how the dag
|
|
147
|
+
* is traversed: either depth first, breadth first, or a custom strategy.
|
|
148
|
+
*/
|
|
149
|
+
traversal?: TraversalStrategy
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Export strategy to use for the export. This should be used to change the
|
|
153
|
+
* blocks included in the exported car file. (e.g. https://specs.ipfs.tech/http-gateways/trustless-gateway/#dag-scope-request-query-parameter)
|
|
154
|
+
*/
|
|
155
|
+
exporter?: ExportStrategy
|
|
87
156
|
}
|
|
88
157
|
|
|
89
158
|
/**
|
|
@@ -152,7 +221,7 @@ export interface Car {
|
|
|
152
221
|
*
|
|
153
222
|
* ```typescript
|
|
154
223
|
* import { createHelia } from 'helia'
|
|
155
|
-
* import { car } from '@helia/car
|
|
224
|
+
* import { car } from '@helia/car'
|
|
156
225
|
* import { CID } from 'multiformats/cid'
|
|
157
226
|
*
|
|
158
227
|
* const helia = await createHelia()
|
|
@@ -165,101 +234,12 @@ export interface Car {
|
|
|
165
234
|
* }
|
|
166
235
|
* ```
|
|
167
236
|
*/
|
|
168
|
-
stream(root: CID | CID[], options?:
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const DAG_WALK_QUEUE_CONCURRENCY = 1
|
|
172
|
-
|
|
173
|
-
class DefaultCar implements Car {
|
|
174
|
-
private readonly components: CarComponents
|
|
175
|
-
|
|
176
|
-
constructor (components: CarComponents, init: any) {
|
|
177
|
-
this.components = components
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
async import (reader: Pick<CarReader, 'blocks'>, options?: AbortOptions & ProgressOptions<PutManyBlocksProgressEvents>): Promise<void> {
|
|
181
|
-
await drain(this.components.blockstore.putMany(
|
|
182
|
-
map(reader.blocks(), ({ cid, bytes }) => ({ cid, block: bytes })),
|
|
183
|
-
options
|
|
184
|
-
))
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async export (root: CID | CID[], writer: Pick<CarWriter, 'put' | 'close'>, options?: ExportCarOptions): Promise<void> {
|
|
188
|
-
const deferred = defer<Error | undefined>()
|
|
189
|
-
const roots = Array.isArray(root) ? root : [root]
|
|
190
|
-
|
|
191
|
-
// use a queue to walk the DAG instead of recursion so we can traverse very large DAGs
|
|
192
|
-
const queue = new PQueue({
|
|
193
|
-
concurrency: DAG_WALK_QUEUE_CONCURRENCY
|
|
194
|
-
})
|
|
195
|
-
queue.on('idle', () => {
|
|
196
|
-
deferred.resolve()
|
|
197
|
-
})
|
|
198
|
-
queue.on('error', (err) => {
|
|
199
|
-
queue.clear()
|
|
200
|
-
deferred.reject(err)
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
for (const root of roots) {
|
|
204
|
-
void queue.add(async () => {
|
|
205
|
-
await this.#walkDag(root, queue, async (cid, bytes) => {
|
|
206
|
-
// if a filter has been passed, skip blocks that have already been written
|
|
207
|
-
if (options?.blockFilter?.has(cid.multihash.bytes) === true) {
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
options?.blockFilter?.add(cid.multihash.bytes)
|
|
212
|
-
await writer.put({ cid, bytes })
|
|
213
|
-
}, options)
|
|
214
|
-
})
|
|
215
|
-
.catch(() => {})
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// wait for the writer to end
|
|
219
|
-
try {
|
|
220
|
-
await deferred.promise
|
|
221
|
-
} finally {
|
|
222
|
-
await writer.close()
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async * stream (root: CID | CID[], options?: ExportCarOptions): AsyncGenerator<Uint8Array, void, undefined> {
|
|
227
|
-
const { writer, out } = CarWriter.create(root)
|
|
228
|
-
|
|
229
|
-
// has to be done async so we write to `writer` and read from `out` at the
|
|
230
|
-
// same time
|
|
231
|
-
this.export(root, writer, options)
|
|
232
|
-
.catch(() => {})
|
|
233
|
-
|
|
234
|
-
for await (const buf of out) {
|
|
235
|
-
yield buf
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Walk the DAG behind the passed CID, ensure all blocks are present in the blockstore
|
|
241
|
-
* and update the pin count for them
|
|
242
|
-
*/
|
|
243
|
-
async #walkDag (cid: CID, queue: PQueue, withBlock: (cid: CID, block: Uint8Array) => Promise<void>, options?: AbortOptions & ProgressOptions<GetBlockProgressEvents>): Promise<void> {
|
|
244
|
-
const codec = await this.components.getCodec(cid.code)
|
|
245
|
-
const bytes = await this.components.blockstore.get(cid, options)
|
|
246
|
-
|
|
247
|
-
await withBlock(cid, bytes)
|
|
248
|
-
|
|
249
|
-
const block = createUnsafe({ bytes, cid, codec })
|
|
250
|
-
|
|
251
|
-
// walk dag, ensure all blocks are present
|
|
252
|
-
for await (const [,cid] of block.links()) {
|
|
253
|
-
void queue.add(async () => {
|
|
254
|
-
await this.#walkDag(cid, queue, withBlock, options)
|
|
255
|
-
})
|
|
256
|
-
}
|
|
257
|
-
}
|
|
237
|
+
stream(root: CID | CID[], options?: ExportCarOptions): AsyncGenerator<Uint8Array, void, undefined>
|
|
258
238
|
}
|
|
259
239
|
|
|
260
240
|
/**
|
|
261
241
|
* Create a {@link Car} instance for use with {@link https://github.com/ipfs/helia Helia}
|
|
262
242
|
*/
|
|
263
243
|
export function car (helia: CarComponents, init: any = {}): Car {
|
|
264
|
-
return new
|
|
244
|
+
return new CarClass(helia, init)
|
|
265
245
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { TraversalStrategy } from '../index.js'
|
|
2
|
+
import type { BlockView } from 'multiformats/block/interface'
|
|
3
|
+
import type { CID } from 'multiformats/cid'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Simple strategy that traverses a known path to a target CID.
|
|
7
|
+
*
|
|
8
|
+
* All this strategy does is yield the next CID in the known path.
|
|
9
|
+
*/
|
|
10
|
+
export class CIDPath implements TraversalStrategy {
|
|
11
|
+
private readonly pathToTarget: CID[]
|
|
12
|
+
private readonly target: CID
|
|
13
|
+
|
|
14
|
+
constructor (pathToTarget: CID[]) {
|
|
15
|
+
this.pathToTarget = pathToTarget
|
|
16
|
+
this.target = pathToTarget[pathToTarget.length - 1]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
isTarget (cid: CID): boolean {
|
|
20
|
+
return this.target.equals(cid)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async * traverse <T extends BlockView<any, any, any, 0 | 1>>(cid: CID, _block?: T): AsyncGenerator<CID, void, undefined> {
|
|
24
|
+
const givenCidIndex = this.pathToTarget.indexOf(cid)
|
|
25
|
+
const nextCid = this.pathToTarget[givenCidIndex + 1]
|
|
26
|
+
|
|
27
|
+
yield nextCid
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { TraversalStrategy } from '../index.js'
|
|
2
|
+
import type { BlockView } from 'multiformats/block/interface'
|
|
3
|
+
import type { CID } from 'multiformats/cid'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A traversal strategy that performs a breadth-first search (so as to not load blocks unnecessarily) looking for a
|
|
7
|
+
* target CID. Traversal stops when we reach the target CID or run out of nodes.
|
|
8
|
+
*/
|
|
9
|
+
export class GraphSearch implements TraversalStrategy {
|
|
10
|
+
private readonly target: CID
|
|
11
|
+
|
|
12
|
+
constructor (target: CID) {
|
|
13
|
+
this.target = target
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
isTarget (cid: CID): boolean {
|
|
17
|
+
return this.target.equals(cid)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async * traverse <T extends BlockView<any, any, any, 0 | 1>>(cid: CID, block: T): AsyncGenerator<CID, void, undefined> {
|
|
21
|
+
for await (const [, linkedCid] of block.links()) {
|
|
22
|
+
yield linkedCid
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { decode } from '@ipld/dag-pb'
|
|
2
|
+
import { UnixFS } from 'ipfs-unixfs'
|
|
3
|
+
import { DAG_PB_CODEC_CODE } from '../constants.js'
|
|
4
|
+
import { NotUnixFSError } from '../errors.js'
|
|
5
|
+
import type { TraversalStrategy } from '../index.js'
|
|
6
|
+
import type { BlockView } from 'multiformats/block/interface'
|
|
7
|
+
import type { CID } from 'multiformats/cid'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Traverses a DAG containing UnixFS directories
|
|
11
|
+
*/
|
|
12
|
+
export class UnixFSPath implements TraversalStrategy {
|
|
13
|
+
private readonly path: string[]
|
|
14
|
+
|
|
15
|
+
constructor (path: string) {
|
|
16
|
+
// "/foo/bar/baz.txt" -> ['foo', 'bar', 'baz.txt']
|
|
17
|
+
this.path = path.replace(/^\//, '').split('/')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
isTarget (): boolean {
|
|
21
|
+
return this.path.length === 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async * traverse <T extends BlockView<any, any, any, 0 | 1>>(cid: CID, block: T): AsyncGenerator<CID, void, undefined> {
|
|
25
|
+
if (cid.code !== DAG_PB_CODEC_CODE) {
|
|
26
|
+
throw new NotUnixFSError('Target CID is not UnixFS')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const segment = this.path.shift()
|
|
30
|
+
|
|
31
|
+
if (segment == null) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const pb = decode(block.bytes)
|
|
36
|
+
|
|
37
|
+
if (pb.Data == null) {
|
|
38
|
+
throw new NotUnixFSError('Target CID has no UnixFS data in decoded bytes')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const unixfs = UnixFS.unmarshal(pb.Data)
|
|
42
|
+
|
|
43
|
+
if (unixfs.type === 'directory') {
|
|
44
|
+
const link = pb.Links.filter(link => link.Name === segment).pop()
|
|
45
|
+
|
|
46
|
+
if (link == null) {
|
|
47
|
+
throw new NotUnixFSError(`Target CID directory has no link with name ${segment}`)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
yield link.Hash
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// TODO: HAMT support
|
|
55
|
+
|
|
56
|
+
throw new NotUnixFSError('Target CID is not a UnixFS directory')
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/typedoc-urls.json
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"Car": "https://ipfs.github.io/helia/interfaces/_helia_car.Car.html",
|
|
3
|
-
".:Car": "https://ipfs.github.io/helia/interfaces/_helia_car.Car.html",
|
|
4
|
-
"CarComponents": "https://ipfs.github.io/helia/interfaces/_helia_car.CarComponents.html",
|
|
5
|
-
".:CarComponents": "https://ipfs.github.io/helia/interfaces/_helia_car.CarComponents.html",
|
|
6
|
-
"ExportCarOptions": "https://ipfs.github.io/helia/interfaces/_helia_car.ExportCarOptions.html",
|
|
7
|
-
".:ExportCarOptions": "https://ipfs.github.io/helia/interfaces/_helia_car.ExportCarOptions.html",
|
|
8
|
-
"car": "https://ipfs.github.io/helia/functions/_helia_car.car-1.html",
|
|
9
|
-
".:car": "https://ipfs.github.io/helia/functions/_helia_car.car-1.html"
|
|
10
|
-
}
|