@cero-base/cero 1.19.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +38 -643
  2. package/package.json +9 -6
  3. package/src/build/index.js +63 -61
  4. package/src/build/internal.js +123 -0
  5. package/src/build/schemas.js +6 -11
  6. package/src/extensions/handle-sync.js +3 -11
  7. package/src/extensions/index.js +86 -0
  8. package/src/extensions/profile-sync.js +15 -16
  9. package/src/handle/index.js +290 -284
  10. package/src/index.js +24 -81
  11. package/src/lib/bluetooth.js +25 -56
  12. package/src/lib/constants.js +0 -15
  13. package/src/lib/operators.js +35 -134
  14. package/src/lib/peek.js +4 -8
  15. package/src/lib/refs.js +10 -8
  16. package/src/lib/spec.js +2 -3
  17. package/src/local/index.js +2 -3
  18. package/src/rpc/client.js +54 -77
  19. package/src/rpc/index.js +3 -3
  20. package/src/rpc/server.js +40 -43
  21. package/types/build/index.d.ts +11 -9
  22. package/types/build/{builtins.d.ts → internal.d.ts} +20 -38
  23. package/types/build/schemas.d.ts +4 -3
  24. package/types/extensions/handle-sync.d.ts +2 -8
  25. package/types/extensions/index.d.ts +102 -0
  26. package/types/extensions/profile-sync.d.ts +0 -5
  27. package/types/handle/index.d.ts +90 -108
  28. package/types/index.d.ts +21 -17
  29. package/types/lib/bluetooth.d.ts +8 -32
  30. package/types/lib/constants.d.ts +0 -11
  31. package/types/lib/operators.d.ts +30 -83
  32. package/types/lib/peek.d.ts +2 -3
  33. package/types/lib/refs.d.ts +5 -5
  34. package/types/lib/spec.d.ts +2 -3
  35. package/types/local/index.d.ts +2 -3
  36. package/types/rpc/client.d.ts +29 -26
  37. package/types/rpc/index.d.ts +3 -3
  38. package/types/rpc/server.d.ts +7 -10
  39. package/src/build/builtins.js +0 -174
  40. package/src/lib/internal.js +0 -9
  41. package/types/lib/internal.d.ts +0 -24
@@ -1,4 +1,5 @@
1
1
  export type Ref = import('./refs.js').Ref;
2
+ export type HookContext = import('@cero-base/core/database').HookContext;
2
3
  export type CeroHandle = import('../handle/index.js').CeroHandle;
3
4
  export type SingleResult = {
4
5
  data: any;
@@ -13,6 +14,7 @@ export type GetByIdResult = {
13
14
  };
14
15
  /**
15
16
  * @typedef {import('./refs.js').Ref} Ref
17
+ * @typedef {import('@cero-base/core/database').HookContext} HookContext
16
18
  * @typedef {import('../handle/index.js').CeroHandle} CeroHandle
17
19
  * @typedef {{ data: any }} SingleResult
18
20
  * @typedef {{ data: any[], total: number, size: number }} ListResult
@@ -35,9 +37,7 @@ export declare function resolveFile(handle: object, id: string, name?: string):
35
37
  name?: string;
36
38
  };
37
39
  /**
38
- * Insert (or overwrite by id) a row on `ref`. The `files` builtin is special:
39
- * `put(handle.files, { data, type, name? })` uploads the bytes to this handle's
40
- * blob store, records `{ id, name }`, and resolves the file.
40
+ * Insert (or overwrite by id) a row on `ref`.
41
41
  *
42
42
  * @param {Ref} ref
43
43
  * @param {Record<string, any>} row
@@ -45,9 +45,7 @@ export declare function resolveFile(handle: object, id: string, name?: string):
45
45
  */
46
46
  export declare function put(ref: Ref, row: Record<string, any>): Promise<SingleResult>;
47
47
  /**
48
- * Upsert a row on `ref` — merges with the existing row and preserves
49
- * `createdAt`. Pass `{ upsert: false }` to update-only: a missing row is left
50
- * untouched instead of created (atomic — never resurrects a deleted row).
48
+ * Upsert a row on `ref` — merges with the existing row and preserves `createdAt`.
51
49
  *
52
50
  * @param {Ref} ref
53
51
  * @param {Record<string, any>} row
@@ -65,16 +63,6 @@ export declare function set(ref: Ref, row: Record<string, any>, opts?: {
65
63
  * @returns {Promise<void>}
66
64
  */
67
65
  export declare function del(ref: Ref, id?: string): Promise<void>;
68
- /**
69
- * Count rows on `ref`, optionally filtered.
70
- *
71
- * @param {Ref} ref
72
- * @param {Record<string, any>} [q]
73
- * @returns {Promise<{ data: number }>}
74
- */
75
- export declare function count(ref: Ref, q?: Record<string, any>): Promise<{
76
- data: number;
77
- }>;
78
66
  /**
79
67
  * Invoke an `action`-kind ref (a custom mutation declared in the schema).
80
68
  *
@@ -84,43 +72,39 @@ export declare function count(ref: Ref, q?: Record<string, any>): Promise<{
84
72
  */
85
73
  export declare function call(ref: Ref, d?: Record<string, any>): Promise<any>;
86
74
  /**
87
- * Intercept writes to `ref` before they commit `fn(ctx)` runs in-path
88
- * (awaited). Return `false` to cancel the write, or mutate `ctx.row`.
89
- * Returns an unsubscribe fn; pass `{ signal }` to unsubscribe on abort.
75
+ * Rule that runs before a write to `ref` lands at apply, on every peer, inside the op's
76
+ * transaction. Return `false` to refuse it: the writer's own call rejects with `REFUSED`.
77
+ * `ctx` is `{ op, name, row, existing, id, memberId, role, get, put, set, del }`; mutate
78
+ * `ctx.row` to rewrite what is stored. `op` is the op as it applies, so an upsert on a
79
+ * collection is a `put`. The four operators on `ctx` read and write the room as it stands at
80
+ * this op, inside the transaction. Must be deterministic — read only `ctx`, never a clock or
81
+ * local state — and registered before any op applies, in the process that owns the data. The
82
+ * imported operators throw inside a hook; use the ones on `ctx`. Not available over RPC.
90
83
  *
91
84
  * @param {Ref} ref
92
- * @param {(ctx: { op: string, name: string, row: any }) => any} fn
85
+ * @param {(ctx: HookContext) => unknown} fn
93
86
  * @param {{ signal?: AbortSignal }} [opts]
94
87
  * @returns {() => void}
95
88
  */
96
- export declare function before(ref: Ref, fn: (ctx: {
97
- op: string;
98
- name: string;
99
- row: any;
100
- }) => any, opts?: {
89
+ export declare function before(ref: Ref, fn: (ctx: HookContext) => unknown, opts?: {
101
90
  signal?: AbortSignal;
102
91
  }): () => void;
103
92
  /**
104
- * Subscribe to writes on `ref` — fires after each committed write,
105
- * non-blocking (observe only). Returns an unsubscribe fn; pass `{ signal }`
106
- * to unsubscribe on abort.
93
+ * Rule that runs after a write to `ref` lands at apply, on every peer, inside the op's
94
+ * transaction. Write derived rows through `ctx.put` / `ctx.set` / `ctx.del`; a throw refuses
95
+ * the whole op. Same `ctx` and the same determinism and registration rules as `before`. Use
96
+ * `changes(ref)` instead to observe writes locally.
107
97
  *
108
98
  * @param {Ref} ref
109
- * @param {(ctx: { op: string, name: string, row: any }) => void} fn
99
+ * @param {(ctx: HookContext) => unknown} fn
110
100
  * @param {{ signal?: AbortSignal }} [opts]
111
101
  * @returns {() => void}
112
102
  */
113
- export declare function after(ref: Ref, fn: (ctx: {
114
- op: string;
115
- name: string;
116
- row: any;
117
- }) => void, opts?: {
103
+ export declare function after(ref: Ref, fn: (ctx: HookContext) => unknown, opts?: {
118
104
  signal?: AbortSignal;
119
105
  }): () => void;
120
106
  /**
121
- * Read from `ref`. For data refs, dispatches to the underlying store. For
122
- * `handle`-kind refs, lists existing child handles of that type from the
123
- * parent's `handles` collection.
107
+ * Read from `ref`. For data refs, dispatches to the underlying store.
124
108
  *
125
109
  * @param {Ref} ref
126
110
  * @param {string | Record<string, any>} [q]
@@ -128,10 +112,8 @@ export declare function after(ref: Ref, fn: (ctx: {
128
112
  */
129
113
  export declare function get(ref: Ref, q?: string | Record<string, any>): Promise<SingleResult | ListResult | GetByIdResult>;
130
114
  /**
131
- * Live snapshot stream on `ref` — re-emits the latest `get()` result on
132
- * every underlying mutation. Tied to `ref.handle`'s lifecycle: closing the
133
- * handle destroys it. Pass `{ signal }` to bind it to a finer scope, or
134
- * destroy the stream directly to stop watching sooner.
115
+ * Live snapshot stream on `ref` — re-emits the latest `get()` result on every underlying
116
+ * mutation.
135
117
  *
136
118
  * @param {Ref} ref
137
119
  * @param {Record<string, any>} [q]
@@ -142,20 +124,13 @@ export declare function watch(ref: Ref, q?: Record<string, any>, opts?: {
142
124
  signal?: AbortSignal;
143
125
  }): import('streamx').Readable;
144
126
  /**
145
- * Delta subscription: batches of `{ prev, next }` row pairs instead of
146
- * full snapshots — lossless under backpressure, self-contained (the first
147
- * batch, and any batch after a view swap, replays current state as inserts
148
- * with `reset: true`). File-typed fields resolve on both sides.
127
+ * Delta subscription: batches of `{ prev, next }` row pairs instead of full snapshots —
128
+ * lossless under backpressure, self-contained (the first batch, and any batch after a view
129
+ * swap, replays current.
149
130
  */
150
131
  export declare function changes(ref: any, q: any, opts: any): any;
151
132
  /**
152
- * Open (or create / join / load) a child handle through a `handle`-kind
153
- * ref. Dispatches on the normalize of `arg`:
154
- *
155
- * - `string` → join via an invite string
156
- * - `{ invite: string }` → join via invite (object form)
157
- * - `{ id: string }` → load an existing handle by id
158
- * - `object | undefined` → create a new handle with the given opts
133
+ * Open (or create / join / load) a child handle through a `handle`-kind ref.
159
134
  *
160
135
  * @param {Ref} ref
161
136
  * @param {string | { invite?: string, id?: string, name?: string, routes?: any, role?: string, accept?: boolean } | undefined} [arg]
@@ -170,13 +145,9 @@ export declare function open(ref: Ref, arg?: string | {
170
145
  accept?: boolean;
171
146
  } | undefined): Promise<CeroHandle>;
172
147
  /**
173
- * Rotate a handle's encryption epoch. A fresh secret is sealed to every
174
- * current member and announced through the log — members removed before the
175
- * rotation cannot decrypt anything written after it. Requires the remove
176
- * permission (admin or owner). Compose with removal:
177
- *
178
- * await cero.del(room.members, memberId)
179
- * await cero.rotate(room)
148
+ * Rotate a handle's encryption epoch. A fresh secret is sealed to every current member and
149
+ * announced through the log — members removed before the rotation cannot decrypt anything
150
+ * written after it.
180
151
  *
181
152
  * @param {any} handle
182
153
  * @returns {Promise<{ epoch: number }>}
@@ -184,27 +155,3 @@ export declare function open(ref: Ref, arg?: string | {
184
155
  export declare function rotate(handle: any): Promise<{
185
156
  epoch: number;
186
157
  }>;
187
- /**
188
- * Put custom operators on `handle`, currying it as their first argument so
189
- * `handle.ns.fn(args)` calls `fn(handle, args)`. `arg` is either:
190
- * - a `{ ns: module }` map → bind exactly those, or
191
- * - `null` → the registered root operators, or
192
- * - a child-handle type → the registered operators for that type.
193
- * The scope forms are how cero binds handles automatically; pass a map yourself
194
- * for manual binding.
195
- *
196
- * @param {any} handle
197
- * @param {Record<string, any> | string | null} arg
198
- * @returns {any} handle
199
- */
200
- export declare function bind(handle: any, arg: Record<string, any> | string | null): any;
201
- /**
202
- * Register custom operators by scope. A bare key binds on the root handle; a key
203
- * that names a child-handle type binds on every handle of that type. Call once
204
- * at startup, before `cero()` / `connect()`, in both processes.
205
- *
206
- * @param {Record<string, any>} map
207
- */
208
- export declare function define(map: Record<string, any>): void;
209
- /** Test seam: clear all registered operators. */
210
- export declare function _clearDefined(): void;
@@ -1,7 +1,6 @@
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
+ * Quickly check whether the on-disk directory at `dir` already holds an initialised cero
3
+ * identity (i.e. a stored master seed).
5
4
  *
6
5
  * @param {string} dir Cero data directory.
7
6
  * @param {any} spec Built spec — same value passed to `cero(dir, spec)`.
@@ -11,28 +11,28 @@ export type RefInfo = {
11
11
  * from a generated spec.
12
12
  */
13
13
  /**
14
- * Typed pointer to a single ref (table or handle slot) on a `Handle` or
15
- * `Local`. Operators (`put`/`get`/`open`/...) take a `Ref` as their first
16
- * argument and dispatch through the owning handle's store.
14
+ * Typed pointer to a single ref (table or handle slot) on a `Handle` or `Local`.
17
15
  */
18
16
  export declare class Ref {
19
17
  handle: any;
20
18
  name: string;
21
19
  kind: string;
22
20
  schema: string;
21
+ type: any;
23
22
  /**
24
23
  * @param {any} handle Owner — a `Handle` (or `Local`) the ref lives on.
25
24
  * @param {string} name Ref name as declared in the schema.
26
25
  * @param {string} kind Ref kind: `'collection'`, `'single'`, `'action'`, or `'handle'`.
27
26
  * @param {string | null} [schema] Fully-qualified schema id, if any.
28
27
  */
29
- constructor(handle: any, name: string, kind: string, schema?: string | null);
28
+ constructor(handle: any, name: string, kind: string, schema?: string | null, type?: any);
30
29
  /**
31
30
  * Attach a `Ref` property to `target` for every entry in `refs`, so callers
32
31
  * write `handle.someRef` instead of looking refs up by name.
33
32
  *
34
33
  * @param {any} target
35
34
  * @param {Record<string, RefInfo>} refs
35
+ * @param {Record<string, any>} [handles] The handle types, so `target.room.notes` names every room's notes.
36
36
  */
37
- static attach(target: any, refs: Record<string, RefInfo>): void;
37
+ static attach(target: any, refs: Record<string, RefInfo>, handles?: Record<string, any>): void;
38
38
  }
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Re-exports of the schema DSL (`t`) and `schema()` wrapper from
3
- * `@cero-base/core/schema`, so cero apps can describe their tables without
4
- * pulling in the core package directly.
2
+ * Re-exports of the schema DSL (`t`) and `schema()` wrapper from `@cero-base/core/schema`,
3
+ * so cero apps can describe their tables without pulling in the core package directly.
5
4
  */
6
5
  export { t, schema } from '@cero-base/core/schema';
@@ -21,9 +21,8 @@ export type LocalOpts = {
21
21
  * @property {Uint8Array} [storageKey] 32-byte key encrypting the local store at rest.
22
22
  */
23
23
  /**
24
- * Per-device, single-writer storage for cero — holds the master seed,
25
- * device keypair and any per-handle keypairs. Wraps a hyperbee-backed
26
- * `Storage` and exposes each local ref as a property of the instance.
24
+ * Per-device, single-writer storage for cero — holds the master seed, device keypair and
25
+ * any per-handle keypairs.
27
26
  */
28
27
  export declare class Local extends ReadyResource {
29
28
  dir: string;
@@ -1,13 +1,13 @@
1
1
  import { RPCClient } from '@cero-base/core/rpc';
2
- import { put, set, get, del, count, watch, changes, call, open, rotate, bind, define } from '../lib/operators.js';
2
+ import { put, set, get, del, watch, changes, call, open, rotate } from '../lib/operators.js';
3
3
  import { t, schema } from '../lib/spec.js';
4
- export { put, set, get, del, count, watch, changes, call, open, rotate, bind, define, t, schema };
4
+ export { put, set, get, del, watch, changes, call, open, rotate, t, schema };
5
5
  export type BaseRPCClient = import('@cero-base/core/rpc').RPCClient;
6
6
  export type RefInfo = {
7
7
  kind?: 'single' | 'collection' | 'action' | 'handle';
8
8
  schema?: string;
9
9
  type?: string;
10
- builtin?: boolean;
10
+ internal?: boolean;
11
11
  };
12
12
  export type Spec = import('@cero-base/core/rpc').Spec & {
13
13
  meta: {
@@ -50,10 +50,7 @@ export type HandleStub = {
50
50
  */
51
51
  export declare function restore(me: Client, phrase: string): Promise<Client>;
52
52
  /**
53
- * Per-device `local`-namespace surface on a Client. Exposes each app-defined
54
- * local ref (e.g. `client.local.settings`) and routes ops over RPC with
55
- * `local: true`, so they hit the server's per-device store and never
56
- * replicate. Built-in local refs (identity master/keypair) are not exposed.
53
+ * Per-device `local`-namespace surface on a Client.
57
54
  */
58
55
  declare class LocalRefs {
59
56
  parent: Client;
@@ -68,11 +65,12 @@ declare class LocalRefs {
68
65
  get id(): any;
69
66
  }
70
67
  /**
71
- * IPC-side RPC client for cero. Wraps an `hrpc` channel and exposes the
72
- * same handle/ref/row API as a local cero instance, transparently
73
- * routing every operation across the wire.
68
+ * IPC-side RPC client for cero. Wraps an `hrpc` channel and exposes the same
69
+ * handle/ref/row API as a local cero instance, transparently routing every operation
70
+ * across the wire.
74
71
  */
75
72
  export declare class Client extends RPCClient {
73
+ operators: Record<string, any>;
76
74
  id: any;
77
75
  deviceId: any;
78
76
  store: this;
@@ -86,9 +84,16 @@ export declare class Client extends RPCClient {
86
84
  /**
87
85
  * @param {any} ipc Framed IPC stream (must be writable).
88
86
  * @param {Spec} spec Compiled cero spec (schema + rpc + handles).
87
+ * @param {{ operators?: Record<string, any> }} [opts] The operators to bind, instead of the ones the spec carries.
89
88
  */
90
- constructor(ipc: any, spec: Spec);
89
+ constructor(ipc: any, spec: Spec, opts?: {
90
+ operators?: Record<string, any>;
91
+ });
91
92
  _open(): Promise<void>;
93
+ /** Pause networking and storage on the server. Idempotent. */
94
+ suspend(): Promise<void>;
95
+ /** Resume a suspended server. Idempotent. */
96
+ resume(): Promise<void>;
92
97
  /**
93
98
  * Create a new child handle of the given type.
94
99
  *
@@ -115,9 +120,9 @@ export declare class Client extends RPCClient {
115
120
  _join(invite: string, type: string): Promise<Handle>;
116
121
  }
117
122
  /**
118
- * Client-side proxy for a remote handle. Exposes the same row-ops surface
119
- * as `Client` but scoped to a single child handle id, and routes every
120
- * call through the parent's RPC channel.
123
+ * Client-side proxy for a remote handle. Exposes the same row-ops surface as `Client` but
124
+ * scoped to a single child handle id, and routes every call through the parent's RPC
125
+ * channel.
121
126
  */
122
127
  declare class Handle {
123
128
  parent: Client;
@@ -145,23 +150,24 @@ declare class Handle {
145
150
  *
146
151
  * @param {any} ipc
147
152
  * @param {object} spec
153
+ * @param {{ operators?: Record<string, any> }} [opts]
148
154
  * @returns {Promise<Client>}
149
155
  */
150
- export declare function connect(ipc: any, spec: object): Promise<Client>;
156
+ export declare function connect(ipc: any, spec: object, opts?: {
157
+ operators?: Record<string, any>;
158
+ }): Promise<Client>;
151
159
  /**
152
- * Symmetric client entry. Mirrors the main `cero`, but `cero(ipc, spec)` connects
153
- * to a server (via `connect`) instead of opening a local store. The same operator
154
- * surface is attached, so `import { cero } from '@cero-base/cero/client'` works
155
- * just like `import { cero } from '@cero-base/cero'`.
156
- *
157
- * Note: `before`/`after`/`peek` are intentionally not exposed here — they hook the
158
- * local write path / probe a local store, which a client proxy has no notion of.
160
+ * Symmetric client entry. Mirrors the main `cero`, but `cero(ipc, spec)` connects to a
161
+ * server (via `connect`) instead of opening a local store.
159
162
  *
160
163
  * @param {any} ipc Framed IPC duplex stream.
161
164
  * @param {any} spec Built cero spec.
165
+ * @param {{ operators?: Record<string, any> }} [opts]
162
166
  * @returns {Promise<Client>}
163
167
  */
164
- export declare function cero(ipc: any, spec: any): Promise<Client>;
168
+ export declare function cero(ipc: any, spec: any, opts?: {
169
+ operators?: Record<string, any>;
170
+ }): Promise<Client>;
165
171
  export declare namespace cero {
166
172
  export { connect };
167
173
  export { restore };
@@ -170,13 +176,10 @@ export declare namespace cero {
170
176
  export { set };
171
177
  export { get };
172
178
  export { del };
173
- export { count };
174
179
  export { watch };
175
180
  export { changes };
176
181
  export { call };
177
182
  export { open };
178
183
  export { rotate };
179
- export { bind };
180
- export { define };
181
184
  export { schema };
182
185
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
- * RPC barrel — re-exports the `Server`/`Client` classes and their
3
- * `serve()`/`connect()` helpers so consumers can spin up either side
4
- * of the cero IPC bridge from a single import.
2
+ * RPC barrel — re-exports the `Server`/`Client` classes and their `serve()`/`connect()`
3
+ * helpers so consumers can spin up either side of the cero IPC bridge from a single
4
+ * import.
5
5
  */
6
6
  export { Server, serve } from './server.js';
7
7
  export { Client, connect } from './client.js';
@@ -57,10 +57,9 @@ export type GetResult = {
57
57
  * @typedef {{ data: any, total?: number, size?: number }} GetResult Single-ref get omits `total`/`size`; list/handle refs include them.
58
58
  */
59
59
  /**
60
- * IPC-side RPC server for cero. Bridges an `hrpc` channel to a live
61
- * `Handle` tree: lazy-initializes the root via `cero()` on the first
62
- * `init` call, then exposes data ops, pairing, and handle lifecycle
63
- * over the wire using the spec-bound codec.
60
+ * IPC-side RPC server for cero. Bridges an `hrpc` channel to a live `Handle` tree:
61
+ * lazy-initializes the root via `cero()` on the first `init` call, then exposes data ops,
62
+ * pairing, and handle lifecycle.
64
63
  */
65
64
  export declare class Server extends RPCServer {
66
65
  storage: string;
@@ -83,8 +82,6 @@ export declare class Server extends RPCServer {
83
82
  * @param {Partial<ServerOpts>} [opts]
84
83
  */
85
84
  constructor(ipc: any, spec: object, { storage, ...opts }?: Partial<ServerOpts>);
86
- /** End every watch stream bound to a handle (e.g. when it closes or leaves). */
87
- _endWatches(handle: any): void;
88
85
  /**
89
86
  * Root cero id (null until `init` has run).
90
87
  *
@@ -98,11 +95,13 @@ export declare class Server extends RPCServer {
98
95
  */
99
96
  get identity(): any;
100
97
  _close(): Promise<void>;
98
+ /** End every watch stream bound to a handle (e.g. when it closes or leaves). */
99
+ _endWatches(handle: any): void;
101
100
  /** Wire the `init` handler that lazily constructs the root cero handle. */
102
101
  _wireInit(): void;
103
102
  /** Wire the `restore` handler that rebuilds the local store from a phrase. */
104
103
  _wireRestore(): void;
105
- /** Register the row-level RPC handlers (put/set/get/del/count/watch/call). */
104
+ /** Register the row-level RPC handlers (put/set/get/del/watch/call). */
106
105
  _wireData(): void;
107
106
  /** Register invite/revoke/join RPC handlers. */
108
107
  _wirePairing(): void;
@@ -117,9 +116,7 @@ export declare class Server extends RPCServer {
117
116
  */
118
117
  _resolve(id: string): any;
119
118
  /**
120
- * Resolve a `{ handle, ref }` pair to its `Ref` and codec. When `local` is
121
- * set, the ref is resolved against the root's per-device `local` store and
122
- * paired with the codec bound from the local schema.
119
+ * Resolve a `{ handle, ref }` pair to its `Ref` and codec.
123
120
  *
124
121
  * @param {string} id
125
122
  * @param {string} name
@@ -1,174 +0,0 @@
1
- // Build wiring for the builtin schemas (schemas.js): turns the grouped field
2
- // maps into the type / collection / dispatch / command descriptors the builder
3
- // registers. Every cero schema includes these; app refs are added on top.
4
- import { CeroError } from '@cero-base/core/errors'
5
- import { COUNTERS, EPOCHS, DB_TYPE } from '../lib/constants.js'
6
- import * as schemas from './schemas.js'
7
-
8
- // Builtin collections by scope — ref name → { type, kind? }. kind defaults to
9
- // 'collection' (id-keyed); 'single' has no key. Dispatches are derived for
10
- // main; counters is internal (a collection with no add/set/del).
11
- export const refs = {
12
- main: {
13
- members: { type: 'member' },
14
- devices: { type: 'device' },
15
- invites: { type: 'invite' },
16
- handles: { type: 'handle' },
17
- files: { type: 'file' }
18
- },
19
- local: {
20
- master: { type: 'master', kind: 'single' },
21
- keypair: { type: 'keypair', kind: 'single' },
22
- 'handle-keypairs': { type: 'handle-keypair' },
23
- environment: { type: 'environment', kind: 'single' }
24
- }
25
- }
26
-
27
- export function getHyperdbType(prim) {
28
- return DB_TYPE[prim] || 'string'
29
- }
30
-
31
- const at = (ns, n) => `@${ns}/${n}`
32
- const keyOf = (def) => (def.kind === 'single' ? [] : ['id'])
33
-
34
- const fields = (map) =>
35
- Object.entries(map).map(([name, m]) => ({
36
- name,
37
- type: getHyperdbType(m.prim),
38
- required: m.required === true
39
- }))
40
-
41
- // Merge app `t.extend` fields into a builtin's base fields — base can't be redeclared.
42
- const merge = (type, base, extra) => {
43
- if (!extra) return base
44
- for (const k in extra) {
45
- if (k in base) {
46
- throw CeroError.INVALID(`'${k}' is a base field of '${type}' and cannot be redeclared`)
47
- }
48
- }
49
- return { ...base, ...extra }
50
- }
51
-
52
- const descriptors = (group, extend = {}) =>
53
- Object.entries(group).map(([name, base]) => ({
54
- name,
55
- compact: false,
56
- fields: fields(merge(name, base, extend[name]))
57
- }))
58
-
59
- // meta.refs entries for a scope's builtins.
60
- export function builtinRefs(ns, scope) {
61
- return Object.fromEntries(
62
- Object.entries(refs[scope]).map(([name, def]) => [
63
- name,
64
- {
65
- kind: def.kind || 'collection',
66
- path: [name],
67
- builtin: true,
68
- ...(scope === 'main' && { verb: def.type }),
69
- schema: at(ns, def.type)
70
- }
71
- ])
72
- )
73
- }
74
-
75
- // hyperschema type descriptors. scope is 'main' | 'local' | 'rpc'; `extend`
76
- // merges app `t.extend` fields into the matching type.
77
- export function builtinTypes(scope, extend) {
78
- return descriptors(schemas[scope], extend)
79
- }
80
- export function rpcTypes() {
81
- return descriptors(schemas.rpc)
82
- }
83
-
84
- // hyperdb collection descriptors for a scope.
85
- export function builtinCollections(ns, scope) {
86
- const out = Object.entries(refs[scope]).map(([name, def]) => ({
87
- name,
88
- schema: at(ns, def.type),
89
- key: keyOf(def)
90
- }))
91
- if (scope === 'main') {
92
- out.push({ name: COUNTERS, schema: at(ns, 'counter'), key: ['name'] })
93
- out.push({ name: EPOCHS, schema: at(ns, 'epoch'), key: ['epoch'] })
94
- }
95
- return out
96
- }
97
-
98
- // hyperdispatch descriptors (main scope only).
99
- export function builtinDispatches(ns) {
100
- return [
101
- { name: 'add-writer', requestType: at(ns, 'writer') },
102
- { name: 'del-writer', requestType: at(ns, 'writer') },
103
- { name: 'claim-writer', requestType: at(ns, 'claim') },
104
- ...Object.values(refs.main).flatMap(({ type }) => [
105
- { name: `add-${type}`, requestType: at(ns, type) },
106
- { name: `set-${type}`, requestType: at(ns, type) },
107
- { name: `del-${type}`, requestType: at(ns, 'del-by-id') }
108
- ])
109
- ]
110
- }
111
-
112
- // Registered AFTER the app's own dispatches (see build/index.js): hyperdispatch
113
- // numbers routes positionally and persists them, so a spec built before
114
- // rotation must see rotate-key appended at the end — inserting it into the
115
- // builtin group would collide with the app's persisted route ids on an
116
- // incremental rebuild.
117
- export function rotateDispatch(ns) {
118
- return { name: 'rotate-key', requestType: at(ns, 'epoch') }
119
- }
120
-
121
- export function rpcCommands(ns) {
122
- const ref = (n) => at(ns, n)
123
- return [
124
- { name: 'init', request: { name: ref('req-empty') }, response: { name: ref('res-identity') } },
125
- {
126
- name: 'restore',
127
- request: { name: ref('req-restore') },
128
- response: { name: ref('res-identity') }
129
- },
130
- { name: 'seed', request: { name: ref('req-empty') }, response: { name: ref('res-seed') } },
131
- { name: 'add-row', request: { name: ref('req-row') }, response: { name: ref('res-data') } },
132
- {
133
- name: 'add-file',
134
- request: { name: ref('req-add-file') },
135
- response: { name: ref('res-data') }
136
- },
137
- {
138
- name: 'add-handle',
139
- request: { name: ref('req-row') },
140
- response: { name: ref('res-handle') }
141
- },
142
- { name: 'set', request: { name: ref('req-row') }, response: { name: ref('res-data') } },
143
- { name: 'get', request: { name: ref('req-query') }, response: { name: ref('res-rows') } },
144
- { name: 'get-one', request: { name: ref('req-id') }, response: { name: ref('res-data') } },
145
- { name: 'del', request: { name: ref('req-id') }, response: { name: ref('res-ok') } },
146
- { name: 'count', request: { name: ref('req-query') }, response: { name: ref('res-count') } },
147
- {
148
- name: 'watch',
149
- request: { name: ref('req-query') },
150
- response: { name: ref('res-rows'), stream: true }
151
- },
152
- { name: 'call', request: { name: ref('req-call') }, response: { name: ref('res-data') } },
153
- { name: 'invite', request: { name: ref('req-invite') }, response: { name: ref('res-invite') } },
154
- { name: 'revoke', request: { name: ref('req-revoke') }, response: { name: ref('res-ok') } },
155
- { name: 'join', request: { name: ref('req-join') }, response: { name: ref('res-handle') } },
156
- {
157
- name: 'open-handle',
158
- request: { name: ref('req-open') },
159
- response: { name: ref('res-handle') }
160
- },
161
- {
162
- name: 'close-handle',
163
- request: { name: ref('req-handle') },
164
- response: { name: ref('res-ok') }
165
- },
166
- { name: 'leave', request: { name: ref('req-handle') }, response: { name: ref('res-ok') } },
167
- {
168
- name: 'changes',
169
- request: { name: ref('req-query') },
170
- response: { name: ref('res-changes'), stream: true }
171
- },
172
- { name: 'rotate', request: { name: ref('req-handle') }, response: { name: ref('res-epoch') } }
173
- ]
174
- }
@@ -1,9 +0,0 @@
1
- // Process-wide registry for extensions registered via `cero.use()`.
2
- // `build()` folds in each extension's schema; `cero()` runs each setup.
3
- // The bundled extensions are on by default — `cero.use(profileSync({...}))`
4
- // replaces a default by name, `{ extensions: false }` (on build and cero)
5
- // leaves them out entirely.
6
- import { profileSync } from '../extensions/profile-sync.js'
7
- import { handleSync } from '../extensions/handle-sync.js'
8
-
9
- export const internal = { extensions: [profileSync(), handleSync()] }
@@ -1,24 +0,0 @@
1
- export declare const internal: {
2
- extensions: ({
3
- name: string;
4
- bundled: boolean;
5
- schema: {
6
- profile: import("@cero-base/core").TypeDef;
7
- members: {
8
- kind: 'extend';
9
- fields: Record<string, import("@cero-base/core").Prim>;
10
- };
11
- };
12
- setup(me: any): void;
13
- } | {
14
- name: string;
15
- bundled: boolean;
16
- schema: {
17
- handles: {
18
- kind: 'extend';
19
- fields: Record<string, import("@cero-base/core").Prim>;
20
- };
21
- };
22
- setup(me: any): void;
23
- })[];
24
- };