@cero-base/core 1.12.0 → 1.14.0
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/package.json +2 -10
- package/src/database/bootstrap.js +6 -4
- package/src/database/changes.js +29 -55
- package/src/database/encryption.js +35 -46
- package/src/database/index.js +51 -55
- package/src/lib/constants.js +14 -0
- package/src/lib/utils.js +19 -0
- package/src/network/index.js +40 -29
- package/src/pairing/index.js +8 -2
- package/src/rpc/peer.js +4 -1
- package/src/storage/index.js +23 -12
- package/types/database/changes.d.ts +6 -5
- package/types/database/encryption.d.ts +4 -3
- package/types/database/index.d.ts +10 -5
- package/types/lib/constants.d.ts +1 -0
- package/types/lib/utils.d.ts +1 -0
- package/types/network/index.d.ts +3 -5
- package/src/network/transports/ble.js +0 -1109
- package/src/network/transports/dht.js +0 -110
- package/src/network/transports/gatt.js +0 -42
- package/src/network/transports/l2cap.js +0 -90
- package/types/network/transports/ble.d.ts +0 -180
- package/types/network/transports/dht.d.ts +0 -75
- package/types/network/transports/gatt.d.ts +0 -21
- package/types/network/transports/l2cap.d.ts +0 -37
package/src/network/index.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import NoiseSecretStream from '@hyperswarm/secret-stream'
|
|
2
|
+
import Autobee from 'autobee'
|
|
2
3
|
import BlindPairing from 'blind-pairing'
|
|
3
4
|
import BlindPeering from 'blind-peering'
|
|
4
5
|
import ProtomuxWakeup from 'protomux-wakeup'
|
|
5
6
|
import ReadyResource from 'ready-resource'
|
|
6
7
|
import safetyCatch from 'safety-catch'
|
|
7
8
|
import { decode as decodeKey } from 'hypercore-id-encoding'
|
|
9
|
+
import { hash } from 'hypercore-crypto'
|
|
10
|
+
import Hyperswarm from 'hyperswarm'
|
|
8
11
|
import b4a from 'b4a'
|
|
9
12
|
|
|
10
13
|
import { ACTIVE, PASSIVE } from '../lib/constants.js'
|
|
11
14
|
import { CeroError } from '../lib/errors.js'
|
|
12
15
|
import { Discovery } from './discovery.js'
|
|
13
|
-
import { DHTTransport, channelTopic } from './transports/dht.js'
|
|
14
16
|
|
|
15
|
-
export
|
|
17
|
+
export function channelTopic(topic, channel) {
|
|
18
|
+
return channel ? hash([topic, b4a.from(channel)]) : topic
|
|
19
|
+
}
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* @typedef {object} NetworkOpts
|
|
@@ -43,7 +47,7 @@ export class Network extends ReadyResource {
|
|
|
43
47
|
this.store = store || null
|
|
44
48
|
this.mirrors = (mirrors || []).map((k) => (typeof k === 'string' ? decodeKey(k) : k))
|
|
45
49
|
|
|
46
|
-
this.
|
|
50
|
+
this._swarm = null
|
|
47
51
|
this.wakeup = new ProtomuxWakeup()
|
|
48
52
|
|
|
49
53
|
this._replicateables = new Set()
|
|
@@ -55,7 +59,7 @@ export class Network extends ReadyResource {
|
|
|
55
59
|
|
|
56
60
|
/** @returns {any} The underlying hyperswarm, or null before ready / after close. */
|
|
57
61
|
get swarm() {
|
|
58
|
-
return this.
|
|
62
|
+
return this._swarm
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/**
|
|
@@ -108,6 +112,7 @@ export class Network extends ReadyResource {
|
|
|
108
112
|
* @returns {Promise<any>}
|
|
109
113
|
*/
|
|
110
114
|
async blind() {
|
|
115
|
+
if (this.closing || this.closed) throw CeroError.CLOSED('Network')
|
|
111
116
|
if (!this._blind) {
|
|
112
117
|
const blind = new BlindPairing(this.swarm)
|
|
113
118
|
this._blind = blind.ready().then(() => {
|
|
@@ -156,17 +161,15 @@ export class Network extends ReadyResource {
|
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
async _open() {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
const opts = {}
|
|
165
|
+
if (this.identity) {
|
|
166
|
+
opts.keyPair = { publicKey: this.identity.publicKey, secretKey: this.identity.secretKey }
|
|
167
|
+
}
|
|
168
|
+
if (this.bootstrap) opts.bootstrap = this.bootstrap
|
|
169
|
+
if (this.firewall) opts.firewall = this.firewall
|
|
170
|
+
if (this.relayThrough) opts.relayThrough = this.relayThrough
|
|
166
171
|
|
|
167
|
-
|
|
168
|
-
// because the handlers touch Network state (wakeup, replicateables, emit).
|
|
169
|
-
const swarm = this._dht.swarm
|
|
172
|
+
const swarm = (this._swarm = new Hyperswarm(opts))
|
|
170
173
|
swarm.on('connection', (stream, info) => {
|
|
171
174
|
if (this.closing || this.closed) {
|
|
172
175
|
stream.destroy()
|
|
@@ -176,12 +179,10 @@ export class Network extends ReadyResource {
|
|
|
176
179
|
for (const r of this._replicateables) replicateInto(r, stream)
|
|
177
180
|
this.emit('connection', stream, info)
|
|
178
181
|
})
|
|
179
|
-
swarm.on('peer-add', (peer) => this.emit('peer-add', peer))
|
|
180
|
-
swarm.on('peer-remove', (peer) => this.emit('peer-remove', peer))
|
|
181
182
|
|
|
182
183
|
if (this.store && this.mirrors.length) {
|
|
183
184
|
this._blindPeering = new BlindPeering(swarm.dht, this.store, {
|
|
184
|
-
|
|
185
|
+
blindPeers: this.mirrors.map((key) => ({ key })),
|
|
185
186
|
wakeup: this.wakeup,
|
|
186
187
|
pick: 2
|
|
187
188
|
})
|
|
@@ -194,8 +195,9 @@ export class Network extends ReadyResource {
|
|
|
194
195
|
* @param {{ timeout?: number }} [opts]
|
|
195
196
|
* @returns {Promise<void>}
|
|
196
197
|
*/
|
|
197
|
-
async flush(
|
|
198
|
-
|
|
198
|
+
async flush({ timeout = 500 } = {}) {
|
|
199
|
+
if (!this._swarm) return
|
|
200
|
+
await Promise.race([this._swarm.flush(), new Promise((r) => setTimeout(r, timeout))])
|
|
199
201
|
}
|
|
200
202
|
|
|
201
203
|
/**
|
|
@@ -206,7 +208,11 @@ export class Network extends ReadyResource {
|
|
|
206
208
|
async suspend() {
|
|
207
209
|
if (this.closing || this.closed) return
|
|
208
210
|
await this._blindPeering?.suspend()
|
|
209
|
-
|
|
211
|
+
try {
|
|
212
|
+
await this._swarm?.suspend()
|
|
213
|
+
} catch (err) {
|
|
214
|
+
safetyCatch(err)
|
|
215
|
+
}
|
|
210
216
|
}
|
|
211
217
|
|
|
212
218
|
/**
|
|
@@ -216,7 +222,11 @@ export class Network extends ReadyResource {
|
|
|
216
222
|
*/
|
|
217
223
|
async resume() {
|
|
218
224
|
if (this.closing || this.closed) return
|
|
219
|
-
|
|
225
|
+
try {
|
|
226
|
+
await this._swarm?.resume()
|
|
227
|
+
} catch (err) {
|
|
228
|
+
safetyCatch(err)
|
|
229
|
+
}
|
|
220
230
|
await this._blindPeering?.resume()
|
|
221
231
|
}
|
|
222
232
|
|
|
@@ -256,13 +266,14 @@ export class Network extends ReadyResource {
|
|
|
256
266
|
this._blind = null
|
|
257
267
|
}
|
|
258
268
|
|
|
259
|
-
if (this.
|
|
269
|
+
if (this._swarm) {
|
|
260
270
|
try {
|
|
261
|
-
await this.
|
|
271
|
+
await this.flush()
|
|
272
|
+
await this._swarm.destroy()
|
|
262
273
|
} catch (err) {
|
|
263
274
|
safetyCatch(err)
|
|
264
275
|
}
|
|
265
|
-
this.
|
|
276
|
+
this._swarm = null
|
|
266
277
|
}
|
|
267
278
|
|
|
268
279
|
if (this.wakeup) {
|
|
@@ -289,10 +300,10 @@ export class Network extends ReadyResource {
|
|
|
289
300
|
if (!this.swarm) throw CeroError.NOT_READY('Network', 'network')
|
|
290
301
|
if (!isTopic(topic)) throw CeroError.INVALID('topic must be a 32-byte buffer')
|
|
291
302
|
|
|
292
|
-
const session =
|
|
293
|
-
mode === ACTIVE
|
|
294
|
-
|
|
295
|
-
|
|
303
|
+
const session = this.swarm.join(channelTopic(topic, this.channel), {
|
|
304
|
+
client: mode === ACTIVE,
|
|
305
|
+
server: true
|
|
306
|
+
})
|
|
296
307
|
|
|
297
308
|
const discovery = new Discovery(this, session, mode)
|
|
298
309
|
this._discoveries.add(discovery)
|
|
@@ -313,7 +324,7 @@ export class Network extends ReadyResource {
|
|
|
313
324
|
// mirror the core so it stays available when its writers are offline —
|
|
314
325
|
// autobees announce their whole writer set, plain cores (blobs) just themselves
|
|
315
326
|
if (this._blindPeering) {
|
|
316
|
-
if (core
|
|
327
|
+
if (Autobee.isAutobee(core)) this._blindPeering.addAutobaseBackground(core)
|
|
317
328
|
else this._blindPeering.addCoreBackground(core)
|
|
318
329
|
}
|
|
319
330
|
}
|
package/src/pairing/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import c from 'compact-encoding'
|
|
|
6
6
|
import { discoveryKey, keyPair, sign } from 'hypercore-crypto'
|
|
7
7
|
|
|
8
8
|
import { Invite } from './invite.js'
|
|
9
|
+
import { channelTopic } from '../network/index.js'
|
|
9
10
|
import { getEncoding } from '../lib/spec/index.js'
|
|
10
11
|
import { CeroError } from '../lib/errors.js'
|
|
11
12
|
|
|
@@ -103,7 +104,8 @@ export class Pairing extends ReadyResource {
|
|
|
103
104
|
// identity throw 'Active member already exist'
|
|
104
105
|
if (this.host) {
|
|
105
106
|
this._member = this._blind.addMember({
|
|
106
|
-
|
|
107
|
+
// channel-scoped; the candidate hashes identically (no channel → identity)
|
|
108
|
+
discoveryKey: channelTopic(discoveryKey(this.topic), this.network.channel),
|
|
107
109
|
onadd: (req) => this._onCandidate(req).catch(this._onerror)
|
|
108
110
|
})
|
|
109
111
|
}
|
|
@@ -486,6 +488,8 @@ class Candidate {
|
|
|
486
488
|
this._candidate = this.pairing._blind.addCandidate({
|
|
487
489
|
invite: this.invite.blind,
|
|
488
490
|
userData: this.userData,
|
|
491
|
+
// must hash exactly like the member side — see Pairing._open
|
|
492
|
+
discoveryKey: channelTopic(this.invite.discoveryKey, this.pairing.network.channel),
|
|
489
493
|
onadd: (result) => this._done(result)
|
|
490
494
|
})
|
|
491
495
|
this._candidate.request.on('rejected', (err) => this._fail(fromBlindError(err)))
|
|
@@ -560,7 +564,9 @@ function decodeUserData(buf, encoding) {
|
|
|
560
564
|
}
|
|
561
565
|
|
|
562
566
|
function fromBlindError(err) {
|
|
563
|
-
|
|
567
|
+
// code-less rejections are protocol failures (undecodable reply, key
|
|
568
|
+
// mismatch, bad signature) — never a host decision, don't report DENIED
|
|
569
|
+
if (!err || !err.code) return CeroError.NETWORK_ERROR(err?.message || 'pairing failed')
|
|
564
570
|
switch (err.code) {
|
|
565
571
|
case 'INVITE_EXPIRED':
|
|
566
572
|
return CeroError.EXPIRED(err.message)
|
package/src/rpc/peer.js
CHANGED
|
@@ -25,8 +25,11 @@ export class RPCPeer extends ReadyResource {
|
|
|
25
25
|
this.ipc = ipc
|
|
26
26
|
this.spec = spec
|
|
27
27
|
this.framed = new FramedStream(ipc)
|
|
28
|
-
this.framed.pause()
|
|
29
28
|
this.rpc = new spec.rpc(this.framed)
|
|
29
|
+
// pause AFTER hrpc attaches its 'data' listener — streamx re-resumes a
|
|
30
|
+
// paused stream the moment a listener attaches, so pausing first holds
|
|
31
|
+
// nothing and early frames would dispatch before the codec is bound
|
|
32
|
+
this.framed.pause()
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
async _open() {
|
package/src/storage/index.js
CHANGED
|
@@ -5,11 +5,19 @@ import Corestore from 'corestore'
|
|
|
5
5
|
import HyperDB from 'hyperdb'
|
|
6
6
|
import ReadyResource from 'ready-resource'
|
|
7
7
|
|
|
8
|
-
import { ROCKS, BEE, SINGLE, COLLECTION } from '../lib/constants.js'
|
|
9
|
-
import { genId, subscribe } from '../lib/utils.js'
|
|
8
|
+
import { ROCKS, BEE, SINGLE, COLLECTION, QUERY_RESERVED } from '../lib/constants.js'
|
|
9
|
+
import { genId, subscribe, searchHit } from '../lib/utils.js'
|
|
10
10
|
import { CeroError } from '../lib/errors.js'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// The subset of the query grammar hyperdb itself honors (pushed down);
|
|
13
|
+
// the rest of QUERY_RESERVED is applied in memory here.
|
|
14
|
+
const RANGE_OPS = ['gt', 'gte', 'lt', 'lte', 'reverse', 'limit']
|
|
15
|
+
|
|
16
|
+
const pick = (query, keys) => {
|
|
17
|
+
const out = {}
|
|
18
|
+
if (query) for (const k of keys) if (query[k] !== undefined) out[k] = query[k]
|
|
19
|
+
return out
|
|
20
|
+
}
|
|
13
21
|
|
|
14
22
|
/**
|
|
15
23
|
* @typedef {object} StorageOpts
|
|
@@ -204,17 +212,18 @@ export class Storage extends ReadyResource {
|
|
|
204
212
|
return { data: (await this.db.get(col, { id: query })) ?? null }
|
|
205
213
|
}
|
|
206
214
|
|
|
207
|
-
// hyperdb only honors range/limit operators — equality fields
|
|
208
|
-
//
|
|
209
|
-
const eq = Object.keys(query || {}).filter((k) => !
|
|
210
|
-
if (eq.length) {
|
|
215
|
+
// hyperdb only honors range/limit operators — equality fields and search
|
|
216
|
+
// must be applied here or they are silently ignored and every row comes back
|
|
217
|
+
const eq = Object.keys(query || {}).filter((k) => !QUERY_RESERVED.has(k))
|
|
218
|
+
if (eq.length || query?.search) {
|
|
211
219
|
const rows = await this.db.find(col, {}).toArray()
|
|
212
|
-
|
|
220
|
+
let matched = rows.filter((r) => eq.every((k) => r[k] === query[k]))
|
|
221
|
+
if (query.search) matched = matched.filter((r) => searchHit(r, query.search, query.fields))
|
|
213
222
|
const data = query.limit != null ? matched.slice(0, query.limit) : matched
|
|
214
223
|
return { data, total: matched.length, size: data.length }
|
|
215
224
|
}
|
|
216
225
|
|
|
217
|
-
const data = await this.db.find(col, query
|
|
226
|
+
const data = await this.db.find(col, pick(query, RANGE_OPS)).toArray()
|
|
218
227
|
// a limit caps `data`, so `total` needs a full count; with no query
|
|
219
228
|
// `data` is already everything — skip the second scan.
|
|
220
229
|
const filtered = query && Object.keys(query).length > 0
|
|
@@ -233,9 +242,11 @@ export class Storage extends ReadyResource {
|
|
|
233
242
|
this._guard()
|
|
234
243
|
const ref = this._ref(name)
|
|
235
244
|
const col = this._col(ref)
|
|
236
|
-
const eq = Object.keys(query || {}).filter((k) => !
|
|
237
|
-
const
|
|
238
|
-
const
|
|
245
|
+
const eq = Object.keys(query || {}).filter((k) => !QUERY_RESERVED.has(k))
|
|
246
|
+
const filtered = eq.length || query?.search
|
|
247
|
+
const rows = await this.db.find(col, filtered ? {} : pick(query, RANGE_OPS)).toArray()
|
|
248
|
+
let matched = filtered ? rows.filter((r) => eq.every((k) => r[k] === query[k])) : rows
|
|
249
|
+
if (query?.search) matched = matched.filter((r) => searchHit(r, query.search, query.fields))
|
|
239
250
|
return { data: matched.length }
|
|
240
251
|
}
|
|
241
252
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pull-driven delta stream over a collection: batches of `{ prev, next }`
|
|
3
|
-
* pairs diffed between per-stream
|
|
4
|
-
* the stream dirty — the diff runs when the reader
|
|
5
|
-
* folds bursts into one bigger batch and nothing is
|
|
6
|
-
* batch, and any batch after the view is swapped or
|
|
7
|
-
* core, carries the full matching state as inserts
|
|
3
|
+
* pairs diffed between per-stream head cursors (`db.diff(col, { from })`).
|
|
4
|
+
* Update ticks only mark the stream dirty — the diff runs when the reader
|
|
5
|
+
* demands, so backpressure folds bursts into one bigger batch and nothing is
|
|
6
|
+
* ever dropped. The first batch, and any batch after the view is swapped or
|
|
7
|
+
* fast-forwarded to a new core, carries the full matching state as inserts
|
|
8
|
+
* with `reset: true`.
|
|
8
9
|
*
|
|
9
10
|
* @param {import('./index.js').Database} db
|
|
10
11
|
* @param {string} name Ref name (scopes the update ticks).
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* @returns {Uint8Array}
|
|
8
8
|
*/
|
|
9
9
|
export function blobEpochKey(entropy: Uint8Array): Uint8Array;
|
|
10
|
+
/** Prime a keyring from a local core's persisted epoch stash. */
|
|
11
|
+
export function loadEpochs(keyring: any, local: any): Promise<void>;
|
|
10
12
|
/**
|
|
11
13
|
* Wire codec for a rotation announcement's envelope list — one sealed box
|
|
12
14
|
* per remaining member, addressed by member id.
|
|
@@ -46,7 +48,6 @@ export class Keyring {
|
|
|
46
48
|
seq: number;
|
|
47
49
|
/** bumped on every add/remove — cheap change detection for retries */
|
|
48
50
|
version: number;
|
|
49
|
-
primed: boolean;
|
|
50
51
|
/** @returns {Array<{ epoch: number, stamp: number, entropy: Uint8Array }>} ascending by seq */
|
|
51
52
|
all(): Array<{
|
|
52
53
|
epoch: number;
|
|
@@ -86,12 +87,12 @@ export class EpochEncryption {
|
|
|
86
87
|
export class EpochAutobee {
|
|
87
88
|
constructor(store: any, key: any, handlers?: {});
|
|
88
89
|
keyring: any;
|
|
90
|
+
_epochStalled: Set<any>;
|
|
89
91
|
_epochRetry: any;
|
|
90
92
|
_epochRetryDelay: number;
|
|
91
93
|
_epochRetrySeen: number;
|
|
92
|
-
_bootState(): Promise<void>;
|
|
93
94
|
_bumpPendingWriters(): Promise<any>;
|
|
94
|
-
|
|
95
|
+
_applyWakeupHints(): Promise<any>;
|
|
95
96
|
_scheduleEpochRetry(): void;
|
|
96
97
|
_close(): Promise<any>;
|
|
97
98
|
}
|
|
@@ -90,7 +90,7 @@ export class Database extends ReadyResource {
|
|
|
90
90
|
onApplyHooks: Set<any>;
|
|
91
91
|
_applySeq: number;
|
|
92
92
|
_touched: Set<any>;
|
|
93
|
-
txQueue: any
|
|
93
|
+
txQueue: any;
|
|
94
94
|
_discovery: import("../network/discovery.js").Discovery;
|
|
95
95
|
/** Open the underlying autobee, wire dispatcher + apply, attach to network. */
|
|
96
96
|
openBee(): Promise<void>;
|
|
@@ -258,14 +258,19 @@ export class Database extends ReadyResource {
|
|
|
258
258
|
*/
|
|
259
259
|
_healEpochs(): Promise<void>;
|
|
260
260
|
/**
|
|
261
|
-
* Batch every write
|
|
262
|
-
*
|
|
261
|
+
* Batch every write made through the transaction handle passed to `fn`
|
|
262
|
+
* into a single autobee append. The handle is this database with its own
|
|
263
|
+
* write queue — same surface (`put`/`set`/`del`/`call`), but writes buffer
|
|
264
|
+
* until `fn` returns, and are discarded if it throws. Writes made on the
|
|
265
|
+
* database itself meanwhile append normally: an unrelated concurrent write
|
|
266
|
+
* can never be swallowed into (and rolled back with) a transaction.
|
|
267
|
+
* Nested `tx()` on the handle joins the same batch.
|
|
263
268
|
*
|
|
264
269
|
* @template T
|
|
265
|
-
* @param {() => Promise<T> | T} fn
|
|
270
|
+
* @param {(tx: Database) => Promise<T> | T} fn
|
|
266
271
|
* @returns {Promise<T>}
|
|
267
272
|
*/
|
|
268
|
-
tx<T>(fn: () => Promise<T> | T): Promise<T>;
|
|
273
|
+
tx<T>(fn: (tx: Database) => Promise<T> | T): Promise<T>;
|
|
269
274
|
_txChain: any;
|
|
270
275
|
/**
|
|
271
276
|
* Encode and append dispatch ops. Buffers into the active `tx` queue if one
|
package/types/lib/constants.d.ts
CHANGED
package/types/lib/utils.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export function subscribe<T>({ get, watch }: {
|
|
|
27
27
|
get: () => Promise<T> | T;
|
|
28
28
|
watch: (fn: () => void) => (() => void) | void;
|
|
29
29
|
}): import("streamx").Readable<T>;
|
|
30
|
+
export function searchHit(row: any, term: any, fields: any): boolean;
|
|
30
31
|
/**
|
|
31
32
|
* z32-encode a 32-byte key into a canonical string id.
|
|
32
33
|
*
|
package/types/network/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export function channelTopic(topic: any, channel: any): any;
|
|
2
2
|
/**
|
|
3
3
|
* @typedef {object} NetworkOpts
|
|
4
4
|
* @property {import('../identity/index.js').Identity} [identity] Long-lived keypair used as the swarm identity.
|
|
@@ -28,7 +28,7 @@ export class Network extends ReadyResource {
|
|
|
28
28
|
channel: string;
|
|
29
29
|
store: any;
|
|
30
30
|
mirrors: any[];
|
|
31
|
-
|
|
31
|
+
_swarm: any;
|
|
32
32
|
wakeup: any;
|
|
33
33
|
_replicateables: Set<any>;
|
|
34
34
|
_discoveries: Set<any>;
|
|
@@ -82,7 +82,7 @@ export class Network extends ReadyResource {
|
|
|
82
82
|
* @param {{ timeout?: number }} [opts]
|
|
83
83
|
* @returns {Promise<void>}
|
|
84
84
|
*/
|
|
85
|
-
flush(
|
|
85
|
+
flush({ timeout }?: {
|
|
86
86
|
timeout?: number;
|
|
87
87
|
}): Promise<void>;
|
|
88
88
|
/**
|
|
@@ -168,7 +168,5 @@ export type NetworkOpts = {
|
|
|
168
168
|
export type Replicable = {
|
|
169
169
|
replicate: (stream: any) => any;
|
|
170
170
|
};
|
|
171
|
-
import { channelTopic } from './transports/dht.js';
|
|
172
171
|
import ReadyResource from 'ready-resource';
|
|
173
|
-
import { DHTTransport } from './transports/dht.js';
|
|
174
172
|
import { Discovery } from './discovery.js';
|