Skip to content

Commit d7e24a6

Browse files
committed
fix: add option to export non-extended unixfs
Adds overrides for non-extended exports
1 parent 5a916ab commit d7e24a6

File tree

6 files changed

+114
-55
lines changed

6 files changed

+114
-55
lines changed

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

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ import type { PBNode } from '@ipld/dag-pb'
5757
import type { Bucket } from 'hamt-sharding'
5858
import type { Blockstore } from 'interface-blockstore'
5959
import type { UnixFS } from 'ipfs-unixfs'
60-
import type { AbortOptions } from 'it-pushable'
6160
import type { ProgressOptions, ProgressEvent } from 'progress-events'
6261

6362
export * from './errors.js'
@@ -314,6 +313,23 @@ export interface IdentityNode extends Exportable<Uint8Array> {
314313
*/
315314
export type UnixFSEntry = UnixFSFile | UnixFSDirectory | ObjectNode | RawNode | IdentityNode
316315

316+
export interface UnixFSBasicEntry {
317+
/**
318+
* The name of the entry
319+
*/
320+
name: string
321+
322+
/**
323+
* The path of the entry within the DAG in which it was encountered
324+
*/
325+
path: string
326+
327+
/**
328+
* The CID of the entry
329+
*/
330+
cid: CID
331+
}
332+
317333
export interface NextResult {
318334
cid: CID
319335
name: string
@@ -327,39 +343,15 @@ export interface ResolveResult {
327343
}
328344

329345
export interface Resolve { (cid: CID, name: string, path: string, toResolve: string[], depth: number, blockstore: ReadableStorage, options: ExporterOptions): Promise<ResolveResult> }
330-
export interface Resolver { (cid: CID, name: string, path: string, toResolve: string[], resolve: Resolve, depth: number, blockstore: ReadableStorage, options: ExporterOptions): Promise<ResolveResult> }
346+
export interface Resolver { (cid: CID, name: string, path: string, toResolve: string[], resolve: Resolve, depth: number, blockstore: ReadableStorage, options: ExporterOptions | BasicExporterOptions): Promise<ResolveResult> }
331347

332348
export type UnixfsV1FileContent = AsyncIterable<Uint8Array> | Iterable<Uint8Array>
333349
export type UnixfsV1DirectoryContent = AsyncIterable<UnixFSEntry> | Iterable<UnixFSEntry>
334350
export type UnixfsV1Content = UnixfsV1FileContent | UnixfsV1DirectoryContent
335351

336-
export interface UnixfsV1BasicContent {
337-
/**
338-
* The name of the entry
339-
*/
340-
name: string
341-
342-
/**
343-
* The path of the entry within the DAG in which it was encountered
344-
*/
345-
path: string
346-
347-
/**
348-
* The CID of the entry
349-
*/
350-
cid: CID
351-
352-
/**
353-
* Resolve the root node of the entry to parse the UnixFS metadata contained
354-
* there. The metadata will contain what kind of node it is (e.g. file,
355-
* directory, etc), the file size, and more.
356-
*/
357-
resolve(options?: AbortOptions): Promise<UnixFSEntry>
358-
}
359-
360352
export interface UnixFsV1ContentResolver {
361353
(options: ExporterOptions): UnixfsV1Content
362-
(options: BasicExporterOptions): UnixfsV1BasicContent
354+
(options: BasicExporterOptions): UnixFSBasicEntry
363355
}
364356

365357
export interface UnixfsV1Resolver {
@@ -435,6 +427,8 @@ const cidAndRest = (path: string | Uint8Array | CID): { cid: CID, toResolve: str
435427
* // entries contains 4x `entry` objects
436428
* ```
437429
*/
430+
export function walkPath (path: string | CID, blockstore: ReadableStorage, options?: ExporterOptions): AsyncGenerator<UnixFSEntry, void, any>
431+
export function walkPath (path: string | CID, blockstore: ReadableStorage, options: BasicExporterOptions): AsyncGenerator<UnixFSBasicEntry, void, any>
438432
export async function * walkPath (path: string | CID, blockstore: ReadableStorage, options: ExporterOptions = {}): AsyncGenerator<UnixFSEntry, void, any> {
439433
let {
440434
cid,
@@ -491,6 +485,8 @@ export async function * walkPath (path: string | CID, blockstore: ReadableStorag
491485
* }
492486
* ```
493487
*/
488+
export async function exporter (path: string | CID, blockstore: ReadableStorage, options?: ExporterOptions): Promise<UnixFSEntry>
489+
export async function exporter (path: string | CID, blockstore: ReadableStorage, options: BasicExporterOptions): Promise<UnixFSBasicEntry>
494490
export async function exporter (path: string | CID, blockstore: ReadableStorage, options: ExporterOptions = {}): Promise<UnixFSEntry> {
495491
const result = await last(walkPath(path, blockstore, options))
496492

@@ -519,6 +515,8 @@ export async function exporter (path: string | CID, blockstore: ReadableStorage,
519515
* // entries contains all children of the `Qmfoo/foo/bar` directory and it's children
520516
* ```
521517
*/
518+
export function recursive (path: string | CID, blockstore: ReadableStorage, options?: ExporterOptions): AsyncGenerator<UnixFSEntry, void, any>
519+
export function recursive (path: string | CID, blockstore: ReadableStorage, options: BasicExporterOptions): AsyncGenerator<UnixFSBasicEntry, void, any>
522520
export async function * recursive (path: string | CID, blockstore: ReadableStorage, options: ExporterOptions = {}): AsyncGenerator<UnixFSEntry, void, any> {
523521
const node = await exporter(path, blockstore, options)
524522

packages/ipfs-unixfs-exporter/src/resolvers/unixfs-v1/content/directory.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import parallel from 'it-parallel'
44
import { pipe } from 'it-pipe'
55
import { CustomProgressEvent } from 'progress-events'
66
import { isBasicExporterOptions } from '../../../utils/is-basic-exporter-options.ts'
7-
import type { BasicExporterOptions, ExporterOptions, ExportWalk, UnixFSEntry, UnixfsV1BasicContent, UnixfsV1Resolver } from '../../../index.js'
7+
import type { BasicExporterOptions, ExporterOptions, ExportWalk, UnixFSBasicEntry, UnixfsV1Resolver } from '../../../index.js'
88

99
const directoryContent: UnixfsV1Resolver = (cid, node, unixfs, path, resolve, depth, blockstore) => {
1010
async function * yieldDirectoryContent (options: ExporterOptions | BasicExporterOptions = {}): any {
@@ -23,23 +23,18 @@ const directoryContent: UnixfsV1Resolver = (cid, node, unixfs, path, resolve, de
2323
const linkName = link.Name ?? ''
2424
const linkPath = `${path}/${linkName}`
2525

26-
const load = async (options = {}): Promise<UnixFSEntry> => {
27-
const result = await resolve(link.Hash, linkName, linkPath, [], depth + 1, blockstore, options)
28-
return result.entry
29-
}
30-
3126
if (isBasicExporterOptions(options)) {
32-
const basic: UnixfsV1BasicContent = {
27+
const basic: UnixFSBasicEntry = {
3328
cid: link.Hash,
3429
name: linkName,
35-
path: linkPath,
36-
resolve: load
30+
path: linkPath
3731
}
3832

3933
return basic
4034
}
4135

42-
return load(options)
36+
const result = await resolve(link.Hash, linkName, linkPath, [], depth + 1, blockstore, options)
37+
return result.entry
4338
}
4439
}),
4540
source => parallel(source, {

packages/ipfs-unixfs-exporter/src/resolvers/unixfs-v1/content/hamt-sharded-directory.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { pipe } from 'it-pipe'
66
import { CustomProgressEvent } from 'progress-events'
77
import { NotUnixFSError } from '../../../errors.js'
88
import { isBasicExporterOptions } from '../../../utils/is-basic-exporter-options.ts'
9-
import type { ExporterOptions, Resolve, UnixfsV1DirectoryContent, UnixfsV1Resolver, ReadableStorage, ExportWalk, BasicExporterOptions, UnixFSEntry } from '../../../index.js'
9+
import type { ExporterOptions, Resolve, UnixfsV1DirectoryContent, UnixfsV1Resolver, ReadableStorage, ExportWalk, BasicExporterOptions, UnixFSBasicEntry } from '../../../index.js'
1010
import type { PBNode } from '@ipld/dag-pb'
1111

1212
const hamtShardedDirectoryContent: UnixfsV1Resolver = (cid, node, unixfs, path, resolve, depth, blockstore) => {
@@ -49,25 +49,26 @@ async function * listDirectory (node: PBNode, path: string, resolve: Resolve, de
4949

5050
if (name != null && name !== '') {
5151
const linkPath = `${path}/${name}`
52-
const load = async (options = {}): Promise<UnixFSEntry> => {
53-
const result = await resolve(link.Hash, name, linkPath, [], depth + 1, blockstore, options)
54-
return result.entry
55-
}
5652

5753
if (isBasicExporterOptions(options)) {
54+
const basic: UnixFSBasicEntry = {
55+
cid: link.Hash,
56+
name,
57+
path: linkPath
58+
}
59+
5860
return {
59-
entries: [{
60-
cid: link.Hash,
61-
name,
62-
path: linkPath,
63-
resolve: load
64-
}]
61+
entries: [
62+
basic
63+
]
6564
}
6665
}
6766

67+
const result = await resolve(link.Hash, name, linkPath, [], depth + 1, blockstore, options)
68+
6869
return {
6970
entries: [
70-
await load()
71+
result.entry
7172
].filter(Boolean)
7273
}
7374
} else {

packages/ipfs-unixfs-exporter/src/resolvers/unixfs-v1/index.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { decode } from '@ipld/dag-pb'
22
import { UnixFS } from 'ipfs-unixfs'
33
import { NotFoundError, NotUnixFSError } from '../../errors.js'
44
import findShardCid from '../../utils/find-cid-in-shard.js'
5+
import { isBasicExporterOptions } from '../../utils/is-basic-exporter-options.ts'
56
import contentDirectory from './content/directory.js'
67
import contentFile from './content/file.js'
78
import contentHamtShardedDirectory from './content/hamt-sharded-directory.js'
8-
import type { Resolver, UnixfsV1Resolver } from '../../index.js'
9+
import type { Resolver, UnixFSBasicEntry, UnixfsV1Resolver } from '../../index.js'
910
import type { PBNode } from '@ipld/dag-pb'
1011
import type { CID } from 'multiformats/cid'
1112

@@ -30,6 +31,18 @@ const contentExporters: Record<string, UnixfsV1Resolver> = {
3031

3132
// @ts-expect-error types are wrong
3233
const unixFsResolver: Resolver = async (cid, name, path, toResolve, resolve, depth, blockstore, options) => {
34+
if (isBasicExporterOptions(options)) {
35+
const basic: UnixFSBasicEntry = {
36+
cid,
37+
name,
38+
path
39+
}
40+
41+
return {
42+
entry: basic
43+
}
44+
}
45+
3346
const block = await blockstore.get(cid, options)
3447
const node = decode(block)
3548
let unixfs

packages/ipfs-unixfs-exporter/test/exporter-sharded.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,10 +409,9 @@ describe('exporter sharded', function () {
409409
expect(dirFile).to.have.property('name')
410410
expect(dirFile).to.have.property('path')
411411
expect(dirFile).to.have.property('cid')
412-
expect(dirFile).to.have.property('resolve')
413412

414413
// should fail because we have deleted this block
415-
await expect(dirFile.resolve()).to.eventually.be.rejected()
414+
await expect(exporter(dirFile.cid, block)).to.eventually.be.rejected()
416415
}
417416
})
418417
})

packages/ipfs-unixfs-exporter/test/exporter.spec.ts

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,7 @@ describe('exporter', () => {
16061606
expect(actualInvocations).to.deep.equal(expectedInvocations)
16071607
})
16081608

1609-
it('exports basic directory', async () => {
1609+
it('exports basic directory contents', async () => {
16101610
const files: Record<string, { content: Uint8Array, cid?: CID }> = {}
16111611

16121612
for (let i = 0; i < 10; i++) {
@@ -1649,10 +1649,63 @@ describe('exporter', () => {
16491649
expect(dirFile).to.have.property('name')
16501650
expect(dirFile).to.have.property('path')
16511651
expect(dirFile).to.have.property('cid')
1652-
expect(dirFile).to.have.property('resolve')
16531652

16541653
// should fail because we have deleted this block
1655-
await expect(dirFile.resolve()).to.eventually.be.rejected()
1654+
await expect(exporter(dirFile.cid, block)).to.eventually.be.rejected()
1655+
}
1656+
})
1657+
1658+
it('exports basic file', async () => {
1659+
const imported = await all(importer([{
1660+
content: uint8ArrayFromString('hello')
1661+
}], block, {
1662+
rawLeaves: false
1663+
}))
1664+
1665+
const regularFile = await exporter(imported[0].cid, block)
1666+
expect(regularFile).to.have.property('unixfs')
1667+
1668+
const basicFile = await exporter(imported[0].cid, block, {
1669+
extended: false
1670+
})
1671+
1672+
expect(basicFile).to.have.property('name')
1673+
expect(basicFile).to.have.property('path')
1674+
expect(basicFile).to.have.property('cid')
1675+
expect(basicFile).to.not.have.property('unixfs')
1676+
})
1677+
1678+
it('exports basic directory', async () => {
1679+
const files: Record<string, { content: Uint8Array, cid?: CID }> = {}
1680+
1681+
for (let i = 0; i < 10; i++) {
1682+
files[`file-${Math.random()}.txt`] = {
1683+
content: uint8ArrayConcat(await all(randomBytes(100)))
1684+
}
1685+
}
1686+
1687+
const imported = await all(importer(Object.keys(files).map(path => ({
1688+
path,
1689+
content: asAsyncIterable(files[path].content)
1690+
})), block, {
1691+
wrapWithDirectory: true,
1692+
rawLeaves: false
1693+
}))
1694+
1695+
const dirCid = imported.pop()?.cid
1696+
1697+
if (dirCid == null) {
1698+
throw new Error('No directory CID found')
16561699
}
1700+
1701+
const basicDir = await exporter(dirCid, block, {
1702+
extended: false
1703+
})
1704+
1705+
expect(basicDir).to.have.property('name')
1706+
expect(basicDir).to.have.property('path')
1707+
expect(basicDir).to.have.property('cid')
1708+
expect(basicDir).to.not.have.property('unixfs')
1709+
expect(basicDir).to.not.have.property('content')
16571710
})
16581711
})

0 commit comments

Comments
 (0)