Skip to content

Commit 3037bd3

Browse files
committed
feat: enable to use defaultDagBuilder with custom fileBuilder or dirBuilder
1 parent 3490930 commit 3037bd3

File tree

6 files changed

+152
-47
lines changed

6 files changed

+152
-47
lines changed

packages/ipfs-unixfs-importer/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"hamt-sharding": "^3.0.6",
8181
"interface-blockstore": "^5.2.10",
8282
"interface-store": "^5.1.8",
83-
"ipfs-unixfs": "^11.0.0",
83+
"ipfs-unixfs": "^11.1.4",
8484
"it-all": "^3.0.4",
8585
"it-batch": "^3.0.4",
8686
"it-first": "^3.0.4",

packages/ipfs-unixfs-importer/src/dag-builder/dir.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
import { encode, prepare } from '@ipld/dag-pb'
22
import { UnixFS } from 'ipfs-unixfs'
33
import { persist } from '../utils/persist.js'
4-
import type { Directory, InProgressImportResult, WritableStorage } from '../index.js'
4+
import type {
5+
Directory,
6+
InProgressImportResult,
7+
WritableStorage
8+
} from '../index.js'
59
import type { Version } from 'multiformats/cid'
610

711
export interface DirBuilderOptions {
812
cidVersion: Version
913
signal?: AbortSignal
1014
}
1115

12-
export const dirBuilder = async (dir: Directory, blockstore: WritableStorage, options: DirBuilderOptions): Promise<InProgressImportResult> => {
16+
export const defaultDirBuilder = async (
17+
dir: Directory,
18+
blockstore: WritableStorage,
19+
options: DirBuilderOptions
20+
): Promise<InProgressImportResult> => {
1321
const unixfs = new UnixFS({
1422
type: 'directory',
1523
mtime: dir.mtime,

packages/ipfs-unixfs-importer/src/dag-builder/file.ts

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import parallelBatch from 'it-parallel-batch'
44
import * as rawCodec from 'multiformats/codecs/raw'
55
import { CustomProgressEvent } from 'progress-events'
66
import { persist } from '../utils/persist.js'
7-
import type { BufferImporter, File, InProgressImportResult, WritableStorage, SingleBlockImportResult, ImporterProgressEvents } from '../index.js'
7+
import type {
8+
BufferImporter,
9+
File,
10+
InProgressImportResult,
11+
WritableStorage,
12+
SingleBlockImportResult,
13+
ImporterProgressEvents
14+
} from '../index.js'
815
import type { FileLayout, Reducer } from '../layout/index.js'
916
import type { CID, Version } from 'multiformats/cid'
1017
import type { ProgressOptions, ProgressEvent } from 'progress-events'
@@ -14,11 +21,18 @@ interface BuildFileBatchOptions {
1421
blockWriteConcurrency: number
1522
}
1623

17-
async function * buildFileBatch (file: File, blockstore: WritableStorage, options: BuildFileBatchOptions): AsyncGenerator<InProgressImportResult> {
24+
async function * buildFileBatch (
25+
file: File,
26+
blockstore: WritableStorage,
27+
options: BuildFileBatchOptions
28+
): AsyncGenerator<InProgressImportResult> {
1829
let count = -1
1930
let previous: SingleBlockImportResult | undefined
2031

21-
for await (const entry of parallelBatch(options.bufferImporter(file, blockstore), options.blockWriteConcurrency)) {
32+
for await (const entry of parallelBatch(
33+
options.bufferImporter(file, blockstore),
34+
options.blockWriteConcurrency
35+
)) {
2236
count++
2337

2438
if (count === 0) {
@@ -29,7 +43,7 @@ async function * buildFileBatch (file: File, blockstore: WritableStorage, option
2943
}
3044

3145
continue
32-
} else if (count === 1 && (previous != null)) {
46+
} else if (count === 1 && previous != null) {
3347
// we have the second block of a multiple block import so yield the first
3448
yield {
3549
...previous,
@@ -63,8 +77,10 @@ export interface LayoutLeafProgress {
6377
path?: string
6478
}
6579

66-
export type ReducerProgressEvents =
67-
ProgressEvent<'unixfs:importer:progress:file:layout', LayoutLeafProgress>
80+
export type ReducerProgressEvents = ProgressEvent<
81+
'unixfs:importer:progress:file:layout',
82+
LayoutLeafProgress
83+
>
6884

6985
interface ReduceOptions extends ProgressOptions<ImporterProgressEvents> {
7086
reduceSingleLeafToSelf: boolean
@@ -76,13 +92,24 @@ function isSingleBlockImport (result: any): result is SingleBlockImportResult {
7692
return result.single === true
7793
}
7894

79-
const reduce = (file: File, blockstore: WritableStorage, options: ReduceOptions): Reducer => {
95+
const reduce = (
96+
file: File,
97+
blockstore: WritableStorage,
98+
options: ReduceOptions
99+
): Reducer => {
80100
const reducer: Reducer = async function (leaves) {
81-
if (leaves.length === 1 && isSingleBlockImport(leaves[0]) && options.reduceSingleLeafToSelf) {
101+
if (
102+
leaves.length === 1 &&
103+
isSingleBlockImport(leaves[0]) &&
104+
options.reduceSingleLeafToSelf
105+
) {
82106
const leaf = leaves[0]
83107
let node: Uint8Array | PBNode = leaf.block
84108

85-
if (isSingleBlockImport(leaf) && (file.mtime !== undefined || file.mode !== undefined)) {
109+
if (
110+
isSingleBlockImport(leaf) &&
111+
(file.mtime !== undefined || file.mode !== undefined)
112+
) {
86113
// only one leaf node which is a raw leaf - we have metadata so convert it into a
87114
// UnixFS entry otherwise we'll have nowhere to store the metadata
88115
leaf.unixfs = new UnixFS({
@@ -103,10 +130,15 @@ const reduce = (file: File, blockstore: WritableStorage, options: ReduceOptions)
103130
leaf.size = BigInt(leaf.block.length)
104131
}
105132

106-
options.onProgress?.(new CustomProgressEvent<LayoutLeafProgress>('unixfs:importer:progress:file:layout', {
107-
cid: leaf.cid,
108-
path: leaf.originalPath
109-
}))
133+
options.onProgress?.(
134+
new CustomProgressEvent<LayoutLeafProgress>(
135+
'unixfs:importer:progress:file:layout',
136+
{
137+
cid: leaf.cid,
138+
path: leaf.originalPath
139+
}
140+
)
141+
)
110142

111143
return {
112144
cid: leaf.cid,
@@ -125,12 +157,16 @@ const reduce = (file: File, blockstore: WritableStorage, options: ReduceOptions)
125157
})
126158

127159
const links: PBLink[] = leaves
128-
.filter(leaf => {
160+
.filter((leaf) => {
129161
if (leaf.cid.code === rawCodec.code && leaf.size > 0) {
130162
return true
131163
}
132164

133-
if ((leaf.unixfs != null) && (leaf.unixfs.data == null) && leaf.unixfs.fileSize() > 0n) {
165+
if (
166+
leaf.unixfs != null &&
167+
leaf.unixfs.data == null &&
168+
leaf.unixfs.fileSize() > 0n
169+
) {
134170
return true
135171
}
136172

@@ -148,7 +184,7 @@ const reduce = (file: File, blockstore: WritableStorage, options: ReduceOptions)
148184
}
149185
}
150186

151-
if ((leaf.unixfs == null) || (leaf.unixfs.data == null)) {
187+
if (leaf.unixfs == null || leaf.unixfs.data == null) {
152188
// node is an intermediate node
153189
f.addBlockSize(leaf.unixfs?.fileSize() ?? 0n)
154190
} else {
@@ -170,16 +206,24 @@ const reduce = (file: File, blockstore: WritableStorage, options: ReduceOptions)
170206
const block = encode(prepare(node))
171207
const cid = await persist(block, blockstore, options)
172208

173-
options.onProgress?.(new CustomProgressEvent<LayoutLeafProgress>('unixfs:importer:progress:file:layout', {
174-
cid,
175-
path: file.originalPath
176-
}))
209+
options.onProgress?.(
210+
new CustomProgressEvent<LayoutLeafProgress>(
211+
'unixfs:importer:progress:file:layout',
212+
{
213+
cid,
214+
path: file.originalPath
215+
}
216+
)
217+
)
177218

178219
return {
179220
cid,
180221
path: file.path,
181222
unixfs: f,
182-
size: BigInt(block.length + node.Links.reduce((acc, curr) => acc + (curr.Tsize ?? 0), 0)),
223+
size: BigInt(
224+
block.length +
225+
node.Links.reduce((acc, curr) => acc + (curr.Tsize ?? 0), 0)
226+
),
183227
originalPath: file.originalPath,
184228
block
185229
}
@@ -188,10 +232,19 @@ const reduce = (file: File, blockstore: WritableStorage, options: ReduceOptions)
188232
return reducer
189233
}
190234

191-
export interface FileBuilderOptions extends BuildFileBatchOptions, ReduceOptions {
235+
export interface FileBuilderOptions
236+
extends BuildFileBatchOptions,
237+
ReduceOptions {
192238
layout: FileLayout
193239
}
194240

195-
export const fileBuilder = async (file: File, block: WritableStorage, options: FileBuilderOptions): Promise<InProgressImportResult> => {
196-
return options.layout(buildFileBatch(file, block, options), reduce(file, block, options))
197-
}
241+
export const defaultFileBuilder = async (
242+
file: File,
243+
block: WritableStorage,
244+
options: FileBuilderOptions
245+
): Promise<InProgressImportResult> => {
246+
return options.layout(
247+
buildFileBatch(file, block, options),
248+
reduce(file, block, options)
249+
);
250+
};

packages/ipfs-unixfs-importer/src/dag-builder/index.ts

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import errCode from 'err-code'
22
import { CustomProgressEvent } from 'progress-events'
3-
import { dirBuilder, type DirBuilderOptions } from './dir.js'
4-
import { fileBuilder, type FileBuilderOptions } from './file.js'
3+
import { defaultDirBuilder, type DirBuilderOptions } from './dir.js'
4+
import { defaultFileBuilder, type FileBuilderOptions } from './file.js'
55
import type { ChunkValidator } from './validate-chunks.js'
66
import type { Chunker } from '../chunker/index.js'
7-
import type { Directory, File, FileCandidate, ImportCandidate, ImporterProgressEvents, InProgressImportResult, WritableStorage } from '../index.js'
7+
import type {
8+
Directory,
9+
File,
10+
FileCandidate,
11+
ImportCandidate,
12+
ImporterProgressEvents,
13+
InProgressImportResult,
14+
WritableStorage
15+
} from '../index.js'
816
import type { ProgressEvent, ProgressOptions } from 'progress-events'
917

1018
/**
@@ -27,8 +35,10 @@ export interface ImportReadProgress {
2735
path?: string
2836
}
2937

30-
export type DagBuilderProgressEvents =
31-
ProgressEvent<'unixfs:importer:progress:file:read', ImportReadProgress>
38+
export type DagBuilderProgressEvents = ProgressEvent<
39+
'unixfs:importer:progress:file:read',
40+
ImportReadProgress
41+
>
3242

3343
function isIterable (thing: any): thing is Iterable<any> {
3444
return Symbol.iterator in thing
@@ -38,16 +48,18 @@ function isAsyncIterable (thing: any): thing is AsyncIterable<any> {
3848
return Symbol.asyncIterator in thing
3949
}
4050

41-
function contentAsAsyncIterable (content: Uint8Array | AsyncIterable<Uint8Array> | Iterable<Uint8Array>): AsyncIterable<Uint8Array> {
51+
function contentAsAsyncIterable (
52+
content: Uint8Array | AsyncIterable<Uint8Array> | Iterable<Uint8Array>
53+
): AsyncIterable<Uint8Array> {
4254
try {
4355
if (content instanceof Uint8Array) {
4456
return (async function * () {
4557
yield content
46-
}())
58+
})()
4759
} else if (isIterable(content)) {
4860
return (async function * () {
4961
yield * content
50-
}())
62+
})()
5163
} else if (isAsyncIterable(content)) {
5264
return content
5365
}
@@ -58,16 +70,33 @@ function contentAsAsyncIterable (content: Uint8Array | AsyncIterable<Uint8Array>
5870
throw errCode(new Error('Content was invalid'), 'ERR_INVALID_CONTENT')
5971
}
6072

61-
export interface DagBuilderOptions extends FileBuilderOptions, DirBuilderOptions, ProgressOptions<ImporterProgressEvents> {
73+
export interface DagBuilderOptions
74+
extends FileBuilderOptions,
75+
DirBuilderOptions,
76+
ProgressOptions<ImporterProgressEvents> {
6277
chunker: Chunker
6378
chunkValidator: ChunkValidator
6479
wrapWithDirectory: boolean
80+
dirBuilder?(
81+
dir: Directory,
82+
blockstore: WritableStorage,
83+
options: DirBuilderOptions
84+
): Promise<InProgressImportResult>
85+
fileBuilder?(
86+
file: File,
87+
blockstore: WritableStorage,
88+
options: FileBuilderOptions
89+
): Promise<InProgressImportResult>
6590
}
6691

67-
export type ImporterSourceStream = AsyncIterable<ImportCandidate> | Iterable<ImportCandidate>
92+
export type ImporterSourceStream =
93+
| AsyncIterable<ImportCandidate>
94+
| Iterable<ImportCandidate>
6895

6996
export interface DAGBuilder {
70-
(source: ImporterSourceStream, blockstore: WritableStorage): AsyncIterable<() => Promise<InProgressImportResult>>
97+
(source: ImporterSourceStream, blockstore: WritableStorage): AsyncIterable<
98+
() => Promise<InProgressImportResult>
99+
>
71100
}
72101

73102
export function defaultDagBuilder (options: DagBuilderOptions): DAGBuilder {
@@ -79,7 +108,7 @@ export function defaultDagBuilder (options: DagBuilderOptions): DAGBuilder {
79108
originalPath = entry.path
80109
entry.path = entry.path
81110
.split('/')
82-
.filter(path => path != null && path !== '.')
111+
.filter((path) => path != null && path !== '.')
83112
.join('/')
84113
}
85114

@@ -91,22 +120,31 @@ export function defaultDagBuilder (options: DagBuilderOptions): DAGBuilder {
91120
content: (async function * () {
92121
let bytesRead = 0n
93122

94-
for await (const chunk of options.chunker(options.chunkValidator(contentAsAsyncIterable(entry.content)))) {
123+
for await (const chunk of options.chunker(
124+
options.chunkValidator(contentAsAsyncIterable(entry.content))
125+
)) {
95126
const currentChunkSize = BigInt(chunk.byteLength)
96127
bytesRead += currentChunkSize
97128

98-
options.onProgress?.(new CustomProgressEvent<ImportReadProgress>('unixfs:importer:progress:file:read', {
99-
bytesRead,
100-
chunkSize: currentChunkSize,
101-
path: entry.path
102-
}))
129+
options.onProgress?.(
130+
new CustomProgressEvent<ImportReadProgress>(
131+
'unixfs:importer:progress:file:read',
132+
{
133+
bytesRead,
134+
chunkSize: currentChunkSize,
135+
path: entry.path
136+
}
137+
)
138+
)
103139

104140
yield chunk
105141
}
106142
})(),
107143
originalPath
108144
}
109145

146+
const fileBuilder = options.fileBuilder ?? defaultFileBuilder
147+
110148
yield async () => fileBuilder(file, blockstore, options)
111149
} else if (entry.path != null) {
112150
const dir: Directory = {
@@ -116,6 +154,10 @@ export function defaultDagBuilder (options: DagBuilderOptions): DAGBuilder {
116154
originalPath
117155
}
118156

157+
const dirBuilder =
158+
options.dirBuilder ??
159+
defaultDirBuilder
160+
119161
yield async () => dirBuilder(dir, blockstore, options)
120162
} else {
121163
throw new Error('Import candidate must have content or path or both')

packages/ipfs-unixfs-importer/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ export async function * importer (source: ImportCandidateStream, blockstore: Wri
319319
const fileImportConcurrency = options.fileImportConcurrency ?? 50
320320
const blockWriteConcurrency = options.blockWriteConcurrency ?? 10
321321
const reduceSingleLeafToSelf = options.reduceSingleLeafToSelf ?? true
322+
322323

323324
const chunker = options.chunker ?? fixedSize()
324325
const chunkValidator = options.chunkValidator ?? defaultChunkValidator()

packages/ipfs-unixfs-importer/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"extends": "aegir/src/config/tsconfig.aegir.json",
33
"compilerOptions": {
4-
"outDir": "dist"
4+
"outDir": "dist",
5+
"esModuleInterop": true
56
},
67
"include": [
78
"src",

0 commit comments

Comments
 (0)