@orkestrel/database 0.0.12 → 0.0.14

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.
@@ -6,22 +6,22 @@ let node_path = require("node:path");
6
6
  let _orkestrel_sqlite = require("@orkestrel/sqlite");
7
7
  //#region src/server/constants.ts
8
8
  /**
9
- * The declared {@link ColumnStorage}s whose SQL EQUALITY comparisons (`equals` /
9
+ * Lists the declared {@link ColumnStorage}s whose SQL equality comparisons (`equals` /
10
10
  * `not` / `any` / `none`) and `starts` / `ends` compiles are provably
11
11
  * engine-exact under declared-type trust — `text` / `integer` / `real` /
12
12
  * `boolean`; a `json` or `blob` column always refines instead.
13
13
  *
14
14
  * @remarks
15
- * This set governs equality and prefix/suffix matching only. RANGE
15
+ * This set governs equality and prefix/suffix matching only. Range
16
16
  * comparisons (`above` / `below` / `from` / `to` / `between`) and `ORDER BY`
17
- * are exact for `integer` / `real` / `boolean` but NOT for `text`: compiled
17
+ * are exact for `integer` / `real` / `boolean` but not for `text`: compiled
18
18
  * SQL orders/ranges under SQLite's default BINARY collation, which compares
19
- * TEXT byte-for-byte as UTF-8 — equivalent to Unicode CODE-POINT order —
19
+ * TEXT byte-for-byte as UTF-8 — equivalent to Unicode code-point order —
20
20
  * while the core engine's `compareValues` orders JS strings with `<`, which
21
- * compares UTF-16 CODE-UNIT order. The two orders diverge for supplementary-
22
- * plane characters (code points ≥ U+10000, e.g. many emoji): a lead surrogate
23
- * (`\uD800`–`\uDBFF`) sorts BELOW ``–`￿` in code-unit order, while
24
- * its code point sorts ABOVE them. So `matchesConditionExactly`'s range family and
21
+ * compares UTF-16 code-unit order. The two orders diverge for supplementary-
22
+ * plane characters (code points ≥ U+10000, for example many emoji): a lead surrogate
23
+ * (`\uD800`–`\uDBFF`) sorts below ``–`￿` in code-unit order, while
24
+ * its code point sorts above them. So `matchesConditionExactly`'s range family and
25
25
  * `matchesOrderExactly` exclude `text`, refining through the core engine instead.
26
26
  */
27
27
  var EXACT_COLUMN_STORAGE = Object.freeze([
@@ -31,7 +31,7 @@ var EXACT_COLUMN_STORAGE = Object.freeze([
31
31
  "boolean"
32
32
  ]);
33
33
  /**
34
- * The declared {@link ColumnStorage}s whose SQL RANGE comparisons
34
+ * Lists the declared {@link ColumnStorage}s whose SQL range comparisons
35
35
  * (`above` / `below` / `from` / `to` / `between`) and `ORDER BY` compiles are
36
36
  * provably engine-exact — `integer` / `real` / `boolean` only. `text` is
37
37
  * excluded: see {@link EXACT_COLUMN_STORAGE}'s remarks for the BINARY-collation
@@ -44,7 +44,7 @@ var EXACT_RANGE_COLUMN_STORAGE = Object.freeze([
44
44
  "boolean"
45
45
  ]);
46
46
  /**
47
- * The reserved metadata table the {@link SQLiteDriver} creates on `open` to
47
+ * Names the reserved metadata table the {@link SQLiteDriver} creates on `open` to
48
48
  * persist its stamped `DriverMetadata` (`version` + declared schema JSON) — the
49
49
  * SQLite realization of the `metadata` / `stamp` driver hooks.
50
50
  *
@@ -56,7 +56,7 @@ var METADATA_TABLE = "_metadata";
56
56
  //#endregion
57
57
  //#region src/server/helpers.ts
58
58
  /**
59
- * Whether a caught filesystem error reports that nothing is there to read.
59
+ * Reports whether a caught filesystem error says that nothing is there to read.
60
60
  *
61
61
  * @remarks
62
62
  * Two codes carry that meaning: `ENOENT` is a plain absence, and `ENOTDIR` is a
@@ -73,7 +73,7 @@ var METADATA_TABLE = "_metadata";
73
73
  * answers `false` rather than being read for a `code` any object could carry.
74
74
  *
75
75
  * @param error - The caught value to classify; any runtime is accepted
76
- * @returns `true` when the error reports that the path holds nothing
76
+ * @returns True if the error reports that the path holds nothing; false otherwise
77
77
  *
78
78
  * @example
79
79
  * ```ts
@@ -87,21 +87,21 @@ function matchesAbsentPath(error) {
87
87
  return error.code === "ENOENT" || error.code === "ENOTDIR";
88
88
  }
89
89
  /**
90
- * Whether a value's runtime type matches a column's declared exact type
91
- * the operand side of the declared-type-trust proof.
90
+ * Reports whether a value's runtime type matches a column's declared exact type
91
+ * the operand side of the declared-type-trust proof.
92
92
  *
93
93
  * @remarks
94
- * `text` ↔ string, `integer` / `real` ↔ FINITE number (`NaN` / `±Infinity`
94
+ * `text` ↔ string, `integer` / `real` ↔ finite number (`NaN` / `±Infinity`
95
95
  * fail), `boolean` ↔ boolean. Backs {@link matchesConditionExactly}'s operand checks.
96
96
  *
97
97
  * @param value - The condition operand to test
98
- * @param type - The column's declared portable type
99
- * @returns `true` when the operand's runtime type matches the declared type
98
+ * @param storage - The column's declared portable storage type
99
+ * @returns True if the operand's runtime type matches the declared type; false otherwise
100
100
  *
101
101
  * @example
102
102
  * ```ts
103
- * matchesDeclaredType('Ada', 'text') // true
104
- * matchesDeclaredType(Number.NaN, 'integer') // false — only finite numbers
103
+ * matchesDeclaredStorage('Ada', 'text') // true
104
+ * matchesDeclaredStorage(Number.NaN, 'integer') // false — only finite numbers
105
105
  * ```
106
106
  */
107
107
  function matchesDeclaredStorage(value, storage) {
@@ -110,9 +110,9 @@ function matchesDeclaredStorage(value, storage) {
110
110
  return (0, _orkestrel_contract.isFiniteNumber)(value);
111
111
  }
112
112
  /**
113
- * Whether one {@link Condition} compiles to SQL that is PROVABLY identical to
114
- * the core engine's `matchesCondition` for every value its column's declared
115
- * type can store.
113
+ * Reports whether one {@link Condition} compiles to SQL that is provably
114
+ * identical to the core engine's `matchesCondition` for every value its
115
+ * column's declared type can store.
116
116
  *
117
117
  * @remarks
118
118
  * `false` for a nested `FieldPath` (an array) or a column absent from `schema`.
@@ -124,32 +124,32 @@ function matchesDeclaredStorage(value, storage) {
124
124
  * Required non-null `equals` / `not` require an operand matching the declared
125
125
  * storage and exclude `json` / `blob`. `above` / `below` / `from` / `to` /
126
126
  * `between` are exact only for {@link EXACT_RANGE_COLUMN_STORAGE} (`integer` /
127
- * `real` / `boolean`) — a `text` column's range conditions REFINE, because
128
- * SQLite's default BINARY collation orders TEXT by Unicode CODE POINT while
129
- * the core engine's `compareValues` orders JS strings by UTF-16 CODE UNIT,
127
+ * `real` / `boolean`) — a `text` column's range conditions refine, because
128
+ * SQLite's default BINARY collation orders TEXT by Unicode code point while
129
+ * the core engine's `compareValues` orders JS strings by UTF-16 code unit,
130
130
  * and the two diverge for supplementary-plane characters (see
131
131
  * {@link EXACT_COLUMN_STORAGE}'s remarks for the full rationale).
132
- * `any` / `none` require a NON-EMPTY list where every element matches (an empty
132
+ * `any` / `none` require a non-empty list where every element matches (an empty
133
133
  * list is exact under neither: the engine's `any([])` matches nothing while
134
134
  * `none([])` matches everything, and SQL `IN ()` is a syntax error) — these
135
135
  * stay exact on `text` (byte equality is collation-independent and engine-
136
136
  * identical). `starts` / `ends` are exact only on a `text` column with a
137
137
  * string operand (case-sensitive `substr` compile, see {@link compileConditionSQL}) —
138
- * likewise collation-independent. `like` / `glob` are NEVER exact — SQLite
138
+ * likewise collation-independent. `like` / `glob` are never exact — SQLite
139
139
  * `LIKE` folds case ASCII-only against the engine's Unicode fold, and `GLOB`
140
140
  * has character classes the engine treats literally.
141
141
  *
142
142
  * @param condition - The condition to test
143
143
  * @param schema - The table's schema
144
- * @returns Whether `condition` is exact
144
+ * @returns True if `condition` is exact; false otherwise
145
145
  */
146
146
  function matchesConditionExactly(condition, schema) {
147
147
  if (!(0, _orkestrel_contract.isString)(condition.column)) return false;
148
- const column = schema.columns.find((candidate) => candidate.name === condition.column);
148
+ const column = (0, _src_core.findColumn)(condition.column, schema);
149
149
  if (column === void 0) return false;
150
150
  if (condition.operator === "absent" || condition.operator === "present") return !(column.optional && column.nullable);
151
151
  if (column.optional || column.nullable) return false;
152
- if (!EXACT_COLUMN_STORAGE.some((storage) => storage === column.storage)) return false;
152
+ if (!EXACT_COLUMN_STORAGE.includes(column.storage)) return false;
153
153
  const first = condition.values[0];
154
154
  const second = condition.values[1];
155
155
  switch (condition.operator) {
@@ -158,8 +158,8 @@ function matchesConditionExactly(condition, schema) {
158
158
  case "above":
159
159
  case "below":
160
160
  case "from":
161
- case "to": return EXACT_RANGE_COLUMN_STORAGE.some((storage) => storage === column.storage) && matchesDeclaredStorage(first, column.storage);
162
- case "between": return EXACT_RANGE_COLUMN_STORAGE.some((storage) => storage === column.storage) && matchesDeclaredStorage(first, column.storage) && matchesDeclaredStorage(second, column.storage);
161
+ case "to": return EXACT_RANGE_COLUMN_STORAGE.includes(column.storage) && matchesDeclaredStorage(first, column.storage);
162
+ case "between": return EXACT_RANGE_COLUMN_STORAGE.includes(column.storage) && matchesDeclaredStorage(first, column.storage) && matchesDeclaredStorage(second, column.storage);
163
163
  case "any":
164
164
  case "none": return condition.values.length > 0 && condition.values.every((value) => matchesDeclaredStorage(value, column.storage));
165
165
  case "starts":
@@ -169,36 +169,43 @@ function matchesConditionExactly(condition, schema) {
169
169
  }
170
170
  }
171
171
  /**
172
- * Whether one {@link Order} term's column compiles to an `ORDER BY` that
173
- * matches the engine's {@link import('@src/core').sortRows} exactly.
172
+ * Reports whether one {@link Order} term's column compiles to an `ORDER BY`
173
+ * that matches the engine's {@link import('@src/core').sortRows} exactly.
174
174
  *
175
175
  * @remarks
176
176
  * `false` for a nested `FieldPath`, a column absent from `schema`, or a
177
177
  * declared type outside {@link EXACT_RANGE_COLUMN_STORAGE} (`integer` / `real` /
178
- * `boolean`). `text` is NOT exact here: SQLite's default BINARY collation
178
+ * `boolean`). `text` is not exact here: SQLite's default BINARY collation
179
179
  * orders TEXT by Unicode code point while the core engine's `compareValues`
180
180
  * orders JS strings by UTF-16 code unit, and the two diverge for
181
181
  * supplementary-plane characters (see {@link EXACT_COLUMN_STORAGE}'s remarks) —
182
- * a `text` order term REFINES through the core engine instead.
182
+ * a `text` order term refines through the core engine instead. The column must
183
+ * also be required and non-null: an optional or nullable column refines, because
184
+ * SQL orders its `NULL`s ahead of every value while the core total order ranks
185
+ * `undefined` before `null` before every other value.
183
186
  *
184
187
  * @param order - The order term to test
185
188
  * @param schema - The table's schema
186
- * @returns Whether `order` is exact
189
+ * @returns True if `order` is exact; false otherwise
187
190
  */
188
191
  function matchesOrderExactly(order, schema) {
189
192
  if (!(0, _orkestrel_contract.isString)(order.column)) return false;
190
- const column = schema.columns.find((candidate) => candidate.name === order.column);
193
+ const column = (0, _src_core.findColumn)(order.column, schema);
191
194
  if (column === void 0) return false;
192
- return !column.optional && !column.nullable && EXACT_RANGE_COLUMN_STORAGE.some((storage) => storage === column.storage);
195
+ return !column.optional && !column.nullable && EXACT_RANGE_COLUMN_STORAGE.includes(column.storage);
193
196
  }
194
197
  /**
195
- * Whether a whole {@link QueryInput} is exact — every condition and every order
196
- * term is exact. `limit` / `offset` never affect exactness (SQL `LIMIT` /
197
- * `OFFSET` are always engine-identical).
198
+ * Reports whether a whole {@link QueryInput} is exact — every condition and
199
+ * every order term is exact. `limit` / `offset` never affect exactness (SQL
200
+ * `LIMIT` / `OFFSET` are always engine-identical).
201
+ *
202
+ * @remarks
203
+ * The gate {@link import('./drivers/SQLiteDriver.js').SQLiteDriver} checks before
204
+ * trusting a native SQL read over a full-scan refine through the core engine.
198
205
  *
199
206
  * @param input - The query input to test
200
207
  * @param schema - The table's schema
201
- * @returns Whether every part of `input` is exact
208
+ * @returns True if every part of `input` is exact; false otherwise
202
209
  */
203
210
  function matchesQueryExactly(input, schema) {
204
211
  const conditions = input.conditions ?? [];
@@ -206,25 +213,25 @@ function matchesQueryExactly(input, schema) {
206
213
  return conditions.every((condition) => matchesConditionExactly(condition, schema)) && order.every((term) => matchesOrderExactly(term, schema));
207
214
  }
208
215
  /**
209
- * Determine whether SQLite can execute an aggregate exactly like the core engine.
216
+ * Reports whether SQLite can execute an aggregate exactly like the core engine.
210
217
  *
211
218
  * @param operation - Aggregate operation
212
219
  * @param column - Aggregate field
213
220
  * @param schema - Current table schema
214
- * @returns Whether native aggregation is exact
221
+ * @returns True if native aggregation is exact; false otherwise
215
222
  */
216
223
  function matchesAggregateExactly(operation, column, schema) {
217
224
  if (operation === "count") return true;
218
225
  if (operation === "sum" || operation === "average" || !(0, _orkestrel_contract.isString)(column)) return false;
219
- const declared = schema.columns.find((candidate) => candidate.name === column);
226
+ const declared = (0, _src_core.findColumn)(column, schema);
220
227
  return declared !== void 0 && (declared.storage === "integer" || declared.storage === "real") && !(declared.optional && declared.nullable);
221
228
  }
222
229
  /**
223
- * Test a declared SQLite type against a portable storage affinity.
230
+ * Checks a declared SQLite type against a portable storage affinity.
224
231
  *
225
232
  * @param declared - Native declared type
226
233
  * @param storage - Portable column storage
227
- * @returns Whether SQLite's official affinity rules yield the expected affinity
234
+ * @returns True if SQLite's official affinity rules yield the expected affinity; false otherwise
228
235
  */
229
236
  function matchesSQLiteAffinity(declared, storage) {
230
237
  if (!(0, _orkestrel_contract.isString)(declared)) return false;
@@ -241,7 +248,7 @@ function matchesSQLiteAffinity(declared, storage) {
241
248
  return affinity === "REAL";
242
249
  }
243
250
  /**
244
- * Quote a SQL identifier (a table or column name) so any characters are literal.
251
+ * Quotes a SQL identifier (a table or column name) so any characters are literal.
245
252
  *
246
253
  * @remarks
247
254
  * Wraps the name in double quotes and doubles any embedded quote — the standard
@@ -260,7 +267,7 @@ function quoteIdentifier(identifier) {
260
267
  return "\"" + identifier.replaceAll("\"", "\"\"") + "\"";
261
268
  }
262
269
  /**
263
- * Encode a JS value to its stored {@link SQLiteValue} for a declared column.
270
+ * Encodes a JS value to its stored {@link SQLiteValue} for a declared column.
264
271
  *
265
272
  * @remarks
266
273
  * The codec is total: a malformed value encodes to SQL `NULL`. Absence always
@@ -299,7 +306,7 @@ function encodeValue(value, column) {
299
306
  }
300
307
  }
301
308
  /**
302
- * Decode a stored {@link SQLiteValue} back to its JS value for a declared column —
309
+ * Decodes a stored {@link SQLiteValue} back to its JS value for a declared column —
303
310
  * the exact inverse of {@link encodeValue}.
304
311
  *
305
312
  * @remarks
@@ -340,7 +347,7 @@ function decodeValue(value, column) {
340
347
  }
341
348
  }
342
349
  /**
343
- * Encode a whole {@link Row} to a {@link SQLiteRow} by its table's schema.
350
+ * Encodes a whole {@link Row} to a {@link SQLiteRow} by its table's schema.
344
351
  *
345
352
  * @remarks
346
353
  * Encodes each declared column's value with {@link encodeValue}; columns the row
@@ -362,7 +369,7 @@ function encodeRow(row, schema) {
362
369
  return result;
363
370
  }
364
371
  /**
365
- * Extract a stored row's values in a declared positional order.
372
+ * Extracts a stored row's values in a declared positional order.
366
373
  *
367
374
  * @remarks
368
375
  * SQLite statements bind arrays positionally. Every requested column must be
@@ -393,7 +400,7 @@ function extractValues(row, names, table) {
393
400
  return values;
394
401
  }
395
402
  /**
396
- * Decode a stored {@link SQLiteRow} back to a {@link Row} by its table's schema.
403
+ * Decodes a stored {@link SQLiteRow} back to a {@link Row} by its table's schema.
397
404
  *
398
405
  * @remarks
399
406
  * Decodes each declared column with {@link decodeValue} and **omits** any column
@@ -422,12 +429,12 @@ function decodeRow(row, schema) {
422
429
  return result;
423
430
  }
424
431
  /**
425
- * Build a collision-free SQL index name for a table + column-group index —
432
+ * Builds a collision-free SQL index name for a table + column-group index —
426
433
  * shared by the compiler module's `schemaToIndexes` and `stepToSQL`,
427
434
  * so a plan-built index name always matches one `open` would have created.
428
435
  *
429
436
  * @remarks
430
- * A naive `idx_<table>_<cols joined by _>` is AMBIGUOUS: table `'a_b'` with
437
+ * A naive `idx_<table>_<cols joined by _>` is ambiguous: table `'a_b'` with
431
438
  * column `'c'` and table `'a'` with columns `['b', 'c']` both produce
432
439
  * `idx_a_b_c`. This encodes each part (the table name, then each column name)
433
440
  * length-prefixed (`<len>_<part>`) so the boundary between parts is always
@@ -448,9 +455,39 @@ function deriveSQLiteIndexName(table, columns) {
448
455
  return "idx_" + [table, ...columns].map((part) => String(part.length) + "_" + part).join("_");
449
456
  }
450
457
  //#endregion
458
+ //#region src/server/inferers.ts
459
+ /**
460
+ * Reads the storage type a nested (`json_extract`) operand encodes as from its
461
+ * runtime value, never as `json`.
462
+ *
463
+ * @remarks
464
+ * `json_extract` returns the unquoted, natively-typed scalar (a JSON boolean as
465
+ * `1` / `0`, a number as-is, a string as-is), so the operand must encode to that
466
+ * same scalar to compare. A boolean → `'boolean'` (→ `1` / `0`); a number →
467
+ * `'integer'` / `'real'`; a bigint → `'integer'`; a string → `'text'`; `null` /
468
+ * `undefined` → `'text'` (encodes to `null`); an object / array → `'json'` (the
469
+ * edge of comparing against a json subtree).
470
+ *
471
+ * @param value - The runtime operand value
472
+ * @returns The {@link ColumnStorage} to encode it as
473
+ *
474
+ * @example
475
+ * ```ts
476
+ * inferValueStorage(true) // 'boolean'
477
+ * inferValueStorage(9) // 'integer'
478
+ * ```
479
+ */
480
+ function inferValueStorage(value) {
481
+ if (typeof value === "boolean") return "boolean";
482
+ if (typeof value === "number") return Number.isInteger(value) ? "integer" : "real";
483
+ if (typeof value === "bigint") return "integer";
484
+ if (typeof value === "object" && value !== null) return "json";
485
+ return "text";
486
+ }
487
+ //#endregion
451
488
  //#region src/server/compilers.ts
452
489
  /**
453
- * Map a portable {@link ColumnStorage} to its SQLite column type.
490
+ * Maps a portable {@link ColumnStorage} to its SQLite column type.
454
491
  *
455
492
  * @param storage - The portable column type
456
493
  * @returns The SQLite column type keyword
@@ -466,7 +503,11 @@ function compileColumnSQL(storage) {
466
503
  }
467
504
  }
468
505
  /**
469
- * Compile a {@link FieldPath} to the SQL expression that reads it.
506
+ * Compiles a {@link FieldPath} to the SQL expression that reads it.
507
+ *
508
+ * @remarks
509
+ * A flat path compiles to the quoted column; a nested path compiles to a
510
+ * `json_extract` over the head column with the rest of the path as its accessor.
470
511
  *
471
512
  * @param path - The field path
472
513
  * @returns The SQL expression selecting the value
@@ -479,7 +520,7 @@ function compileFieldSQL(path) {
479
520
  return "json_extract(" + quoteIdentifier(column) + ", '$" + rest + "')";
480
521
  }
481
522
  /**
482
- * Compile an {@link AggregateOperation} over a {@link FieldPath}.
523
+ * Compiles an {@link AggregateOperation} over a {@link FieldPath}.
483
524
  *
484
525
  * @param operation - The aggregate to compute
485
526
  * @param column - The column or nested path to aggregate
@@ -495,9 +536,9 @@ function compileAggregateSQL(operation, column) {
495
536
  }
496
537
  }
497
538
  /**
498
- * Compile a NESTED {@link FieldPath} to the `json_type(<col>, <path>)` SQL
539
+ * Compiles a nested {@link FieldPath} to the `json_type(<col>, <path>)` SQL
499
540
  * expression — the {@link compileFieldSQL} `json_extract` sibling used to tell a
500
- * PRESENT JSON `null` apart from an ABSENT path (both read back as SQL `NULL`
541
+ * present JSON `null` apart from an absent path (both read back as SQL `NULL`
501
542
  * through `json_extract`, but `json_type` reports `'null'` for the former and
502
543
  * SQL `NULL` for the latter).
503
544
  *
@@ -516,83 +557,25 @@ function compileJSONTypeSQL(path) {
516
557
  return "json_type(" + quoteIdentifier(column) + ", '$" + rest + "')";
517
558
  }
518
559
  /**
519
- * Escape `\`, `%`, and `_` (each with a leading `\`) so a `starts` / `ends`
520
- * operand is matched literally under the `LIKE … ESCAPE '\'` clause.
521
- *
522
- * @param text - The raw operand text
523
- * @returns The text with LIKE metacharacters escaped
524
- *
525
- * @example
526
- * ```ts
527
- * escapeLike('50%_off') // '50\\%\\_off'
528
- * ```
529
- */
530
- function escapeLike(text) {
531
- return text.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
532
- }
533
- /**
534
- * The declared storage type of a flat (string) column, read from the schema.
535
- *
536
- * @param column - The column name
537
- * @param schema - The table's schema
538
- * @returns The column's {@link ColumnStorage}, or `undefined` if the schema does not carry it
539
- *
540
- * @example
541
- * ```ts
542
- * findColumnStorage('age', schema) // 'integer'
543
- * ```
544
- */
545
- function findColumnStorage(column, schema) {
546
- return schema.columns.find((candidate) => candidate.name === column)?.storage;
547
- }
548
- /**
549
- * The storage type a nested (`json_extract`) operand encodes as, derived from its
550
- * RUNTIME value — NOT `json`.
551
- *
552
- * @remarks
553
- * `json_extract` returns the unquoted, natively-typed scalar (a JSON boolean as
554
- * `1` / `0`, a number as-is, a string as-is), so the operand must encode to that
555
- * same scalar to compare. A boolean → `'boolean'` (→ `1` / `0`); a number →
556
- * `'integer'` / `'real'`; a bigint → `'integer'`; a string → `'text'`; `null` /
557
- * `undefined` → `'text'` (encodes to `null`); an object / array → `'json'` (the
558
- * edge of comparing against a json subtree).
559
- *
560
- * @param value - The runtime operand value
561
- * @returns The {@link ColumnStorage} to encode it as
562
- *
563
- * @example
564
- * ```ts
565
- * inferValueStorage(true) // 'boolean'
566
- * inferValueStorage(9) // 'integer'
567
- * ```
568
- */
569
- function inferValueStorage(value) {
570
- if (typeof value === "boolean") return "boolean";
571
- if (typeof value === "number") return Number.isInteger(value) ? "integer" : "real";
572
- if (typeof value === "bigint") return "integer";
573
- if (typeof value === "object" && value !== null) return "json";
574
- return "text";
575
- }
576
- /**
577
- * Compile one condition to its `<column> <operator>` SQL fragment and the parameters
560
+ * Compiles one condition to its `<column> <operator>` SQL fragment and the parameters
578
561
  * it binds — engine-exact under SQL's three-valued NULL logic.
579
562
  *
580
563
  * @remarks
581
564
  * Every operand is run through `encodeValue`, so a bound value matches the SQL
582
- * the column side compiles to. A flat column encodes operands with its DECLARED
565
+ * the column side compiles to. A flat column encodes operands with its declared
583
566
  * schema type (a flat `json` column → `JSON.stringify`); a nested `FieldPath`
584
- * encodes each operand as the NATIVE scalar `json_extract` returns, derived from
567
+ * encodes each operand as the native scalar `json_extract` returns, derived from
585
568
  * the operand's runtime type (per-operand, since `between` / `any` / `none` can
586
569
  * mix types). `any` / `none` collapse an empty list to a constant (`0` matches
587
570
  * nothing, `1` matches all) with no parameters.
588
571
  *
589
- * The core engine's total order ranks `undefined` (rank 0) BELOW `null`
590
- * (rank 1) (see `compareValues`), so a MISSING/`NULL` column MATCHES
572
+ * The core engine's total order ranks `undefined` (rank 0) below `null`
573
+ * (rank 1) (see `compareValues`), so a missing/`NULL` column matches
591
574
  * `below` / `to` / a scalar `not` / `none` — the opposite of raw SQL, where a
592
575
  * comparison against `NULL` is `NULL` (excluded). This fragment replicates the
593
576
  * engine exactly. Truth table (`value` = the engine's decoded field read; a
594
- * FLAT column's stored `NULL` decodes to `undefined` per `decodeRow`, so a
595
- * flat `value` is NEVER a present `null` — only a NESTED path can be
577
+ * flat column's stored `NULL` decodes to `undefined` per `decodeRow`, so a
578
+ * flat `value` is never a present `null` — only a nested path can be
596
579
  * present-but-`null`):
597
580
  *
598
581
  * ```text
@@ -618,15 +601,15 @@ function inferValueStorage(value) {
618
601
  * refines every optional or nullable scalar comparison through the core engine.
619
602
  * This compiler still emits a total SQL fragment for direct consumers.
620
603
  *
621
- * A NESTED path can be present-but-`null` (a stored JSON `null`), which
622
- * `json_extract` reads back as SQL `NULL` — indistinguishable from an ABSENT
604
+ * A nested path can be present-but-`null` (a stored JSON `null`), which
605
+ * `json_extract` reads back as SQL `NULL` — indistinguishable from an absent
623
606
  * path. `json_type(col, path)` disambiguates them (`'null'` for present-null,
624
607
  * SQL `NULL` for absent), so nested `equals` / `not` against a `null` operand
625
608
  * compile through `json_type` instead of `IS NULL` / `IS NOT NULL`.
626
609
  *
627
610
  * Every other MATCH-on-null-or-absent row is expressed uniformly (flat and
628
611
  * nested alike) as `(<column> <op> ? OR <column> IS NULL)` — for a nested
629
- * path, `json_extract` already collapses BOTH absent and present-null to SQL
612
+ * path, `json_extract` already collapses both absent and present-null to SQL
630
613
  * `NULL`, so `IS NULL` catches both in one clause; for a flat column there is
631
614
  * only the absent case to catch.
632
615
  *
@@ -645,7 +628,7 @@ function inferValueStorage(value) {
645
628
  function compileConditionSQL(condition, schema) {
646
629
  const column = compileFieldSQL(condition.column);
647
630
  const nested = !(0, _orkestrel_contract.isString)(condition.column);
648
- const declared = (0, _orkestrel_contract.isString)(condition.column) ? schema.columns.find((candidate) => candidate.name === condition.column) : void 0;
631
+ const declared = (0, _orkestrel_contract.isString)(condition.column) ? (0, _src_core.findColumn)(condition.column, schema) : void 0;
649
632
  const first = condition.values[0];
650
633
  const second = condition.values[1];
651
634
  const nullOperand = first === null || first === void 0;
@@ -764,13 +747,13 @@ function compileConditionSQL(condition, schema) {
764
747
  };
765
748
  }
766
749
  /**
767
- * Fold the conditions into one WHERE clause, parenthesizing progressively
750
+ * Folds the conditions into one WHERE clause, parenthesizing progressively
768
751
  * left-to-right so the grouping matches the engine's `matchesQuery` fold.
769
752
  *
770
753
  * @remarks
771
754
  * The first condition's connector is ignored, per the {@link Condition} types.
772
755
  * Every fragment (see {@link compileConditionSQL}'s truth table) replicates the core
773
- * engine's total order EXACTLY under SQL's three-valued NULL logic, so this
756
+ * engine's total order exactly under SQL's three-valued NULL logic, so this
774
757
  * clause matches `applyQuery` row-for-row over the same table — a native
775
758
  * `records` / `count` read never disagrees with a scan-and-filter fallback.
776
759
  *
@@ -780,11 +763,11 @@ function compileConditionSQL(condition, schema) {
780
763
  *
781
764
  * @example
782
765
  * ```ts
783
- * compileWhere([{ column: 'age', operator: 'from', values: [18], connector: 'and' }], schema)
766
+ * compileWhereSQL([{ column: 'age', operator: 'from', values: [18], connector: 'and' }], schema)
784
767
  * // { sql: 'WHERE "age" >= ?', parameters: [18] }
785
768
  * ```
786
769
  */
787
- function compileWhere(conditions, schema) {
770
+ function compileWhereSQL(conditions, schema) {
788
771
  const [first, ...remaining] = conditions;
789
772
  if (first === void 0) return {
790
773
  sql: "",
@@ -805,14 +788,14 @@ function compileWhere(conditions, schema) {
805
788
  };
806
789
  }
807
790
  /**
808
- * Compile the ORDER BY clause from the order terms, always ending with the
791
+ * Compiles the ORDER BY clause from the order terms, always ending with the
809
792
  * primary key as the final determinant.
810
793
  *
811
794
  * @remarks
812
795
  * The native `records` read then resolves ties in key order, matching a
813
796
  * primary-key-ordered `scan` and the core engine's stable `sortRows` over a
814
797
  * key-ordered scan (and IndexedDB's key-ordered reads), so a native read equals
815
- * the scan path (AGENTS §21 / §22 native ↔ engine parity). SQLite without an
798
+ * the scan path native ↔ engine parity. SQLite without an
816
799
  * `ORDER BY` returns rowid (insertion) order, and an explicit order alone breaks
817
800
  * ties by rowid too — both diverge from every key-ordered backend. The
818
801
  * tie-breaker is ASCENDING regardless of the explicit directions: the engine's
@@ -826,17 +809,17 @@ function compileWhere(conditions, schema) {
826
809
  *
827
810
  * @example
828
811
  * ```ts
829
- * compileOrder([{ column: 'age', direction: 'descending' }], schema)
812
+ * compileOrderSQL([{ column: 'age', direction: 'descending' }], schema)
830
813
  * // 'ORDER BY "age" DESC, "id"'
831
814
  * ```
832
815
  */
833
- function compileOrder(order, schema) {
816
+ function compileOrderSQL(order, schema) {
834
817
  const terms = (order ?? []).map((term) => compileFieldSQL(term.column) + (term.direction === "descending" ? " DESC" : " ASC"));
835
818
  if (!(order ?? []).some((term) => (0, _orkestrel_contract.isString)(term.column) && term.column === schema.primary)) terms.push(quoteIdentifier(schema.primary));
836
819
  return terms.length === 0 ? "" : "ORDER BY " + terms.join(", ");
837
820
  }
838
821
  /**
839
- * Compile the LIMIT / OFFSET clause.
822
+ * Compiles the LIMIT / OFFSET clause.
840
823
  *
841
824
  * @remarks
842
825
  * An offset without a limit uses `LIMIT -1` (SQLite's "no limit") so OFFSET is
@@ -848,10 +831,10 @@ function compileOrder(order, schema) {
848
831
  *
849
832
  * @example
850
833
  * ```ts
851
- * compilePage(undefined, 5) // { sql: 'LIMIT -1 OFFSET ?', parameters: [5] }
834
+ * compilePageSQL(undefined, 5) // { sql: 'LIMIT -1 OFFSET ?', parameters: [5] }
852
835
  * ```
853
836
  */
854
- function compilePage(limit, offset) {
837
+ function compilePageSQL(limit, offset) {
855
838
  (0, _src_core.validatePage)({
856
839
  ...limit === void 0 ? {} : { limit },
857
840
  ...offset === void 0 ? {} : { offset }
@@ -874,7 +857,7 @@ function compilePage(limit, offset) {
874
857
  };
875
858
  }
876
859
  /**
877
- * Compile a {@link QueryInput} into the SQL clause that follows a table name, with
860
+ * Compiles a {@link QueryInput} into the SQL clause that follows a table name, with
878
861
  * its bound parameters in clause order.
879
862
  *
880
863
  * @remarks
@@ -884,11 +867,13 @@ function compilePage(limit, offset) {
884
867
  * over a JS `scan`. The WHERE fold is parenthesized **left-to-right** to mirror
885
868
  * the core engine's `matchesQuery` (not SQL's native AND-over-OR precedence),
886
869
  * so a native and an engine read return identical rows. Each operand is encoded
887
- * via `encodeValue`: a flat column uses its declared schema type, while a nested
870
+ * through `encodeValue`: a flat column uses its declared schema type, while a nested
888
871
  * `FieldPath` (a `json_extract` read) encodes each operand as the native scalar
889
872
  * the extract returns — derived from the operand's runtime type — so it compares.
890
- * The 15 operators map per the databases guide's operator table, with
891
- * `starts` / `ends` using `LIKE ESCAPE '\'` and an empty `any` / `none` list
873
+ * Every operator maps per the databases guide's operator table, with
874
+ * `starts` / `ends` compiling to a code-point `substr` slice guarded by
875
+ * `typeof(<column>) = 'text'` (case-sensitive, matching the engine's
876
+ * `String.prototype.startsWith` / `endsWith`) and an empty `any` / `none` list
892
877
  * collapsing to a constant. An `undefined` input (or one with no parts)
893
878
  * compiles to an empty clause.
894
879
  *
@@ -904,9 +889,9 @@ function compilePage(limit, offset) {
904
889
  */
905
890
  function compileQuerySQL(input, schema) {
906
891
  (0, _src_core.validatePage)(input);
907
- const where = compileWhere(input?.conditions ?? [], schema);
908
- const orderBy = compileOrder(input?.order, schema);
909
- const page = compilePage(input?.limit, input?.offset);
892
+ const where = compileWhereSQL(input?.conditions ?? [], schema);
893
+ const orderBy = compileOrderSQL(input?.order, schema);
894
+ const page = compilePageSQL(input?.limit, input?.offset);
910
895
  return {
911
896
  sql: [
912
897
  where.sql,
@@ -917,7 +902,7 @@ function compileQuerySQL(input, schema) {
917
902
  };
918
903
  }
919
904
  /**
920
- * Project a {@link TableSchema} to its `CREATE TABLE IF NOT EXISTS` statement.
905
+ * Projects a {@link TableSchema} to its `CREATE TABLE IF NOT EXISTS` statement.
921
906
  *
922
907
  * @param schema - The table schema
923
908
  * @returns The complete table declaration
@@ -927,7 +912,11 @@ function schemaToTable(schema) {
927
912
  return "CREATE TABLE IF NOT EXISTS " + quoteIdentifier(schema.name) + " (" + columns.join(", ") + ", PRIMARY KEY (" + quoteIdentifier(schema.primary) + "))";
928
913
  }
929
914
  /**
930
- * Project a {@link TableSchema} to its declared SQLite indexes.
915
+ * Projects a {@link TableSchema} to its declared SQLite indexes.
916
+ *
917
+ * @remarks
918
+ * Each statement is a `CREATE INDEX IF NOT EXISTS` named by
919
+ * {@link deriveSQLiteIndexName}, so a reopen re-issues the set safely.
931
920
  *
932
921
  * @param schema - The table schema
933
922
  * @returns One statement per declared index
@@ -936,7 +925,11 @@ function schemaToIndexes(schema) {
936
925
  return schema.indexes.map((group) => "CREATE INDEX IF NOT EXISTS " + quoteIdentifier(deriveSQLiteIndexName(schema.name, group)) + " ON " + quoteIdentifier(schema.name) + " (" + group.map(quoteIdentifier).join(", ") + ")");
937
926
  }
938
927
  /**
939
- * Project one {@link MigrationStep} to SQLite DDL.
928
+ * Projects one {@link MigrationStep} to SQLite DDL.
929
+ *
930
+ * @remarks
931
+ * These are the statements the SQLite driver's `migrate` executes for the step,
932
+ * inside whichever native transaction is active.
940
933
  *
941
934
  * @param step - The migration step
942
935
  * @returns The statements that apply the step
@@ -952,97 +945,9 @@ function stepToSQL(step) {
952
945
  }
953
946
  }
954
947
  //#endregion
955
- //#region src/core/DriverIterator.ts
956
- /**
957
- * The internal continuation boundary for a root driver async iterator.
958
- *
959
- * @remarks
960
- * A driver transaction can begin while a caller holds an idle root iterator.
961
- * Every `next` therefore checks the driver's root-state guard immediately
962
- * before and after advancing the source. A failed continuation terminalizes the
963
- * iterator, discards any row produced before the post-advance guard failed, and
964
- * attempts source cleanup exactly once.
965
- */
966
- var DriverIterator = class {
967
- #source;
968
- #guard;
969
- #terminal = false;
970
- #cleaned = false;
971
- constructor(source, guard) {
972
- this.#source = source;
973
- this.#guard = guard;
974
- }
975
- [Symbol.asyncIterator]() {
976
- return this;
977
- }
978
- async next() {
979
- if (this.#terminal) return {
980
- done: true,
981
- value: void 0
982
- };
983
- try {
984
- this.#guard();
985
- const result = await this.#source.next();
986
- this.#guard();
987
- if (result.done === true) {
988
- this.#terminal = true;
989
- this.#cleaned = true;
990
- }
991
- return result;
992
- } catch (error) {
993
- this.#terminal = true;
994
- await this.#discard();
995
- throw error;
996
- }
997
- }
998
- async return() {
999
- if (this.#terminal) return {
1000
- done: true,
1001
- value: void 0
1002
- };
1003
- this.#terminal = true;
1004
- if (this.#cleaned || this.#source.return === void 0) {
1005
- this.#cleaned = true;
1006
- return {
1007
- done: true,
1008
- value: void 0
1009
- };
1010
- }
1011
- this.#cleaned = true;
1012
- return this.#source.return();
1013
- }
1014
- async throw(error) {
1015
- if (this.#terminal) throw error;
1016
- if (this.#source.throw === void 0) {
1017
- this.#terminal = true;
1018
- await this.#discard();
1019
- throw error;
1020
- }
1021
- try {
1022
- const result = await this.#source.throw(error);
1023
- if (result.done === true) {
1024
- this.#terminal = true;
1025
- this.#cleaned = true;
1026
- }
1027
- return result;
1028
- } catch (cause) {
1029
- this.#terminal = true;
1030
- await this.#discard();
1031
- throw cause;
1032
- }
1033
- }
1034
- async #discard() {
1035
- if (this.#cleaned) return;
1036
- this.#cleaned = true;
1037
- try {
1038
- await this.#source.return?.();
1039
- } catch {}
1040
- }
1041
- };
1042
- //#endregion
1043
948
  //#region src/server/drivers/JSONDriver.ts
1044
949
  /**
1045
- * A persistent {@link DriverInterface} backed by a single JSON file — the
950
+ * Implements a persistent {@link DriverInterface} backed by a single JSON file — the
1046
951
  * reference {@link MemoryDriver} plus file load / flush.
1047
952
  *
1048
953
  * @remarks
@@ -1056,15 +961,15 @@ var DriverIterator = class {
1056
961
  * primary (the table contract), so the key is recovered on load with
1057
962
  * {@link extractKey} and the file need not store it. The parsed JSON crosses the
1058
963
  * boundary as `unknown` and is narrowed with {@link isRecord} / {@link extractKey},
1059
- * never asserted (AGENTS §14). A read that reports no document there starts empty —
964
+ * never asserted. A read that reports no document there starts empty —
1060
965
  * `ENOENT` for a plain absence, and `ENOTDIR` for a path whose parent is not a
1061
966
  * directory, which no later write could find either; every other read failure or
1062
967
  * invalid existing document fails closed without publication, mutation, or
1063
- * automatic repair. It is scan-only it implements none of
1064
- * the optional native `records` / `aggregate` hooks, so the core engine
1065
- * over `scan` answers every query. For development, small datasets, and portable /
1066
- * inspectable data; for large or concurrent workloads reach for a SQLite-backed
1067
- * driver.
968
+ * automatic repair. It implements the optional native `stream` hook that
969
+ * `TableInterface.scan` prefers over `scan`, and neither `records` nor
970
+ * `aggregate`, so the core engine's `matchesQuery` answers every query on
971
+ * either path. For development, small datasets, and portable / inspectable
972
+ * data; for large or concurrent workloads reach for a SQLite-backed driver.
1068
973
  *
1069
974
  * Metadata crosses {@link cloneDriverMetadata} at parsed-file ingress, public and
1070
975
  * scoped write ingress, candidate/root publication, serialization, and copy-out.
@@ -1125,10 +1030,10 @@ var JSONDriver = class {
1125
1030
  return this.#memory.keys(table);
1126
1031
  }
1127
1032
  scan(table) {
1128
- return new DriverIterator(this.#scan(table)[Symbol.asyncIterator](), () => this.#root());
1033
+ return new _src_core.DriverIterator(this.#scan(table)[Symbol.asyncIterator](), () => this.#root());
1129
1034
  }
1130
1035
  /**
1131
- * Natively filtered lazy iteration — delegates to the inner {@link MemoryDriver}.
1036
+ * Iterates rows lazily with native filtering — delegates to the inner {@link MemoryDriver}.
1132
1037
  *
1133
1038
  * @remarks
1134
1039
  * Semantics are the memory driver's own: `input.conditions` filters, `offset`
@@ -1140,14 +1045,14 @@ var JSONDriver = class {
1140
1045
  */
1141
1046
  stream(table, input) {
1142
1047
  (0, _src_core.validatePage)(input);
1143
- return new DriverIterator(this.#stream(table, input)[Symbol.asyncIterator](), () => this.#root());
1048
+ return new _src_core.DriverIterator(this.#stream(table, input)[Symbol.asyncIterator](), () => this.#root());
1144
1049
  }
1145
1050
  async clear(table) {
1146
1051
  this.#root();
1147
1052
  await this.#enqueue(() => this.#clear(table));
1148
1053
  }
1149
1054
  /**
1150
- * Run an isolated native transaction callback over a candidate memory store.
1055
+ * Runs an isolated native transaction callback over a candidate memory store.
1151
1056
  *
1152
1057
  * @remarks
1153
1058
  * Single-writer: nesting and root operations while active throw `CONFLICT`.
@@ -1164,7 +1069,7 @@ var JSONDriver = class {
1164
1069
  return this.#enqueue(() => this.#transact(scope));
1165
1070
  }
1166
1071
  /**
1167
- * Capture an owned row snapshot at an exact writer-queue position.
1072
+ * Captures an owned row snapshot at an exact writer-queue position.
1168
1073
  *
1169
1074
  * @remarks
1170
1075
  * Capture owns table names, schemas, rows, and one session-local identity per
@@ -1192,7 +1097,7 @@ var JSONDriver = class {
1192
1097
  return this.#metadata === void 0 ? void 0 : (0, _src_core.cloneDriverMetadata)(this.#metadata);
1193
1098
  }
1194
1099
  /**
1195
- * Persist an owned metadata snapshot for a later `metadata()` to copy out.
1100
+ * Persists an owned metadata snapshot for a later `metadata()` to copy out.
1196
1101
  *
1197
1102
  * @remarks
1198
1103
  * Root stamping conflicts while a transaction is active. The scoped
@@ -1207,7 +1112,7 @@ var JSONDriver = class {
1207
1112
  await this.#enqueue(() => this.#stamp(owned));
1208
1113
  }
1209
1114
  /**
1210
- * Apply one atomic {@link MigrationInput} through an isolated candidate.
1115
+ * Applies one atomic {@link MigrationInput} through an isolated candidate.
1211
1116
  *
1212
1117
  * @remarks
1213
1118
  * The candidate receives every plan step plus optional metadata. Its complete
@@ -1421,7 +1326,8 @@ var JSONDriver = class {
1421
1326
  return this.#requireCandidate(token).keys(table);
1422
1327
  }
1423
1328
  #scanCandidate(token, table) {
1424
- return new DriverIterator(this.#requireCandidate(token).scan(table)[Symbol.asyncIterator](), () => {
1329
+ const source = this.#requireCandidate(token).scan(table);
1330
+ return new _src_core.DriverIterator(source[Symbol.asyncIterator](), () => {
1425
1331
  this.#requireCandidate(token);
1426
1332
  });
1427
1333
  }
@@ -1668,8 +1574,8 @@ var JSONDriver = class {
1668
1574
  //#endregion
1669
1575
  //#region src/server/drivers/SQLiteDriver.ts
1670
1576
  /**
1671
- * The SQLite {@link DriverInterface} — the server-native, trusted-mode backend
1672
- * built on the published `@orkestrel/sqlite` synchronous wrapper.
1577
+ * Implements the {@link DriverInterface} over SQLite — the server-native, trusted-mode
1578
+ * backend built on the published `@orkestrel/sqlite` synchronous wrapper.
1673
1579
  *
1674
1580
  * @remarks
1675
1581
  * A thin adapter: it implements the storage primitives the core database layer
@@ -1680,41 +1586,40 @@ var JSONDriver = class {
1680
1586
  * reopen-safe), and readies a reserved `_metadata` single-row table `metadata()` /
1681
1587
  * `stamp()` read and write — **a user table named `_metadata` collides with it**;
1682
1588
  * avoid the name. Rows cross the boundary through the codecs in `helpers.ts`
1683
- * (`json` columns store / parse JSON text, a `boolean` stores `1` / `0`), so the
1684
- * typed layer above imposes the exact shape (AGENTS §14). `write` is an
1589
+ * (`json` columns store / parse JSON text, a `boolean` stores `1` / `0`), so
1590
+ * the typed layer above imposes the exact shape. `write` is an
1685
1591
  * `INSERT OR REPLACE` upsert, while `insert` uses a plain `INSERT` and maps its
1686
1592
  * atomic primary-key constraint failure to `CONFLICT`; every other backend
1687
1593
  * `SQLiteError` is contained by the same `DatabaseError` boundary described
1688
- * below. Querying, ordering, paging, and
1689
- * aggregation is native: `records` / `stream` compile a `QueryInput`
1690
- * to SQL with `compileQuerySQL`, and `aggregate` runs a SQL
1691
- * `COUNT`/`SUM`/`AVG`/`MIN`/`MAX` (via `compileAggregateSQL`) over the same compiled
1692
- * WHERE. `transaction` runs a callback inside native `BEGIN` / `COMMIT` /
1693
- * `ROLLBACK`, passing a scoped storage capability that becomes invalid after
1694
- * settlement. `migrate` runs the plan's projected DDL
1695
- * ({@link import('../compilers.js').stepToSQL}) inside whichever native
1696
- * transaction is active: joined into the active transaction callback
1697
- * when one exists (the core's versioned reconcile path wraps migrate + stamp
1698
- * in one native `BEGIN`, and node:sqlite rejects a nested `BEGIN`), or inside
1699
- * its own `database.transaction` otherwise a mid-plan failure rolls back
1700
- * atomically either way, an improvement over the non-atomic `MemoryDriver` /
1701
- * `JSONDriver` migrate; a step referencing an undeclared table throws
1594
+ * below. Querying, ordering, paging, and aggregation is native: `records` /
1595
+ * `stream` compile a `QueryInput` to SQL with `compileQuerySQL`, and
1596
+ * `aggregate` runs a SQL `COUNT`/`SUM`/`AVG`/`MIN`/`MAX` (through
1597
+ * `compileAggregateSQL`) over the same compiled WHERE. `transaction` runs a
1598
+ * callback inside native `BEGIN` / `COMMIT` / `ROLLBACK`, passing a scoped
1599
+ * storage capability that becomes invalid after settlement. `migrate` runs the
1600
+ * plan's projected DDL ({@link import('../compilers.js').stepToSQL}) inside
1601
+ * whichever native transaction is active: joined into the active transaction
1602
+ * callback when one exists (the core's versioned reconcile path wraps migrate +
1603
+ * stamp in one native `BEGIN`, and node:sqlite rejects a nested `BEGIN`), or
1604
+ * inside its own `database.transact` otherwise a mid-plan failure rolls
1605
+ * back atomically either way, an improvement over the non-atomic `MemoryDriver`
1606
+ * / `JSONDriver` migrate; a step referencing an undeclared table throws
1702
1607
  * `DatabaseError` `MIGRATION` before any DDL for that step runs. `snapshot` is
1703
- * capture-replay (SELECT the
1704
- * named tables' rows, replay via DELETE + INSERT OR REPLACE inside a native
1705
- * transaction on rollback) rather than a SQL `SAVEPOINT`, since the core
1706
- * `transaction` calls the rollback thunk only on failure with no commit-on-
1707
- * success signal a long-lived `SAVEPOINT` would leave the connection
1708
- * uncommitted (lost on close). Every backend interaction runs through `#guard`,
1709
- * which maps a thrown backend `SQLiteError` (or any unexpected non-`SQLiteError`
1710
- * throw) to a typed {@link DatabaseError} never a raw backend error escapes
1711
- * `DriverInterface`: `CONSTRAINT` → `CONFLICT`, the wrapper's own `CLOSED`
1712
- * `CLOSED`, `BUSY` (a locked database that outlasted the configured `timeout`)
1713
- * → a retryable `DRIVER` (`context.retryable` is `true`), and `UNKNOWN` / any
1714
- * other throw `DRIVER`. The original error is preserved as `context.cause`.
1715
- * A `DatabaseError` this driver throws directly (`CLOSED` from the `#require`
1716
- * gate, `NOT_FOUND` from `#table`, `MIGRATION` from a migration-plan fault)
1717
- * passes through `#guard` unchanged, never re-wrapped.
1608
+ * capture-replay (SELECT the named tables' rows, replay through DELETE + INSERT OR
1609
+ * REPLACE inside a native transaction on rollback) rather than a SQL
1610
+ * `SAVEPOINT`, since the core `transaction` calls the rollback thunk only on
1611
+ * failure with no commit-on-success signal — a long-lived `SAVEPOINT` would
1612
+ * leave the connection uncommitted (lost on close). Every backend interaction
1613
+ * runs through `#guard`, which maps a thrown backend `SQLiteError` (or any
1614
+ * unexpected non-`SQLiteError` throw) to a typed {@link DatabaseError} never
1615
+ * a raw backend error escapes `DriverInterface`: `CONSTRAINT` `CONFLICT`, the
1616
+ * wrapper's own `CLOSED` → `CLOSED`, `BUSY` (a locked database that outlasted
1617
+ * the configured `timeout`) a retryable `DRIVER` (`context.retryable` is
1618
+ * `true`), and `UNKNOWN` / any other throw → `DRIVER`. The original error is
1619
+ * preserved as `context.cause`. A `DatabaseError` this driver throws directly
1620
+ * (`CLOSED` from the `#require` gate, `NOT_FOUND` from `#table`, `MIGRATION`
1621
+ * from a migration-plan fault) passes through `#guard` unchanged, never
1622
+ * re-wrapped.
1718
1623
  */
1719
1624
  var SQLiteDriver = class {
1720
1625
  #path;
@@ -1751,7 +1656,7 @@ var SQLiteDriver = class {
1751
1656
  for (const [name, value] of Object.entries(this.#options.pragmas ?? {})) database.pragma(name, value);
1752
1657
  const map = /* @__PURE__ */ new Map();
1753
1658
  const identities = /* @__PURE__ */ new Map();
1754
- database.transaction(() => {
1659
+ database.transact(() => {
1755
1660
  this.#ensureMetadataTable(database);
1756
1661
  const stored = this.#readMetadata(database);
1757
1662
  const deployed = (0, _src_core.normalizeDriverSchema)(stored?.schema ?? owned);
@@ -1769,11 +1674,11 @@ var SQLiteDriver = class {
1769
1674
  for (const table of deployed) {
1770
1675
  const absent = missing.get(table.name);
1771
1676
  if (absent === void 0) {
1772
- database.exec(schemaToTable(table));
1773
- for (const sql of schemaToIndexes(table)) database.exec(sql);
1677
+ database.execute(schemaToTable(table));
1678
+ for (const sql of schemaToIndexes(table)) database.execute(sql);
1774
1679
  } else for (const [index, group] of table.indexes.entries()) if (absent.includes(deriveSQLiteIndexName(table.name, group))) {
1775
1680
  const sql = schemaToIndexes(table)[index];
1776
- if (sql !== void 0) database.exec(sql);
1681
+ if (sql !== void 0) database.execute(sql);
1777
1682
  }
1778
1683
  identities.set(table.name, previousIdentities.get(table.name) ?? {});
1779
1684
  }
@@ -1819,7 +1724,7 @@ var SQLiteDriver = class {
1819
1724
  return this.#keys(table);
1820
1725
  }
1821
1726
  scan(table) {
1822
- return new DriverIterator(this.#scan(table)[Symbol.asyncIterator](), () => this.#root());
1727
+ return new _src_core.DriverIterator(this.#scan(table)[Symbol.asyncIterator](), () => this.#root());
1823
1728
  }
1824
1729
  async clear(table) {
1825
1730
  this.#root();
@@ -1845,7 +1750,7 @@ var SQLiteDriver = class {
1845
1750
  const conditionsExact = conditions.every((condition) => matchesConditionExactly(condition, schema));
1846
1751
  const columnExact = matchesAggregateExactly(operation, column, schema);
1847
1752
  if (conditionsExact && columnExact) return this.#guard(() => {
1848
- const { sql, parameters } = compileWhere(conditions, schema);
1753
+ const { sql, parameters } = compileWhereSQL(conditions, schema);
1849
1754
  const value = this.#require().prepare("SELECT " + compileAggregateSQL(operation, column) + " AS value FROM " + quoteIdentifier(table) + (sql === "" ? "" : " " + sql)).get(parameters)?.value;
1850
1755
  return value === null || value === void 0 ? void 0 : Number(value);
1851
1756
  });
@@ -1855,10 +1760,10 @@ var SQLiteDriver = class {
1855
1760
  }
1856
1761
  stream(table, input) {
1857
1762
  (0, _src_core.validatePage)(input);
1858
- return new DriverIterator(this.#stream(table, input)[Symbol.asyncIterator](), () => this.#root());
1763
+ return new _src_core.DriverIterator(this.#stream(table, input)[Symbol.asyncIterator](), () => this.#root());
1859
1764
  }
1860
1765
  /**
1861
- * Begin a native transaction — real `BEGIN`, `COMMIT`, `ROLLBACK`.
1766
+ * Begins a native transaction — real `BEGIN`, `COMMIT`, `ROLLBACK`.
1862
1767
  *
1863
1768
  * @remarks
1864
1769
  * The callback receives a scoped {@link StorageInterface}. Fulfillment
@@ -1905,23 +1810,22 @@ var SQLiteDriver = class {
1905
1810
  }
1906
1811
  }
1907
1812
  /**
1908
- * Apply a {@link Migration} plan by executing each step's projected DDL
1813
+ * Applies a {@link Migration} plan by executing each step's projected DDL
1909
1814
  * ({@link import('../compilers.js').stepToSQL}).
1910
1815
  *
1911
1816
  * @remarks
1912
- * Atomicity is provided by whichever native transaction is active: when
1913
- * this driver's own `transaction()` callback is active (the
1914
- * core's versioned reconcile / migrate path joins migrate + stamp under
1915
- * one native `BEGIN`), the plan's DDL runs directly inside that enclosing
1916
- * transaction — a mid-plan failure rejects the callback and the driver
1917
- * rolls it back. node:sqlite (and SQLite
1918
- * generally) rejects a nested `BEGIN`, so this driver must never open a
1817
+ * Atomicity is provided by whichever native transaction is active: when this
1818
+ * driver's own `transaction()` callback is active (the core's versioned
1819
+ * reconcile / migrate path joins migrate + stamp under one native `BEGIN`),
1820
+ * the plan's DDL runs directly inside that enclosing transaction — a mid-plan
1821
+ * failure rejects the callback and the driver rolls it back. node:sqlite (and
1822
+ * SQLite generally) rejects a nested `BEGIN`, so this driver must never open a
1919
1823
  * second native transaction while one is already open. Otherwise (no
1920
1824
  * enclosing transaction), `migrate` wraps the plan in its own native
1921
1825
  * `database.transaction` — atomic on its own: a mid-plan failure rolls
1922
1826
  * back every DDL statement already applied by the plan. A scoped migration
1923
1827
  * uses one fixed internal savepoint literal because the published SQLite
1924
- * wrapper intentionally exposes raw `exec` but no savepoint manager. That
1828
+ * wrapper intentionally exposes raw `execute` but no savepoint manager. That
1925
1829
  * savepoint contains a caught inner migration so the outer callback
1926
1830
  * transaction remains active and may continue safely. A step referencing a
1927
1831
  * table not in this driver's declared schema (and that is not itself a
@@ -1941,7 +1845,7 @@ var SQLiteDriver = class {
1941
1845
  metadata: owned.metadata.schema
1942
1846
  });
1943
1847
  this.#guard(() => {
1944
- database.transaction(() => {
1848
+ database.transact(() => {
1945
1849
  this.#applyPlan(database, owned);
1946
1850
  if (owned.metadata !== void 0) this.#writeMetadata(database, owned.metadata);
1947
1851
  });
@@ -1950,7 +1854,7 @@ var SQLiteDriver = class {
1950
1854
  this.#identities = identities;
1951
1855
  }
1952
1856
  /**
1953
- * Read the persisted {@link DriverMetadata} from the reserved `_metadata` table.
1857
+ * Reads the persisted {@link DriverMetadata} from the reserved `_metadata` table.
1954
1858
  *
1955
1859
  * @returns The last-stamped `DriverMetadata`, or `undefined` when never stamped
1956
1860
  * (or the stored row is malformed)
@@ -1960,7 +1864,7 @@ var SQLiteDriver = class {
1960
1864
  return this.#metadata();
1961
1865
  }
1962
1866
  /**
1963
- * Persist an owned metadata snapshot into the reserved `_metadata` table's
1867
+ * Persists an owned metadata snapshot into the reserved `_metadata` table's
1964
1868
  * single row.
1965
1869
  *
1966
1870
  * @param metadata - The {@link DriverMetadata} to persist
@@ -2015,11 +1919,11 @@ var SQLiteDriver = class {
2015
1919
  }
2016
1920
  this.#guard(() => {
2017
1921
  const current = this.#require();
2018
- current.transaction(() => {
1922
+ current.transact(() => {
2019
1923
  for (const [name, replacement] of replacements) {
2020
- current.exec("DELETE FROM " + quoteIdentifier(name));
1924
+ current.execute("DELETE FROM " + quoteIdentifier(name));
2021
1925
  const statement = current.prepare("INSERT OR REPLACE INTO " + quoteIdentifier(name) + " (" + replacement.names.map(quoteIdentifier).join(", ") + ") VALUES (" + replacement.names.map(() => "?").join(", ") + ")");
2022
- for (const values of replacement.values) statement.run(values);
1926
+ for (const values of replacement.values) statement.execute(values);
2023
1927
  }
2024
1928
  });
2025
1929
  });
@@ -2040,7 +1944,7 @@ var SQLiteDriver = class {
2040
1944
  const values = extractValues(encoded, names, table);
2041
1945
  const statement = this.#require().prepare("INSERT OR REPLACE INTO " + quoteIdentifier(table) + " (" + names.map(quoteIdentifier).join(", ") + ") VALUES (" + names.map(() => "?").join(", ") + ")");
2042
1946
  (0, _src_core.checkAbort)(options?.signal);
2043
- statement.run(values);
1947
+ statement.execute(values);
2044
1948
  });
2045
1949
  }
2046
1950
  async #insert(table, key, row, options) {
@@ -2051,7 +1955,7 @@ var SQLiteDriver = class {
2051
1955
  const values = extractValues(encoded, names, table);
2052
1956
  const statement = this.#require().prepare("INSERT INTO " + quoteIdentifier(table) + " (" + names.map(quoteIdentifier).join(", ") + ") VALUES (" + names.map(() => "?").join(", ") + ")");
2053
1957
  (0, _src_core.checkAbort)(options?.signal);
2054
- statement.run(values);
1958
+ statement.execute(values);
2055
1959
  });
2056
1960
  }
2057
1961
  async #delete(table, key, options) {
@@ -2059,7 +1963,7 @@ var SQLiteDriver = class {
2059
1963
  return this.#guard(() => {
2060
1964
  const statement = this.#require().prepare("DELETE FROM " + quoteIdentifier(table) + " WHERE " + quoteIdentifier(schema.primary) + " = ?");
2061
1965
  (0, _src_core.checkAbort)(options?.signal);
2062
- return statement.run([this.#key(key, schema)]).changes > 0;
1966
+ return statement.execute([this.#key(key, schema)]).changes > 0;
2063
1967
  });
2064
1968
  }
2065
1969
  async #keys(table) {
@@ -2121,14 +2025,14 @@ var SQLiteDriver = class {
2121
2025
  async #clear(table) {
2122
2026
  this.#table(table);
2123
2027
  this.#guard(() => {
2124
- this.#require().prepare("DELETE FROM " + quoteIdentifier(table)).run();
2028
+ this.#require().prepare("DELETE FROM " + quoteIdentifier(table)).execute();
2125
2029
  });
2126
2030
  }
2127
2031
  async #metadata() {
2128
2032
  return this.#guard(() => this.#readMetadata(this.#require()));
2129
2033
  }
2130
2034
  #ensureMetadataTable(database) {
2131
- database.exec("CREATE TABLE IF NOT EXISTS " + quoteIdentifier(METADATA_TABLE) + " (\"id\" INTEGER, \"version\" INTEGER, \"schema\" TEXT, PRIMARY KEY (\"id\"))");
2035
+ database.execute("CREATE TABLE IF NOT EXISTS " + quoteIdentifier(METADATA_TABLE) + " (\"id\" INTEGER, \"version\" INTEGER, \"schema\" TEXT, PRIMARY KEY (\"id\"))");
2132
2036
  }
2133
2037
  #validateTable(database, schema) {
2134
2038
  const object = database.prepare("SELECT \"type\" AS \"category\" FROM \"sqlite_schema\" WHERE \"name\" = ?").get([schema.name]);
@@ -2263,7 +2167,7 @@ var SQLiteDriver = class {
2263
2167
  this.#guard(() => this.#writeMetadata(database, owned));
2264
2168
  }
2265
2169
  #writeMetadata(database, metadata) {
2266
- database.prepare("INSERT OR REPLACE INTO " + quoteIdentifier(METADATA_TABLE) + " (\"id\", \"version\", \"schema\") VALUES (1, ?, ?)").run([metadata.version, JSON.stringify(metadata.schema)]);
2170
+ database.prepare("INSERT OR REPLACE INTO " + quoteIdentifier(METADATA_TABLE) + " (\"id\", \"version\", \"schema\") VALUES (1, ?, ?)").execute([metadata.version, JSON.stringify(metadata.schema)]);
2267
2171
  }
2268
2172
  #capability(token) {
2269
2173
  return {
@@ -2300,7 +2204,7 @@ var SQLiteDriver = class {
2300
2204
  return this.#keys(table);
2301
2205
  }
2302
2206
  #scanTransaction(token, table) {
2303
- return new DriverIterator(this.#scan(table)[Symbol.asyncIterator](), () => {
2207
+ return new _src_core.DriverIterator(this.#scan(table)[Symbol.asyncIterator](), () => {
2304
2208
  this.#requireTransaction(token);
2305
2209
  });
2306
2210
  }
@@ -2322,16 +2226,16 @@ var SQLiteDriver = class {
2322
2226
  metadata: owned.metadata.schema
2323
2227
  });
2324
2228
  this.#guard(() => {
2325
- database.exec("SAVEPOINT \"_orkestrel_migration\"");
2229
+ database.execute("SAVEPOINT \"_orkestrel_migration\"");
2326
2230
  try {
2327
2231
  this.#applyPlan(database, owned);
2328
2232
  if (owned.metadata !== void 0) this.#writeMetadata(database, owned.metadata);
2329
- database.exec("RELEASE SAVEPOINT \"_orkestrel_migration\"");
2233
+ database.execute("RELEASE SAVEPOINT \"_orkestrel_migration\"");
2330
2234
  } catch (error) {
2331
2235
  try {
2332
- database.exec("ROLLBACK TO SAVEPOINT \"_orkestrel_migration\"");
2236
+ database.execute("ROLLBACK TO SAVEPOINT \"_orkestrel_migration\"");
2333
2237
  } finally {
2334
- database.exec("RELEASE SAVEPOINT \"_orkestrel_migration\"");
2238
+ database.execute("RELEASE SAVEPOINT \"_orkestrel_migration\"");
2335
2239
  }
2336
2240
  throw error;
2337
2241
  }
@@ -2407,13 +2311,13 @@ var SQLiteDriver = class {
2407
2311
  });
2408
2312
  }
2409
2313
  #applyPlan(database, input) {
2410
- for (const step of input.plan.steps) for (const sql of stepToSQL(step)) database.exec(sql);
2314
+ for (const step of input.plan.steps) for (const sql of stepToSQL(step)) database.execute(sql);
2411
2315
  }
2412
2316
  };
2413
2317
  //#endregion
2414
2318
  //#region src/server/factories.ts
2415
2319
  /**
2416
- * Create a persistent JSON-file {@link DriverInterface} for the core database layer.
2320
+ * Creates a persistent JSON-file {@link DriverInterface} for a given path.
2417
2321
  *
2418
2322
  * @remarks
2419
2323
  * Pass it to `createDatabase` from `@orkestrel/database` to run the typed
@@ -2421,9 +2325,10 @@ var SQLiteDriver = class {
2421
2325
  * `Table` / `Query` API is unchanged; only where the bytes live changes.
2422
2326
  * The driver is the reference `MemoryDriver` plus JSON-file persistence: `open` loads
2423
2327
  * the file, every mutation flushes the whole store back, and querying runs through
2424
- * the core engine over `scan` (it is scan-only no native `records` / `count` /
2425
- * `aggregate`). A missing, corrupt, or wrong-shaped file starts empty rather than
2426
- * throwing.
2328
+ * the core engine's `matchesQuery`. The driver implements the native `stream`
2329
+ * hook and neither `records` nor `aggregate`, so the engine answers every query
2330
+ * on either path. A missing, corrupt, or wrong-shaped file starts empty rather
2331
+ * than throwing.
2427
2332
  *
2428
2333
  * @param path - The JSON file path data is loaded from and flushed to
2429
2334
  * @returns A {@link DriverInterface} backed by a JSON file
@@ -2445,7 +2350,8 @@ function createJSONDriver(path) {
2445
2350
  return new JSONDriver(path);
2446
2351
  }
2447
2352
  /**
2448
- * Create a trusted-mode SQLite {@link DriverInterface} for the core database layer.
2353
+ * Creates a trusted-mode, server-native SQLite {@link DriverInterface} for a database path,
2354
+ * or for `:memory:` when the options bag omits one.
2449
2355
  *
2450
2356
  * @remarks
2451
2357
  * Pass it to `createDatabase` from `@orkestrel/database` to run the typed
@@ -2494,10 +2400,10 @@ exports.compileColumnSQL = compileColumnSQL;
2494
2400
  exports.compileConditionSQL = compileConditionSQL;
2495
2401
  exports.compileFieldSQL = compileFieldSQL;
2496
2402
  exports.compileJSONTypeSQL = compileJSONTypeSQL;
2497
- exports.compileOrder = compileOrder;
2498
- exports.compilePage = compilePage;
2403
+ exports.compileOrderSQL = compileOrderSQL;
2404
+ exports.compilePageSQL = compilePageSQL;
2499
2405
  exports.compileQuerySQL = compileQuerySQL;
2500
- exports.compileWhere = compileWhere;
2406
+ exports.compileWhereSQL = compileWhereSQL;
2501
2407
  exports.createJSONDriver = createJSONDriver;
2502
2408
  exports.createSQLiteDriver = createSQLiteDriver;
2503
2409
  exports.decodeRow = decodeRow;
@@ -2505,9 +2411,7 @@ exports.decodeValue = decodeValue;
2505
2411
  exports.deriveSQLiteIndexName = deriveSQLiteIndexName;
2506
2412
  exports.encodeRow = encodeRow;
2507
2413
  exports.encodeValue = encodeValue;
2508
- exports.escapeLike = escapeLike;
2509
2414
  exports.extractValues = extractValues;
2510
- exports.findColumnStorage = findColumnStorage;
2511
2415
  exports.inferValueStorage = inferValueStorage;
2512
2416
  exports.matchesAbsentPath = matchesAbsentPath;
2513
2417
  exports.matchesAggregateExactly = matchesAggregateExactly;