@helia/utils 2.1.1 → 2.2.0-f35ecd1c

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.
package/src/errors.ts CHANGED
@@ -8,7 +8,12 @@ export class BlockPinnedError extends Error {
8
8
  name = 'BlockPinnedError'
9
9
  }
10
10
 
11
- export class InvalidDatastoreVersion extends Error {
12
- static name = 'InvalidDatastoreVersion'
13
- name = 'InvalidDatastoreVersion'
11
+ export class InvalidDatastoreVersionError extends Error {
12
+ static name = 'InvalidDatastoreVersionError'
13
+ name = 'InvalidDatastoreVersionError'
14
+ }
15
+
16
+ export class InvalidConfigurationError extends Error {
17
+ static name = 'InvalidConfigurationError'
18
+ name = 'InvalidConfigurationError'
14
19
  }
@@ -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
  }
package/src/index.ts CHANGED
@@ -306,7 +306,7 @@ export class Helia<T extends Libp2p> implements HeliaInterface<T> {
306
306
 
307
307
  options.onProgress?.(new CustomProgressEvent<CID>('helia:gc:deleted', cid))
308
308
  } catch (err: any) {
309
- helia.log.error('Error during gc', err)
309
+ helia.log.error('error during gc - %e', err)
310
310
  options.onProgress?.(new CustomProgressEvent<Error>('helia:gc:error', err))
311
311
  }
312
312
  }
package/src/routing.ts CHANGED
@@ -104,7 +104,7 @@ export class Routing implements RoutingInterface, Startable {
104
104
 
105
105
  return provider
106
106
  } catch (err) {
107
- this.log.error('could not load multiaddrs for peer %p', peer.id, err)
107
+ this.log.error('could not load multiaddrs for peer %p - %e', peer.id, err)
108
108
  return null
109
109
  }
110
110
  }, {
@@ -112,7 +112,7 @@ export class Routing implements RoutingInterface, Startable {
112
112
  signal: options.signal
113
113
  })
114
114
  .catch(err => {
115
- this.log.error('could not load multiaddrs for peer %p', peer.id, err)
115
+ this.log.error('could not load multiaddrs for peer %p - %e', peer.id, err)
116
116
  })
117
117
  }
118
118
 
@@ -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
@@ -471,7 +476,7 @@ async function raceBlockRetrievers (cid: CID, blockBrokers: BlockBroker[], hashe
471
476
 
472
477
  return block
473
478
  } catch (err) {
474
- options.log.error('could not retrieve verified block for %c', cid, err)
479
+ options.log.error('could not retrieve verified block for %c - %e', cid, err)
475
480
  throw err
476
481
  }
477
482
  })
@@ -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
- }