@cero-base/core 1.2.0 → 1.4.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 +8 -4
- package/src/network/index.js +40 -54
- package/src/network/transports/ble.js +779 -0
- package/src/network/transports/dht.js +110 -0
- package/src/network/transports/gatt.js +57 -0
- package/types/network/index.d.ts +7 -3
- package/types/network/{bluetooth.d.ts → transports/ble.d.ts} +59 -19
- package/types/network/transports/dht.d.ts +75 -0
- package/types/network/transports/gatt.d.ts +25 -0
- package/src/network/bluetooth.js +0 -324
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cero-base/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "cero p2p primitives — identity, storage, network, database, blobs, rpc, pairing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -41,9 +41,13 @@
|
|
|
41
41
|
"types": "./types/network/index.d.ts",
|
|
42
42
|
"default": "./src/network/index.js"
|
|
43
43
|
},
|
|
44
|
-
"./network/
|
|
45
|
-
"types": "./types/network/
|
|
46
|
-
"default": "./src/network/
|
|
44
|
+
"./network/transports/ble": {
|
|
45
|
+
"types": "./types/network/transports/ble.d.ts",
|
|
46
|
+
"default": "./src/network/transports/ble.js"
|
|
47
|
+
},
|
|
48
|
+
"./network/transports/dht": {
|
|
49
|
+
"types": "./types/network/transports/dht.d.ts",
|
|
50
|
+
"default": "./src/network/transports/dht.js"
|
|
47
51
|
},
|
|
48
52
|
"./database": {
|
|
49
53
|
"types": "./types/database/index.d.ts",
|
package/src/network/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import Hyperswarm from 'hyperswarm'
|
|
2
1
|
import NoiseSecretStream from '@hyperswarm/secret-stream'
|
|
3
2
|
import BlindPairing from 'blind-pairing'
|
|
4
3
|
import ProtomuxWakeup from 'protomux-wakeup'
|
|
5
4
|
import ReadyResource from 'ready-resource'
|
|
6
5
|
import safetyCatch from 'safety-catch'
|
|
7
6
|
import b4a from 'b4a'
|
|
8
|
-
import { hash } from 'hypercore-crypto'
|
|
9
7
|
|
|
10
8
|
import { ACTIVE, PASSIVE } from '../lib/constants.js'
|
|
11
9
|
import { CeroError } from '../lib/errors.js'
|
|
12
10
|
import { Discovery } from './discovery.js'
|
|
11
|
+
import { DHTTransport, channelTopic } from './transports/dht.js'
|
|
12
|
+
|
|
13
|
+
export { channelTopic }
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @typedef {object} NetworkOpts
|
|
@@ -36,7 +37,7 @@ export class Network extends ReadyResource {
|
|
|
36
37
|
this.relayThrough = relayThrough || null
|
|
37
38
|
this.channel = channel || null
|
|
38
39
|
|
|
39
|
-
this.
|
|
40
|
+
this._dht = null
|
|
40
41
|
this.wakeup = new ProtomuxWakeup()
|
|
41
42
|
|
|
42
43
|
this._replicateables = new Set()
|
|
@@ -45,6 +46,11 @@ export class Network extends ReadyResource {
|
|
|
45
46
|
this._blind = null
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/** @returns {any} The underlying hyperswarm, or null before ready / after close. */
|
|
50
|
+
get swarm() {
|
|
51
|
+
return this._dht ? this._dht.swarm : null
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
/**
|
|
49
55
|
* Feed an externally-established connection — a Bluetooth L2CAP channel, a
|
|
50
56
|
* serial link, an in-process pair, any duplex — into the network. A raw
|
|
@@ -60,8 +66,16 @@ export class Network extends ReadyResource {
|
|
|
60
66
|
inject(stream, { isInitiator } = {}) {
|
|
61
67
|
if (this.closing || this.closed) throw CeroError.CLOSED('Network')
|
|
62
68
|
if (!stream) throw CeroError.REQUIRED('stream')
|
|
69
|
+
// Injected links authenticate with the same long-lived identity as the
|
|
70
|
+
// swarm — an ephemeral key per link would defeat duplicate-peer detection
|
|
71
|
+
// (two radios between one pair would look like two different peers).
|
|
72
|
+
const keyPair = this.identity
|
|
73
|
+
? { publicKey: this.identity.publicKey, secretKey: this.identity.secretKey }
|
|
74
|
+
: (this.swarm?.keyPair ?? undefined)
|
|
63
75
|
const conn =
|
|
64
|
-
stream.noiseStream === stream
|
|
76
|
+
stream.noiseStream === stream
|
|
77
|
+
? stream
|
|
78
|
+
: new NoiseSecretStream(isInitiator === true, stream, keyPair ? { keyPair } : undefined)
|
|
65
79
|
|
|
66
80
|
// blind-pairing picks the lowest-`rtt` unvisited channel to send on; that
|
|
67
81
|
// field only exists on real udx sockets. A raw injected duplex has none,
|
|
@@ -135,24 +149,18 @@ export class Network extends ReadyResource {
|
|
|
135
149
|
}
|
|
136
150
|
|
|
137
151
|
async _open() {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this.swarm = new Hyperswarm(opts)
|
|
146
|
-
|
|
147
|
-
if (this.channel) {
|
|
148
|
-
const channel = this.channel
|
|
149
|
-
const join = this.swarm.join.bind(this.swarm)
|
|
150
|
-
const leave = this.swarm.leave.bind(this.swarm)
|
|
151
|
-
this.swarm.join = (topic, opts) => join(channelTopic(topic, channel), opts)
|
|
152
|
-
this.swarm.leave = (topic) => leave(channelTopic(topic, channel))
|
|
153
|
-
}
|
|
152
|
+
this._dht = new DHTTransport({
|
|
153
|
+
identity: this.identity,
|
|
154
|
+
bootstrap: this.bootstrap,
|
|
155
|
+
firewall: this.firewall,
|
|
156
|
+
relayThrough: this.relayThrough,
|
|
157
|
+
channel: this.channel
|
|
158
|
+
})
|
|
154
159
|
|
|
155
|
-
|
|
160
|
+
// DHTTransport owns the swarm's lifecycle; Network subscribes to its events
|
|
161
|
+
// because the handlers touch Network state (wakeup, replicateables, emit).
|
|
162
|
+
const swarm = this._dht.swarm
|
|
163
|
+
swarm.on('connection', (stream, info) => {
|
|
156
164
|
if (this.closing || this.closed) {
|
|
157
165
|
stream.destroy()
|
|
158
166
|
return
|
|
@@ -161,8 +169,8 @@ export class Network extends ReadyResource {
|
|
|
161
169
|
for (const r of this._replicateables) replicateInto(r, stream)
|
|
162
170
|
this.emit('connection', stream, info)
|
|
163
171
|
})
|
|
164
|
-
|
|
165
|
-
|
|
172
|
+
swarm.on('peer-add', (peer) => this.emit('peer-add', peer))
|
|
173
|
+
swarm.on('peer-remove', (peer) => this.emit('peer-remove', peer))
|
|
166
174
|
}
|
|
167
175
|
|
|
168
176
|
/**
|
|
@@ -171,9 +179,8 @@ export class Network extends ReadyResource {
|
|
|
171
179
|
* @param {{ timeout?: number }} [opts]
|
|
172
180
|
* @returns {Promise<void>}
|
|
173
181
|
*/
|
|
174
|
-
async flush(
|
|
175
|
-
|
|
176
|
-
await Promise.race([this.swarm.flush(), new Promise((r) => setTimeout(r, timeout))])
|
|
182
|
+
async flush(opts) {
|
|
183
|
+
await this._dht?.flush(opts)
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
/**
|
|
@@ -182,13 +189,8 @@ export class Network extends ReadyResource {
|
|
|
182
189
|
* @returns {Promise<void>}
|
|
183
190
|
*/
|
|
184
191
|
async suspend() {
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
try {
|
|
188
|
-
await this.swarm.suspend()
|
|
189
|
-
} catch (err) {
|
|
190
|
-
safetyCatch(err)
|
|
191
|
-
}
|
|
192
|
+
if (this.closing || this.closed) return
|
|
193
|
+
await this._dht?.suspend()
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
/**
|
|
@@ -197,13 +199,8 @@ export class Network extends ReadyResource {
|
|
|
197
199
|
* @returns {Promise<void>}
|
|
198
200
|
*/
|
|
199
201
|
async resume() {
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
await this.swarm.resume()
|
|
204
|
-
} catch (err) {
|
|
205
|
-
safetyCatch(err)
|
|
206
|
-
}
|
|
202
|
+
if (this.closing || this.closed) return
|
|
203
|
+
await this._dht?.resume()
|
|
207
204
|
}
|
|
208
205
|
|
|
209
206
|
async _close() {
|
|
@@ -233,18 +230,13 @@ export class Network extends ReadyResource {
|
|
|
233
230
|
this._blind = null
|
|
234
231
|
}
|
|
235
232
|
|
|
236
|
-
if (this.
|
|
233
|
+
if (this._dht) {
|
|
237
234
|
try {
|
|
238
|
-
await this.
|
|
235
|
+
await this._dht.destroy()
|
|
239
236
|
} catch (err) {
|
|
240
237
|
safetyCatch(err)
|
|
241
238
|
}
|
|
242
|
-
|
|
243
|
-
await this.swarm.destroy()
|
|
244
|
-
} catch (err) {
|
|
245
|
-
safetyCatch(err)
|
|
246
|
-
}
|
|
247
|
-
this.swarm = null
|
|
239
|
+
this._dht = null
|
|
248
240
|
}
|
|
249
241
|
|
|
250
242
|
if (this.wakeup) {
|
|
@@ -342,12 +334,6 @@ function replicateInto(core, stream) {
|
|
|
342
334
|
}
|
|
343
335
|
}
|
|
344
336
|
|
|
345
|
-
// A channel re-namespaces every swarm topic so only same-channel peers meet.
|
|
346
|
-
// No channel → identity (unchanged, back-compat).
|
|
347
|
-
export function channelTopic(topic, channel) {
|
|
348
|
-
return channel ? hash([topic, b4a.from(channel)]) : topic
|
|
349
|
-
}
|
|
350
|
-
|
|
351
337
|
function isTopic(x) {
|
|
352
338
|
return b4a.isBuffer(x) && x.length === 32
|
|
353
339
|
}
|