@cero-base/cero 1.19.0 → 2.1.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/README.md +38 -643
- package/package.json +9 -6
- package/src/build/index.js +63 -61
- package/src/build/internal.js +123 -0
- package/src/build/schemas.js +6 -11
- package/src/extensions/handle-sync.js +3 -11
- package/src/extensions/index.js +86 -0
- package/src/extensions/profile-sync.js +15 -16
- package/src/handle/index.js +290 -284
- package/src/index.js +24 -81
- package/src/lib/bluetooth.js +25 -56
- package/src/lib/constants.js +0 -15
- package/src/lib/operators.js +35 -134
- package/src/lib/peek.js +4 -8
- package/src/lib/refs.js +10 -8
- package/src/lib/spec.js +2 -3
- package/src/local/index.js +2 -3
- package/src/rpc/client.js +54 -77
- package/src/rpc/index.js +3 -3
- package/src/rpc/server.js +40 -43
- package/types/build/index.d.ts +11 -9
- package/types/build/{builtins.d.ts → internal.d.ts} +20 -38
- package/types/build/schemas.d.ts +4 -3
- package/types/extensions/handle-sync.d.ts +2 -8
- package/types/extensions/index.d.ts +102 -0
- package/types/extensions/profile-sync.d.ts +0 -5
- package/types/handle/index.d.ts +90 -108
- package/types/index.d.ts +21 -17
- package/types/lib/bluetooth.d.ts +8 -32
- package/types/lib/constants.d.ts +0 -11
- package/types/lib/operators.d.ts +30 -83
- package/types/lib/peek.d.ts +2 -3
- package/types/lib/refs.d.ts +5 -5
- package/types/lib/spec.d.ts +2 -3
- package/types/local/index.d.ts +2 -3
- package/types/rpc/client.d.ts +29 -26
- package/types/rpc/index.d.ts +3 -3
- package/types/rpc/server.d.ts +7 -10
- package/src/build/builtins.js +0 -174
- package/src/lib/internal.js +0 -9
- package/types/lib/internal.d.ts +0 -24
package/src/index.js
CHANGED
|
@@ -17,21 +17,18 @@ import {
|
|
|
17
17
|
set,
|
|
18
18
|
get,
|
|
19
19
|
del,
|
|
20
|
-
count,
|
|
21
20
|
watch,
|
|
22
21
|
changes,
|
|
23
22
|
call,
|
|
24
23
|
open,
|
|
25
24
|
rotate,
|
|
26
25
|
before,
|
|
27
|
-
after
|
|
28
|
-
bind,
|
|
29
|
-
define
|
|
26
|
+
after
|
|
30
27
|
} from './lib/operators.js'
|
|
31
28
|
import { peek } from './lib/peek.js'
|
|
32
29
|
import { t, schema } from './lib/spec.js'
|
|
33
30
|
import { FLUSH, TIMEOUT } from './lib/constants.js'
|
|
34
|
-
import {
|
|
31
|
+
import { bind } from './extensions/index.js'
|
|
35
32
|
|
|
36
33
|
export { Handle, Ref, Local }
|
|
37
34
|
export {
|
|
@@ -39,16 +36,13 @@ export {
|
|
|
39
36
|
set,
|
|
40
37
|
get,
|
|
41
38
|
del,
|
|
42
|
-
count,
|
|
43
39
|
watch,
|
|
44
40
|
changes,
|
|
45
41
|
call,
|
|
46
42
|
open,
|
|
47
43
|
rotate,
|
|
48
44
|
before,
|
|
49
|
-
after
|
|
50
|
-
bind,
|
|
51
|
-
define
|
|
45
|
+
after
|
|
52
46
|
} from './lib/operators.js'
|
|
53
47
|
export { peek } from './lib/peek.js'
|
|
54
48
|
export { t, schema } from './lib/spec.js'
|
|
@@ -69,20 +63,20 @@ export { t, schema } from './lib/spec.js'
|
|
|
69
63
|
* @property {number[]} [backoffs] Swarm reconnect backoff tiers in ms (testing/tuning).
|
|
70
64
|
* @property {string} [channel] Optional network-isolation label; only same-channel peers connect.
|
|
71
65
|
* @property {Array<string | Uint8Array>} [mirrors] Blind-peer public keys. Rooms and files are mirrored through them so peers sync even when never online at the same time. Mirrors hold only encrypted blocks — they never read your data.
|
|
66
|
+
* @property {{ active?: number, announced?: number, idle?: number }} [presence] How many rooms search, how many only announce, and the idle ms before the rest leave the swarm.
|
|
72
67
|
* @property {Uint8Array} [key] Existing database key to recover into, skipping the pointer lookup.
|
|
73
68
|
* @property {Uint8Array} [encryptionKey] Pre-existing encryption key.
|
|
74
69
|
* @property {Record<string, Function>} [routes] Custom RPC routes for the database dispatcher.
|
|
75
70
|
* @property {(err: any) => void} [onerror] Background-task error handler.
|
|
76
71
|
* @property {number} [recoveryTimeout] Max wait to find another device and be admitted, in ms. Defaults to 30000.
|
|
77
72
|
* @property {Uint8Array} [storageKey] 32-byte key encrypting local key material (master seed, device keypairs) at rest. Source it from the OS keychain — cero never stores it.
|
|
78
|
-
* @property {
|
|
73
|
+
* @property {import('./extensions/index.js').Extension[]} [extensions] The extensions this instance runs, instead of the ones the spec carries. Build with the same list.
|
|
74
|
+
* @property {Record<string, any>} [operators] The operators to bind, instead of the ones the spec carries.
|
|
79
75
|
* @property {boolean | { autoStart?: boolean, backend?: any, maxOutbound?: number, maxInbound?: number, pipe?: 'l2cap' | 'gatt' }} [bluetooth] `true` enables nearby (Bluetooth) sync via `me.bluetooth` (auto-started). `{ autoStart: false }` creates the facade without starting the radio — the app calls `me.bluetooth.start()`/`stop()` (user toggle). `backend` injects a bare-bluetooth-shaped backend (tests). `maxOutbound`/`maxInbound` cap concurrent outbound links and inbound sessions. `pipe` picks the data pipe — `'l2cap'` (default, faster) or `'gatt'`; both peers must match. Absent backend on an unsupported host → `me.bluetooth.state === 'unsupported'`.
|
|
80
76
|
*/
|
|
81
77
|
|
|
82
78
|
/**
|
|
83
|
-
* Open (or create) a cero handle at `dir`.
|
|
84
|
-
* identity, then returns a ready root `Handle` with all schema refs
|
|
85
|
-
* attached as properties.
|
|
79
|
+
* Open (or create) a cero handle at `dir`.
|
|
86
80
|
*
|
|
87
81
|
* @param {string} dir Data directory.
|
|
88
82
|
* @param {any} spec Built spec — output of `cero/build`.
|
|
@@ -99,9 +93,7 @@ export async function cero(dir, spec, opts = {}) {
|
|
|
99
93
|
let network = null
|
|
100
94
|
let discovery = null
|
|
101
95
|
let me = null
|
|
102
|
-
//
|
|
103
|
-
// timeout, extension setup) must close whatever opened — else the corestore
|
|
104
|
-
// lock leaks and a retry in the same process hits locked storage.
|
|
96
|
+
// any failure during open must close what opened, or the storage lock leaks
|
|
105
97
|
try {
|
|
106
98
|
await storage.ready()
|
|
107
99
|
await fs.promises.chmod(`${dir}/main`, 0o700)
|
|
@@ -115,15 +107,11 @@ export async function cero(dir, spec, opts = {}) {
|
|
|
115
107
|
|
|
116
108
|
const { identity, fresh } = await resolveIdentity(opts, local)
|
|
117
109
|
const writer = local ? (await local.store.get('keypair')).data : null
|
|
118
|
-
// a supplied identity on a device with no writer
|
|
119
|
-
// authors the identity's pointer core or any core that exists elsewhere.
|
|
120
|
-
// Only an identity cero generated here creates a database
|
|
110
|
+
// a supplied identity on a device with no writer recovers; only a cero-minted identity creates
|
|
121
111
|
const recovering = !writer && (!!opts.key || !fresh)
|
|
122
112
|
const timeout = opts.recoveryTimeout || TIMEOUT
|
|
123
113
|
|
|
124
|
-
//
|
|
125
|
-
// including no channel at all, which would silently rejoin the global network — is a
|
|
126
|
-
// misconfiguration that could leak data across networks, so reject it.
|
|
114
|
+
// a storage remembers its channel; reopening under another would silently rejoin the global network
|
|
127
115
|
if (local) {
|
|
128
116
|
const stored = (await local.store.get('environment')).data?.channel ?? null
|
|
129
117
|
const wanted = opts.channel ?? null
|
|
@@ -137,17 +125,15 @@ export async function cero(dir, spec, opts = {}) {
|
|
|
137
125
|
backoffs: opts.backoffs,
|
|
138
126
|
channel: opts.channel,
|
|
139
127
|
store,
|
|
140
|
-
mirrors: opts.mirrors
|
|
128
|
+
mirrors: opts.mirrors,
|
|
129
|
+
presence: opts.presence
|
|
141
130
|
})
|
|
142
131
|
await network.ready()
|
|
143
132
|
discovery = network.join(identity.topic)
|
|
144
|
-
// a
|
|
145
|
-
// before proceeding only delays first onboarding
|
|
133
|
+
// a fresh identity has no peers yet, flushing the announce would only delay onboarding
|
|
146
134
|
if (!fresh) await Promise.race([discovery.flush(), new Promise((r) => setTimeout(r, FLUSH))])
|
|
147
135
|
|
|
148
|
-
// the pointer core: signed
|
|
149
|
-
// created the identity, read by every device that recovers it. It holds the
|
|
150
|
-
// root database key, so a phrase alone finds the data
|
|
136
|
+
// the pointer core: identity-signed, written once by the creating device, holds the root key
|
|
151
137
|
const manifest = pointerManifest(store, identity)
|
|
152
138
|
const pointer = store.get(
|
|
153
139
|
!writer && !recovering
|
|
@@ -194,39 +180,20 @@ export async function cero(dir, spec, opts = {}) {
|
|
|
194
180
|
secretKey: result.writer.secretKey
|
|
195
181
|
})
|
|
196
182
|
}
|
|
197
|
-
if (!recovering) {
|
|
198
|
-
|
|
199
|
-
await me.store.call('add-member', {
|
|
200
|
-
id: identity.id,
|
|
201
|
-
key: me.store.writerKey,
|
|
202
|
-
role: 'owner',
|
|
203
|
-
name: opts.name || null,
|
|
204
|
-
createdAt: ts,
|
|
205
|
-
updatedAt: ts
|
|
206
|
-
})
|
|
207
|
-
if (pointer.length === 0) await pointer.append(c.encode(c.fixed32, me.store.key))
|
|
183
|
+
if (!recovering && pointer.length === 0) {
|
|
184
|
+
await pointer.append(c.encode(c.fixed32, me.store.key))
|
|
208
185
|
}
|
|
209
186
|
}
|
|
210
187
|
|
|
211
|
-
for (const ext of
|
|
212
|
-
if (ext.bundled && opts.extensions === false) continue
|
|
188
|
+
for (const ext of me.extensions) {
|
|
213
189
|
const off = await ext.setup?.(me)
|
|
214
190
|
if (typeof off === 'function') me.once('close', off)
|
|
215
191
|
}
|
|
216
192
|
|
|
217
193
|
if (opts.bluetooth) {
|
|
218
|
-
|
|
219
|
-
// object (pre-1.3 shape, has Central/Server) is still accepted.
|
|
220
|
-
const bt =
|
|
221
|
-
opts.bluetooth === true
|
|
222
|
-
? {}
|
|
223
|
-
: opts.bluetooth.Central
|
|
224
|
-
? { backend: opts.bluetooth }
|
|
225
|
-
: opts.bluetooth
|
|
194
|
+
const bt = opts.bluetooth === true ? {} : opts.bluetooth
|
|
226
195
|
me.bluetooth = new Bluetooth(me, {
|
|
227
|
-
//
|
|
228
|
-
// backend (= lazy-load bare-bluetooth) into an explicit null
|
|
229
|
-
// (= disabled), leaving BLE 'unsupported' on every platform
|
|
196
|
+
// an omitted backend lazy-loads bare-bluetooth, null disables it
|
|
230
197
|
backend: bt.backend,
|
|
231
198
|
autoStart: bt.autoStart !== false,
|
|
232
199
|
maxOutbound: bt.maxOutbound,
|
|
@@ -237,11 +204,10 @@ export async function cero(dir, spec, opts = {}) {
|
|
|
237
204
|
await me.bluetooth.ready()
|
|
238
205
|
}
|
|
239
206
|
|
|
240
|
-
bind(me, null)
|
|
207
|
+
bind(me, null, me.operators)
|
|
241
208
|
return me
|
|
242
209
|
} catch (err) {
|
|
243
|
-
//
|
|
244
|
-
// tear the raw resources down in reverse order.
|
|
210
|
+
// before the root Handle exists, tear the raw resources down in reverse order
|
|
245
211
|
if (me) await me.close().catch(safetyCatch)
|
|
246
212
|
else {
|
|
247
213
|
await discovery?.destroy().catch(safetyCatch)
|
|
@@ -255,9 +221,7 @@ export async function cero(dir, spec, opts = {}) {
|
|
|
255
221
|
}
|
|
256
222
|
|
|
257
223
|
/**
|
|
258
|
-
* Restore a cero instance from a mnemonic phrase.
|
|
259
|
-
* instance, wipes the on-disk `main/` tree and re-opens with the phrase, which
|
|
260
|
-
* recovers so the writer slot is re-claimed.
|
|
224
|
+
* Restore a cero instance from a mnemonic phrase.
|
|
261
225
|
*
|
|
262
226
|
* @param {Handle} me Existing root handle to restore.
|
|
263
227
|
* @param {string} phrase BIP-39 mnemonic phrase.
|
|
@@ -267,12 +231,10 @@ export async function restore(me, phrase) {
|
|
|
267
231
|
if (!me?._dir) throw CeroError.INVALID('me must be a cero instance')
|
|
268
232
|
if (!phrase || typeof phrase !== 'string') throw CeroError.INVALID('phrase must be a string')
|
|
269
233
|
|
|
270
|
-
// Already this identity? Nothing to restore — return the running instance.
|
|
271
234
|
const current = await Identity.fromSeed(Identity.toSeed(phrase))
|
|
272
235
|
if (current.id === me.identity.id) return me
|
|
273
236
|
|
|
274
|
-
// everything carries over except the
|
|
275
|
-
// particular, or the recovered instance never meets its peers
|
|
237
|
+
// everything carries over except the identity, the channel above all
|
|
276
238
|
const {
|
|
277
239
|
_dir: dir,
|
|
278
240
|
spec,
|
|
@@ -285,15 +247,12 @@ export async function restore(me, phrase) {
|
|
|
285
247
|
return cero(dir, spec, { ...opts, phrase })
|
|
286
248
|
}
|
|
287
249
|
|
|
288
|
-
//
|
|
289
|
-
// `import { define }` and `cero.define(...)` work. Explicit assignments (not
|
|
290
|
-
// `Object.assign`) so tsc reflects them onto the `cero` namespace in the .d.ts.
|
|
250
|
+
// the facade: cero.put and import { put } are the same function
|
|
291
251
|
cero.t = t
|
|
292
252
|
cero.put = put
|
|
293
253
|
cero.set = set
|
|
294
254
|
cero.get = get
|
|
295
255
|
cero.del = del
|
|
296
|
-
cero.count = count
|
|
297
256
|
cero.watch = watch
|
|
298
257
|
cero.changes = changes
|
|
299
258
|
cero.call = call
|
|
@@ -304,22 +263,6 @@ cero.after = after
|
|
|
304
263
|
cero.peek = peek
|
|
305
264
|
cero.restore = restore
|
|
306
265
|
cero.schema = schema
|
|
307
|
-
cero.bind = bind
|
|
308
|
-
cero.define = define
|
|
309
|
-
// test-only escape hatch (cross-package tests reset/seed the registry)
|
|
310
|
-
cero._internal = internal
|
|
311
|
-
// A bare function is shorthand for a behavior-only extension: `{ setup: fn }`.
|
|
312
|
-
// Accepts both `use(a, b)` and `use([a, b])` (and a mix) for easier composition.
|
|
313
|
-
// A named extension replaces any registered one with the same name — so
|
|
314
|
-
// `use(profileSync({ fields }))` reconfigures the bundled default instead of
|
|
315
|
-
// doubling it.
|
|
316
|
-
cero.use = (...exts) => {
|
|
317
|
-
for (const e of exts.flat().map((e) => (typeof e === 'function' ? { setup: e } : e))) {
|
|
318
|
-
const i = e.name ? internal.extensions.findIndex((x) => x.name === e.name) : -1
|
|
319
|
-
if (i >= 0) internal.extensions[i] = e
|
|
320
|
-
else internal.extensions.push(e)
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
266
|
|
|
324
267
|
function pointerManifest(store, identity) {
|
|
325
268
|
return { version: store.manifestVersion, signers: [{ publicKey: identity.publicKey }] }
|
package/src/lib/bluetooth.js
CHANGED
|
@@ -8,19 +8,8 @@ import { Pairing } from '@cero-base/core/pairing'
|
|
|
8
8
|
import { Invite } from '@cero-base/core/invite'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* `me.bluetooth` — the app-facing surface for nearby (Bluetooth) sync, a thin
|
|
12
|
-
*
|
|
13
|
-
* bytes; capability-gated replication still decides what syncs. Discovery is
|
|
14
|
-
* one topic-derived service UUID at a time (tag `cero-ble`) — the data service
|
|
15
|
-
* sits on a fixed per-tag UUID, so switching topics only retunes the radio.
|
|
16
|
-
*
|
|
17
|
-
* ```js
|
|
18
|
-
* const me = await cero(dir, spec, { channel, bluetooth: true })
|
|
19
|
-
* me.bluetooth.state // 'unsupported' | 'unauthorized' | 'off' | 'waiting' | 'starting' | 'on'
|
|
20
|
-
* await me.bluetooth.start()
|
|
21
|
-
* me.bluetooth.peers // Map of live BLE links
|
|
22
|
-
* me.bluetooth.on('update', () => {})
|
|
23
|
-
* ```
|
|
11
|
+
* `me.bluetooth` — the app-facing surface for nearby (Bluetooth) sync, a thin facade over
|
|
12
|
+
* ble-swarm.
|
|
24
13
|
*
|
|
25
14
|
* @extends ReadyResource
|
|
26
15
|
*/
|
|
@@ -43,14 +32,11 @@ export class Bluetooth extends ReadyResource {
|
|
|
43
32
|
this._restorePending = false
|
|
44
33
|
|
|
45
34
|
const identity = handle.identity
|
|
46
|
-
//
|
|
47
|
-
// channelless — any nearby cero device links (global nearby; strangers
|
|
48
|
-
// still sync zero bytes, replication is capability-gated)
|
|
35
|
+
// one mesh topic per channel, or a global one when channelless; strangers still sync nothing
|
|
49
36
|
this._topic = crypto.hash(b4a.from(handle.network.channel || 'cero-ble'))
|
|
50
37
|
this.swarm = new BluetoothSwarm({
|
|
51
38
|
backend,
|
|
52
|
-
//
|
|
53
|
-
// swarm, so one person reached over Wi-Fi and BLE dedupes to one peer
|
|
39
|
+
// the swarm identity, so one person over Wi-Fi and BLE dedupes to one peer
|
|
54
40
|
keyPair: { publicKey: identity.publicKey, secretKey: identity.secretKey },
|
|
55
41
|
topic: this._topic,
|
|
56
42
|
tag: 'cero-ble',
|
|
@@ -59,9 +45,7 @@ export class Bluetooth extends ReadyResource {
|
|
|
59
45
|
maxInbound
|
|
60
46
|
})
|
|
61
47
|
this.swarm.on('update', () => {
|
|
62
|
-
//
|
|
63
|
-
// drain — retuning drops links, and the pairing link still carries
|
|
64
|
-
// the joiner's initial replication
|
|
48
|
+
// retune to the mesh only once the pairing link's initial replication drained
|
|
65
49
|
if (this._restorePending && this.swarm.peers.size === 0) {
|
|
66
50
|
this._restorePending = false
|
|
67
51
|
this.swarm.setTopic(this._topic).catch(safetyCatch)
|
|
@@ -69,7 +53,6 @@ export class Bluetooth extends ReadyResource {
|
|
|
69
53
|
this.emit('update')
|
|
70
54
|
})
|
|
71
55
|
this.swarm.on('connection', (conn) => {
|
|
72
|
-
// the same treatment as any transport: wakeup, replication, pairing
|
|
73
56
|
this._handle.network.inject(conn)
|
|
74
57
|
})
|
|
75
58
|
}
|
|
@@ -88,6 +71,11 @@ export class Bluetooth extends ReadyResource {
|
|
|
88
71
|
if (this._autoStart) await this.start()
|
|
89
72
|
}
|
|
90
73
|
|
|
74
|
+
async _close() {
|
|
75
|
+
this._clearAnnounce()
|
|
76
|
+
await this.swarm.destroy()
|
|
77
|
+
}
|
|
78
|
+
|
|
91
79
|
/**
|
|
92
80
|
* Begin advertising + scanning. Idempotent; no-op when unsupported.
|
|
93
81
|
*
|
|
@@ -98,34 +86,20 @@ export class Bluetooth extends ReadyResource {
|
|
|
98
86
|
}
|
|
99
87
|
|
|
100
88
|
/**
|
|
101
|
-
* Stop advertising/scanning and drop links; open invite rendezvous end with
|
|
102
|
-
* the radio. Idempotent. Local data and the rest of the network (DHT) are
|
|
103
|
-
* untouched.
|
|
89
|
+
* Stop advertising/scanning and drop links; open invite rendezvous end with the radio.
|
|
104
90
|
*
|
|
105
91
|
* @returns {Promise<void>}
|
|
106
92
|
*/
|
|
107
93
|
async stop() {
|
|
108
94
|
this._clearAnnounce()
|
|
109
95
|
await this.swarm.stop()
|
|
110
|
-
// while stopped setTopic
|
|
111
|
-
// mesh. A start() + announce() that landed during the await wins.
|
|
96
|
+
// while stopped setTopic sticks; a start() + announce() during the await wins
|
|
112
97
|
if (!this._announce) await this.swarm.setTopic(this._topic)
|
|
113
98
|
}
|
|
114
99
|
|
|
115
100
|
/**
|
|
116
|
-
* Offline join rendezvous: retune the radio to the invite-derived topic so
|
|
117
|
-
*
|
|
118
|
-
* announcing a new invite replaces the previous rendezvous. Returns a stop
|
|
119
|
-
* function — closing the QR must stop the rendezvous so a photographed
|
|
120
|
-
* invite doesn't stay an ambient discovery beacon (admission itself is
|
|
121
|
-
* always gated by blind-pairing verifying the invite). The retune back to
|
|
122
|
-
* the mesh topic waits for live links to drain: the link a join just
|
|
123
|
-
* established survives and carries the joiner's initial replication.
|
|
124
|
-
* Auto-stops at the invite's expiry, on `stop()`, and on close.
|
|
125
|
-
*
|
|
126
|
-
* Only active while nearby sync is on: before `start()` (and after `stop()`)
|
|
127
|
-
* this is a no-op — the user controls the radio, and a join must not touch
|
|
128
|
-
* Bluetooth (OS permissions, GATT server) they never enabled.
|
|
101
|
+
* Offline join rendezvous: retune the radio to the invite-derived topic so holder and
|
|
102
|
+
* joiner find each other with zero DHT.
|
|
129
103
|
*
|
|
130
104
|
* @param {string} invite Z32 invite string.
|
|
131
105
|
* @returns {() => void}
|
|
@@ -161,20 +135,6 @@ export class Bluetooth extends ReadyResource {
|
|
|
161
135
|
}
|
|
162
136
|
}
|
|
163
137
|
|
|
164
|
-
_stopAnnounce() {
|
|
165
|
-
this._clearAnnounce()
|
|
166
|
-
if (this.swarm.peers.size > 0) this._restorePending = true
|
|
167
|
-
else this.swarm.setTopic(this._topic).catch(safetyCatch)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
_clearAnnounce() {
|
|
171
|
-
const e = this._announce
|
|
172
|
-
if (!e) return
|
|
173
|
-
if (e.timer) clearTimeout(e.timer)
|
|
174
|
-
this._announce = null
|
|
175
|
-
this._restorePending = false
|
|
176
|
-
}
|
|
177
|
-
|
|
178
138
|
/**
|
|
179
139
|
* Host-lifecycle pause (app backgrounded): radio down, user intent kept.
|
|
180
140
|
*
|
|
@@ -193,8 +153,17 @@ export class Bluetooth extends ReadyResource {
|
|
|
193
153
|
await this.swarm.resume()
|
|
194
154
|
}
|
|
195
155
|
|
|
196
|
-
|
|
156
|
+
_stopAnnounce() {
|
|
197
157
|
this._clearAnnounce()
|
|
198
|
-
|
|
158
|
+
if (this.swarm.peers.size > 0) this._restorePending = true
|
|
159
|
+
else this.swarm.setTopic(this._topic).catch(safetyCatch)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_clearAnnounce() {
|
|
163
|
+
const e = this._announce
|
|
164
|
+
if (!e) return
|
|
165
|
+
if (e.timer) clearTimeout(e.timer)
|
|
166
|
+
this._announce = null
|
|
167
|
+
this._restorePending = false
|
|
199
168
|
}
|
|
200
169
|
}
|
package/src/lib/constants.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// Shared cero constants.
|
|
2
|
-
|
|
3
1
|
export const NS = 'cero'
|
|
4
2
|
export const COUNTERS = 'counters'
|
|
5
3
|
export const EPOCHS = 'epochs'
|
|
@@ -7,16 +5,3 @@ export const EPOCHS = 'epochs'
|
|
|
7
5
|
// Writer-admission / pairing timeout (ms), and the initial discovery-flush wait (ms).
|
|
8
6
|
export const TIMEOUT = 30000
|
|
9
7
|
export const FLUSH = 500
|
|
10
|
-
|
|
11
|
-
// schema-DSL primitive → HyperDB type (used by the builder).
|
|
12
|
-
export const DB_TYPE = {
|
|
13
|
-
string: 'string',
|
|
14
|
-
uint: 'uint',
|
|
15
|
-
int: 'int',
|
|
16
|
-
bool: 'bool',
|
|
17
|
-
bytes: 'buffer',
|
|
18
|
-
json: 'json',
|
|
19
|
-
fixed32: 'fixed32',
|
|
20
|
-
fixed64: 'fixed64',
|
|
21
|
-
file: 'string'
|
|
22
|
-
}
|