58
58
* ```
59
59
*/
60
60
61
+ import { CarWriter } from '@ipld/car'
61
62
import drain from 'it-drain'
62
63
import map from 'it-map'
63
64
import defer from 'p-defer'
64
65
import PQueue from 'p-queue'
65
66
import type { DAGWalker } from '@helia/interface'
66
67
import type { GetBlockProgressEvents , PutManyBlocksProgressEvents } from '@helia/interface/blocks'
67
- import type { CarReader , CarWriter } from '@ipld/car'
68
+ import type { CarReader } from '@ipld/car'
68
69
import type { AbortOptions } from '@libp2p/interfaces'
69
70
import type { Blockstore } from 'interface-blockstore'
70
71
import type { CID } from 'multiformats/cid'
@@ -129,6 +130,28 @@ export interface Car {
129
130
* ```
130
131
*/
131
132
export ( root : CID | CID [ ] , writer : Pick < CarWriter , 'put' | 'close' > , options ?: AbortOptions & ProgressOptions < GetBlockProgressEvents > ) : Promise < void >
133
+
134
+ /**
135
+ * Returns an AsyncGenerator that yields CAR file bytes.
136
+ *
137
+ * @example
138
+ *
139
+ * ```typescript
140
+ * import { createHelia } from 'helia'
141
+ * import { car } from '@helia/car
142
+ * import { CID } from 'multiformats/cid'
143
+ *
144
+ * const helia = await createHelia()
145
+ * const cid = CID.parse('QmFoo...')
146
+ *
147
+ * const c = car(helia)
148
+ *
149
+ * for (const buf of c.stream(cid)) {
150
+ * // store or send `buf` somewhere
151
+ * }
152
+ * ```
153
+ */
154
+ stream ( root : CID | CID [ ] , options ?: AbortOptions & ProgressOptions < GetBlockProgressEvents > ) : AsyncGenerator < Uint8Array >
132
155
}
133
156
134
157
const DAG_WALK_QUEUE_CONCURRENCY = 1
@@ -148,7 +171,7 @@ class DefaultCar implements Car {
148
171
}
149
172
150
173
async export ( root : CID | CID [ ] , writer : Pick < CarWriter , 'put' | 'close' > , options ?: AbortOptions & ProgressOptions < GetBlockProgressEvents > ) : Promise < void > {
151
- const deferred = defer ( )
174
+ const deferred = defer < Error | undefined > ( )
152
175
const roots = Array . isArray ( root ) ? root : [ root ]
153
176
154
177
// use a queue to walk the DAG instead of recursion so we can traverse very large DAGs
@@ -159,6 +182,7 @@ class DefaultCar implements Car {
159
182
deferred . resolve ( )
160
183
} )
161
184
queue . on ( 'error' , ( err ) => {
185
+ queue . clear ( )
162
186
deferred . reject ( err )
163
187
} )
164
188
@@ -168,6 +192,7 @@ class DefaultCar implements Car {
168
192
await writer . put ( { cid, bytes } )
169
193
} , options )
170
194
} )
195
+ . catch ( ( ) => { } )
171
196
}
172
197
173
198
// wait for the writer to end
@@ -178,6 +203,19 @@ class DefaultCar implements Car {
178
203
}
179
204
}
180
205
206
+ async * stream ( root : CID | CID [ ] , options ?: AbortOptions & ProgressOptions < GetBlockProgressEvents > ) : AsyncGenerator < Uint8Array , void , undefined > {
207
+ const { writer, out } = CarWriter . create ( root )
208
+
209
+ // has to be done async so we write to `writer` and read from `out` at the
210
+ // same time
211
+ this . export ( root , writer , options )
212
+ . catch ( ( ) => { } )
213
+
214
+ for await ( const buf of out ) {
215
+ yield buf
216
+ }
217
+ }
218
+
181
219
/**
182
220
* Walk the DAG behind the passed CID, ensure all blocks are present in the blockstore
183
221
* and update the pin count for them
0 commit comments