@cero-base/cero 1.18.2 → 2.0.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.
Files changed (44) hide show
  1. package/README.md +38 -644
  2. package/package.json +16 -12
  3. package/src/build/index.js +21 -50
  4. package/src/build/internal.js +121 -0
  5. package/src/build/schemas.js +2 -8
  6. package/src/extensions/handle-sync.js +3 -10
  7. package/src/extensions/index.js +6 -0
  8. package/src/extensions/profile-sync.js +0 -5
  9. package/src/handle/index.js +255 -300
  10. package/src/index.js +65 -112
  11. package/src/lib/bluetooth.js +25 -56
  12. package/src/lib/constants.js +0 -15
  13. package/src/lib/operators.js +24 -74
  14. package/src/lib/peek.js +4 -8
  15. package/src/lib/refs.js +46 -0
  16. package/src/lib/spec.js +2 -3
  17. package/src/local/index.js +4 -5
  18. package/src/rpc/client.js +27 -39
  19. package/src/rpc/index.js +3 -3
  20. package/src/rpc/server.js +29 -40
  21. package/types/build/index.d.ts +19 -7
  22. package/types/build/internal.d.ts +78 -0
  23. package/types/build/schemas.d.ts +3 -3
  24. package/types/extensions/handle-sync.d.ts +4 -9
  25. package/types/extensions/index.d.ts +24 -2
  26. package/types/extensions/profile-sync.d.ts +2 -6
  27. package/types/handle/index.d.ts +218 -254
  28. package/types/index.d.ts +78 -102
  29. package/types/lib/bluetooth.d.ts +24 -46
  30. package/types/lib/constants.d.ts +5 -16
  31. package/types/lib/operators.d.ts +49 -77
  32. package/types/lib/peek.d.ts +3 -4
  33. package/types/lib/refs.d.ts +36 -0
  34. package/types/lib/spec.d.ts +5 -1
  35. package/types/local/index.d.ts +24 -23
  36. package/types/rpc/client.d.ts +107 -127
  37. package/types/rpc/index.d.ts +7 -2
  38. package/types/rpc/server.d.ts +62 -76
  39. package/src/build/builtins.js +0 -174
  40. package/src/lib/internal.js +0 -9
  41. package/src/lib/utils.js +0 -67
  42. package/types/build/builtins.d.ts +0 -100
  43. package/types/lib/internal.d.ts +0 -24
  44. package/types/lib/utils.d.ts +0 -55
package/src/rpc/server.js CHANGED
@@ -12,7 +12,6 @@ import { put, set, get, del, count, watch, changes, call } from '../lib/operator
12
12
  *
13
13
  * @typedef {object} ServerOpts
14
14
  * @property {string} storage Directory passed to `cero()` for the local store.
15
- * @property {object} spec Compiled cero spec (schema + rpc + handles).
16
15
  * @property {string} [name] Optional display name forwarded to `cero()`.
17
16
  * @property {Array<{ host: string, port: number }>} [bootstrap] Custom DHT bootstrap.
18
17
  * @property {boolean} [isMobile]
@@ -28,19 +27,19 @@ import { put, set, get, del, count, watch, changes, call } from '../lib/operator
28
27
  */
29
28
 
30
29
  /**
31
- * IPC-side RPC server for cero. Bridges an `hrpc` channel to a live
32
- * `Handle` tree: lazy-initializes the root via `cero()` on the first
33
- * `init` call, then exposes data ops, pairing, and handle lifecycle
34
- * over the wire using the spec-bound codec.
30
+ * IPC-side RPC server for cero. Bridges an `hrpc` channel to a live `Handle` tree:
31
+ * lazy-initializes the root via `cero()` on the first `init` call, then exposes data ops,
32
+ * pairing, and handle lifecycle.
35
33
  */
36
34
  export class Server extends RPCServer {
37
35
  /**
38
36
  * @param {any} ipc Framed IPC stream (must be writable).
37
+ * @param {object} spec
39
38
  * @param {Partial<ServerOpts>} [opts]
40
39
  */
41
- constructor(ipc, { storage, spec, ...opts } = {}) {
42
- if (!storage) throw CeroError.REQUIRED('storage')
40
+ constructor(ipc, spec, { storage, ...opts } = {}) {
43
41
  if (!spec) throw CeroError.REQUIRED('spec')
42
+ if (!storage) throw CeroError.REQUIRED('storage')
44
43
  super(ipc, spec)
45
44
  if (spec.local?.schema && !spec.local.codec) bindCodec(spec.local)
46
45
  this.storage = storage
@@ -52,14 +51,6 @@ export class Server extends RPCServer {
52
51
  this._wireInit()
53
52
  }
54
53
 
55
- /** End every watch stream bound to a handle (e.g. when it closes or leaves). */
56
- _endWatches(handle) {
57
- const set = this._watchStreams.get(handle)
58
- if (!set) return
59
- for (const s of set) s.destroy()
60
- this._watchStreams.delete(handle)
61
- }
62
-
63
54
  /**
64
55
  * Root cero id (null until `init` has run).
65
56
  *
@@ -87,6 +78,14 @@ export class Server extends RPCServer {
87
78
  await super._close()
88
79
  }
89
80
 
81
+ /** End every watch stream bound to a handle (e.g. when it closes or leaves). */
82
+ _endWatches(handle) {
83
+ const set = this._watchStreams.get(handle)
84
+ if (!set) return
85
+ for (const s of set) s.destroy()
86
+ this._watchStreams.delete(handle)
87
+ }
88
+
90
89
  /** Wire the `init` handler that lazily constructs the root cero handle. */
91
90
  _wireInit() {
92
91
  this.rpc.onInit(async () => {
@@ -147,7 +146,7 @@ export class Server extends RPCServer {
147
146
  r.kind === 'single'
148
147
  ? codec.encodeRow(r.schema, result.data)
149
148
  : codec.encodeRows(r.schema, result.data)
150
- // total is int on the wire: -1 encodes "skipped" (null), see T2.1 lazy total
149
+ // total is int on the wire: -1 encodes null
151
150
  return { data, total: result.total ?? -1, size: result.size ?? 0 }
152
151
  })
153
152
 
@@ -175,17 +174,13 @@ export class Server extends RPCServer {
175
174
  ;({ ref: r, codec } = this._refOf(handle, ref, local))
176
175
  live = watch(r, codec.decodeQuery(query))
177
176
  } catch (err) {
178
- // Forward the error to the client over the wire by destroying the
179
- // underlying response stream with it (emits a CLOSE|ERROR frame). The
180
- // local RPCStream's 'error' is swallowed so it doesn't crash the server.
177
+ // forward the error over the wire by destroying the response stream with it
181
178
  stream.once('error', () => {})
182
179
  stream.writeStream.destroy(err)
183
180
  stream.destroy()
184
181
  return
185
182
  }
186
- // keep-latest under IPC backpressure: snapshots are idempotent, so an
187
- // un-drained wire holds one pending snapshot (newest wins) instead of
188
- // queueing every intermediate result set — encode only what ships
183
+ // keep-latest under backpressure: snapshots are idempotent
189
184
  let pending = null
190
185
  let blocked = false
191
186
  const onData = (snap) => {
@@ -212,8 +207,7 @@ export class Server extends RPCServer {
212
207
  if (!set) this._watchStreams.set(handle, (set = new Set()))
213
208
  set.add(stream)
214
209
  live.on('data', onData)
215
- // channel teardown destroys the stream with CHANNEL_CLOSED 'close'
216
- // below does the cleanup; the error itself must not crash the server
210
+ // channel teardown destroys the stream with CHANNEL_CLOSED; 'close' cleans up
217
211
  stream.on('error', safetyCatch)
218
212
  stream.on('close', () => {
219
213
  live.off('data', onData)
@@ -237,8 +231,7 @@ export class Server extends RPCServer {
237
231
  let set = this._watchStreams.get(handle)
238
232
  if (!set) this._watchStreams.set(handle, (set = new Set()))
239
233
  set.add(stream)
240
- // no keep-latest: delta batches are not idempotent hold the iteration
241
- // on wire backpressure instead; the cursor folds everything missed
234
+ // deltas are not idempotent: hold the iteration on backpressure instead
242
235
  const pump = async () => {
243
236
  for await (const batch of live) {
244
237
  const ok = stream.write({
@@ -282,13 +275,10 @@ export class Server extends RPCServer {
282
275
  return { ok }
283
276
  })
284
277
 
285
- // specs built before rotation have no rotate command
286
- if (typeof this.rpc.onRotate === 'function') {
287
- this.rpc.onRotate(async ({ handle }) => {
288
- const { epoch } = await this._resolve(handle).store.rotate()
289
- return { epoch }
290
- })
291
- }
278
+ this.rpc.onRotate(async ({ handle }) => {
279
+ const { epoch } = await this._resolve(handle).store.rotate()
280
+ return { epoch }
281
+ })
292
282
 
293
283
  this.rpc.onJoin(async ({ parent, ref, invite }) => {
294
284
  if (this._resolve(parent) !== this.me) throw CeroError.UNSUPPORTED('nested handles')
@@ -306,7 +296,7 @@ export class Server extends RPCServer {
306
296
  const info = parent.spec.meta.refs?.[ref] || parent.spec.handles?.[ref]
307
297
  if (!info) throw CeroError.UNKNOWN('handle type', ref)
308
298
  const wire = parent.spec.codec.decodeCreate(data) || {}
309
- // routes can't cross the wire (functions) both sides register them via define()
299
+ // routes are functions and cannot cross the wire
310
300
  const opts = { name: wire.name, role: wire.role, accept: wire.noAccept ? false : undefined }
311
301
  const child = await this.me._create(ref, opts)
312
302
  const id = child.id
@@ -358,9 +348,7 @@ export class Server extends RPCServer {
358
348
  }
359
349
 
360
350
  /**
361
- * Resolve a `{ handle, ref }` pair to its `Ref` and codec. When `local` is
362
- * set, the ref is resolved against the root's per-device `local` store and
363
- * paired with the codec bound from the local schema.
351
+ * Resolve a `{ handle, ref }` pair to its `Ref` and codec.
364
352
  *
365
353
  * @param {string} id
366
354
  * @param {string} name
@@ -370,7 +358,7 @@ export class Server extends RPCServer {
370
358
  _refOf(id, name, local) {
371
359
  if (local) {
372
360
  const info = this.spec.meta.local?.refs?.[name]
373
- if (!info || info.builtin) throw CeroError.UNKNOWN('local ref', name)
361
+ if (!info || info.internal) throw CeroError.UNKNOWN('local ref', name)
374
362
  const r = this.me.local?.[name]
375
363
  if (!r) throw CeroError.UNKNOWN('local ref', name)
376
364
  return { ref: r, codec: this.spec.local.codec }
@@ -409,11 +397,12 @@ export class Server extends RPCServer {
409
397
  * Construct a `Server`, wait for it to be ready, and return it.
410
398
  *
411
399
  * @param {any} ipc
400
+ * @param {object} spec
412
401
  * @param {ServerOpts} opts
413
402
  * @returns {Promise<Server>}
414
403
  */
415
- export async function serve(ipc, opts) {
416
- const server = new Server(ipc, opts)
404
+ export async function serve(ipc, spec, opts) {
405
+ const server = new Server(ipc, spec, opts)
417
406
  await server.ready()
418
407
  return server
419
408
  }
@@ -1,10 +1,5 @@
1
- export function build(specDir: any, schema: any, { ns, extensions }?: {
2
- ns?: string;
3
- extensions?: boolean;
4
- }): Promise<void>;
5
- export { getHyperdbType } from "./builtins.js";
6
- export type Schema = import("@cero-base/core/schema").Schema;
7
- export type SchemaDefs = import("@cero-base/core/schema").SchemaDefs;
1
+ export type Schema = import('@cero-base/core/schema').Schema;
2
+ export type SchemaDefs = import('@cero-base/core/schema').SchemaDefs;
8
3
  export type SchemaInput = Schema | (SchemaDefs & {
9
4
  local?: SchemaDefs;
10
5
  });
@@ -14,3 +9,20 @@ export type BuildOpts = {
14
9
  */
15
10
  ns?: string;
16
11
  };
12
+ /**
13
+ * @typedef {import('@cero-base/core/schema').Schema} Schema
14
+ * @typedef {import('@cero-base/core/schema').SchemaDefs} SchemaDefs
15
+ * @typedef {Schema | (SchemaDefs & { local?: SchemaDefs })} SchemaInput
16
+ *
17
+ * @typedef {object} BuildOpts
18
+ * @property {string} [ns] Namespace prefix for emitted schema ids. Defaults to `'cero'`.
19
+ */
20
+ /**
21
+ * Compile a cero schema into wire-level artifacts and write them to disk.
22
+ *
23
+ * @param {string} specDir Output directory.
24
+ * @param {SchemaInput} schema Either a `schema(...)` wrapper or its raw defs object.
25
+ * @param {BuildOpts} [opts]
26
+ * @returns {Promise<void>}
27
+ */
28
+ export declare function build(specDir: string, schema: SchemaInput, { ns, extensions }?: BuildOpts): Promise<void>;
@@ -0,0 +1,78 @@
1
+ export declare const defs: {
2
+ main: {
3
+ members: {
4
+ type: string;
5
+ };
6
+ devices: {
7
+ type: string;
8
+ };
9
+ invites: {
10
+ type: string;
11
+ };
12
+ handles: {
13
+ type: string;
14
+ };
15
+ files: {
16
+ type: string;
17
+ };
18
+ };
19
+ local: {
20
+ master: {
21
+ type: string;
22
+ kind: string;
23
+ };
24
+ keypair: {
25
+ type: string;
26
+ kind: string;
27
+ };
28
+ 'handle-keypairs': {
29
+ type: string;
30
+ };
31
+ environment: {
32
+ type: string;
33
+ kind: string;
34
+ };
35
+ };
36
+ };
37
+ export declare function fields(map: any): {
38
+ name: string;
39
+ type: any;
40
+ required: boolean;
41
+ }[];
42
+ export declare function types(scope: any, extend?: {}): {
43
+ name: string;
44
+ compact: boolean;
45
+ fields: {
46
+ name: string;
47
+ type: any;
48
+ required: boolean;
49
+ }[];
50
+ }[];
51
+ export declare function refs(ns: any, scope: any): {
52
+ [k: string]: {
53
+ kind: any;
54
+ path: string[];
55
+ internal: boolean;
56
+ verb: any;
57
+ schema: string;
58
+ };
59
+ };
60
+ export declare function collections(ns: any, scope: any): {
61
+ name: string;
62
+ schema: string;
63
+ key: string[];
64
+ }[];
65
+ export declare function dispatches(ns: any): {
66
+ name: string;
67
+ requestType: string;
68
+ }[];
69
+ export declare function commands(ns: any): {
70
+ name: string | boolean;
71
+ request: {
72
+ name: string;
73
+ };
74
+ response: {
75
+ name: string;
76
+ stream: string | true;
77
+ };
78
+ }[];
@@ -1,4 +1,4 @@
1
- export const main: {
1
+ export declare const main: {
2
2
  'del-by-id': {
3
3
  id: import("@cero-base/core").Prim;
4
4
  };
@@ -80,7 +80,7 @@ export const main: {
80
80
  stamp: import("@cero-base/core").Prim;
81
81
  };
82
82
  };
83
- export const local: {
83
+ export declare const local: {
84
84
  master: {
85
85
  seed: import("@cero-base/core").Prim;
86
86
  };
@@ -98,7 +98,7 @@ export const local: {
98
98
  channel: import("@cero-base/core").Prim;
99
99
  };
100
100
  };
101
- export const rpc: {
101
+ export declare const rpc: {
102
102
  'req-empty': {
103
103
  ok: import("@cero-base/core").Prim;
104
104
  };
@@ -1,22 +1,17 @@
1
1
  /**
2
- * Mirror a child handle's `profile` (name + avatar) onto its row in the parent's
3
- * `handles` list — so a handle list shows names + avatars without opening each
4
- * one. Adds the synced `fields` to the `handle` builtin.
5
- *
6
- * On create it seeds the handle's `profile.name` from the open `{ name }`, then
7
- * reflects the (app-owned) `profile` onto the row. Requires your handle types to
8
- * declare a `profile` single — handles without one are left untouched.
2
+ * Mirror a child handle's `profile` (name + avatar) onto its row in the parent's `handles`
3
+ * list — so a handle list shows names + avatars without opening each one.
9
4
  *
10
5
  * @param {{ fields?: Record<string, any> }} [opts]
11
6
  */
12
- export function handleSync({ fields }?: {
7
+ export declare function handleSync({ fields }?: {
13
8
  fields?: Record<string, any>;
14
9
  }): {
15
10
  name: string;
16
11
  bundled: boolean;
17
12
  schema: {
18
13
  handles: {
19
- kind: "extend";
14
+ kind: 'extend';
20
15
  fields: Record<string, import("@cero-base/core").Prim>;
21
16
  };
22
17
  };
@@ -1,2 +1,24 @@
1
- export * from "./profile-sync.js";
2
- export * from "./handle-sync.js";
1
+ export * from './profile-sync.js';
2
+ export * from './handle-sync.js';
3
+ export declare const registry: ({
4
+ name: string;
5
+ bundled: boolean;
6
+ schema: {
7
+ profile: import("@cero-base/core").TypeDef;
8
+ members: {
9
+ kind: 'extend';
10
+ fields: Record<string, import("@cero-base/core").Prim>;
11
+ };
12
+ };
13
+ setup(me: any): void;
14
+ } | {
15
+ name: string;
16
+ bundled: boolean;
17
+ schema: {
18
+ handles: {
19
+ kind: 'extend';
20
+ fields: Record<string, import("@cero-base/core").Prim>;
21
+ };
22
+ };
23
+ setup(me: any): void;
24
+ })[];
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * Mirror your `profile` onto your `member` row in every handle you're in.
3
- * Declares a `profile` single (`name`, `avatar`, plus any extra `fields`) and
4
- * mirrors them onto the `member` builtin, then publishes when you open/join a
5
- * handle and whenever your profile changes. An app may declare its own richer
6
- * `profile` instead — the app schema wins.
7
3
  *
8
4
  * @param {{ fields?: Record<string, any> }} [opts]
9
5
  */
10
- export function profileSync({ fields }?: {
6
+ export declare function profileSync({ fields }?: {
11
7
  fields?: Record<string, any>;
12
8
  }): {
13
9
  name: string;
@@ -15,7 +11,7 @@ export function profileSync({ fields }?: {
15
11
  schema: {
16
12
  profile: import("@cero-base/core").TypeDef;
17
13
  members: {
18
- kind: "extend";
14
+ kind: 'extend';
19
15
  fields: Record<string, import("@cero-base/core").Prim>;
20
16
  };
21
17
  };