@helia/utils 0.0.2 → 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.
package/src/routing.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { CodeError, start, stop } from '@libp2p/interface'
2
- import { PeerSet } from '@libp2p/peer-collections'
3
2
  import merge from 'it-merge'
4
3
  import type { Routing as RoutingInterface, Provider, RoutingOptions } from '@helia/interface'
5
4
  import type { AbortOptions, ComponentLogger, Logger, PeerId, PeerInfo, Startable } from '@libp2p/interface'
@@ -38,8 +37,6 @@ export class Routing implements RoutingInterface, Startable {
38
37
  throw new CodeError('No content routers available', 'ERR_NO_ROUTERS_AVAILABLE')
39
38
  }
40
39
 
41
- const seen = new PeerSet()
42
-
43
40
  for await (const peer of merge(
44
41
  ...supports(this.routers, 'findProviders')
45
42
  .map(router => router.findProviders(key, options))
@@ -50,13 +47,6 @@ export class Routing implements RoutingInterface, Startable {
50
47
  continue
51
48
  }
52
49
 
53
- // deduplicate peers
54
- if (seen.has(peer.id)) {
55
- continue
56
- }
57
-
58
- seen.add(peer.id)
59
-
60
50
  yield peer
61
51
  }
62
52
  }
@@ -142,8 +132,6 @@ export class Routing implements RoutingInterface, Startable {
142
132
  throw new CodeError('No peer routers available', 'ERR_NO_ROUTERS_AVAILABLE')
143
133
  }
144
134
 
145
- const seen = new PeerSet()
146
-
147
135
  for await (const peer of merge(
148
136
  ...supports(this.routers, 'getClosestPeers')
149
137
  .map(router => router.getClosestPeers(key, options))
@@ -152,13 +140,6 @@ export class Routing implements RoutingInterface, Startable {
152
140
  continue
153
141
  }
154
142
 
155
- // deduplicate peers
156
- if (seen.has(peer.id)) {
157
- continue
158
- }
159
-
160
- seen.add(peer.id)
161
-
162
143
  yield peer
163
144
  }
164
145
  }
package/src/storage.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { start, stop } from '@libp2p/interface'
1
+ import { CodeError, start, stop } from '@libp2p/interface'
2
2
  import createMortice from 'mortice'
3
3
  import type { Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions } from '@helia/interface/blocks'
4
4
  import type { Pins } from '@helia/interface/pins'
@@ -24,14 +24,14 @@ export interface GetOptions extends AbortOptions {
24
24
  */
25
25
  export class BlockStorage implements Blocks, Startable {
26
26
  public lock: Mortice
27
- private readonly child: Blockstore
27
+ private readonly child: Blocks
28
28
  private readonly pins: Pins
29
29
  private started: boolean
30
30
 
31
31
  /**
32
32
  * Create a new BlockStorage
33
33
  */
34
- constructor (blockstore: Blockstore, pins: Pins, options: BlockStorageInit = {}) {
34
+ constructor (blockstore: Blocks, pins: Pins, options: BlockStorageInit = {}) {
35
35
  this.child = blockstore
36
36
  this.pins = pins
37
37
  this.lock = createMortice({
@@ -169,4 +169,20 @@ export class BlockStorage implements Blocks, Startable {
169
169
  releaseLock()
170
170
  }
171
171
  }
172
+
173
+ async createSession (root: CID, options?: AbortOptions): Promise<Blockstore> {
174
+ const releaseLock = await this.lock.readLock()
175
+
176
+ try {
177
+ const blocks = await this.child.createSession(root, options)
178
+
179
+ if (blocks == null) {
180
+ throw new CodeError('Sessions not supported', 'ERR_UNSUPPORTED')
181
+ }
182
+
183
+ return blocks
184
+ } finally {
185
+ releaseLock()
186
+ }
187
+ }
172
188
  }
@@ -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()
@@ -1,6 +0,0 @@
1
- {
2
- "Helia": "https://ipfs.github.io/helia/classes/_helia_utils.Helia.html",
3
- ".:Helia": "https://ipfs.github.io/helia/classes/_helia_utils.Helia.html",
4
- "HeliaInit": "https://ipfs.github.io/helia/interfaces/_helia_utils.HeliaInit.html",
5
- ".:HeliaInit": "https://ipfs.github.io/helia/interfaces/_helia_utils.HeliaInit.html"
6
- }