@orkestrel/database 0.0.5 → 0.0.7
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 +13 -35
- package/dist/src/browser/index.d.ts +100 -87
- package/dist/src/browser/index.js +369 -219
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +2389 -1244
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +545 -706
- package/dist/src/core/index.d.ts +545 -706
- package/dist/src/core/index.js +2368 -1231
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +1620 -827
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +466 -571
- package/dist/src/server/index.d.ts +466 -571
- package/dist/src/server/index.js +1603 -811
- package/dist/src/server/index.js.map +1 -1
- package/package.json +21 -19
|
@@ -1,116 +1,155 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { DriverInterface } from '
|
|
8
|
-
import {
|
|
9
|
-
import { DriverMeta } from '../core/index.js';
|
|
1
|
+
import { AggregateOperation } from '../core/index.ts';
|
|
2
|
+
import { AggregateOperation as AggregateOperation_2 } from '../../core/index.ts';
|
|
3
|
+
import { ColumnSchema } from '../core/index.ts';
|
|
4
|
+
import { ColumnStorage } from '../core/index.ts';
|
|
5
|
+
import { Condition } from '../core/index.ts';
|
|
6
|
+
import { DriverInterface } from '../core/index.ts';
|
|
7
|
+
import { DriverInterface as DriverInterface_2 } from '../../core/index.ts';
|
|
8
|
+
import { DriverMetadata } from '../../core/index.ts';
|
|
10
9
|
import { FieldPath } from '@orkestrel/contract';
|
|
11
|
-
import { Key } from '
|
|
12
|
-
import {
|
|
13
|
-
import { MigrationStep } from '../core/index.
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
* mirroring the engine's `computeAggregate('count')`, which is `rows.length`); the
|
|
29
|
-
* numeric aggregates wrap the column's read expression (a flat column, or a nested
|
|
30
|
-
* `json_extract` path) in `SUM` / `AVG` / `MIN` / `MAX`. Over zero matched rows
|
|
31
|
-
* `COUNT(*)` is `0` and the numeric aggregates are SQL `NULL` (→ `undefined`),
|
|
32
|
-
* matching the engine.
|
|
10
|
+
import { Key } from '../../core/index.ts';
|
|
11
|
+
import { MigrationInput } from '../../core/index.ts';
|
|
12
|
+
import { MigrationStep } from '../core/index.ts';
|
|
13
|
+
import { OperationOptions } from '../../core/index.ts';
|
|
14
|
+
import { Order } from '../core/index.ts';
|
|
15
|
+
import { QueryInput } from '../core/index.ts';
|
|
16
|
+
import { QueryInput as QueryInput_2 } from '../../core/index.ts';
|
|
17
|
+
import { Row } from '../core/index.ts';
|
|
18
|
+
import { Row as Row_2 } from '../../core/index.ts';
|
|
19
|
+
import { SQLiteRow } from '@orkestrel/sqlite';
|
|
20
|
+
import { SQLiteValue } from '@orkestrel/sqlite';
|
|
21
|
+
import { StorageInterface } from '../../core/index.ts';
|
|
22
|
+
import { TableSchema } from '../core/index.ts';
|
|
23
|
+
import { TableSchema as TableSchema_2 } from '../../core/index.ts';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Compile an {@link AggregateOperation} over a {@link FieldPath}.
|
|
33
27
|
*
|
|
34
28
|
* @param operation - The aggregate to compute
|
|
35
|
-
* @param column - The column
|
|
29
|
+
* @param column - The column or nested path to aggregate
|
|
36
30
|
* @returns The SQL aggregate expression
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* aggregateSQL('count', 'age') // 'COUNT(*)'
|
|
41
|
-
* aggregateSQL('sum', 'age') // 'SUM("age")'
|
|
42
|
-
* aggregateSQL('average', ['payload', 'score']) // 'AVG(json_extract("payload", \'$.score\'))'
|
|
43
|
-
* ```
|
|
44
31
|
*/
|
|
45
|
-
export declare function
|
|
32
|
+
export declare function compileAggregateSQL(operation: AggregateOperation, column: FieldPath): string;
|
|
46
33
|
|
|
47
34
|
/**
|
|
48
|
-
* Map a portable {@link
|
|
49
|
-
*
|
|
50
|
-
* @remarks
|
|
51
|
-
* `text` / `json` → `TEXT` (JSON is stored as text and read back with
|
|
52
|
-
* `json_extract` for nested-field queries); `integer` / `boolean` → `INTEGER`
|
|
53
|
-
* (a boolean stores `1` / `0`); `real` → `REAL`; `blob` → `BLOB`. No `NOT NULL`
|
|
54
|
-
* is ever emitted — the contract validates required-ness; the database is just
|
|
55
|
-
* storage (AGENTS §14, the typed layer above imposes the shape).
|
|
35
|
+
* Map a portable {@link ColumnStorage} to its SQLite column type.
|
|
56
36
|
*
|
|
57
|
-
* @param
|
|
37
|
+
* @param storage - The portable column type
|
|
58
38
|
* @returns The SQLite column type keyword
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* ```ts
|
|
62
|
-
* columnSQL('integer') // 'INTEGER'
|
|
63
|
-
* columnSQL('json') // 'TEXT'
|
|
64
|
-
* ```
|
|
65
39
|
*/
|
|
66
|
-
export declare function
|
|
40
|
+
export declare function compileColumnSQL(storage: ColumnStorage): string;
|
|
67
41
|
|
|
68
42
|
/**
|
|
69
|
-
* Compile
|
|
70
|
-
*
|
|
43
|
+
* Compile one condition to its `<column> <operator>` SQL fragment and the parameters
|
|
44
|
+
* it binds — engine-exact under SQL's three-valued NULL logic.
|
|
71
45
|
*
|
|
72
46
|
* @remarks
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* the
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* `FieldPath` (a `json_extract` read) encodes each operand as the native scalar
|
|
81
|
-
* the extract returns — derived from the operand's runtime type — so it compares.
|
|
82
|
-
* The 15 operators map per the databases guide's operator table, with
|
|
83
|
-
* `starts` / `ends` using `LIKE … ESCAPE '\'` and an empty `any` / `none` list
|
|
84
|
-
* collapsing to a constant. A `undefined` criteria (or one with no parts)
|
|
85
|
-
* compiles to an empty clause.
|
|
47
|
+
* Every operand is run through `encodeValue`, so a bound value matches the SQL
|
|
48
|
+
* the column side compiles to. A flat column encodes operands with its DECLARED
|
|
49
|
+
* schema type (a flat `json` column → `JSON.stringify`); a nested `FieldPath`
|
|
50
|
+
* encodes each operand as the NATIVE scalar `json_extract` returns, derived from
|
|
51
|
+
* the operand's runtime type (per-operand, since `between` / `any` / `none` can
|
|
52
|
+
* mix types). `any` / `none` collapse an empty list to a constant (`0` matches
|
|
53
|
+
* nothing, `1` matches all) with no parameters.
|
|
86
54
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
55
|
+
* The core engine's total order ranks `undefined` (rank 0) BELOW `null`
|
|
56
|
+
* (rank 1) (see `compareValues`), so a MISSING/`NULL` column MATCHES
|
|
57
|
+
* `below` / `to` / a scalar `not` / `none` — the opposite of raw SQL, where a
|
|
58
|
+
* comparison against `NULL` is `NULL` (excluded). This fragment replicates the
|
|
59
|
+
* engine exactly. Truth table (`value` = the engine's decoded field read; a
|
|
60
|
+
* FLAT column's stored `NULL` decodes to `undefined` per `decodeRow`, so a
|
|
61
|
+
* flat `value` is NEVER a present `null` — only a NESTED path can be
|
|
62
|
+
* present-but-`null`):
|
|
63
|
+
*
|
|
64
|
+
* ```text
|
|
65
|
+
* operator | value=undefined (absent) | value=null (nested only) | value=scalar
|
|
66
|
+
* --------------------|--------------------------------|---------------------------|-------------
|
|
67
|
+
* equals, first=null | no match | MATCH | no match
|
|
68
|
+
* equals, first=X | no match | no match | value===X
|
|
69
|
+
* not, first=null | MATCH (flat: unconditionally; | no match | MATCH
|
|
70
|
+
* | nested: absent still matches)| |
|
|
71
|
+
* not, first=X | MATCH | MATCH | value!==X
|
|
72
|
+
* below/to, first=X | MATCH (rank 0 < rank(X)) | MATCH (rank 1 < rank(X)) | rank compare
|
|
73
|
+
* none, list=[…] | MATCH (no scalar rank-equal) | MATCH | not-in-list
|
|
74
|
+
* any, list=[…] | no match | no match | in-list
|
|
75
|
+
* above/from/between | no match | no match | rank compare
|
|
76
|
+
* like/glob/starts/… | no match (not a string) | no match | string test
|
|
77
|
+
* present | false | false | true
|
|
78
|
+
* absent | true | true | false
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* A flat SQL `NULL` represents absence or explicit null according to its
|
|
82
|
+
* {@link ColumnSchema}; optional-and-nullable columns use a storage-class
|
|
83
|
+
* sentinel to distinguish the two. The native exactness gate therefore
|
|
84
|
+
* refines every optional or nullable scalar comparison through the core engine.
|
|
85
|
+
* This compiler still emits a total SQL fragment for direct consumers.
|
|
86
|
+
*
|
|
87
|
+
* A NESTED path can be present-but-`null` (a stored JSON `null`), which
|
|
88
|
+
* `json_extract` reads back as SQL `NULL` — indistinguishable from an ABSENT
|
|
89
|
+
* path. `json_type(col, path)` disambiguates them (`'null'` for present-null,
|
|
90
|
+
* SQL `NULL` for absent), so nested `equals` / `not` against a `null` operand
|
|
91
|
+
* compile through `json_type` instead of `IS NULL` / `IS NOT NULL`.
|
|
92
|
+
*
|
|
93
|
+
* Every other MATCH-on-null-or-absent row is expressed uniformly (flat and
|
|
94
|
+
* nested alike) as `(<column> <op> ? OR <column> IS NULL)` — for a nested
|
|
95
|
+
* path, `json_extract` already collapses BOTH absent and present-null to SQL
|
|
96
|
+
* `NULL`, so `IS NULL` catches both in one clause; for a flat column there is
|
|
97
|
+
* only the absent case to catch.
|
|
98
|
+
*
|
|
99
|
+
* @param condition - The condition to compile
|
|
100
|
+
* @param schema - The table's schema (for declared column types)
|
|
101
|
+
* @returns The SQL fragment and its bound parameters
|
|
90
102
|
*
|
|
91
103
|
* @example
|
|
92
104
|
* ```ts
|
|
93
|
-
*
|
|
94
|
-
* // { sql: '
|
|
105
|
+
* compileConditionSQL({ column: 'age', operator: 'above', values: [18], connector: 'and' }, schema)
|
|
106
|
+
* // { sql: '"age" > ?', parameters: [18] }
|
|
107
|
+
* compileConditionSQL({ column: 'age', operator: 'below', values: [18], connector: 'and' }, schema)
|
|
108
|
+
* // { sql: '("age" < ? OR "age" IS NULL)', parameters: [18] }
|
|
95
109
|
* ```
|
|
96
110
|
*/
|
|
97
|
-
export declare function
|
|
111
|
+
export declare function compileConditionSQL(condition: Condition, schema: TableSchema): CompiledSQL;
|
|
98
112
|
|
|
99
113
|
/**
|
|
100
114
|
* A parameterized SQL fragment or statement plus its bind values.
|
|
101
115
|
*
|
|
102
116
|
* @remarks
|
|
103
117
|
* Produced by the pure SQL compilers (`compilers.ts`) that turn a core
|
|
104
|
-
* `
|
|
105
|
-
* consumed by the SQLite driver, which runs `sql` with `
|
|
106
|
-
* order — no further assembly. Keeping `sql` and `
|
|
118
|
+
* `QueryInput` (or a table definition) into SQL text with `?` placeholders, and
|
|
119
|
+
* consumed by the SQLite driver, which runs `sql` with `parameters` bound in
|
|
120
|
+
* order — no further assembly. Keeping `sql` and `parameters` together prevents
|
|
107
121
|
* the two from drifting apart across compile and execute.
|
|
108
122
|
*/
|
|
109
123
|
export declare interface CompiledSQL {
|
|
110
124
|
readonly sql: string;
|
|
111
|
-
readonly
|
|
125
|
+
readonly parameters: readonly SQLiteValue[];
|
|
112
126
|
}
|
|
113
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Compile a {@link FieldPath} to the SQL expression that reads it.
|
|
130
|
+
*
|
|
131
|
+
* @param path - The field path
|
|
132
|
+
* @returns The SQL expression selecting the value
|
|
133
|
+
*/
|
|
134
|
+
export declare function compileFieldSQL(path: FieldPath): string;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Compile a NESTED {@link FieldPath} to the `json_type(<col>, <path>)` SQL
|
|
138
|
+
* expression — the {@link compileFieldSQL} `json_extract` sibling used to tell a
|
|
139
|
+
* PRESENT JSON `null` apart from an ABSENT path (both read back as SQL `NULL`
|
|
140
|
+
* through `json_extract`, but `json_type` reports `'null'` for the former and
|
|
141
|
+
* SQL `NULL` for the latter).
|
|
142
|
+
*
|
|
143
|
+
* @param path - The nested field path (a column plus its JSON keys)
|
|
144
|
+
* @returns The SQL expression reading the value's JSON type
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* compileJSONTypeSQL(['payload', 'user', 'id']) // "json_type(\"payload\", '$.user.id')"
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
export declare function compileJSONTypeSQL(path: readonly string[]): string;
|
|
152
|
+
|
|
114
153
|
/**
|
|
115
154
|
* Compile the ORDER BY clause from the order terms, always ending with the
|
|
116
155
|
* primary key as the final determinant.
|
|
@@ -152,20 +191,51 @@ export declare function compileOrder(order: readonly Order[] | undefined, schema
|
|
|
152
191
|
*
|
|
153
192
|
* @example
|
|
154
193
|
* ```ts
|
|
155
|
-
* compilePage(undefined, 5) // { sql: 'LIMIT -1 OFFSET ?',
|
|
194
|
+
* compilePage(undefined, 5) // { sql: 'LIMIT -1 OFFSET ?', parameters: [5] }
|
|
156
195
|
* ```
|
|
157
196
|
*/
|
|
158
197
|
export declare function compilePage(limit: number | undefined, offset: number | undefined): CompiledSQL;
|
|
159
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Compile a {@link QueryInput} into the SQL clause that follows a table name, with
|
|
201
|
+
* its bound parameters in clause order.
|
|
202
|
+
*
|
|
203
|
+
* @remarks
|
|
204
|
+
* The driver's native `records` / `count` path: it assembles
|
|
205
|
+
* `[where, orderBy, limitOffset]` (each possibly empty) into one clause so a
|
|
206
|
+
* `SELECT * FROM <table> <clause>` runs the whole read in the engine instead of
|
|
207
|
+
* over a JS `scan`. The WHERE fold is parenthesized **left-to-right** to mirror
|
|
208
|
+
* the core engine's `matchesQuery` (not SQL's native AND-over-OR precedence),
|
|
209
|
+
* so a native and an engine read return identical rows. Each operand is encoded
|
|
210
|
+
* via `encodeValue`: a flat column uses its declared schema type, while a nested
|
|
211
|
+
* `FieldPath` (a `json_extract` read) encodes each operand as the native scalar
|
|
212
|
+
* the extract returns — derived from the operand's runtime type — so it compares.
|
|
213
|
+
* The 15 operators map per the databases guide's operator table, with
|
|
214
|
+
* `starts` / `ends` using `LIKE … ESCAPE '\'` and an empty `any` / `none` list
|
|
215
|
+
* collapsing to a constant. An `undefined` input (or one with no parts)
|
|
216
|
+
* compiles to an empty clause.
|
|
217
|
+
*
|
|
218
|
+
* @param input - The read specification, or `undefined` for all rows
|
|
219
|
+
* @param schema - The table's schema (column types for operand encoding)
|
|
220
|
+
* @returns The SQL tail and its bound parameters
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* compileQuerySQL({ conditions: [{ column: 'age', operator: 'from', values: [18], connector: 'and' }] }, schema)
|
|
225
|
+
* // { sql: 'WHERE "age" >= ? ORDER BY "id"', parameters: [18] }
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
export declare function compileQuerySQL(input: QueryInput | undefined, schema: TableSchema): CompiledSQL;
|
|
229
|
+
|
|
160
230
|
/**
|
|
161
231
|
* Fold the conditions into one WHERE clause, parenthesizing progressively
|
|
162
|
-
* left-to-right so the grouping matches the engine's `
|
|
232
|
+
* left-to-right so the grouping matches the engine's `matchesQuery` fold.
|
|
163
233
|
*
|
|
164
234
|
* @remarks
|
|
165
235
|
* The first condition's connector is ignored, per the {@link Condition} types.
|
|
166
|
-
* Every fragment (see {@link
|
|
236
|
+
* Every fragment (see {@link compileConditionSQL}'s truth table) replicates the core
|
|
167
237
|
* engine's total order EXACTLY under SQL's three-valued NULL logic, so this
|
|
168
|
-
* clause matches `
|
|
238
|
+
* clause matches `applyQuery` row-for-row over the same table — a native
|
|
169
239
|
* `records` / `count` read never disagrees with a scan-and-filter fallback.
|
|
170
240
|
*
|
|
171
241
|
* @param conditions - The conditions to fold
|
|
@@ -175,7 +245,7 @@ export declare function compilePage(limit: number | undefined, offset: number |
|
|
|
175
245
|
* @example
|
|
176
246
|
* ```ts
|
|
177
247
|
* compileWhere([{ column: 'age', operator: 'from', values: [18], connector: 'and' }], schema)
|
|
178
|
-
* // { sql: 'WHERE "age" >= ?',
|
|
248
|
+
* // { sql: 'WHERE "age" >= ?', parameters: [18] }
|
|
179
249
|
* ```
|
|
180
250
|
*/
|
|
181
251
|
export declare function compileWhere(conditions: readonly Condition[], schema: TableSchema): CompiledSQL;
|
|
@@ -184,9 +254,9 @@ export declare function compileWhere(conditions: readonly Condition[], schema: T
|
|
|
184
254
|
* Create a persistent JSON-file {@link DriverInterface} for the core database layer.
|
|
185
255
|
*
|
|
186
256
|
* @remarks
|
|
187
|
-
* Pass it to `createDatabase` from `@orkestrel/database` to run the
|
|
188
|
-
*
|
|
189
|
-
* `Table` / `Query`
|
|
257
|
+
* Pass it to `createDatabase` from `@orkestrel/database` to run the typed
|
|
258
|
+
* database against a single JSON file instead of memory — the `Database` /
|
|
259
|
+
* `Table` / `Query` API is unchanged; only where the bytes live changes.
|
|
190
260
|
* The driver is the reference `MemoryDriver` plus JSON-file persistence: `open` loads
|
|
191
261
|
* the file, every mutation flushes the whole store back, and querying runs through
|
|
192
262
|
* the core engine over `scan` (it is scan-only — no native `records` / `count` /
|
|
@@ -215,19 +285,20 @@ export declare function createJSONDriver(path: string): DriverInterface;
|
|
|
215
285
|
* Create a trusted-mode SQLite {@link DriverInterface} for the core database layer.
|
|
216
286
|
*
|
|
217
287
|
* @remarks
|
|
218
|
-
* Pass it to `createDatabase` from `@orkestrel/database` to run the
|
|
219
|
-
*
|
|
220
|
-
* `Query`
|
|
288
|
+
* Pass it to `createDatabase` from `@orkestrel/database` to run the typed
|
|
289
|
+
* database against a real SQLite database — the `Database` / `Table` /
|
|
290
|
+
* `Query` API is unchanged; only where the bytes live changes. Built
|
|
221
291
|
* on the published `@orkestrel/sqlite` synchronous wrapper: `open` issues real
|
|
222
292
|
* typed `CREATE TABLE` / `CREATE INDEX` statements (reopen-safe) plus a reserved
|
|
223
|
-
* `
|
|
293
|
+
* `_metadata` table for `metadata()` / `stamp()` — avoid naming a table `_metadata`.
|
|
224
294
|
* Querying, paging, and aggregation run natively (`records` / `count` /
|
|
225
295
|
* `aggregate` / `stream`); `transaction` and `migrate` use real `BEGIN` /
|
|
226
296
|
* `COMMIT` / `ROLLBACK`, so `migrate` is atomic even mid-plan.
|
|
227
297
|
*
|
|
228
|
-
* @param options -
|
|
229
|
-
*
|
|
230
|
-
*
|
|
298
|
+
* @param options - The {@link SQLiteDriverOptions} bag (`path`, `readonly`,
|
|
299
|
+
* `timeout`, `references`, `pragmas`); `references` directly enables or
|
|
300
|
+
* disables foreign-key enforcement, and omission retains the upstream
|
|
301
|
+
* default; omit the whole bag for an in-memory database
|
|
231
302
|
* @returns A {@link DriverInterface} backed by SQLite
|
|
232
303
|
*
|
|
233
304
|
* @example
|
|
@@ -237,30 +308,16 @@ export declare function createJSONDriver(path: string): DriverInterface;
|
|
|
237
308
|
* import { createSQLiteDriver } from '@orkestrel/database/server'
|
|
238
309
|
*
|
|
239
310
|
* const db = createDatabase({
|
|
240
|
-
* driver: createSQLiteDriver('data/app.sqlite'),
|
|
311
|
+
* driver: createSQLiteDriver({ path: 'data/app.sqlite' }),
|
|
241
312
|
* tables: { users: { id: stringShape(), name: stringShape() } },
|
|
242
313
|
* })
|
|
243
314
|
* await db.table('users').set({ id: 'u1', name: 'Ada' }) // persisted to app.sqlite
|
|
244
315
|
*
|
|
245
|
-
* // Or with options:
|
|
316
|
+
* // Or with additional options:
|
|
246
317
|
* createSQLiteDriver({ path: 'data/app.sqlite', pragmas: { journal_mode: 'WAL' } })
|
|
247
318
|
* ```
|
|
248
319
|
*/
|
|
249
|
-
export declare function createSQLiteDriver(options?:
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* The declared storage type of a flat (string) column, read from the schema.
|
|
253
|
-
*
|
|
254
|
-
* @param column - The column name
|
|
255
|
-
* @param schema - The table's schema
|
|
256
|
-
* @returns The column's {@link ColumnType}, or `undefined` if the schema does not carry it
|
|
257
|
-
*
|
|
258
|
-
* @example
|
|
259
|
-
* ```ts
|
|
260
|
-
* declaredType('age', schema) // 'integer'
|
|
261
|
-
* ```
|
|
262
|
-
*/
|
|
263
|
-
export declare function declaredType(column: string, schema: TableSchema): ColumnType | undefined;
|
|
320
|
+
export declare function createSQLiteDriver(options?: SQLiteDriverOptions): DriverInterface;
|
|
264
321
|
|
|
265
322
|
/**
|
|
266
323
|
* Decode a stored {@link SQLiteRow} back to a {@link Row} by its table's schema.
|
|
@@ -269,9 +326,8 @@ export declare function declaredType(column: string, schema: TableSchema): Colum
|
|
|
269
326
|
* Decodes each declared column with {@link decodeValue} and **omits** any column
|
|
270
327
|
* whose decoded value is `undefined` — so an absent / `NULL` optional column does
|
|
271
328
|
* not surface as `{ bio: undefined }`, matching how the contract's optional
|
|
272
|
-
* columns expect absence.
|
|
273
|
-
*
|
|
274
|
-
* `undefined`, and an `undefined` value is omitted).
|
|
329
|
+
* columns expect absence. Nullable-only SQL `NULL` cells remain explicit
|
|
330
|
+
* `null`; optional-and-nullable columns use their storage-class sentinel.
|
|
275
331
|
*
|
|
276
332
|
* @param row - The stored SQLite row
|
|
277
333
|
* @param schema - The table's schema
|
|
@@ -285,27 +341,51 @@ export declare function declaredType(column: string, schema: TableSchema): Colum
|
|
|
285
341
|
export declare function decodeRow(row: SQLiteRow, schema: TableSchema): Row;
|
|
286
342
|
|
|
287
343
|
/**
|
|
288
|
-
* Decode a stored {@link SQLiteValue} back to its JS value for a column
|
|
344
|
+
* Decode a stored {@link SQLiteValue} back to its JS value for a declared column —
|
|
289
345
|
* the exact inverse of {@link encodeValue}.
|
|
290
346
|
*
|
|
291
347
|
* @remarks
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* omit absent columns.
|
|
348
|
+
* Stored values must use the declared SQLite storage class. SQL `NULL` decodes
|
|
349
|
+
* to explicit `null` only for nullable-only columns and otherwise to absence.
|
|
350
|
+
* Optional-and-nullable sentinels decode to explicit `null`; malformed values
|
|
351
|
+
* decode to `undefined` so {@link decodeRow} omits them.
|
|
297
352
|
*
|
|
298
353
|
* @param value - The stored SQLite value
|
|
299
|
-
* @param
|
|
300
|
-
* @returns The decoded JS value
|
|
354
|
+
* @param column - The declared storage and absence/null contract
|
|
355
|
+
* @returns The decoded JS value, or `undefined` for absence/malformed storage
|
|
301
356
|
*
|
|
302
357
|
* @example
|
|
303
358
|
* ```ts
|
|
304
|
-
* decodeValue(1,
|
|
305
|
-
* decodeValue('{"a":1}',
|
|
359
|
+
* decodeValue(1, booleanColumn) // true
|
|
360
|
+
* decodeValue('{"a":1}', jsonColumn) // { a: 1 }
|
|
306
361
|
* ```
|
|
307
362
|
*/
|
|
308
|
-
export declare function decodeValue(value: SQLiteValue,
|
|
363
|
+
export declare function decodeValue(value: SQLiteValue, column: ColumnSchema): unknown;
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Build a collision-free SQL index name for a table + column-group index —
|
|
367
|
+
* shared by the compiler module's `schemaToIndexes` and `stepToSQL`,
|
|
368
|
+
* so a plan-built index name always matches one `open` would have created.
|
|
369
|
+
*
|
|
370
|
+
* @remarks
|
|
371
|
+
* A naive `idx_<table>_<cols joined by _>` is AMBIGUOUS: table `'a_b'` with
|
|
372
|
+
* column `'c'` and table `'a'` with columns `['b', 'c']` both produce
|
|
373
|
+
* `idx_a_b_c`. This encodes each part (the table name, then each column name)
|
|
374
|
+
* length-prefixed (`<len>_<part>`) so the boundary between parts is always
|
|
375
|
+
* unambiguous, however the names themselves are punctuated.
|
|
376
|
+
*
|
|
377
|
+
* @param table - The table name
|
|
378
|
+
* @param columns - The index's column names, in order
|
|
379
|
+
* @returns The deterministic, collision-free index identifier (unquoted)
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* deriveSQLiteIndexName('users', ['name']) // 'idx_5_users_4_name'
|
|
384
|
+
* deriveSQLiteIndexName('a_b', ['c']) // 'idx_3_a_b_1_c'
|
|
385
|
+
* deriveSQLiteIndexName('a', ['b', 'c']) // 'idx_1_a_1_b_1_c'
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
export declare function deriveSQLiteIndexName(table: string, columns: readonly string[]): string;
|
|
309
389
|
|
|
310
390
|
/**
|
|
311
391
|
* Encode a whole {@link Row} to a {@link SQLiteRow} by its table's schema.
|
|
@@ -327,28 +407,25 @@ export declare function decodeValue(value: SQLiteValue, type: ColumnType): unkno
|
|
|
327
407
|
export declare function encodeRow(row: Row, schema: TableSchema): SQLiteRow;
|
|
328
408
|
|
|
329
409
|
/**
|
|
330
|
-
* Encode a JS value to its stored {@link SQLiteValue} for a column
|
|
410
|
+
* Encode a JS value to its stored {@link SQLiteValue} for a declared column.
|
|
331
411
|
*
|
|
332
412
|
* @remarks
|
|
333
|
-
* The
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
* `real` keep a `number` / `bigint`, else `null`; `text` keeps a `string`, else
|
|
338
|
-
* `null`; `blob` keeps a `Uint8Array`, else `null`. Narrowed with `typeof` /
|
|
339
|
-
* `instanceof`, never `as`.
|
|
413
|
+
* The codec is total: a malformed value encodes to SQL `NULL`. Absence always
|
|
414
|
+
* uses SQL `NULL`. A nullable-only column also uses SQL `NULL` for explicit
|
|
415
|
+
* `null`; an optional-and-nullable column uses a storage-class sentinel so
|
|
416
|
+
* absence and explicit `null` remain distinct.
|
|
340
417
|
*
|
|
341
418
|
* @param value - The JS value to store
|
|
342
|
-
* @param
|
|
419
|
+
* @param column - The declared storage and absence/null contract
|
|
343
420
|
* @returns The value SQLite stores
|
|
344
421
|
*
|
|
345
422
|
* @example
|
|
346
423
|
* ```ts
|
|
347
|
-
* encodeValue(true,
|
|
348
|
-
* encodeValue({ a: 1 },
|
|
424
|
+
* encodeValue(true, booleanColumn) // 1
|
|
425
|
+
* encodeValue({ a: 1 }, jsonColumn) // '{"a":1}'
|
|
349
426
|
* ```
|
|
350
427
|
*/
|
|
351
|
-
export declare function encodeValue(value: unknown,
|
|
428
|
+
export declare function encodeValue(value: unknown, column: ColumnSchema): SQLiteValue;
|
|
352
429
|
|
|
353
430
|
/**
|
|
354
431
|
* Escape `\`, `%`, and `_` (each with a leading `\`) so a `starts` / `ends`
|
|
@@ -365,7 +442,7 @@ export declare function encodeValue(value: unknown, type: ColumnType): SQLiteVal
|
|
|
365
442
|
export declare function escapeLike(text: string): string;
|
|
366
443
|
|
|
367
444
|
/**
|
|
368
|
-
* The declared {@link
|
|
445
|
+
* The declared {@link ColumnStorage}s whose SQL EQUALITY comparisons (`equals` /
|
|
369
446
|
* `not` / `any` / `none`) and `starts` / `ends` compiles are provably
|
|
370
447
|
* engine-exact under declared-type trust — `text` / `integer` / `real` /
|
|
371
448
|
* `boolean`; a `json` or `blob` column always refines instead.
|
|
@@ -380,258 +457,112 @@ export declare function escapeLike(text: string): string;
|
|
|
380
457
|
* compares UTF-16 CODE-UNIT order. The two orders diverge for supplementary-
|
|
381
458
|
* plane characters (code points ≥ U+10000, e.g. many emoji): a lead surrogate
|
|
382
459
|
* (`\uD800`–`\uDBFF`) sorts BELOW ``–`` in code-unit order, while
|
|
383
|
-
* its code point sorts ABOVE them. So `
|
|
384
|
-
* `
|
|
385
|
-
* future opt-in "trusted collation" mode (the caller vouches the column's
|
|
386
|
-
* values are BMP-only, or a custom SQLite collation matching `compareValues`
|
|
387
|
-
* is registered) could restore native text ranges/ordering.
|
|
460
|
+
* its code point sorts ABOVE them. So `matchesConditionExactly`'s range family and
|
|
461
|
+
* `matchesOrderExactly` exclude `text`, refining through the core engine instead.
|
|
388
462
|
*/
|
|
389
|
-
export declare const
|
|
463
|
+
export declare const EXACT_COLUMN_STORAGE: readonly ColumnStorage[];
|
|
390
464
|
|
|
391
465
|
/**
|
|
392
|
-
* The declared {@link
|
|
466
|
+
* The declared {@link ColumnStorage}s whose SQL RANGE comparisons
|
|
393
467
|
* (`above` / `below` / `from` / `to` / `between`) and `ORDER BY` compiles are
|
|
394
468
|
* provably engine-exact — `integer` / `real` / `boolean` only. `text` is
|
|
395
|
-
* excluded: see {@link
|
|
469
|
+
* excluded: see {@link EXACT_COLUMN_STORAGE}'s remarks for the BINARY-collation
|
|
396
470
|
* (code-point) vs. JS `<` (code-unit) divergence on supplementary-plane
|
|
397
471
|
* characters.
|
|
398
472
|
*/
|
|
399
|
-
export declare const
|
|
473
|
+
export declare const EXACT_RANGE_COLUMN_STORAGE: readonly ColumnStorage[];
|
|
400
474
|
|
|
401
475
|
/**
|
|
402
|
-
*
|
|
476
|
+
* Extract a stored row's values in a declared positional order.
|
|
403
477
|
*
|
|
404
478
|
* @remarks
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
* examples (simple identifier keys). The string's value is never split on `.`
|
|
409
|
-
* (AGENTS — `FieldPath` semantics): a dotted string is one column literally.
|
|
479
|
+
* SQLite statements bind arrays positionally. Every requested column must be
|
|
480
|
+
* present in `row`; an incomplete backend row is a typed `DRIVER` fault carrying
|
|
481
|
+
* the table and missing column in its context.
|
|
410
482
|
*
|
|
411
|
-
* @param
|
|
412
|
-
* @
|
|
413
|
-
*
|
|
414
|
-
* @
|
|
415
|
-
*
|
|
416
|
-
* fieldColumn('payload') // '"payload"'
|
|
417
|
-
* fieldColumn(['payload', 'user', 'id']) // 'json_extract("payload", \'$.user.id\')'
|
|
418
|
-
* ```
|
|
419
|
-
*/
|
|
420
|
-
export declare function fieldColumn(path: FieldPath): string;
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Compile one condition to its `<column> <operator>` SQL fragment and the params
|
|
424
|
-
* it binds — engine-exact under SQL's three-valued NULL logic.
|
|
425
|
-
*
|
|
426
|
-
* @remarks
|
|
427
|
-
* Every operand is run through `encodeValue`, so a bound value matches the SQL
|
|
428
|
-
* the column side compiles to. A flat column encodes operands with its DECLARED
|
|
429
|
-
* schema type (a flat `json` column → `JSON.stringify`); a nested `FieldPath`
|
|
430
|
-
* encodes each operand as the NATIVE scalar `json_extract` returns, derived from
|
|
431
|
-
* the operand's runtime type (per-operand, since `between` / `any` / `none` can
|
|
432
|
-
* mix types). `any` / `none` collapse an empty list to a constant (`0` matches
|
|
433
|
-
* nothing, `1` matches all) with no params.
|
|
434
|
-
*
|
|
435
|
-
* The core engine's total order ranks `undefined` (rank 0) BELOW `null`
|
|
436
|
-
* (rank 1) (see `compareValues`), so a MISSING/`NULL` column MATCHES
|
|
437
|
-
* `below` / `to` / a scalar `not` / `none` — the opposite of raw SQL, where a
|
|
438
|
-
* comparison against `NULL` is `NULL` (excluded). This fragment replicates the
|
|
439
|
-
* engine exactly. Truth table (`value` = the engine's decoded field read; a
|
|
440
|
-
* FLAT column's stored `NULL` decodes to `undefined` per `decodeRow`, so a
|
|
441
|
-
* flat `value` is NEVER a present `null` — only a NESTED path can be
|
|
442
|
-
* present-but-`null`):
|
|
443
|
-
*
|
|
444
|
-
* ```text
|
|
445
|
-
* operator | value=undefined (absent) | value=null (nested only) | value=scalar
|
|
446
|
-
* --------------------|--------------------------------|---------------------------|-------------
|
|
447
|
-
* equals, first=null | no match | MATCH | no match
|
|
448
|
-
* equals, first=X | no match | no match | value===X
|
|
449
|
-
* not, first=null | MATCH (flat: unconditionally; | no match | MATCH
|
|
450
|
-
* | nested: absent still matches)| |
|
|
451
|
-
* not, first=X | MATCH | MATCH | value!==X
|
|
452
|
-
* below/to, first=X | MATCH (rank 0 < rank(X)) | MATCH (rank 1 < rank(X)) | rank compare
|
|
453
|
-
* none, list=[…] | MATCH (no scalar rank-equal) | MATCH | not-in-list
|
|
454
|
-
* any, list=[…] | no match | no match | in-list
|
|
455
|
-
* above/from/between | no match | no match | rank compare
|
|
456
|
-
* like/glob/starts/… | no match (not a string) | no match | string test
|
|
457
|
-
* present | false | false | true
|
|
458
|
-
* absent | true | true | false
|
|
459
|
-
* ```
|
|
460
|
-
*
|
|
461
|
-
* Because a flat column's `NULL` always decodes to `undefined`, `equals`
|
|
462
|
-
* against a `null` operand needs no special flat compilation (`col = ?`
|
|
463
|
-
* binding a `NULL` param is already always-false in SQL, matching "no match"
|
|
464
|
-
* above) — but flat `not` against `null` must match EVERY row (both the
|
|
465
|
-
* absent and the scalar rows), which `col != ? OR col IS NULL` cannot express
|
|
466
|
-
* (it only catches the `IS NULL` row), so a flat `not`-with-`null`-operand
|
|
467
|
-
* compiles to the constant `1`.
|
|
468
|
-
*
|
|
469
|
-
* A NESTED path can be present-but-`null` (a stored JSON `null`), which
|
|
470
|
-
* `json_extract` reads back as SQL `NULL` — indistinguishable from an ABSENT
|
|
471
|
-
* path. `json_type(col, path)` disambiguates them (`'null'` for present-null,
|
|
472
|
-
* SQL `NULL` for absent), so nested `equals` / `not` against a `null` operand
|
|
473
|
-
* compile through `json_type` instead of `IS NULL` / `IS NOT NULL`.
|
|
474
|
-
*
|
|
475
|
-
* Every other MATCH-on-null-or-absent row is expressed uniformly (flat and
|
|
476
|
-
* nested alike) as `(<column> <op> ? OR <column> IS NULL)` — for a nested
|
|
477
|
-
* path, `json_extract` already collapses BOTH absent and present-null to SQL
|
|
478
|
-
* `NULL`, so `IS NULL` catches both in one clause; for a flat column there is
|
|
479
|
-
* only the absent case to catch.
|
|
480
|
-
*
|
|
481
|
-
* @param condition - The condition to compile
|
|
482
|
-
* @param schema - The table's schema (for declared column types)
|
|
483
|
-
* @returns The SQL fragment and its bound parameters
|
|
483
|
+
* @param row - The stored SQLite row
|
|
484
|
+
* @param names - The column names in binding order
|
|
485
|
+
* @param table - The owning table name for fault context
|
|
486
|
+
* @returns The row values in the same order as `names`
|
|
487
|
+
* @throws A `DRIVER` {@link DatabaseError} when a requested column is missing
|
|
484
488
|
*
|
|
485
489
|
* @example
|
|
486
490
|
* ```ts
|
|
487
|
-
*
|
|
488
|
-
* // { sql: '"age" > ?', params: [18] }
|
|
489
|
-
* fragment({ column: 'age', operator: 'below', values: [18], connector: 'and' }, schema)
|
|
490
|
-
* // { sql: '("age" < ? OR "age" IS NULL)', params: [18] }
|
|
491
|
+
* extractValues({ id: 'u1', age: 36 }, ['age', 'id'], 'users') // [36, 'u1']
|
|
491
492
|
* ```
|
|
492
493
|
*/
|
|
493
|
-
export declare function
|
|
494
|
+
export declare function extractValues(row: SQLiteRow, names: readonly string[], table: string): readonly SQLiteValue[];
|
|
494
495
|
|
|
495
496
|
/**
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
* @remarks
|
|
499
|
-
* Supply this as {@link import('@orkestrel/database').DatabaseOptions.key} so a table mints
|
|
500
|
-
* a key when a written row lacks its primary-key value. Strings work as keys on
|
|
501
|
-
* every backend; supply your own key values directly to use numeric keys instead.
|
|
497
|
+
* The declared storage type of a flat (string) column, read from the schema.
|
|
502
498
|
*
|
|
503
|
-
* @
|
|
499
|
+
* @param column - The column name
|
|
500
|
+
* @param schema - The table's schema
|
|
501
|
+
* @returns The column's {@link ColumnStorage}, or `undefined` if the schema does not carry it
|
|
504
502
|
*
|
|
505
503
|
* @example
|
|
506
504
|
* ```ts
|
|
507
|
-
*
|
|
505
|
+
* findColumnStorage('age', schema) // 'integer'
|
|
508
506
|
* ```
|
|
509
507
|
*/
|
|
510
|
-
export declare function
|
|
508
|
+
export declare function findColumnStorage(column: string, schema: TableSchema): ColumnStorage | undefined;
|
|
511
509
|
|
|
512
510
|
/**
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
* {@link stepToSQL}'s `index.add` / `index.remove` (a migration-time DDL),
|
|
516
|
-
* so a plan-built index name always matches one `open` would have created.
|
|
511
|
+
* The storage type a nested (`json_extract`) operand encodes as, derived from its
|
|
512
|
+
* RUNTIME value — NOT `json`.
|
|
517
513
|
*
|
|
518
514
|
* @remarks
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
515
|
+
* `json_extract` returns the unquoted, natively-typed scalar (a JSON boolean as
|
|
516
|
+
* `1` / `0`, a number as-is, a string as-is), so the operand must encode to that
|
|
517
|
+
* same scalar to compare. A boolean → `'boolean'` (→ `1` / `0`); a number →
|
|
518
|
+
* `'integer'` / `'real'`; a bigint → `'integer'`; a string → `'text'`; `null` /
|
|
519
|
+
* `undefined` → `'text'` (encodes to `null`); an object / array → `'json'` (the
|
|
520
|
+
* edge of comparing against a json subtree).
|
|
524
521
|
*
|
|
525
|
-
* @param
|
|
526
|
-
* @
|
|
527
|
-
* @returns The deterministic, collision-free index identifier (unquoted)
|
|
522
|
+
* @param value - The runtime operand value
|
|
523
|
+
* @returns The {@link ColumnStorage} to encode it as
|
|
528
524
|
*
|
|
529
525
|
* @example
|
|
530
526
|
* ```ts
|
|
531
|
-
*
|
|
532
|
-
*
|
|
533
|
-
* indexName('a', ['b', 'c']) // 'idx_1_a_1_b_1_c'
|
|
527
|
+
* inferValueStorage(true) // 'boolean'
|
|
528
|
+
* inferValueStorage(9) // 'integer'
|
|
534
529
|
* ```
|
|
535
530
|
*/
|
|
536
|
-
export declare function
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Whether one {@link Condition} compiles to SQL that is PROVABLY identical to
|
|
540
|
-
* the core engine's `matchesCondition` for every value its column's declared
|
|
541
|
-
* type can store.
|
|
542
|
-
*
|
|
543
|
-
* @remarks
|
|
544
|
-
* `false` for a nested `FieldPath` (an array), a column absent from `schema`,
|
|
545
|
-
* or a column whose declared type is not `text` / `integer` / `real` /
|
|
546
|
-
* `boolean` (a `json` / `blob` column) — EXCEPT `absent` / `present`, which
|
|
547
|
-
* compile to `IS NULL` / `IS NOT NULL` and match `decodeRow`'s "a stored NULL
|
|
548
|
-
* decodes to `undefined`" rule for every column type, so they are exact
|
|
549
|
-
* regardless of declared type. `equals` / `not` require a operand matching the
|
|
550
|
-
* column's declared type (a `null` / `undefined` operand is never exact here —
|
|
551
|
-
* `encodeRow` stores both an explicit `null` and an absent field as SQL NULL,
|
|
552
|
-
* so native `IS NULL` semantics cannot match the engine's `deepEqual`-over-
|
|
553
|
-
* decoded-rows truth). `above` / `below` / `from` / `to` / `between` are exact
|
|
554
|
-
* ONLY for a declared type in {@link EXACT_RANGE_COLUMN_TYPES} (`integer` /
|
|
555
|
-
* `real` / `boolean`) — a `text` column's range conditions REFINE, because
|
|
556
|
-
* SQLite's default BINARY collation orders TEXT by Unicode CODE POINT while
|
|
557
|
-
* the core engine's `compareValues` orders JS strings by UTF-16 CODE UNIT,
|
|
558
|
-
* and the two diverge for supplementary-plane characters (see
|
|
559
|
-
* {@link EXACT_COLUMN_TYPES}'s remarks for the full rationale).
|
|
560
|
-
* `any` / `none` require a NON-EMPTY list where every element matches (an empty
|
|
561
|
-
* list is exact under neither: the engine's `any([])` matches nothing while
|
|
562
|
-
* `none([])` matches everything, and SQL `IN ()` is a syntax error) — these
|
|
563
|
-
* stay exact on `text` (byte equality is collation-independent and engine-
|
|
564
|
-
* identical). `starts` / `ends` are exact only on a `text` column with a
|
|
565
|
-
* string operand (case-sensitive `substr` compile, see {@link fragment}) —
|
|
566
|
-
* likewise collation-independent. `like` / `glob` are NEVER exact — SQLite
|
|
567
|
-
* `LIKE` folds case ASCII-only against the engine's Unicode fold, and `GLOB`
|
|
568
|
-
* has character classes the engine treats literally.
|
|
569
|
-
*
|
|
570
|
-
* @param condition - The condition to test
|
|
571
|
-
* @param schema - The table's schema
|
|
572
|
-
* @returns Whether `condition` is exact
|
|
573
|
-
*/
|
|
574
|
-
export declare function isExactCondition(condition: Condition, schema: TableSchema): boolean;
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Whether a whole {@link Criteria} is exact — every condition and every order
|
|
578
|
-
* term is exact. `limit` / `offset` never affect exactness (SQL `LIMIT` /
|
|
579
|
-
* `OFFSET` are always engine-identical).
|
|
580
|
-
*
|
|
581
|
-
* @param criteria - The criteria to test
|
|
582
|
-
* @param schema - The table's schema
|
|
583
|
-
* @returns Whether every part of `criteria` is exact
|
|
584
|
-
*/
|
|
585
|
-
export declare function isExactCriteria(criteria: Criteria, schema: TableSchema): boolean;
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* Whether one {@link Order} term's column compiles to an `ORDER BY` that
|
|
589
|
-
* matches the engine's {@link import('../core/index.js').sortRows} exactly.
|
|
590
|
-
*
|
|
591
|
-
* @remarks
|
|
592
|
-
* `false` for a nested `FieldPath`, a column absent from `schema`, or a
|
|
593
|
-
* declared type outside {@link EXACT_RANGE_COLUMN_TYPES} (`integer` / `real` /
|
|
594
|
-
* `boolean`). `text` is NOT exact here: SQLite's default BINARY collation
|
|
595
|
-
* orders TEXT by Unicode code point while the core engine's `compareValues`
|
|
596
|
-
* orders JS strings by UTF-16 code unit, and the two diverge for
|
|
597
|
-
* supplementary-plane characters (see {@link EXACT_COLUMN_TYPES}'s remarks) —
|
|
598
|
-
* a `text` order term REFINES through the core engine instead.
|
|
599
|
-
*
|
|
600
|
-
* @param order - The order term to test
|
|
601
|
-
* @param schema - The table's schema
|
|
602
|
-
* @returns Whether `order` is exact
|
|
603
|
-
*/
|
|
604
|
-
export declare function isExactOrder(order: Order, schema: TableSchema): boolean;
|
|
531
|
+
export declare function inferValueStorage(value: unknown): ColumnStorage;
|
|
605
532
|
|
|
606
533
|
/**
|
|
607
534
|
* A persistent {@link DriverInterface} backed by a single JSON file — the
|
|
608
535
|
* reference {@link MemoryDriver} plus file load / flush.
|
|
609
536
|
*
|
|
610
537
|
* @remarks
|
|
611
|
-
* A decorator, not a reimplementation:
|
|
612
|
-
* {@link MemoryDriver},
|
|
613
|
-
*
|
|
538
|
+
* A decorator, not a reimplementation: storage primitives delegate to an inner
|
|
539
|
+
* {@link MemoryDriver}, while this layer owns persistence, writer ordering,
|
|
540
|
+
* isolated transactions, and queued row-snapshot restoration. `open`
|
|
614
541
|
* loads the file into memory; every mutation (`write` / `delete` / `clear`) flushes
|
|
615
|
-
* the whole store back. The file is one JSON object, `{
|
|
616
|
-
* [name]: rows } }` — `
|
|
617
|
-
* (an unstamped store
|
|
618
|
-
* backward compatibility); a per-table array of rows, each row carrying its own
|
|
542
|
+
* the whole store back. The file is one JSON object, `{ metadata?: DriverMetadata, tables: {
|
|
543
|
+
* [name]: rows } }` — `metadata` is present only once the store has been `stamp`ed
|
|
544
|
+
* (an unstamped store omits `metadata`); a per-table array of rows, each row carrying its own
|
|
619
545
|
* primary (the table contract), so the key is recovered on load with
|
|
620
546
|
* {@link extractKey} and the file need not store it. The parsed JSON crosses the
|
|
621
547
|
* boundary as `unknown` and is narrowed with {@link isRecord} / {@link extractKey},
|
|
622
|
-
* never asserted (AGENTS §14)
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
* the optional native `records` / `
|
|
548
|
+
* never asserted (AGENTS §14). Only an `ENOENT` read starts empty; every other
|
|
549
|
+
* read failure or invalid existing document fails closed without publication,
|
|
550
|
+
* mutation, or automatic repair. It is scan-only — it implements none of
|
|
551
|
+
* the optional native `records` / `aggregate` hooks, so the core engine
|
|
626
552
|
* over `scan` answers every query. For development, small datasets, and portable /
|
|
627
553
|
* inspectable data; for large or concurrent workloads reach for a SQLite-backed
|
|
628
554
|
* driver.
|
|
629
555
|
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
*
|
|
556
|
+
* Metadata crosses {@link cloneDriverMetadata} at parsed-file ingress, public and
|
|
557
|
+
* scoped write ingress, candidate/root publication, serialization, and copy-out.
|
|
558
|
+
* Callers therefore cannot mutate queued metadata, and `metadata()` always returns a
|
|
559
|
+
* distinct deeply frozen snapshot. A failure in the write path ({@link
|
|
560
|
+
* JSONDriver.#serialize} — `mkdir` / `writeFile` / `rename`) is wrapped and
|
|
561
|
+
* rethrown as `DatabaseError` `DRIVER`, carrying the target `path` and native
|
|
562
|
+
* `cause` in its context. If temporary-file cleanup also fails, the top-level
|
|
563
|
+
* `DRIVER` context additionally carries `temp` and `cleanup`; a precommit abort
|
|
564
|
+
* remains an `ABORTED` `DatabaseError` in `context.cause`. The fail-closed read path
|
|
565
|
+
* ({@link JSONDriver.#document}) remains separate from this write-error contract.
|
|
635
566
|
*/
|
|
636
567
|
export declare class JSONDriver implements DriverInterface_2 {
|
|
637
568
|
#private;
|
|
@@ -639,92 +570,124 @@ export declare class JSONDriver implements DriverInterface_2 {
|
|
|
639
570
|
open(schema: readonly TableSchema_2[]): Promise<void>;
|
|
640
571
|
close(): Promise<void>;
|
|
641
572
|
read(table: string, key: Key): Promise<Row_2 | undefined>;
|
|
642
|
-
write(table: string, key: Key, row: Row_2): Promise<void>;
|
|
643
|
-
|
|
573
|
+
write(table: string, key: Key, row: Row_2, options?: OperationOptions): Promise<void>;
|
|
574
|
+
insert(table: string, key: Key, row: Row_2, options?: OperationOptions): Promise<void>;
|
|
575
|
+
delete(table: string, key: Key, options?: OperationOptions): Promise<boolean>;
|
|
644
576
|
keys(table: string): Promise<readonly Key[]>;
|
|
645
577
|
scan(table: string): AsyncIterable<Row_2>;
|
|
646
578
|
/**
|
|
647
579
|
* Natively filtered lazy iteration — delegates to the inner {@link MemoryDriver}.
|
|
648
580
|
*
|
|
649
581
|
* @remarks
|
|
650
|
-
* Semantics are the memory driver's own: `
|
|
651
|
-
* / `limit` page lazily, and `
|
|
582
|
+
* Semantics are the memory driver's own: `input.conditions` filters, `offset`
|
|
583
|
+
* / `limit` page lazily, and `input.order` is ignored (streaming yields key
|
|
652
584
|
* order; sorted output is `records()`'s job).
|
|
653
585
|
*
|
|
654
586
|
* @param table - The table to stream
|
|
655
|
-
* @param
|
|
587
|
+
* @param input - The filter / offset / limit to apply lazily
|
|
656
588
|
*/
|
|
657
|
-
stream(table: string,
|
|
589
|
+
stream(table: string, input: QueryInput_2): AsyncIterable<Row_2>;
|
|
658
590
|
clear(table: string): Promise<void>;
|
|
659
591
|
/**
|
|
660
|
-
*
|
|
592
|
+
* Run an isolated native transaction callback over a candidate memory store.
|
|
661
593
|
*
|
|
662
594
|
* @remarks
|
|
663
|
-
* Single-writer:
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
669
|
-
* `#flush()`, persisting the transaction's net state. `rollback()` restores
|
|
670
|
-
* memory via the captured snapshot thunk, then `#flush()`s so the file reflects
|
|
671
|
-
* the restored state. Outside a transaction, behavior is unchanged — every
|
|
672
|
-
* mutation flushes on its own. Calling `commit` / `rollback` a second time (on
|
|
673
|
-
* either method, in either order) throws `DatabaseError` `CONFLICT`.
|
|
595
|
+
* Single-writer: nesting and root operations while active throw `CONFLICT`.
|
|
596
|
+
* The callback receives a capability over cloned rows, schema, and metadata.
|
|
597
|
+
* Fulfillment atomically serializes that candidate and publishes it to root
|
|
598
|
+
* memory only after file replacement succeeds. Rejection or persistence
|
|
599
|
+
* failure discards the candidate, and every captured capability call after
|
|
600
|
+
* settlement throws `CONFLICT`.
|
|
674
601
|
*
|
|
675
|
-
* @returns
|
|
602
|
+
* @returns The callback's resolved value
|
|
603
|
+
*/
|
|
604
|
+
transaction<R>(scope: (storage: StorageInterface) => Promise<R>): Promise<R>;
|
|
605
|
+
/**
|
|
606
|
+
* Capture an owned row snapshot at an exact writer-queue position.
|
|
607
|
+
*
|
|
608
|
+
* @remarks
|
|
609
|
+
* Capture owns table names, schemas, rows, and one session-local identity per
|
|
610
|
+
* table. Rollback is repeatable: it clones the then-current root into a
|
|
611
|
+
* candidate, adapts captured rows to each surviving same-identity table
|
|
612
|
+
* through the portable migration engine, persists the candidate with current
|
|
613
|
+
* metadata, and publishes memory only after file replacement succeeds.
|
|
614
|
+
* Removed, replaced, uncaptured, and later-added tables remain untouched.
|
|
615
|
+
*
|
|
616
|
+
* @param tables - Existing tables to capture; omitted captures every current table
|
|
617
|
+
* @returns A repeatable rollback operation
|
|
676
618
|
*/
|
|
677
|
-
transaction(): Promise<TransactionInterface>;
|
|
678
619
|
snapshot(tables?: readonly string[]): Promise<() => Promise<void>>;
|
|
679
|
-
|
|
620
|
+
metadata(): Promise<DriverMetadata | undefined>;
|
|
680
621
|
/**
|
|
681
|
-
* Persist
|
|
622
|
+
* Persist an owned metadata snapshot for a later `metadata()` to copy out.
|
|
682
623
|
*
|
|
683
624
|
* @remarks
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
*
|
|
625
|
+
* Root stamping conflicts while a transaction is active. The scoped
|
|
626
|
+
* {@link StorageInterface.stamp} updates candidate metadata and publishes
|
|
627
|
+
* with the candidate rows on callback fulfillment.
|
|
687
628
|
*
|
|
688
|
-
* @param
|
|
629
|
+
* @param metadata - The {@link DriverMetadata} to persist
|
|
689
630
|
*/
|
|
690
|
-
stamp(
|
|
631
|
+
stamp(metadata: DriverMetadata): Promise<void>;
|
|
691
632
|
/**
|
|
692
|
-
* Apply
|
|
693
|
-
* then persist the migrated state.
|
|
633
|
+
* Apply one atomic {@link MigrationInput} through an isolated candidate.
|
|
694
634
|
*
|
|
695
635
|
* @remarks
|
|
696
|
-
* The
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
* this driver's own declared `#schema`, mirroring the bookkeeping `open` does,
|
|
701
|
-
* so a subsequent `#flush` / `#load` round-trip includes (or drops) the table.
|
|
702
|
-
* A successful migration ends with one atomic `#flush()` so the new state
|
|
703
|
-
* survives a close and reopen. A multi-step plan applies its steps
|
|
704
|
-
* sequentially and is NOT atomic — a failure partway through a plan leaves
|
|
705
|
-
* the earlier steps already applied.
|
|
636
|
+
* The candidate receives every plan step plus optional metadata. Its complete
|
|
637
|
+
* rows, derived schema, and metadata serialize through one atomic file
|
|
638
|
+
* replacement before root memory changes. Any migration or persistence failure
|
|
639
|
+
* therefore leaves root state and the prior file exact.
|
|
706
640
|
*
|
|
707
|
-
* @param
|
|
641
|
+
* @param input - The plan and optional metadata to settle together
|
|
708
642
|
*/
|
|
709
|
-
migrate(
|
|
643
|
+
migrate(input: MigrationInput): Promise<void>;
|
|
710
644
|
}
|
|
711
645
|
|
|
712
646
|
/**
|
|
713
|
-
*
|
|
714
|
-
* expression — the {@link fieldColumn} `json_extract` sibling used to tell a
|
|
715
|
-
* PRESENT JSON `null` apart from an ABSENT path (both read back as SQL `NULL`
|
|
716
|
-
* through `json_extract`, but `json_type` reports `'null'` for the former and
|
|
717
|
-
* SQL `NULL` for the latter).
|
|
647
|
+
* Determine whether SQLite can execute an aggregate exactly like the core engine.
|
|
718
648
|
*
|
|
719
|
-
* @param
|
|
720
|
-
* @
|
|
649
|
+
* @param operation - Aggregate operation
|
|
650
|
+
* @param column - Aggregate field
|
|
651
|
+
* @param schema - Current table schema
|
|
652
|
+
* @returns Whether native aggregation is exact
|
|
653
|
+
*/
|
|
654
|
+
export declare function matchesAggregateExactly(operation: AggregateOperation, column: FieldPath, schema: TableSchema): boolean;
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Whether one {@link Condition} compiles to SQL that is PROVABLY identical to
|
|
658
|
+
* the core engine's `matchesCondition` for every value its column's declared
|
|
659
|
+
* type can store.
|
|
721
660
|
*
|
|
722
|
-
* @
|
|
723
|
-
*
|
|
724
|
-
*
|
|
725
|
-
*
|
|
661
|
+
* @remarks
|
|
662
|
+
* `false` for a nested `FieldPath` (an array) or a column absent from `schema`.
|
|
663
|
+
* `absent` / `present` are exact unless a column is both optional and nullable.
|
|
664
|
+
* In that combined case the storage sentinel for explicit `null` is not SQL
|
|
665
|
+
* `NULL`, while the core treats both absence and explicit `null` as absent.
|
|
666
|
+
* Every scalar operator refines when the column is optional
|
|
667
|
+
* OR nullable, because SQL null semantics and the core total order differ.
|
|
668
|
+
* Required non-null `equals` / `not` require an operand matching the declared
|
|
669
|
+
* storage and exclude `json` / `blob`. `above` / `below` / `from` / `to` /
|
|
670
|
+
* `between` are exact only for {@link EXACT_RANGE_COLUMN_STORAGE} (`integer` /
|
|
671
|
+
* `real` / `boolean`) — a `text` column's range conditions REFINE, because
|
|
672
|
+
* SQLite's default BINARY collation orders TEXT by Unicode CODE POINT while
|
|
673
|
+
* the core engine's `compareValues` orders JS strings by UTF-16 CODE UNIT,
|
|
674
|
+
* and the two diverge for supplementary-plane characters (see
|
|
675
|
+
* {@link EXACT_COLUMN_STORAGE}'s remarks for the full rationale).
|
|
676
|
+
* `any` / `none` require a NON-EMPTY list where every element matches (an empty
|
|
677
|
+
* list is exact under neither: the engine's `any([])` matches nothing while
|
|
678
|
+
* `none([])` matches everything, and SQL `IN ()` is a syntax error) — these
|
|
679
|
+
* stay exact on `text` (byte equality is collation-independent and engine-
|
|
680
|
+
* identical). `starts` / `ends` are exact only on a `text` column with a
|
|
681
|
+
* string operand (case-sensitive `substr` compile, see {@link compileConditionSQL}) —
|
|
682
|
+
* likewise collation-independent. `like` / `glob` are NEVER exact — SQLite
|
|
683
|
+
* `LIKE` folds case ASCII-only against the engine's Unicode fold, and `GLOB`
|
|
684
|
+
* has character classes the engine treats literally.
|
|
685
|
+
*
|
|
686
|
+
* @param condition - The condition to test
|
|
687
|
+
* @param schema - The table's schema
|
|
688
|
+
* @returns Whether `condition` is exact
|
|
726
689
|
*/
|
|
727
|
-
export declare function
|
|
690
|
+
export declare function matchesConditionExactly(condition: Condition, schema: TableSchema): boolean;
|
|
728
691
|
|
|
729
692
|
/**
|
|
730
693
|
* Whether a value's runtime type matches a column's declared exact type —
|
|
@@ -732,7 +695,7 @@ export declare function jsonTypeColumn(path: readonly string[]): string;
|
|
|
732
695
|
*
|
|
733
696
|
* @remarks
|
|
734
697
|
* `text` ↔ string, `integer` / `real` ↔ FINITE number (`NaN` / `±Infinity`
|
|
735
|
-
* fail), `boolean` ↔ boolean. Backs {@link
|
|
698
|
+
* fail), `boolean` ↔ boolean. Backs {@link matchesConditionExactly}'s operand checks.
|
|
736
699
|
*
|
|
737
700
|
* @param value - The condition operand to test
|
|
738
701
|
* @param type - The column's declared portable type
|
|
@@ -744,18 +707,57 @@ export declare function jsonTypeColumn(path: readonly string[]): string;
|
|
|
744
707
|
* matchesDeclaredType(Number.NaN, 'integer') // false — only finite numbers
|
|
745
708
|
* ```
|
|
746
709
|
*/
|
|
747
|
-
export declare function
|
|
710
|
+
export declare function matchesDeclaredStorage(value: unknown, storage: ColumnStorage): boolean;
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Whether one {@link Order} term's column compiles to an `ORDER BY` that
|
|
714
|
+
* matches the engine's {@link import('@src/core').sortRows} exactly.
|
|
715
|
+
*
|
|
716
|
+
* @remarks
|
|
717
|
+
* `false` for a nested `FieldPath`, a column absent from `schema`, or a
|
|
718
|
+
* declared type outside {@link EXACT_RANGE_COLUMN_STORAGE} (`integer` / `real` /
|
|
719
|
+
* `boolean`). `text` is NOT exact here: SQLite's default BINARY collation
|
|
720
|
+
* orders TEXT by Unicode code point while the core engine's `compareValues`
|
|
721
|
+
* orders JS strings by UTF-16 code unit, and the two diverge for
|
|
722
|
+
* supplementary-plane characters (see {@link EXACT_COLUMN_STORAGE}'s remarks) —
|
|
723
|
+
* a `text` order term REFINES through the core engine instead.
|
|
724
|
+
*
|
|
725
|
+
* @param order - The order term to test
|
|
726
|
+
* @param schema - The table's schema
|
|
727
|
+
* @returns Whether `order` is exact
|
|
728
|
+
*/
|
|
729
|
+
export declare function matchesOrderExactly(order: Order, schema: TableSchema): boolean;
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Whether a whole {@link QueryInput} is exact — every condition and every order
|
|
733
|
+
* term is exact. `limit` / `offset` never affect exactness (SQL `LIMIT` /
|
|
734
|
+
* `OFFSET` are always engine-identical).
|
|
735
|
+
*
|
|
736
|
+
* @param input - The query input to test
|
|
737
|
+
* @param schema - The table's schema
|
|
738
|
+
* @returns Whether every part of `input` is exact
|
|
739
|
+
*/
|
|
740
|
+
export declare function matchesQueryExactly(input: QueryInput, schema: TableSchema): boolean;
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Test a declared SQLite type against a portable storage affinity.
|
|
744
|
+
*
|
|
745
|
+
* @param declared - Native declared type
|
|
746
|
+
* @param storage - Portable column storage
|
|
747
|
+
* @returns Whether SQLite's official affinity rules yield the expected affinity
|
|
748
|
+
*/
|
|
749
|
+
export declare function matchesSQLiteAffinity(declared: unknown, storage: ColumnStorage): boolean;
|
|
748
750
|
|
|
749
751
|
/**
|
|
750
752
|
* The reserved metadata table the {@link SQLiteDriver} creates on `open` to
|
|
751
|
-
* persist its stamped `
|
|
752
|
-
* SQLite realization of the `
|
|
753
|
+
* persist its stamped `DriverMetadata` (`version` + declared schema JSON) — the
|
|
754
|
+
* SQLite realization of the `metadata` / `stamp` driver hooks.
|
|
753
755
|
*
|
|
754
756
|
* @remarks
|
|
755
|
-
* A single-row table (`id = 1`). A user table named `
|
|
757
|
+
* A single-row table (`id = 1`). A user table named `_metadata` collides with the
|
|
756
758
|
* reservation — the caller's concern to avoid, documented on the driver class.
|
|
757
759
|
*/
|
|
758
|
-
export declare const
|
|
760
|
+
export declare const METADATA_TABLE = "_metadata";
|
|
759
761
|
|
|
760
762
|
/**
|
|
761
763
|
* Quote a SQL identifier (a table or column name) so any characters are literal.
|
|
@@ -770,48 +772,24 @@ export declare const META_TABLE = "_meta";
|
|
|
770
772
|
*
|
|
771
773
|
* @example
|
|
772
774
|
* ```ts
|
|
773
|
-
*
|
|
775
|
+
* quoteIdentifier('order') // '"order"'
|
|
774
776
|
* ```
|
|
775
777
|
*/
|
|
776
|
-
export declare function
|
|
778
|
+
export declare function quoteIdentifier(identifier: string): string;
|
|
777
779
|
|
|
778
780
|
/**
|
|
779
|
-
* Project a {@link TableSchema} to
|
|
780
|
-
* SQLite driver's `open` issues for its declared indexes.
|
|
781
|
-
*
|
|
782
|
-
* @remarks
|
|
783
|
-
* One statement per index group; the index name is built by {@link indexName}
|
|
784
|
-
* (collision-free and deterministic), matching the driver's naming so a
|
|
785
|
-
* repeated `open` is idempotent.
|
|
781
|
+
* Project a {@link TableSchema} to its declared SQLite indexes.
|
|
786
782
|
*
|
|
787
|
-
* @param schema - The table
|
|
788
|
-
* @returns One
|
|
789
|
-
*
|
|
790
|
-
* @example
|
|
791
|
-
* ```ts
|
|
792
|
-
* schemaToIndexes(schema)
|
|
793
|
-
* // ['CREATE INDEX IF NOT EXISTS "idx_5_users_4_name" ON "users" ("name")']
|
|
794
|
-
* ```
|
|
783
|
+
* @param schema - The table schema
|
|
784
|
+
* @returns One statement per declared index
|
|
795
785
|
*/
|
|
796
786
|
export declare function schemaToIndexes(schema: TableSchema): readonly string[];
|
|
797
787
|
|
|
798
788
|
/**
|
|
799
|
-
* Project a {@link TableSchema} to
|
|
800
|
-
* SQLite driver's `open` issues for it.
|
|
789
|
+
* Project a {@link TableSchema} to its `CREATE TABLE IF NOT EXISTS` statement.
|
|
801
790
|
*
|
|
802
|
-
* @
|
|
803
|
-
*
|
|
804
|
-
* with `PRIMARY KEY (<quoted primary>)`. No `NOT NULL` is emitted — the contract
|
|
805
|
-
* validates required-ness, the database is just storage (AGENTS §14).
|
|
806
|
-
*
|
|
807
|
-
* @param schema - The table's schema
|
|
808
|
-
* @returns The `CREATE TABLE IF NOT EXISTS …` statement
|
|
809
|
-
*
|
|
810
|
-
* @example
|
|
811
|
-
* ```ts
|
|
812
|
-
* schemaToTable(schema)
|
|
813
|
-
* // 'CREATE TABLE IF NOT EXISTS "users" ("id" TEXT, "age" INTEGER, PRIMARY KEY ("id"))'
|
|
814
|
-
* ```
|
|
791
|
+
* @param schema - The table schema
|
|
792
|
+
* @returns The complete table declaration
|
|
815
793
|
*/
|
|
816
794
|
export declare function schemaToTable(schema: TableSchema): string;
|
|
817
795
|
|
|
@@ -825,21 +803,23 @@ export declare function schemaToTable(schema: TableSchema): string;
|
|
|
825
803
|
* raw `node:sqlite`. `open` issues `CREATE TABLE IF NOT EXISTS` with real typed
|
|
826
804
|
* columns (mapped from each {@link TableSchema}'s portable column types) and a
|
|
827
805
|
* `PRIMARY KEY`, plus a `CREATE INDEX IF NOT EXISTS` per declared index (both
|
|
828
|
-
* reopen-safe), and readies a reserved `
|
|
829
|
-
* `stamp()` read and write — **a user table named `
|
|
806
|
+
* reopen-safe), and readies a reserved `_metadata` single-row table `metadata()` /
|
|
807
|
+
* `stamp()` read and write — **a user table named `_metadata` collides with it**;
|
|
830
808
|
* avoid the name. Rows cross the boundary through the codecs in `helpers.ts`
|
|
831
809
|
* (`json` columns store / parse JSON text, a `boolean` stores `1` / `0`), so the
|
|
832
810
|
* typed layer above imposes the exact shape (AGENTS §14). `write` is an
|
|
833
|
-
* `INSERT OR REPLACE` upsert
|
|
834
|
-
*
|
|
835
|
-
* `SQLiteError`
|
|
836
|
-
*
|
|
837
|
-
*
|
|
838
|
-
* `
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
811
|
+
* `INSERT OR REPLACE` upsert, while `insert` uses a plain `INSERT` and maps its
|
|
812
|
+
* atomic primary-key constraint failure to `CONFLICT`; every other backend
|
|
813
|
+
* `SQLiteError` is contained by the same `DatabaseError` boundary described
|
|
814
|
+
* below. Querying, ordering, paging, and
|
|
815
|
+
* aggregation is native: `records` / `stream` compile a `QueryInput`
|
|
816
|
+
* to SQL with `compileQuerySQL`, and `aggregate` runs a SQL
|
|
817
|
+
* `COUNT`/`SUM`/`AVG`/`MIN`/`MAX` (via `compileAggregateSQL`) over the same compiled
|
|
818
|
+
* WHERE. `transaction` runs a callback inside native `BEGIN` / `COMMIT` /
|
|
819
|
+
* `ROLLBACK`, passing a scoped storage capability that becomes invalid after
|
|
820
|
+
* settlement. `migrate` runs the plan's projected DDL
|
|
821
|
+
* ({@link import('../compilers.js').stepToSQL}) inside whichever native
|
|
822
|
+
* transaction is active: joined into the active transaction callback
|
|
843
823
|
* when one exists (the core's versioned reconcile path wraps migrate + stamp
|
|
844
824
|
* in one native `BEGIN`, and node:sqlite rejects a nested `BEGIN`), or inside
|
|
845
825
|
* its own `database.transaction` otherwise — a mid-plan failure rolls back
|
|
@@ -864,67 +844,72 @@ export declare function schemaToTable(schema: TableSchema): string;
|
|
|
864
844
|
*/
|
|
865
845
|
export declare class SQLiteDriver implements DriverInterface_2 {
|
|
866
846
|
#private;
|
|
867
|
-
constructor(
|
|
847
|
+
constructor(options?: SQLiteDriverOptions);
|
|
868
848
|
open(schema: readonly TableSchema_2[]): Promise<void>;
|
|
869
849
|
close(): Promise<void>;
|
|
870
850
|
read(table: string, key: Key): Promise<Row_2 | undefined>;
|
|
871
|
-
write(table: string, key: Key, row: Row_2): Promise<void>;
|
|
872
|
-
|
|
851
|
+
write(table: string, key: Key, row: Row_2, options?: OperationOptions): Promise<void>;
|
|
852
|
+
insert(table: string, key: Key, row: Row_2, options?: OperationOptions): Promise<void>;
|
|
853
|
+
delete(table: string, key: Key, options?: OperationOptions): Promise<boolean>;
|
|
873
854
|
keys(table: string): Promise<readonly Key[]>;
|
|
874
855
|
scan(table: string): AsyncIterable<Row_2>;
|
|
875
856
|
clear(table: string): Promise<void>;
|
|
876
|
-
records(table: string,
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
stream(table: string, criteria: Criteria_2): AsyncIterable<Row_2>;
|
|
857
|
+
records(table: string, input: QueryInput_2): Promise<readonly Row_2[]>;
|
|
858
|
+
aggregate(table: string, operation: AggregateOperation_2, column: FieldPath, input: QueryInput_2): Promise<number | undefined>;
|
|
859
|
+
stream(table: string, input: QueryInput_2): AsyncIterable<Row_2>;
|
|
880
860
|
/**
|
|
881
861
|
* Begin a native transaction — real `BEGIN`, `COMMIT`, `ROLLBACK`.
|
|
882
862
|
*
|
|
883
863
|
* @remarks
|
|
884
|
-
*
|
|
885
|
-
*
|
|
864
|
+
* The callback receives a scoped {@link StorageInterface}. Fulfillment
|
|
865
|
+
* commits and returns its value; rejection rolls back and preserves the
|
|
866
|
+
* original error. Root operations and nesting conflict while active, and a
|
|
867
|
+
* captured capability conflicts after settlement.
|
|
886
868
|
*
|
|
887
|
-
* @returns
|
|
869
|
+
* @returns The callback's resolved value
|
|
888
870
|
*/
|
|
889
|
-
transaction(): Promise<
|
|
871
|
+
transaction<R>(scope: (storage: StorageInterface) => Promise<R>): Promise<R>;
|
|
890
872
|
/**
|
|
891
873
|
* Apply a {@link Migration} plan by executing each step's projected DDL
|
|
892
|
-
* ({@link import('../
|
|
874
|
+
* ({@link import('../compilers.js').stepToSQL}).
|
|
893
875
|
*
|
|
894
876
|
* @remarks
|
|
895
877
|
* Atomicity is provided by whichever native transaction is active: when
|
|
896
|
-
* this driver's own `transaction()`
|
|
878
|
+
* this driver's own `transaction()` callback is active (the
|
|
897
879
|
* core's versioned reconcile / migrate path joins migrate + stamp under
|
|
898
880
|
* one native `BEGIN`), the plan's DDL runs directly inside that enclosing
|
|
899
|
-
* transaction — a mid-plan failure
|
|
900
|
-
*
|
|
881
|
+
* transaction — a mid-plan failure rejects the callback and the driver
|
|
882
|
+
* rolls it back. node:sqlite (and SQLite
|
|
901
883
|
* generally) rejects a nested `BEGIN`, so this driver must never open a
|
|
902
884
|
* second native transaction while one is already open. Otherwise (no
|
|
903
885
|
* enclosing transaction), `migrate` wraps the plan in its own native
|
|
904
886
|
* `database.transaction` — atomic on its own: a mid-plan failure rolls
|
|
905
|
-
* back every DDL statement already applied by the plan. A
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
909
|
-
* active
|
|
887
|
+
* back every DDL statement already applied by the plan. A scoped migration
|
|
888
|
+
* uses one fixed internal savepoint literal because the published SQLite
|
|
889
|
+
* wrapper intentionally exposes raw `exec` but no savepoint manager. That
|
|
890
|
+
* savepoint contains a caught inner migration so the outer callback
|
|
891
|
+
* transaction remains active and may continue safely. A step referencing a
|
|
892
|
+
* table not in this driver's declared schema (and that is not itself a
|
|
893
|
+
* `table.add`) throws `DatabaseError` `MIGRATION` before any DDL for that
|
|
894
|
+
* step runs.
|
|
910
895
|
*
|
|
911
|
-
* @param
|
|
896
|
+
* @param input - The migration plan and optional metadata stamp to apply atomically
|
|
912
897
|
*/
|
|
913
|
-
migrate(
|
|
898
|
+
migrate(input: MigrationInput): Promise<void>;
|
|
914
899
|
/**
|
|
915
|
-
* Read the persisted {@link
|
|
900
|
+
* Read the persisted {@link DriverMetadata} from the reserved `_metadata` table.
|
|
916
901
|
*
|
|
917
|
-
* @returns The last-stamped `
|
|
902
|
+
* @returns The last-stamped `DriverMetadata`, or `undefined` when never stamped
|
|
918
903
|
* (or the stored row is malformed)
|
|
919
904
|
*/
|
|
920
|
-
|
|
905
|
+
metadata(): Promise<DriverMetadata | undefined>;
|
|
921
906
|
/**
|
|
922
|
-
* Persist
|
|
923
|
-
* row.
|
|
907
|
+
* Persist an owned metadata snapshot into the reserved `_metadata` table's
|
|
908
|
+
* single row.
|
|
924
909
|
*
|
|
925
|
-
* @param
|
|
910
|
+
* @param metadata - The {@link DriverMetadata} to persist
|
|
926
911
|
*/
|
|
927
|
-
stamp(
|
|
912
|
+
stamp(metadata: DriverMetadata): Promise<void>;
|
|
928
913
|
snapshot(tables?: readonly string[]): Promise<() => Promise<void>>;
|
|
929
914
|
}
|
|
930
915
|
|
|
@@ -936,8 +921,9 @@ export declare class SQLiteDriver implements DriverInterface_2 {
|
|
|
936
921
|
* `path` is the database file path (`':memory:'` when omitted); `readonly`
|
|
937
922
|
* opens the connection read-only (a write then fails as a typed `DRIVER`
|
|
938
923
|
* {@link DatabaseError}); `timeout` is the busy-timeout in milliseconds before
|
|
939
|
-
* a locked database fails `BUSY`; `
|
|
940
|
-
* constraint enforcement
|
|
924
|
+
* a locked database fails `BUSY`; `references` enables or disables foreign-key
|
|
925
|
+
* constraint enforcement, while omission retains the upstream default.
|
|
926
|
+
* `pragmas` is an ordered record of PRAGMA name to
|
|
941
927
|
* value, applied via the wrapper's `pragma()` right after `connect()`, in
|
|
942
928
|
* insertion order (e.g. `{ journal_mode: 'WAL' }`). Core rows are
|
|
943
929
|
* number-typed — this driver never surfaces a `bigint`, so a stored integer
|
|
@@ -948,107 +934,16 @@ export declare interface SQLiteDriverOptions {
|
|
|
948
934
|
readonly path?: string;
|
|
949
935
|
readonly readonly?: boolean;
|
|
950
936
|
readonly timeout?: number;
|
|
951
|
-
readonly
|
|
937
|
+
readonly references?: boolean;
|
|
952
938
|
readonly pragmas?: Readonly<Record<string, string | number>>;
|
|
953
939
|
}
|
|
954
940
|
|
|
955
941
|
/**
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
* @remarks
|
|
959
|
-
* Every column value is a {@link SQLiteValue}. The SQLite driver decodes each
|
|
960
|
-
* raw row into this shape before handing it to the core query engine; nothing
|
|
961
|
-
* above the driver ever sees SQLite's native row representation directly.
|
|
962
|
-
*/
|
|
963
|
-
export declare type SQLiteRow = Record<string, SQLiteValue>;
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
* The value domain a SQLite binding accepts as a bound parameter and returns
|
|
967
|
-
* from a row.
|
|
968
|
-
*
|
|
969
|
-
* @remarks
|
|
970
|
-
* Mirrors SQLite's storage classes (`NULL`, `INTEGER`, `REAL`, `TEXT`, `BLOB`)
|
|
971
|
-
* at the TypeScript boundary: `null`, `number` / `bigint` for integer and
|
|
972
|
-
* floating-point values, `string` for text, and `Uint8Array` for blobs. This
|
|
973
|
-
* type is pure — it names the shape a value must have to cross the binding,
|
|
974
|
-
* independent of any concrete sqlite package (`node:sqlite`, `better-sqlite3`,
|
|
975
|
-
* etc.), so a driver can encode/decode against it without importing one.
|
|
976
|
-
*/
|
|
977
|
-
export declare type SQLiteValue = null | number | bigint | string | Uint8Array;
|
|
978
|
-
|
|
979
|
-
/**
|
|
980
|
-
* Project one {@link MigrationStep} onto its table's declared {@link TableSchema}
|
|
981
|
-
* — the bookkeeping counterpart to {@link stepToSQL} (which projects the DDL a
|
|
982
|
-
* driver's `migrate` runs against the live database).
|
|
983
|
-
*
|
|
984
|
-
* @remarks
|
|
985
|
-
* `column.add` / `column.remove` add / filter the named column;
|
|
986
|
-
* `index.add` / `index.remove` add / filter the matching index group (an exact
|
|
987
|
-
* ordered match on `index`). `table.add` / `table.remove` act on a WHOLE
|
|
988
|
-
* schema map rather than one table's shape, so they are the caller's concern
|
|
989
|
-
* (a driver's `migrate` applies them directly against its table map) — passed
|
|
990
|
-
* here, they return `schema` unchanged.
|
|
991
|
-
*
|
|
992
|
-
* @param schema - The table's current declared schema
|
|
993
|
-
* @param step - The migration step to project onto it
|
|
994
|
-
* @returns The table's schema after the step
|
|
995
|
-
*
|
|
996
|
-
* @example
|
|
997
|
-
* ```ts
|
|
998
|
-
* stepToSchema(schema, { operation: 'column.remove', table: 'users', column: 'legacy' })
|
|
999
|
-
* // schema with the 'legacy' column dropped from `columns`
|
|
1000
|
-
* ```
|
|
1001
|
-
*/
|
|
1002
|
-
export declare function stepToSchema(schema: TableSchema, step: MigrationStep): TableSchema;
|
|
1003
|
-
|
|
1004
|
-
/**
|
|
1005
|
-
* Project one {@link MigrationStep} to the DDL statement(s) a SQLite driver's
|
|
1006
|
-
* `migrate` executes for it.
|
|
1007
|
-
*
|
|
1008
|
-
* @remarks
|
|
1009
|
-
* `table.add` emits the `CREATE TABLE` plus one `CREATE INDEX` per declared
|
|
1010
|
-
* index (via {@link schemaToTable} / {@link schemaToIndexes}); `table.remove`
|
|
1011
|
-
* emits `DROP TABLE IF EXISTS`; `column.add` / `column.remove` emit `ALTER
|
|
1012
|
-
* TABLE … ADD COLUMN` / `… DROP COLUMN`; `index.add` / `index.remove` emit
|
|
1013
|
-
* `CREATE INDEX IF NOT EXISTS` / `DROP INDEX IF EXISTS`, naming the index the
|
|
1014
|
-
* same way `schemaToIndexes` does (`idx_<table>_<columns joined by _>`) so a
|
|
1015
|
-
* plan-built index matches one `open` would have created. Whether the named
|
|
1016
|
-
* table actually exists is the caller's concern (a driver's `migrate` checks
|
|
1017
|
-
* its own declared schema before running these statements) — this projection
|
|
1018
|
-
* is pure and never inspects live state.
|
|
1019
|
-
*
|
|
1020
|
-
* @param step - The migration step to project
|
|
1021
|
-
* @returns The DDL statement(s) that apply the step
|
|
942
|
+
* Project one {@link MigrationStep} to SQLite DDL.
|
|
1022
943
|
*
|
|
1023
|
-
* @
|
|
1024
|
-
*
|
|
1025
|
-
* stepToSQL({ operation: 'column.remove', table: 'users', column: 'legacy' })
|
|
1026
|
-
* // ['ALTER TABLE "users" DROP COLUMN "legacy"']
|
|
1027
|
-
* ```
|
|
944
|
+
* @param step - The migration step
|
|
945
|
+
* @returns The statements that apply the step
|
|
1028
946
|
*/
|
|
1029
947
|
export declare function stepToSQL(step: MigrationStep): readonly string[];
|
|
1030
948
|
|
|
1031
|
-
/**
|
|
1032
|
-
* The storage type a nested (`json_extract`) operand encodes as, derived from its
|
|
1033
|
-
* RUNTIME value — NOT `json`.
|
|
1034
|
-
*
|
|
1035
|
-
* @remarks
|
|
1036
|
-
* `json_extract` returns the unquoted, natively-typed scalar (a JSON boolean as
|
|
1037
|
-
* `1` / `0`, a number as-is, a string as-is), so the operand must encode to that
|
|
1038
|
-
* same scalar to compare. A boolean → `'boolean'` (→ `1` / `0`); a number →
|
|
1039
|
-
* `'integer'` / `'real'`; a bigint → `'integer'`; a string → `'text'`; `null` /
|
|
1040
|
-
* `undefined` → `'text'` (encodes to `null`); an object / array → `'json'` (the
|
|
1041
|
-
* edge of comparing against a json subtree).
|
|
1042
|
-
*
|
|
1043
|
-
* @param value - The runtime operand value
|
|
1044
|
-
* @returns The {@link ColumnType} to encode it as
|
|
1045
|
-
*
|
|
1046
|
-
* @example
|
|
1047
|
-
* ```ts
|
|
1048
|
-
* valueType(true) // 'boolean'
|
|
1049
|
-
* valueType(9) // 'integer'
|
|
1050
|
-
* ```
|
|
1051
|
-
*/
|
|
1052
|
-
export declare function valueType(value: unknown): ColumnType;
|
|
1053
|
-
|
|
1054
949
|
export { }
|