11import { CID } from 'multiformats/cid'
22import { createUnsafe } from 'multiformats/block'
3- import { base58btc } from 'multiformats/bases/base58'
43import { CarWriter } from '@ipld/car/writer'
54import { withTimeoutOption } from 'ipfs-core-utils/with-timeout-option'
65import debug from 'debug'
76import * as raw from 'multiformats/codecs/raw'
87import * as json from 'multiformats/codecs/json'
8+ import { walk } from 'multiformats/traversal'
99
1010const log = debug ( 'ipfs:components:dag:import' )
1111
@@ -23,6 +23,11 @@ const NO_LINKS_CODECS = [
2323 * @typedef {import('ipfs-core-types/src/utils').AbortOptions } AbortOptions
2424 */
2525
26+ /**
27+ * @template T
28+ * @typedef {import('multiformats/block').Block<T> } Block
29+ */
30+
2631/**
2732 * @param {Object } config
2833 * @param {IPFSRepo } config.repo
@@ -53,12 +58,11 @@ export function createExport ({ repo, preload, codecs }) {
5358 let err = null
5459 ; ( async ( ) => {
5560 try {
56- await traverseWrite (
57- repo ,
58- { signal : options . signal , timeout : options . timeout } ,
59- cid ,
60- writer ,
61- codecs )
61+ const load = makeLoader ( repo , writer , {
62+ signal : options . signal ,
63+ timeout : options . timeout
64+ } , codecs )
65+ await walk ( { cid, load } )
6266 } catch ( /** @type {any } */ e ) {
6367 err = e
6468 } finally {
@@ -81,52 +85,30 @@ export function createExport ({ repo, preload, codecs }) {
8185}
8286
8387/**
88+ * @template T
8489 * @param {IPFSRepo } repo
85- * @param {AbortOptions } options
86- * @param {CID } cid
8790 * @param {BlockWriter } writer
91+ * @param {AbortOptions } options
8892 * @param {import('ipfs-core-utils/multicodecs').Multicodecs } codecs
89- * @param {Set<string> } seen
90- * @returns {Promise<void> }
93+ * @returns {(cid:CID)=>Promise<Block<T>|null> }
9194 */
92- async function traverseWrite ( repo , options , cid , writer , codecs , seen = new Set ( ) ) {
93- const b58Cid = cid . toString ( base58btc )
94- if ( seen . has ( b58Cid ) ) {
95- return
96- }
95+ function makeLoader ( repo , writer , options , codecs ) {
96+ return async ( cid ) => {
97+ const codec = await codecs . getCodec ( cid . code )
9798
98- const block = await getBlock ( repo , options , cid , codecs )
99+ if ( ! codec ) {
100+ throw new Error ( `Can't decode links in block with codec 0x${ cid . code . toString ( 16 ) } to form complete DAG` )
101+ }
99102
100- log ( `Adding block ${ cid } to car` )
101- await writer . put ( block )
102- seen . add ( b58Cid )
103+ const bytes = await repo . blocks . get ( cid , options )
103104
104- // recursive traversal of all links
105- for ( const link of block . links ) {
106- await traverseWrite ( repo , options , link , writer , codecs , seen )
107- }
108- }
105+ log ( `Adding block ${ cid } to car` )
106+ await writer . put ( { cid, bytes } )
109107
110- /**
111- * @param {IPFSRepo } repo
112- * @param {AbortOptions } options
113- * @param {CID } cid
114- * @param {import('ipfs-core-utils/multicodecs').Multicodecs } codecs
115- * @returns {Promise<{cid:CID, bytes:Uint8Array, links:CID[]}> }
116- */
117- async function getBlock ( repo , options , cid , codecs ) {
118- const bytes = await repo . blocks . get ( cid , options )
119-
120- /** @type {CID[] } */
121- let links = [ ]
122- const codec = await codecs . getCodec ( cid . code )
123-
124- if ( codec ) {
125- const block = createUnsafe ( { bytes, cid, codec } )
126- links = [ ...block . links ( ) ] . map ( ( l ) => l [ 1 ] )
127- } else if ( ! NO_LINKS_CODECS . includes ( cid . code ) ) {
128- throw new Error ( `Can't decode links in block with codec 0x${ cid . code . toString ( 16 ) } to form complete DAG` )
129- }
108+ if ( NO_LINKS_CODECS . includes ( cid . code ) ) {
109+ return null // skip this block, no need to look inside
110+ }
130111
131- return { cid, bytes, links }
112+ return createUnsafe ( { bytes, cid, codec } )
113+ }
132114}
0 commit comments