@helia/utils 0.1.0-1561e4a → 0.1.0-329652a

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.
@@ -6,30 +6,26 @@ import filter from 'it-filter'
6
6
  import forEach from 'it-foreach'
7
7
  import { CustomProgressEvent, type ProgressOptions } from 'progress-events'
8
8
  import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
9
- import type { BlockBroker, Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions, BlockRetriever, BlockAnnouncer, BlockRetrievalOptions } from '@helia/interface/blocks'
9
+ import type { BlockBroker, Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions, BlockRetrievalOptions } from '@helia/interface/blocks'
10
10
  import type { AbortOptions, ComponentLogger, Logger, LoggerOptions, Startable } from '@libp2p/interface'
11
11
  import type { Blockstore } from 'interface-blockstore'
12
12
  import type { AwaitIterable } from 'interface-store'
13
13
  import type { CID } from 'multiformats/cid'
14
14
  import type { MultihashHasher } from 'multiformats/hashes/interface'
15
15
 
16
- export interface GetOptions extends AbortOptions {
17
- progress?(evt: Event): void
18
- }
19
-
20
- function isBlockRetriever (b: any): b is BlockRetriever {
21
- return typeof b.retrieve === 'function'
16
+ export interface NetworkedStorageInit {
17
+ root?: CID
22
18
  }
23
19
 
24
- function isBlockAnnouncer (b: any): b is BlockAnnouncer {
25
- return typeof b.announce === 'function'
20
+ export interface GetOptions extends AbortOptions {
21
+ progress?(evt: Event): void
26
22
  }
27
23
 
28
24
  export interface NetworkedStorageComponents {
29
25
  blockstore: Blockstore
30
26
  logger: ComponentLogger
31
- blockBrokers?: BlockBroker[]
32
- hashers?: Record<number, MultihashHasher>
27
+ blockBrokers: BlockBroker[]
28
+ hashers: Record<number, MultihashHasher>
33
29
  }
34
30
 
35
31
  /**
@@ -38,23 +34,23 @@ export interface NetworkedStorageComponents {
38
34
  */
39
35
  export class NetworkedStorage implements Blocks, Startable {
40
36
  private readonly child: Blockstore
41
- private readonly blockRetrievers: BlockRetriever[]
42
- private readonly blockAnnouncers: BlockAnnouncer[]
43
37
  private readonly hashers: Record<number, MultihashHasher>
44
38
  private started: boolean
45
39
  private readonly log: Logger
40
+ private readonly logger: ComponentLogger
41
+ private readonly components: NetworkedStorageComponents
46
42
 
47
43
  /**
48
44
  * Create a new BlockStorage
49
45
  */
50
- constructor (components: NetworkedStorageComponents) {
51
- this.log = components.logger.forComponent('helia:networked-storage')
46
+ constructor (components: NetworkedStorageComponents, init: NetworkedStorageInit = {}) {
47
+ this.log = components.logger.forComponent(`helia:networked-storage${init.root == null ? '' : `:${init.root}`}`)
48
+ this.logger = components.logger
49
+ this.components = components
52
50
  this.child = new TieredBlockstore([
53
51
  new IdentityBlockstore(),
54
52
  components.blockstore
55
53
  ])
56
- this.blockRetrievers = (components.blockBrokers ?? []).filter(isBlockRetriever)
57
- this.blockAnnouncers = (components.blockBrokers ?? []).filter(isBlockAnnouncer)
58
54
  this.hashers = components.hashers ?? {}
59
55
  this.started = false
60
56
  }
@@ -64,12 +60,12 @@ export class NetworkedStorage implements Blocks, Startable {
64
60
  }
65
61
 
66
62
  async start (): Promise<void> {
67
- await start(this.child, ...new Set([...this.blockRetrievers, ...this.blockAnnouncers]))
63
+ await start(this.child, ...this.components.blockBrokers)
68
64
  this.started = true
69
65
  }
70
66
 
71
67
  async stop (): Promise<void> {
72
- await stop(this.child, ...new Set([...this.blockRetrievers, ...this.blockAnnouncers]))
68
+ await stop(this.child, ...this.components.blockBrokers)
73
69
  this.started = false
74
70
  }
75
71
 
@@ -88,9 +84,9 @@ export class NetworkedStorage implements Blocks, Startable {
88
84
 
89
85
  options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:providers:notify', cid))
90
86
 
91
- this.blockAnnouncers.forEach(provider => {
92
- provider.announce(cid, block, options)
93
- })
87
+ await Promise.all(
88
+ this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
89
+ )
94
90
 
95
91
  options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:blockstore:put', cid))
96
92
 
@@ -111,11 +107,11 @@ export class NetworkedStorage implements Blocks, Startable {
111
107
  return !has
112
108
  })
113
109
 
114
- const notifyEach = forEach(missingBlocks, ({ cid, block }): void => {
110
+ const notifyEach = forEach(missingBlocks, async ({ cid, block }): Promise<void> => {
115
111
  options.onProgress?.(new CustomProgressEvent<CID>('blocks:put-many:providers:notify', cid))
116
- this.blockAnnouncers.forEach(provider => {
117
- provider.announce(cid, block, options)
118
- })
112
+ await Promise.all(
113
+ this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
114
+ )
119
115
  })
120
116
 
121
117
  options.onProgress?.(new CustomProgressEvent('blocks:put-many:blockstore:put-many'))
@@ -129,7 +125,7 @@ export class NetworkedStorage implements Blocks, Startable {
129
125
  if (options.offline !== true && !(await this.child.has(cid))) {
130
126
  // we do not have the block locally, get it from a block provider
131
127
  options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:providers:get', cid))
132
- const block = await raceBlockRetrievers(cid, this.blockRetrievers, this.hashers[cid.multihash.code], {
128
+ const block = await raceBlockRetrievers(cid, this.components.blockBrokers, this.hashers[cid.multihash.code], {
133
129
  ...options,
134
130
  log: this.log
135
131
  })
@@ -138,9 +134,9 @@ export class NetworkedStorage implements Blocks, Startable {
138
134
 
139
135
  // notify other block providers of the new block
140
136
  options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:providers:notify', cid))
141
- this.blockAnnouncers.forEach(provider => {
142
- provider.announce(cid, block, options)
143
- })
137
+ await Promise.all(
138
+ this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
139
+ )
144
140
 
145
141
  return block
146
142
  }
@@ -160,7 +156,7 @@ export class NetworkedStorage implements Blocks, Startable {
160
156
  if (options.offline !== true && !(await this.child.has(cid))) {
161
157
  // we do not have the block locally, get it from a block provider
162
158
  options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:providers:get', cid))
163
- const block = await raceBlockRetrievers(cid, this.blockRetrievers, this.hashers[cid.multihash.code], {
159
+ const block = await raceBlockRetrievers(cid, this.components.blockBrokers, this.hashers[cid.multihash.code], {
164
160
  ...options,
165
161
  log: this.log
166
162
  })
@@ -169,9 +165,9 @@ export class NetworkedStorage implements Blocks, Startable {
169
165
 
170
166
  // notify other block providers of the new block
171
167
  options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:providers:notify', cid))
172
- this.blockAnnouncers.forEach(provider => {
173
- provider.announce(cid, block, options)
174
- })
168
+ await Promise.all(
169
+ this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
170
+ )
175
171
  }
176
172
  }))
177
173
  }
@@ -205,8 +201,30 @@ export class NetworkedStorage implements Blocks, Startable {
205
201
  options.onProgress?.(new CustomProgressEvent('blocks:get-all:blockstore:get-many'))
206
202
  yield * this.child.getAll(options)
207
203
  }
204
+
205
+ async createSession (root: CID, options?: AbortOptions & ProgressOptions<GetBlockProgressEvents>): Promise<Blocks> {
206
+ const blockBrokers = await Promise.all(this.components.blockBrokers.map(async broker => {
207
+ if (broker.createSession == null) {
208
+ return broker
209
+ }
210
+
211
+ return broker.createSession(root, options)
212
+ }))
213
+
214
+ return new NetworkedStorage({
215
+ blockstore: this.child,
216
+ blockBrokers,
217
+ hashers: this.hashers,
218
+ logger: this.logger
219
+ }, {
220
+ root
221
+ })
222
+ }
208
223
  }
209
224
 
225
+ function isRetrievingBlockBroker (broker: BlockBroker): broker is Required<Pick<BlockBroker, 'retrieve'>> {
226
+ return typeof broker.retrieve === 'function'
227
+ }
210
228
  export const getCidBlockVerifierFunction = (cid: CID, hasher: MultihashHasher): Required<BlockRetrievalOptions>['validateFn'] => {
211
229
  if (hasher == null) {
212
230
  throw new CodeError(`No hasher configured for multihash code 0x${cid.multihash.code.toString(16)}, please configure one. You can look up which hash this is at https://github.com/multiformats/multicodec/blob/master/table.csv`, 'ERR_UNKNOWN_HASH_ALG')
@@ -227,38 +245,47 @@ export const getCidBlockVerifierFunction = (cid: CID, hasher: MultihashHasher):
227
245
  * Race block providers cancelling any pending requests once the block has been
228
246
  * found.
229
247
  */
230
- async function raceBlockRetrievers (cid: CID, providers: BlockRetriever[], hasher: MultihashHasher, options: AbortOptions & LoggerOptions): Promise<Uint8Array> {
248
+ async function raceBlockRetrievers (cid: CID, blockBrokers: BlockBroker[], hasher: MultihashHasher, options: AbortOptions & LoggerOptions): Promise<Uint8Array> {
231
249
  const validateFn = getCidBlockVerifierFunction(cid, hasher)
232
250
 
233
251
  const controller = new AbortController()
234
252
  const signal = anySignal([controller.signal, options.signal])
235
253
 
254
+ const retrievers: Array<Required<Pick<BlockBroker, 'retrieve'>>> = []
255
+
256
+ for (const broker of blockBrokers) {
257
+ if (isRetrievingBlockBroker(broker)) {
258
+ retrievers.push(broker)
259
+ }
260
+ }
261
+
236
262
  try {
237
263
  return await Promise.any(
238
- providers.map(async provider => {
239
- try {
240
- let blocksWereValidated = false
241
- const block = await provider.retrieve(cid, {
242
- ...options,
243
- signal,
244
- validateFn: async (block: Uint8Array): Promise<void> => {
264
+ retrievers
265
+ .map(async retriever => {
266
+ try {
267
+ let blocksWereValidated = false
268
+ const block = await retriever.retrieve(cid, {
269
+ ...options,
270
+ signal,
271
+ validateFn: async (block: Uint8Array): Promise<void> => {
272
+ await validateFn(block)
273
+ blocksWereValidated = true
274
+ }
275
+ })
276
+
277
+ if (!blocksWereValidated) {
278
+ // the blockBroker either did not throw an error when attempting to validate the block
279
+ // or did not call the validateFn at all. We should validate the block ourselves
245
280
  await validateFn(block)
246
- blocksWereValidated = true
247
281
  }
248
- })
249
282
 
250
- if (!blocksWereValidated) {
251
- // the blockBroker either did not throw an error when attempting to validate the block
252
- // or did not call the validateFn at all. We should validate the block ourselves
253
- await validateFn(block)
283
+ return block
284
+ } catch (err) {
285
+ options.log.error('could not retrieve verified block for %c', cid, err)
286
+ throw err
254
287
  }
255
-
256
- return block
257
- } catch (err) {
258
- options.log.error('could not retrieve verified block for %c', cid, err)
259
- throw err
260
- }
261
- })
288
+ })
262
289
  )
263
290
  } finally {
264
291
  signal.clear()