@cero-base/cero 2.0.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.
package/types/index.d.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import { Identity } from '@cero-base/core/identity';
2
2
  import { Handle, Ref } from './handle/index.js';
3
3
  import { Local } from './local/index.js';
4
- import { put, set, get, del, count, watch, changes, call, open, rotate, before, after, bind, define } from './lib/operators.js';
4
+ import { put, set, get, del, watch, changes, call, open, rotate, before, after } from './lib/operators.js';
5
5
  import { peek } from './lib/peek.js';
6
6
  import { t, schema } from './lib/spec.js';
7
- import { registry } from './extensions/index.js';
8
7
  export { Handle, Ref, Local };
9
- export { put, set, get, del, count, watch, changes, call, open, rotate, before, after, bind, define } from './lib/operators.js';
8
+ export { put, set, get, del, watch, changes, call, open, rotate, before, after } from './lib/operators.js';
10
9
  export { peek } from './lib/peek.js';
11
10
  export { t, schema } from './lib/spec.js';
12
11
  export type CeroHandle = import('./handle/index.js').CeroHandle;
@@ -54,6 +53,14 @@ export type CeroOpts = {
54
53
  * Blind-peer public keys. Rooms and files are mirrored through them so peers sync even when never online at the same time. Mirrors hold only encrypted blocks — they never read your data.
55
54
  */
56
55
  mirrors?: Array<string | Uint8Array>;
56
+ /**
57
+ * How many rooms search, how many only announce, and the idle ms before the rest leave the swarm.
58
+ */
59
+ presence?: {
60
+ active?: number;
61
+ announced?: number;
62
+ idle?: number;
63
+ };
57
64
  /**
58
65
  * Existing database key to recover into, skipping the pointer lookup.
59
66
  */
@@ -79,9 +86,13 @@ export type CeroOpts = {
79
86
  */
80
87
  storageKey?: Uint8Array;
81
88
  /**
82
- * `false` disables the bundled extensions (profileSync, handleSync) for this instance. Build with `{ extensions: false }` too so the spec matches.
89
+ * The extensions this instance runs, instead of the ones the spec carries. Build with the same list.
90
+ */
91
+ extensions?: import('./extensions/index.js').Extension[];
92
+ /**
93
+ * The operators to bind, instead of the ones the spec carries.
83
94
  */
84
- extensions?: boolean;
95
+ operators?: Record<string, any>;
85
96
  /**
86
97
  * `true` enables nearby (Bluetooth) sync via `me.bluetooth` (auto-started). `{ autoStart: false }` creates the facade without starting the radio — the app calls `me.bluetooth.start()`/`stop()` (user toggle). `backend` injects a bare-bluetooth-shaped backend (tests). `maxOutbound`/`maxInbound` cap concurrent outbound links and inbound sessions. `pipe` picks the data pipe — `'l2cap'` (default, faster) or `'gatt'`; both peers must match. Absent backend on an unsupported host → `me.bluetooth.state === 'unsupported'`.
87
98
  */
@@ -108,13 +119,15 @@ export type CeroOpts = {
108
119
  * @property {number[]} [backoffs] Swarm reconnect backoff tiers in ms (testing/tuning).
109
120
  * @property {string} [channel] Optional network-isolation label; only same-channel peers connect.
110
121
  * @property {Array<string | Uint8Array>} [mirrors] Blind-peer public keys. Rooms and files are mirrored through them so peers sync even when never online at the same time. Mirrors hold only encrypted blocks — they never read your data.
122
+ * @property {{ active?: number, announced?: number, idle?: number }} [presence] How many rooms search, how many only announce, and the idle ms before the rest leave the swarm.
111
123
  * @property {Uint8Array} [key] Existing database key to recover into, skipping the pointer lookup.
112
124
  * @property {Uint8Array} [encryptionKey] Pre-existing encryption key.
113
125
  * @property {Record<string, Function>} [routes] Custom RPC routes for the database dispatcher.
114
126
  * @property {(err: any) => void} [onerror] Background-task error handler.
115
127
  * @property {number} [recoveryTimeout] Max wait to find another device and be admitted, in ms. Defaults to 30000.
116
128
  * @property {Uint8Array} [storageKey] 32-byte key encrypting local key material (master seed, device keypairs) at rest. Source it from the OS keychain — cero never stores it.
117
- * @property {boolean} [extensions] `false` disables the bundled extensions (profileSync, handleSync) for this instance. Build with `{ extensions: false }` too so the spec matches.
129
+ * @property {import('./extensions/index.js').Extension[]} [extensions] The extensions this instance runs, instead of the ones the spec carries. Build with the same list.
130
+ * @property {Record<string, any>} [operators] The operators to bind, instead of the ones the spec carries.
118
131
  * @property {boolean | { autoStart?: boolean, backend?: any, maxOutbound?: number, maxInbound?: number, pipe?: 'l2cap' | 'gatt' }} [bluetooth] `true` enables nearby (Bluetooth) sync via `me.bluetooth` (auto-started). `{ autoStart: false }` creates the facade without starting the radio — the app calls `me.bluetooth.start()`/`stop()` (user toggle). `backend` injects a bare-bluetooth-shaped backend (tests). `maxOutbound`/`maxInbound` cap concurrent outbound links and inbound sessions. `pipe` picks the data pipe — `'l2cap'` (default, faster) or `'gatt'`; both peers must match. Absent backend on an unsupported host → `me.bluetooth.state === 'unsupported'`.
119
132
  */
120
133
  /**
@@ -132,7 +145,6 @@ export declare namespace cero {
132
145
  export { set };
133
146
  export { get };
134
147
  export { del };
135
- export { count };
136
148
  export { watch };
137
149
  export { changes };
138
150
  export { call };
@@ -143,10 +155,6 @@ export declare namespace cero {
143
155
  export { peek };
144
156
  export { restore };
145
157
  export { schema };
146
- export { bind };
147
- export { define };
148
- export { registry as _registry };
149
- export var use: (...exts: any[]) => void;
150
158
  }
151
159
  /**
152
160
  * Restore a cero instance from a mnemonic phrase.
@@ -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
@@ -61,16 +63,6 @@ export declare function set(ref: Ref, row: Record<string, any>, opts?: {
61
63
  * @returns {Promise<void>}
62
64
  */
63
65
  export declare function del(ref: Ref, id?: string): Promise<void>;
64
- /**
65
- * Count rows on `ref`, optionally filtered.
66
- *
67
- * @param {Ref} ref
68
- * @param {Record<string, any>} [q]
69
- * @returns {Promise<{ data: number }>}
70
- */
71
- export declare function count(ref: Ref, q?: Record<string, any>): Promise<{
72
- data: number;
73
- }>;
74
66
  /**
75
67
  * Invoke an `action`-kind ref (a custom mutation declared in the schema).
76
68
  *
@@ -80,34 +72,35 @@ export declare function count(ref: Ref, q?: Record<string, any>): Promise<{
80
72
  */
81
73
  export declare function call(ref: Ref, d?: Record<string, any>): Promise<any>;
82
74
  /**
83
- * Intercept writes to `ref` before they commit `fn(ctx)` runs in-path (awaited).
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.
84
83
  *
85
84
  * @param {Ref} ref
86
- * @param {(ctx: { op: string, name: string, row: any }) => any} fn
85
+ * @param {(ctx: HookContext) => unknown} fn
87
86
  * @param {{ signal?: AbortSignal }} [opts]
88
87
  * @returns {() => void}
89
88
  */
90
- export declare function before(ref: Ref, fn: (ctx: {
91
- op: string;
92
- name: string;
93
- row: any;
94
- }) => any, opts?: {
89
+ export declare function before(ref: Ref, fn: (ctx: HookContext) => unknown, opts?: {
95
90
  signal?: AbortSignal;
96
91
  }): () => void;
97
92
  /**
98
- * Subscribe to writes on `ref` — fires after each committed write, non-blocking (observe
99
- * only).
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.
100
97
  *
101
98
  * @param {Ref} ref
102
- * @param {(ctx: { op: string, name: string, row: any }) => void} fn
99
+ * @param {(ctx: HookContext) => unknown} fn
103
100
  * @param {{ signal?: AbortSignal }} [opts]
104
101
  * @returns {() => void}
105
102
  */
106
- export declare function after(ref: Ref, fn: (ctx: {
107
- op: string;
108
- name: string;
109
- row: any;
110
- }) => void, opts?: {
103
+ export declare function after(ref: Ref, fn: (ctx: HookContext) => unknown, opts?: {
111
104
  signal?: AbortSignal;
112
105
  }): () => void;
113
106
  /**
@@ -162,21 +155,3 @@ export declare function open(ref: Ref, arg?: string | {
162
155
  export declare function rotate(handle: any): Promise<{
163
156
  epoch: number;
164
157
  }>;
165
- /**
166
- * Put custom operators on `handle`, currying it as their first argument so
167
- * `handle.ns.fn(args)` calls `fn(handle, args)`.
168
- *
169
- * @param {any} handle
170
- * @param {Record<string, any> | string | null} arg
171
- * @returns {any} handle
172
- */
173
- export declare function bind(handle: any, arg: Record<string, any> | string | null): any;
174
- /**
175
- * Register custom operators by scope. A bare key binds on the root handle; a key that
176
- * names a child-handle type binds on every handle of that type.
177
- *
178
- * @param {Record<string, any>} map
179
- */
180
- export declare function define(map: Record<string, any>): void;
181
- /** Test seam: clear all registered operators. */
182
- export declare function _clearDefined(): void;
@@ -18,19 +18,21 @@ export declare class Ref {
18
18
  name: string;
19
19
  kind: string;
20
20
  schema: string;
21
+ type: any;
21
22
  /**
22
23
  * @param {any} handle Owner — a `Handle` (or `Local`) the ref lives on.
23
24
  * @param {string} name Ref name as declared in the schema.
24
25
  * @param {string} kind Ref kind: `'collection'`, `'single'`, `'action'`, or `'handle'`.
25
26
  * @param {string | null} [schema] Fully-qualified schema id, if any.
26
27
  */
27
- constructor(handle: any, name: string, kind: string, schema?: string | null);
28
+ constructor(handle: any, name: string, kind: string, schema?: string | null, type?: any);
28
29
  /**
29
30
  * Attach a `Ref` property to `target` for every entry in `refs`, so callers
30
31
  * write `handle.someRef` instead of looking refs up by name.
31
32
  *
32
33
  * @param {any} target
33
34
  * @param {Record<string, RefInfo>} refs
35
+ * @param {Record<string, any>} [handles] The handle types, so `target.room.notes` names every room's notes.
34
36
  */
35
- static attach(target: any, refs: Record<string, RefInfo>): void;
37
+ static attach(target: any, refs: Record<string, RefInfo>, handles?: Record<string, any>): void;
36
38
  }
@@ -1,7 +1,7 @@
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';
@@ -70,6 +70,7 @@ declare class LocalRefs {
70
70
  * across the wire.
71
71
  */
72
72
  export declare class Client extends RPCClient {
73
+ operators: Record<string, any>;
73
74
  id: any;
74
75
  deviceId: any;
75
76
  store: this;
@@ -83,9 +84,16 @@ export declare class Client extends RPCClient {
83
84
  /**
84
85
  * @param {any} ipc Framed IPC stream (must be writable).
85
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.
86
88
  */
87
- constructor(ipc: any, spec: Spec);
89
+ constructor(ipc: any, spec: Spec, opts?: {
90
+ operators?: Record<string, any>;
91
+ });
88
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>;
89
97
  /**
90
98
  * Create a new child handle of the given type.
91
99
  *
@@ -142,18 +150,24 @@ declare class Handle {
142
150
  *
143
151
  * @param {any} ipc
144
152
  * @param {object} spec
153
+ * @param {{ operators?: Record<string, any> }} [opts]
145
154
  * @returns {Promise<Client>}
146
155
  */
147
- 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>;
148
159
  /**
149
160
  * Symmetric client entry. Mirrors the main `cero`, but `cero(ipc, spec)` connects to a
150
161
  * server (via `connect`) instead of opening a local store.
151
162
  *
152
163
  * @param {any} ipc Framed IPC duplex stream.
153
164
  * @param {any} spec Built cero spec.
165
+ * @param {{ operators?: Record<string, any> }} [opts]
154
166
  * @returns {Promise<Client>}
155
167
  */
156
- 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>;
157
171
  export declare namespace cero {
158
172
  export { connect };
159
173
  export { restore };
@@ -162,13 +176,10 @@ export declare namespace cero {
162
176
  export { set };
163
177
  export { get };
164
178
  export { del };
165
- export { count };
166
179
  export { watch };
167
180
  export { changes };
168
181
  export { call };
169
182
  export { open };
170
183
  export { rotate };
171
- export { bind };
172
- export { define };
173
184
  export { schema };
174
185
  }
@@ -101,7 +101,7 @@ export declare class Server extends RPCServer {
101
101
  _wireInit(): void;
102
102
  /** Wire the `restore` handler that rebuilds the local store from a phrase. */
103
103
  _wireRestore(): void;
104
- /** 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). */
105
105
  _wireData(): void;
106
106
  /** Register invite/revoke/join RPC handlers. */
107
107
  _wirePairing(): void;