@cero-base/cero 0.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/LICENSE +201 -0
- package/README.md +287 -0
- package/package.json +79 -0
- package/src/CLAUDE.md +3 -0
- package/src/builder.js +276 -0
- package/src/handle/CLAUDE.md +3 -0
- package/src/handle/index.js +600 -0
- package/src/index.js +170 -0
- package/src/lib/CLAUDE.md +3 -0
- package/src/lib/builtins.js +410 -0
- package/src/lib/operators.js +128 -0
- package/src/lib/peek.js +36 -0
- package/src/lib/spec.js +6 -0
- package/src/lib/utils.js +41 -0
- package/src/local/index.js +49 -0
- package/src/rpc/CLAUDE.md +3 -0
- package/src/rpc/client.js +337 -0
- package/src/rpc/index.js +8 -0
- package/src/rpc/server.js +290 -0
- package/types/builder.d.ts +32 -0
- package/types/handle/index.d.ts +353 -0
- package/types/index.d.ts +102 -0
- package/types/lib/builtins.d.ts +141 -0
- package/types/lib/operators.d.ts +29 -0
- package/types/lib/peek.d.ts +10 -0
- package/types/lib/spec.d.ts +1 -0
- package/types/lib/utils.d.ts +44 -0
- package/types/local/index.d.ts +33 -0
- package/types/rpc/client.d.ts +125 -0
- package/types/rpc/index.d.ts +2 -0
- package/types/rpc/server.d.ts +154 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { RPCServer, bindCodec } from '@cero-base/core/rpc'
|
|
2
|
+
import { CeroError } from '@cero-base/core/errors'
|
|
3
|
+
|
|
4
|
+
import { cero, restore } from '../index.js'
|
|
5
|
+
import { put, set, get, del, count, watch, call } from '../lib/operators.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('@cero-base/core/rpc').RPCServer} BaseRPCServer
|
|
9
|
+
*
|
|
10
|
+
* @typedef {object} ServerOpts
|
|
11
|
+
* @property {string} storage Directory passed to `cero()` for the local store.
|
|
12
|
+
* @property {object} spec Compiled cero spec (schema + rpc + handles).
|
|
13
|
+
* @property {string} [name] Optional display name forwarded to `cero()`.
|
|
14
|
+
* @property {Array<{ host: string, port: number }>} [bootstrap] Custom DHT bootstrap.
|
|
15
|
+
* @property {boolean} [isMobile]
|
|
16
|
+
* @property {(err: Error) => void} [onerror]
|
|
17
|
+
*
|
|
18
|
+
* @typedef {object} Identity
|
|
19
|
+
* @property {string} id Long-lived cero identity id.
|
|
20
|
+
* @property {string} deviceId Per-device id (empty when no `local` spec).
|
|
21
|
+
* @property {string|null} phrase BIP39 recovery phrase for the identity.
|
|
22
|
+
*
|
|
23
|
+
* @typedef {{ ref: any, codec: any }} RefAndCodec
|
|
24
|
+
*
|
|
25
|
+
* @typedef {{ data: any, total?: number, size?: number }} GetResult Single-ref get omits `total`/`size`; list/handle refs include them.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* IPC-side RPC server for cero. Bridges an `hrpc` channel to a live
|
|
30
|
+
* `Handle` tree: lazy-initializes the root via `cero()` on the first
|
|
31
|
+
* `init` call, then exposes data ops, pairing, and handle lifecycle
|
|
32
|
+
* over the wire using the spec-bound codec.
|
|
33
|
+
*/
|
|
34
|
+
export class Server extends RPCServer {
|
|
35
|
+
/**
|
|
36
|
+
* @param {any} ipc Framed IPC stream (must be writable).
|
|
37
|
+
* @param {Partial<ServerOpts>} [opts]
|
|
38
|
+
*/
|
|
39
|
+
constructor(ipc, { storage, spec, ...opts } = {}) {
|
|
40
|
+
if (!storage) throw CeroError.REQUIRED('storage')
|
|
41
|
+
if (!spec) throw CeroError.REQUIRED('spec')
|
|
42
|
+
super(ipc, spec)
|
|
43
|
+
this.storage = storage
|
|
44
|
+
this.opts = opts
|
|
45
|
+
this.me = null
|
|
46
|
+
this.handles = new Map()
|
|
47
|
+
this._wireInit()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Root cero id (null until `init` has run).
|
|
52
|
+
*
|
|
53
|
+
* @returns {string|undefined}
|
|
54
|
+
*/
|
|
55
|
+
get id() {
|
|
56
|
+
return this.me?.id
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Root identity object (null until `init` has run).
|
|
61
|
+
*
|
|
62
|
+
* @returns {any}
|
|
63
|
+
*/
|
|
64
|
+
get identity() {
|
|
65
|
+
return this.me?.identity
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async _close() {
|
|
69
|
+
if (this.me) {
|
|
70
|
+
try {
|
|
71
|
+
await this.me.close()
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
await super._close()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Wire the `init` handler that lazily constructs the root cero handle. */
|
|
78
|
+
_wireInit() {
|
|
79
|
+
this.rpc.onInit(async () => {
|
|
80
|
+
if (this.me) throw new CeroError('CONFLICT', 'already initialized')
|
|
81
|
+
this.me = await cero(this.storage, this.spec, this.opts)
|
|
82
|
+
this.handles.set(this.me.id, this.me)
|
|
83
|
+
this._wireData()
|
|
84
|
+
this._wirePairing()
|
|
85
|
+
this._wireHandles()
|
|
86
|
+
this._wireRestore()
|
|
87
|
+
return this._identity()
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Wire the `restore` handler that rebuilds the local store from a phrase. */
|
|
92
|
+
_wireRestore() {
|
|
93
|
+
this.rpc.onRestore(async ({ phrase }) => {
|
|
94
|
+
if (!this.me) throw new CeroError('NOT_READY', 'init')
|
|
95
|
+
this.me = await restore(this.me, phrase)
|
|
96
|
+
this.handles = new Map([[this.me.id, this.me]])
|
|
97
|
+
return this._identity()
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Register the row-level RPC handlers (put/set/get/del/count/watch/call). */
|
|
102
|
+
_wireData() {
|
|
103
|
+
this.rpc.onAddRow(async ({ handle, ref, data }) => {
|
|
104
|
+
const { ref: r, codec } = this._refOf(handle, ref)
|
|
105
|
+
const { data: row } = await put(r, codec.decodeRow(r.schema, data))
|
|
106
|
+
return { data: codec.encodeRow(r.schema, row) }
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
this.rpc.onSet(async ({ handle, ref, data }) => {
|
|
110
|
+
const { ref: r, codec } = this._refOf(handle, ref)
|
|
111
|
+
const { data: row } = await set(r, codec.decodeRow(r.schema, data))
|
|
112
|
+
return { data: codec.encodeRow(r.schema, row) }
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
this.rpc.onGet(async ({ handle, ref, query }) => {
|
|
116
|
+
const { ref: r, codec } = this._refOf(handle, ref)
|
|
117
|
+
const result = /** @type {GetResult} */ (await get(r, codec.decodeQuery(query)))
|
|
118
|
+
const data =
|
|
119
|
+
r.kind === 'single'
|
|
120
|
+
? codec.encodeRow(r.schema, result.data)
|
|
121
|
+
: codec.encodeRows(r.schema, result.data)
|
|
122
|
+
return { data, total: result.total ?? 0, size: result.size ?? 0 }
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
this.rpc.onGetOne(async ({ handle, ref, id }) => {
|
|
126
|
+
const { ref: r, codec } = this._refOf(handle, ref)
|
|
127
|
+
const { data } = await get(r, id)
|
|
128
|
+
return { data: data ? codec.encodeRow(r.schema, data) : null }
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
this.rpc.onDel(async ({ handle, ref, id }) => {
|
|
132
|
+
await del(this._refOf(handle, ref).ref, id)
|
|
133
|
+
return {}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
this.rpc.onCount(async ({ handle, ref, query }) => {
|
|
137
|
+
const { ref: r, codec } = this._refOf(handle, ref)
|
|
138
|
+
const { data } = await count(r, codec.decodeQuery(query))
|
|
139
|
+
return { count: data }
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
this.rpc.onWatch((stream) => {
|
|
143
|
+
const { handle, ref, query } = stream.data
|
|
144
|
+
let r, codec, live
|
|
145
|
+
try {
|
|
146
|
+
;({ ref: r, codec } = this._refOf(handle, ref))
|
|
147
|
+
live = watch(r, codec.decodeQuery(query))
|
|
148
|
+
} catch (err) {
|
|
149
|
+
stream.destroy(err)
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
const onData = (snap) => {
|
|
153
|
+
const data =
|
|
154
|
+
r.kind === 'single'
|
|
155
|
+
? codec.encodeRow(r.schema, snap.data)
|
|
156
|
+
: codec.encodeRows(r.schema, snap.data)
|
|
157
|
+
stream.write({ data, total: snap.total ?? 0, size: snap.size ?? 0 })
|
|
158
|
+
}
|
|
159
|
+
live.on('data', onData)
|
|
160
|
+
stream.on('close', () => {
|
|
161
|
+
live.off('data', onData)
|
|
162
|
+
live.destroy()
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
this.rpc.onCall(async ({ handle, op, data }) => {
|
|
167
|
+
const h = this._resolve(handle)
|
|
168
|
+
const r = h[op]
|
|
169
|
+
if (!r) throw CeroError.UNKNOWN('ref', op)
|
|
170
|
+
await call(r, h.spec.codec.decodeAction(h, op, data))
|
|
171
|
+
return { data: null }
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Register invite/revoke/join RPC handlers. */
|
|
176
|
+
_wirePairing() {
|
|
177
|
+
this.rpc.onInvite(async ({ handle, role }) => {
|
|
178
|
+
const h = this._resolve(handle)
|
|
179
|
+
const invite = await h.invite({ role: role || undefined })
|
|
180
|
+
return { invite }
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
this.rpc.onRevoke(async ({ handle, invite }) => {
|
|
184
|
+
const ok = this._resolve(handle).revoke(invite)
|
|
185
|
+
return { ok }
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
this.rpc.onJoin(async ({ parent, ref, invite }) => {
|
|
189
|
+
if (this._resolve(parent) !== this.me) throw CeroError.UNSUPPORTED('nested handles')
|
|
190
|
+
const child = await this.me._join(invite, ref)
|
|
191
|
+
const id = child.id
|
|
192
|
+
this.handles.set(id, child)
|
|
193
|
+
return { id, type: ref, name: '' }
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Register add/open/close/leave RPC handlers for child handles. */
|
|
198
|
+
_wireHandles() {
|
|
199
|
+
this.rpc.onAddHandle(async ({ handle, ref, data }) => {
|
|
200
|
+
const parent = this._resolve(handle)
|
|
201
|
+
const info = parent.spec.meta.refs?.[ref] || parent.spec.handles?.[ref]
|
|
202
|
+
if (!info) throw CeroError.UNKNOWN('handle type', ref)
|
|
203
|
+
const opts = parent.spec.codec.decodeCreate(data) || {}
|
|
204
|
+
const child = await this.me._create(ref, opts)
|
|
205
|
+
const id = child.id
|
|
206
|
+
this.handles.set(id, child)
|
|
207
|
+
return { id, type: ref, name: opts.name || '' }
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
this.rpc.onOpenHandle(async ({ parent, row }) => {
|
|
211
|
+
if (this._resolve(parent) !== this.me) throw CeroError.UNSUPPORTED('nested handles')
|
|
212
|
+
const { data } = await this.me.store.get('handles', row)
|
|
213
|
+
if (!data) throw CeroError.UNKNOWN('handle', row)
|
|
214
|
+
const child = await this.me._load(data.type, row)
|
|
215
|
+
this.handles.set(row, child)
|
|
216
|
+
return { id: row, type: data.type, name: data.name || '' }
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
this.rpc.onCloseHandle(async ({ handle }) => {
|
|
220
|
+
const h = this.handles.get(handle)
|
|
221
|
+
if (!h || h === this.me) return {}
|
|
222
|
+
this.handles.delete(handle)
|
|
223
|
+
await h.close()
|
|
224
|
+
return {}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
this.rpc.onLeave(async ({ handle }) => {
|
|
228
|
+
const h = this.handles.get(handle)
|
|
229
|
+
if (!h || h === this.me) return {}
|
|
230
|
+
await this.me.store.call('del-handle', { id: handle })
|
|
231
|
+
this.handles.delete(handle)
|
|
232
|
+
await h.close()
|
|
233
|
+
return {}
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Look up a live handle by id, throwing if unknown. Binds the handle's
|
|
239
|
+
* codec on first use.
|
|
240
|
+
*
|
|
241
|
+
* @param {string} id
|
|
242
|
+
* @returns {any}
|
|
243
|
+
*/
|
|
244
|
+
_resolve(id) {
|
|
245
|
+
const h = this.handles.get(id)
|
|
246
|
+
if (!h) throw CeroError.UNKNOWN('handle', id)
|
|
247
|
+
if (!h.spec.codec) bindCodec(h.spec)
|
|
248
|
+
return h
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Resolve a `{ handle, ref }` pair to its `Ref` and the handle's codec.
|
|
253
|
+
*
|
|
254
|
+
* @param {string} id
|
|
255
|
+
* @param {string} name
|
|
256
|
+
* @returns {RefAndCodec}
|
|
257
|
+
*/
|
|
258
|
+
_refOf(id, name) {
|
|
259
|
+
const h = this._resolve(id)
|
|
260
|
+
const r = h[name]
|
|
261
|
+
if (!r) throw CeroError.UNKNOWN('ref', name)
|
|
262
|
+
return { ref: r, codec: h.spec.codec }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Snapshot the current identity for return to the client.
|
|
267
|
+
*
|
|
268
|
+
* @returns {Identity}
|
|
269
|
+
*/
|
|
270
|
+
_identity() {
|
|
271
|
+
return {
|
|
272
|
+
id: this.me.id,
|
|
273
|
+
deviceId: this.me.device?.id || '',
|
|
274
|
+
phrase: this.me.identity.toPhrase()
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Construct a `Server`, wait for it to be ready, and return it.
|
|
281
|
+
*
|
|
282
|
+
* @param {any} ipc
|
|
283
|
+
* @param {ServerOpts} opts
|
|
284
|
+
* @returns {Promise<Server>}
|
|
285
|
+
*/
|
|
286
|
+
export async function serve(ipc, opts) {
|
|
287
|
+
const server = new Server(ipc, opts)
|
|
288
|
+
await server.ready()
|
|
289
|
+
return server
|
|
290
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('@cero-base/core/schema').Schema} Schema
|
|
3
|
+
* @typedef {import('@cero-base/core/schema').SchemaDefs} SchemaDefs
|
|
4
|
+
* @typedef {Schema | (SchemaDefs & { local?: SchemaDefs })} SchemaInput
|
|
5
|
+
*
|
|
6
|
+
* @typedef {object} BuildOpts
|
|
7
|
+
* @property {string} [ns] Namespace prefix for emitted schema ids. Defaults to `'cero'`.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Compile a cero schema into wire-level artifacts and write them to disk.
|
|
11
|
+
*
|
|
12
|
+
* Emits a `main/` tree (schema + hyperdb + dispatch + rpc), a `local/` tree
|
|
13
|
+
* for per-device data, one `handles/<name>/` tree per child handle type, and
|
|
14
|
+
* an `index.js` that re-exports a ready-to-use `spec` object.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} specDir Output directory.
|
|
17
|
+
* @param {SchemaInput} schema Either a `schema(...)` wrapper or its raw defs object.
|
|
18
|
+
* @param {BuildOpts} [opts]
|
|
19
|
+
* @returns {Promise<void>}
|
|
20
|
+
*/
|
|
21
|
+
export function build(specDir: string, schema: SchemaInput, { ns }?: BuildOpts): Promise<void>;
|
|
22
|
+
export type Schema = import("@cero-base/core/schema").Schema;
|
|
23
|
+
export type SchemaDefs = import("@cero-base/core/schema").SchemaDefs;
|
|
24
|
+
export type SchemaInput = Schema | (SchemaDefs & {
|
|
25
|
+
local?: SchemaDefs;
|
|
26
|
+
});
|
|
27
|
+
export type BuildOpts = {
|
|
28
|
+
/**
|
|
29
|
+
* Namespace prefix for emitted schema ids. Defaults to `'cero'`.
|
|
30
|
+
*/
|
|
31
|
+
ns?: string;
|
|
32
|
+
};
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
export { Ref } from "../lib/utils.js";
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('@cero-base/core/network').Network} Network
|
|
4
|
+
* @typedef {import('../local/index.js').Local} Local
|
|
5
|
+
* @typedef {import('@cero-base/core/identity').KeyPair} KeyPair
|
|
6
|
+
*
|
|
7
|
+
* @typedef {object} HandleOpts
|
|
8
|
+
* @property {Handle} [parent] Parent handle when this is a child slot.
|
|
9
|
+
* @property {Identity} [identity] Long-lived user identity. Inherited from `parent` if omitted.
|
|
10
|
+
* @property {Network} [network] Shared swarm. Inherited from `parent` if omitted.
|
|
11
|
+
* @property {any} [store] Pre-existing Corestore. Falls back to `parent.store.store`.
|
|
12
|
+
* @property {any} [spec] Built cero spec.
|
|
13
|
+
* @property {Local} [local] Local store for per-handle keypairs.
|
|
14
|
+
* @property {any} [storage] Owned HypercoreStorage to close on shutdown.
|
|
15
|
+
* @property {any} [discovery] Owned identity Discovery to destroy on shutdown.
|
|
16
|
+
* @property {string} [dir] Data directory (root handles only).
|
|
17
|
+
* @property {any} [opts] Pass-through cero(...) options.
|
|
18
|
+
* @property {Record<string, Function>} [routes]
|
|
19
|
+
* @property {Uint8Array} [key] Existing database key.
|
|
20
|
+
* @property {Uint8Array} [encryptionKey] Existing encryption key.
|
|
21
|
+
* @property {string} [namespace] Corestore namespace.
|
|
22
|
+
* @property {KeyPair} [keyPair] Writer keypair.
|
|
23
|
+
* @property {boolean} [pair] When `false`, skips creating a `Pairing` session.
|
|
24
|
+
*
|
|
25
|
+
* @typedef {object} CreateChildOpts
|
|
26
|
+
* @property {string | null} [name]
|
|
27
|
+
* @property {Record<string, Function>} [routes]
|
|
28
|
+
* @property {string} [role]
|
|
29
|
+
* @property {boolean} [accept]
|
|
30
|
+
*
|
|
31
|
+
* @typedef {object} JoinChildOpts
|
|
32
|
+
* @property {Record<string, Function>} [routes]
|
|
33
|
+
* @property {number} [timeout]
|
|
34
|
+
*
|
|
35
|
+
* @typedef {object} StaticJoinOpts
|
|
36
|
+
* @property {Handle} [parent]
|
|
37
|
+
* @property {Network} [network]
|
|
38
|
+
* @property {Identity} [identity]
|
|
39
|
+
* @property {any} [store]
|
|
40
|
+
* @property {any} [spec]
|
|
41
|
+
* @property {string} [namespace]
|
|
42
|
+
* @property {Record<string, Function>} [routes]
|
|
43
|
+
* @property {number} [timeout]
|
|
44
|
+
*
|
|
45
|
+
* @typedef {object} AcceptOpts
|
|
46
|
+
* @property {string} [role] Role to grant the joining peer. Falls back to the invite's role, then `'write'`.
|
|
47
|
+
* @property {string | null} [name]
|
|
48
|
+
*
|
|
49
|
+
* @typedef {object} RecoverOpts
|
|
50
|
+
* @property {number} [timeout]
|
|
51
|
+
* @property {string | null} [name]
|
|
52
|
+
* @property {boolean} [isMobile]
|
|
53
|
+
*
|
|
54
|
+
* @typedef {object} HandleExtra
|
|
55
|
+
* @property {string | null} [name] Display name; set on child handles by the owner flow.
|
|
56
|
+
* @property {import('../lib/utils.js').Ref} [profile] `profile` ref, attached dynamically when the schema declares one.
|
|
57
|
+
* @property {import('../lib/utils.js').Ref} [members] `members` ref, attached dynamically when the schema declares one.
|
|
58
|
+
*
|
|
59
|
+
* @typedef {Handle & HandleExtra} Child A child handle plus its dynamically-attached refs.
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* A cero handle — a single writable database session attached to a swarm.
|
|
63
|
+
* The "root" handle is the user's facade; child handles (created via
|
|
64
|
+
* `_create`/`_join`/`_load`) live under it and share the same identity,
|
|
65
|
+
* network and corestore. Ref properties (`profile`, `members`, ...) are
|
|
66
|
+
* attached dynamically per the schema; child handles also carry a `name`.
|
|
67
|
+
*/
|
|
68
|
+
export class Handle extends ReadyResource {
|
|
69
|
+
/**
|
|
70
|
+
* Pair into an existing handle via an invite, returning a brand-new
|
|
71
|
+
* `Handle` already configured with the resolved key + encryption key.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} invite
|
|
74
|
+
* @param {StaticJoinOpts} [opts]
|
|
75
|
+
* @returns {Promise<Handle>}
|
|
76
|
+
*/
|
|
77
|
+
static join(invite: string, { parent, network, identity, store, spec, namespace, routes, timeout }?: StaticJoinOpts): Promise<Handle>;
|
|
78
|
+
/** @param {HandleOpts} [opts] */
|
|
79
|
+
constructor(opts?: HandleOpts);
|
|
80
|
+
identity: any;
|
|
81
|
+
network: any;
|
|
82
|
+
spec: any;
|
|
83
|
+
parent: Handle;
|
|
84
|
+
local: import("../index.js").Local;
|
|
85
|
+
_storage: any;
|
|
86
|
+
_discovery: any;
|
|
87
|
+
_dir: string;
|
|
88
|
+
_opts: any;
|
|
89
|
+
_onerror: any;
|
|
90
|
+
children: Set<any>;
|
|
91
|
+
store: Database;
|
|
92
|
+
pair: Pairing;
|
|
93
|
+
_wantsPair: boolean;
|
|
94
|
+
_offUpdate: () => void;
|
|
95
|
+
/** Canonical id — identity id for the root handle, store key for children. */
|
|
96
|
+
get id(): any;
|
|
97
|
+
/** This device's id + name. `null` on child handles. */
|
|
98
|
+
get device(): {
|
|
99
|
+
id: string;
|
|
100
|
+
name: any;
|
|
101
|
+
};
|
|
102
|
+
get suspended(): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Initialise a fresh database: write the genesis claim, derive the writer.
|
|
105
|
+
* Forwards to `Database.bootstrap`.
|
|
106
|
+
*
|
|
107
|
+
* @param {any} [opts]
|
|
108
|
+
* @returns {Promise<any>}
|
|
109
|
+
*/
|
|
110
|
+
bootstrap(opts?: any): Promise<any>;
|
|
111
|
+
/**
|
|
112
|
+
* Claim writer capability on an existing database (paired-device flow).
|
|
113
|
+
* Forwards to `Database.claim`.
|
|
114
|
+
*
|
|
115
|
+
* @param {{ name?: string | null, isMobile?: boolean }} [opts]
|
|
116
|
+
* @returns {Promise<void>}
|
|
117
|
+
*/
|
|
118
|
+
claim(opts?: {
|
|
119
|
+
name?: string | null;
|
|
120
|
+
isMobile?: boolean;
|
|
121
|
+
}): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Claim + wait until this peer becomes a writer + bring the bee up to date.
|
|
124
|
+
* Used by `restore()` after wiping local state.
|
|
125
|
+
*
|
|
126
|
+
* @param {RecoverOpts} [opts]
|
|
127
|
+
* @returns {Promise<void>}
|
|
128
|
+
*/
|
|
129
|
+
recover({ timeout, name, isMobile }?: RecoverOpts): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Mint a pairing invite for this handle.
|
|
132
|
+
*
|
|
133
|
+
* @param {{ role?: string, expiresIn?: number, data?: any }} [opts]
|
|
134
|
+
* @returns {Promise<string>} Z32-encoded invite string.
|
|
135
|
+
*/
|
|
136
|
+
invite(opts?: {
|
|
137
|
+
role?: string;
|
|
138
|
+
expiresIn?: number;
|
|
139
|
+
data?: any;
|
|
140
|
+
}): Promise<string>;
|
|
141
|
+
/**
|
|
142
|
+
* Revoke a previously-minted invite by its string form.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} invite
|
|
145
|
+
* @returns {boolean} `true` if the invite was found and removed.
|
|
146
|
+
*/
|
|
147
|
+
revoke(invite: string): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Accept a paired candidate — adds them as a writer (or read-only member)
|
|
150
|
+
* and confirms the pairing so they receive this handle's keys.
|
|
151
|
+
*
|
|
152
|
+
* @param {any} candidate
|
|
153
|
+
* @param {AcceptOpts} [opts]
|
|
154
|
+
* @returns {Promise<void>}
|
|
155
|
+
*/
|
|
156
|
+
accept(candidate: any, { role, name }?: AcceptOpts): Promise<void>;
|
|
157
|
+
/**
|
|
158
|
+
* Leave a child handle — removes it from the parent's `handles` collection
|
|
159
|
+
* and closes the session. No-op on root handles.
|
|
160
|
+
*
|
|
161
|
+
* @returns {Promise<void>}
|
|
162
|
+
*/
|
|
163
|
+
leave(): Promise<void>;
|
|
164
|
+
/**
|
|
165
|
+
* Create a new child handle of `type`. Owner-flow — generates a fresh
|
|
166
|
+
* writer, adds it as a writer + member, and registers the child on the
|
|
167
|
+
* parent's `handles` collection.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} type
|
|
170
|
+
* @param {CreateChildOpts} [opts]
|
|
171
|
+
* @returns {Promise<Handle>}
|
|
172
|
+
*/
|
|
173
|
+
_create(type: string, { name, routes, role, accept }?: CreateChildOpts): Promise<Handle>;
|
|
174
|
+
/**
|
|
175
|
+
* Join a child handle by invite (joiner-flow). Waits for writer
|
|
176
|
+
* capability and registers the child on the parent's `handles`
|
|
177
|
+
* collection.
|
|
178
|
+
*
|
|
179
|
+
* @param {string} invite
|
|
180
|
+
* @param {string} type
|
|
181
|
+
* @param {JoinChildOpts} [opts]
|
|
182
|
+
* @returns {Promise<Handle>}
|
|
183
|
+
*/
|
|
184
|
+
_join(invite: string, type: string, { routes, timeout }?: JoinChildOpts): Promise<Handle>;
|
|
185
|
+
/**
|
|
186
|
+
* Re-open an existing child handle by id. Reuses the stored writer
|
|
187
|
+
* keypair if available; otherwise generates a fresh one and claims
|
|
188
|
+
* writer capability.
|
|
189
|
+
*
|
|
190
|
+
* @param {string} type
|
|
191
|
+
* @param {string} id
|
|
192
|
+
* @returns {Promise<Handle>}
|
|
193
|
+
*/
|
|
194
|
+
_load(type: string, id: string): Promise<Handle>;
|
|
195
|
+
/**
|
|
196
|
+
* Pause networking + storage. Idempotent; no-op on child handles.
|
|
197
|
+
*
|
|
198
|
+
* @returns {Promise<void>}
|
|
199
|
+
*/
|
|
200
|
+
suspend(): Promise<void>;
|
|
201
|
+
_suspended: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Resume a suspended root handle. Idempotent; no-op on child handles.
|
|
204
|
+
*
|
|
205
|
+
* @returns {Promise<void>}
|
|
206
|
+
*/
|
|
207
|
+
resume(): Promise<void>;
|
|
208
|
+
_syncMember(child: any): Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* @param {Handle} child
|
|
211
|
+
* @param {{ role?: string }} [opts]
|
|
212
|
+
*/
|
|
213
|
+
_wireAccept(child: Handle, { role }?: {
|
|
214
|
+
role?: string;
|
|
215
|
+
}): void;
|
|
216
|
+
/**
|
|
217
|
+
* @param {string} id
|
|
218
|
+
* @param {KeyPair | { publicKey: Uint8Array, secretKey: Uint8Array } | null} keyPair
|
|
219
|
+
* @returns {Promise<void>}
|
|
220
|
+
*/
|
|
221
|
+
_saveKeyPair(id: string, keyPair: KeyPair | {
|
|
222
|
+
publicKey: Uint8Array;
|
|
223
|
+
secretKey: Uint8Array;
|
|
224
|
+
} | null): Promise<void>;
|
|
225
|
+
/**
|
|
226
|
+
* @param {string} id
|
|
227
|
+
* @returns {Promise<{ publicKey: Uint8Array, secretKey: Uint8Array } | null>}
|
|
228
|
+
*/
|
|
229
|
+
_loadKeyPair(id: string): Promise<{
|
|
230
|
+
publicKey: Uint8Array;
|
|
231
|
+
secretKey: Uint8Array;
|
|
232
|
+
} | null>;
|
|
233
|
+
}
|
|
234
|
+
export type Network = import("@cero-base/core/network").Network;
|
|
235
|
+
export type Local = import("../local/index.js").Local;
|
|
236
|
+
export type KeyPair = import("@cero-base/core/identity").KeyPair;
|
|
237
|
+
export type HandleOpts = {
|
|
238
|
+
/**
|
|
239
|
+
* Parent handle when this is a child slot.
|
|
240
|
+
*/
|
|
241
|
+
parent?: Handle;
|
|
242
|
+
/**
|
|
243
|
+
* Long-lived user identity. Inherited from `parent` if omitted.
|
|
244
|
+
*/
|
|
245
|
+
identity?: Identity;
|
|
246
|
+
/**
|
|
247
|
+
* Shared swarm. Inherited from `parent` if omitted.
|
|
248
|
+
*/
|
|
249
|
+
network?: Network;
|
|
250
|
+
/**
|
|
251
|
+
* Pre-existing Corestore. Falls back to `parent.store.store`.
|
|
252
|
+
*/
|
|
253
|
+
store?: any;
|
|
254
|
+
/**
|
|
255
|
+
* Built cero spec.
|
|
256
|
+
*/
|
|
257
|
+
spec?: any;
|
|
258
|
+
/**
|
|
259
|
+
* Local store for per-handle keypairs.
|
|
260
|
+
*/
|
|
261
|
+
local?: Local;
|
|
262
|
+
/**
|
|
263
|
+
* Owned HypercoreStorage to close on shutdown.
|
|
264
|
+
*/
|
|
265
|
+
storage?: any;
|
|
266
|
+
/**
|
|
267
|
+
* Owned identity Discovery to destroy on shutdown.
|
|
268
|
+
*/
|
|
269
|
+
discovery?: any;
|
|
270
|
+
/**
|
|
271
|
+
* Data directory (root handles only).
|
|
272
|
+
*/
|
|
273
|
+
dir?: string;
|
|
274
|
+
/**
|
|
275
|
+
* Pass-through cero(...) options.
|
|
276
|
+
*/
|
|
277
|
+
opts?: any;
|
|
278
|
+
routes?: Record<string, Function>;
|
|
279
|
+
/**
|
|
280
|
+
* Existing database key.
|
|
281
|
+
*/
|
|
282
|
+
key?: Uint8Array;
|
|
283
|
+
/**
|
|
284
|
+
* Existing encryption key.
|
|
285
|
+
*/
|
|
286
|
+
encryptionKey?: Uint8Array;
|
|
287
|
+
/**
|
|
288
|
+
* Corestore namespace.
|
|
289
|
+
*/
|
|
290
|
+
namespace?: string;
|
|
291
|
+
/**
|
|
292
|
+
* Writer keypair.
|
|
293
|
+
*/
|
|
294
|
+
keyPair?: KeyPair;
|
|
295
|
+
/**
|
|
296
|
+
* When `false`, skips creating a `Pairing` session.
|
|
297
|
+
*/
|
|
298
|
+
pair?: boolean;
|
|
299
|
+
};
|
|
300
|
+
export type CreateChildOpts = {
|
|
301
|
+
name?: string | null;
|
|
302
|
+
routes?: Record<string, Function>;
|
|
303
|
+
role?: string;
|
|
304
|
+
accept?: boolean;
|
|
305
|
+
};
|
|
306
|
+
export type JoinChildOpts = {
|
|
307
|
+
routes?: Record<string, Function>;
|
|
308
|
+
timeout?: number;
|
|
309
|
+
};
|
|
310
|
+
export type StaticJoinOpts = {
|
|
311
|
+
parent?: Handle;
|
|
312
|
+
network?: Network;
|
|
313
|
+
identity?: Identity;
|
|
314
|
+
store?: any;
|
|
315
|
+
spec?: any;
|
|
316
|
+
namespace?: string;
|
|
317
|
+
routes?: Record<string, Function>;
|
|
318
|
+
timeout?: number;
|
|
319
|
+
};
|
|
320
|
+
export type AcceptOpts = {
|
|
321
|
+
/**
|
|
322
|
+
* Role to grant the joining peer. Falls back to the invite's role, then `'write'`.
|
|
323
|
+
*/
|
|
324
|
+
role?: string;
|
|
325
|
+
name?: string | null;
|
|
326
|
+
};
|
|
327
|
+
export type RecoverOpts = {
|
|
328
|
+
timeout?: number;
|
|
329
|
+
name?: string | null;
|
|
330
|
+
isMobile?: boolean;
|
|
331
|
+
};
|
|
332
|
+
export type HandleExtra = {
|
|
333
|
+
/**
|
|
334
|
+
* Display name; set on child handles by the owner flow.
|
|
335
|
+
*/
|
|
336
|
+
name?: string | null;
|
|
337
|
+
/**
|
|
338
|
+
* `profile` ref, attached dynamically when the schema declares one.
|
|
339
|
+
*/
|
|
340
|
+
profile?: import("../lib/utils.js").Ref;
|
|
341
|
+
/**
|
|
342
|
+
* `members` ref, attached dynamically when the schema declares one.
|
|
343
|
+
*/
|
|
344
|
+
members?: import("../lib/utils.js").Ref;
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* A child handle plus its dynamically-attached refs.
|
|
348
|
+
*/
|
|
349
|
+
export type Child = Handle & HandleExtra;
|
|
350
|
+
import ReadyResource from 'ready-resource';
|
|
351
|
+
import { Database } from '@cero-base/core/database';
|
|
352
|
+
import { Pairing } from '@cero-base/core/pairing';
|
|
353
|
+
import { Identity } from '@cero-base/core/identity';
|