@cero-base/core 1.1.0 → 1.2.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 +14 -12
- package/package.json +13 -4
- package/src/blobs/index.js +1 -1
- package/src/database/bootstrap.js +2 -2
- package/src/database/dispatch.js +41 -6
- package/src/database/index.js +177 -17
- package/src/identity/index.js +15 -1
- package/src/lib/errors.js +8 -0
- package/src/lib/utils.js +33 -12
- package/src/network/bluetooth.js +324 -0
- package/src/network/index.js +113 -8
- package/src/pairing/index.js +6 -6
- package/src/rpc/index.js +2 -2
- package/src/storage/index.js +30 -8
- package/types/database/dispatch.d.ts +2 -1
- package/types/database/index.d.ts +34 -8
- package/types/identity/index.d.ts +9 -0
- package/types/lib/errors.d.ts +6 -0
- package/types/lib/utils.d.ts +4 -6
- package/types/network/bluetooth.d.ts +93 -0
- package/types/network/index.d.ts +36 -1
- package/types/storage/index.d.ts +7 -1
- package/src/database/CLAUDE.md +0 -3
- package/src/identity/CLAUDE.md +0 -3
- package/src/lib/CLAUDE.md +0 -3
package/src/storage/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
|
|
1
3
|
import HypercoreStorage from 'hypercore-storage'
|
|
2
4
|
import Corestore from 'corestore'
|
|
3
5
|
import HyperDB from 'hyperdb'
|
|
@@ -7,12 +9,15 @@ import { ROCKS, BEE, SINGLE, COLLECTION } from '../lib/constants.js'
|
|
|
7
9
|
import { genId, subscribe } from '../lib/utils.js'
|
|
8
10
|
import { CeroError } from '../lib/errors.js'
|
|
9
11
|
|
|
12
|
+
const RANGE_OPS = new Set(['gt', 'gte', 'lt', 'lte', 'reverse', 'limit'])
|
|
13
|
+
|
|
10
14
|
/**
|
|
11
15
|
* @typedef {object} StorageOpts
|
|
12
16
|
* @property {{ database: any, meta?: { ns?: string, refs?: Record<string, { kind?: string }> } }} spec
|
|
13
17
|
* @property {'rocks' | 'bee'} backend
|
|
14
18
|
* @property {any} [root] Pre-existing HypercoreStorage to reuse.
|
|
15
19
|
* @property {any} [store] Pre-existing Corestore to reuse.
|
|
20
|
+
* @property {Uint8Array} [storageKey] 32-byte key encrypting the backing core at rest (bee backend only).
|
|
16
21
|
*
|
|
17
22
|
* @typedef {{ name: string, kind: string }} Ref
|
|
18
23
|
* @typedef {{ id?: string, createdAt: number, updatedAt: number, [k: string]: any }} StoredRow
|
|
@@ -31,17 +36,22 @@ export class Storage extends ReadyResource {
|
|
|
31
36
|
* @param {string} dir
|
|
32
37
|
* @param {StorageOpts} [opts]
|
|
33
38
|
*/
|
|
34
|
-
constructor(dir, { spec, backend, root, store } = {}) {
|
|
39
|
+
constructor(dir, { spec, backend, root, store, storageKey } = {}) {
|
|
35
40
|
super()
|
|
36
41
|
if (!root && !store && (typeof dir !== 'string' || !dir))
|
|
37
42
|
throw CeroError.REQUIRED('dir, root, or store')
|
|
38
43
|
if (!spec || !spec.database) throw CeroError.REQUIRED('spec.database')
|
|
39
44
|
if (backend !== ROCKS && backend !== BEE)
|
|
40
45
|
throw CeroError.INVALID('backend must be "rocks" or "bee"')
|
|
46
|
+
if (storageKey && backend === ROCKS)
|
|
47
|
+
throw CeroError.INVALID('storageKey requires the bee backend')
|
|
48
|
+
if (storageKey && storageKey.byteLength !== 32)
|
|
49
|
+
throw CeroError.INVALID('storageKey must be 32 bytes')
|
|
41
50
|
|
|
42
51
|
this.dir = dir
|
|
43
52
|
this.spec = spec
|
|
44
53
|
this.backend = backend
|
|
54
|
+
this.storageKey = storageKey || null
|
|
45
55
|
this.ns = spec.meta?.ns || 'cero'
|
|
46
56
|
this.refs = spec.meta?.refs || {}
|
|
47
57
|
|
|
@@ -77,6 +87,7 @@ export class Storage extends ReadyResource {
|
|
|
77
87
|
if (this._ownsRoot) {
|
|
78
88
|
this.root = new HypercoreStorage(this.dir, { columnFamilies: [this._cf] })
|
|
79
89
|
await this.root.ready()
|
|
90
|
+
await fs.promises.chmod(this.dir, 0o700)
|
|
80
91
|
}
|
|
81
92
|
|
|
82
93
|
if (this._ownsStore) {
|
|
@@ -90,7 +101,7 @@ export class Storage extends ReadyResource {
|
|
|
90
101
|
const cf = this.root.rocks.columnFamily(this._cf)
|
|
91
102
|
this.db = HyperDB.rocks(cf, this.spec.database)
|
|
92
103
|
} else {
|
|
93
|
-
const core = this.store.get({ name: 'local' })
|
|
104
|
+
const core = this.store.get({ name: 'local', encryptionKey: this.storageKey })
|
|
94
105
|
await core.ready()
|
|
95
106
|
this.db = HyperDB.bee(core, this.spec.database, { extension: false, autoUpdate: true })
|
|
96
107
|
}
|
|
@@ -193,9 +204,19 @@ export class Storage extends ReadyResource {
|
|
|
193
204
|
return { data: (await this.db.get(col, { id: query })) ?? null }
|
|
194
205
|
}
|
|
195
206
|
|
|
207
|
+
// hyperdb only honors range/limit operators — equality fields must be
|
|
208
|
+
// filtered here or they are silently ignored and every row comes back
|
|
209
|
+
const eq = Object.keys(query || {}).filter((k) => !RANGE_OPS.has(k))
|
|
210
|
+
if (eq.length) {
|
|
211
|
+
const rows = await this.db.find(col, {}).toArray()
|
|
212
|
+
const matched = rows.filter((r) => eq.every((k) => r[k] === query[k]))
|
|
213
|
+
const data = query.limit != null ? matched.slice(0, query.limit) : matched
|
|
214
|
+
return { data, total: matched.length, size: data.length }
|
|
215
|
+
}
|
|
216
|
+
|
|
196
217
|
const data = await this.db.find(col, query || {}).toArray()
|
|
197
|
-
// a
|
|
198
|
-
//
|
|
218
|
+
// a limit caps `data`, so `total` needs a full count; with no query
|
|
219
|
+
// `data` is already everything — skip the second scan.
|
|
199
220
|
const filtered = query && Object.keys(query).length > 0
|
|
200
221
|
const total = filtered ? (await this.db.find(col, {}).toArray()).length : data.length
|
|
201
222
|
return { data, total, size: data.length }
|
|
@@ -212,8 +233,10 @@ export class Storage extends ReadyResource {
|
|
|
212
233
|
this._guard()
|
|
213
234
|
const ref = this._ref(name)
|
|
214
235
|
const col = this._col(ref)
|
|
215
|
-
const
|
|
216
|
-
|
|
236
|
+
const eq = Object.keys(query || {}).filter((k) => !RANGE_OPS.has(k))
|
|
237
|
+
const rows = await this.db.find(col, eq.length ? {} : query || {}).toArray()
|
|
238
|
+
const matched = eq.length ? rows.filter((r) => eq.every((k) => r[k] === query[k])) : rows
|
|
239
|
+
return { data: matched.length }
|
|
217
240
|
}
|
|
218
241
|
|
|
219
242
|
/**
|
|
@@ -259,8 +282,7 @@ export class Storage extends ReadyResource {
|
|
|
259
282
|
async _read(ref, id) {
|
|
260
283
|
if (ref.kind === SINGLE) return this.db.findOne(this._col(ref), {})
|
|
261
284
|
if (id == null) return null
|
|
262
|
-
|
|
263
|
-
return rows.find((r) => r.id === id) ?? null
|
|
285
|
+
return (await this.db.get(this._col(ref), { id })) ?? null
|
|
264
286
|
}
|
|
265
287
|
|
|
266
288
|
/** @param {Ref} ref @param {Record<string, any>} row @returns {Promise<void>} */
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @param {{ dispatch: { Router: Function }, meta?: { refs?: Record<string, { kind?: string, builtin?: boolean, verb?: string }> } }} spec Generated hyperdispatch spec.
|
|
6
6
|
* @param {string} ns Namespace prefix for collection and op names.
|
|
7
7
|
* @param {Record<string, Function>} routes Custom action handlers keyed by route name.
|
|
8
|
+
* @param {(err: Error) => void} [onerror] Called when a malformed node is skipped.
|
|
8
9
|
* @returns {{ dispatcher: object, apply: (nodes: Array<{ value: Buffer, key: Buffer }>, view: object, host: object) => Promise<void> }}
|
|
9
10
|
*/
|
|
10
11
|
export function makeDispatcher(spec: {
|
|
@@ -18,7 +19,7 @@ export function makeDispatcher(spec: {
|
|
|
18
19
|
verb?: string;
|
|
19
20
|
}>;
|
|
20
21
|
};
|
|
21
|
-
}, ns: string, routes: Record<string, Function
|
|
22
|
+
}, ns: string, routes: Record<string, Function>, onerror?: (err: Error) => void, getDbKey?: () => any): {
|
|
22
23
|
dispatcher: object;
|
|
23
24
|
apply: (nodes: Array<{
|
|
24
25
|
value: Buffer;
|
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
* @property {Uint8Array | null} [encryptionKey] Optional encryption key; falls back to identity's key.
|
|
10
10
|
* @property {(nodes: any, view: any, host: any) => Promise<void>} [apply] Override the default apply function.
|
|
11
11
|
* @property {Uint8Array | null} [key] Existing autobee key to reopen.
|
|
12
|
+
* @property {boolean} [passive] Join discovery server-only (reachable but not searching). Flip at runtime with `setActive`.
|
|
12
13
|
* @property {import('../identity/index.js').KeyPair} [keyPair] Device writer keypair; defaults to identity's keypair.
|
|
13
|
-
* @property {(err: Error) => void} [onerror] Called when a background after-hook or onApply callback fails.
|
|
14
|
+
* @property {(err: Error) => void} [onerror] Called when a background after-hook or onApply callback fails, a malformed node is skipped, or the bee errors.
|
|
14
15
|
*
|
|
15
16
|
* @typedef {{ data: any | null }} SingleResult
|
|
16
|
-
* @typedef {{ data: any[], total: number, size: number }} ListResult
|
|
17
|
-
* @typedef {{ gt?: string, gte?: string, lt?: string, lte?: string, reverse?: boolean, limit?: number, search?: string, fields?: string[] }} Query
|
|
17
|
+
* @typedef {{ data: any[], total: number | null, size: number }} ListResult `total` is null when a limited read skipped the full count — pass `{ total: true }` to force it.
|
|
18
|
+
* @typedef {{ gt?: string, gte?: string, lt?: string, lte?: string, reverse?: boolean, limit?: number, search?: string, fields?: string[], total?: boolean }} Query
|
|
18
19
|
* @typedef {{ kind: string, verb: string, name: string }} Ref
|
|
19
20
|
* @typedef {(ctx: any) => any | Promise<any>} HookFn
|
|
20
21
|
*/
|
|
@@ -58,6 +59,7 @@ export class Database extends ReadyResource {
|
|
|
58
59
|
encryptionKey: Uint8Array<ArrayBufferLike>;
|
|
59
60
|
applyOverride: (nodes: any, view: any, host: any) => Promise<void>;
|
|
60
61
|
key: Uint8Array<ArrayBufferLike>;
|
|
62
|
+
passive: boolean;
|
|
61
63
|
keyPair: import("../index.js").KeyPair | {
|
|
62
64
|
publicKey: Uint8Array<ArrayBufferLike>;
|
|
63
65
|
secretKey: Uint8Array<ArrayBufferLike>;
|
|
@@ -73,13 +75,22 @@ export class Database extends ReadyResource {
|
|
|
73
75
|
};
|
|
74
76
|
beforeHooks: Map<any, any>;
|
|
75
77
|
afterHooks: Map<any, any>;
|
|
76
|
-
updaters:
|
|
78
|
+
updaters: Map<any, any>;
|
|
77
79
|
onApplyHooks: Set<any>;
|
|
78
80
|
_applySeq: number;
|
|
81
|
+
_touched: Set<any>;
|
|
79
82
|
txQueue: any[];
|
|
80
83
|
_discovery: import("../network/discovery.js").Discovery;
|
|
81
84
|
/** Open the underlying autobee, wire dispatcher + apply, attach to network. */
|
|
82
85
|
openBee(): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Flip announce mode at runtime — passive stays reachable (server) but
|
|
88
|
+
* stops actively looking (client). Cheap; use it to demote idle rooms.
|
|
89
|
+
*
|
|
90
|
+
* @param {boolean} active
|
|
91
|
+
* @returns {Promise<void>}
|
|
92
|
+
*/
|
|
93
|
+
setActive(active: boolean): Promise<void>;
|
|
83
94
|
/** @returns {Uint8Array | null} discovery key of the underlying bee */
|
|
84
95
|
get discoveryKey(): Uint8Array | null;
|
|
85
96
|
/** @returns {Uint8Array | null} this device's local writer key */
|
|
@@ -107,12 +118,16 @@ export class Database extends ReadyResource {
|
|
|
107
118
|
*/
|
|
108
119
|
after(op: string, fn: HookFn): () => void;
|
|
109
120
|
/**
|
|
110
|
-
* Subscribe to local apply notifications. Fires whenever the view updates
|
|
121
|
+
* Subscribe to local apply notifications. Fires whenever the view updates;
|
|
122
|
+
* pass `scope` (a ref name) to fire only when that ref was touched.
|
|
111
123
|
*
|
|
112
124
|
* @param {() => void} fn
|
|
125
|
+
* @param {string} [scope]
|
|
113
126
|
* @returns {() => void} disposer
|
|
114
127
|
*/
|
|
115
|
-
onUpdate(fn: () => void): () => void;
|
|
128
|
+
onUpdate(fn: () => void, scope?: string): () => void;
|
|
129
|
+
_touch(nodes: any): void;
|
|
130
|
+
_byVerb: Map<any, any>;
|
|
116
131
|
/**
|
|
117
132
|
* Observe every applied op — local AND replicated (apply processes the merged
|
|
118
133
|
* log). The callback receives `{ op, name, row, writerKey, seq }` and runs
|
|
@@ -210,6 +225,9 @@ export class Database extends ReadyResource {
|
|
|
210
225
|
* @returns {Promise<SingleResult | ListResult>}
|
|
211
226
|
*/
|
|
212
227
|
get(name: string, query?: string | Query): Promise<SingleResult | ListResult>;
|
|
228
|
+
_orderReady(name: any, col: any): Promise<any>;
|
|
229
|
+
_orderOk: Map<any, any>;
|
|
230
|
+
_total(col: any, query: any, rows: any): Promise<any>;
|
|
213
231
|
/**
|
|
214
232
|
* Number of rows that match `query` (or total if omitted).
|
|
215
233
|
*
|
|
@@ -359,21 +377,28 @@ export type DatabaseOpts = {
|
|
|
359
377
|
* Existing autobee key to reopen.
|
|
360
378
|
*/
|
|
361
379
|
key?: Uint8Array | null;
|
|
380
|
+
/**
|
|
381
|
+
* Join discovery server-only (reachable but not searching). Flip at runtime with `setActive`.
|
|
382
|
+
*/
|
|
383
|
+
passive?: boolean;
|
|
362
384
|
/**
|
|
363
385
|
* Device writer keypair; defaults to identity's keypair.
|
|
364
386
|
*/
|
|
365
387
|
keyPair?: import("../identity/index.js").KeyPair;
|
|
366
388
|
/**
|
|
367
|
-
* Called when a background after-hook or onApply callback fails.
|
|
389
|
+
* Called when a background after-hook or onApply callback fails, a malformed node is skipped, or the bee errors.
|
|
368
390
|
*/
|
|
369
391
|
onerror?: (err: Error) => void;
|
|
370
392
|
};
|
|
371
393
|
export type SingleResult = {
|
|
372
394
|
data: any | null;
|
|
373
395
|
};
|
|
396
|
+
/**
|
|
397
|
+
* `total` is null when a limited read skipped the full count — pass `{ total: true }` to force it.
|
|
398
|
+
*/
|
|
374
399
|
export type ListResult = {
|
|
375
400
|
data: any[];
|
|
376
|
-
total: number;
|
|
401
|
+
total: number | null;
|
|
377
402
|
size: number;
|
|
378
403
|
};
|
|
379
404
|
export type Query = {
|
|
@@ -385,6 +410,7 @@ export type Query = {
|
|
|
385
410
|
limit?: number;
|
|
386
411
|
search?: string;
|
|
387
412
|
fields?: string[];
|
|
413
|
+
total?: boolean;
|
|
388
414
|
};
|
|
389
415
|
export type Ref = {
|
|
390
416
|
kind: string;
|
|
@@ -125,6 +125,15 @@ export class Identity {
|
|
|
125
125
|
* @type {Uint8Array}
|
|
126
126
|
*/
|
|
127
127
|
seed: Uint8Array;
|
|
128
|
+
/**
|
|
129
|
+
* Redacted — secretKey/encryptionKey/seed must never reach JSON.stringify
|
|
130
|
+
* or a structured logger.
|
|
131
|
+
*
|
|
132
|
+
* @returns {{ id: string }}
|
|
133
|
+
*/
|
|
134
|
+
toJSON(): {
|
|
135
|
+
id: string;
|
|
136
|
+
};
|
|
128
137
|
/**
|
|
129
138
|
* Detached Ed25519 signature over `message`.
|
|
130
139
|
*
|
package/types/lib/errors.d.ts
CHANGED
|
@@ -42,6 +42,12 @@ export class CeroError extends Error {
|
|
|
42
42
|
* @param {string} name
|
|
43
43
|
*/
|
|
44
44
|
static NOT_READY(resource: string, name: string): CeroError;
|
|
45
|
+
/**
|
|
46
|
+
* Operation raced an existing state (e.g. double init).
|
|
47
|
+
*
|
|
48
|
+
* @param {string} msg
|
|
49
|
+
*/
|
|
50
|
+
static CONFLICT(msg: string): CeroError;
|
|
45
51
|
/**
|
|
46
52
|
* Resource has been destroyed.
|
|
47
53
|
*
|
package/types/lib/utils.d.ts
CHANGED
|
@@ -39,11 +39,9 @@ export const toId: (key: Uint8Array) => string;
|
|
|
39
39
|
* @type {(id: string) => Uint8Array}
|
|
40
40
|
*/
|
|
41
41
|
export const toKey: (id: string) => Uint8Array;
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*/
|
|
47
|
-
export const isKeyId: (id: string) => boolean;
|
|
42
|
+
/** @type {(dbKey: Uint8Array, writer: Uint8Array, appender: Uint8Array) => Uint8Array} */
|
|
43
|
+
export const addWriterPayload: (dbKey: Uint8Array, writer: Uint8Array, appender: Uint8Array) => Uint8Array;
|
|
44
|
+
/** @type {(dbKey: Uint8Array, writer: Uint8Array) => Uint8Array} */
|
|
45
|
+
export const claimWriterPayload: (dbKey: Uint8Array, writer: Uint8Array) => Uint8Array;
|
|
48
46
|
export function grants(a: any, b: any): boolean;
|
|
49
47
|
export function outranks(a: any, b: any): boolean;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive a stable 128-bit BLE service UUID from a topic. Only devices that
|
|
3
|
+
* compute the same UUID (same channel / same invite) ever discover each other.
|
|
4
|
+
*
|
|
5
|
+
* @param {Uint8Array} topic
|
|
6
|
+
* @param {string} [tag] Namespace so channel and invite meshes never collide.
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
export function toServiceUUID(topic: Uint8Array, tag?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Dual-role BLE transport: advertises + scans one service UUID, opens an L2CAP
|
|
12
|
+
* channel to each discovered peer, and feeds it into `network.inject`. From
|
|
13
|
+
* there replication and pairing are transport-agnostic (see Network.inject).
|
|
14
|
+
*
|
|
15
|
+
* Choreography mirrors the proven bare-mobile-doctor worklet: server adds a
|
|
16
|
+
* readable PSM characteristic at startup and answers reads dynamically; the
|
|
17
|
+
* central connects, reads "<psm>:<nodeId>", tie-breaks, then opens the channel.
|
|
18
|
+
* `backend` is bare-bluetooth in production and a mock in tests.
|
|
19
|
+
*
|
|
20
|
+
* ponytail: capability-handshake DoS link-scoring is deferred — it needs a
|
|
21
|
+
* replication-progress signal (design §4b). v1 caps links + times out dials.
|
|
22
|
+
*
|
|
23
|
+
* @extends ReadyResource
|
|
24
|
+
*/
|
|
25
|
+
export class BluetoothTransport extends ReadyResource {
|
|
26
|
+
/**
|
|
27
|
+
* @param {object} opts
|
|
28
|
+
* @param {any} opts.backend bare-bluetooth-shaped module (Central, Server, Service, Characteristic).
|
|
29
|
+
* @param {import('./index.js').Network} opts.network
|
|
30
|
+
* @param {Uint8Array} opts.uuid The 32-byte topic the service UUID derives from.
|
|
31
|
+
* @param {Uint8Array} opts.nodeId Stable local id (identity/device key) for the initiate tie-break.
|
|
32
|
+
* @param {string} [opts.tag] UUID namespace (channel mesh vs invite mesh).
|
|
33
|
+
* @param {number} [opts.cap] Max concurrent links; gossip covers the rest.
|
|
34
|
+
* @param {{ scanMode?: any }} [opts.scanOptions] Platform scan options (e.g. Android low-power).
|
|
35
|
+
* @param {boolean} [opts.keepLinks] On close, stop the radio but leave established links alive (invite rendezvous: the link outlives the QR and carries the initial replication).
|
|
36
|
+
*/
|
|
37
|
+
constructor({ backend, network, uuid, nodeId, tag, cap, scanOptions, keepLinks }: {
|
|
38
|
+
backend: any;
|
|
39
|
+
network: import("./index.js").Network;
|
|
40
|
+
uuid: Uint8Array;
|
|
41
|
+
nodeId: Uint8Array;
|
|
42
|
+
tag?: string;
|
|
43
|
+
cap?: number;
|
|
44
|
+
scanOptions?: {
|
|
45
|
+
scanMode?: any;
|
|
46
|
+
};
|
|
47
|
+
keepLinks?: boolean;
|
|
48
|
+
});
|
|
49
|
+
backend: any;
|
|
50
|
+
network: import("./index.js").Network;
|
|
51
|
+
nodeId: Uint8Array<ArrayBufferLike>;
|
|
52
|
+
nodeHex: any;
|
|
53
|
+
serviceUUID: string;
|
|
54
|
+
cap: number;
|
|
55
|
+
scanOptions: {
|
|
56
|
+
scanMode?: any;
|
|
57
|
+
};
|
|
58
|
+
keepLinks: boolean;
|
|
59
|
+
state: string;
|
|
60
|
+
central: any;
|
|
61
|
+
server: any;
|
|
62
|
+
psm: any;
|
|
63
|
+
_scanning: boolean;
|
|
64
|
+
_advertising: boolean;
|
|
65
|
+
_serviceAdded: boolean;
|
|
66
|
+
/** peripheral id being dialed → its connect-timeout timer */
|
|
67
|
+
_dialing: Map<any, any>;
|
|
68
|
+
/** live injected links keyed by remote node id hex */
|
|
69
|
+
peers: Map<any, any>;
|
|
70
|
+
/**
|
|
71
|
+
* Whether we should be the one to open the connection to `peerNodeId`. The
|
|
72
|
+
* lexicographically smaller id initiates; the larger waits — so a pair
|
|
73
|
+
* connects once, not twice. Equal (our own reflection) → false.
|
|
74
|
+
*
|
|
75
|
+
* @param {Uint8Array} peerNodeId
|
|
76
|
+
* @returns {boolean}
|
|
77
|
+
*/
|
|
78
|
+
shouldInitiate(peerNodeId: Uint8Array): boolean;
|
|
79
|
+
get linkCount(): number;
|
|
80
|
+
_startServer(Service: any, Characteristic: any): void;
|
|
81
|
+
_maybeAdvertise(): void;
|
|
82
|
+
_startScan(): void;
|
|
83
|
+
_onState(raw: any): void;
|
|
84
|
+
_onDiscover(peripheral: any): void;
|
|
85
|
+
_onConnect(peripheral: any): void;
|
|
86
|
+
_abortDial(peripheral: any, _reason: any): void;
|
|
87
|
+
_clearDial(id: any): void;
|
|
88
|
+
_onCentralError(err: any): void;
|
|
89
|
+
_onChannel(l2cap: any, isInitiator: any, peripheralId: any): void;
|
|
90
|
+
_track(conn: any, peripheralId: any): void;
|
|
91
|
+
_untrack(conn: any): void;
|
|
92
|
+
}
|
|
93
|
+
import ReadyResource from 'ready-resource';
|
package/types/network/index.d.ts
CHANGED
|
@@ -28,9 +28,44 @@ export class Network extends ReadyResource {
|
|
|
28
28
|
wakeup: any;
|
|
29
29
|
_replicateables: Set<any>;
|
|
30
30
|
_discoveries: Set<any>;
|
|
31
|
+
_injected: Set<any>;
|
|
32
|
+
_blind: any;
|
|
33
|
+
/**
|
|
34
|
+
* Feed an externally-established connection — a Bluetooth L2CAP channel, a
|
|
35
|
+
* serial link, an in-process pair, any duplex — into the network. A raw
|
|
36
|
+
* duplex is wrapped in NoiseSecretStream (pass `isInitiator`); a stream
|
|
37
|
+
* that already IS one is used as-is. From here it gets the exact same
|
|
38
|
+
* treatment as a swarm connection: wakeup, replication of every attached
|
|
39
|
+
* core, pairing, and the 'connection' event.
|
|
40
|
+
*
|
|
41
|
+
* @param {any} stream Duplex transport, or a ready NoiseSecretStream.
|
|
42
|
+
* @param {{ isInitiator?: boolean }} [opts] Which side initiates the noise handshake (raw duplexes only).
|
|
43
|
+
* @returns {any} The encrypted connection stream.
|
|
44
|
+
*/
|
|
45
|
+
inject(stream: any, { isInitiator }?: {
|
|
46
|
+
isInitiator?: boolean;
|
|
47
|
+
}): any;
|
|
48
|
+
/**
|
|
49
|
+
* Lazily create the network-shared BlindPairing. One instance serves every
|
|
50
|
+
* handle's pairing member — per-handle instances each added their own swarm
|
|
51
|
+
* and DHT listeners plus a protomux channel per connection.
|
|
52
|
+
*
|
|
53
|
+
* @returns {Promise<any>}
|
|
54
|
+
*/
|
|
55
|
+
blind(): Promise<any>;
|
|
56
|
+
/**
|
|
57
|
+
* Re-attach pairing channels on injected connections. blind-pairing only
|
|
58
|
+
* auto-attaches refs that existed when a connection arrived — swarm peers
|
|
59
|
+
* meet again over topic joins, injected links (Bluetooth, pipes) don't, so
|
|
60
|
+
* a member/candidate added later must re-run the attach. Idempotent:
|
|
61
|
+
* protomux refuses duplicate channels.
|
|
62
|
+
*
|
|
63
|
+
* @returns {Promise<void>}
|
|
64
|
+
*/
|
|
65
|
+
refreshInjected(): Promise<void>;
|
|
31
66
|
/** @returns {Map<string, any>} Known peers keyed by public-key string. */
|
|
32
67
|
get peers(): Map<string, any>;
|
|
33
|
-
/** @returns {Set<any>} Live connection streams. */
|
|
68
|
+
/** @returns {Set<any>} Live connection streams — swarm and injected. */
|
|
34
69
|
get connections(): Set<any>;
|
|
35
70
|
/** @returns {boolean} */
|
|
36
71
|
get suspended(): boolean;
|
package/types/storage/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @property {'rocks' | 'bee'} backend
|
|
5
5
|
* @property {any} [root] Pre-existing HypercoreStorage to reuse.
|
|
6
6
|
* @property {any} [store] Pre-existing Corestore to reuse.
|
|
7
|
+
* @property {Uint8Array} [storageKey] 32-byte key encrypting the backing core at rest (bee backend only).
|
|
7
8
|
*
|
|
8
9
|
* @typedef {{ name: string, kind: string }} Ref
|
|
9
10
|
* @typedef {{ id?: string, createdAt: number, updatedAt: number, [k: string]: any }} StoredRow
|
|
@@ -35,7 +36,7 @@ export class Storage extends ReadyResource {
|
|
|
35
36
|
* @param {string} dir
|
|
36
37
|
* @param {StorageOpts} [opts]
|
|
37
38
|
*/
|
|
38
|
-
constructor(dir: string, { spec, backend, root, store }?: StorageOpts);
|
|
39
|
+
constructor(dir: string, { spec, backend, root, store, storageKey }?: StorageOpts);
|
|
39
40
|
dir: string;
|
|
40
41
|
spec: {
|
|
41
42
|
database: any;
|
|
@@ -47,6 +48,7 @@ export class Storage extends ReadyResource {
|
|
|
47
48
|
};
|
|
48
49
|
};
|
|
49
50
|
backend: "rocks" | "bee";
|
|
51
|
+
storageKey: Uint8Array<ArrayBufferLike>;
|
|
50
52
|
ns: string;
|
|
51
53
|
refs: Record<string, {
|
|
52
54
|
kind?: string;
|
|
@@ -143,6 +145,10 @@ export type StorageOpts = {
|
|
|
143
145
|
* Pre-existing Corestore to reuse.
|
|
144
146
|
*/
|
|
145
147
|
store?: any;
|
|
148
|
+
/**
|
|
149
|
+
* 32-byte key encrypting the backing core at rest (bee backend only).
|
|
150
|
+
*/
|
|
151
|
+
storageKey?: Uint8Array;
|
|
146
152
|
};
|
|
147
153
|
export type Ref = {
|
|
148
154
|
name: string;
|
package/src/database/CLAUDE.md
DELETED
package/src/identity/CLAUDE.md
DELETED
package/src/lib/CLAUDE.md
DELETED