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,12 +58,11 @@ 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 )
61
+ const load = makeLoader ( repo , writer , {
62
+ signal : options . signal ,
63
+ timeout : options . timeout
64
+ } , codecs )
65
+ await walk ( { cid, load } )
62
66
} catch ( /** @type {any } */ e ) {
63
67
err = e
64
68
} finally {
@@ -81,52 +85,30 @@ export function createExport ({ repo, preload, codecs }) {
81
85
}
82
86
83
87
/**
88
+ * @template T
84
89
* @param {IPFSRepo } repo
85
- * @param {AbortOptions } options
86
- * @param {CID } cid
87
90
* @param {BlockWriter } writer
91
+ * @param {AbortOptions } options
88
92
* @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> }
91
94
*/
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 )
97
98
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
+ }
99
102
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 )
103
104
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 } )
109
107
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
+ }
130
111
131
- return { cid, bytes, links }
112
+ return createUnsafe ( { bytes, cid, codec } )
113
+ }
132
114
}
0 commit comments