@cero-base/cero 0.8.9 → 1.0.1

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/src/rpc/server.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { RPCServer, bindCodec } from '@cero-base/core/rpc'
2
2
  import { CeroError } from '@cero-base/core/errors'
3
+ import { encodeId } from '@cero-base/core/blobs/codec'
3
4
 
4
5
  import { cero, restore } from '../index.js'
5
6
  import { put, set, get, del, count, watch, call } from '../lib/operators.js'
@@ -18,7 +19,6 @@ import { put, set, get, del, count, watch, call } from '../lib/operators.js'
18
19
  * @typedef {object} Identity
19
20
  * @property {string} id Long-lived cero identity id.
20
21
  * @property {string} deviceId Per-device id (empty when no `local` spec).
21
- * @property {string|null} phrase BIP39 recovery phrase for the identity.
22
22
  *
23
23
  * @typedef {{ ref: any, codec: any }} RefAndCodec
24
24
  *
@@ -45,9 +45,19 @@ export class Server extends RPCServer {
45
45
  this.opts = opts
46
46
  this.me = null
47
47
  this.handles = new Map()
48
+ /** @type {Map<string, Set<object>>} handle id → its open watch streams */
49
+ this._watchStreams = new Map()
48
50
  this._wireInit()
49
51
  }
50
52
 
53
+ /** End every watch stream bound to a handle (e.g. when it closes or leaves). */
54
+ _endWatches(handle) {
55
+ const set = this._watchStreams.get(handle)
56
+ if (!set) return
57
+ for (const s of set) s.destroy()
58
+ this._watchStreams.delete(handle)
59
+ }
60
+
51
61
  /**
52
62
  * Root cero id (null until `init` has run).
53
63
  *
@@ -81,10 +91,12 @@ export class Server extends RPCServer {
81
91
  if (this.me) throw new CeroError('CONFLICT', 'already initialized')
82
92
  this.me = await cero(this.storage, this.spec, this.opts)
83
93
  this.handles.set(this.me.id, this.me)
94
+ await this.me.fileServer.listen()
84
95
  this._wireData()
85
96
  this._wirePairing()
86
97
  this._wireHandles()
87
98
  this._wireRestore()
99
+ this._wireSeed()
88
100
  return this._identity()
89
101
  })
90
102
  }
@@ -101,16 +113,28 @@ export class Server extends RPCServer {
101
113
 
102
114
  /** Register the row-level RPC handlers (put/set/get/del/count/watch/call). */
103
115
  _wireData() {
116
+ this.rpc.onAddFile(async ({ handle, data, name, type }) => {
117
+ const h = this._resolve(handle)
118
+ await h.blobs.ready()
119
+ const blobId = await h.blobs.put(data)
120
+ const id = encodeId(h.blobs.key, blobId, type || '')
121
+ const codec = h.spec.codec
122
+ await h.store.call('add-file', { id, name: name || null })
123
+ const { data: row } = await get(h.files, id)
124
+ return { data: codec.encodeRow(h.files.schema, row) }
125
+ })
126
+
104
127
  this.rpc.onAddRow(async ({ handle, ref, data, local }) => {
105
128
  const { ref: r, codec } = this._refOf(handle, ref, local)
106
129
  const { data: row } = await put(r, codec.decodeRow(r.schema, data))
107
130
  return { data: codec.encodeRow(r.schema, row) }
108
131
  })
109
132
 
110
- this.rpc.onSet(async ({ handle, ref, data, local }) => {
133
+ this.rpc.onSet(async ({ handle, ref, data, local, noUpsert }) => {
111
134
  const { ref: r, codec } = this._refOf(handle, ref, local)
112
- const { data: row } = await set(r, codec.decodeRow(r.schema, data))
113
- return { data: codec.encodeRow(r.schema, row) }
135
+ const result = await set(r, codec.decodeRow(r.schema, data), { upsert: !noUpsert })
136
+ // an update-only miss returns null — encode an empty row back
137
+ return { data: codec.encodeRow(r.schema, result?.data ?? null) }
114
138
  })
115
139
 
116
140
  this.rpc.onGet(async ({ handle, ref, query, local }) => {
@@ -147,7 +171,12 @@ export class Server extends RPCServer {
147
171
  ;({ ref: r, codec } = this._refOf(handle, ref, local))
148
172
  live = watch(r, codec.decodeQuery(query))
149
173
  } catch (err) {
150
- stream.destroy(err)
174
+ // Forward the error to the client over the wire by destroying the
175
+ // underlying response stream with it (emits a CLOSE|ERROR frame). The
176
+ // local RPCStream's 'error' is swallowed so it doesn't crash the server.
177
+ stream.once('error', () => {})
178
+ stream.writeStream.destroy(err)
179
+ stream.destroy()
151
180
  return
152
181
  }
153
182
  const onData = (snap) => {
@@ -157,10 +186,14 @@ export class Server extends RPCServer {
157
186
  : codec.encodeRows(r.schema, snap.data)
158
187
  stream.write({ data, total: snap.total ?? 0, size: snap.size ?? 0 })
159
188
  }
189
+ let set = this._watchStreams.get(handle)
190
+ if (!set) this._watchStreams.set(handle, (set = new Set()))
191
+ set.add(stream)
160
192
  live.on('data', onData)
161
193
  stream.on('close', () => {
162
194
  live.off('data', onData)
163
195
  live.destroy()
196
+ this._watchStreams.get(handle)?.delete(stream)
164
197
  })
165
198
  })
166
199
 
@@ -201,7 +234,9 @@ export class Server extends RPCServer {
201
234
  const parent = this._resolve(handle)
202
235
  const info = parent.spec.meta.refs?.[ref] || parent.spec.handles?.[ref]
203
236
  if (!info) throw CeroError.UNKNOWN('handle type', ref)
204
- const opts = parent.spec.codec.decodeCreate(data) || {}
237
+ const wire = parent.spec.codec.decodeCreate(data) || {}
238
+ // routes can't cross the wire (functions) — both sides register them via define()
239
+ const opts = { name: wire.name, role: wire.role, accept: wire.noAccept ? false : undefined }
205
240
  const child = await this.me._create(ref, opts)
206
241
  const id = child.id
207
242
  this.handles.set(id, child)
@@ -220,6 +255,7 @@ export class Server extends RPCServer {
220
255
  this.rpc.onCloseHandle(async ({ handle }) => {
221
256
  const h = this.handles.get(handle)
222
257
  if (!h || h === this.me) return {}
258
+ this._endWatches(handle)
223
259
  this.handles.delete(handle)
224
260
  await h.close()
225
261
  return {}
@@ -228,6 +264,7 @@ export class Server extends RPCServer {
228
264
  this.rpc.onLeave(async ({ handle }) => {
229
265
  const h = this.handles.get(handle)
230
266
  if (!h || h === this.me) return {}
267
+ this._endWatches(handle)
231
268
  await this.me.store.call('del-handle', { id: handle })
232
269
  this.handles.delete(handle)
233
270
  await h.close()
@@ -279,12 +316,22 @@ export class Server extends RPCServer {
279
316
  * @returns {Identity}
280
317
  */
281
318
  _identity() {
319
+ const fs = this.me.fileServer
282
320
  return {
283
321
  id: this.me.id,
284
322
  deviceId: this.me.device?.id || '',
285
- phrase: this.me.identity.toPhrase()
323
+ fileBase: `http://127.0.0.1:${fs.port}`,
324
+ fileToken: fs.server.token || ''
286
325
  }
287
326
  }
327
+
328
+ /** Wire the on-demand `seed` handler — surfaces the recovery phrase only when asked. */
329
+ _wireSeed() {
330
+ this.rpc.onSeed(async () => {
331
+ if (!this.me) throw new CeroError('NOT_READY', 'init')
332
+ return { phrase: this.me.identity.toPhrase() }
333
+ })
334
+ }
288
335
  }
289
336
 
290
337
  /**
@@ -15,6 +15,10 @@ export namespace refs {
15
15
  let type_3: string;
16
16
  export { type_3 as type };
17
17
  }
18
+ namespace files {
19
+ let type_4: string;
20
+ export { type_4 as type };
21
+ }
18
22
  }
19
23
  let local: {
20
24
  master: {
@@ -30,7 +34,7 @@ export namespace refs {
30
34
  };
31
35
  };
32
36
  }
33
- export function hyperdbType(prim: any): any;
37
+ export function getHyperdbType(prim: any): any;
34
38
  export function builtinRefs(ns: any, scope: any): {
35
39
  [k: string]: {
36
40
  schema: string;
@@ -1,24 +1,7 @@
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>;
1
+ export function build(specDir: any, schema: any, { ns }?: {
2
+ ns?: string;
3
+ }): Promise<void>;
4
+ export { getHyperdbType } from "./builtins.js";
22
5
  export type Schema = import("@cero-base/core/schema").Schema;
23
6
  export type SchemaDefs = import("@cero-base/core/schema").SchemaDefs;
24
7
  export type SchemaInput = Schema | (SchemaDefs & {
@@ -52,6 +52,14 @@ export const main: {
52
52
  updatedAt: import("@cero-base/core").Prim;
53
53
  index: import("@cero-base/core").Prim;
54
54
  };
55
+ file: {
56
+ id: import("@cero-base/core").Prim;
57
+ memberId: import("@cero-base/core").Prim;
58
+ name: import("@cero-base/core").Prim;
59
+ createdAt: import("@cero-base/core").Prim;
60
+ updatedAt: import("@cero-base/core").Prim;
61
+ index: import("@cero-base/core").Prim;
62
+ };
55
63
  claim: {
56
64
  identity: import("@cero-base/core").Prim;
57
65
  writer: import("@cero-base/core").Prim;
@@ -85,6 +93,7 @@ export const rpc: {
85
93
  ref: import("@cero-base/core").Prim;
86
94
  data: import("@cero-base/core").Prim;
87
95
  local: import("@cero-base/core").Prim;
96
+ noUpsert: import("@cero-base/core").Prim;
88
97
  };
89
98
  'req-id': {
90
99
  handle: import("@cero-base/core").Prim;
@@ -142,9 +151,19 @@ export const rpc: {
142
151
  type: import("@cero-base/core").Prim;
143
152
  name: import("@cero-base/core").Prim;
144
153
  };
154
+ 'req-add-file': {
155
+ handle: import("@cero-base/core").Prim;
156
+ data: import("@cero-base/core").Prim;
157
+ name: import("@cero-base/core").Prim;
158
+ type: import("@cero-base/core").Prim;
159
+ };
145
160
  'res-identity': {
146
161
  id: import("@cero-base/core").Prim;
147
162
  deviceId: import("@cero-base/core").Prim;
163
+ fileBase: import("@cero-base/core").Prim;
164
+ fileToken: import("@cero-base/core").Prim;
165
+ };
166
+ 'res-seed': {
148
167
  phrase: import("@cero-base/core").Prim;
149
168
  };
150
169
  'res-ok': {
@@ -43,7 +43,7 @@ export { Ref } from "../lib/utils.js";
43
43
  * @property {number} [timeout]
44
44
  *
45
45
  * @typedef {object} AcceptOpts
46
- * @property {string} [role] Role to grant the joining peer. Falls back to the invite's role, then `'write'`.
46
+ * @property {string} [role] Role to grant the joining peer. Falls back to the invite's role, then `'member'`.
47
47
  * @property {string | null} [name]
48
48
  *
49
49
  * @typedef {object} RecoverOpts
@@ -55,6 +55,8 @@ export { Ref } from "../lib/utils.js";
55
55
  * @property {import('../lib/utils.js').Ref} [members] `members` ref, attached dynamically when the schema declares one.
56
56
  *
57
57
  * @typedef {Handle & HandleExtra} Child A child handle plus its dynamically-attached refs.
58
+ *
59
+ * @typedef {Handle & Record<string, import('../lib/utils.js').Ref>} CeroHandle A handle with every schema ref reachable as a `Ref` property (e.g. `me.profile`, `room.messages`).
58
60
  */
59
61
  /**
60
62
  * A cero handle — a single writable database session attached to a swarm.
@@ -87,6 +89,9 @@ export class Handle extends ReadyResource {
87
89
  _onerror: any;
88
90
  children: Set<any>;
89
91
  _loading: Map<any, any>;
92
+ _coreKeys: Map<any, any>;
93
+ _fileServer: FileServer;
94
+ _blobs: Blobs;
90
95
  _owned: Set<any>;
91
96
  store: Database;
92
97
  pair: Pairing;
@@ -125,6 +130,40 @@ export class Handle extends ReadyResource {
125
130
  on(event: string, fn: (...args: any[]) => void, opts?: {
126
131
  signal?: AbortSignal;
127
132
  }): this;
133
+ /** The top-most handle in the parent chain — itself for a root handle. */
134
+ get root(): this;
135
+ /**
136
+ * Lazily-built file server for this identity. Root-only — child handles
137
+ * reach it through `this.root.fileServer`.
138
+ *
139
+ * @returns {FileServer}
140
+ */
141
+ get fileServer(): FileServer;
142
+ /**
143
+ * @param {Uint8Array} coreKey
144
+ * @param {object} info
145
+ * @returns {{ key: Uint8Array, encryptionKey: Uint8Array } | null}
146
+ */
147
+ _resolveCore(coreKey: Uint8Array, info: object): {
148
+ key: Uint8Array;
149
+ encryptionKey: Uint8Array;
150
+ } | null;
151
+ /**
152
+ * Resolve a durable file id to an ephemeral download url via this identity's
153
+ * file server.
154
+ *
155
+ * @param {string} id
156
+ * @returns {string}
157
+ */
158
+ getLink(id: string): string;
159
+ /**
160
+ * Lazily-built blob store for THIS handle's writing device. One core per
161
+ * writer, encrypted with the handle's encryptionKey and attached to the
162
+ * shared network for replication.
163
+ *
164
+ * @returns {Blobs}
165
+ */
166
+ get blobs(): Blobs;
128
167
  /** Canonical id — identity id for the root handle, store key for children. */
129
168
  get id(): any;
130
169
  /** This device's id + name. `null` on child handles. */
@@ -145,13 +184,9 @@ export class Handle extends ReadyResource {
145
184
  * Claim writer capability on an existing database (paired-device flow).
146
185
  * Forwards to `Database.claim`.
147
186
  *
148
- * @param {{ name?: string | null, isMobile?: boolean }} [opts]
149
187
  * @returns {Promise<void>}
150
188
  */
151
- claim(opts?: {
152
- name?: string | null;
153
- isMobile?: boolean;
154
- }): Promise<void>;
189
+ claim(): Promise<void>;
155
190
  /**
156
191
  * Claim + wait until this peer becomes a writer + bring the bee up to date.
157
192
  * Used by `restore()` after wiping local state.
@@ -360,7 +395,7 @@ export type StaticJoinOpts = {
360
395
  };
361
396
  export type AcceptOpts = {
362
397
  /**
363
- * Role to grant the joining peer. Falls back to the invite's role, then `'write'`.
398
+ * Role to grant the joining peer. Falls back to the invite's role, then `'member'`.
364
399
  */
365
400
  role?: string;
366
401
  name?: string | null;
@@ -386,8 +421,14 @@ export type HandleExtra = {
386
421
  * A child handle plus its dynamically-attached refs.
387
422
  */
388
423
  export type Child = Handle & HandleExtra;
424
+ /**
425
+ * A handle with every schema ref reachable as a `Ref` property (e.g. `me.profile`, `room.messages`).
426
+ */
427
+ export type CeroHandle = Handle & Record<string, import("../lib/utils.js").Ref>;
389
428
  import ReadyResource from 'ready-resource';
429
+ import { FileServer } from '@cero-base/core/blobs/server';
430
+ import { Blobs } from '@cero-base/core/blobs';
390
431
  import { Database } from '@cero-base/core/database';
391
432
  import { Pairing } from '@cero-base/core/pairing';
392
- import AbortController from 'abort-controller';
433
+ import AbortController from 'bare-abort-controller';
393
434
  import { Identity } from '@cero-base/core/identity';
package/types/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ /**
2
+ * @typedef {import('./handle/index.js').CeroHandle} CeroHandle
3
+ */
1
4
  /**
2
5
  * @typedef {object} CeroOpts
3
6
  * @property {Identity} [identity] Pre-resolved identity. If absent, derived from `seed`/`phrase` or generated.
@@ -22,9 +25,9 @@
22
25
  * @param {string} dir Data directory.
23
26
  * @param {any} spec Built spec — output of `cero/build`.
24
27
  * @param {CeroOpts} [opts]
25
- * @returns {Promise<Handle>}
28
+ * @returns {Promise<CeroHandle>}
26
29
  */
27
- export function cero(dir: string, spec: any, opts?: CeroOpts): Promise<Handle>;
30
+ export function cero(dir: string, spec: any, opts?: CeroOpts): Promise<CeroHandle>;
28
31
  export namespace cero {
29
32
  export { t };
30
33
  export { put };
@@ -55,6 +58,8 @@ export namespace cero {
55
58
  * @returns {Promise<Handle>} Freshly restored root handle.
56
59
  */
57
60
  export function restore(me: Handle, phrase: string): Promise<Handle>;
61
+ export { peek } from "./lib/peek.js";
62
+ export type CeroHandle = import("./handle/index.js").CeroHandle;
58
63
  export type CeroOpts = {
59
64
  /**
60
65
  * Pre-resolved identity. If absent, derived from `seed`/`phrase` or generated.
@@ -112,7 +117,6 @@ export type CeroOpts = {
112
117
  */
113
118
  recoveryTimeout?: number;
114
119
  };
115
- import { Handle } from './handle/index.js';
116
120
  import { t } from './lib/spec.js';
117
121
  import { put } from './lib/operators.js';
118
122
  import { set } from './lib/operators.js';
@@ -124,14 +128,15 @@ import { call } from './lib/operators.js';
124
128
  import { open } from './lib/operators.js';
125
129
  import { before } from './lib/operators.js';
126
130
  import { after } from './lib/operators.js';
127
- import { peek } from './lib/operators.js';
131
+ import { peek } from './lib/peek.js';
128
132
  import { schema } from './lib/spec.js';
129
133
  import { bind } from './lib/operators.js';
130
134
  import { define } from './lib/operators.js';
131
135
  import { internal } from './lib/internal.js';
136
+ import { Handle } from './handle/index.js';
132
137
  import { Ref } from './handle/index.js';
133
138
  import { Local } from './local/index.js';
134
139
  import { Identity } from '@cero-base/core/identity';
135
140
  export { Handle, Ref, Local };
136
- export { put, set, get, del, count, watch, call, open, before, after, bind, define, peek } from "./lib/operators.js";
141
+ export { put, set, get, del, count, watch, call, open, before, after, bind, define } from "./lib/operators.js";
137
142
  export { t, schema } from "./lib/spec.js";
@@ -11,4 +11,5 @@ export namespace DB_TYPE {
11
11
  let json: string;
12
12
  let fixed32: string;
13
13
  let fixed64: string;
14
+ let file: string;
14
15
  }
@@ -1,13 +1,26 @@
1
1
  /**
2
- * Quickly check whether the on-disk directory at `dir` already holds an
3
- * initialised cero identity (i.e. a stored master seed). Opens the local store
4
- * read-only and closes everything before returning.
2
+ * @typedef {import('./utils.js').Ref} Ref
3
+ * @typedef {import('../handle/index.js').CeroHandle} CeroHandle
4
+ * @typedef {{ data: any }} SingleResult
5
+ * @typedef {{ data: any[], total: number, size: number }} ListResult
6
+ * @typedef {{ data: any | null }} GetByIdResult
7
+ */
8
+ /**
9
+ * Resolve a durable file id (+ optional name) to the read shape returned
10
+ * everywhere: `{ id, name?, type, size, url }`.
5
11
  *
6
- * @param {string} dir Cero data directory.
7
- * @param {any} spec Built spec — same value passed to `cero(dir, spec)`.
8
- * @returns {Promise<boolean>} `true` if a master seed exists on disk.
12
+ * @param {object} handle
13
+ * @param {string} id
14
+ * @param {string} [name]
15
+ * @returns {{ id: string, type: string, size: number, url: string, name?: string }}
9
16
  */
10
- export function peek(dir: string, spec: any): Promise<boolean>;
17
+ export function resolveFile(handle: object, id: string, name?: string): {
18
+ id: string;
19
+ type: string;
20
+ size: number;
21
+ url: string;
22
+ name?: string;
23
+ };
11
24
  /**
12
25
  * Put custom operators on `handle`, currying it as their first argument so
13
26
  * `handle.ns.fn(args)` calls `fn(handle, args)`. `arg` is either:
@@ -32,7 +45,13 @@ export function bind(handle: any, arg: Record<string, any> | string | null): any
32
45
  export function define(map: Record<string, any>): void;
33
46
  /** Test seam: clear all registered operators. */
34
47
  export function _clearDefined(): void;
35
- export function put(ref: Ref, row: Record<string, any>): Promise<SingleResult>;
48
+ export function put(ref: Ref, row: Record<string, any>): Promise<SingleResult | {
49
+ id: string;
50
+ name?: string;
51
+ type: string;
52
+ size: number;
53
+ url: string;
54
+ }>;
36
55
  export function set(ref: Ref, row: Record<string, any>, opts?: {
37
56
  upsert?: boolean;
38
57
  }): Promise<SingleResult>;
@@ -58,7 +77,7 @@ export function after(ref: Ref, fn: (ctx: {
58
77
  export function get(ref: Ref, q?: string | Record<string, any>): Promise<SingleResult | ListResult | GetByIdResult>;
59
78
  export function watch(ref: Ref, q?: Record<string, any>, opts?: {
60
79
  signal?: AbortSignal;
61
- }): any;
80
+ }): import("streamx").Readable;
62
81
  export function open(ref: Ref, arg?: string | {
63
82
  invite?: string;
64
83
  id?: string;
@@ -66,8 +85,9 @@ export function open(ref: Ref, arg?: string | {
66
85
  routes?: any;
67
86
  role?: string;
68
87
  accept?: boolean;
69
- } | undefined): Promise<any>;
88
+ } | undefined): Promise<CeroHandle>;
70
89
  export type Ref = import("./utils.js").Ref;
90
+ export type CeroHandle = import("../handle/index.js").CeroHandle;
71
91
  export type SingleResult = {
72
92
  data: any;
73
93
  };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Quickly check whether the on-disk directory at `dir` already holds an
3
+ * initialised cero identity (i.e. a stored master seed). Opens the local store
4
+ * read-only and closes everything before returning.
5
+ *
6
+ * @param {string} dir Cero data directory.
7
+ * @param {any} spec Built spec — same value passed to `cero(dir, spec)`.
8
+ * @returns {Promise<boolean>} `true` if a master seed exists on disk.
9
+ */
10
+ export function peek(dir: string, spec: any): Promise<boolean>;
@@ -9,12 +9,15 @@
9
9
  export function attachRefs(target: any, refs: Record<string, RefInfo>): void;
10
10
  /**
11
11
  * Run `cb` when `signal` aborts — or immediately if it already has. No-op
12
- * without a signal. The listener removes itself on fire.
12
+ * without a signal. The listener removes itself on fire. Returns a disposer that
13
+ * detaches the listener early, so a manual unsubscribe doesn't leave it lingering
14
+ * on a long-lived signal.
13
15
  *
14
- * @param {AbortSignal} [signal]
16
+ * @param {AbortSignal | undefined} signal
15
17
  * @param {() => void} cb
18
+ * @returns {(() => void) | undefined}
16
19
  */
17
- export function onAbort(signal?: AbortSignal, cb: () => void): void;
20
+ export function onAbort(signal: AbortSignal | undefined, cb: () => void): (() => void) | undefined;
18
21
  /**
19
22
  * @typedef {'collection' | 'single' | 'action' | 'handle'} RefKind
20
23
  * @typedef {{ kind?: string, schema?: string }} RefInfo
@@ -60,9 +60,11 @@ export class Client extends RPCClient {
60
60
  deviceId: any;
61
61
  store: this;
62
62
  local: LocalRefs;
63
+ _fileBase: any;
64
+ _fileToken: any;
63
65
  identity: {
64
66
  id: any;
65
- toPhrase: () => any;
67
+ toPhrase: () => Promise<any>;
66
68
  };
67
69
  /**
68
70
  * Create a new child handle of the given type.
@@ -20,7 +20,6 @@ export function serve(ipc: any, opts: ServerOpts): Promise<Server>;
20
20
  * @typedef {object} Identity
21
21
  * @property {string} id Long-lived cero identity id.
22
22
  * @property {string} deviceId Per-device id (empty when no `local` spec).
23
- * @property {string|null} phrase BIP39 recovery phrase for the identity.
24
23
  *
25
24
  * @typedef {{ ref: any, codec: any }} RefAndCodec
26
25
  *
@@ -56,6 +55,10 @@ export class Server extends RPCServer {
56
55
  };
57
56
  me: any;
58
57
  handles: Map<any, any>;
58
+ /** @type {Map<string, Set<object>>} handle id → its open watch streams */
59
+ _watchStreams: Map<string, Set<object>>;
60
+ /** End every watch stream bound to a handle (e.g. when it closes or leaves). */
61
+ _endWatches(handle: any): void;
59
62
  /**
60
63
  * Root cero id (null until `init` has run).
61
64
  *
@@ -103,6 +106,8 @@ export class Server extends RPCServer {
103
106
  * @returns {Identity}
104
107
  */
105
108
  _identity(): Identity;
109
+ /** Wire the on-demand `seed` handler — surfaces the recovery phrase only when asked. */
110
+ _wireSeed(): void;
106
111
  }
107
112
  export type BaseRPCServer = import("@cero-base/core/rpc").RPCServer;
108
113
  export type ServerOpts = {
@@ -137,10 +142,6 @@ export type Identity = {
137
142
  * Per-device id (empty when no `local` spec).
138
143
  */
139
144
  deviceId: string;
140
- /**
141
- * BIP39 recovery phrase for the identity.
142
- */
143
- phrase: string | null;
144
145
  };
145
146
  export type RefAndCodec = {
146
147
  ref: any;