@yydb/yydb 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -17,10 +17,15 @@ For a wire client only (renderer or an app that already runs `yydb serve`), use
17
17
  | Import | Runtime | Role |
18
18
  |--------|---------|------|
19
19
  | `@yydb/yydb` | Node | `Database.open`, micro helpers, VOS re-exports |
20
- | `@yydb/yydb/cli` | Node | CLI (`version`, `init`, `info`, `serve`) |
20
+ | `@yydb/yydb/cli` | Node | CLI (`version`, `init`, `info`, `verify`, `serve`) |
21
21
 
22
22
  Node.js 18+.
23
23
 
24
+ ```bash
25
+ npm install @yydb/yydb
26
+ npx yydb version
27
+ ```
28
+
24
29
  Schema truth lives in the `.yydb` file via `ensureSchema` and VOS documents.
25
30
 
26
31
  ## Example
@@ -65,6 +70,21 @@ await db.close();
65
70
  `OpenOptions`: `cli` (explicit CLI script path), `port` (preferred loopback port), `serveArgs` (extra `yydb serve`
66
71
  arguments). `binary` is a deprecated alias for `cli`.
67
72
 
73
+ ### Prepared statements
74
+
75
+ `db.prepare` parses and lowers a VOS query once; the plan stays in a native thread-local registry and re-executes
76
+ with different bindings.
77
+
78
+ ```ts
79
+ const stmt = await db.prepare('User.all().take($limit).collect()');
80
+ const rows = await stmt.execute({ limit: 100 });
81
+ const more = await stmt.execute({ limit: 50 });
82
+ stmt.close();
83
+ ```
84
+
85
+ `stmt.parameters` lists the expected bindings, and `stmt.info` exposes the read/write sets and source fingerprint.
86
+ Missing, extra, or mistyped parameters raise `YydbError` with code `yydb.query.bind`.
87
+
68
88
  The native binding exposes additive operations (`schemaVersion`, `schemaDocument`, `recordCount`, `journalMode`,
69
89
  `checkpoint`, `walFrameCount`, `get`, `put`); the CLI composes commands from them.
70
90
 
@@ -73,7 +93,7 @@ The native binding exposes additive operations (`schemaVersion`, `schemaDocument
73
93
  | Need | Package |
74
94
  |------|---------|
75
95
  | Node embedded engine and CLI (this package) | **`@yydb/yydb`** |
76
- | Browser, pure TypeScript | **`@yydb/light-db`** |
96
+ | Browser engine (pure TS and WASM sessions) | **`@yydb/light-db`** |
77
97
  | Wire client only | [`@yydb/yydb-client`](https://www.npmjs.com/package/@yydb/yydb-client) |
78
98
  | In-process Rust embed | [`yydb`](https://crates.io/crates/yydb) |
79
99
  | Agent skills (wire protocol) | [`@yydb/yydb-skills`](https://www.npmjs.com/package/@yydb/yydb-skills) |
@@ -83,4 +103,4 @@ endpoint.
83
103
 
84
104
  The managed engine listens on `127.0.0.1` with no authentication — keep the endpoint private.
85
105
 
86
- [YYDB overview](https://github.com/yy-database/yydb.rs) · [User guides](https://github.com/yy-database/yydb.rs/tree/dev/projects/packages/homepage/documentation/zh-hans)
106
+ [YYDB overview](https://github.com/yy-database/yydb) · [User guides](https://github.com/yy-database/yydb/tree/dev/projects/packages/homepage/documentation/zh-hans)
@@ -1,50 +1,276 @@
1
+ import { type EmittableItem } from '@voml/vos';
1
2
  import { defineMicro as buildMicro, type DefinedMicro, type MicroHandle } from '../shared/micro.js';
3
+ import { loadNative, type NativeStatement as NativeStatementHandle } from '../native/binding.js';
2
4
  import type { UdfScalarDescriptor } from '../shared/udf-types.js';
3
- export type { SchemaVersion } from '@yydb/yydb-client';
5
+ /** Published schema metadata. */
6
+ export interface SchemaVersion {
7
+ version: number;
8
+ document: string;
9
+ }
10
+ /** Options for opening a local YYDB file. */
4
11
  export interface OpenOptions {
5
- /** Override CLI script path (tests / special installs). */
6
- cli?: string;
7
- /** @deprecated Use `cli`. */
8
- binary?: string;
9
- /** Prefer a specific loopback port; default = ephemeral free port. */
10
- port?: number;
11
- /** Extra args after `serve <path> --bind …`. */
12
- serveArgs?: string[];
12
+ /** Create parent directories when missing (default: true). */
13
+ mkdir?: boolean;
14
+ }
15
+ /** One row from a {@link Database.scan}. */
16
+ export interface ScanRow {
17
+ key: string;
18
+ value: Uint8Array;
19
+ }
20
+ /** Options for {@link Database.scan}. */
21
+ export interface ScanOptions {
22
+ /** Include this prefix in every returned key; default `""` returns everything. */
23
+ prefix?: string;
24
+ /** Exclusive continuation cursor from a previous scan's last row; default `null` starts fresh. */
25
+ after?: string | null;
26
+ /** Max rows returned per call; default `1000`. */
27
+ limit?: number;
28
+ }
29
+ /** One change in a {@link Database.batch}. Use `null` to delete. */
30
+ export interface BatchChange {
31
+ key: string;
32
+ value: Uint8Array | string | null;
33
+ }
34
+ /** Options for {@link Database.migrateSchema}. */
35
+ export interface MigrateOptions {
36
+ /** Rename entries — `fromType`/`toType` updates a type name; `type`/`fromField`/`toField` updates one field. */
37
+ renames?: ReadonlyArray<{
38
+ fromType: string;
39
+ toType: string;
40
+ } | {
41
+ type: string;
42
+ fromField: string;
43
+ toField: string;
44
+ }>;
45
+ }
46
+ /** One consistency finding from {@link Database.doctor}. */
47
+ export interface DoctorIssue {
48
+ severity: 'warn' | 'error';
49
+ code: string;
50
+ message: string;
51
+ key_hint?: string | null;
52
+ }
53
+ /** Per-namespace usage snapshot from {@link Database.doctor}. */
54
+ export interface DoctorNamespaceStats {
55
+ prefix: string;
56
+ record_count: number;
57
+ bytes_used: number;
58
+ object_bytes: number;
59
+ quota_max_bytes?: number | null;
60
+ ttl_expired_pending: number;
61
+ }
62
+ /** Read-only consistency report. */
63
+ export interface DoctorReport {
64
+ file_path?: string | null;
65
+ last_commit_sequence: number;
66
+ last_checkpoint_sequence: number;
67
+ checkpoint_lag_records: number;
68
+ wal_bytes_uncheckpointed: number;
69
+ orphan_object_count: number;
70
+ namespaces: DoctorNamespaceStats[];
71
+ issues: DoctorIssue[];
72
+ }
73
+ /** Summary of a v0 `.yydx` compaction pass. */
74
+ export interface CompactReport {
75
+ checkpointed: boolean;
76
+ orphans_reclaimed: number;
77
+ }
78
+ /** Declared type for a prepared statement parameter slot. */
79
+ export type SlotType = 'any' | 'bool' | 'i64' | 'text' | 'null';
80
+ /** One named parameter descriptor on a {@link Statement}. */
81
+ export interface ParameterDescriptor {
82
+ /** Canonical parameter name (no leading `$`). */
83
+ name: string;
84
+ /** Positional slot index within the bound-value slice (0-based). */
85
+ index: number;
86
+ /** Declared slot type; narrowed from surrounding context when possible. */
87
+ slotTy: SlotType;
88
+ /** Number of times this parameter appears in the source. */
89
+ usageCount: number;
90
+ }
91
+ /** Whether a prepared statement is a read pipeline or a write program. */
92
+ export type QueryKind = 'Read' | 'Write';
93
+ /** Metadata about a prepared statement — useful for logging and cache keys. */
94
+ export interface StatementInfo {
95
+ statementId: string;
96
+ queryKind: QueryKind;
97
+ parameters: ParameterDescriptor[];
98
+ readSet: string[];
99
+ writeSet: string[];
100
+ sourceFingerprint: string;
101
+ source: string;
13
102
  }
14
103
  /**
15
- * Node-facing YYDB handle. Looks like one library; owns a private loopback
16
- * `yydb serve` child under the hood.
104
+ * A pre-parsed, pre-lowered VOS query plan.
105
+ *
106
+ * Created by {@link Database.prepare}. The actual lowered op tree lives on
107
+ * the native side in a thread-local registry keyed by `statementId`; this
108
+ * class is a thin JS handle. Bind parameters and re-execute via
109
+ * {@link Statement.execute} without re-parsing or re-lowering the source.
110
+ *
111
+ * ```ts
112
+ * const stmt = await db.prepare('User.all().take($limit).collect()');
113
+ * const rows = await stmt.execute({ limit: 100 });
114
+ * // re-execute with different bind
115
+ * const rows2 = await stmt.execute({ limit: 50 });
116
+ * // release when done
117
+ * stmt.close();
118
+ * ```
119
+ */
120
+ export declare class Statement {
121
+ private readonly native;
122
+ private readonly db;
123
+ private closed;
124
+ constructor(native: NativeStatementHandle, db: unknown);
125
+ /** The fingerprint-prefixed unique id for this statement. */
126
+ get statementId(): string;
127
+ /** Parameter descriptors — empty array when the statement takes no parameters. */
128
+ get parameters(): ParameterDescriptor[];
129
+ /** Full statement metadata. */
130
+ get info(): StatementInfo;
131
+ /** Whether this statement has been closed and can no longer execute. */
132
+ get isClosed(): boolean;
133
+ /**
134
+ * Execute this prepared statement with optional named parameter bindings.
135
+ *
136
+ * Missing parameters, extraneous parameters or mistyped parameters raise
137
+ * `YydbError` with code `yydb.query.bind`.
138
+ *
139
+ * @param params Named parameter values — keys match the parameter names
140
+ * in the VOS source (`$name` → `{ name: value }`). Omit when the statement
141
+ * takes no parameters.
142
+ */
143
+ execute<T = Record<string, unknown>>(params?: Record<string, unknown>): Promise<T[]>;
144
+ /** Release this statement's plan from the native registry. */
145
+ close(): void;
146
+ }
147
+ /**
148
+ * Transaction-local handle passed to {@link Database.transaction} callbacks.
149
+ * All CRUD methods forward to the underlying native handle while the
150
+ * transaction is in flight. Commit/rollback are owned by {@link Database}.
151
+ */
152
+ export declare class TransactionHandle {
153
+ private readonly native;
154
+ constructor(native: ReturnType<ReturnType<typeof loadNative>['Database']['open']>);
155
+ get(key: string): Promise<Uint8Array | null>;
156
+ put(key: string, value: Uint8Array | string): Promise<void>;
157
+ delete(key: string): Promise<void>;
158
+ scan(options?: ScanOptions): Promise<ScanRow[]>;
159
+ batch(changes: readonly BatchChange[]): Promise<void>;
160
+ compareExchange(key: string, expected: Uint8Array | string | null, replacement: Uint8Array | string | null): Promise<boolean>;
161
+ query<T = Record<string, unknown>>(source: string): Promise<T[]>;
162
+ execute(source: string): Promise<void>;
163
+ }
164
+ /**
165
+ * Node-facing YYDB local database handle.
166
+ *
167
+ * Directly owns a native `yydb::Connection` via the Node-API binding. No
168
+ * child process, no loopback TCP, no wire protocol in the hot path.
17
169
  */
18
170
  export declare class Database {
19
171
  readonly path: string;
20
- readonly endpoint: string;
21
- private client;
22
- private child;
172
+ private readonly native;
23
173
  private readonly microRegistry;
24
174
  private constructor();
175
+ /** Open (create when missing) the database at `dbPath`. */
176
+ static open(dbPath: string, options?: OpenOptions): Promise<Database>;
177
+ /** Engine version string (same as `yydb --version`). */
178
+ serverVersion(): string;
179
+ /** Basic diagnostics: path, schema, record count, journal mode. */
180
+ info(): string;
181
+ /** Published schema metadata, or `null` before the first schema. */
182
+ getSchema(): Promise<{
183
+ version: number;
184
+ document: string;
185
+ } | null>;
25
186
  /**
26
- * Open (create) a `.yydb` file. Spawns the engine on `127.0.0.1` automatically.
187
+ * Validate and publish a VOS schema document.
188
+ *
189
+ * Accepts either a raw `.vos` source string, one `@voml/vos` descriptor
190
+ * (`v.table()`, `v.class()`) or a list of descriptors. When descriptors
191
+ * are supplied, they are rendered to source via `emitSource` first.
27
192
  */
28
- static open(dbPath: string, options?: OpenOptions): Promise<Database>;
29
- serverVersion(): Promise<string>;
30
- info(): Promise<string>;
31
- getSchema(): Promise<import("@yydb/yydb-client").SchemaVersion | null>;
32
- ensureSchema(version: number, document: string): Promise<void>;
193
+ ensureSchema(...input: [string] | [string, ...EmittableItem[]] | EmittableItem[]): Promise<void>;
194
+ /** Read one record, or `null` when the key is absent. */
33
195
  get(key: string): Promise<Uint8Array | null>;
196
+ /** Insert or replace one record. */
34
197
  put(key: string, value: Uint8Array | string): Promise<void>;
198
+ /** Delete one record by key. Silently succeeds when the key is absent. */
199
+ delete(key: string): Promise<void>;
200
+ /** List key/value pairs in lexical order. `prefix` defaults to `""` (all keys). */
201
+ scan(options?: ScanOptions): Promise<ScanRow[]>;
202
+ /** Atomically write or delete many keys in one call. */
203
+ batch(changes: readonly BatchChange[]): Promise<void>;
204
+ /**
205
+ * Compare-and-swap: replace or delete `key` only when its current value
206
+ * exactly equals `expected`. Both `expected` and `replacement` may be
207
+ * `null` (meaning absent / delete). Returns `true` when the swap applied.
208
+ */
209
+ compareExchange(key: string, expected: Uint8Array | string | null, replacement: Uint8Array | string | null): Promise<boolean>;
210
+ /** Fold the WAL into the main file and truncate the sidecar. */
211
+ checkpoint(): Promise<void>;
212
+ /** Checkpoint then copy a self-contained `.yydb` main file to `dest`. */
213
+ backup(dest: string): Promise<void>;
214
+ /** Copy main + WAL + SHM without checkpoint; opening `dest` replays the WAL tail. */
215
+ backupWithWal(dest: string): Promise<void>;
216
+ /** Run read-only consistency probes against the database. */
217
+ doctor(): Promise<DoctorReport>;
218
+ /** Fold WAL + reclaim orphan blobs (`.yydx` layouts only). */
219
+ compactYydx(): Promise<CompactReport>;
220
+ /**
221
+ * Run `fn` inside an explicit transaction. The transaction commits on
222
+ * normal return and rolls back on throw. Nested calls are not supported.
223
+ *
224
+ * ```ts
225
+ * await db.transaction(async (tx) => {
226
+ * await tx.put('a', '1');
227
+ * await tx.put('b', '2');
228
+ * });
229
+ * ```
230
+ */
231
+ transaction<T>(fn: (tx: TransactionHandle) => Promise<T>): Promise<T>;
232
+ /**
233
+ * Prepare a VOS read pipeline into a reusable query plan.
234
+ *
235
+ * Returns a {@link Statement} handle. The actual lowered op tree lives
236
+ * on the native side in a thread-local registry — you can call
237
+ * `statement.execute({ paramName: value })` cheaply multiple times
238
+ * without re-parsing or re-lowering the source.
239
+ *
240
+ * Prepare requires no schema binding; plan building is a pure parse +
241
+ * lower pass. Execute still needs `ensureSchema` first.
242
+ *
243
+ * ```ts
244
+ * const stmt = await db.prepare('User.all().take($limit).collect()');
245
+ * const rows = await stmt.execute({ limit: 100 });
246
+ * const rows2 = await stmt.execute({ limit: 50 });
247
+ * stmt.close();
248
+ * ```
249
+ */
250
+ prepare(source: string): Promise<Statement>;
251
+ /** Execute a VOS read pipeline and return typed rows. */
252
+ query<T = Record<string, unknown>>(source: string): Promise<T[]>;
253
+ /** Execute a VOS write program. */
254
+ execute(source: string): Promise<void>;
35
255
  /**
36
- * Build a typed session-local micro definition. Does not register it with
37
- * the engine until [`registerMicro`](./database.ts) is called.
256
+ * Publish a new schema version, migrating existing records when their
257
+ * shape no longer matches.
258
+ *
259
+ * Accepts raw `.vos` source or `@voml/vos` descriptors (same as
260
+ * {@link Database.ensureSchema}). Use `renames` to tell the engine which
261
+ * old type/field names map to the new ones — records that would otherwise
262
+ * look structurally unrelated get preserved.
38
263
  */
264
+ migrateSchema(documentOrSource: string | EmittableItem, ...rest: (EmittableItem | MigrateOptions)[]): Promise<void>;
265
+ /** Build a typed session-local micro definition (without registering). */
39
266
  defineMicro<TArgs extends readonly UdfScalarDescriptor[], TReturn extends UdfScalarDescriptor>(definition: Parameters<typeof buildMicro<TArgs, TReturn>>[0]): DefinedMicro<TArgs, TReturn>;
40
267
  /**
41
- * Install a [`defineMicro`](./database.ts) definition into the current
42
- * session registry and sync metadata to the private engine. Host micros are
43
- * session-local and never written to `.yydb`.
268
+ * Install a session-local host micro into both the local registry and the
269
+ * native engine. Host micros never persist across processes.
44
270
  */
45
271
  registerMicro<TArgs extends readonly UdfScalarDescriptor[], TReturn extends UdfScalarDescriptor>(definition: DefinedMicro<TArgs, TReturn>): Promise<MicroHandle>;
46
- /** Invoke a registered scalar UDF on the private engine. */
272
+ /** Invoke a registered scalar UDF on the native engine. */
47
273
  callScalar(name: string, version: number, args: readonly (string | number | boolean | null)[]): Promise<string | number | boolean | null>;
48
- /** Stop the private engine process. */
274
+ /** Release the native handle (best-effort; the engine also drops on GC). */
49
275
  close(): Promise<void>;
50
276
  }