@helia/utils 2.1.0 → 2.1.1-4e5ff555

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import { Queue } from '@libp2p/utils'
2
+ import filter from 'it-filter'
2
3
  import toBuffer from 'it-to-buffer'
3
4
  import { createUnsafe } from 'multiformats/block'
4
5
  import type { CodecLoader } from '@helia/interface'
@@ -22,7 +23,7 @@ export interface GraphNode <T = unknown, C extends number = number, A extends nu
22
23
  }
23
24
 
24
25
  export interface GraphWalker {
25
- walk <T = any> (cid: CID, options?: AbortOptions): AsyncGenerator<GraphNode<T>>
26
+ walk <T = any> (cid: CID, options?: WalkOptions<T>): AsyncGenerator<GraphNode<T>>
26
27
  }
27
28
 
28
29
  export function depthFirstWalker (components: GraphWalkerComponents, init: GraphWalkerInit = {}): GraphWalker {
@@ -39,57 +40,56 @@ interface JobOptions extends AbortOptions {
39
40
  path: CID[]
40
41
  }
41
42
 
42
- class DepthFirstGraphWalker {
43
+ export interface WalkOptions<T> extends AbortOptions {
44
+ includeChild?(child: CID, parent: BlockView<T, number, number, 0 | 1>): boolean
45
+ }
46
+
47
+ abstract class AbstractGraphWalker {
43
48
  private readonly components: GraphWalkerComponents
44
49
 
45
- constructor (components: GraphWalkerComponents, init: GraphWalkerInit = {}) {
50
+ constructor (components: GraphWalkerComponents, init: GraphWalkerInit) {
46
51
  this.components = components
47
52
  }
48
53
 
49
- async * walk <T = any> (cid: CID, options: AbortOptions): AsyncGenerator<GraphNode<T>> {
50
- const queue = new Queue<GraphNode<T>, JobOptions>({
51
- concurrency: 1,
52
- sort: (a, b) => {
53
- if (a.options.depth === b.options.depth) {
54
- return 0
55
- }
54
+ async * walk <T = any> (cid: CID, options?: WalkOptions<T>): AsyncGenerator<GraphNode<T>> {
55
+ const queue = this.getQueue()
56
+ const gen = filter(queue.toGenerator(options), (node) => node != null) as AsyncGenerator<GraphNode<T>>
57
+ let finished = false
56
58
 
57
- if (a.options.depth < b.options.depth) {
58
- return 1
59
- }
60
-
61
- return -1
62
- }
63
- })
64
-
65
- const gen = queue.toGenerator()
66
-
67
- const job = async (options: JobOptions): Promise<GraphNode<T>> => {
68
- const cid = options.cid
69
- const bytes = await toBuffer(this.components.blockstore.get(cid, options))
70
- const block = createUnsafe({
59
+ const job = async (opts: JobOptions): Promise<GraphNode<T> | undefined> => {
60
+ const cid = opts.cid
61
+ const bytes = await toBuffer(this.components.blockstore.get(cid, opts))
62
+ const block = createUnsafe<T, number, number, 0 | 1>({
71
63
  cid,
72
64
  bytes,
73
65
  codec: await this.components.getCodec(cid.code)
74
66
  })
75
67
 
76
68
  for (const [, linkedCid] of block.links()) {
69
+ if (options?.includeChild?.(linkedCid, block) === false) {
70
+ continue
71
+ }
72
+
77
73
  queue.add(job, {
78
- ...options,
74
+ ...opts,
79
75
  cid: linkedCid,
80
- depth: options.depth + 1,
81
- path: [...options.path, linkedCid]
76
+ depth: opts.depth + 1,
77
+ path: [...opts.path, linkedCid]
82
78
  })
79
+ // eslint-disable-next-line no-loop-func
83
80
  .catch(err => {
84
- gen.throw(err)
85
- queue.abort()
81
+ // only throw if the generator is still yielding results, otherwise
82
+ // it can cause unhandled promise rejections
83
+ if (!finished) {
84
+ gen.throw(err)
85
+ }
86
86
  })
87
87
  }
88
88
 
89
89
  return {
90
90
  block,
91
- depth: options.depth,
92
- path: options.path
91
+ depth: opts.depth,
92
+ path: opts.path
93
93
  }
94
94
  }
95
95
 
@@ -100,23 +100,28 @@ class DepthFirstGraphWalker {
100
100
  path: [cid]
101
101
  })
102
102
  .catch(err => {
103
- gen.throw(err)
104
- queue.abort()
103
+ // only throw if the generator is still yielding results, otherwise it
104
+ // can cause unhandled promise rejections
105
+ if (!finished) {
106
+ gen.throw(err)
107
+ }
105
108
  })
106
109
 
107
- yield * gen
110
+ try {
111
+ yield * gen
112
+ } finally {
113
+ finished = true
114
+ // abort any in-progress operations
115
+ queue.abort()
116
+ }
108
117
  }
109
- }
110
118
 
111
- class BreadthFirstGraphWalker {
112
- private readonly components: GraphWalkerComponents
113
-
114
- constructor (components: GraphWalkerComponents, init: GraphWalkerInit = {}) {
115
- this.components = components
116
- }
119
+ abstract getQueue <T> (): Queue<GraphNode<T> | undefined, JobOptions>
120
+ }
117
121
 
118
- async * walk <T = any> (cid: CID, options: AbortOptions): AsyncGenerator<GraphNode<T>> {
119
- const queue = new Queue<GraphNode<T>, JobOptions>({
122
+ class DepthFirstGraphWalker extends AbstractGraphWalker {
123
+ getQueue<T>(): Queue<GraphNode<T> | undefined, JobOptions> {
124
+ return new Queue<GraphNode<T> | undefined, JobOptions>({
120
125
  concurrency: 1,
121
126
  sort: (a, b) => {
122
127
  if (a.options.depth === b.options.depth) {
@@ -124,55 +129,30 @@ class BreadthFirstGraphWalker {
124
129
  }
125
130
 
126
131
  if (a.options.depth < b.options.depth) {
127
- return -1
132
+ return 1
128
133
  }
129
134
 
130
- return 1
135
+ return -1
131
136
  }
132
137
  })
138
+ }
139
+ }
133
140
 
134
- const gen = queue.toGenerator()
135
-
136
- const job = async (options: JobOptions): Promise<GraphNode<T>> => {
137
- const cid = options.cid
138
- const bytes = await toBuffer(this.components.blockstore.get(cid, options))
139
- const block = createUnsafe({
140
- cid,
141
- bytes,
142
- codec: await this.components.getCodec(cid.code)
143
- })
141
+ class BreadthFirstGraphWalker extends AbstractGraphWalker {
142
+ getQueue<T>(): Queue<GraphNode<T> | undefined, JobOptions> {
143
+ return new Queue<GraphNode<T> | undefined, JobOptions>({
144
+ concurrency: 1,
145
+ sort: (a, b) => {
146
+ if (a.options.depth === b.options.depth) {
147
+ return 0
148
+ }
144
149
 
145
- for (const [, linkedCid] of block.links()) {
146
- queue.add(job, {
147
- ...options,
148
- cid: linkedCid,
149
- depth: options.depth + 1,
150
- path: [...options.path, linkedCid]
151
- })
152
- .catch(err => {
153
- gen.throw(err)
154
- queue.abort()
155
- })
156
- }
150
+ if (a.options.depth < b.options.depth) {
151
+ return -1
152
+ }
157
153
 
158
- return {
159
- block,
160
- depth: options.depth,
161
- path: options.path
154
+ return 1
162
155
  }
163
- }
164
-
165
- queue.add(job, {
166
- ...options,
167
- cid,
168
- depth: 0,
169
- path: [cid]
170
156
  })
171
- .catch(err => {
172
- gen.throw(err)
173
- queue.abort()
174
- })
175
-
176
- yield * gen
177
157
  }
178
158
  }
@@ -1,7 +1,7 @@
1
1
  import { Key } from 'interface-datastore'
2
2
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
3
3
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
4
- import { InvalidDatastoreVersion } from '../errors.js'
4
+ import { InvalidDatastoreVersionError } from '../errors.js'
5
5
  import type { Datastore } from 'interface-datastore'
6
6
 
7
7
  const DS_VERSION_KEY = new Key('/version')
@@ -20,6 +20,6 @@ export async function assertDatastoreVersionIsCurrent (datastore: Datastore): Pr
20
20
 
21
21
  if (version !== CURRENT_VERSION) {
22
22
  // TODO: write migrations when we break compatibility - for an example, see https://github.com/ipfs/js-ipfs-repo/tree/master/packages/ipfs-repo-migrations
23
- throw new InvalidDatastoreVersion('Invalid datastore version, a datastore migration may be required')
23
+ throw new InvalidDatastoreVersionError('Invalid datastore version, a datastore migration may be required')
24
24
  }
25
25
  }
@@ -5,6 +5,7 @@ import filter from 'it-filter'
5
5
  import forEach from 'it-foreach'
6
6
  import { CustomProgressEvent } from 'progress-events'
7
7
  import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
8
+ import { InvalidConfigurationError } from '../errors.ts'
8
9
  import { isPromise } from './is-promise.js'
9
10
  import type { HasherLoader } from '@helia/interface'
10
11
  import type { BlockBroker, Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions, BlockRetrievalOptions, CreateSessionOptions, SessionBlockstore } from '@helia/interface/blocks'
@@ -446,6 +447,10 @@ async function raceBlockRetrievers (cid: CID, blockBrokers: BlockBroker[], hashe
446
447
  }
447
448
  }
448
449
 
450
+ if (retrievers.length === 0) {
451
+ throw new InvalidConfigurationError(`No block brokers capable of retrieving blocks are configured, the CID ${cid} cannot be fetched from the network`)
452
+ }
453
+
449
454
  try {
450
455
  return await Promise.any(
451
456
  retrievers
@@ -457,6 +462,7 @@ async function raceBlockRetrievers (cid: CID, blockBrokers: BlockBroker[], hashe
457
462
  signal,
458
463
  validateFn: async (block: Uint8Array): Promise<void> => {
459
464
  await validateFn(block)
465
+ options.signal?.throwIfAborted()
460
466
  blocksWereValidated = true
461
467
  }
462
468
  })
@@ -465,6 +471,7 @@ async function raceBlockRetrievers (cid: CID, blockBrokers: BlockBroker[], hashe
465
471
  // the blockBroker either did not throw an error when attempting to validate the block
466
472
  // or did not call the validateFn at all. We should validate the block ourselves
467
473
  await validateFn(block)
474
+ options.signal?.throwIfAborted()
468
475
  }
469
476
 
470
477
  return block
@@ -1,18 +0,0 @@
1
- {
2
- "AbstractSession": "https://ipfs.github.io/helia/classes/_helia_utils.AbstractSession.html",
3
- "Helia": "https://ipfs.github.io/helia/classes/_helia_utils.Helia.html",
4
- ".:Helia": "https://ipfs.github.io/helia/classes/_helia_utils.Helia.html",
5
- "AbstractCreateSessionOptions": "https://ipfs.github.io/helia/interfaces/_helia_utils.AbstractCreateSessionOptions.html",
6
- "AbstractSessionComponents": "https://ipfs.github.io/helia/interfaces/_helia_utils.AbstractSessionComponents.html",
7
- "BlockStorage": "https://ipfs.github.io/helia/interfaces/_helia_utils.BlockStorage.html",
8
- "BlockStorageInit": "https://ipfs.github.io/helia/interfaces/_helia_utils.BlockStorageInit.html",
9
- "BlockstoreSessionEvents": "https://ipfs.github.io/helia/interfaces/_helia_utils.BlockstoreSessionEvents.html",
10
- "GraphNode": "https://ipfs.github.io/helia/interfaces/_helia_utils.GraphNode.html",
11
- "GraphWalker": "https://ipfs.github.io/helia/interfaces/_helia_utils.GraphWalker.html",
12
- "GraphWalkerComponents": "https://ipfs.github.io/helia/interfaces/_helia_utils.GraphWalkerComponents.html",
13
- "GraphWalkerInit": "https://ipfs.github.io/helia/interfaces/_helia_utils.GraphWalkerInit.html",
14
- "HeliaInit": "https://ipfs.github.io/helia/interfaces/_helia_utils.HeliaInit.html",
15
- ".:HeliaInit": "https://ipfs.github.io/helia/interfaces/helia.HeliaInit.html",
16
- "breadthFirstWalker": "https://ipfs.github.io/helia/functions/_helia_utils.breadthFirstWalker.html",
17
- "depthFirstWalker": "https://ipfs.github.io/helia/functions/_helia_utils.depthFirstWalker.html"
18
- }