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