corestore 7.9.2 → 7.10.1

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/README.md CHANGED
@@ -114,6 +114,42 @@ store.watch(function (core) {
114
114
 
115
115
  Unregister a callback used with `store.watch(callback)` so it no longer fires.
116
116
 
117
+ #### `const handle = store.notifyGroup(topic)`
118
+
119
+ > [!IMPORTANT]
120
+ > This feature is _experimental_. The API is subject to change, and everything may break.
121
+
122
+ Get a `handle` for updates from all `hypercore`s with the group `topic` set.
123
+
124
+ #### `const stream = handle.update(opts = {})`
125
+
126
+ Gets updates for the `topic` the handle is for.
127
+
128
+ `opts` includes:
129
+
130
+ ```js
131
+ {
132
+ since: 0, // What timestamp to start returning updates from. Default `0` returns all updates
133
+ reverse: true, // Flag to return updates in reverse order. Defaults to `true` so latest returned first
134
+ }
135
+ ```
136
+
137
+ Stream returns the core's `key`:
138
+
139
+ ```js
140
+ for await (const key of handle.updates()) {
141
+ // ...
142
+ }
143
+ ```
144
+
145
+ #### `handle.destroy()`
146
+
147
+ Destroys and unregisters the `handle` from its `store`.
148
+
149
+ #### `handle.on('update', callback)`
150
+
151
+ Calls the callback whenever a core with the `topic` for the `handle` updates.
152
+
117
153
  #### `await store.suspend()`
118
154
 
119
155
  Suspend the underlying storage for the Corestore.
@@ -132,6 +168,13 @@ This is useful for creating deterministic key pairs that are unique to a peer.
132
168
 
133
169
  Fully close this Corestore instance.
134
170
 
171
+ #### `store.on('group-active', (topic) => {})`
172
+
173
+ > [!IMPORTANT]
174
+ > This feature is _experimental_. The API is subject to change, and everything may break.
175
+
176
+ The `group-active` event emits whenever an opened Hypercore in the store updates. The `topic` is the group topic the core belongs to.
177
+
135
178
  ### License
136
179
 
137
180
  MIT
package/index.js CHANGED
@@ -8,6 +8,7 @@ const { isAndroid } = require('which-runtime')
8
8
  const { STORAGE_EMPTY, ASSERTION } = require('hypercore-errors')
9
9
 
10
10
  const auditStore = require('./lib/audit.js')
11
+ const GroupNotifyHandle = require('./lib/notify.js')
11
12
 
12
13
  const [NS] = crypto.namespace('corestore', 1)
13
14
  const DEFAULT_NAMESPACE = b4a.alloc(32) // This is meant to be 32 0-bytes
@@ -259,8 +260,10 @@ class Corestore extends ReadyResource {
259
260
  this.watchers = null
260
261
  this.watchIndex = -1
261
262
 
263
+ this._groupNotifiers = new Map() // group notifications
262
264
  this._findingPeers = null // here for legacy
263
265
  this._ongcBound = this._ongc.bind(this)
266
+ this._onGroupActiveBound = this._onGroupActive.bind(this)
264
267
 
265
268
  if (opts.primaryKey && !this.root && !opts.unsafe) {
266
269
  throw ASSERTION(
@@ -291,6 +294,44 @@ class Corestore extends ReadyResource {
291
294
  }
292
295
  }
293
296
 
297
+ _onGroupActive(topic) {
298
+ this.emit('group-active', topic)
299
+
300
+ const topicHex = b4a.toString(topic, 'hex')
301
+ const handles = this._groupNotifiers.get(topicHex)
302
+ if (!handles) return
303
+ for (const handle of handles) handle.emit('update')
304
+ }
305
+
306
+ notifyGroup(topic) {
307
+ const topicHex = b4a.toString(topic, 'hex')
308
+ let handles = this._groupNotifiers.get(topicHex)
309
+
310
+ if (!handles) {
311
+ handles = []
312
+ this._groupNotifiers.set(topicHex, handles)
313
+ }
314
+
315
+ const handle = new GroupNotifyHandle(this, topic)
316
+ const length = handles.push(handle)
317
+ handle.index = length - 1
318
+
319
+ return handle
320
+ }
321
+
322
+ _removeGroupNotify(handle) {
323
+ if (handle.index === -1) return
324
+
325
+ const topicHex = b4a.toString(handle._topic, 'hex')
326
+ const handles = this._groupNotifiers.get(topicHex)
327
+ if (!handles) return
328
+
329
+ const popped = handles.pop()
330
+ if (popped !== handle) handles[(popped.index = handle.index)] = popped
331
+ if (handles.length === 0) this._groupNotifiers.delete(topicHex)
332
+ handle.index = -1
333
+ }
334
+
294
335
  findingPeers() {
295
336
  if (this._findingPeers === null) this._findingPeers = new FindingPeers()
296
337
  this._findingPeers.inc(this.sessions)
@@ -309,10 +350,14 @@ class Corestore extends ReadyResource {
309
350
  async suspend({ log = noop } = {}) {
310
351
  await log('Flushing db...')
311
352
  // If readOnly we don't need to flush
312
- if (!this.storage.readOnly) await this.storage.db.flush()
353
+ if (!this.storage.readOnly) {
354
+ await this.storage.db.flush()
355
+ await log('Flusing db completed')
356
+ }
313
357
  if (!this.shouldSuspend) return
314
358
  await log('Suspending db...')
315
- await this.storage.suspend()
359
+ await this.storage.suspend({ log })
360
+ await log('Suspending db completed')
316
361
  }
317
362
 
318
363
  resume() {
@@ -568,6 +613,7 @@ class Corestore extends ReadyResource {
568
613
 
569
614
  _makeSession(conf) {
570
615
  const session = new Hypercore(null, null, conf)
616
+
571
617
  if (this._findingPeers !== null) this._findingPeers.add(session)
572
618
  return session
573
619
  }
@@ -610,7 +656,8 @@ class Corestore extends ReadyResource {
610
656
  keyPair: null,
611
657
  key: null,
612
658
  discoveryKey,
613
- manifest: null
659
+ manifest: null,
660
+ group: null
614
661
  }
615
662
 
616
663
  if (opts.name) {
@@ -628,6 +675,10 @@ class Corestore extends ReadyResource {
628
675
  }
629
676
  }
630
677
 
678
+ if (opts.group) {
679
+ result.group = opts.group
680
+ }
681
+
631
682
  if (opts.key) result.key = ID.decode(opts.key)
632
683
  else if (result.manifest) result.key = Hypercore.key(result.manifest)
633
684
 
@@ -644,7 +695,7 @@ class Corestore extends ReadyResource {
644
695
  const auth = this._auth(discoveryKey, opts)
645
696
 
646
697
  const id = toHex(auth.discoveryKey)
647
- const existing = this.cores.resume(id)
698
+ const existing = this.cores.resume(id, auth.group)
648
699
  if (existing && !existing.closing) return existing
649
700
 
650
701
  const core = Hypercore.createCore(this.storage, {
@@ -663,6 +714,7 @@ class Corestore extends ReadyResource {
663
714
  keyPair: auth.keyPair,
664
715
  legacy: opts.legacy,
665
716
  manifest: auth.manifest,
717
+ group: auth.group,
666
718
  globalCache: opts.globalCache || this.globalCache || null,
667
719
  alias: opts.name ? { name: opts.name, namespace: this.ns } : null
668
720
  })
@@ -671,6 +723,8 @@ class Corestore extends ReadyResource {
671
723
  this.cores.gc(core)
672
724
  }
673
725
 
726
+ core.ongroupupdate = this._onGroupActiveBound
727
+
674
728
  core.replicator.ondownloading = () => {
675
729
  if (this.active) this.streamTracker.attachAll(core)
676
730
  }
package/lib/notify.js ADDED
@@ -0,0 +1,27 @@
1
+ const { EventEmitter } = require('events')
2
+ const { Readable } = require('streamx')
3
+
4
+ class GroupNotifyHandle extends EventEmitter {
5
+ constructor(store, topic) {
6
+ super()
7
+ this._store = store
8
+ this._topic = topic
9
+ this.index = -1
10
+ }
11
+
12
+ updates(opts) {
13
+ return Readable.deferred(async () => {
14
+ const groupId = await this._store.storage.getGroup(this._topic).catch(noop)
15
+ if (groupId === null) return null
16
+ return this._store.storage.createGroupUpdateStream(groupId, opts)
17
+ })
18
+ }
19
+
20
+ destroy() {
21
+ this._store._removeGroupNotify(this)
22
+ }
23
+ }
24
+
25
+ module.exports = GroupNotifyHandle
26
+
27
+ function noop() {}
package/package.json CHANGED
@@ -1,20 +1,28 @@
1
1
  {
2
2
  "name": "corestore",
3
- "version": "7.9.2",
3
+ "version": "7.10.1",
4
4
  "description": "A Hypercore factory that simplifies managing collections of cores.",
5
5
  "main": "index.js",
6
6
  "files": [
7
7
  "index.js",
8
8
  "lib/*"
9
9
  ],
10
+ "imports": {
11
+ "events": {
12
+ "bare": "bare-events",
13
+ "default": "events"
14
+ }
15
+ },
10
16
  "dependencies": {
11
17
  "b4a": "^1.6.7",
12
- "hypercore": "^11.19.0",
18
+ "bare-events": "^2.8.3",
19
+ "hypercore": "^11.32.0",
13
20
  "hypercore-crypto": "^3.4.2",
14
21
  "hypercore-errors": "^1.4.0",
15
22
  "hypercore-id-encoding": "^1.3.0",
16
23
  "ready-resource": "^1.1.1",
17
24
  "sodium-universal": "^5.0.1",
25
+ "streamx": "^2.26.0",
18
26
  "which-runtime": "^1.2.1"
19
27
  },
20
28
  "devDependencies": {
@@ -22,15 +30,14 @@
22
30
  "lunte": "^1.3.0",
23
31
  "prettier": "^3.6.2",
24
32
  "prettier-config-holepunch": "^2.0.0",
25
- "rache": "^1.0.0",
26
- "test-tmp": "^1.3.0"
33
+ "rache": "^1.0.0"
27
34
  },
28
35
  "scripts": {
29
36
  "format": "prettier --write .",
30
37
  "test": "npm run test:node && npm run test:bare",
31
- "test:bare": "brittle-bare --coverage test/*.js",
38
+ "test:bare": "brittle-bare --coverage test/all.js",
32
39
  "lint": "prettier --check . && lunte",
33
- "test:node": "brittle-node --coverage test/*.js"
40
+ "test:node": "brittle-node --coverage test/all.js"
34
41
  },
35
42
  "repository": {
36
43
  "type": "git",