@comapeo/core 2.3.1 → 2.3.2

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.
@@ -49,10 +49,7 @@ import { omit } from './lib/omit.js'
49
49
  import { MemberApi } from './member-api.js'
50
50
  import {
51
51
  SyncApi,
52
- kAddBlobWantRange,
53
- kClearBlobWantRanges,
54
52
  kHandleDiscoveryKey,
55
- kSetBlobDownloadFilter,
56
53
  kWaitForInitialSyncWithPeer,
57
54
  } from './sync/sync-api.js'
58
55
  import { Logger } from './logger.js'
@@ -60,9 +57,8 @@ import { IconApi } from './icon-api.js'
60
57
  import { readConfig } from './config-import.js'
61
58
  import TranslationApi from './translation-api.js'
62
59
  import { NotFoundError, nullIfNotFound } from './errors.js'
63
- import { getErrorCode, getErrorMessage } from './lib/error.js'
64
60
  /** @import { ProjectSettingsValue } from '@comapeo/schema' */
65
- /** @import { CoreStorage, BlobFilter, BlobStoreEntriesStream, KeyPair, Namespace, ReplicationStream } from './types.js' */
61
+ /** @import { CoreStorage, BlobFilter, BlobStoreEntriesStream, KeyPair, Namespace, ReplicationStream, GenericBlobFilter } from './types.js' */
66
62
 
67
63
  /** @typedef {Omit<ProjectSettingsValue, 'schemaName'>} EditableProjectSettings */
68
64
  /** @typedef {ProjectSettingsValue['configMetadata']} ConfigMetadata */
@@ -82,13 +78,6 @@ export const kIsArchiveDevice = Symbol('isArchiveDevice (temp - test only)')
82
78
 
83
79
  const EMPTY_PROJECT_SETTINGS = Object.freeze({})
84
80
 
85
- /** @type {import('./types.js').BlobFilter} */
86
- const NON_ARCHIVE_DEVICE_DOWNLOAD_FILTER = {
87
- photo: ['preview', 'thumbnail'],
88
- // Don't download any audio of video files, since previews and
89
- // thumbnails aren't supported yet.
90
- }
91
-
92
81
  /**
93
82
  * @extends {TypedEmitter<{ close: () => void }>}
94
83
  */
@@ -112,7 +101,6 @@ export class MapeoProject extends TypedEmitter {
112
101
  #l
113
102
  /** @type {Boolean} this avoids loading multiple configs in parallel */
114
103
  #loadingConfig
115
- #isArchiveDevice
116
104
 
117
105
  static EMPTY_PROJECT_SETTINGS = EMPTY_PROJECT_SETTINGS
118
106
 
@@ -154,12 +142,9 @@ export class MapeoProject extends TypedEmitter {
154
142
  this.#deviceId = getDeviceId(keyManager)
155
143
  this.#projectKey = projectKey
156
144
  this.#loadingConfig = false
157
- this.#isArchiveDevice = isArchiveDevice
158
145
 
159
146
  const getReplicationStream = this[kProjectReplicate].bind(this, true)
160
147
 
161
- const blobDownloadFilter = getBlobDownloadFilter(isArchiveDevice)
162
-
163
148
  ///////// 1. Setup database
164
149
 
165
150
  this.#sqlite = new Database(dbPath)
@@ -376,7 +361,8 @@ export class MapeoProject extends TypedEmitter {
376
361
 
377
362
  this.#blobStore = new BlobStore({
378
363
  coreManager: this.#coreManager,
379
- downloadFilter: blobDownloadFilter,
364
+ isArchiveDevice: isArchiveDevice,
365
+ logger: this.#l,
380
366
  })
381
367
 
382
368
  this.#blobStore.on('error', (err) => {
@@ -412,7 +398,7 @@ export class MapeoProject extends TypedEmitter {
412
398
  coreManager: this.#coreManager,
413
399
  coreOwnership: this.#coreOwnership,
414
400
  roles: this.#roles,
415
- blobDownloadFilter,
401
+ blobStore: this.#blobStore,
416
402
  logger: this.#l,
417
403
  getServerWebsocketUrls: async () => {
418
404
  const members = await this.#memberApi.getMany()
@@ -434,48 +420,6 @@ export class MapeoProject extends TypedEmitter {
434
420
  getReplicationStream,
435
421
  })
436
422
 
437
- /** @type {Map<string, BlobStoreEntriesStream>} */
438
- const entriesReadStreams = new Map()
439
-
440
- this.#coreManager.on('peer-download-intent', async (filter, peerId) => {
441
- entriesReadStreams.get(peerId)?.destroy()
442
-
443
- const entriesReadStream = this.#blobStore.createEntriesReadStream({
444
- live: true,
445
- filter,
446
- })
447
- entriesReadStreams.set(peerId, entriesReadStream)
448
-
449
- entriesReadStream.once('close', () => {
450
- if (entriesReadStreams.get(peerId) === entriesReadStream) {
451
- entriesReadStreams.delete(peerId)
452
- }
453
- })
454
-
455
- this.#syncApi[kClearBlobWantRanges](peerId)
456
-
457
- try {
458
- for await (const entry of entriesReadStream) {
459
- const { blockOffset, blockLength } = entry.value.blob
460
- this.#syncApi[kAddBlobWantRange](peerId, blockOffset, blockLength)
461
- }
462
- } catch (err) {
463
- if (getErrorCode(err) === 'ERR_STREAM_PREMATURE_CLOSE') return
464
- this.#l.log(
465
- 'Error getting blob entries stream for peer %h: %s',
466
- peerId,
467
- getErrorMessage(err)
468
- )
469
- }
470
- })
471
-
472
- this.#coreManager.creatorCore.on('peer-remove', (peer) => {
473
- const peerKey = peer.protomux.stream.remotePublicKey
474
- const peerId = peerKey.toString('hex')
475
- entriesReadStreams.get(peerId)?.destroy()
476
- entriesReadStreams.delete(peerId)
477
- })
478
-
479
423
  this.#translationApi = new TranslationApi({
480
424
  dataType: this.#dataTypes.translation,
481
425
  })
@@ -516,7 +460,7 @@ export class MapeoProject extends TypedEmitter {
516
460
  localPeers.off('discovery-key', onDiscoverykey)
517
461
  })
518
462
 
519
- this.#l.log('Created project instance %h', projectKey)
463
+ this.#l.log('Created project instance %h, %s', projectKey, isArchiveDevice)
520
464
  }
521
465
 
522
466
  /**
@@ -785,16 +729,12 @@ export class MapeoProject extends TypedEmitter {
785
729
 
786
730
  /** @param {boolean} isArchiveDevice */
787
731
  async [kSetIsArchiveDevice](isArchiveDevice) {
788
- if (this.#isArchiveDevice === isArchiveDevice) return
789
- const blobDownloadFilter = getBlobDownloadFilter(isArchiveDevice)
790
- this.#blobStore.setDownloadFilter(blobDownloadFilter)
791
- this.#syncApi[kSetBlobDownloadFilter](blobDownloadFilter)
792
- this.#isArchiveDevice = isArchiveDevice
732
+ this.#blobStore.setIsArchiveDevice(isArchiveDevice)
793
733
  }
794
734
 
795
735
  /** @returns {boolean} */
796
736
  get [kIsArchiveDevice]() {
797
- return this.#isArchiveDevice
737
+ return this.#blobStore.isArchiveDevice
798
738
  }
799
739
 
800
740
  /**
@@ -1054,15 +994,6 @@ export class MapeoProject extends TypedEmitter {
1054
994
  }
1055
995
  }
1056
996
  }
1057
-
1058
- /**
1059
- * @param {boolean} isArchiveDevice
1060
- * @returns {null | BlobFilter}
1061
- */
1062
- function getBlobDownloadFilter(isArchiveDevice) {
1063
- return isArchiveDevice ? null : NON_ARCHIVE_DEVICE_DOWNLOAD_FILTER
1064
- }
1065
-
1066
997
  /**
1067
998
  * @param {import("@comapeo/schema").ProjectSettings & { forks: string[] }} projectDoc
1068
999
  * @returns {EditableProjectSettings}
@@ -67,25 +67,41 @@ export class CoreSyncState {
67
67
  /** @type {InternalState['remoteStates']} */
68
68
  #remoteStates = new Map()
69
69
  /** @type {InternalState['localState']} */
70
- #localState = new PeerState()
70
+ #localState
71
71
  #preHavesLength = 0
72
72
  #update
73
73
  #peerSyncControllers
74
74
  #namespace
75
+ #deviceId
75
76
  #l
77
+ #hasDownloadFilter
76
78
 
77
79
  /**
78
80
  * @param {object} opts
79
81
  * @param {() => void} opts.onUpdate Called when a state update is available (via getState())
80
82
  * @param {Map<string, import('./peer-sync-controller.js').PeerSyncController>} opts.peerSyncControllers
81
83
  * @param {Namespace} opts.namespace
84
+ * @param {string} opts.deviceId
85
+ * @param {(peerId: string) => boolean} opts.hasDownloadFilter
82
86
  * @param {Logger} [opts.logger]
83
87
  */
84
- constructor({ onUpdate, peerSyncControllers, namespace, logger }) {
88
+ constructor({
89
+ onUpdate,
90
+ peerSyncControllers,
91
+ namespace,
92
+ deviceId,
93
+ hasDownloadFilter,
94
+ logger,
95
+ }) {
85
96
  // The logger parameter is already namespaced by NamespaceSyncState
86
97
  this.#l = logger || Logger.create('css')
87
98
  this.#peerSyncControllers = peerSyncControllers
88
99
  this.#namespace = namespace
100
+ this.#deviceId = deviceId
101
+ this.#hasDownloadFilter = hasDownloadFilter
102
+ this.#localState = new PeerState({
103
+ wantsEverything: !hasDownloadFilter(deviceId),
104
+ })
89
105
  // Called whenever the state changes, so we clear the cache because next
90
106
  // call to getState() will need to re-derive the state
91
107
  this.#update = () => {
@@ -187,18 +203,21 @@ export class CoreSyncState {
187
203
  * @returns {void}
188
204
  */
189
205
  addWantRange(peerId, start, length) {
206
+ this.#l.log('Peer %S wants range %d-%d', peerId, start, start + length)
190
207
  const peerState = this.#getOrCreatePeerState(peerId)
191
208
  peerState.addWantRange(start, length)
192
209
  this.#update()
193
210
  }
194
211
 
195
212
  /**
213
+ * Set whether a peer wants everything or only blocks specified by addWantRange()
196
214
  * @param {PeerId} peerId
197
- * @returns {void}
215
+ * @param {boolean} wantsEverything
198
216
  */
199
- clearWantRanges(peerId) {
217
+ setWantsEverything(peerId, wantsEverything) {
218
+ this.#l.log('Peer %S wants everything: %s', peerId, wantsEverything)
200
219
  const peerState = this.#getOrCreatePeerState(peerId)
201
- peerState.clearWantRanges()
220
+ peerState.setWantsEverything(wantsEverything)
202
221
  this.#update()
203
222
  }
204
223
 
@@ -206,8 +225,7 @@ export class CoreSyncState {
206
225
  * @param {PeerId} peerId
207
226
  */
208
227
  addPeer(peerId) {
209
- if (this.#remoteStates.has(peerId)) return
210
- this.#remoteStates.set(peerId, new PeerState())
228
+ this.#getOrCreatePeerState(peerId)
211
229
  this.#update()
212
230
  }
213
231
 
@@ -225,9 +243,12 @@ export class CoreSyncState {
225
243
  * @param {PeerId} peerId
226
244
  */
227
245
  #getOrCreatePeerState(peerId) {
246
+ if (peerId === this.#deviceId) return this.#localState
228
247
  let peerState = this.#remoteStates.get(peerId)
229
248
  if (!peerState) {
230
- peerState = new PeerState()
249
+ peerState = new PeerState({
250
+ wantsEverything: !this.#hasDownloadFilter(peerId),
251
+ })
231
252
  this.#remoteStates.set(peerId, peerState)
232
253
  }
233
254
  return peerState
@@ -305,9 +326,14 @@ export class PeerState {
305
326
  * What blocks do we want? If `null`, we want everything.
306
327
  * @type {null | Bitfield}
307
328
  */
308
- #wants = null
329
+ #wants
309
330
  /** @type {PeerNamespaceState['status']} */
310
331
  status = 'stopped'
332
+
333
+ constructor({ wantsEverything = true } = {}) {
334
+ this.#wants = wantsEverything ? null : new RemoteBitfield()
335
+ }
336
+
311
337
  get preHavesBitfield() {
312
338
  return this.#preHaves
313
339
  }
@@ -340,11 +366,11 @@ export class PeerState {
340
366
  this.#wants.setRange(start, length, true)
341
367
  }
342
368
  /**
343
- * Set the range of blocks that this peer wants to the empty set. In other
344
- * words, this peer wants nothing from this core.
369
+ * Set whether this peer wants everything or only blocks specified by addWantRange()
370
+ * @param {boolean} wantsEverything
345
371
  */
346
- clearWantRanges() {
347
- this.#wants = new RemoteBitfield()
372
+ setWantsEverything(wantsEverything) {
373
+ this.#wants = wantsEverything ? null : new RemoteBitfield()
348
374
  }
349
375
  /**
350
376
  * Returns whether the peer has the block at `index`. If a pre-have bitfield
@@ -427,6 +453,7 @@ export function deriveState(coreState) {
427
453
  const truncate = 2 ** Math.min(32, length - i) - 1
428
454
 
429
455
  const localHaves = coreState.localState.haveWord(i) & truncate
456
+ const localWants = coreState.localState.wantWord(i) & truncate
430
457
  localState.have += bitCount32(localHaves)
431
458
 
432
459
  let someoneElseWantsFromMe = 0
@@ -440,7 +467,7 @@ export function deriveState(coreState) {
440
467
  remoteStates[peerId].want += bitCount32(theyWantFromMe)
441
468
  someoneElseWantsFromMe |= theyWantFromMe
442
469
 
443
- const iWantFromThem = peerHaves & ~localHaves
470
+ const iWantFromThem = peerHaves & ~localHaves & localWants
444
471
  remoteStates[peerId].wanted += bitCount32(iWantFromThem)
445
472
  iWantFromSomeoneElse |= iWantFromThem
446
473
  }
@@ -20,6 +20,8 @@ export class NamespaceSyncState {
20
20
  #cachedState = null
21
21
  #peerSyncControllers
22
22
  #logger
23
+ #deviceId
24
+ #blobStore
23
25
 
24
26
  /**
25
27
  * @param {object} opts
@@ -27,6 +29,7 @@ export class NamespaceSyncState {
27
29
  * @param {import('../core-manager/index.js').CoreManager} opts.coreManager
28
30
  * @param {() => void} opts.onUpdate Called when a state update is available (via getState())
29
31
  * @param {Map<string, import('./peer-sync-controller.js').PeerSyncController>} opts.peerSyncControllers
32
+ * @param {import('../blob-store/index.js').BlobStore} opts.blobStore
30
33
  * @param {Logger} [opts.logger]
31
34
  */
32
35
  constructor({
@@ -34,12 +37,15 @@ export class NamespaceSyncState {
34
37
  coreManager,
35
38
  onUpdate,
36
39
  peerSyncControllers,
40
+ blobStore,
37
41
  logger,
38
42
  }) {
39
43
  // Currently we don't create a logger for this class, just pass it down
40
44
  this.#logger = logger
41
45
  this.#namespace = namespace
42
46
  this.#peerSyncControllers = peerSyncControllers
47
+ this.#blobStore = blobStore
48
+ this.#deviceId = coreManager.deviceId
43
49
  // Called whenever the state changes, so we clear the cache because next
44
50
  // call to getState() will need to re-derive the state
45
51
  this.#handleUpdate = () => {
@@ -60,6 +66,18 @@ export class NamespaceSyncState {
60
66
  if (namespace !== this.#namespace) return
61
67
  this.#insertPreHaves(msg)
62
68
  })
69
+
70
+ if (this.#namespace !== 'blob') return
71
+ blobStore.on('blob-filter', (peerId, filter) => {
72
+ const wantsEverything = !filter
73
+ for (const css of this.#coreStates.values()) {
74
+ css.setWantsEverything(peerId, wantsEverything)
75
+ }
76
+ })
77
+ blobStore.on('want-blob-range', ({ blobCoreId, peerId, start, length }) => {
78
+ const coreState = this.#getCoreState(blobCoreId)
79
+ coreState.addWantRange(peerId, start, length)
80
+ })
63
81
  }
64
82
 
65
83
  get namespace() {
@@ -136,28 +154,6 @@ export class NamespaceSyncState {
136
154
  this.#getCoreState(coreDiscoveryId).insertPreHaves(peerId, start, bitfield)
137
155
  }
138
156
 
139
- /**
140
- * @param {string} peerId
141
- * @param {number} start
142
- * @param {number} length
143
- * @returns {void}
144
- */
145
- addWantRange(peerId, start, length) {
146
- for (const coreState of this.#coreStates.values()) {
147
- coreState.addWantRange(peerId, start, length)
148
- }
149
- }
150
-
151
- /**
152
- * @param {string} peerId
153
- * @returns {void}
154
- */
155
- clearWantRanges(peerId) {
156
- for (const coreState of this.#coreStates.values()) {
157
- coreState.clearWantRanges(peerId)
158
- }
159
- }
160
-
161
157
  /**
162
158
  * @param {string} discoveryId
163
159
  */
@@ -169,6 +165,13 @@ export class NamespaceSyncState {
169
165
  onUpdate: this.#handleUpdate,
170
166
  peerSyncControllers: this.#peerSyncControllers,
171
167
  namespace: this.#namespace,
168
+ deviceId: this.#deviceId,
169
+ hasDownloadFilter: (peerId) => {
170
+ return (
171
+ this.#namespace === 'blob' &&
172
+ !!this.#blobStore.getBlobFilter(peerId)
173
+ )
174
+ },
172
175
  logger: Logger.create('css:' + this.#namespace, this.#logger, {
173
176
  prefix: logPrefix,
174
177
  }),
@@ -25,9 +25,6 @@ export const kRescindFullStopRequest = Symbol('foreground')
25
25
  export const kWaitForInitialSyncWithPeer = Symbol(
26
26
  'wait for initial sync with peer'
27
27
  )
28
- export const kSetBlobDownloadFilter = Symbol('set isArchiveDevice')
29
- export const kAddBlobWantRange = Symbol('add blob want range')
30
- export const kClearBlobWantRanges = Symbol('clear blob want ranges')
31
28
 
32
29
  /**
33
30
  * @typedef {'initial' | 'full'} SyncType
@@ -37,6 +34,15 @@ export const kClearBlobWantRanges = Symbol('clear blob want ranges')
37
34
  * @typedef {'none' | 'presync' | 'all'} SyncEnabledState
38
35
  */
39
36
 
37
+ /**
38
+ * @internal
39
+ * @typedef {object} BlobWantRange
40
+ * @property {number} start
41
+ * @property {number} length
42
+ * @property {string} blobCoreId
43
+ * @property {string} peerId
44
+ */
45
+
40
46
  /**
41
47
  * @internal
42
48
  * @typedef {object} RemoteDeviceNamespaceGroupSyncState
@@ -93,8 +99,6 @@ export class SyncApi extends TypedEmitter {
93
99
  #getReplicationStream
94
100
  /** @type {Map<string, WebSocket>} */
95
101
  #serverWebsockets = new Map()
96
- /** @type {null | BlobFilter} */
97
- #blobDownloadFilter = null
98
102
 
99
103
  /**
100
104
  * @param {object} opts
@@ -103,7 +107,7 @@ export class SyncApi extends TypedEmitter {
103
107
  * @param {import('../roles.js').Roles} opts.roles
104
108
  * @param {() => Promise<Iterable<string>>} opts.getServerWebsocketUrls
105
109
  * @param {() => ReplicationStream} opts.getReplicationStream
106
- * @param {null | BlobFilter} opts.blobDownloadFilter
110
+ * @param {import('../blob-store/index.js').BlobStore} opts.blobStore
107
111
  * @param {number} [opts.throttleMs]
108
112
  * @param {Logger} [opts.logger]
109
113
  */
@@ -115,7 +119,7 @@ export class SyncApi extends TypedEmitter {
115
119
  getReplicationStream,
116
120
  logger,
117
121
  coreOwnership,
118
- blobDownloadFilter,
122
+ blobStore,
119
123
  }) {
120
124
  super()
121
125
  this.#l = Logger.create('syncApi', logger)
@@ -128,6 +132,7 @@ export class SyncApi extends TypedEmitter {
128
132
  coreManager,
129
133
  throttleMs,
130
134
  peerSyncControllers: this.#pscByPeerId,
135
+ blobStore,
131
136
  logger,
132
137
  })
133
138
  this[kSyncState].setMaxListeners(0)
@@ -135,8 +140,6 @@ export class SyncApi extends TypedEmitter {
135
140
  this.#updateState(namespaceSyncState)
136
141
  })
137
142
 
138
- this[kSetBlobDownloadFilter](blobDownloadFilter)
139
-
140
143
  this.#coreManager.creatorCore.on('peer-add', this.#handlePeerAdd)
141
144
  this.#coreManager.creatorCore.on('peer-remove', this.#handlePeerDisconnect)
142
145
 
@@ -156,37 +159,6 @@ export class SyncApi extends TypedEmitter {
156
159
  .catch(noop)
157
160
  }
158
161
 
159
- /** @param {import('../types.js').BlobFilter | null} blobDownloadFilter */
160
- [kSetBlobDownloadFilter](blobDownloadFilter) {
161
- this.#blobDownloadFilter = blobDownloadFilter
162
- if (!blobDownloadFilter) return // No download intents = intend to download everything
163
- for (const peer of this.#coreManager.creatorCore.peers) {
164
- this.#coreManager.sendDownloadIntents(blobDownloadFilter, peer)
165
- }
166
- }
167
-
168
- /**
169
- * Add some blob blocks this peer wants.
170
- *
171
- * @param {string} peerId
172
- * @param {number} start
173
- * @param {number} length
174
- * @returns {void}
175
- */
176
- [kAddBlobWantRange](peerId, start, length) {
177
- this[kSyncState].addBlobWantRange(peerId, start, length)
178
- }
179
-
180
- /**
181
- * Clear the blob blocks this peer wants.
182
- *
183
- * @param {string} peerId
184
- * @returns {void}
185
- */
186
- [kClearBlobWantRanges](peerId) {
187
- this[kSyncState].clearBlobWantRanges(peerId)
188
- }
189
-
190
162
  /** @type {import('../local-peers.js').LocalPeersEvents['discovery-key']} */
191
163
  [kHandleDiscoveryKey](discoveryKey, protomux) {
192
164
  const peerSyncController = this.#peerSyncControllers.get(protomux)
@@ -540,9 +512,6 @@ export class SyncApi extends TypedEmitter {
540
512
  )
541
513
  return
542
514
  }
543
- if (this.#blobDownloadFilter) {
544
- this.#coreManager.sendDownloadIntents(this.#blobDownloadFilter, peer)
545
- }
546
515
  const peerSyncController = new PeerSyncController({
547
516
  protomux,
548
517
  coreManager: this.#coreManager,
@@ -23,10 +23,17 @@ export class SyncState extends TypedEmitter {
23
23
  * @param {object} opts
24
24
  * @param {import('../core-manager/index.js').CoreManager} opts.coreManager
25
25
  * @param {Map<string, import('./peer-sync-controller.js').PeerSyncController>} opts.peerSyncControllers
26
+ * @param {import('../blob-store/index.js').BlobStore} opts.blobStore
26
27
  * @param {number} [opts.throttleMs]
27
28
  * @param {import('../logger.js').Logger} [opts.logger]
28
29
  */
29
- constructor({ coreManager, peerSyncControllers, throttleMs = 200, logger }) {
30
+ constructor({
31
+ coreManager,
32
+ peerSyncControllers,
33
+ blobStore,
34
+ throttleMs = 200,
35
+ logger,
36
+ }) {
30
37
  super()
31
38
  const throttledHandleUpdate = throttle(throttleMs, this.#handleUpdate)
32
39
  for (const namespace of NAMESPACES) {
@@ -35,6 +42,7 @@ export class SyncState extends TypedEmitter {
35
42
  coreManager,
36
43
  onUpdate: throttledHandleUpdate,
37
44
  peerSyncControllers,
45
+ blobStore,
38
46
  logger,
39
47
  })
40
48
  }
@@ -68,24 +76,6 @@ export class SyncState extends TypedEmitter {
68
76
  ])
69
77
  }
70
78
 
71
- /**
72
- * @param {string} peerId
73
- * @param {number} start
74
- * @param {number} length
75
- * @returns {void}
76
- */
77
- addBlobWantRange(peerId, start, length) {
78
- this.#syncStates.blob.addWantRange(peerId, start, length)
79
- }
80
-
81
- /**
82
- * @param {string} peerId
83
- * @returns {void}
84
- */
85
- clearBlobWantRanges(peerId) {
86
- this.#syncStates.blob.clearWantRanges(peerId)
87
- }
88
-
89
79
  #handleUpdate = () => {
90
80
  this.emit('state', this.getState())
91
81
  }
package/src/types.ts CHANGED
@@ -152,6 +152,6 @@ export type DefaultEmitterEvents<
152
152
 
153
153
  export type BlobStoreEntriesStream = Readable & {
154
154
  [Symbol.asyncIterator](): AsyncIterableIterator<
155
- HyperdriveEntry & { driveId: string }
155
+ HyperdriveEntry & { driveId: string; blobCoreId: string }
156
156
  >
157
157
  }