@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.
- package/README.md +12 -8
- package/dist/src/browser/index.d.ts +95 -70
- package/dist/src/browser/index.js +115 -86
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +595 -410
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +712 -313
- package/dist/src/core/index.d.ts +712 -313
- package/dist/src/core/index.js +588 -409
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +236 -332
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +213 -223
- package/dist/src/server/index.d.ts +213 -223
- package/dist/src/server/index.js +230 -324
- package/dist/src/server/index.js.map +1 -1
- package/package.json +22 -18
|
@@ -1,17 +1,48 @@
|
|
|
1
|
-
import { ContractInterface } from '@orkestrel/contract';
|
|
2
|
-
import { ContractShape } from '@orkestrel/contract';
|
|
3
|
-
import { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
4
|
-
import { EmitterHooks } from '@orkestrel/emitter';
|
|
5
|
-
import { EmitterInterface } from '@orkestrel/emitter';
|
|
6
|
-
import { FieldPath } from '@orkestrel/contract';
|
|
7
|
-
import { Infer } from '@orkestrel/contract';
|
|
8
|
-
import { JSONSchema } from '@orkestrel/contract';
|
|
9
|
-
|
|
10
|
-
/**
|
|
1
|
+
import type { ContractInterface } from '@orkestrel/contract';
|
|
2
|
+
import type { ContractShape } from '@orkestrel/contract';
|
|
3
|
+
import type { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
4
|
+
import type { EmitterHooks } from '@orkestrel/emitter';
|
|
5
|
+
import type { EmitterInterface } from '@orkestrel/emitter';
|
|
6
|
+
import type { FieldPath } from '@orkestrel/contract';
|
|
7
|
+
import type { Infer } from '@orkestrel/contract';
|
|
8
|
+
import type { JSONSchema } from '@orkestrel/contract';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Represents the admission boundary a scoped operation enters before it runs.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* The one contract the root database context and a transaction scope both
|
|
15
|
+
* expose: `accepting` reports whether the boundary still admits work, and
|
|
16
|
+
* `track` enters an operation into the boundary's ledger so whoever stops the
|
|
17
|
+
* boundary can contain everything already accepted. A streamed read enters each
|
|
18
|
+
* continuation independently through the same pair, so an idle iterator never
|
|
19
|
+
* pins the boundary open.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import type { AdmissionInterface } from '@orkestrel/database'
|
|
24
|
+
*
|
|
25
|
+
* const boundary: AdmissionInterface = {
|
|
26
|
+
* accepting: true,
|
|
27
|
+
* track: (operation) => operation(),
|
|
28
|
+
* }
|
|
29
|
+
* await boundary.track(async () => 42) // 42
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare interface AdmissionInterface {
|
|
33
|
+
readonly accepting: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Enters one operation into the boundary's ledger so whoever stops the boundary
|
|
36
|
+
* contains everything already accepted.
|
|
37
|
+
*/
|
|
38
|
+
track<R>(operation: () => Promise<R>): Promise<R>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Names an aggregate computed over a numeric column. */
|
|
11
42
|
export declare type AggregateOperation = 'count' | 'sum' | 'average' | 'minimum' | 'maximum';
|
|
12
43
|
|
|
13
44
|
/**
|
|
14
|
-
*
|
|
45
|
+
* Applies a {@link QueryInput} to rows — filter, then sort, then page.
|
|
15
46
|
*
|
|
16
47
|
* @remarks
|
|
17
48
|
* The whole portable read pipeline in one place: conditions filter, `order`
|
|
@@ -26,12 +57,12 @@ export declare type AggregateOperation = 'count' | 'sum' | 'average' | 'minimum'
|
|
|
26
57
|
export declare function applyQuery(rows: readonly Row[], input?: QueryInput): readonly Row[];
|
|
27
58
|
|
|
28
59
|
/**
|
|
29
|
-
*
|
|
60
|
+
* Runs the full driver-conformance battery and collects every violation — the
|
|
30
61
|
* audit entry point for a driver author who wants a complete report rather
|
|
31
62
|
* than a single fail-fast throw.
|
|
32
63
|
*
|
|
33
64
|
* @remarks
|
|
34
|
-
* Drains {@link
|
|
65
|
+
* Drains {@link scanDriver} to completion: every phase runs regardless
|
|
35
66
|
* of earlier violations, so a driver breaking two independent invariants
|
|
36
67
|
* reports both. An empty array means the driver is fully conformant.
|
|
37
68
|
*
|
|
@@ -49,7 +80,7 @@ export declare function applyQuery(rows: readonly Row[], input?: QueryInput): re
|
|
|
49
80
|
export declare function auditDriver(factory: () => DriverInterface): Promise<readonly ConformanceFinding[]>;
|
|
50
81
|
|
|
51
82
|
/**
|
|
52
|
-
*
|
|
83
|
+
* Returns a fresh row whose primary column is authoritatively bound to its storage key.
|
|
53
84
|
*
|
|
54
85
|
* @param row - The caller row
|
|
55
86
|
* @param primary - The primary column
|
|
@@ -59,7 +90,7 @@ export declare function auditDriver(factory: () => DriverInterface): Promise<rea
|
|
|
59
90
|
export declare function bindRowKey(row: Row, primary: string, key: Key): Row;
|
|
60
91
|
|
|
61
92
|
/**
|
|
62
|
-
*
|
|
93
|
+
* Throws when an {@link OperationOptions.signal | AbortSignal} has fired — the shared
|
|
63
94
|
* abort gate checked at operation boundaries and between streamed rows.
|
|
64
95
|
*
|
|
65
96
|
* @remarks
|
|
@@ -86,7 +117,12 @@ export declare function bindRowKey(row: Row, primary: string, key: Key): Row;
|
|
|
86
117
|
export declare function checkAbort(signal: AbortSignal | undefined): void;
|
|
87
118
|
|
|
88
119
|
/**
|
|
89
|
-
*
|
|
120
|
+
* Clones unknown driver metadata into a distinct deeply frozen snapshot.
|
|
121
|
+
*
|
|
122
|
+
* @remarks
|
|
123
|
+
* The clone is validated as {@link DriverMetadata} before it is returned, so a
|
|
124
|
+
* malformed or hostile value throws a `VALIDATION` {@link DatabaseError} at
|
|
125
|
+
* `context.path === 'metadata'` rather than surfacing a raw Contract or caller error.
|
|
90
126
|
*
|
|
91
127
|
* @param value - Unknown metadata
|
|
92
128
|
* @returns Owned driver metadata
|
|
@@ -94,7 +130,12 @@ export declare function checkAbort(signal: AbortSignal | undefined): void;
|
|
|
94
130
|
export declare function cloneDriverMetadata(value: unknown): DriverMetadata;
|
|
95
131
|
|
|
96
132
|
/**
|
|
97
|
-
*
|
|
133
|
+
* Clones unknown driver schema into a distinct deeply frozen snapshot.
|
|
134
|
+
*
|
|
135
|
+
* @remarks
|
|
136
|
+
* The clone is validated as a table-schema collection before it is returned, so a
|
|
137
|
+
* malformed or hostile value throws a `VALIDATION` {@link DatabaseError} at
|
|
138
|
+
* `context.path === 'schema'` rather than surfacing a raw Contract or caller error.
|
|
98
139
|
*
|
|
99
140
|
* @param value - Unknown table schema collection
|
|
100
141
|
* @returns Owned driver schema
|
|
@@ -102,7 +143,12 @@ export declare function cloneDriverMetadata(value: unknown): DriverMetadata;
|
|
|
102
143
|
export declare function cloneDriverSchema(value: unknown): readonly TableSchema[];
|
|
103
144
|
|
|
104
145
|
/**
|
|
105
|
-
*
|
|
146
|
+
* Clones unknown migration input into a distinct deeply frozen snapshot.
|
|
147
|
+
*
|
|
148
|
+
* @remarks
|
|
149
|
+
* The clone is validated as a {@link MigrationInput} before it is returned, so a
|
|
150
|
+
* malformed or hostile value throws a `VALIDATION` {@link DatabaseError} at
|
|
151
|
+
* `context.path === 'migration'` rather than surfacing a raw Contract or caller error.
|
|
106
152
|
*
|
|
107
153
|
* @param value - Unknown migration input
|
|
108
154
|
* @returns Owned migration input
|
|
@@ -110,7 +156,7 @@ export declare function cloneDriverSchema(value: unknown): readonly TableSchema[
|
|
|
110
156
|
export declare function cloneMigrationInput(value: unknown): MigrationInput;
|
|
111
157
|
|
|
112
158
|
/**
|
|
113
|
-
*
|
|
159
|
+
* Represents one table's columns — a map of column name to its value {@link ContractShape}.
|
|
114
160
|
*
|
|
115
161
|
* @remarks
|
|
116
162
|
* This is exactly the property map an `objectShape` takes. A table row is always
|
|
@@ -122,7 +168,7 @@ export declare function cloneMigrationInput(value: unknown): MigrationInput;
|
|
|
122
168
|
export declare type ColumnMap = Readonly<Record<string, ContractShape>>;
|
|
123
169
|
|
|
124
170
|
/**
|
|
125
|
-
*
|
|
171
|
+
* Represents one column of a {@link TableSchema} — its name, portable {@link ColumnStorage}, and
|
|
126
172
|
* whether it independently accepts absence (`optional`) and explicit `null`
|
|
127
173
|
* (`nullable`).
|
|
128
174
|
*/
|
|
@@ -134,7 +180,7 @@ export declare interface ColumnSchema {
|
|
|
134
180
|
}
|
|
135
181
|
|
|
136
182
|
/**
|
|
137
|
-
*
|
|
183
|
+
* Names a portable storage type for a column — the backend maps it to its native type
|
|
138
184
|
* (SQLite affinity, an IndexedDB value). Derived from a column's `ContractShape`
|
|
139
185
|
* by `shapeToColumnStorage`; `json` covers object/array/union/raw values a backend stores
|
|
140
186
|
* as JSON text and can `json_extract` for nested-field queries.
|
|
@@ -142,8 +188,8 @@ export declare interface ColumnSchema {
|
|
|
142
188
|
export declare type ColumnStorage = 'text' | 'integer' | 'real' | 'boolean' | 'json' | 'blob';
|
|
143
189
|
|
|
144
190
|
/**
|
|
145
|
-
*
|
|
146
|
-
* range operators.
|
|
191
|
+
* Compares two arbitrary values under one total order — the comparator behind
|
|
192
|
+
* sorting and the range operators.
|
|
147
193
|
*
|
|
148
194
|
* @remarks
|
|
149
195
|
* Values of different types order by a fixed type rank (`undefined` < `null` <
|
|
@@ -158,7 +204,7 @@ export declare type ColumnStorage = 'text' | 'integer' | 'real' | 'boolean' | 'j
|
|
|
158
204
|
export declare function compareValues(left: unknown, right: unknown): number;
|
|
159
205
|
|
|
160
206
|
/**
|
|
161
|
-
*
|
|
207
|
+
* Computes an aggregate over a column across rows.
|
|
162
208
|
*
|
|
163
209
|
* @remarks
|
|
164
210
|
* `count` returns the row count. The numeric aggregates coerce each cell with
|
|
@@ -174,14 +220,14 @@ export declare function compareValues(left: unknown, right: unknown): number;
|
|
|
174
220
|
export declare function computeAggregate(rows: readonly unknown[], operation: AggregateOperation, column: FieldPath): number | undefined;
|
|
175
221
|
|
|
176
222
|
/**
|
|
177
|
-
*
|
|
223
|
+
* Represents one compiled WHERE condition.
|
|
178
224
|
*
|
|
179
225
|
* @remarks
|
|
180
226
|
* `values` carries the operands the operator needs — none for `absent` /
|
|
181
227
|
* `present`, one for most, two for `between`, a list for `any` / `none`.
|
|
182
228
|
* `connector` folds this condition into the accumulated result left-to-right;
|
|
183
229
|
* the first condition's connector seeds the fold and is otherwise ignored.
|
|
184
|
-
* `column` is a {@link FieldPath}: a single string is
|
|
230
|
+
* `column` is a {@link FieldPath}: a single string is one column (never split on
|
|
185
231
|
* `.`), an array descends into a nested (object/`json`) value.
|
|
186
232
|
*/
|
|
187
233
|
export declare interface Condition {
|
|
@@ -191,11 +237,11 @@ export declare interface Condition {
|
|
|
191
237
|
readonly connector: ConditionConnector;
|
|
192
238
|
}
|
|
193
239
|
|
|
194
|
-
/**
|
|
240
|
+
/** Names how a {@link Condition} joins to the running result of the conditions before it. */
|
|
195
241
|
export declare type ConditionConnector = 'and' | 'or';
|
|
196
242
|
|
|
197
243
|
/**
|
|
198
|
-
*
|
|
244
|
+
* Represents a WHERE operator — the comparison a single {@link Condition} applies.
|
|
199
245
|
*
|
|
200
246
|
* @remarks
|
|
201
247
|
* Each maps to a SQL operator and an IndexedDB read strategy (a key range where
|
|
@@ -205,12 +251,46 @@ export declare type ConditionConnector = 'and' | 'or';
|
|
|
205
251
|
export declare type ConditionOperator = 'equals' | 'not' | 'above' | 'below' | 'from' | 'to' | 'between' | 'like' | 'glob' | 'starts' | 'ends' | 'any' | 'none' | 'absent' | 'present';
|
|
206
252
|
|
|
207
253
|
/**
|
|
208
|
-
*
|
|
254
|
+
* Describes the `posts` table the driver-conformance battery opens — keyed by a non-`id`
|
|
255
|
+
* `slug` primary column.
|
|
256
|
+
*
|
|
257
|
+
* @remarks
|
|
258
|
+
* Pairs with {@link CONFORMANCE_USERS_SCHEMA} so one battery exercises both
|
|
259
|
+
* primary-key shapes: the default `id` and an explicit override.
|
|
260
|
+
*/
|
|
261
|
+
export declare const CONFORMANCE_POSTS_SCHEMA: TableSchema;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Holds the fixed `users` and `posts` schema every driver-conformance phase opens.
|
|
265
|
+
*
|
|
266
|
+
* @remarks
|
|
267
|
+
* Each phase mints a fresh driver and opens this exact schema, so a finding
|
|
268
|
+
* names a violated invariant rather than a setup difference between phases. The
|
|
269
|
+
* array and each schema in it are frozen, so a consumer holding it cannot change
|
|
270
|
+
* what a later phase opens.
|
|
271
|
+
*/
|
|
272
|
+
export declare const CONFORMANCE_SCHEMA: readonly TableSchema[];
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Describes the `users` table the driver-conformance battery opens — keyed by the default
|
|
276
|
+
* `id` primary column.
|
|
277
|
+
*
|
|
278
|
+
* @remarks
|
|
279
|
+
* `age` is optional and `meta` is a declared `json` column, so the battery's
|
|
280
|
+
* nested-round-trip phase is fair to a typed-column backend: a SQL driver
|
|
281
|
+
* persists only declared columns, while a schemaless backend ignores the
|
|
282
|
+
* declarations entirely.
|
|
283
|
+
*/
|
|
284
|
+
export declare const CONFORMANCE_USERS_SCHEMA: TableSchema;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Represents one violated invariant from the driver-conformance battery.
|
|
209
288
|
*
|
|
210
289
|
* @remarks
|
|
211
290
|
* Mirrors the payload shape of a `DatabaseError` `CONFORMANCE` `context` —
|
|
212
291
|
* `check` names the invariant, `message` describes the violation, and
|
|
213
|
-
* `context` carries the offending table / key / value that failed it.
|
|
292
|
+
* `context` carries the offending table / key / value that failed it. Yielded one at a
|
|
293
|
+
* time by {@link scanDriver} and collected whole by {@link auditDriver}.
|
|
214
294
|
*/
|
|
215
295
|
export declare interface ConformanceFinding {
|
|
216
296
|
readonly check: string;
|
|
@@ -219,17 +299,19 @@ export declare interface ConformanceFinding {
|
|
|
219
299
|
}
|
|
220
300
|
|
|
221
301
|
/**
|
|
222
|
-
*
|
|
302
|
+
* Runs the driver-conformance battery, throwing on the first violated
|
|
223
303
|
* invariant — the fail-fast entry point most callers (test setup, CI smoke
|
|
224
304
|
* checks) want.
|
|
225
305
|
*
|
|
226
306
|
* @remarks
|
|
227
|
-
*
|
|
228
|
-
* lazy,
|
|
229
|
-
*
|
|
307
|
+
* Consumes only the first value {@link scanDriver} yields: because that
|
|
308
|
+
* generator is lazy, every later phase never runs — true fail-fast, not
|
|
309
|
+
* merely "report only the first". The
|
|
230
310
|
* thrown error is byte-compatible with the historical shape: a
|
|
231
311
|
* `CONFORMANCE` {@link DatabaseError} whose `message` is the finding's
|
|
232
|
-
* `message` and whose `context` is `{ check, ...finding.context }`.
|
|
312
|
+
* `message` and whose `context` is `{ check, ...finding.context }`. The battery
|
|
313
|
+
* takes a driver factory and reports through a throw, so it binds no test
|
|
314
|
+
* framework and runs from any runner.
|
|
233
315
|
*
|
|
234
316
|
* @param factory - Mints a fresh, unopened driver instance (called once per phase)
|
|
235
317
|
* @returns Nothing — resolves once every phase has passed
|
|
@@ -245,40 +327,49 @@ export declare interface ConformanceFinding {
|
|
|
245
327
|
export declare function conformDriver(factory: () => DriverInterface): Promise<void>;
|
|
246
328
|
|
|
247
329
|
/**
|
|
248
|
-
*
|
|
330
|
+
* Creates a database over a driver and a declared `tables` schema.
|
|
249
331
|
*
|
|
250
332
|
* @remarks
|
|
251
333
|
* `tables` maps each name to its columns (a `column → shape` map); the database
|
|
252
334
|
* wraps each in an `objectShape`, so you never write `objectShape` at the table
|
|
253
335
|
* level. The `const` type parameter captures the literal names and columns, so
|
|
254
336
|
* `db.table('users')` is checked against the schema and typed by `Infer` of its
|
|
255
|
-
* columns — no annotations. Name a non-`id` primary-key column per table
|
|
256
|
-
* optional `primary` and `indexes` maps.
|
|
337
|
+
* columns — no annotations. Name a non-`id` primary-key column per table through
|
|
338
|
+
* the optional `primary` and `indexes` maps.
|
|
257
339
|
*
|
|
258
340
|
* @param options - The driver, `tables`, and optional `primary`, `indexes`,
|
|
259
341
|
* `name`, `generator`, `version`, and emitter hooks
|
|
260
342
|
* @returns A typed {@link DatabaseInterface}
|
|
261
343
|
*
|
|
262
|
-
* @example
|
|
344
|
+
* @example Create a database
|
|
263
345
|
* ```ts
|
|
264
346
|
* import { createDatabase, createMemoryDriver } from '@orkestrel/database'
|
|
265
347
|
* import { integerShape, stringShape } from '@orkestrel/contract'
|
|
266
348
|
*
|
|
267
349
|
* const db = createDatabase({
|
|
268
|
-
* driver: createMemoryDriver(),
|
|
350
|
+
* driver: createMemoryDriver(), // any DriverInterface — a persistent backend swaps in, same API
|
|
269
351
|
* tables: {
|
|
270
|
-
* users: { id: stringShape(), age: integerShape() },
|
|
352
|
+
* users: { id: stringShape(), name: stringShape(), age: integerShape() },
|
|
271
353
|
* posts: { slug: stringShape(), title: stringShape() },
|
|
272
354
|
* },
|
|
273
|
-
* primary: { posts: 'slug' },
|
|
355
|
+
* primary: { posts: 'slug' }, // non-`id` primary-key columns, per table
|
|
274
356
|
* })
|
|
275
|
-
*
|
|
357
|
+
*
|
|
358
|
+
* const users = db.table('users') // hold the handle; TableInterface<{ id; name; age }>
|
|
359
|
+
*
|
|
360
|
+
* await users.set({ id: 'u1', name: 'Ada', age: 36 }) // coerced + validated through the contract
|
|
361
|
+
* await users.get('u1') // typed { id; name; age } | undefined — narrowed, never `as`
|
|
362
|
+
* await users
|
|
363
|
+
* .query()
|
|
364
|
+
* .condition({ column: 'age', operator: 'from', values: [18], connector: 'and' })
|
|
365
|
+
* .order({ column: 'age', direction: 'descending' })
|
|
366
|
+
* .collect() // typed rows
|
|
276
367
|
* ```
|
|
277
368
|
*/
|
|
278
369
|
export declare function createDatabase<const T extends TableMap>(options: DatabaseOptions<T>): DatabaseInterface<T>;
|
|
279
370
|
|
|
280
371
|
/**
|
|
281
|
-
*
|
|
372
|
+
* Creates the in-memory reference {@link DriverInterface}.
|
|
282
373
|
*
|
|
283
374
|
* @remarks
|
|
284
375
|
* Backed by nested maps with no I/O — the same driver runs in a browser or on a
|
|
@@ -289,7 +380,7 @@ export declare function createDatabase<const T extends TableMap>(options: Databa
|
|
|
289
380
|
export declare function createMemoryDriver(): DriverInterface;
|
|
290
381
|
|
|
291
382
|
/**
|
|
292
|
-
*
|
|
383
|
+
* Walks a table's rows forward for bulk in-place mutation.
|
|
293
384
|
*
|
|
294
385
|
* @remarks
|
|
295
386
|
* Iterates a snapshot of the table's keys taken at creation; `update` and
|
|
@@ -304,20 +395,36 @@ export declare interface CursorInterface<T = Row> {
|
|
|
304
395
|
readonly value: T | undefined;
|
|
305
396
|
readonly index: number;
|
|
306
397
|
readonly done: boolean;
|
|
398
|
+
/**
|
|
399
|
+
* Advances to the next present row.
|
|
400
|
+
*/
|
|
307
401
|
next(): Promise<void>;
|
|
402
|
+
/**
|
|
403
|
+
* Merges changes into the row at the current position.
|
|
404
|
+
*/
|
|
308
405
|
update(changes: Partial<T>): Promise<void>;
|
|
406
|
+
/**
|
|
407
|
+
* Deletes the row at the current position.
|
|
408
|
+
*/
|
|
309
409
|
remove(): Promise<void>;
|
|
410
|
+
/**
|
|
411
|
+
* Closes the cursor terminally, so every later operation is a no-op.
|
|
412
|
+
*/
|
|
310
413
|
close(): void;
|
|
311
414
|
}
|
|
312
415
|
|
|
313
416
|
/**
|
|
314
|
-
*
|
|
417
|
+
* Exposes a typed view over one shared internal lifecycle and storage context.
|
|
315
418
|
*
|
|
316
419
|
* @remarks
|
|
317
420
|
* Each view owns only its table contracts, primary columns, indexes, and key
|
|
318
421
|
* generator. Imported views register their physical schemas with the same
|
|
319
422
|
* internal context before opening begins, so every view observes one driver,
|
|
320
423
|
* merged schema, emitter, status, transaction boundary, and terminal close.
|
|
424
|
+
*
|
|
425
|
+
* The view owns the driver and its declared `tables`, connects that driver lazily on
|
|
426
|
+
* first use, `import`s further tables and `export`s their portable definitions, and
|
|
427
|
+
* runs `transaction` scopes over the shared context.
|
|
321
428
|
*/
|
|
322
429
|
export declare class Database<T extends TableMap = TableMap> implements DatabaseInterface<T> {
|
|
323
430
|
#private;
|
|
@@ -335,7 +442,7 @@ export declare class Database<T extends TableMap = TableMap> implements Database
|
|
|
335
442
|
}
|
|
336
443
|
|
|
337
444
|
/**
|
|
338
|
-
*
|
|
445
|
+
* Represents an error thrown by the database layer.
|
|
339
446
|
*
|
|
340
447
|
* @remarks
|
|
341
448
|
* Carries a {@link DatabaseErrorCode} and an optional `context` bag naming the
|
|
@@ -346,7 +453,7 @@ export declare class Database<T extends TableMap = TableMap> implements Database
|
|
|
346
453
|
* `context`), an inapplicable {@link Migration} plan (`MIGRATION`), a
|
|
347
454
|
* driver that violates a {@link DriverInterface} invariant, thrown by the
|
|
348
455
|
* `conformDriver` helper (`CONFORMANCE`), and an unexpected infrastructure
|
|
349
|
-
* fault surfaced by a driver seam —
|
|
456
|
+
* fault surfaced by a driver seam — for example a filesystem failure while
|
|
350
457
|
* persisting (`DRIVER`) — as opposed to expected domain conditions, which
|
|
351
458
|
* keep their specific codes.
|
|
352
459
|
*/
|
|
@@ -356,48 +463,48 @@ export declare class DatabaseError extends Error {
|
|
|
356
463
|
constructor(code: DatabaseErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
|
|
357
464
|
}
|
|
358
465
|
|
|
359
|
-
/**
|
|
466
|
+
/** Names a machine-readable {@link DatabaseError} code. */
|
|
360
467
|
export declare type DatabaseErrorCode = 'CLOSED' | 'NOT_FOUND' | 'CONFLICT' | 'VALIDATION' | 'ABORTED' | 'MIGRATION' | 'CONFORMANCE' | 'DRIVER';
|
|
361
468
|
|
|
362
469
|
/**
|
|
363
|
-
*
|
|
470
|
+
* Describes the push observation surface of a {@link DatabaseInterface} — the
|
|
364
471
|
* connection + transaction lifecycle a fire-and-forget observer (logging, metrics,
|
|
365
472
|
* tracing, cache invalidation) subscribes to.
|
|
366
473
|
*
|
|
367
474
|
* @remarks
|
|
368
475
|
* Pure signals carrying no row data — these are the database-level (not per-row)
|
|
369
476
|
* moments, so a non-generic map stays lean (per-row writes are {@link TableEventMap}).
|
|
370
|
-
* Listener isolation is the emitter's
|
|
371
|
-
* listener throw is routed to the emitter's
|
|
477
|
+
* Listener isolation is the emitter's: every event is emitted directly and a
|
|
478
|
+
* listener throw is routed to the emitter's own `error` handler (the `error` option), never
|
|
372
479
|
* onto this domain map and never into the snapshot / commit / rollback flow — so a buggy
|
|
373
|
-
* observer can never reorder, throw into, or corrupt a transaction. Every emit sits
|
|
480
|
+
* observer can never reorder, throw into, or corrupt a transaction. Every emit sits after the
|
|
374
481
|
* relevant transition: `commit` only after the scope succeeds, `rollback` only after the
|
|
375
|
-
* rollback operation completes (it
|
|
482
|
+
* rollback operation completes (it observes the propagated scope error; that exact reason
|
|
376
483
|
* still propagates). A rollback failure propagates instead and emits no misleading
|
|
377
|
-
* `rollback` event. Subscribe
|
|
484
|
+
* `rollback` event. Subscribe through `database.emitter.on(...)`.
|
|
378
485
|
*
|
|
379
|
-
* Declared as a `type` alias (not `interface extends EventMap
|
|
486
|
+
* Declared as a `type` alias (not `interface extends EventMap` — `EventMap` is a
|
|
380
487
|
* `type` kind): a type-literal satisfies the `EventMap` constraint
|
|
381
488
|
* (`Record<string, readonly unknown[]>`) structurally, whereas an interface lacks the
|
|
382
489
|
* required index signature.
|
|
383
490
|
*/
|
|
384
491
|
export declare type DatabaseEventMap = {
|
|
385
|
-
/**
|
|
492
|
+
/** Signals that the driver connected (`open`, or the lazy first-use connect completed). */
|
|
386
493
|
readonly open: readonly [];
|
|
387
|
-
/**
|
|
494
|
+
/** Signals that the database was closed (the driver released). */
|
|
388
495
|
readonly close: readonly [];
|
|
389
|
-
/**
|
|
496
|
+
/** Signals that a transaction scope began after its native boundary or fallback snapshot was acquired. */
|
|
390
497
|
readonly transaction: readonly [];
|
|
391
|
-
/**
|
|
498
|
+
/** Signals that a transaction scope completed successfully (no rollback). */
|
|
392
499
|
readonly commit: readonly [];
|
|
393
|
-
/**
|
|
500
|
+
/** Signals that a transaction scope failed and rollback completed — the exact propagated scope error. */
|
|
394
501
|
readonly rollback: readonly [error: unknown];
|
|
395
|
-
/**
|
|
502
|
+
/** Signals that a {@link Migration} plan was applied through `migrate` — the applied plan. */
|
|
396
503
|
readonly migrate: readonly [migration: Migration];
|
|
397
504
|
};
|
|
398
505
|
|
|
399
506
|
/**
|
|
400
|
-
*
|
|
507
|
+
* Represents a database — the ergonomic entry point that owns the driver and its tables.
|
|
401
508
|
*
|
|
402
509
|
* @remarks
|
|
403
510
|
* A database is a typed view over a set of tables on one driver. Tables are
|
|
@@ -415,19 +522,43 @@ export declare interface DatabaseInterface<T extends TableMap = TableMap> {
|
|
|
415
522
|
readonly emitter: EmitterInterface<DatabaseEventMap>;
|
|
416
523
|
readonly name: string;
|
|
417
524
|
readonly status: DatabaseStatus;
|
|
525
|
+
/**
|
|
526
|
+
* Returns the typed handle for a declared table.
|
|
527
|
+
*/
|
|
418
528
|
table<K extends keyof T & string>(name: K): TableInterface<RowOf<T[K]>>;
|
|
529
|
+
/**
|
|
530
|
+
* Defines a further shape map of tables as a typed view over the same driver and storage.
|
|
531
|
+
*/
|
|
419
532
|
import<U extends TableMap>(tables: U, primary?: PrimaryMap): DatabaseInterface<U>;
|
|
533
|
+
/**
|
|
534
|
+
* Returns one portable {@link TableDefinition} per declared table.
|
|
535
|
+
*/
|
|
420
536
|
export(): Readonly<Record<string, TableDefinition>>;
|
|
537
|
+
/**
|
|
538
|
+
* Connects the driver eagerly, ahead of the lazy connect on first use.
|
|
539
|
+
*/
|
|
421
540
|
open(): Promise<void>;
|
|
541
|
+
/**
|
|
542
|
+
* Closes the database and releases its driver.
|
|
543
|
+
*/
|
|
422
544
|
close(): Promise<void>;
|
|
545
|
+
/**
|
|
546
|
+
* Runs a scope over a {@link DatabaseStorageInterface}, committing when the callback
|
|
547
|
+
* fulfills and rolling back when it rejects.
|
|
548
|
+
*
|
|
549
|
+
* @remarks
|
|
550
|
+
* A native driver transaction carries the scope where the driver offers one;
|
|
551
|
+
* otherwise the universal whole-store snapshot floor does. `options.signal` is
|
|
552
|
+
* checked once, at entry.
|
|
553
|
+
*/
|
|
423
554
|
transaction<R>(scope: (transaction: DatabaseStorageInterface<T>) => Promise<R>, options?: OperationOptions): Promise<R>;
|
|
424
555
|
/**
|
|
425
|
-
*
|
|
426
|
-
* schema (its `tables`, as configured)
|
|
427
|
-
* resulting plan through the driver's optional `migrate` hook, and
|
|
556
|
+
* Diffs a caller-supplied deployed schema against this database's declared
|
|
557
|
+
* schema (its `tables`, as configured) through `planMigration`, applies the
|
|
558
|
+
* resulting plan through the driver's optional `migrate` hook, and returns
|
|
428
559
|
* the applied plan.
|
|
429
560
|
*
|
|
430
|
-
* @param deployed - The schema
|
|
561
|
+
* @param deployed - The deployed schema, as {@link TableSchema}s
|
|
431
562
|
* @param options - Optional abort signal, checked at entry
|
|
432
563
|
* @returns The applied {@link Migration} plan
|
|
433
564
|
*
|
|
@@ -459,8 +590,8 @@ export declare interface DatabaseInterface<T extends TableMap = TableMap> {
|
|
|
459
590
|
* `primary` overrides the primary-key column per table ({@link DEFAULT_PRIMARY}
|
|
460
591
|
* otherwise); `indexes` declares secondary indexes per table (contracts don't
|
|
461
592
|
* express them) that flow into each derived {@link TableSchema}; `name` labels
|
|
462
|
-
* the database; `on` wires initial {@link DatabaseEventMap} listeners
|
|
463
|
-
* is the emitter's listener-error handler (
|
|
593
|
+
* the database; `on` wires initial {@link DatabaseEventMap} listeners; `error`
|
|
594
|
+
* is the emitter's listener-error handler (a listener throw routes here);
|
|
464
595
|
* `generator` is the authoritative key-generation override a table uses when a
|
|
465
596
|
* written row's primary is exactly `undefined`. When omitted, the table uses
|
|
466
597
|
* global `crypto.randomUUID()`; numeric primary keys require a custom generator.
|
|
@@ -468,7 +599,7 @@ export declare interface DatabaseInterface<T extends TableMap = TableMap> {
|
|
|
468
599
|
export declare interface DatabaseOptions<T extends TableMap = TableMap> {
|
|
469
600
|
readonly on?: EmitterHooks<DatabaseEventMap>;
|
|
470
601
|
/**
|
|
471
|
-
*
|
|
602
|
+
* Holds the listener-error handler shared by the database and every table emitter.
|
|
472
603
|
*
|
|
473
604
|
* @remarks
|
|
474
605
|
* Listener throws from root, imported, and transaction-scoped handles route
|
|
@@ -482,31 +613,31 @@ export declare interface DatabaseOptions<T extends TableMap = TableMap> {
|
|
|
482
613
|
readonly indexes?: IndexMap;
|
|
483
614
|
readonly name?: string;
|
|
484
615
|
/**
|
|
485
|
-
*
|
|
616
|
+
* Holds the authoritative key-generation override for a keyless write.
|
|
486
617
|
*
|
|
487
618
|
* @remarks
|
|
488
619
|
* Omit it to use global `crypto.randomUUID()`. A numeric primary requires a
|
|
489
620
|
* custom generator. Explicit primary values never invoke this function. A
|
|
490
|
-
* custom generator throw is `VALIDATION`; a host
|
|
491
|
-
*
|
|
492
|
-
*
|
|
621
|
+
* custom generator throw is `VALIDATION`; a host `crypto.randomUUID()` failure
|
|
622
|
+
* is `DRIVER`. An invalid returned key is `VALIDATION`; neither branch falls
|
|
623
|
+
* back or retries.
|
|
493
624
|
*/
|
|
494
625
|
readonly generator?: KeyFunction;
|
|
495
626
|
/**
|
|
496
|
-
*
|
|
627
|
+
* Holds the declared schema version.
|
|
497
628
|
*
|
|
498
629
|
* @remarks
|
|
499
|
-
* Only meaningful when the driver implements
|
|
630
|
+
* Only meaningful when the driver implements both {@link DriverInterface.metadata}
|
|
500
631
|
* and {@link DriverInterface.stamp} (a versioning driver); unset, or a
|
|
501
632
|
* non-versioning driver, leaves `open()` unchanged from today's behavior.
|
|
502
|
-
* When set and the driver versions, `open()` reconciles against the
|
|
503
|
-
*
|
|
633
|
+
* When set and the driver versions, `open()` reconciles against the driver's
|
|
634
|
+
* persisted {@link DriverMetadata}:
|
|
504
635
|
* - **Fresh store** (`metadata()` returns `undefined` after the durable
|
|
505
636
|
* driver proves absence) — no migration is possible (there is nothing
|
|
506
|
-
* deployed to diff against), so `open()`
|
|
507
|
-
*
|
|
637
|
+
* deployed to diff against), so `open()` stamps `{ version, schema }` for
|
|
638
|
+
* next time.
|
|
508
639
|
* - **Stored version < `version`** — `planMigration(stored.schema, declared
|
|
509
|
-
* schema)` computes the upgrade plan, applied
|
|
640
|
+
* schema)` computes the upgrade plan, applied through the driver's optional
|
|
510
641
|
* `migrate` hook. If `migrate` is absent and the plan is non-empty,
|
|
511
642
|
* `open()` throws `DatabaseError` `MIGRATION`. On success, `open()`
|
|
512
643
|
* `stamp`s the new `{ version, schema }` and emits the `migrate` event.
|
|
@@ -524,11 +655,11 @@ export declare interface DatabaseOptions<T extends TableMap = TableMap> {
|
|
|
524
655
|
readonly version?: number;
|
|
525
656
|
}
|
|
526
657
|
|
|
527
|
-
/**
|
|
658
|
+
/** Names the lifecycle state of a {@link DatabaseInterface}. */
|
|
528
659
|
export declare type DatabaseStatus = 'idle' | 'open' | 'closed';
|
|
529
660
|
|
|
530
661
|
/**
|
|
531
|
-
*
|
|
662
|
+
* Represents a database view valid only inside one {@link DatabaseInterface.transaction}
|
|
532
663
|
* scope.
|
|
533
664
|
*
|
|
534
665
|
* @remarks
|
|
@@ -538,11 +669,17 @@ export declare type DatabaseStatus = 'idle' | 'open' | 'closed';
|
|
|
538
669
|
* after the scope settles.
|
|
539
670
|
*/
|
|
540
671
|
export declare interface DatabaseStorageInterface<T extends TableMap = TableMap> {
|
|
672
|
+
/**
|
|
673
|
+
* Returns a table bound to the active transaction scope.
|
|
674
|
+
*
|
|
675
|
+
* @remarks
|
|
676
|
+
* The returned table throws `CONFLICT` for work started after the scope settles.
|
|
677
|
+
*/
|
|
541
678
|
table<K extends keyof T & string>(name: K): TableInterface<RowOf<T[K]>>;
|
|
542
679
|
}
|
|
543
680
|
|
|
544
681
|
/**
|
|
545
|
-
*
|
|
682
|
+
* Supplies the primary-key column, `'id'`, assumed when {@link PrimaryMap} does not name one.
|
|
546
683
|
*
|
|
547
684
|
* @remarks
|
|
548
685
|
* `id` is the convention IndexedDB (`keyPath: 'id'`) and SQL (`id` / rowid) both
|
|
@@ -551,93 +688,43 @@ export declare interface DatabaseStorageInterface<T extends TableMap = TableMap>
|
|
|
551
688
|
export declare const DEFAULT_PRIMARY = "id";
|
|
552
689
|
|
|
553
690
|
/**
|
|
554
|
-
*
|
|
555
|
-
* per phase, yielding one {@link ConformanceFinding} per violated invariant —
|
|
556
|
-
* the shared invariant suite every backend (in-memory, SQLite, IndexedDB)
|
|
557
|
-
* must uphold to be a drop-in {@link DriverInterface}.
|
|
691
|
+
* Declares the storage primitive every backend implements — the whole of the bridge.
|
|
558
692
|
*
|
|
559
693
|
* @remarks
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
* `
|
|
572
|
-
* `keys`/`scan` yield in ascending key order; `clear` empties only its target
|
|
573
|
-
* table; `snapshot`'s rollback thunk restores pre-snapshot state, including a
|
|
574
|
-
* NESTED field mutated in place on a read-back row between capture and
|
|
575
|
-
* restore; a scoped `snapshot(['users'])` rolls back only the named table,
|
|
576
|
-
* leaving a concurrent mutation to another table intact; a
|
|
577
|
-
* non-`id` primary key (`posts.slug`) round-trips; a nested-object row
|
|
578
|
-
* round-trips structurally (via {@link equalsValue}). The optional surface is
|
|
579
|
-
* presence-gated: when `migrate` exists, a `column.remove` plan strips the
|
|
580
|
-
* column from stored rows and a plan referencing an unknown table throws
|
|
581
|
-
* `DatabaseError` `MIGRATION`; when `stream` exists, it yields only
|
|
582
|
-
* condition-matching rows and honors `offset`/`limit`; when `transaction`
|
|
583
|
-
* exists, `commit` persists and `rollback` restores; when both `metadata` and
|
|
584
|
-
* `stamp` exist, a fresh store's `metadata()` is `undefined`, and after
|
|
585
|
-
* `stamp({ version, schema })`, `metadata()` returns the exact stamped value.
|
|
586
|
-
*
|
|
587
|
-
* Each phase runs within a `try`/`catch`: an EXPECTED mismatch yields a
|
|
588
|
-
* finding built from the assertion, while an UNEXPECTED throw (a driver
|
|
589
|
-
* crash mid-phase) is caught and yielded as a finding too, naming the phase
|
|
590
|
-
* as `check` and carrying the caught error in `context.error` — a broken
|
|
591
|
-
* driver can never escape the battery as an unhandled rejection. Within a
|
|
592
|
-
* phase, the FIRST violated assertion yields and the phase stops (matching
|
|
593
|
-
* the historical fail-fast shape at phase granularity); the generator then
|
|
594
|
-
* moves on to the next phase regardless. Because this is a **generator**,
|
|
595
|
-
* consuming only the first yielded value reproduces true fail-fast (later
|
|
596
|
-
* phases never run) — that is exactly what {@link conformDriver} does.
|
|
597
|
-
*
|
|
598
|
-
* @param factory - Mints a fresh, unopened driver instance (called once per phase)
|
|
599
|
-
* @yields One {@link ConformanceFinding} per violated invariant, in phase order
|
|
600
|
-
*
|
|
601
|
-
* @example
|
|
602
|
-
* ```ts
|
|
603
|
-
* import { createMemoryDriver, driverFindings } from '@orkestrel/database'
|
|
604
|
-
*
|
|
605
|
-
* for await (const finding of driverFindings(() => createMemoryDriver())) {
|
|
606
|
-
* console.log(finding.check, finding.message)
|
|
607
|
-
* }
|
|
608
|
-
* ```
|
|
609
|
-
*/
|
|
610
|
-
export declare function driverFindings(factory: () => DriverInterface): AsyncIterable<ConformanceFinding>;
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* The storage primitive every backend implements — the whole of the bridge.
|
|
614
|
-
*
|
|
615
|
-
* @remarks
|
|
616
|
-
* The REQUIRED surface is deliberately minimal: keyed read / write / atomic
|
|
617
|
-
* insert / delete, an ordered `scan`, a key listing, and a `snapshot` that backs
|
|
618
|
-
* transactions — the irreducible primitive. There is **no** required query,
|
|
619
|
-
* count, or aggregate
|
|
620
|
-
* here: all of that is one query engine in the core (`helpers.ts`) running over
|
|
621
|
-
* `scan`, so a new backend implements a handful of tiny methods rather than
|
|
622
|
-
* re-deriving WHERE compilation. `open` now receives a derived
|
|
623
|
-
* {@link TableSchema}`[]` (columns, types, primary, indexes) so a native backend
|
|
624
|
-
* can build real tables and indexes; a scan-only backend reads only `name`. The
|
|
625
|
-
* optional `records?` / `aggregate?` are native overrides the engine
|
|
626
|
-
* falls back from (AGENTS §21). The API is async (Promises) because IndexedDB is; synchronous
|
|
627
|
-
* backends resolve immediately. Lookups that may miss return `undefined` /
|
|
628
|
-
* `false` rather than throwing (AGENTS §12). Metadata has the same ownership
|
|
694
|
+
* The required surface is deliberately minimal: keyed read / write / atomic
|
|
695
|
+
* insert / delete, an ordered `scan`, a key listing, and a `snapshot` that
|
|
696
|
+
* backs transactions — the irreducible primitive. There is **no** required
|
|
697
|
+
* query, count, or aggregate here: all of that is one query engine in the core
|
|
698
|
+
* (`helpers.ts`) running over `scan`, so a new backend implements a handful of
|
|
699
|
+
* tiny methods rather than re-deriving WHERE compilation. `open` receives a
|
|
700
|
+
* derived {@link TableSchema}`[]` (columns, types, primary, indexes) so a
|
|
701
|
+
* native backend can build real tables and indexes; a scan-only backend reads
|
|
702
|
+
* only `name`. The optional `records?` / `aggregate?` are native overrides the
|
|
703
|
+
* engine falls back from. The API is async (Promises) because IndexedDB is;
|
|
704
|
+
* synchronous backends resolve immediately. Lookups that may miss return
|
|
705
|
+
* `undefined` / `false` rather than throwing. Metadata has the same ownership
|
|
629
706
|
* boundary across every implementation: `stamp` and `migrate` snapshot
|
|
630
|
-
* {@link DriverMetadata} at entry, while `metadata` returns a distinct deeply
|
|
631
|
-
* snapshot. A durable driver returns `undefined` only when it proves the
|
|
707
|
+
* {@link DriverMetadata} at entry, while `metadata` returns a distinct deeply
|
|
708
|
+
* frozen snapshot. A durable driver returns `undefined` only when it proves the
|
|
632
709
|
* metadata record or durable store is absent. Existing unreadable or malformed
|
|
633
710
|
* durable state fails `open` / `metadata` closed; it is never treated as fresh,
|
|
634
711
|
* rewritten, or repaired automatically.
|
|
635
712
|
*/
|
|
636
713
|
export declare interface DriverInterface extends StorageInterface {
|
|
714
|
+
/**
|
|
715
|
+
* Readies the tables from a derived {@link TableSchema} list.
|
|
716
|
+
*
|
|
717
|
+
* @remarks
|
|
718
|
+
* A native backend builds real tables and indexes from the derived columns,
|
|
719
|
+
* types, primary, and index groups; a scan-only backend reads `name` alone.
|
|
720
|
+
*/
|
|
637
721
|
open(schema: readonly TableSchema[]): Promise<void>;
|
|
722
|
+
/**
|
|
723
|
+
* Releases the backend.
|
|
724
|
+
*/
|
|
638
725
|
close(): Promise<void>;
|
|
639
726
|
/**
|
|
640
|
-
*
|
|
727
|
+
* Captures table rows and returns a repeatable thunk that restores those rows —
|
|
641
728
|
* the primitive transactions are built on.
|
|
642
729
|
*
|
|
643
730
|
* @remarks
|
|
@@ -648,14 +735,61 @@ export declare interface DriverInterface extends StorageInterface {
|
|
|
648
735
|
*/
|
|
649
736
|
snapshot(tables?: readonly string[]): Promise<() => Promise<void>>;
|
|
650
737
|
/**
|
|
651
|
-
*
|
|
652
|
-
* rollback, release, and invalidation of the scoped capability.
|
|
738
|
+
* Opens a native transaction scope — an optional driver hook. The driver owns acquisition,
|
|
739
|
+
* commit or rollback, release, and invalidation of the scoped capability.
|
|
653
740
|
*/
|
|
654
741
|
transaction?<R>(scope: (storage: StorageInterface) => Promise<R>): Promise<R>;
|
|
655
742
|
}
|
|
656
743
|
|
|
657
744
|
/**
|
|
658
|
-
*
|
|
745
|
+
* Forms the internal continuation boundary for a root driver async iterator.
|
|
746
|
+
*
|
|
747
|
+
* @remarks
|
|
748
|
+
* A driver transaction can begin while a caller holds an idle root iterator.
|
|
749
|
+
* Every `next` therefore checks the driver's root-state guard immediately
|
|
750
|
+
* before and after advancing the source. A failed continuation terminalizes the
|
|
751
|
+
* iterator, discards any row produced before the post-advance guard failed, and
|
|
752
|
+
* attempts source cleanup exactly once.
|
|
753
|
+
*
|
|
754
|
+
* A driver implementing the published `DriverInterface` extension seam wraps its
|
|
755
|
+
* own source iterator in one so a root `scan` / `stream` cannot outlive the
|
|
756
|
+
* driver state it was opened against.
|
|
757
|
+
*
|
|
758
|
+
* @typeParam T - The value the wrapped source yields
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* ```ts
|
|
762
|
+
* import type { Row } from '@orkestrel/database'
|
|
763
|
+
* import { DatabaseError, DriverIterator } from '@orkestrel/database'
|
|
764
|
+
*
|
|
765
|
+
* // Inside a driver's `scan`, over its own row source and root-state guard.
|
|
766
|
+
* declare const rows: AsyncIterator<Row>
|
|
767
|
+
* declare const transacting: () => boolean
|
|
768
|
+
* const scan = new DriverIterator(rows, () => {
|
|
769
|
+
* if (transacting()) {
|
|
770
|
+
* throw new DatabaseError('CONFLICT', 'scan: a transaction is active')
|
|
771
|
+
* }
|
|
772
|
+
* })
|
|
773
|
+
* for await (const row of scan) row // one row at a time, guarded around each advance
|
|
774
|
+
* ```
|
|
775
|
+
*/
|
|
776
|
+
export declare class DriverIterator<T> implements AsyncIterableIterator<T> {
|
|
777
|
+
#private;
|
|
778
|
+
/**
|
|
779
|
+
* Wraps one source iterator in the continuation boundary.
|
|
780
|
+
*
|
|
781
|
+
* @param source - The driver's own row iterator, advanced once per `next`
|
|
782
|
+
* @param guard - The root-state check, run immediately before and after each advance; it throws to terminalize the iteration
|
|
783
|
+
*/
|
|
784
|
+
constructor(source: AsyncIterator<T>, guard: () => void);
|
|
785
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
|
|
786
|
+
next(): Promise<IteratorResult<T>>;
|
|
787
|
+
return(): Promise<IteratorResult<T>>;
|
|
788
|
+
throw(error?: unknown): Promise<IteratorResult<T>>;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Represents persisted schema metadata a versioning driver owns as an immutable snapshot.
|
|
659
793
|
*
|
|
660
794
|
* @remarks
|
|
661
795
|
* A driver snapshots metadata when it enters through `stamp` or a
|
|
@@ -671,15 +805,16 @@ export declare interface DriverMetadata {
|
|
|
671
805
|
}
|
|
672
806
|
|
|
673
807
|
/**
|
|
674
|
-
*
|
|
675
|
-
* checks and any test/fixture that needs "same data", not
|
|
808
|
+
* Compares two values structurally by SameValueZero leaves — the comparator
|
|
809
|
+
* behind conformance checks and any test/fixture that needs "same data", not
|
|
810
|
+
* "same reference".
|
|
676
811
|
*
|
|
677
812
|
* @remarks
|
|
678
813
|
* Primitives compare by SameValueZero (`NaN` equals itself; `+0` equals `-0`).
|
|
679
814
|
* Arrays compare by index (same length, every element `equalsValue`). Plain
|
|
680
|
-
* records (
|
|
681
|
-
*
|
|
682
|
-
* with a `equalsValue` value — so a key present with value `undefined` is
|
|
815
|
+
* records (through `isRecord`) compare by their own enumerable keys: same key
|
|
816
|
+
* count and, for every key in `left`, `right` has that key (`Object.hasOwn`)
|
|
817
|
+
* with a `equalsValue` value — so a key present with value `undefined` is not
|
|
683
818
|
* equal to that key being absent (both differ in `Object.keys` membership).
|
|
684
819
|
* Anything else (functions, class instances, mismatched shapes) falls through
|
|
685
820
|
* to `false`. Container pairs are tracked iteratively, so self-referential and
|
|
@@ -688,7 +823,7 @@ export declare interface DriverMetadata {
|
|
|
688
823
|
*
|
|
689
824
|
* @param left - The left value
|
|
690
825
|
* @param right - The right value
|
|
691
|
-
* @returns
|
|
826
|
+
* @returns True if `left` and `right` are structurally equal; false otherwise
|
|
692
827
|
*
|
|
693
828
|
* @example
|
|
694
829
|
* ```ts
|
|
@@ -700,7 +835,7 @@ export declare interface DriverMetadata {
|
|
|
700
835
|
export declare function equalsValue(left: unknown, right: unknown): boolean;
|
|
701
836
|
|
|
702
837
|
/**
|
|
703
|
-
*
|
|
838
|
+
* Reads a row's primary key from a column, when it is a usable {@link Key}.
|
|
704
839
|
*
|
|
705
840
|
* @param row - The row to read
|
|
706
841
|
* @param column - The primary-key column name
|
|
@@ -709,7 +844,7 @@ export declare function equalsValue(left: unknown, right: unknown): boolean;
|
|
|
709
844
|
export declare function extractKey(row: Row, column: string): Key | undefined;
|
|
710
845
|
|
|
711
846
|
/**
|
|
712
|
-
*
|
|
847
|
+
* Filters rows by a list of conditions — the shared basis for a table's count
|
|
713
848
|
* and aggregate paths (no sort/page, unlike {@link applyQuery}).
|
|
714
849
|
*
|
|
715
850
|
* @remarks
|
|
@@ -731,7 +866,29 @@ export declare function extractKey(row: Row, column: string): Key | undefined;
|
|
|
731
866
|
export declare function filterRows(rows: readonly Row[], conditions: readonly Condition[]): readonly Row[];
|
|
732
867
|
|
|
733
868
|
/**
|
|
734
|
-
*
|
|
869
|
+
* Reads one flat column's declaration out of a table schema.
|
|
870
|
+
*
|
|
871
|
+
* @remarks
|
|
872
|
+
* The single lookup behind every declared-column question — storage type,
|
|
873
|
+
* optionality, and nullability all come off the returned {@link ColumnSchema},
|
|
874
|
+
* so a caller that needs more than one of them reads them from one result. A
|
|
875
|
+
* nested {@link FieldPath} names no declared column, so resolve the path's head
|
|
876
|
+
* before calling. A schema that does not declare the column returns `undefined`.
|
|
877
|
+
*
|
|
878
|
+
* @param name - The flat column name
|
|
879
|
+
* @param schema - The table's schema
|
|
880
|
+
* @returns The column's {@link ColumnSchema}, or `undefined` when the schema does not declare it
|
|
881
|
+
*
|
|
882
|
+
* @example
|
|
883
|
+
* ```ts
|
|
884
|
+
* findColumn('age', schema)?.storage // 'integer'
|
|
885
|
+
* findColumn('absent', schema) // undefined
|
|
886
|
+
* ```
|
|
887
|
+
*/
|
|
888
|
+
export declare function findColumn(name: string, schema: TableSchema): ColumnSchema | undefined;
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Holds per-table secondary indexes — `{ [table]: groups }`, each group one
|
|
735
892
|
* (possibly compound) index of column names.
|
|
736
893
|
*
|
|
737
894
|
* @remarks
|
|
@@ -742,18 +899,22 @@ export declare function filterRows(rows: readonly Row[], conditions: readonly Co
|
|
|
742
899
|
export declare type IndexMap = Readonly<Record<string, ReadonlyArray<readonly string[]>>>;
|
|
743
900
|
|
|
744
901
|
/**
|
|
745
|
-
*
|
|
902
|
+
* Checks whether a value is a portable column schema.
|
|
903
|
+
*
|
|
904
|
+
* @remarks
|
|
905
|
+
* Total over any input: a hostile getter, a revoked proxy, or a cyclic value is
|
|
906
|
+
* contained as a non-match rather than a throw.
|
|
746
907
|
*
|
|
747
908
|
* @param value - The value to test
|
|
748
|
-
* @returns
|
|
909
|
+
* @returns True if `value` is a complete {@link ColumnSchema}; false otherwise
|
|
749
910
|
*/
|
|
750
911
|
export declare function isColumnSchema(value: unknown): value is ColumnSchema;
|
|
751
912
|
|
|
752
913
|
/**
|
|
753
|
-
*
|
|
914
|
+
* Narrows an unknown caught value to a {@link DatabaseError}.
|
|
754
915
|
*
|
|
755
916
|
* @param value - The value to test (typically a `catch` binding)
|
|
756
|
-
* @returns
|
|
917
|
+
* @returns True if `value` is a {@link DatabaseError}; false otherwise
|
|
757
918
|
*
|
|
758
919
|
* @example
|
|
759
920
|
* ```ts
|
|
@@ -767,63 +928,89 @@ export declare function isColumnSchema(value: unknown): value is ColumnSchema;
|
|
|
767
928
|
export declare function isDatabaseError(value: unknown): value is DatabaseError;
|
|
768
929
|
|
|
769
930
|
/**
|
|
770
|
-
*
|
|
931
|
+
* Checks whether a value is persisted driver metadata.
|
|
932
|
+
*
|
|
933
|
+
* @remarks
|
|
934
|
+
* The boundary check a versioning driver's `metadata()` narrows a stored or
|
|
935
|
+
* deserialized record through, so no call site needs an assertion. Total over any
|
|
936
|
+
* input: a hostile getter, a revoked proxy, or a cyclic value is contained as a
|
|
937
|
+
* non-match rather than a throw.
|
|
771
938
|
*
|
|
772
939
|
* @param value - The value to test
|
|
773
|
-
* @returns
|
|
940
|
+
* @returns True if `value` is complete {@link DriverMetadata}; false otherwise
|
|
774
941
|
*/
|
|
775
942
|
export declare function isDriverMetadata(value: unknown): value is DriverMetadata;
|
|
776
943
|
|
|
777
944
|
/**
|
|
778
|
-
*
|
|
945
|
+
* Checks whether a value is a complete portable driver schema.
|
|
946
|
+
*
|
|
947
|
+
* @remarks
|
|
948
|
+
* Total over any input: a hostile getter, a revoked proxy, or a cyclic value is
|
|
949
|
+
* contained as a non-match rather than a throw.
|
|
779
950
|
*
|
|
780
951
|
* @param value - The value to test
|
|
781
|
-
* @returns
|
|
952
|
+
* @returns True if `value` is a table-schema collection with unique table names; false otherwise
|
|
782
953
|
*/
|
|
783
954
|
export declare function isDriverSchema(value: unknown): value is readonly TableSchema[];
|
|
784
955
|
|
|
785
956
|
/**
|
|
786
|
-
*
|
|
957
|
+
* Checks whether a value is a usable database key.
|
|
787
958
|
*
|
|
788
959
|
* @param value - The value to test
|
|
789
|
-
* @returns
|
|
960
|
+
* @returns True if `value` is a string or a finite number; false otherwise
|
|
790
961
|
*/
|
|
791
962
|
export declare function isKey(value: unknown): value is Key;
|
|
792
963
|
|
|
793
964
|
/**
|
|
794
|
-
*
|
|
965
|
+
* Checks whether a value is an ordered migration plan.
|
|
966
|
+
*
|
|
967
|
+
* @remarks
|
|
968
|
+
* Total over any input: a hostile getter, a revoked proxy, or a cyclic value is
|
|
969
|
+
* contained as a non-match rather than a throw.
|
|
795
970
|
*
|
|
796
971
|
* @param value - The value to test
|
|
797
|
-
* @returns
|
|
972
|
+
* @returns True if `value` is a complete {@link Migration}; false otherwise
|
|
798
973
|
*/
|
|
799
974
|
export declare function isMigration(value: unknown): value is Migration;
|
|
800
975
|
|
|
801
976
|
/**
|
|
802
|
-
*
|
|
977
|
+
* Checks whether a value is one atomic migration request.
|
|
978
|
+
*
|
|
979
|
+
* @remarks
|
|
980
|
+
* Total over any input: a hostile getter, a revoked proxy, or a cyclic value is
|
|
981
|
+
* contained as a non-match rather than a throw.
|
|
803
982
|
*
|
|
804
983
|
* @param value - The value to test
|
|
805
|
-
* @returns
|
|
984
|
+
* @returns True if `value` is a complete {@link MigrationInput}; false otherwise
|
|
806
985
|
*/
|
|
807
986
|
export declare function isMigrationInput(value: unknown): value is MigrationInput;
|
|
808
987
|
|
|
809
988
|
/**
|
|
810
|
-
*
|
|
989
|
+
* Checks whether a value is one ordered migration step.
|
|
990
|
+
*
|
|
991
|
+
* @remarks
|
|
992
|
+
* Total over any input: a hostile getter, a revoked proxy, or a cyclic value is
|
|
993
|
+
* contained as a non-match rather than a throw.
|
|
811
994
|
*
|
|
812
995
|
* @param value - The value to test
|
|
813
|
-
* @returns
|
|
996
|
+
* @returns True if `value` is a complete {@link MigrationStep}; false otherwise
|
|
814
997
|
*/
|
|
815
998
|
export declare function isMigrationStep(value: unknown): value is MigrationStep;
|
|
816
999
|
|
|
817
1000
|
/**
|
|
818
|
-
*
|
|
1001
|
+
* Checks whether a value is a portable table schema.
|
|
1002
|
+
*
|
|
1003
|
+
* @remarks
|
|
1004
|
+
* Total over any input: a hostile getter, a revoked proxy, or a cyclic value is
|
|
1005
|
+
* contained as a non-match rather than a throw.
|
|
819
1006
|
*
|
|
820
1007
|
* @param value - The value to test
|
|
821
|
-
* @returns
|
|
1008
|
+
* @returns True if `value` is a complete {@link TableSchema}; false otherwise
|
|
822
1009
|
*/
|
|
823
1010
|
export declare function isTableSchema(value: unknown): value is TableSchema;
|
|
824
1011
|
|
|
825
1012
|
/**
|
|
826
|
-
*
|
|
1013
|
+
* Represents a primary key — the value identifying a row within its table.
|
|
827
1014
|
*
|
|
828
1015
|
* @remarks
|
|
829
1016
|
* `string | number` is the intersection of what IndexedDB key ranges and SQL
|
|
@@ -833,7 +1020,7 @@ export declare function isTableSchema(value: unknown): value is TableSchema;
|
|
|
833
1020
|
export declare type Key = string | number;
|
|
834
1021
|
|
|
835
1022
|
/**
|
|
836
|
-
*
|
|
1023
|
+
* Represents a key-generating function.
|
|
837
1024
|
*
|
|
838
1025
|
* @remarks
|
|
839
1026
|
* Supplied through {@link DatabaseOptions.generator} as an authoritative
|
|
@@ -844,56 +1031,75 @@ export declare type Key = string | number;
|
|
|
844
1031
|
export declare type KeyFunction = () => Key;
|
|
845
1032
|
|
|
846
1033
|
/**
|
|
847
|
-
*
|
|
1034
|
+
* Evaluates one {@link Condition} against a row — the per-operator predicate.
|
|
848
1035
|
*
|
|
849
1036
|
* @remarks
|
|
850
1037
|
* Reads the condition's column — a `FieldPath`, resolved with `resolveField` (a
|
|
851
1038
|
* string is one column; an array descends a nested value) — and applies the
|
|
852
1039
|
* operator. Range operators (`above` / `below` / `from` / `to` / `between`) use
|
|
853
1040
|
* {@link compareValues}, the total order; the equality family (`equals` / `not`
|
|
854
|
-
* / `any` / `none`) uses {@link equalsValue} —
|
|
1041
|
+
* / `any` / `none`) uses {@link equalsValue} — structural equality, not the total
|
|
855
1042
|
* order's rank-5-collapses-all-objects behavior, so `equals` on an object/array
|
|
856
1043
|
* operand only matches a structurally-equal value, never every row holding any
|
|
857
1044
|
* object. This is a semantics change from ranking: `equalsValue` is SameValueZero
|
|
858
1045
|
* on leaves, so `NaN` now equals `NaN` under `equals` / `any` (it never matched
|
|
859
1046
|
* anything under the old rank-based comparison). `like` / `glob` / `starts` /
|
|
860
1047
|
* `ends` match only strings; `absent` / `present` test nullishness. Total — a
|
|
861
|
-
* type mismatch is
|
|
1048
|
+
* type mismatch is a non-match.
|
|
862
1049
|
*
|
|
863
1050
|
* @param row - The row to test
|
|
864
1051
|
* @param condition - The condition to apply
|
|
865
|
-
* @returns
|
|
1052
|
+
* @returns True if the row satisfies the condition; false otherwise
|
|
866
1053
|
*/
|
|
867
1054
|
export declare function matchesCondition(row: Row, condition: Condition): boolean;
|
|
868
1055
|
|
|
869
1056
|
/**
|
|
870
|
-
*
|
|
1057
|
+
* Matches a value against a `GLOB` pattern, preserving case.
|
|
871
1058
|
*
|
|
872
1059
|
* @remarks
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
*
|
|
1060
|
+
* `*` matches any run of characters (including none) and `?` matches exactly one
|
|
1061
|
+
* character; every other pattern character matches itself literally, so a
|
|
1062
|
+
* character class such as `[a-z]` is not interpreted. Runs on
|
|
1063
|
+
* {@link matchesWildcardPattern}, so the match is linear in the value length and
|
|
1064
|
+
* the pattern is capped at {@link MAX_PATTERN_LENGTH}.
|
|
878
1065
|
*
|
|
879
|
-
* @param value - The
|
|
880
|
-
* @param
|
|
881
|
-
* @returns
|
|
1066
|
+
* @param value - The value to test
|
|
1067
|
+
* @param pattern - The `GLOB` pattern
|
|
1068
|
+
* @returns True if `value` matches `pattern` case-sensitively; false otherwise
|
|
1069
|
+
* @throws A `VALIDATION` {@link DatabaseError} when `pattern` exceeds {@link MAX_PATTERN_LENGTH}
|
|
882
1070
|
*
|
|
883
1071
|
* @example
|
|
884
1072
|
* ```ts
|
|
885
|
-
*
|
|
886
|
-
*
|
|
1073
|
+
* matchesGlobPattern('hello', 'h*o') // true — `*` spans any run
|
|
1074
|
+
* matchesGlobPattern('Hello', 'h*o') // false — `GLOB` is case-sensitive
|
|
887
1075
|
* ```
|
|
888
1076
|
*/
|
|
889
|
-
export declare function matchesFuzzy(value: string, query: string): boolean;
|
|
890
|
-
|
|
891
1077
|
export declare function matchesGlobPattern(value: string, pattern: string): boolean;
|
|
892
1078
|
|
|
1079
|
+
/**
|
|
1080
|
+
* Matches a value against a SQL `LIKE` pattern, folding case.
|
|
1081
|
+
*
|
|
1082
|
+
* @remarks
|
|
1083
|
+
* `%` matches any run of characters (including none) and `_` matches exactly one
|
|
1084
|
+
* character; every other pattern character matches itself literally. Runs on
|
|
1085
|
+
* {@link matchesWildcardPattern}, so the match is linear in the value length and
|
|
1086
|
+
* the pattern is capped at {@link MAX_PATTERN_LENGTH}.
|
|
1087
|
+
*
|
|
1088
|
+
* @param value - The value to test
|
|
1089
|
+
* @param pattern - The `LIKE` pattern
|
|
1090
|
+
* @returns True if `value` matches `pattern` under case folding; false otherwise
|
|
1091
|
+
* @throws A `VALIDATION` {@link DatabaseError} when `pattern` exceeds {@link MAX_PATTERN_LENGTH}
|
|
1092
|
+
*
|
|
1093
|
+
* @example
|
|
1094
|
+
* ```ts
|
|
1095
|
+
* matchesLikePattern('Hello', 'h%o') // true — `%` spans any run, and case folds
|
|
1096
|
+
* matchesLikePattern('Hello', 'h_llo') // true — `_` matches exactly one character
|
|
1097
|
+
* ```
|
|
1098
|
+
*/
|
|
893
1099
|
export declare function matchesLikePattern(value: string, pattern: string): boolean;
|
|
894
1100
|
|
|
895
1101
|
/**
|
|
896
|
-
*
|
|
1102
|
+
* Folds a row through a list of conditions, joining each by its connector.
|
|
897
1103
|
*
|
|
898
1104
|
* @remarks
|
|
899
1105
|
* Evaluated left-to-right: the first condition seeds the result, and each later
|
|
@@ -903,21 +1109,21 @@ export declare function matchesLikePattern(value: string, pattern: string): bool
|
|
|
903
1109
|
*
|
|
904
1110
|
* @param row - The row to test
|
|
905
1111
|
* @param conditions - The conditions to fold
|
|
906
|
-
* @returns
|
|
1112
|
+
* @returns True if the row satisfies the combined conditions; false otherwise
|
|
907
1113
|
*/
|
|
908
1114
|
export declare function matchesQuery(row: Row, conditions: readonly Condition[]): boolean;
|
|
909
1115
|
|
|
910
1116
|
/**
|
|
911
|
-
*
|
|
1117
|
+
* Matches a value against a wildcard pattern in linear time — the shared, ReDoS-safe
|
|
912
1118
|
* engine behind {@link matchesLikePattern} and {@link matchesGlobPattern}.
|
|
913
1119
|
*
|
|
914
1120
|
* @remarks
|
|
915
|
-
* A backtracking RegExp (`a%b%c` → `^a.*b.*c$`) is
|
|
1121
|
+
* A backtracking RegExp (`a%b%c` → `^a.*b.*c$`) is catastrophic on a hostile pattern:
|
|
916
1122
|
* `.*` segments separated by literals, matched against a long non-matching input, blow
|
|
917
|
-
* up super-linearly — and JS has no atomic groups / possessive quantifiers to bound it
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
* the `any` wildcard records its position and, on a later mismatch, backtracks
|
|
1123
|
+
* up super-linearly — and JS has no atomic groups / possessive quantifiers to bound it,
|
|
1124
|
+
* while a `LIKE` / `GLOB` pattern is a caller-supplied operand this package cannot
|
|
1125
|
+
* trust. So this builds no regex. It runs the classic greedy two-pointer wildcard match:
|
|
1126
|
+
* the `any` wildcard records its position and, on a later mismatch, backtracks only to
|
|
921
1127
|
* that last `any` (letting it absorb one more char) — so the work is O(value × pattern),
|
|
922
1128
|
* never the exponential / polynomial backtracking a regex would do. The pattern length
|
|
923
1129
|
* is capped at {@link MAX_PATTERN_LENGTH} (a `VALIDATION` {@link DatabaseError} over it),
|
|
@@ -925,28 +1131,29 @@ export declare function matchesQuery(row: Row, conditions: readonly Condition[])
|
|
|
925
1131
|
* pattern.
|
|
926
1132
|
*
|
|
927
1133
|
* The `any` wildcard matches any run (including empty); `single` matches exactly one
|
|
928
|
-
* char; every other pattern char matches itself
|
|
1134
|
+
* char; every other pattern char matches itself literally (a pattern `.` / `(` / `\` is
|
|
929
1135
|
* a literal — the regex-metacharacter hazard is gone with the regex). `any` is tested
|
|
930
|
-
*
|
|
931
|
-
* shadows the wildcard. Case folding is applied to
|
|
1136
|
+
* before a literal match, so a value that literally contains the wildcard char never
|
|
1137
|
+
* shadows the wildcard. Case folding is applied to both sides when `fold` is set.
|
|
932
1138
|
*
|
|
933
1139
|
* @param value - The value to test
|
|
934
1140
|
* @param pattern - The wildcard pattern
|
|
935
1141
|
* @param any - The any-run wildcard char (`%` for `LIKE`, `*` for `GLOB`)
|
|
936
1142
|
* @param single - The single-char wildcard char (`_` for `LIKE`, `?` for `GLOB`)
|
|
937
|
-
* @param fold - Whether to match case-
|
|
938
|
-
* @returns
|
|
1143
|
+
* @param fold - Whether to match case-insensitively (`LIKE` folds; `GLOB` does not)
|
|
1144
|
+
* @returns True if `value` matches `pattern`; false otherwise
|
|
939
1145
|
* @throws A `VALIDATION` {@link DatabaseError} when `pattern` exceeds {@link MAX_PATTERN_LENGTH}
|
|
940
1146
|
*/
|
|
941
1147
|
export declare function matchesWildcardPattern(value: string, pattern: string, any: string, single: string, fold: boolean): boolean;
|
|
942
1148
|
|
|
943
1149
|
/**
|
|
944
|
-
*
|
|
1150
|
+
* Sets the longest `LIKE` / `GLOB` pattern the wildcard matcher accepts, 1024 characters, before
|
|
1151
|
+
* rejecting it.
|
|
945
1152
|
*
|
|
946
1153
|
* @remarks
|
|
947
|
-
* A
|
|
948
|
-
*
|
|
949
|
-
*
|
|
1154
|
+
* A `LIKE` / `GLOB` pattern is a caller-supplied operand, so
|
|
1155
|
+
* `matchesLikePattern` / `matchesGlobPattern` run patterns this package cannot
|
|
1156
|
+
* trust. The matcher is the linear greedy two-pointer wildcard match — never a
|
|
950
1157
|
* backtracking regex (`.*`-segments-separated-by-literals against a long input is the
|
|
951
1158
|
* catastrophic shape JS cannot bound without atomic groups), so it is O(value ×
|
|
952
1159
|
* pattern). Capping the pattern length bounds that pattern factor, leaving a match
|
|
@@ -956,16 +1163,16 @@ export declare function matchesWildcardPattern(value: string, pattern: string, a
|
|
|
956
1163
|
export declare const MAX_PATTERN_LENGTH = 1024;
|
|
957
1164
|
|
|
958
1165
|
/**
|
|
959
|
-
*
|
|
1166
|
+
* Implements the reference {@link DriverInterface} — nested maps, no I/O.
|
|
960
1167
|
*
|
|
961
1168
|
* @remarks
|
|
962
1169
|
* The in-between made concrete: it runs identically in a browser or on a server,
|
|
963
1170
|
* so it is the storage behind tests, ephemeral caches, and any code that wants
|
|
964
|
-
* the database API without a persistent backend. Rows are
|
|
1171
|
+
* the database API without a persistent backend. Rows are deep-copied (through
|
|
965
1172
|
* `structuredClone`) in and out — at `write`, `read`, `scan`, `stream`, and both
|
|
966
1173
|
* snapshot capture and restore — so a caller mutating a nested field of an input
|
|
967
1174
|
* row, a returned row, or a row mutated in place between snapshot and rollback
|
|
968
|
-
* can never perturb stored state
|
|
1175
|
+
* can never perturb stored state; a shallow `{ ...row }` spread
|
|
969
1176
|
* would still share nested object/array references. Metadata instead routes
|
|
970
1177
|
* through `cloneDriverMetadata`: `stamp` and migration snapshot exact JSON at
|
|
971
1178
|
* ingress, and `metadata` returns a distinct deeply frozen owned copy. `snapshot`
|
|
@@ -987,19 +1194,19 @@ export declare class MemoryDriver implements DriverInterface {
|
|
|
987
1194
|
keys(table: string): Promise<readonly Key[]>;
|
|
988
1195
|
scan(table: string): AsyncIterable<Row>;
|
|
989
1196
|
/**
|
|
990
|
-
*
|
|
1197
|
+
* Iterates rows lazily with native filtering — the {@link DriverInterface.stream} hook.
|
|
991
1198
|
*
|
|
992
1199
|
* @remarks
|
|
993
1200
|
* Iterates the table's keys in the same key order `scan` and `keys` yield
|
|
994
1201
|
* (sorted by {@link compareValues}), testing each row against
|
|
995
|
-
* `input.conditions` (
|
|
1202
|
+
* `input.conditions` (through {@link matchesQuery}) before counting it
|
|
996
1203
|
* toward `offset` / `limit`. Both are applied lazily as matches are found —
|
|
997
1204
|
* `offset` matches are skipped without being yielded, and iteration stops the
|
|
998
1205
|
* instant `limit` yields have been produced, so a large table is never fully
|
|
999
|
-
* walked for a small page. `input.order` is
|
|
1206
|
+
* walked for a small page. `input.order` is ignored (the same contract as
|
|
1000
1207
|
* `TableInterface.scan` and `QueryInterface.stream`): streaming yields key
|
|
1001
|
-
* order, sorted output is `records()`'s job. Rows yield copy-out
|
|
1002
|
-
*
|
|
1208
|
+
* order, sorted output is `records()`'s job. Rows yield copy-out, and an
|
|
1209
|
+
* unknown table mirrors `scan`'s empty-yield behavior.
|
|
1003
1210
|
*
|
|
1004
1211
|
* @param table - The table to stream
|
|
1005
1212
|
* @param input - The filter / offset / limit to apply lazily
|
|
@@ -1014,7 +1221,7 @@ export declare class MemoryDriver implements DriverInterface {
|
|
|
1014
1221
|
stream(table: string, input: QueryInput): AsyncIterable<Row>;
|
|
1015
1222
|
clear(table: string): Promise<void>;
|
|
1016
1223
|
/**
|
|
1017
|
-
*
|
|
1224
|
+
* Captures the current state and returns a thunk that rolls back to it.
|
|
1018
1225
|
*
|
|
1019
1226
|
* @remarks
|
|
1020
1227
|
* Capture owns rows, schema, and one session-local table identity. Replay
|
|
@@ -1028,7 +1235,7 @@ export declare class MemoryDriver implements DriverInterface {
|
|
|
1028
1235
|
*/
|
|
1029
1236
|
snapshot(tables?: readonly string[]): Promise<() => Promise<void>>;
|
|
1030
1237
|
/**
|
|
1031
|
-
*
|
|
1238
|
+
* Returns the persisted {@link DriverMetadata}, or `undefined` when the store has
|
|
1032
1239
|
* never been stamped.
|
|
1033
1240
|
*
|
|
1034
1241
|
* @remarks
|
|
@@ -1041,13 +1248,13 @@ export declare class MemoryDriver implements DriverInterface {
|
|
|
1041
1248
|
*/
|
|
1042
1249
|
metadata(): Promise<DriverMetadata | undefined>;
|
|
1043
1250
|
/**
|
|
1044
|
-
*
|
|
1251
|
+
* Persists an owned snapshot for a later `metadata()` to return.
|
|
1045
1252
|
*
|
|
1046
1253
|
* @param metadata - The {@link DriverMetadata} to persist
|
|
1047
1254
|
*/
|
|
1048
1255
|
stamp(metadata: DriverMetadata): Promise<void>;
|
|
1049
1256
|
/**
|
|
1050
|
-
*
|
|
1257
|
+
* Applies a {@link Migration} plan's steps against the in-memory store.
|
|
1051
1258
|
*
|
|
1052
1259
|
* @remarks
|
|
1053
1260
|
* Steps apply against an isolated candidate. Rows, schema changes, and
|
|
@@ -1059,11 +1266,11 @@ export declare class MemoryDriver implements DriverInterface {
|
|
|
1059
1266
|
}
|
|
1060
1267
|
|
|
1061
1268
|
/**
|
|
1062
|
-
*
|
|
1269
|
+
* Applies one table's {@link MigrationStep}s to its rows — a pure row transform.
|
|
1063
1270
|
*
|
|
1064
1271
|
* @remarks
|
|
1065
1272
|
* `column.remove` drops that field from every row (a fresh copy — inputs are
|
|
1066
|
-
* never mutated
|
|
1273
|
+
* never mutated); `column.add` leaves rows as-is (an absent field
|
|
1067
1274
|
* reads as `undefined`, backfill is application policy). `table.add` /
|
|
1068
1275
|
* `table.remove` / `index.add` / `index.remove` are no-ops here (they operate
|
|
1069
1276
|
* on storage shape, not row shape). Steps for tables other than the one
|
|
@@ -1083,12 +1290,12 @@ export declare class MemoryDriver implements DriverInterface {
|
|
|
1083
1290
|
export declare function migrateRows(rows: readonly Row[], steps: readonly MigrationStep[]): readonly Row[];
|
|
1084
1291
|
|
|
1085
1292
|
/**
|
|
1086
|
-
*
|
|
1293
|
+
* Represents a schema migration plan — an ordered set of {@link MigrationStep}s moving a
|
|
1087
1294
|
* database from one schema version to another.
|
|
1088
1295
|
*
|
|
1089
1296
|
* @remarks
|
|
1090
1297
|
* `from` / `to` are the source and target schema versions; `steps` runs in
|
|
1091
|
-
* order. Applied natively
|
|
1298
|
+
* order. Applied natively through {@link DriverInterface.migrate} when a driver
|
|
1092
1299
|
* implements it.
|
|
1093
1300
|
*/
|
|
1094
1301
|
export declare interface Migration {
|
|
@@ -1098,7 +1305,7 @@ export declare interface Migration {
|
|
|
1098
1305
|
}
|
|
1099
1306
|
|
|
1100
1307
|
/**
|
|
1101
|
-
*
|
|
1308
|
+
* Represents one atomic migration request.
|
|
1102
1309
|
*
|
|
1103
1310
|
* @remarks
|
|
1104
1311
|
* `plan` carries the schema changes. `metadata`, when present, is the snapshot that
|
|
@@ -1111,11 +1318,11 @@ export declare interface MigrationInput {
|
|
|
1111
1318
|
}
|
|
1112
1319
|
|
|
1113
1320
|
/**
|
|
1114
|
-
*
|
|
1321
|
+
* Represents one step of a {@link Migration} plan — a single schema change applied to one
|
|
1115
1322
|
* table.
|
|
1116
1323
|
*
|
|
1117
1324
|
* @remarks
|
|
1118
|
-
* `operation` names the axis it splits on
|
|
1325
|
+
* `operation` names the axis it splits on: adding / removing a
|
|
1119
1326
|
* whole table, a column, or an index. A driver's optional `migrate` applies each
|
|
1120
1327
|
* step natively; a step referencing an unknown table throws `DatabaseError`
|
|
1121
1328
|
* `MIGRATION`.
|
|
@@ -1145,7 +1352,7 @@ export declare type MigrationStep = {
|
|
|
1145
1352
|
};
|
|
1146
1353
|
|
|
1147
1354
|
/**
|
|
1148
|
-
*
|
|
1355
|
+
* Canonicalizes an unknown driver schema into a distinct deeply frozen snapshot.
|
|
1149
1356
|
*
|
|
1150
1357
|
* @remarks
|
|
1151
1358
|
* Table and column lists are sorted by name. The index list is sorted by the
|
|
@@ -1165,23 +1372,24 @@ export declare function normalizeDriverSchema(value: unknown): readonly TableSch
|
|
|
1165
1372
|
* When `signal` aborts, the operation throws a {@link DatabaseError} with code
|
|
1166
1373
|
* `ABORTED` carrying `signal.reason` in `context`. Reads check at their
|
|
1167
1374
|
* documented boundaries; point mutations propagate the signal through the
|
|
1168
|
-
* driver to the backend commit point.
|
|
1375
|
+
* driver to the backend commit point. A streamed read — {@link TableInterface.scan}
|
|
1376
|
+
* or {@link QueryInterface.stream} — checks the signal before each yield.
|
|
1169
1377
|
*/
|
|
1170
1378
|
export declare interface OperationOptions {
|
|
1171
1379
|
readonly signal?: AbortSignal;
|
|
1172
1380
|
}
|
|
1173
1381
|
|
|
1174
|
-
/**
|
|
1382
|
+
/** Represents one ordering term — a column ({@link FieldPath}, flat or nested) and its direction. */
|
|
1175
1383
|
export declare interface Order {
|
|
1176
1384
|
readonly column: FieldPath;
|
|
1177
1385
|
readonly direction: OrderDirection;
|
|
1178
1386
|
}
|
|
1179
1387
|
|
|
1180
|
-
/**
|
|
1388
|
+
/** Names a sort direction. */
|
|
1181
1389
|
export declare type OrderDirection = 'ascending' | 'descending';
|
|
1182
1390
|
|
|
1183
1391
|
/**
|
|
1184
|
-
*
|
|
1392
|
+
* Diffs a deployed and a declared table set structurally into a {@link Migration}
|
|
1185
1393
|
* plan.
|
|
1186
1394
|
*
|
|
1187
1395
|
* @remarks
|
|
@@ -1196,17 +1404,16 @@ export declare type OrderDirection = 'ascending' | 'descending';
|
|
|
1196
1404
|
* plan labels only; versioning drivers persist and reconcile them through
|
|
1197
1405
|
* {@link DriverMetadata}.
|
|
1198
1406
|
*
|
|
1199
|
-
* A column present in
|
|
1407
|
+
* A column present in both schemas under the same name but with a different
|
|
1200
1408
|
* `storage`, `optional`, or `nullable` value throws a `MIGRATION`
|
|
1201
|
-
* {@link DatabaseError} naming the
|
|
1202
|
-
*
|
|
1203
|
-
*
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1206
|
-
*
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
1209
|
-
* @param deployed - The table schemas currently applied
|
|
1409
|
+
* {@link DatabaseError} naming the table, the column, and the from→to
|
|
1410
|
+
* difference — a name-only diff would otherwise silently produce no step for
|
|
1411
|
+
* the drift, and versioned reconciliation would stamp over it. There is no
|
|
1412
|
+
* automatic in-place type-change step: the manual path is to add a new column,
|
|
1413
|
+
* copy/convert the data at the application layer, then remove the old column —
|
|
1414
|
+
* two separate plans, never a single implicit "alter" step.
|
|
1415
|
+
*
|
|
1416
|
+
* @param deployed - The already-applied table schemas
|
|
1210
1417
|
* @param declared - The table schemas the caller wants applied
|
|
1211
1418
|
* @param from - The plan's source version label (defaults to `0`)
|
|
1212
1419
|
* @param to - The plan's target version label (defaults to `1`)
|
|
@@ -1228,7 +1435,7 @@ export declare type OrderDirection = 'ascending' | 'descending';
|
|
|
1228
1435
|
export declare function planMigration(deployed: readonly TableSchema[], declared: readonly TableSchema[], from?: number, to?: number): Migration;
|
|
1229
1436
|
|
|
1230
1437
|
/**
|
|
1231
|
-
*
|
|
1438
|
+
* Holds per-table primary-key column overrides — `{ [table]: column }`.
|
|
1232
1439
|
*
|
|
1233
1440
|
* @remarks
|
|
1234
1441
|
* A table absent from this map keys its rows by {@link DEFAULT_PRIMARY} (`id`).
|
|
@@ -1237,7 +1444,9 @@ export declare function planMigration(deployed: readonly TableSchema[], declared
|
|
|
1237
1444
|
export declare type PrimaryMap = Readonly<Record<string, string>>;
|
|
1238
1445
|
|
|
1239
1446
|
/**
|
|
1240
|
-
*
|
|
1447
|
+
* Projects migration steps sequentially over a canonical validated owned schema.
|
|
1448
|
+
*
|
|
1449
|
+
* @remarks
|
|
1241
1450
|
* Adding a required non-null column to an existing table rejects with
|
|
1242
1451
|
* `MIGRATION`; optional-only and nullable-only additions remain portable.
|
|
1243
1452
|
*
|
|
@@ -1248,7 +1457,7 @@ export declare type PrimaryMap = Readonly<Record<string, string>>;
|
|
|
1248
1457
|
export declare function projectMigrationSchema(schema: readonly TableSchema[], steps: readonly MigrationStep[]): readonly TableSchema[];
|
|
1249
1458
|
|
|
1250
1459
|
/**
|
|
1251
|
-
*
|
|
1460
|
+
* Represents a serializable read specification — everything a backend needs to compile one
|
|
1252
1461
|
* read, free of JS callbacks so any backend can honor it.
|
|
1253
1462
|
*
|
|
1254
1463
|
* @remarks
|
|
@@ -1265,50 +1474,118 @@ export declare interface QueryInput {
|
|
|
1265
1474
|
}
|
|
1266
1475
|
|
|
1267
1476
|
/**
|
|
1268
|
-
*
|
|
1477
|
+
* Builds a read through a fluent chain.
|
|
1269
1478
|
*
|
|
1270
1479
|
* @remarks
|
|
1271
1480
|
* `condition` appends one portable condition and `order` appends one portable
|
|
1272
1481
|
* ordering term. `filter` adds a post-fetch JavaScript predicate (applied after
|
|
1273
1482
|
* the backend read, before paging). The terminals (`collect` / `find` / `count`
|
|
1274
|
-
* / `aggregate`) execute against the table; each
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1277
|
-
* descends a nested value.
|
|
1483
|
+
* / `aggregate`) execute against the table; each call mutates and returns the
|
|
1484
|
+
* same builder, so a chain reads as one statement. Every `column` is a
|
|
1485
|
+
* {@link FieldPath} — a string is one column, an array descends a nested value.
|
|
1278
1486
|
*/
|
|
1279
1487
|
export declare interface QueryInterface<T = Row> {
|
|
1488
|
+
/**
|
|
1489
|
+
* Adds one portable condition, including its explicit connector.
|
|
1490
|
+
*/
|
|
1280
1491
|
condition(input: Condition): QueryInterface<T>;
|
|
1492
|
+
/**
|
|
1493
|
+
* Adds one portable ordering term — a column and a direction.
|
|
1494
|
+
*/
|
|
1281
1495
|
order(input: Order): QueryInterface<T>;
|
|
1496
|
+
/**
|
|
1497
|
+
* Adds a post-fetch JavaScript predicate.
|
|
1498
|
+
*/
|
|
1282
1499
|
filter(predicate: (row: T) => boolean): QueryInterface<T>;
|
|
1500
|
+
/**
|
|
1501
|
+
* Caps the result count.
|
|
1502
|
+
*/
|
|
1283
1503
|
limit(count: number): QueryInterface<T>;
|
|
1504
|
+
/**
|
|
1505
|
+
* Skips the leading rows.
|
|
1506
|
+
*/
|
|
1284
1507
|
offset(count: number): QueryInterface<T>;
|
|
1508
|
+
/**
|
|
1509
|
+
* Executes the accumulated read and collects every matching row.
|
|
1510
|
+
*/
|
|
1285
1511
|
collect(): Promise<readonly T[]>;
|
|
1512
|
+
/**
|
|
1513
|
+
* Executes the accumulated read and returns the first match, or `undefined`.
|
|
1514
|
+
*/
|
|
1286
1515
|
find(): Promise<T | undefined>;
|
|
1516
|
+
/**
|
|
1517
|
+
* Executes the accumulated read and returns the match count.
|
|
1518
|
+
*/
|
|
1287
1519
|
count(): Promise<number>;
|
|
1288
1520
|
/**
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1521
|
+
* Evaluates this query's conditions / filters / offset / limit lazily, row
|
|
1522
|
+
* by row.
|
|
1291
1523
|
*
|
|
1292
1524
|
* @remarks
|
|
1293
|
-
* `order` and its comparators are
|
|
1525
|
+
* `order` and its comparators are ignored (streaming yields unsorted, as
|
|
1294
1526
|
* rows are evaluated one at a time). Same abort semantics as
|
|
1295
1527
|
* {@link TableInterface.scan}: the signal (if any) is checked before each
|
|
1296
1528
|
* yield, and breaking out early closes the underlying source.
|
|
1297
1529
|
*/
|
|
1298
1530
|
stream(options?: OperationOptions): AsyncIterable<T>;
|
|
1531
|
+
/**
|
|
1532
|
+
* Executes a named aggregate over one column.
|
|
1533
|
+
*/
|
|
1299
1534
|
aggregate(operation: AggregateOperation, column: FieldPath): Promise<number | undefined>;
|
|
1300
1535
|
}
|
|
1301
1536
|
|
|
1302
|
-
/**
|
|
1537
|
+
/**
|
|
1538
|
+
* Requires one declared table's columns out of a table map.
|
|
1539
|
+
*
|
|
1540
|
+
* @remarks
|
|
1541
|
+
* The overload preserves the map's own value type for a statically known table
|
|
1542
|
+
* name, so a typed view keeps its row type without an assertion. An undeclared
|
|
1543
|
+
* table is a caller error rather than an absence, so it throws instead of
|
|
1544
|
+
* returning `undefined`.
|
|
1545
|
+
*
|
|
1546
|
+
* @param tables - The declared table map
|
|
1547
|
+
* @param name - The table name
|
|
1548
|
+
* @returns The table's {@link ColumnMap}
|
|
1549
|
+
* @throws A `NOT_FOUND` {@link DatabaseError} when `tables` does not declare `name`
|
|
1550
|
+
*
|
|
1551
|
+
* @example
|
|
1552
|
+
* ```ts
|
|
1553
|
+
* requireColumns({ users: { id: stringShape() } }, 'users') // { id: … }
|
|
1554
|
+
* ```
|
|
1555
|
+
*/
|
|
1556
|
+
export declare function requireColumns<T extends TableMap, K extends keyof T & string>(tables: T, name: K): T[K];
|
|
1557
|
+
|
|
1558
|
+
export declare function requireColumns(tables: TableMap, name: string): ColumnMap;
|
|
1559
|
+
|
|
1560
|
+
/**
|
|
1561
|
+
* Resolves the primary-key column one table keys its rows by.
|
|
1562
|
+
*
|
|
1563
|
+
* @remarks
|
|
1564
|
+
* A table absent from the {@link PrimaryMap} keys its rows by
|
|
1565
|
+
* {@link DEFAULT_PRIMARY}, so this is total over any table name.
|
|
1566
|
+
*
|
|
1567
|
+
* @param primary - The per-table primary-key overrides
|
|
1568
|
+
* @param name - The table name
|
|
1569
|
+
* @returns The table's primary-key column
|
|
1570
|
+
*
|
|
1571
|
+
* @example
|
|
1572
|
+
* ```ts
|
|
1573
|
+
* resolvePrimary({ posts: 'slug' }, 'posts') // 'slug'
|
|
1574
|
+
* resolvePrimary({ posts: 'slug' }, 'users') // 'id' — the default primary
|
|
1575
|
+
* ```
|
|
1576
|
+
*/
|
|
1577
|
+
export declare function resolvePrimary(primary: PrimaryMap, name: string): string;
|
|
1578
|
+
|
|
1579
|
+
/** Represents a table row — a plain record of column values keyed by column name. */
|
|
1303
1580
|
export declare type Row = Record<string, unknown>;
|
|
1304
1581
|
|
|
1305
1582
|
/**
|
|
1306
|
-
*
|
|
1583
|
+
* Represents the row type a table's {@link ColumnMap} describe — `Infer` of the `objectShape`
|
|
1307
1584
|
* the database wraps them in.
|
|
1308
1585
|
*
|
|
1309
1586
|
* @remarks
|
|
1310
1587
|
* Contract 0.0.4's non-distributive `Infer` resolves the OPEN case (the broad
|
|
1311
|
-
* `ColumnMap` —
|
|
1588
|
+
* `ColumnMap` — for example when a database is held at its default type) directly:
|
|
1312
1589
|
* `RowOf<ColumnMap>` and {@link Row} are mutually assignable, so no short-circuit
|
|
1313
1590
|
* to `Row` and no `additionalProperties: false` pin are needed — `Infer` no
|
|
1314
1591
|
* longer trips TS's instantiation-depth guard over the open shape, and the
|
|
@@ -1317,12 +1594,71 @@ export declare type Row = Record<string, unknown>;
|
|
|
1317
1594
|
* concrete column map.
|
|
1318
1595
|
*/
|
|
1319
1596
|
export declare type RowOf<C extends ColumnMap> = Infer<{
|
|
1320
|
-
readonly
|
|
1597
|
+
readonly category: 'object';
|
|
1321
1598
|
readonly properties: C;
|
|
1322
1599
|
}>;
|
|
1323
1600
|
|
|
1324
1601
|
/**
|
|
1325
|
-
*
|
|
1602
|
+
* Walks the driver-conformance battery against a fresh {@link DriverInterface}
|
|
1603
|
+
* per phase, yielding one {@link ConformanceFinding} per violated invariant —
|
|
1604
|
+
* the shared invariant suite every backend (in-memory, SQLite, IndexedDB)
|
|
1605
|
+
* must uphold to be a drop-in {@link DriverInterface}.
|
|
1606
|
+
*
|
|
1607
|
+
* @remarks
|
|
1608
|
+
* Framework-agnostic: no test-runner or Node imports, only sibling core
|
|
1609
|
+
* modules — so it runs equally from a unit test, a smoke script, or a new
|
|
1610
|
+
* driver's own README. Opens a fixed two-table schema (`users` keyed by the
|
|
1611
|
+
* default `id`, `posts` keyed by a non-id `slug`) and, calling `factory()`
|
|
1612
|
+
* fresh for each phase so failures stay isolated, verifies: `open`/`close`;
|
|
1613
|
+
* `read` of a missing key returns `undefined`; `write`/`read` round-trip
|
|
1614
|
+
* with deep copy-in/copy-out isolation (mutating the caller's row —
|
|
1615
|
+
* including a nested field — after `write`, or a row `read` returns, never
|
|
1616
|
+
* perturbs stored state) and upsert-overwrite; simultaneous same-key
|
|
1617
|
+
* `insert` calls produce exactly one commit and one `CONFLICT`; pre-aborted
|
|
1618
|
+
* `write`, `insert`, and `delete` calls leave storage unchanged; `delete`
|
|
1619
|
+
* returns `true` then `false`; `keys`/`scan` yield in ascending key order;
|
|
1620
|
+
* `clear` empties only its target table; `snapshot`'s rollback thunk
|
|
1621
|
+
* restores pre-snapshot state, including a nested field mutated in place on
|
|
1622
|
+
* a read-back row between capture and restore; a scoped
|
|
1623
|
+
* `snapshot(['users'])` rolls back only the named table, leaving a
|
|
1624
|
+
* concurrent mutation to another table intact; a non-`id` primary key
|
|
1625
|
+
* (`posts.slug`) round-trips; a nested-object row round-trips structurally
|
|
1626
|
+
* (through {@link equalsValue}). The optional surface is presence-gated: when
|
|
1627
|
+
* `migrate` exists, a `column.remove` plan strips the column from stored
|
|
1628
|
+
* rows and a plan referencing an unknown table throws `DatabaseError`
|
|
1629
|
+
* `MIGRATION`; when `stream` exists, it yields only condition-matching rows
|
|
1630
|
+
* and honors `offset`/`limit`; when `transaction` exists, `commit` persists
|
|
1631
|
+
* and `rollback` restores; when both `metadata` and `stamp` exist, a fresh
|
|
1632
|
+
* store's `metadata()` is `undefined`, and after
|
|
1633
|
+
* `stamp({ version, schema })`, `metadata()` returns the exact stamped value.
|
|
1634
|
+
*
|
|
1635
|
+
* Each phase runs within a `try`/`catch`: an expected mismatch yields a
|
|
1636
|
+
* finding built from the assertion, while an unexpected throw (a driver
|
|
1637
|
+
* crash mid-phase) is caught and yielded as a finding too, naming the phase
|
|
1638
|
+
* as `check` and carrying the caught error in `context.error` — a broken
|
|
1639
|
+
* driver can never escape the battery as an unhandled rejection. Within a
|
|
1640
|
+
* phase, the first violated assertion yields and the phase stops (matching
|
|
1641
|
+
* the historical fail-fast shape at phase granularity); the generator then
|
|
1642
|
+
* moves on to the next phase regardless. Because this is a **generator**,
|
|
1643
|
+
* consuming only the first yielded value reproduces true fail-fast (later
|
|
1644
|
+
* phases never run) — that is exactly what {@link conformDriver} does.
|
|
1645
|
+
*
|
|
1646
|
+
* @param factory - Mints a fresh, unopened driver instance (called once per phase)
|
|
1647
|
+
* @yields One {@link ConformanceFinding} per violated invariant, in phase order
|
|
1648
|
+
*
|
|
1649
|
+
* @example
|
|
1650
|
+
* ```ts
|
|
1651
|
+
* import { createMemoryDriver, scanDriver } from '@orkestrel/database'
|
|
1652
|
+
*
|
|
1653
|
+
* for await (const finding of scanDriver(() => createMemoryDriver())) {
|
|
1654
|
+
* console.log(finding.check, finding.message)
|
|
1655
|
+
* }
|
|
1656
|
+
* ```
|
|
1657
|
+
*/
|
|
1658
|
+
export declare function scanDriver(factory: () => DriverInterface): AsyncIterable<ConformanceFinding>;
|
|
1659
|
+
|
|
1660
|
+
/**
|
|
1661
|
+
* Projects one contract shape into a portable column schema.
|
|
1326
1662
|
*
|
|
1327
1663
|
* @param name - The column name
|
|
1328
1664
|
* @param shape - The column contract shape
|
|
@@ -1331,7 +1667,7 @@ export declare type RowOf<C extends ColumnMap> = Infer<{
|
|
|
1331
1667
|
export declare function shapeToColumnSchema(name: string, shape: ContractShape): ColumnSchema;
|
|
1332
1668
|
|
|
1333
1669
|
/**
|
|
1334
|
-
*
|
|
1670
|
+
* Maps a column's {@link ContractShape} to its portable {@link ColumnStorage} — the
|
|
1335
1671
|
* value a `TableSchema` carries so a native backend can declare a real column.
|
|
1336
1672
|
*
|
|
1337
1673
|
* @remarks
|
|
@@ -1357,7 +1693,7 @@ export declare function shapeToColumnSchema(name: string, shape: ContractShape):
|
|
|
1357
1693
|
export declare function shapeToColumnStorage(shape: ContractShape): ColumnStorage;
|
|
1358
1694
|
|
|
1359
1695
|
/**
|
|
1360
|
-
*
|
|
1696
|
+
* Sorts rows by an ordering specification, leaving the input untouched.
|
|
1361
1697
|
*
|
|
1362
1698
|
* @remarks
|
|
1363
1699
|
* Applies the terms in priority order — the first term that distinguishes two
|
|
@@ -1370,7 +1706,7 @@ export declare function shapeToColumnStorage(shape: ContractShape): ColumnStorag
|
|
|
1370
1706
|
export declare function sortRows(rows: readonly Row[], order: readonly Order[]): readonly Row[];
|
|
1371
1707
|
|
|
1372
1708
|
/**
|
|
1373
|
-
*
|
|
1709
|
+
* Declares the storage operations available only inside a driver's transaction scope.
|
|
1374
1710
|
*
|
|
1375
1711
|
* @remarks
|
|
1376
1712
|
* A driver owns acquisition, commit or rollback, release, and lifetime. This
|
|
@@ -1380,28 +1716,67 @@ export declare function sortRows(rows: readonly Row[], order: readonly Order[]):
|
|
|
1380
1716
|
* callers fall back to `scan` when a native read hook is absent.
|
|
1381
1717
|
*/
|
|
1382
1718
|
export declare interface StorageInterface {
|
|
1719
|
+
/**
|
|
1720
|
+
* Reads one row by key.
|
|
1721
|
+
*/
|
|
1383
1722
|
read(table: string, key: Key): Promise<Row | undefined>;
|
|
1723
|
+
/**
|
|
1724
|
+
* Writes one row at a key.
|
|
1725
|
+
*/
|
|
1384
1726
|
write(table: string, key: Key, row: Row, options?: OperationOptions): Promise<void>;
|
|
1727
|
+
/**
|
|
1728
|
+
* Inserts one row atomically, rejecting `CONFLICT` when its key already exists.
|
|
1729
|
+
*/
|
|
1385
1730
|
insert(table: string, key: Key, row: Row, options?: OperationOptions): Promise<void>;
|
|
1731
|
+
/**
|
|
1732
|
+
* Deletes one row by key.
|
|
1733
|
+
*/
|
|
1386
1734
|
delete(table: string, key: Key, options?: OperationOptions): Promise<boolean>;
|
|
1735
|
+
/**
|
|
1736
|
+
* Lists a table's keys.
|
|
1737
|
+
*/
|
|
1387
1738
|
keys(table: string): Promise<readonly Key[]>;
|
|
1739
|
+
/**
|
|
1740
|
+
* Iterates a table's rows in ascending key order.
|
|
1741
|
+
*/
|
|
1388
1742
|
scan(table: string): AsyncIterable<Row>;
|
|
1743
|
+
/**
|
|
1744
|
+
* Empties a table.
|
|
1745
|
+
*/
|
|
1389
1746
|
clear(table: string): Promise<void>;
|
|
1747
|
+
/**
|
|
1748
|
+
* Reads the rows matching a {@link QueryInput} natively — an optional hook.
|
|
1749
|
+
*/
|
|
1390
1750
|
records?(table: string, input: QueryInput): Promise<readonly Row[]>;
|
|
1751
|
+
/**
|
|
1752
|
+
* Computes an aggregate over a column natively — an optional hook.
|
|
1753
|
+
*/
|
|
1391
1754
|
aggregate?(table: string, operation: AggregateOperation, column: FieldPath, input: QueryInput): Promise<number | undefined>;
|
|
1755
|
+
/**
|
|
1756
|
+
* Iterates the natively filtered rows lazily — an optional hook.
|
|
1757
|
+
*/
|
|
1392
1758
|
stream?(table: string, input: QueryInput): AsyncIterable<Row>;
|
|
1759
|
+
/**
|
|
1760
|
+
* Applies one atomic {@link MigrationInput} — an optional hook.
|
|
1761
|
+
*/
|
|
1393
1762
|
migrate?(input: MigrationInput): Promise<void>;
|
|
1763
|
+
/**
|
|
1764
|
+
* Reads the persisted {@link DriverMetadata} as a deeply frozen copy — an optional hook.
|
|
1765
|
+
*/
|
|
1394
1766
|
metadata?(): Promise<DriverMetadata | undefined>;
|
|
1767
|
+
/**
|
|
1768
|
+
* Writes the persisted {@link DriverMetadata}, snapshot at entry — an optional hook.
|
|
1769
|
+
*/
|
|
1395
1770
|
stamp?(metadata: DriverMetadata): Promise<void>;
|
|
1396
1771
|
}
|
|
1397
1772
|
|
|
1398
1773
|
/**
|
|
1399
|
-
*
|
|
1774
|
+
* Represents one table's portable definition, produced by `export` — the unit of schema /
|
|
1400
1775
|
* migration exchange across environments.
|
|
1401
1776
|
*
|
|
1402
1777
|
* @remarks
|
|
1403
1778
|
* `schema` is the JSON Schema (universally portable, serializable); `columns` is
|
|
1404
|
-
* the source column map, which re-imports losslessly
|
|
1779
|
+
* the source column map, which re-imports losslessly through `import` within a
|
|
1405
1780
|
* TypeScript environment. `primary` is the primary-key column.
|
|
1406
1781
|
*/
|
|
1407
1782
|
export declare interface TableDefinition {
|
|
@@ -1411,34 +1786,34 @@ export declare interface TableDefinition {
|
|
|
1411
1786
|
}
|
|
1412
1787
|
|
|
1413
1788
|
/**
|
|
1414
|
-
*
|
|
1789
|
+
* Describes the push observation surface of a {@link TableInterface} — the per-row
|
|
1415
1790
|
* mutation moments a fire-and-forget observer (cache invalidation, sync, an audit log)
|
|
1416
|
-
* subscribes to,
|
|
1791
|
+
* subscribes to, alongside the database-level {@link DatabaseEventMap}.
|
|
1417
1792
|
*
|
|
1418
1793
|
* @remarks
|
|
1419
|
-
* Events carry the affected
|
|
1794
|
+
* Events carry the affected key only — never the row value — to keep fan-out lean and
|
|
1420
1795
|
* avoid leaking row data through the observation channel; a consumer that needs the
|
|
1421
1796
|
* value re-reads it by key. Any row put — `set`, `add`, or `update` — emits a single
|
|
1422
1797
|
* `write` (the consumer re-reads if it needs to know what changed); a delete emits
|
|
1423
|
-
* `remove`; emptying the table emits `clear`. Reads / queries / counts are
|
|
1424
|
-
* (too hot, and a reader does not mutate). Listener isolation is the emitter's
|
|
1798
|
+
* `remove`; emptying the table emits `clear`. Reads / queries / counts are not emitted
|
|
1799
|
+
* (too hot, and a reader does not mutate). Listener isolation is the emitter's:
|
|
1425
1800
|
* every event is emitted directly and a listener throw is routed to the emitter's `error`
|
|
1426
|
-
* handler (the `error` option), never onto this map, and sits
|
|
1801
|
+
* handler (the `error` option), never onto this map, and sits after the driver write / delete
|
|
1427
1802
|
* / clear has completed — so a throwing observer can never corrupt a write or perturb a
|
|
1428
|
-
* transaction. Subscribe
|
|
1803
|
+
* transaction. Subscribe through `table.emitter.on(...)`. Declared as a `type` alias (
|
|
1429
1804
|
* `EventMap` is a `type` kind).
|
|
1430
1805
|
*/
|
|
1431
1806
|
export declare type TableEventMap = {
|
|
1432
|
-
/**
|
|
1807
|
+
/** Signals that a row was written (set / added / updated) — the affected key (no value payload). */
|
|
1433
1808
|
readonly write: readonly [key: Key];
|
|
1434
|
-
/**
|
|
1809
|
+
/** Signals that a row was removed — the affected key. */
|
|
1435
1810
|
readonly remove: readonly [key: Key];
|
|
1436
|
-
/**
|
|
1811
|
+
/** Signals that the table was cleared (every row removed). */
|
|
1437
1812
|
readonly clear: readonly [];
|
|
1438
1813
|
};
|
|
1439
1814
|
|
|
1440
1815
|
/**
|
|
1441
|
-
*
|
|
1816
|
+
* Exposes typed keyed CRUD plus fluent query and cursor access.
|
|
1442
1817
|
*
|
|
1443
1818
|
* @remarks
|
|
1444
1819
|
* Writes are coerced through the table's contract: a string input to a numeric
|
|
@@ -1448,7 +1823,7 @@ export declare type TableEventMap = {
|
|
|
1448
1823
|
* inserts and throws `CONFLICT` on a duplicate key. `contract` exposes the
|
|
1449
1824
|
* compiled contract for introspection (`schema`) and fixtures (`generate`).
|
|
1450
1825
|
*
|
|
1451
|
-
* The keyed methods batch by overload
|
|
1826
|
+
* The keyed methods batch by overload: pass one key/row for one
|
|
1452
1827
|
* result, or an array for an array of results in the same order — a single verb,
|
|
1453
1828
|
* never `getMany` / `setAll`. Batches run as independent sequential operations;
|
|
1454
1829
|
* wrap them in `transaction` for atomicity.
|
|
@@ -1458,16 +1833,31 @@ export declare interface TableInterface<T = Row> {
|
|
|
1458
1833
|
readonly name: string;
|
|
1459
1834
|
readonly primary: string;
|
|
1460
1835
|
readonly contract: ContractInterface<T>;
|
|
1836
|
+
/**
|
|
1837
|
+
* Reads one row by key, or one row per key for a list — `undefined` for each miss.
|
|
1838
|
+
*/
|
|
1461
1839
|
get(key: Key): Promise<T | undefined>;
|
|
1462
1840
|
get(keys: readonly Key[]): Promise<ReadonlyArray<T | undefined>>;
|
|
1841
|
+
/**
|
|
1842
|
+
* Reads one row by key, or one row per key for a list, throwing `NOT_FOUND` on a miss.
|
|
1843
|
+
*/
|
|
1463
1844
|
resolve(key: Key): Promise<T>;
|
|
1464
1845
|
resolve(keys: readonly Key[]): Promise<readonly T[]>;
|
|
1846
|
+
/**
|
|
1847
|
+
* Reports whether one key exists, or one result per key for a list.
|
|
1848
|
+
*/
|
|
1465
1849
|
has(key: Key): Promise<boolean>;
|
|
1466
1850
|
has(keys: readonly Key[]): Promise<readonly boolean[]>;
|
|
1851
|
+
/**
|
|
1852
|
+
* Lists every primary key in order.
|
|
1853
|
+
*/
|
|
1467
1854
|
keys(): Promise<readonly Key[]>;
|
|
1855
|
+
/**
|
|
1856
|
+
* Reads the contract-valid rows matching an optional {@link QueryInput}.
|
|
1857
|
+
*/
|
|
1468
1858
|
records(input?: QueryInput, options?: OperationOptions): Promise<readonly T[]>;
|
|
1469
1859
|
/**
|
|
1470
|
-
*
|
|
1860
|
+
* Counts contract-valid rows matching `input`'s conditions.
|
|
1471
1861
|
*
|
|
1472
1862
|
* @remarks
|
|
1473
1863
|
* Paging is ignored. Like `records()` / `scan()`, `count()` narrows every
|
|
@@ -1476,11 +1866,11 @@ export declare interface TableInterface<T = Row> {
|
|
|
1476
1866
|
*/
|
|
1477
1867
|
count(input?: QueryInput, options?: OperationOptions): Promise<number>;
|
|
1478
1868
|
/**
|
|
1479
|
-
*
|
|
1869
|
+
* Computes an aggregate over `column` across rows matching `input`'s
|
|
1480
1870
|
* conditions.
|
|
1481
1871
|
*
|
|
1482
1872
|
* @remarks
|
|
1483
|
-
* Unlike {@link TableInterface.count}, `aggregate` operates on
|
|
1873
|
+
* Unlike {@link TableInterface.count}, `aggregate` operates on stored rows
|
|
1484
1874
|
* without the contract guard that `records()` / `scan()` apply — a
|
|
1485
1875
|
* non-conforming stored row still contributes to the aggregate (or to the
|
|
1486
1876
|
* `count` operation's tally) when it matches the conditions, even though
|
|
@@ -1488,18 +1878,18 @@ export declare interface TableInterface<T = Row> {
|
|
|
1488
1878
|
*/
|
|
1489
1879
|
aggregate(operation: AggregateOperation, column: FieldPath, input?: QueryInput, options?: OperationOptions): Promise<number | undefined>;
|
|
1490
1880
|
/**
|
|
1491
|
-
*
|
|
1881
|
+
* Iterates the table's rows lazily with filtering.
|
|
1492
1882
|
*
|
|
1493
1883
|
* @remarks
|
|
1494
1884
|
* `input`'s `conditions` / `offset` / `limit` are honored lazily as rows
|
|
1495
|
-
* stream; `order` is intentionally
|
|
1885
|
+
* stream; `order` is intentionally ignored — streaming yields driver
|
|
1496
1886
|
* key-order, sorted output is `records()`'s job. Breaking out of the
|
|
1497
1887
|
* iteration early closes the underlying source. The signal (if any) is
|
|
1498
1888
|
* checked before each yield.
|
|
1499
1889
|
*/
|
|
1500
1890
|
scan(input?: QueryInput, options?: OperationOptions): AsyncIterable<T>;
|
|
1501
1891
|
/**
|
|
1502
|
-
*
|
|
1892
|
+
* Upserts one or more rows.
|
|
1503
1893
|
*
|
|
1504
1894
|
* @param row - The row to upsert
|
|
1505
1895
|
* @param options - Optional abort signal
|
|
@@ -1507,7 +1897,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1507
1897
|
*/
|
|
1508
1898
|
set(row: T, options?: OperationOptions): Promise<Key>;
|
|
1509
1899
|
/**
|
|
1510
|
-
*
|
|
1900
|
+
* Upserts one or more rows.
|
|
1511
1901
|
*
|
|
1512
1902
|
* @param rows - The rows to upsert
|
|
1513
1903
|
* @param options - Optional abort signal, checked at entry and between items
|
|
@@ -1520,7 +1910,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1520
1910
|
*/
|
|
1521
1911
|
set(rows: readonly T[], options?: OperationOptions): Promise<readonly Key[]>;
|
|
1522
1912
|
/**
|
|
1523
|
-
*
|
|
1913
|
+
* Inserts one or more rows, throwing `CONFLICT` on a duplicate key.
|
|
1524
1914
|
*
|
|
1525
1915
|
* @param row - The row to insert
|
|
1526
1916
|
* @param options - Optional abort signal
|
|
@@ -1528,7 +1918,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1528
1918
|
*/
|
|
1529
1919
|
add(row: T, options?: OperationOptions): Promise<Key>;
|
|
1530
1920
|
/**
|
|
1531
|
-
*
|
|
1921
|
+
* Inserts one or more rows, throwing `CONFLICT` on a duplicate key.
|
|
1532
1922
|
*
|
|
1533
1923
|
* @param rows - The rows to insert
|
|
1534
1924
|
* @param options - Optional abort signal, checked at entry and between items
|
|
@@ -1541,16 +1931,16 @@ export declare interface TableInterface<T = Row> {
|
|
|
1541
1931
|
*/
|
|
1542
1932
|
add(rows: readonly T[], options?: OperationOptions): Promise<readonly Key[]>;
|
|
1543
1933
|
/**
|
|
1544
|
-
*
|
|
1934
|
+
* Applies a partial change to one or more rows.
|
|
1545
1935
|
*
|
|
1546
1936
|
* @param key - The key of the row to update
|
|
1547
1937
|
* @param changes - The partial changes to apply
|
|
1548
1938
|
* @param options - Optional abort signal
|
|
1549
|
-
* @returns
|
|
1939
|
+
* @returns True if the row existed and was updated; false otherwise
|
|
1550
1940
|
*/
|
|
1551
1941
|
update(key: Key, changes: Partial<T>, options?: OperationOptions): Promise<boolean>;
|
|
1552
1942
|
/**
|
|
1553
|
-
*
|
|
1943
|
+
* Applies a partial change to one or more rows.
|
|
1554
1944
|
*
|
|
1555
1945
|
* @param keys - The keys of the rows to update
|
|
1556
1946
|
* @param changes - The partial changes to apply to each row
|
|
@@ -1564,15 +1954,15 @@ export declare interface TableInterface<T = Row> {
|
|
|
1564
1954
|
*/
|
|
1565
1955
|
update(keys: readonly Key[], changes: Partial<T>, options?: OperationOptions): Promise<readonly boolean[]>;
|
|
1566
1956
|
/**
|
|
1567
|
-
*
|
|
1957
|
+
* Deletes one or more rows.
|
|
1568
1958
|
*
|
|
1569
1959
|
* @param key - The key of the row to remove
|
|
1570
1960
|
* @param options - Optional abort signal
|
|
1571
|
-
* @returns
|
|
1961
|
+
* @returns True if the row existed and was removed; false otherwise
|
|
1572
1962
|
*/
|
|
1573
1963
|
remove(key: Key, options?: OperationOptions): Promise<boolean>;
|
|
1574
1964
|
/**
|
|
1575
|
-
*
|
|
1965
|
+
* Deletes one or more rows.
|
|
1576
1966
|
*
|
|
1577
1967
|
* @param keys - The keys of the rows to remove
|
|
1578
1968
|
* @param options - Optional abort signal, checked at entry and between items
|
|
@@ -1584,27 +1974,36 @@ export declare interface TableInterface<T = Row> {
|
|
|
1584
1974
|
* applied — there is no rollback. Wrap in `transaction()` for atomicity.
|
|
1585
1975
|
*/
|
|
1586
1976
|
remove(keys: readonly Key[], options?: OperationOptions): Promise<readonly boolean[]>;
|
|
1977
|
+
/**
|
|
1978
|
+
* Empties the table.
|
|
1979
|
+
*/
|
|
1587
1980
|
clear(): Promise<void>;
|
|
1981
|
+
/**
|
|
1982
|
+
* Opens a fluent query builder over the table.
|
|
1983
|
+
*/
|
|
1588
1984
|
query(): QueryInterface<T>;
|
|
1985
|
+
/**
|
|
1986
|
+
* Opens a forward row cursor for bulk mutation.
|
|
1987
|
+
*/
|
|
1589
1988
|
cursor(): Promise<CursorInterface<T>>;
|
|
1590
1989
|
}
|
|
1591
1990
|
|
|
1592
1991
|
/**
|
|
1593
|
-
*
|
|
1992
|
+
* Represents a database's table schema — a map of table name to its {@link ColumnMap}.
|
|
1594
1993
|
*
|
|
1595
1994
|
* @remarks
|
|
1596
1995
|
* Each table's row type is `Infer` of its columns (see {@link RowOf}); primary-key
|
|
1597
|
-
* columns are named separately
|
|
1996
|
+
* columns are named separately through {@link PrimaryMap}.
|
|
1598
1997
|
*/
|
|
1599
1998
|
export declare type TableMap = Readonly<Record<string, ColumnMap>>;
|
|
1600
1999
|
|
|
1601
2000
|
/**
|
|
1602
|
-
*
|
|
2001
|
+
* Represents a backend-agnostic description of one table — what `open` hands each driver so a
|
|
1603
2002
|
* native backend can create real tables and indexes.
|
|
1604
2003
|
*
|
|
1605
2004
|
* @remarks
|
|
1606
2005
|
* Derived by the database from its `tables` contract shapes ({@link ColumnSchema}
|
|
1607
|
-
* per column,
|
|
2006
|
+
* per column, through `shapeToColumnStorage`), its `primary`, and its `indexes` option
|
|
1608
2007
|
* (`indexes`, each entry one possibly-compound index of column names). A scan-only
|
|
1609
2008
|
* backend (the reference `MemoryDriver`) ignores everything but `name`.
|
|
1610
2009
|
*/
|
|
@@ -1616,7 +2015,7 @@ export declare interface TableSchema {
|
|
|
1616
2015
|
}
|
|
1617
2016
|
|
|
1618
2017
|
/**
|
|
1619
|
-
*
|
|
2018
|
+
* Validates the paging fields of a portable query.
|
|
1620
2019
|
*
|
|
1621
2020
|
* @remarks
|
|
1622
2021
|
* A present `limit` or `offset` must be a finite nonnegative integer; zero is
|