@helia/utils 2.1.0 → 2.1.1-760ed27f

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'
@@ -39,35 +40,22 @@ interface JobOptions extends AbortOptions {
39
40
  path: CID[]
40
41
  }
41
42
 
42
- class DepthFirstGraphWalker {
43
+ abstract class AbstractGraphWalker {
43
44
  private readonly components: GraphWalkerComponents
44
45
 
45
- constructor (components: GraphWalkerComponents, init: GraphWalkerInit = {}) {
46
+ constructor (components: GraphWalkerComponents, init: GraphWalkerInit) {
46
47
  this.components = components
47
48
  }
48
49
 
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
- }
56
-
57
- if (a.options.depth < b.options.depth) {
58
- return 1
59
- }
50
+ async * walk <T = any> (cid: CID, options?: AbortOptions): AsyncGenerator<GraphNode<T>> {
51
+ const queue = this.getQueue()
52
+ const gen = filter(queue.toGenerator(options), (node) => node != null) as AsyncGenerator<GraphNode<T>>
53
+ let finished = false
60
54
 
61
- return -1
62
- }
63
- })
64
-
65
- const gen = queue.toGenerator()
66
-
67
- const job = async (options: JobOptions): Promise<GraphNode<T>> => {
55
+ const job = async (options: JobOptions): Promise<GraphNode<T> | undefined> => {
68
56
  const cid = options.cid
69
57
  const bytes = await toBuffer(this.components.blockstore.get(cid, options))
70
- const block = createUnsafe({
58
+ const block = createUnsafe<T, number, number, 0 | 1>({
71
59
  cid,
72
60
  bytes,
73
61
  codec: await this.components.getCodec(cid.code)
@@ -80,9 +68,13 @@ class DepthFirstGraphWalker {
80
68
  depth: options.depth + 1,
81
69
  path: [...options.path, linkedCid]
82
70
  })
71
+ // eslint-disable-next-line no-loop-func
83
72
  .catch(err => {
84
- gen.throw(err)
85
- queue.abort()
73
+ // only throw if the generator is still yielding results, otherwise
74
+ // it can cause unhandled promise rejections
75
+ if (!finished) {
76
+ gen.throw(err)
77
+ }
86
78
  })
87
79
  }
88
80
 
@@ -100,23 +92,28 @@ class DepthFirstGraphWalker {
100
92
  path: [cid]
101
93
  })
102
94
  .catch(err => {
103
- gen.throw(err)
104
- queue.abort()
95
+ // only throw if the generator is still yielding results, otherwise it
96
+ // can cause unhandled promise rejections
97
+ if (!finished) {
98
+ gen.throw(err)
99
+ }
105
100
  })
106
101
 
107
- yield * gen
102
+ try {
103
+ yield * gen
104
+ } finally {
105
+ finished = true
106
+ // abort any in-progress operations
107
+ queue.abort()
108
+ }
108
109
  }
109
- }
110
110
 
111
- class BreadthFirstGraphWalker {
112
- private readonly components: GraphWalkerComponents
113
-
114
- constructor (components: GraphWalkerComponents, init: GraphWalkerInit = {}) {
115
- this.components = components
116
- }
111
+ abstract getQueue <T> (): Queue<GraphNode<T> | undefined, JobOptions>
112
+ }
117
113
 
118
- async * walk <T = any> (cid: CID, options: AbortOptions): AsyncGenerator<GraphNode<T>> {
119
- const queue = new Queue<GraphNode<T>, JobOptions>({
114
+ class DepthFirstGraphWalker extends AbstractGraphWalker {
115
+ getQueue<T>(): Queue<GraphNode<T> | undefined, JobOptions> {
116
+ return new Queue<GraphNode<T> | undefined, JobOptions>({
120
117
  concurrency: 1,
121
118
  sort: (a, b) => {
122
119
  if (a.options.depth === b.options.depth) {
@@ -124,55 +121,30 @@ class BreadthFirstGraphWalker {
124
121
  }
125
122
 
126
123
  if (a.options.depth < b.options.depth) {
127
- return -1
124
+ return 1
128
125
  }
129
126
 
130
- return 1
127
+ return -1
131
128
  }
132
129
  })
130
+ }
131
+ }
133
132
 
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
- })
133
+ class BreadthFirstGraphWalker extends AbstractGraphWalker {
134
+ getQueue<T>(): Queue<GraphNode<T> | undefined, JobOptions> {
135
+ return new Queue<GraphNode<T> | undefined, JobOptions>({
136
+ concurrency: 1,
137
+ sort: (a, b) => {
138
+ if (a.options.depth === b.options.depth) {
139
+ return 0
140
+ }
144
141
 
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
- }
142
+ if (a.options.depth < b.options.depth) {
143
+ return -1
144
+ }
157
145
 
158
- return {
159
- block,
160
- depth: options.depth,
161
- path: options.path
146
+ return 1
162
147
  }
163
- }
164
-
165
- queue.add(job, {
166
- ...options,
167
- cid,
168
- depth: 0,
169
- path: [cid]
170
148
  })
171
- .catch(err => {
172
- gen.throw(err)
173
- queue.abort()
174
- })
175
-
176
- yield * gen
177
149
  }
178
150
  }
@@ -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
- }