Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/ipfs-unixfs-importer/src/dag-builder/dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ export interface DirBuilderOptions {
signal?: AbortSignal
}

export const dirBuilder = async (dir: Directory, blockstore: WritableStorage, options: DirBuilderOptions): Promise<InProgressImportResult> => {
export interface DirBuilder {
(dir: Directory, blockstore: WritableStorage, options: DirBuilderOptions): Promise<InProgressImportResult>
}

export const defaultDirBuilder: DirBuilder = async (dir: Directory, blockstore: WritableStorage, options: DirBuilderOptions): Promise<InProgressImportResult> => {
const unixfs = new UnixFS({
type: 'directory',
mtime: dir.mtime,
Expand Down
15 changes: 11 additions & 4 deletions packages/ipfs-unixfs-importer/src/dag-builder/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async function * buildFileBatch (file: File, blockstore: WritableStorage, option
}

continue
} else if (count === 1 && (previous != null)) {
} else if (count === 1 && previous != null) {
// we have the second block of a multiple block import so yield the first
yield {
...previous,
Expand Down Expand Up @@ -131,7 +131,7 @@ const reduce = (file: File, blockstore: WritableStorage, options: ReduceOptions)
return true
}

if ((leaf.unixfs != null) && (leaf.unixfs.data == null) && leaf.unixfs.fileSize() > 0n) {
if (leaf.unixfs != null && leaf.unixfs.data == null && leaf.unixfs.fileSize() > 0n) {
return true
}

Expand Down Expand Up @@ -189,10 +189,17 @@ const reduce = (file: File, blockstore: WritableStorage, options: ReduceOptions)
return reducer
}

export interface FileBuilder {
(file: File, blockstore: WritableStorage, options: FileBuilderOptions): Promise<InProgressImportResult>
}

export interface FileBuilderOptions extends BuildFileBatchOptions, ReduceOptions {
layout: FileLayout
}

export const fileBuilder = async (file: File, block: WritableStorage, options: FileBuilderOptions): Promise<InProgressImportResult> => {
return options.layout(buildFileBatch(file, block, options), reduce(file, block, options))
export const defaultFileBuilder: FileBuilder = async (file: File, block: WritableStorage, options: FileBuilderOptions): Promise<InProgressImportResult> => {
return options.layout(
buildFileBatch(file, block, options),
reduce(file, block, options)
)
}
22 changes: 15 additions & 7 deletions packages/ipfs-unixfs-importer/src/dag-builder/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { CustomProgressEvent } from 'progress-events'
import { InvalidContentError } from '../errors.js'
import { dirBuilder } from './dir.js'
import { fileBuilder } from './file.js'
import type { DirBuilderOptions } from './dir.js'
import type { FileBuilderOptions } from './file.js'
import { defaultDirBuilder } from './dir.js'
import { defaultFileBuilder } from './file.js'
import type { DirBuilder, DirBuilderOptions } from './dir.js'
import type { FileBuilder, FileBuilderOptions } from './file.js'
import type { ChunkValidator } from './validate-chunks.js'
import type { Chunker } from '../chunker/index.js'
import type { Directory, File, FileCandidate, ImportCandidate, ImporterProgressEvents, InProgressImportResult, WritableStorage } from '../index.js'
Expand Down Expand Up @@ -45,11 +45,11 @@
if (content instanceof Uint8Array) {
return (async function * () {
yield content
}())
})()
} else if (isIterable(content)) {
return (async function * () {
yield * content
}())
})()

Check warning on line 52 in packages/ipfs-unixfs-importer/src/dag-builder/index.ts

View check run for this annotation

Codecov / codecov/patch

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

Added line #L52 was not covered by tests
} else if (isAsyncIterable(content)) {
return content
}
Expand All @@ -64,9 +64,13 @@
chunker: Chunker
chunkValidator: ChunkValidator
wrapWithDirectory: boolean
dirBuilder?: DirBuilder
fileBuilder?: FileBuilder
}

export type ImporterSourceStream = AsyncIterable<ImportCandidate> | Iterable<ImportCandidate>
export type ImporterSourceStream =
| AsyncIterable<ImportCandidate>
| Iterable<ImportCandidate>

export interface DAGBuilder {
(source: ImporterSourceStream, blockstore: WritableStorage): AsyncIterable<() => Promise<InProgressImportResult>>
Expand Down Expand Up @@ -109,6 +113,8 @@
originalPath
}

const fileBuilder = options.fileBuilder ?? defaultFileBuilder

yield async () => fileBuilder(file, blockstore, options)
} else if (entry.path != null) {
const dir: Directory = {
Expand All @@ -118,6 +124,8 @@
originalPath
}

const dirBuilder = options.dirBuilder ?? defaultDirBuilder

yield async () => dirBuilder(dir, blockstore, options)
} else {
throw new Error('Import candidate must have content or path or both')
Expand Down
21 changes: 19 additions & 2 deletions packages/ipfs-unixfs-importer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ import { balanced } from './layout/index.js'
import { defaultTreeBuilder } from './tree-builder.js'
import type { Chunker } from './chunker/index.js'
import type { BufferImportProgressEvents } from './dag-builder/buffer-importer.js'
import type { ReducerProgressEvents } from './dag-builder/file.js'
import type { DirBuilder } from './dag-builder/dir.js'
import type { FileBuilder, ReducerProgressEvents } from './dag-builder/file.js'
import type { DAGBuilder, DagBuilderProgressEvents } from './dag-builder/index.js'
import type { ChunkValidator } from './dag-builder/validate-chunks.js'
import type { FileLayout } from './layout/index.js'
Expand Down Expand Up @@ -276,6 +277,20 @@ export interface ImporterOptions extends ProgressOptions<ImporterProgressEvents>
* `Error`
*/
chunkValidator?: ChunkValidator

/**
* This option can be used to override how a directory IPLD node is built.
*
* This function takes a `Directory` object and returns a `Promise` that resolves to an `InProgressImportResult`.
*/
dirBuilder?: DirBuilder

/**
* This option can be used to override how a file IPLD node is built.
*
* This function takes a `File` object and returns a `Promise` that resolves to an `InProgressImportResult`.
*/
fileBuilder?: FileBuilder
}

export type ImportCandidateStream = AsyncIterable<FileCandidate | DirectoryCandidate> | Iterable<FileCandidate | DirectoryCandidate>
Expand Down Expand Up @@ -342,7 +357,9 @@ export async function * importer (source: ImportCandidateStream, blockstore: Wri
blockWriteConcurrency,
reduceSingleLeafToSelf,
cidVersion,
onProgress: options.onProgress
onProgress: options.onProgress,
dirBuilder: options.dirBuilder,
fileBuilder: options.fileBuilder
})
const buildTree: TreeBuilder = options.treeBuilder ?? defaultTreeBuilder({
wrapWithDirectory,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect } from 'aegir/chai'
import { MemoryBlockstore } from 'blockstore-core'
import { defaultDirBuilder } from '../src/dag-builder/dir.js'
import { defaultFileBuilder } from '../src/dag-builder/file.js'
import { importer } from '../src/index.js'
import type { DirBuilder } from '../src/dag-builder/dir.js'
import type { FileBuilder } from '../src/dag-builder/file.js'

describe('CustomParamsDagBuilder', () => {
it('should build a dag with custom dir builder', async () => {
const counter = { dirCounter: 0, fileCounter: 0 }
const customDirBuilder: DirBuilder = async (...args) => {
counter.dirCounter++
return defaultDirBuilder(...args)
}

const customFileBuilder: FileBuilder = async (...args) => {
counter.fileCounter++
return defaultFileBuilder(...args)
}

const blockstore = new MemoryBlockstore()
const files = []
for await (const file of importer([{
path: './src/file.txt',
content: new Uint8Array(
'hello world'.split('').map((char) => char.charCodeAt(0))
)
}, {
path: './src'
}], blockstore, {
dirBuilder: customDirBuilder,
fileBuilder: customFileBuilder
})) {
files.push(file)
}

expect(counter.dirCounter).to.equal(1)
})
})