autobee 1.0.3 → 1.0.5

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/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  const ReadyResource = require('ready-resource')
2
+ const ReadyGuard = require('ready-guard')
2
3
  const b4a = require('b4a')
3
4
  const safetyCatch = require('safety-catch')
4
5
  const Hyperbee = require('hyperbee2')
5
6
  const ID = require('hypercore-id-encoding')
7
+ const rrp = require('resolve-reject-promise')
6
8
  const { AutobeeEncryption, WriterEncryption } = require('autobee-encryption')
7
9
  const AutobeeWakeup = require('autobee-wakeup')
8
10
  const Hypercore = require('hypercore')
@@ -11,6 +13,7 @@ const c = require('compact-encoding')
11
13
  const asserts = require('./lib/asserts.js')
12
14
  const boot = require('./lib/boot.js')
13
15
  const encoding = require('./lib/encoding.js')
16
+ const FastForward = require('./lib/fast-forward.js')
14
17
  const System = require('./lib/system.js')
15
18
  const ApplyCalls = require('./lib/apply-calls.js')
16
19
  const topo = require('./lib/topo.js')
@@ -19,6 +22,7 @@ const UpdateChanges = require('./lib/updates.js')
19
22
 
20
23
  const EMPTY_HEAD = { length: 0, key: null }
21
24
  const INTERRUPT = new Error('Apply interrupted')
25
+ const MIN_FF_GAP = 32
22
26
 
23
27
  module.exports = class Autobee extends ReadyResource {
24
28
  constructor(store, key = null, handlers = {}) {
@@ -32,14 +36,15 @@ module.exports = class Autobee extends ReadyResource {
32
36
  const { name = null, encrypted, encryptionKey } = handlers
33
37
 
34
38
  this.encrypted = encrypted === true || !!encryptionKey
39
+ this._getEncryptionProviderBound = this._getEncryptionProvider.bind(this)
35
40
 
36
41
  const bee = new Hyperbee(store.namespace('view'), {
37
42
  // defer one tick to ensure consistent state, then return state prom
38
43
  preload: async () => {
39
44
  await 1
40
- await this._bootingState
45
+ if (!this._bootGuard.opened) await this._bootGuard.ready()
41
46
  },
42
- getEncryptionProvider: () => this._getEncryptionProvider()
47
+ getEncryptionProvider: this._getEncryptionProviderBound
43
48
  })
44
49
 
45
50
  this.store = store
@@ -50,7 +55,7 @@ module.exports = class Autobee extends ReadyResource {
50
55
  this.bootstrap = null
51
56
 
52
57
  this.system = new System(this.store.namespace('system'), this.name, {
53
- getEncryptionProvider: () => this._getEncryptionProvider(),
58
+ getEncryptionProvider: this._getEncryptionProviderBound,
54
59
  encrypted: this.encrypted
55
60
  })
56
61
 
@@ -66,12 +71,18 @@ module.exports = class Autobee extends ReadyResource {
66
71
  this.writers = null
67
72
  this.bumping = 0
68
73
 
74
+ this.fastForwardBoot = handlers.fastForward || null
75
+ this.fastForward = null
76
+ this.fastForwarding = null
77
+ this.fastForwardTo = null
78
+
69
79
  this._workingBee = bee
70
80
  this._workingView = handlers.open ? handlers.open(this._workingBee, this) : this._workingBee
71
81
 
72
82
  this._appending = []
73
83
  this._draining = null
74
84
 
85
+ this._bootGuard = new ReadyGuard()
75
86
  this._bootingState = null
76
87
  this._bootingAll = null
77
88
 
@@ -81,13 +92,16 @@ module.exports = class Autobee extends ReadyResource {
81
92
  this._needsUpdate = false
82
93
  this._updateLocalCore = null
83
94
  this._host = new ApplyCalls(this)
95
+ this._notifyHandler = null
84
96
 
85
97
  this.interrupted = null
86
98
  this._interrupting = false
87
99
  this._onErrorBound = this._onError.bind(this)
100
+ this._bumpSoonBound = this.bumpSoon.bind(this)
88
101
 
89
- this._wakeup = new AutobeeWakeup(this, handlers)
90
102
  this.wakeupCapability = null
103
+ this._wakeup = new AutobeeWakeup(this, handlers)
104
+ this.previousDrain = 0
91
105
 
92
106
  this.ready().catch(noop)
93
107
  }
@@ -127,6 +141,7 @@ module.exports = class Autobee extends ReadyResource {
127
141
  }
128
142
 
129
143
  _registerWakeup() {
144
+ if (!this.wakeupCapability) return
130
145
  this._wakeup.recouple()
131
146
  this._wakeup.setCapability(this.wakeupCapability.key, this.wakeupCapability.discoveryKey)
132
147
  }
@@ -140,7 +155,7 @@ module.exports = class Autobee extends ReadyResource {
140
155
  return keys
141
156
  }
142
157
 
143
- async getWriterViews(key) {
158
+ getWriterViews(key) {
144
159
  const id = b4a.toString(key, 'hex')
145
160
  const w = this.writers.active.get(id)
146
161
  if (!w) return []
@@ -160,6 +175,7 @@ module.exports = class Autobee extends ReadyResource {
160
175
 
161
176
  async _close() {
162
177
  this._interrupting = true
178
+ if (this._notifyHandler) this._notifyHandler.destroy()
163
179
  if (this._draining) await this._draining
164
180
 
165
181
  if (this._handlers.close) await this._handlers.close(this.view)
@@ -215,6 +231,8 @@ module.exports = class Autobee extends ReadyResource {
215
231
  }
216
232
 
217
233
  async _bootState() {
234
+ if (!this._bootGuard.enter()) return this._bootGuard.ready()
235
+
218
236
  const result = await boot(this.store, this.key, {
219
237
  encryptionKey: this.encryptionKey,
220
238
  keyPair: this.keyPair
@@ -225,6 +243,7 @@ module.exports = class Autobee extends ReadyResource {
225
243
  this.discoveryKey = result.bootstrap.core.discoveryKey
226
244
  this.id = result.bootstrap.core.id
227
245
  this.encryptionKey = result.encryptionKey
246
+ this.previousDrain = result.previousDrain
228
247
 
229
248
  if (this.encrypted) {
230
249
  asserts.assert(this.encryptionKey !== null, 'Encryption key is expected')
@@ -244,6 +263,15 @@ module.exports = class Autobee extends ReadyResource {
244
263
 
245
264
  this._registerWakeup()
246
265
 
266
+ if (this.wakeupCapability) {
267
+ if (this.bootstrap !== this.local) {
268
+ await this.bootstrap.setGroup(this.wakeupCapability.discoveryKey)
269
+ }
270
+
271
+ this._notifyHandler = this.store.notifyGroup(this.wakeupCapability.discoveryKey)
272
+ this._notifyHandler.on('update', this._bumpSoonBound)
273
+ }
274
+
247
275
  const system = result.system || EMPTY_HEAD
248
276
 
249
277
  await this.system.boot(system)
@@ -256,10 +284,14 @@ module.exports = class Autobee extends ReadyResource {
256
284
  this.bee.move(view)
257
285
 
258
286
  await this.writers.updateLocalState()
287
+
288
+ this._bootGuard.exit()
289
+
290
+ return this._bootGuard.ready()
259
291
  }
260
292
 
261
293
  async _bootAll() {
262
- await this._bootingState
294
+ if (!this._bootGuard.opened) await this._bootGuard.ready()
263
295
 
264
296
  for await (const node of this.system.list()) {
265
297
  await this.writers.add(node.key)
@@ -272,7 +304,10 @@ module.exports = class Autobee extends ReadyResource {
272
304
  }
273
305
 
274
306
  async _bump() {
307
+ if (!this._bootGuard.opened) await this._bootGuard.ready()
308
+
275
309
  await this._flushWakeup()
310
+
276
311
  this.bumping++
277
312
 
278
313
  if (!this._draining) {
@@ -327,18 +362,30 @@ module.exports = class Autobee extends ReadyResource {
327
362
  }
328
363
 
329
364
  async _drain() {
330
- if (this._updateLocalCore !== null) {
331
- await this._rotateLocalWriter(this._updateLocalCore)
365
+ if (this.fastForwardBoot) {
366
+ this._initFastForward(this.fastForwardBoot)
367
+ this.fastForwardBoot = null
332
368
  }
333
369
 
334
370
  const changes = this._hasUpdate ? new UpdateChanges(this) : null
335
371
  if (changes) changes.track()
336
372
 
373
+ // Anything expecting work to be done during bumpSoon should do it here
337
374
  while (!this._interrupting && this.bumping > 0) {
338
- if (this._interrupting) return
375
+ if (this._interrupting) break
376
+
377
+ // Ensure we catch updates during the drain (i.e. setLocal will bump)
378
+ if (this._updateLocalCore !== null) {
379
+ await this._rotateLocalWriter(this._updateLocalCore)
380
+ }
339
381
 
340
382
  try {
341
383
  while (!this._interrupting) {
384
+ if (this.fastForwardTo !== null) {
385
+ await this._applyFastForward()
386
+ break // revaluate conditions...
387
+ }
388
+
342
389
  if (!(await this._bumpPendingWriters())) break
343
390
  this._needsUpdate = true
344
391
  }
@@ -361,6 +408,16 @@ module.exports = class Autobee extends ReadyResource {
361
408
  async _flushWakeup() {
362
409
  const hints = this._wakeup.flush()
363
410
 
411
+ if (this._notifyHandler) {
412
+ for await (const key of this._notifyHandler.updates({ since: this.previousDrain })) {
413
+ const hex = b4a.toString(key, 'hex')
414
+ hints.set(hex, -1)
415
+ }
416
+ }
417
+
418
+ this.previousDrain = Date.now()
419
+ this.queueWakeupFastForward(hints).catch(noop)
420
+
364
421
  for (const [hex, length] of hints) {
365
422
  const key = b4a.from(hex, 'hex')
366
423
  if (this.writers.has(hex)) continue
@@ -372,6 +429,25 @@ module.exports = class Autobee extends ReadyResource {
372
429
  }
373
430
  }
374
431
 
432
+ _initFastForward({ key, length }) {
433
+ this.moveTo({ key, length })
434
+ }
435
+
436
+ async _getOplog(key, length) {
437
+ const core = this.openCore(key)
438
+ await core.ready()
439
+
440
+ const target = length >= 0 ? length : core.length
441
+ if (target === 0) return null
442
+
443
+ const buf = await core.get(target - 1)
444
+ await core.close()
445
+
446
+ if (buf === null) return null
447
+
448
+ return encoding.decodeOplog(buf)
449
+ }
450
+
375
451
  _update(changes) {
376
452
  this._needsUpdate = false
377
453
  this.bee.update(this._workingBee.root)
@@ -407,7 +483,7 @@ module.exports = class Autobee extends ReadyResource {
407
483
 
408
484
  let runs = 0
409
485
  while (!this._interrupting && this.appending && runs++ < 16) await this.update()
410
- await this.bumpSoon()
486
+ this.bumpSoon()
411
487
  }
412
488
 
413
489
  async _rotateLocalWriter(newLocal) {
@@ -573,6 +649,10 @@ module.exports = class Autobee extends ReadyResource {
573
649
  this._workingBee.move(t.view)
574
650
  }
575
651
 
652
+ return this._processApplyBatch(t)
653
+ }
654
+
655
+ async _processApplyBatch(t) {
576
656
  // first writer is always added with full permissions
577
657
  if (this.system.isGenesis()) {
578
658
  await this._host.addWriter(t.tip[0][0].key)
@@ -609,9 +689,20 @@ module.exports = class Autobee extends ReadyResource {
609
689
  }
610
690
 
611
691
  _storeBoot() {
692
+ const proms = []
693
+ proms.push(
694
+ this.local.setUserData(
695
+ 'autobee/previous-drain',
696
+ encoding.encodePreviousDrain(this.previousDrain)
697
+ )
698
+ )
699
+
612
700
  const boot = this.system.bootRecord()
613
- if (!boot) return
614
- return this.local.setUserData('autobee/head', encoding.encodeBootRecord(boot))
701
+ if (boot) {
702
+ proms.push(this.local.setUserData('autobee/head', encoding.encodeBootRecord(boot)))
703
+ }
704
+
705
+ return Promise.all(proms)
615
706
  }
616
707
 
617
708
  static decodeValue(buf, opts) {
@@ -623,7 +714,7 @@ module.exports = class Autobee extends ReadyResource {
623
714
  }
624
715
 
625
716
  async wakeup({ key, length }) {
626
- await this._bootingState
717
+ if (!this._bootGuard.opened) await this._bootGuard.ready()
627
718
  await this.writers.wakeup(key, length)
628
719
  await this._bump()
629
720
  }
@@ -655,8 +746,119 @@ module.exports = class Autobee extends ReadyResource {
655
746
  await this.writers.flushLocal(this._workingBee.head())
656
747
  }
657
748
 
658
- async replay() {
659
- return topo.replay(this)
749
+ moveTo(head, tip) {
750
+ if (this.fastForwardTo !== null || this.fastForwarding !== null) return null
751
+ return this._runFastForward(new FastForward(this, head, tip)).catch(noop)
752
+ }
753
+
754
+ async queueWakeupFastForward(hints) {
755
+ if (!this._handlers.onwakeup) return false
756
+ if (!hints.size || this.fastForwarding || this.fastForwardTo || this.fastForwardBoot) {
757
+ return false
758
+ }
759
+
760
+ const promises = []
761
+ for (const [hex, length] of hints) {
762
+ if (length === 0) continue
763
+ const key = b4a.from(hex, 'hex')
764
+ promises.push(this._getOplog(key, length))
765
+ }
766
+
767
+ const ops = await Promise.all(promises)
768
+ if (this.fastForwarding || this.fastForwardTo) return false
769
+
770
+ let best = null
771
+ let bestFlushes = -1
772
+
773
+ for (const msg of ops) {
774
+ if (msg === null) continue
775
+
776
+ if (msg.views && msg.views.flushes > bestFlushes) {
777
+ bestFlushes = msg.views.flushes
778
+ best = msg.views
779
+ }
780
+ }
781
+
782
+ if (best === null || bestFlushes - this.system.flushes < MIN_FF_GAP) return false
783
+
784
+ const view = this.bee.checkout(best.view)
785
+
786
+ let trusted = null
787
+ try {
788
+ trusted = await this._handlers.onwakeup(view, this)
789
+ if (!trusted || this.fastForwarding || this.fastForwardTo) return false
790
+ } finally {
791
+ view.close()
792
+ }
793
+
794
+ const oplog = await this._getOplog(trusted.key, trusted.length)
795
+
796
+ return this.moveTo(oplog.views.system, {
797
+ system: best.system,
798
+ verified: {
799
+ node: trusted,
800
+ flushes: oplog.views.flushes
801
+ }
802
+ })
803
+ }
804
+
805
+ async _runFastForward(ff) {
806
+ this.fastForwarding = ff
807
+
808
+ const result = await ff.upgrade()
809
+ await ff.close()
810
+
811
+ if (this.fastForwarding === ff) this.fastForwarding = null
812
+
813
+ if (!result) return null
814
+
815
+ this.fastForwardTo = result
816
+ this.fastForward = rrp()
817
+
818
+ this.bumpSoon()
819
+
820
+ return this.fastForward.promise
821
+ }
822
+
823
+ async _applyFastForward() {
824
+ const changes = this._hasUpdate ? new UpdateChanges(this) : null
825
+ if (changes) changes.track(this._applyState)
826
+
827
+ const { head, tip } = this.fastForwardTo
828
+
829
+ const from = this.system.bee.head()
830
+ const to = head
831
+
832
+ this.system.bee.move(head)
833
+ await this.system.reset()
834
+
835
+ this.bee.move(this.system.view)
836
+ this._workingBee.move(this.system.view)
837
+
838
+ this.fastForwardTo = null
839
+
840
+ await this.writers.refresh()
841
+
842
+ if (changes) {
843
+ changes.finalise()
844
+ await this._handlers.update(this.view, changes)
845
+ }
846
+
847
+ this.emit('move-to', to, from)
848
+ this.fastForward.resolve({ to, from })
849
+
850
+ // tip is null when handlers.fastForward set
851
+ if (!tip) return
852
+
853
+ return this._reapply(tip)
854
+ }
855
+
856
+ async _reapply({ system, verified }) {
857
+ const sys = this.system.bee.checkout(system)
858
+ const t = await topo.rollback(this, sys, verified)
859
+ await sys.close()
860
+
861
+ return this._processApplyBatch(t)
660
862
  }
661
863
  }
662
864
 
package/lib/boot.js CHANGED
@@ -11,7 +11,8 @@ module.exports = async function boot(
11
11
  bootstrap: null,
12
12
  encryptionKey: null,
13
13
  system: null,
14
- flushes: null
14
+ flushes: null,
15
+ previousDrain: 0
15
16
  }
16
17
 
17
18
  const manifest = keyPair
@@ -91,15 +92,15 @@ module.exports = async function boot(
91
92
  await result.local.setUserData('referrer', result.key)
92
93
  }
93
94
 
94
- const [systemHead, encryptionKeyBuffer] = await Promise.all([
95
+ const [systemHead, encryptionKeyBuffer, prevDrainBuffer] = await Promise.all([
95
96
  result.local.getUserData('autobee/head'),
96
- result.local.getUserData('autobee/encryption')
97
+ result.local.getUserData('autobee/encryption'),
98
+ result.local.getUserData('autobee/previous-drain')
97
99
  ])
98
100
 
99
101
  if (systemHead) result.system = encoding.decodeBootRecord(systemHead)
100
- if (encryptionKeyBuffer) {
101
- result.encryptionKey = encryptionKeyBuffer
102
- }
102
+ if (encryptionKeyBuffer) result.encryptionKey = encryptionKeyBuffer
103
+ if (prevDrainBuffer) result.previousDrain = encoding.decodePreviousDrain(prevDrainBuffer)
103
104
 
104
105
  if (!result.encryptionKey && (encryptionKey || encrypt)) {
105
106
  if (!encryptionKey) {
package/lib/encoding.js CHANGED
@@ -18,6 +18,8 @@ exports.Oplog = Oplog
18
18
  exports.ManifestData = ManifestData
19
19
  exports.encodeBootRecord = encodeBootRecord
20
20
  exports.decodeBootRecord = decodeBootRecord
21
+ exports.encodePreviousDrain = encodePreviousDrain
22
+ exports.decodePreviousDrain = decodePreviousDrain
21
23
  exports.encodeSystemWriter = encodeSystemWriter
22
24
  exports.decodeSystemWriter = decodeSystemWriter
23
25
 
@@ -29,6 +31,14 @@ function decodeBootRecord(value) {
29
31
  return c.decode(Link, value)
30
32
  }
31
33
 
34
+ function encodePreviousDrain(d) {
35
+ return c.encode(c.uint, d)
36
+ }
37
+
38
+ function decodePreviousDrain(value) {
39
+ return c.decode(c.uint, value)
40
+ }
41
+
32
42
  function encodeSystemWriter(m) {
33
43
  return c.encode(SystemWriter, m)
34
44
  }
@@ -0,0 +1,71 @@
1
+ const safetyCatch = require('safety-catch')
2
+
3
+ const System = require('./system.js')
4
+
5
+ const DEFAULT_OP_TIMEOUT = 5_000
6
+
7
+ module.exports = class FastForward {
8
+ constructor(auto, head, tip, { timeout = DEFAULT_OP_TIMEOUT } = {}) {
9
+ this.auto = auto
10
+ this.system = new System(auto.store.namespace('fast-forward'), null, {
11
+ getEncryptionProvider: this.auto._getEncryptionProviderBound,
12
+ encrypted: this.encrypted
13
+ })
14
+
15
+ this.head = head
16
+ this.timeout = timeout
17
+ this.tip = tip || null
18
+ this.destroyed = false
19
+ this.upgrading = null
20
+ this.failed = false
21
+ this.cores = []
22
+ }
23
+
24
+ async upgrade() {
25
+ try {
26
+ if (!this.upgrading) this.upgrading = this._upgrade()
27
+
28
+ if (!(await this.upgrading)) return null
29
+
30
+ return {
31
+ head: this.head,
32
+ tip: this.tip
33
+ }
34
+ } catch (err) {
35
+ safetyCatch(err)
36
+ this.failed = true
37
+ return null
38
+ } finally {
39
+ await this.close()
40
+ }
41
+ }
42
+
43
+ async _upgrade() {
44
+ await this.system.boot(this.head)
45
+
46
+ const promises = []
47
+
48
+ // ensure local key is locally available always
49
+ promises.push(this.system.get(this.auto.local.key, { timeout: this.timeout }))
50
+
51
+ const view = this.auto.store.get({ key: this.system.view.key, active: true })
52
+ this.cores.push(view)
53
+
54
+ promises.push(view.get(this.system.view.length - 1, { timeout: this.timeout }))
55
+
56
+ for (const head of this.system.heads) {
57
+ promises.push(this.system.get(head.key, { timeout: this.timeout }))
58
+ }
59
+
60
+ await Promise.all(promises)
61
+ if (this.destroyed) return false
62
+
63
+ return true
64
+ }
65
+
66
+ async close() {
67
+ this.destroyed = true
68
+ if (this.system) await this.system.close()
69
+ for (const core of this.cores) await core.close()
70
+ }
71
+ }
package/lib/topo.js CHANGED
@@ -5,8 +5,8 @@ const asserts = require('./asserts.js')
5
5
 
6
6
  const EMPTY_HEAD = { length: 0, key: null }
7
7
 
8
- exports.replay = replay
9
8
  exports.sort = sort
9
+ exports.rollback = rollback
10
10
 
11
11
  exports.isLinking = isLinking
12
12
  exports.isLinkingAll = isLinkingAll
@@ -29,17 +29,29 @@ class Clock {
29
29
  }
30
30
  }
31
31
 
32
- async function replay(auto) {
33
- const ch = auto.system.bee.createChangesStream()
34
- const replay = []
32
+ async function rollback(auto, system, verified) {
33
+ const tip = []
34
+ const ch = system.createChangesStream()
35
35
 
36
36
  for await (const data of ch) {
37
37
  const { ref, info } = getOplog(data)
38
- replay.push(ref)
38
+ if (sameNode(ref.oplog, verified.node)) {
39
+ asserts.assert(info.flushes === verified.flushes, 'Expect tip to be continuous')
40
+ break
41
+ }
42
+
43
+ tip.push(ref)
39
44
  }
40
45
 
41
- const inflated = await inflate(auto, replay.reverse())
42
- return inflated.flatMap((b) => b.batch)
46
+ const inflated = await inflate(auto, tip)
47
+
48
+ // @todo: verify sort
49
+
50
+ return {
51
+ undo: null,
52
+ view: null,
53
+ tip: inflated.map(getBatch)
54
+ }
43
55
  }
44
56
 
45
57
  async function sort(auto, batch) {
@@ -269,3 +281,11 @@ function isLinkingAll(node, heads) {
269
281
 
270
282
  return true
271
283
  }
284
+
285
+ function sameNode(a, b) {
286
+ return b4a.equals(a.key, b.key) && a.length === b.length
287
+ }
288
+
289
+ function getBatch(b) {
290
+ return b.batch
291
+ }
package/lib/writers.js CHANGED
@@ -37,7 +37,7 @@ class ActiveWriters {
37
37
  }
38
38
 
39
39
  bump() {
40
- this.auto._bump()
40
+ this.auto.bumpSoon()
41
41
  }
42
42
 
43
43
  async wakeup(key, length) {
@@ -66,6 +66,7 @@ class ActiveWriters {
66
66
 
67
67
  this.localWriter = new Writer(this, this.auto.system, true, core, b4a.toString(core.key, 'hex'))
68
68
  this.localWriter.addPending()
69
+ this.auto.emit('writer', this.localWriter)
69
70
  }
70
71
 
71
72
  clearLocal() {
@@ -76,6 +77,20 @@ class ActiveWriters {
76
77
  return this.localWriter.flush(head)
77
78
  }
78
79
 
80
+ async refresh() {
81
+ if (this.localWriter && !this.localWriter.closed) {
82
+ await this.updateLocalState()
83
+ }
84
+
85
+ for (const writer of this.active.values()) {
86
+ await writer.update()
87
+
88
+ if (writer !== this.localWriter && !writer.isIndexer) {
89
+ await writer.detachAndClose()
90
+ }
91
+ }
92
+ }
93
+
79
94
  async updateLocalState() {
80
95
  const w = this.localWriter
81
96
  const bootstrapping = w.core.length === 0 && b4a.equals(this.auto.key, w.core.key)
@@ -96,7 +111,8 @@ class ActiveWriters {
96
111
  }
97
112
 
98
113
  async add(key) {
99
- if (b4a.equals(key, this.auto.local.key)) await this.updateLocalState()
114
+ const local = b4a.equals(key, this.auto.local.key)
115
+ if (local) await this.updateLocalState()
100
116
 
101
117
  const id = b4a.toString(key, 'hex')
102
118
  let w = this.active.get(id)
@@ -109,8 +125,10 @@ class ActiveWriters {
109
125
 
110
126
  const core = this.auto.store.get({
111
127
  key,
112
- encryption
128
+ encryption,
129
+ group: local || !this.auto.wakeupCapability ? null : this.auto.wakeupCapability.discoveryKey
113
130
  })
131
+
114
132
  await core.ready()
115
133
  await core.setUserData('referrer', this.auto.key)
116
134
 
@@ -239,13 +257,14 @@ class Writer {
239
257
  if (this.isAttached) return
240
258
  this.isAttached = true
241
259
  this.writers.active.set(this.id, this)
260
+ this.writers.auto.emit('writer', this)
242
261
  this.addPending()
243
262
  }
244
263
 
245
264
  detach() {
246
265
  if (!this.isAttached) return
247
266
  this.isAttached = false
248
- this.download.destroy()
267
+ if (this.download) this.download.destroy()
249
268
  this.download = null
250
269
  this.writers.active.delete(this.id)
251
270
  this.removePending()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autobee",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Bee based autobase",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Holepunch Inc.",
@@ -28,15 +28,17 @@
28
28
  "b4a": "^1.8.0",
29
29
  "compact-encoding": "^2.18.0",
30
30
  "hyperbee2": "^2.8.0",
31
- "hypercore": "^11.28.1",
31
+ "hypercore": "^11.33.0",
32
32
  "hypercore-id-encoding": "^1.3.0",
33
33
  "hyperschema": "^1.20.1",
34
+ "ready-guard": "^1.0.0",
34
35
  "ready-resource": "^1.2.0",
36
+ "resolve-reject-promise": "^1.1.0",
35
37
  "safety-catch": "^1.0.3"
36
38
  },
37
39
  "devDependencies": {
38
40
  "brittle": "^3.19.1",
39
- "corestore": "^7.7.0",
41
+ "corestore": "^7.10.0",
40
42
  "hypercore-crypto": "^3.6.1",
41
43
  "lunte": "^1.6.0",
42
44
  "prettier": "^3.8.1",