autobee 0.0.0 → 1.0.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.
- package/README.md +255 -0
- package/index.js +694 -0
- package/lib/apply-calls.js +65 -0
- package/lib/asserts.js +25 -0
- package/lib/boot.js +117 -0
- package/lib/constants.js +9 -0
- package/lib/encoding.js +150 -0
- package/lib/system.js +327 -0
- package/lib/topo.js +271 -0
- package/lib/triggers.js +69 -0
- package/lib/updates.js +31 -0
- package/lib/writers.js +501 -0
- package/package.json +39 -6
package/lib/writers.js
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
const b4a = require('b4a')
|
|
2
|
+
const encoding = require('./encoding.js')
|
|
3
|
+
const Triggers = require('./triggers.js')
|
|
4
|
+
const { WriterEncryption } = require('autobee-encryption')
|
|
5
|
+
|
|
6
|
+
const WRITER_PREFETCH = 10
|
|
7
|
+
|
|
8
|
+
const BATCH_WRITER_SANITY = 1024
|
|
9
|
+
const BATCH_OPTIMISTIC_SANITY = 32
|
|
10
|
+
|
|
11
|
+
const MAX_FORWARD_TIME_DRIFT = 2 * 3600 * 1000
|
|
12
|
+
|
|
13
|
+
class ActiveWriters {
|
|
14
|
+
constructor(auto) {
|
|
15
|
+
const local = auto.local
|
|
16
|
+
|
|
17
|
+
this.auto = auto
|
|
18
|
+
this.active = new Map()
|
|
19
|
+
this.triggers = new Triggers()
|
|
20
|
+
this.localWriter = new Writer(this, auto.system, true, local, b4a.toString(local.key, 'hex'))
|
|
21
|
+
this.writable = false
|
|
22
|
+
this.pending = []
|
|
23
|
+
|
|
24
|
+
this.localWriter.attach()
|
|
25
|
+
|
|
26
|
+
this._bootstrap()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
[Symbol.iterator]() {
|
|
30
|
+
return this.active.values()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
*external() {
|
|
34
|
+
for (const w of this.active.values()) {
|
|
35
|
+
if (w !== this.localWriter) yield w
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
bump() {
|
|
40
|
+
this.auto._bump()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async wakeup(key, length) {
|
|
44
|
+
const id = b4a.toString(key, 'hex')
|
|
45
|
+
let w = this.active.get(id)
|
|
46
|
+
|
|
47
|
+
if (w === this.localWriter) return
|
|
48
|
+
if (!w) w = await this.add(key)
|
|
49
|
+
|
|
50
|
+
// TODO: signal that we've seen length on w for gc
|
|
51
|
+
|
|
52
|
+
this.bump()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getLatestLocalOplog() {
|
|
56
|
+
return this.localWriter.getLatest()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
appendLocal(value, timestamp, batch, links, optimistic) {
|
|
60
|
+
return this.localWriter.append(value, timestamp, batch, links, optimistic)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async rotateLocalWriter(core) {
|
|
64
|
+
this.clearLocal()
|
|
65
|
+
await this.localWriter.detachAndClose()
|
|
66
|
+
|
|
67
|
+
this.localWriter = new Writer(this, this.auto.system, true, core, b4a.toString(core.key, 'hex'))
|
|
68
|
+
this.localWriter.addPending()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
clearLocal() {
|
|
72
|
+
return this.localWriter.clear()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
flushLocal(head) {
|
|
76
|
+
return this.localWriter.flush(head)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async updateLocalState() {
|
|
80
|
+
const w = this.localWriter
|
|
81
|
+
const bootstrapping = w.core.length === 0 && b4a.equals(this.auto.key, w.core.key)
|
|
82
|
+
const info = await this.auto.system.get(this.auto.local.key)
|
|
83
|
+
const writable = this.writable
|
|
84
|
+
|
|
85
|
+
w.weight = info ? info.weight : 0
|
|
86
|
+
this.writable = info ? !info.isRemoved : bootstrapping
|
|
87
|
+
|
|
88
|
+
if (writable === this.writable) return
|
|
89
|
+
|
|
90
|
+
this.auto.emit(this.writable ? 'writable' : 'unwritable')
|
|
91
|
+
this.auto.emit('update')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
has(id) {
|
|
95
|
+
return !!this.active.get(id)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async add(key) {
|
|
99
|
+
if (b4a.equals(key, this.auto.local.key)) await this.updateLocalState()
|
|
100
|
+
|
|
101
|
+
const id = b4a.toString(key, 'hex')
|
|
102
|
+
let w = this.active.get(id)
|
|
103
|
+
if (w) {
|
|
104
|
+
w.couple() // couple if we weren't ready first time
|
|
105
|
+
return w
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const encryption = this.auto.encryptionKey ? new WriterEncryption(this.auto) : null
|
|
109
|
+
|
|
110
|
+
const core = this.auto.store.get({
|
|
111
|
+
key,
|
|
112
|
+
encryption
|
|
113
|
+
})
|
|
114
|
+
await core.ready()
|
|
115
|
+
await core.setUserData('referrer', this.auto.key)
|
|
116
|
+
|
|
117
|
+
w = new Writer(this, this.auto.system, false, core, id)
|
|
118
|
+
w.attach()
|
|
119
|
+
w.couple()
|
|
120
|
+
|
|
121
|
+
return w
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async remove(key) {
|
|
125
|
+
if (b4a.equals(key, this.auto.local.key)) await this.updateLocalState()
|
|
126
|
+
|
|
127
|
+
const id = b4a.toString(key, 'hex')
|
|
128
|
+
const w = this.active.get(id)
|
|
129
|
+
if (!w) return
|
|
130
|
+
|
|
131
|
+
w.detach()
|
|
132
|
+
w.decouple()
|
|
133
|
+
if (w !== this.localWriter) await w.close()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
_bootstrap() {
|
|
137
|
+
const bootstrap = this.auto.bootstrap
|
|
138
|
+
|
|
139
|
+
bootstrap.setEncryption(this.auto._getEncryptionProvider())
|
|
140
|
+
bootstrap.setActive(true)
|
|
141
|
+
|
|
142
|
+
const id = b4a.toString(bootstrap.key, 'hex')
|
|
143
|
+
let w = this.active.get(id)
|
|
144
|
+
if (w) {
|
|
145
|
+
w.couple() // couple if we weren't ready first time
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
w = new Writer(this, this.auto.system, false, bootstrap, id)
|
|
150
|
+
w.attach()
|
|
151
|
+
w.couple()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
class Writer {
|
|
156
|
+
constructor(writers, system, local, core, id) {
|
|
157
|
+
this.writers = writers
|
|
158
|
+
this.system = system
|
|
159
|
+
|
|
160
|
+
this.core = core
|
|
161
|
+
this.id = id
|
|
162
|
+
this.index = 0
|
|
163
|
+
this.weight = 0
|
|
164
|
+
this.pending = null
|
|
165
|
+
this.waiting = null
|
|
166
|
+
this.download = null
|
|
167
|
+
|
|
168
|
+
this.isPending = false
|
|
169
|
+
this.isClosed = false
|
|
170
|
+
this.isAttached = false
|
|
171
|
+
this.isAdded = false
|
|
172
|
+
this.isRemoved = false
|
|
173
|
+
this.isCoupled = false
|
|
174
|
+
|
|
175
|
+
this._onchangeBound = this._onchange.bind(this)
|
|
176
|
+
|
|
177
|
+
if (!local) this.core.on('append', this._onchangeBound)
|
|
178
|
+
if (!local) this.core.on('download', this._onchangeBound)
|
|
179
|
+
|
|
180
|
+
this.couple()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
get isIndexer() {
|
|
184
|
+
return this.weight === 2
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
couple() {
|
|
188
|
+
if (this.isCoupled) return
|
|
189
|
+
// if we don't have coupler yet we can't add
|
|
190
|
+
if (this.writers.auto._wakeup.addCore(this.core)) {
|
|
191
|
+
this.isCoupled = true
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
decouple() {
|
|
196
|
+
if (!this.isCoupled) return
|
|
197
|
+
// if we don't have coupler yet we can't remove
|
|
198
|
+
if (this.writers.auto._wakeup.removeCore(this.core)) {
|
|
199
|
+
this.isCoupled = false
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
hasReferrals() {
|
|
204
|
+
const entries = this.writers.triggers.get(this.id)
|
|
205
|
+
if (!entries) return false
|
|
206
|
+
// TODO: should implement a system where we KNOW that a future writers IS not removed
|
|
207
|
+
// to avoid accidental spam, for now kept simple as the algo still runs
|
|
208
|
+
return true
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
addPending() {
|
|
212
|
+
if (this.isPending) return
|
|
213
|
+
this.isPending = true
|
|
214
|
+
this.index = this.writers.pending.push(this) - 1
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
removePending() {
|
|
218
|
+
if (!this.isPending) return
|
|
219
|
+
this.isPending = false
|
|
220
|
+
|
|
221
|
+
const head = this.writers.pending.pop()
|
|
222
|
+
if (head !== this) {
|
|
223
|
+
this.writers.pending[this.index] = head
|
|
224
|
+
head.index = this.index
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_onchange() {
|
|
229
|
+
if (this.waiting === null) this.bump()
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
bump() {
|
|
233
|
+
this.waiting = null
|
|
234
|
+
if (this.isAttached) this.addPending() // TODO: remove the isAttach guard
|
|
235
|
+
this.writers.bump()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
attach() {
|
|
239
|
+
if (this.isAttached) return
|
|
240
|
+
this.isAttached = true
|
|
241
|
+
this.writers.active.set(this.id, this)
|
|
242
|
+
this.addPending()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
detach() {
|
|
246
|
+
if (!this.isAttached) return
|
|
247
|
+
this.isAttached = false
|
|
248
|
+
this.download.destroy()
|
|
249
|
+
this.download = null
|
|
250
|
+
this.writers.active.delete(this.id)
|
|
251
|
+
this.removePending()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
close() {
|
|
255
|
+
this.isClosed = true
|
|
256
|
+
|
|
257
|
+
if (this.waiting) {
|
|
258
|
+
this.writers.triggers.remove(this.waiting)
|
|
259
|
+
this.waiting = null
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.removePending()
|
|
263
|
+
|
|
264
|
+
return this.core.close()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
detachAndClose() {
|
|
268
|
+
this.detach()
|
|
269
|
+
return this.close()
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async views() {
|
|
273
|
+
const latest = await this.getLatest()
|
|
274
|
+
if (!latest || !latest.views) return []
|
|
275
|
+
|
|
276
|
+
// signedLength for autobase compat
|
|
277
|
+
return [
|
|
278
|
+
{ ...latest.views.system, signedLength: latest.views.system.length },
|
|
279
|
+
{ ...latest.views.view, signedLength: latest.views.view.length }
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async getLatest() {
|
|
284
|
+
if (this.core.length === 0) return null
|
|
285
|
+
const block = await this.core.get(this.core.length - 1)
|
|
286
|
+
const oplog = encoding.decodeOplog(block)
|
|
287
|
+
return oplog
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async update() {
|
|
291
|
+
const info = await this.system.get(this.core.key)
|
|
292
|
+
|
|
293
|
+
if (!info) {
|
|
294
|
+
this.isAdded = this.system.isGenesis()
|
|
295
|
+
} else {
|
|
296
|
+
this.isAdded = true
|
|
297
|
+
this.isRemoved = info.isRemoved
|
|
298
|
+
this.weight = info.weight
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return info ? info.length : 0
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async next() {
|
|
305
|
+
if (this.waiting !== null) return null
|
|
306
|
+
|
|
307
|
+
const batch = []
|
|
308
|
+
const genesis = this.system.isGenesis()
|
|
309
|
+
|
|
310
|
+
let length = await this.update()
|
|
311
|
+
let optimistic = false
|
|
312
|
+
let timestamp = 0
|
|
313
|
+
|
|
314
|
+
if (this.download) this.download.destroy()
|
|
315
|
+
this.download = this.core.download({ start: length, end: length + WRITER_PREFETCH })
|
|
316
|
+
|
|
317
|
+
while (length < this.core.length) {
|
|
318
|
+
// @todo aim to prefetch when writer added, with read-ahead
|
|
319
|
+
// download the block if it's not available locally
|
|
320
|
+
// writer will be added back as pending automatically
|
|
321
|
+
if (!(await this.core.has(length))) {
|
|
322
|
+
this.removePending()
|
|
323
|
+
return null
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const block = await this.core.get(length++)
|
|
327
|
+
const oplog = encoding.decodeOplog(block)
|
|
328
|
+
const node = createNode(this.core, length, this.weight, oplog)
|
|
329
|
+
const b = oplog.batch
|
|
330
|
+
|
|
331
|
+
// auto correct some batch invariants
|
|
332
|
+
node.timestamp = timestamp = Math.max(node.timestamp, timestamp)
|
|
333
|
+
node.optimistic = optimistic = optimistic || node.optimistic
|
|
334
|
+
|
|
335
|
+
if (!this.isAdded && !node.optimistic && !genesis) {
|
|
336
|
+
return null
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// an optimistic writer cannot be genesis
|
|
340
|
+
if (node.optimistic && genesis) {
|
|
341
|
+
return null
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (node.optimistic && !this.isAdded && b.end >= BATCH_OPTIMISTIC_SANITY) {
|
|
345
|
+
return null
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (node.timestamp - Date.now() > MAX_FORWARD_TIME_DRIFT) {
|
|
349
|
+
return null
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!node.optimistic && b.start === 0 && b.end > 1 && b.end < BATCH_WRITER_SANITY) {
|
|
353
|
+
this.core.download({ start: length, end: length + oplog.batch.end })
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!(await this._isLinked(node))) {
|
|
357
|
+
this.removePending()
|
|
358
|
+
return null
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
batch.push(node)
|
|
362
|
+
if (b.end === 0) return batch
|
|
363
|
+
|
|
364
|
+
// TODO: mark as dead
|
|
365
|
+
if (batch.length >= BATCH_WRITER_SANITY) return null
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// handle local writer
|
|
369
|
+
if (this.core.writable && this.pending !== null) {
|
|
370
|
+
for (let i = 0; i < this.pending.length; i++) {
|
|
371
|
+
const node = this.pending[i]
|
|
372
|
+
|
|
373
|
+
node.batch = { start: i, end: this.pending.length - 1 - i }
|
|
374
|
+
|
|
375
|
+
if (node.length <= length) continue
|
|
376
|
+
|
|
377
|
+
if (!this.writers.writable && !node.optimistic) {
|
|
378
|
+
this.writers.clearLocal()
|
|
379
|
+
throw new Error('Not writable')
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// an optimistic writer cannot be genesis
|
|
383
|
+
if (node.optimistic && genesis) {
|
|
384
|
+
return null
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (node.optimistic && !this.isAdded && this.pending.length >= BATCH_OPTIMISTIC_SANITY) {
|
|
388
|
+
return null
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (node.timestamp - Date.now() > MAX_FORWARD_TIME_DRIFT) {
|
|
392
|
+
return null
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!(await this._isLinked(node))) return null
|
|
396
|
+
|
|
397
|
+
this.removePending()
|
|
398
|
+
|
|
399
|
+
return this.pending.slice(i)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return null
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
notify(batch) {
|
|
407
|
+
this.writers.triggers.trigger(this.id, batch[batch.length - 1].length)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async _isLinked(node) {
|
|
411
|
+
const promises = new Array(node.links.length)
|
|
412
|
+
|
|
413
|
+
for (let i = 0; i < node.links.length; i++) {
|
|
414
|
+
promises[i] = this.system.has(node.links[i])
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const has = await Promise.all(promises)
|
|
418
|
+
|
|
419
|
+
for (let i = 0; i < node.links.length; i++) {
|
|
420
|
+
if (has[i]) continue
|
|
421
|
+
const link = node.links[i]
|
|
422
|
+
const id = b4a.toString(link.key, 'hex')
|
|
423
|
+
this.waiting = this.writers.triggers.add(id, link.length, this)
|
|
424
|
+
this.writers.wakeup(link.key, link.length).catch(noop)
|
|
425
|
+
return false
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return true
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
get appending() {
|
|
432
|
+
return this.pending !== null
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
append(value, timestamp, batch, links, optimistic) {
|
|
436
|
+
if (this.pending === null) this.pending = []
|
|
437
|
+
|
|
438
|
+
const oplog = {
|
|
439
|
+
version: encoding.OPLOG_VERSION,
|
|
440
|
+
timestamp,
|
|
441
|
+
links,
|
|
442
|
+
batch,
|
|
443
|
+
views: null,
|
|
444
|
+
optimistic,
|
|
445
|
+
value
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const node = createNode(
|
|
449
|
+
this.core,
|
|
450
|
+
this.core.length + this.pending.length + 1,
|
|
451
|
+
this.weight,
|
|
452
|
+
oplog
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
this.pending.push(node)
|
|
456
|
+
this.addPending()
|
|
457
|
+
|
|
458
|
+
return node
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
clear() {
|
|
462
|
+
this.pending = null
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
flush(view) {
|
|
466
|
+
if (this.pending === null) return Promise.resolve()
|
|
467
|
+
const buffers = []
|
|
468
|
+
const s = this.system.bee.head()
|
|
469
|
+
const flushes = this.system.flushes
|
|
470
|
+
this.pending[this.pending.length - 1].views = { system: s, flushes, view }
|
|
471
|
+
for (let i = 0; i < this.pending.length; i++) {
|
|
472
|
+
const node = this.pending[i]
|
|
473
|
+
node.batch = { start: i, end: this.pending.length - 1 - i }
|
|
474
|
+
buffers.push(encoding.encodeOplog(node))
|
|
475
|
+
}
|
|
476
|
+
this.pending = null
|
|
477
|
+
return this.core.append(buffers)
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
exports.ActiveWriters = ActiveWriters
|
|
482
|
+
exports.Writer = Writer
|
|
483
|
+
exports.createNode = createNode
|
|
484
|
+
|
|
485
|
+
function createNode(core, length, weight, oplog) {
|
|
486
|
+
return {
|
|
487
|
+
core,
|
|
488
|
+
key: core.key,
|
|
489
|
+
length,
|
|
490
|
+
weight,
|
|
491
|
+
version: oplog.version,
|
|
492
|
+
timestamp: oplog.timestamp,
|
|
493
|
+
links: oplog.links,
|
|
494
|
+
batch: oplog.batch,
|
|
495
|
+
views: oplog.views,
|
|
496
|
+
optimistic: oplog.optimistic,
|
|
497
|
+
value: oplog.value
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function noop() {}
|
package/package.json
CHANGED
|
@@ -1,12 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autobee",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Bee based autobase",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Holepunch Inc.",
|
|
5
7
|
"main": "index.js",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/holepunchto/autobee.git"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"index.js",
|
|
14
|
+
"lib/*.js",
|
|
15
|
+
"spec"
|
|
16
|
+
],
|
|
6
17
|
"scripts": {
|
|
7
|
-
"
|
|
18
|
+
"format": "prettier . --write",
|
|
19
|
+
"lint": "lunte && prettier . --check",
|
|
20
|
+
"test": "node test/all.js",
|
|
21
|
+
"test:bare": "bare test/all.js",
|
|
22
|
+
"test:encrypted": "bare test/all.js --encrypt-all",
|
|
23
|
+
"test:generate": "brittle -r test/all.js test/*.js"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"autobee-encryption": "^0.0.5",
|
|
27
|
+
"autobee-wakeup": "^1.0.5",
|
|
28
|
+
"b4a": "^1.8.0",
|
|
29
|
+
"compact-encoding": "^2.18.0",
|
|
30
|
+
"hyperbee2": "^2.8.0",
|
|
31
|
+
"hypercore": "^11.28.1",
|
|
32
|
+
"hypercore-id-encoding": "^1.3.0",
|
|
33
|
+
"hyperschema": "^1.20.1",
|
|
34
|
+
"ready-resource": "^1.2.0",
|
|
35
|
+
"safety-catch": "^1.0.3"
|
|
8
36
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"brittle": "^3.19.1",
|
|
39
|
+
"corestore": "^7.7.0",
|
|
40
|
+
"hypercore-crypto": "^3.6.1",
|
|
41
|
+
"lunte": "^1.6.0",
|
|
42
|
+
"prettier": "^3.8.1",
|
|
43
|
+
"prettier-config-holepunch": "^2.0.0"
|
|
44
|
+
}
|
|
12
45
|
}
|