Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
25 changes: 23 additions & 2 deletions packages/car/src/export-strategies/subgraph-exporter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { breadthFirstWalker } from '@helia/utils'
import type { ExportStrategy } from '../index.js'
import type { CodecLoader } from '@helia/interface'
import type { GraphWalker, GraphWalkerComponents } from '@helia/utils'
import type { AbortOptions } from '@libp2p/interface'
import type { Blockstore } from 'interface-blockstore'
import type { BlockView } from 'multiformats'
import type { CID } from 'multiformats/cid'

export interface SubgraphExporterInit {
/**
* Graph traversal strategy, defaults to breadth-first
*/
walker?(components: GraphWalkerComponents): GraphWalker
}

/**
* Traverses the DAG breadth-first starting at the target CID and yields all
* encountered blocks.
Expand All @@ -14,11 +22,24 @@ import type { CID } from 'multiformats/cid'
* the helia config.
*/
export class SubgraphExporter implements ExportStrategy {
private walker?: (components: GraphWalkerComponents) => GraphWalker

constructor (init?: SubgraphExporterInit) {
this.walker = init?.walker
}

async * export (cid: CID, blockstore: Blockstore, getCodec: CodecLoader, options?: AbortOptions): AsyncGenerator<BlockView<unknown, number, number, 0 | 1>, void, undefined> {
const walker = breadthFirstWalker({
let walker: GraphWalker
const components = {
blockstore,
getCodec
})
}

if (this.walker != null) {
walker = this.walker(components)
} else {
walker = breadthFirstWalker()(components)
}

for await (const node of walker.walk(cid, options)) {
yield node.block
Expand Down
2 changes: 1 addition & 1 deletion packages/car/src/export-strategies/unixfs-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class UnixFSExporter implements ExportStrategy {
throw new NotUnixFSError('Target CID was not UnixFS - use the SubGraphExporter to export arbitrary graphs')
}

const walker = depthFirstWalker({
const walker = depthFirstWalker()({
blockstore,
getCodec
})
Expand Down
4 changes: 4 additions & 0 deletions packages/car/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ export interface ExportStrategy {
export * from './export-strategies/index.js'
export * from './traversal-strategies/index.js'

// re-export walkers from @helia/utils so consumers don't need an extra dep
export type { GraphWalker } from '@helia/utils'
export { depthFirstWalker, breadthFirstWalker, naturalOrderWalker } from '@helia/utils'

export interface ExportCarOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents>, ProviderOptions {
/**
* If true, the blockstore will not do any network requests.
Expand Down
33 changes: 23 additions & 10 deletions packages/car/src/traversal-strategies/graph-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@ import { createUnsafe } from 'multiformats/block'
import { InvalidTraversalError } from '../errors.ts'
import type { TraversalStrategy } from '../index.js'
import type { CodecLoader } from '@helia/interface'
import type { GraphWalker } from '@helia/utils'
import type { GraphWalker, GraphWalkerComponents } from '@helia/utils'
import type { AbortOptions } from '@libp2p/interface'
import type { Blockstore } from 'interface-blockstore'
import type { BlockView } from 'multiformats'
import type { CID } from 'multiformats/cid'

export interface GraphSearchOptions {
/**
* Graph traversal strategy, defaults to breadth-first
*/
walker?(components: GraphWalkerComponents): GraphWalker

/**
* How to search the graph
*
* @deprecated use `walker` instead - this will be removed in a future release
*/
strategy?: 'depth-first' | 'breadth-first'
}

Expand All @@ -27,6 +37,7 @@ export class GraphSearch implements TraversalStrategy {
private haystack?: CID
private readonly needle: CID
private readonly options?: GraphSearchOptions
private walker?: (components: GraphWalkerComponents) => GraphWalker

constructor (needle: CID, options?: GraphSearchOptions)
constructor (haystack: CID, needle: CID, options?: GraphSearchOptions)
Expand All @@ -43,22 +54,24 @@ export class GraphSearch implements TraversalStrategy {
} else {
throw new InvalidParametersError('needle must be specified')
}

this.walker = this.options?.walker
}

async * traverse (root: CID, blockstore: Blockstore, getCodec: CodecLoader, options?: AbortOptions): AsyncGenerator<BlockView<unknown, number, number, 0 | 1>, void, undefined> {
const start = this.haystack ?? root
let walker: GraphWalker
const components = {
blockstore,
getCodec
}

if (this.options?.strategy === 'breadth-first') {
walker = breadthFirstWalker({
blockstore,
getCodec
})
if (this.walker != null) {
walker = this.walker(components)
} else if (this.options?.strategy === 'breadth-first') {
walker = breadthFirstWalker()(components)
} else {
walker = depthFirstWalker({
blockstore,
getCodec
})
walker = depthFirstWalker()(components)
}

for await (const node of walker.walk(start, options)) {
Expand Down
Loading