@orkestrel/database 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -35
- package/dist/src/browser/index.d.ts +92 -79
- package/dist/src/browser/index.js +357 -220
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +1796 -803
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +545 -706
- package/dist/src/core/index.d.ts +545 -706
- package/dist/src/core/index.js +1775 -790
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +1508 -757
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +443 -569
- package/dist/src/server/index.d.ts +443 -569
- package/dist/src/server/index.js +1492 -741
- package/dist/src/server/index.js.map +1 -1
- package/package.json +9 -10
package/dist/src/core/index.d.ts
CHANGED
|
@@ -6,25 +6,24 @@ import { EmitterInterface } from '@orkestrel/emitter';
|
|
|
6
6
|
import { FieldPath } from '@orkestrel/contract';
|
|
7
7
|
import { Infer } from '@orkestrel/contract';
|
|
8
8
|
import { JSONSchema } from '@orkestrel/contract';
|
|
9
|
-
import { RandomFunction } from '@orkestrel/contract';
|
|
10
9
|
|
|
11
10
|
/** An aggregate computed over a numeric column. */
|
|
12
|
-
export declare type
|
|
11
|
+
export declare type AggregateOperation = 'count' | 'sum' | 'average' | 'minimum' | 'maximum';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
|
-
* Apply a {@link
|
|
14
|
+
* Apply a {@link QueryInput} to rows — filter, then sort, then page.
|
|
16
15
|
*
|
|
17
16
|
* @remarks
|
|
18
17
|
* The whole portable read pipeline in one place: conditions filter, `order`
|
|
19
18
|
* sorts, and `offset` / `limit` window the result. Each step is skipped when its
|
|
20
|
-
* part of the
|
|
19
|
+
* part of the input is absent. The reference {@link DriverInterface} backends
|
|
21
20
|
* lean on this rather than each re-deriving it.
|
|
22
21
|
*
|
|
23
22
|
* @param rows - The rows to process (typically a table's full `scan`)
|
|
24
|
-
* @param
|
|
23
|
+
* @param input - The read specification, or `undefined` for all rows as-is
|
|
25
24
|
* @returns The filtered, sorted, paged rows
|
|
26
25
|
*/
|
|
27
|
-
export declare function
|
|
26
|
+
export declare function applyQuery(rows: readonly Row[], input?: QueryInput): readonly Row[];
|
|
28
27
|
|
|
29
28
|
/**
|
|
30
29
|
* Run the FULL driver-conformance battery and collect every violation — the
|
|
@@ -50,15 +49,25 @@ export declare function applyCriteria(rows: readonly Row[], criteria?: Criteria)
|
|
|
50
49
|
export declare function auditDriver(factory: () => DriverInterface): Promise<readonly ConformanceFinding[]>;
|
|
51
50
|
|
|
52
51
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
52
|
+
* Return a fresh row whose primary column is authoritatively bound to its storage key.
|
|
53
|
+
*
|
|
54
|
+
* @param row - The caller row
|
|
55
|
+
* @param primary - The primary column
|
|
56
|
+
* @param key - The authoritative storage key
|
|
57
|
+
* @returns A fresh row with the bound primary
|
|
58
|
+
*/
|
|
59
|
+
export declare function bindRowKey(row: Row, primary: string, key: Key): Row;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Throw when an {@link OperationOptions.signal | AbortSignal} has fired — the shared
|
|
63
|
+
* abort gate checked at operation boundaries and between streamed rows.
|
|
55
64
|
*
|
|
56
65
|
* @remarks
|
|
57
66
|
* A no-op for `undefined` or a live signal, so callers thread `options?.signal`
|
|
58
67
|
* straight through. When the signal has aborted, throws an `ABORTED`
|
|
59
68
|
* {@link DatabaseError} carrying the signal's `reason` in its context — callers
|
|
60
|
-
* mint signals with
|
|
61
|
-
* `new AbortController()
|
|
69
|
+
* mint signals with native APIs such as `AbortSignal.timeout(ms)` or
|
|
70
|
+
* `new AbortController()`.
|
|
62
71
|
*
|
|
63
72
|
* @param signal - The signal to check, if any
|
|
64
73
|
* @returns Nothing — returns normally while the signal is live
|
|
@@ -77,59 +86,28 @@ export declare function auditDriver(factory: () => DriverInterface): Promise<rea
|
|
|
77
86
|
export declare function checkAbort(signal: AbortSignal | undefined): void;
|
|
78
87
|
|
|
79
88
|
/**
|
|
80
|
-
*
|
|
89
|
+
* Clone unknown driver metadata into a distinct deeply frozen snapshot.
|
|
81
90
|
*
|
|
82
|
-
* @
|
|
83
|
-
*
|
|
84
|
-
* before it, and a recorder the owning query supplies. Each operator builds the
|
|
85
|
-
* {@link Condition}, hands it to the recorder, and returns the query — so the
|
|
86
|
-
* fluent chain flows straight back into the builder without exposing a mutator.
|
|
91
|
+
* @param value - Unknown metadata
|
|
92
|
+
* @returns Owned driver metadata
|
|
87
93
|
*/
|
|
88
|
-
export declare
|
|
89
|
-
#private;
|
|
90
|
-
constructor(record: (condition: Condition) => QueryInterface<T>, column: FieldPath, connector: Connector);
|
|
91
|
-
equals(value: unknown): QueryInterface<T>;
|
|
92
|
-
not(value: unknown): QueryInterface<T>;
|
|
93
|
-
above(value: unknown): QueryInterface<T>;
|
|
94
|
-
below(value: unknown): QueryInterface<T>;
|
|
95
|
-
from(value: unknown): QueryInterface<T>;
|
|
96
|
-
to(value: unknown): QueryInterface<T>;
|
|
97
|
-
between(lower: unknown, upper: unknown): QueryInterface<T>;
|
|
98
|
-
like(pattern: string): QueryInterface<T>;
|
|
99
|
-
glob(pattern: string): QueryInterface<T>;
|
|
100
|
-
starts(prefix: string): QueryInterface<T>;
|
|
101
|
-
ends(suffix: string): QueryInterface<T>;
|
|
102
|
-
any(values: readonly unknown[]): QueryInterface<T>;
|
|
103
|
-
none(values: readonly unknown[]): QueryInterface<T>;
|
|
104
|
-
absent(): QueryInterface<T>;
|
|
105
|
-
present(): QueryInterface<T>;
|
|
106
|
-
}
|
|
94
|
+
export declare function cloneDriverMetadata(value: unknown): DriverMetadata;
|
|
107
95
|
|
|
108
96
|
/**
|
|
109
|
-
*
|
|
97
|
+
* Clone unknown driver schema into a distinct deeply frozen snapshot.
|
|
110
98
|
*
|
|
111
|
-
* @
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
between(lower: unknown, upper: unknown): QueryInterface<T>;
|
|
124
|
-
like(pattern: string): QueryInterface<T>;
|
|
125
|
-
glob(pattern: string): QueryInterface<T>;
|
|
126
|
-
starts(prefix: string): QueryInterface<T>;
|
|
127
|
-
ends(suffix: string): QueryInterface<T>;
|
|
128
|
-
any(values: readonly unknown[]): QueryInterface<T>;
|
|
129
|
-
none(values: readonly unknown[]): QueryInterface<T>;
|
|
130
|
-
absent(): QueryInterface<T>;
|
|
131
|
-
present(): QueryInterface<T>;
|
|
132
|
-
}
|
|
99
|
+
* @param value - Unknown table schema collection
|
|
100
|
+
* @returns Owned driver schema
|
|
101
|
+
*/
|
|
102
|
+
export declare function cloneDriverSchema(value: unknown): readonly TableSchema[];
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Clone unknown migration input into a distinct deeply frozen snapshot.
|
|
106
|
+
*
|
|
107
|
+
* @param value - Unknown migration input
|
|
108
|
+
* @returns Owned migration input
|
|
109
|
+
*/
|
|
110
|
+
export declare function cloneMigrationInput(value: unknown): MigrationInput;
|
|
133
111
|
|
|
134
112
|
/**
|
|
135
113
|
* One table's columns — a map of column name to its value {@link ContractShape}.
|
|
@@ -141,25 +119,27 @@ export declare interface ClauseInterface<T = Row> {
|
|
|
141
119
|
* at the table level. (Nested object *columns* still use `objectShape`, since a
|
|
142
120
|
* column is not always an object.)
|
|
143
121
|
*/
|
|
144
|
-
export declare type
|
|
122
|
+
export declare type ColumnMap = Readonly<Record<string, ContractShape>>;
|
|
145
123
|
|
|
146
124
|
/**
|
|
147
|
-
* One column of a {@link TableSchema} — its name, portable {@link
|
|
148
|
-
* whether it
|
|
125
|
+
* One column of a {@link TableSchema} — its name, portable {@link ColumnStorage}, and
|
|
126
|
+
* whether it independently accepts absence (`optional`) and explicit `null`
|
|
127
|
+
* (`nullable`).
|
|
149
128
|
*/
|
|
150
129
|
export declare interface ColumnSchema {
|
|
151
130
|
readonly name: string;
|
|
152
|
-
readonly
|
|
131
|
+
readonly storage: ColumnStorage;
|
|
132
|
+
readonly optional: boolean;
|
|
153
133
|
readonly nullable: boolean;
|
|
154
134
|
}
|
|
155
135
|
|
|
156
136
|
/**
|
|
157
137
|
* A portable storage type for a column — the backend maps it to its native type
|
|
158
138
|
* (SQLite affinity, an IndexedDB value). Derived from a column's `ContractShape`
|
|
159
|
-
* by `
|
|
139
|
+
* by `shapeToColumnStorage`; `json` covers object/array/union/raw values a backend stores
|
|
160
140
|
* as JSON text and can `json_extract` for nested-field queries.
|
|
161
141
|
*/
|
|
162
|
-
export declare type
|
|
142
|
+
export declare type ColumnStorage = 'text' | 'integer' | 'real' | 'boolean' | 'json' | 'blob';
|
|
163
143
|
|
|
164
144
|
/**
|
|
165
145
|
* A total ordering over arbitrary values — the comparator behind sorting and the
|
|
@@ -191,7 +171,7 @@ export declare function compareValues(left: unknown, right: unknown): number;
|
|
|
191
171
|
* @param column - The column to aggregate
|
|
192
172
|
* @returns The aggregate value, or `undefined` when undefined for the inputs
|
|
193
173
|
*/
|
|
194
|
-
export declare function computeAggregate(rows: readonly unknown[], operation:
|
|
174
|
+
export declare function computeAggregate(rows: readonly unknown[], operation: AggregateOperation, column: FieldPath): number | undefined;
|
|
195
175
|
|
|
196
176
|
/**
|
|
197
177
|
* One compiled WHERE condition.
|
|
@@ -208,9 +188,12 @@ export declare interface Condition {
|
|
|
208
188
|
readonly column: FieldPath;
|
|
209
189
|
readonly operator: ConditionOperator;
|
|
210
190
|
readonly values: readonly unknown[];
|
|
211
|
-
readonly connector:
|
|
191
|
+
readonly connector: ConditionConnector;
|
|
212
192
|
}
|
|
213
193
|
|
|
194
|
+
/** How a {@link Condition} joins to the running result of the conditions before it. */
|
|
195
|
+
export declare type ConditionConnector = 'and' | 'or';
|
|
196
|
+
|
|
214
197
|
/**
|
|
215
198
|
* A WHERE operator — the comparison a single {@link Condition} applies.
|
|
216
199
|
*
|
|
@@ -261,9 +244,6 @@ export declare interface ConformanceFinding {
|
|
|
261
244
|
*/
|
|
262
245
|
export declare function conformDriver(factory: () => DriverInterface): Promise<void>;
|
|
263
246
|
|
|
264
|
-
/** How a {@link Condition} joins to the running result of the conditions before it. */
|
|
265
|
-
export declare type Connector = 'and' | 'or';
|
|
266
|
-
|
|
267
247
|
/**
|
|
268
248
|
* Create a database over a driver and a declared `tables` schema.
|
|
269
249
|
*
|
|
@@ -273,10 +253,10 @@ export declare type Connector = 'and' | 'or';
|
|
|
273
253
|
* level. The `const` type parameter captures the literal names and columns, so
|
|
274
254
|
* `db.table('users')` is checked against the schema and typed by `Infer` of its
|
|
275
255
|
* columns — no annotations. Name a non-`id` primary-key column per table via the
|
|
276
|
-
* optional `
|
|
256
|
+
* optional `primary` and `indexes` maps.
|
|
277
257
|
*
|
|
278
|
-
* @param options - The driver,
|
|
279
|
-
*
|
|
258
|
+
* @param options - The driver, `tables`, and optional `primary`, `indexes`,
|
|
259
|
+
* `name`, `generator`, `version`, and emitter hooks
|
|
280
260
|
* @returns A typed {@link DatabaseInterface}
|
|
281
261
|
*
|
|
282
262
|
* @example
|
|
@@ -290,12 +270,12 @@ export declare type Connector = 'and' | 'or';
|
|
|
290
270
|
* users: { id: stringShape(), age: integerShape() },
|
|
291
271
|
* posts: { slug: stringShape(), title: stringShape() },
|
|
292
272
|
* },
|
|
293
|
-
*
|
|
273
|
+
* primary: { posts: 'slug' },
|
|
294
274
|
* })
|
|
295
275
|
* await db.table('users').set({ id: 'u1', age: 36 }) // typed; coerced + validated
|
|
296
276
|
* ```
|
|
297
277
|
*/
|
|
298
|
-
export declare function createDatabase<const T extends
|
|
278
|
+
export declare function createDatabase<const T extends TableMap>(options: DatabaseOptions<T>): DatabaseInterface<T>;
|
|
299
279
|
|
|
300
280
|
/**
|
|
301
281
|
* Create the in-memory reference {@link DriverInterface}.
|
|
@@ -308,49 +288,17 @@ export declare function createDatabase<const T extends TablesShape>(options: Dat
|
|
|
308
288
|
*/
|
|
309
289
|
export declare function createMemoryDriver(): DriverInterface;
|
|
310
290
|
|
|
311
|
-
/**
|
|
312
|
-
* A serializable read specification — everything a backend needs to compile one
|
|
313
|
-
* read, free of JS callbacks so any backend can honor it.
|
|
314
|
-
*
|
|
315
|
-
* @remarks
|
|
316
|
-
* The post-fetch `filter` predicate lives on {@link QueryInterface}, never here,
|
|
317
|
-
* so `Criteria` stays portable across backends.
|
|
318
|
-
*/
|
|
319
|
-
export declare interface Criteria {
|
|
320
|
-
readonly conditions?: readonly Condition[];
|
|
321
|
-
readonly order?: readonly Order[];
|
|
322
|
-
readonly limit?: number;
|
|
323
|
-
readonly offset?: number;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* A forward row cursor for bulk in-place mutation.
|
|
328
|
-
*
|
|
329
|
-
* @remarks
|
|
330
|
-
* Iterates a snapshot of the table's keys captured when the cursor was opened,
|
|
331
|
-
* reading each row lazily through the owning table — so a mutation made during
|
|
332
|
-
* iteration cannot corrupt the walk, and a key removed mid-iteration is simply
|
|
333
|
-
* skipped. `update` and `remove` act on the row at the current position.
|
|
334
|
-
*/
|
|
335
|
-
export declare class Cursor<T = Record<string, unknown>> implements CursorInterface<T> {
|
|
336
|
-
#private;
|
|
337
|
-
constructor(table: TableInterface<T>, keys: readonly Key[]);
|
|
338
|
-
get value(): T | undefined;
|
|
339
|
-
get index(): number;
|
|
340
|
-
get done(): boolean;
|
|
341
|
-
next(): Promise<void>;
|
|
342
|
-
update(changes: Partial<T>): Promise<void>;
|
|
343
|
-
remove(): Promise<void>;
|
|
344
|
-
close(): void;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
291
|
/**
|
|
348
292
|
* A forward row cursor for bulk in-place mutation.
|
|
349
293
|
*
|
|
350
294
|
* @remarks
|
|
351
295
|
* Iterates a snapshot of the table's keys taken at creation; `update` and
|
|
352
296
|
* `remove` act on the row at the current position through the owning table.
|
|
353
|
-
*
|
|
297
|
+
* Promise operations execute serially in invocation order, and one rejection
|
|
298
|
+
* does not prevent later admitted work. `done` is `true` once iteration has
|
|
299
|
+
* advanced past the last key. `close` is synchronous and terminal: it clears
|
|
300
|
+
* the current value, queued work becomes a no-op, and in-flight work never
|
|
301
|
+
* republishes a value after settling.
|
|
354
302
|
*/
|
|
355
303
|
export declare interface CursorInterface<T = Row> {
|
|
356
304
|
readonly value: T | undefined;
|
|
@@ -363,82 +311,27 @@ export declare interface CursorInterface<T = Row> {
|
|
|
363
311
|
}
|
|
364
312
|
|
|
365
313
|
/**
|
|
366
|
-
* A database
|
|
367
|
-
*
|
|
368
|
-
* @remarks
|
|
369
|
-
* Owns the driver and a `tables` shape map, connecting the driver lazily on first
|
|
370
|
-
* use so a freshly created database is immediately usable. `table(name)` returns
|
|
371
|
-
* a table typed by that table's shape `Infer`. `import` registers more tables and
|
|
372
|
-
* returns a database re-typed with them over the **same** driver and storage;
|
|
373
|
-
* `export` emits a portable {@link TableExport} per table. `transaction` snapshots
|
|
374
|
-
* the driver, runs the scope, and rolls every table back if it throws — an
|
|
375
|
-
* optimistic model that works uniformly across backends rather than reconciling
|
|
376
|
-
* SQL's and IndexedDB's incompatible native transactions.
|
|
314
|
+
* A typed database view over one shared internal lifecycle and storage context.
|
|
377
315
|
*
|
|
378
316
|
* @remarks
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
* connection + transaction lifecycle — `open` / `close` / `transaction` / `commit` /
|
|
386
|
-
* `rollback` — for fire-and-forget observers, ALONGSIDE each table's per-row events. Every
|
|
387
|
-
* event is emitted directly, strictly AFTER the relevant transition: `commit` only after
|
|
388
|
-
* the scope succeeds, `rollback` only after every table is restored. The `rollback` emit
|
|
389
|
-
* OBSERVES the propagated error — it never swallows it (the original throw propagates
|
|
390
|
-
* exactly as before). The emitter isolates a listener throw and routes it to its `error`
|
|
391
|
-
* handler (the `error` option), so observation can never reorder, throw into, or corrupt
|
|
392
|
-
* the snapshot / commit / rollback flow.
|
|
393
|
-
*/
|
|
394
|
-
export declare class Database<T extends TablesShape = TablesShape> implements DatabaseInterface<T> {
|
|
317
|
+
* Each view owns only its table contracts, primary columns, indexes, and key
|
|
318
|
+
* generator. Imported views register their physical schemas with the same
|
|
319
|
+
* internal context before opening begins, so every view observes one driver,
|
|
320
|
+
* merged schema, emitter, status, transaction boundary, and terminal close.
|
|
321
|
+
*/
|
|
322
|
+
export declare class Database<T extends TableMap = TableMap> implements DatabaseInterface<T> {
|
|
395
323
|
#private;
|
|
396
324
|
constructor(options: DatabaseOptions<T>);
|
|
397
325
|
get emitter(): EmitterInterface<DatabaseEventMap>;
|
|
398
326
|
get name(): string;
|
|
399
327
|
get status(): DatabaseStatus;
|
|
400
328
|
table<K extends keyof T & string>(name: K): TableInterface<RowOf<T[K]>>;
|
|
401
|
-
import<U extends
|
|
402
|
-
export(): Readonly<Record<string,
|
|
329
|
+
import<U extends TableMap>(tables: U, primary?: PrimaryMap): DatabaseInterface<U>;
|
|
330
|
+
export(): Readonly<Record<string, TableDefinition>>;
|
|
403
331
|
open(): Promise<void>;
|
|
404
332
|
close(): Promise<void>;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
* back if it throws.
|
|
408
|
-
*
|
|
409
|
-
* @remarks
|
|
410
|
-
* When the driver implements the optional native {@link DriverInterface.transaction}
|
|
411
|
-
* hook, that native `commit` / `rollback` handle drives the transaction; otherwise
|
|
412
|
-
* the universal snapshot floor (`driver.snapshot()`) runs unchanged. Either path
|
|
413
|
-
* emits the same `transaction` / `commit` / `rollback` lifecycle (AGENTS §13).
|
|
414
|
-
* `options.signal` is checked ONCE at entry, before connecting or starting any
|
|
415
|
-
* transactional work — an already-aborted signal throws `ABORTED` and neither the
|
|
416
|
-
* native hook nor the snapshot floor is invoked. Nesting is unguarded and
|
|
417
|
-
* unsupported exactly as before: this is a single-writer model, not reentrant.
|
|
418
|
-
* On the native path, a `scope` throw rolls back via the native handle; a
|
|
419
|
-
* native `commit` failure propagates as-is with no rollback attempt — the
|
|
420
|
-
* engine owns transaction state after a failed COMMIT.
|
|
421
|
-
*
|
|
422
|
-
* @param scope - The transactional work to run
|
|
423
|
-
* @param options - `{ signal }` to abort before the transaction starts
|
|
424
|
-
* @returns The scope's resolved value
|
|
425
|
-
* @throws An `ABORTED` {@link DatabaseError} when `options.signal` has already fired
|
|
426
|
-
*/
|
|
427
|
-
transaction<R>(scope: () => Promise<R>, options?: ReadOptions): Promise<R>;
|
|
428
|
-
/**
|
|
429
|
-
* Diff `deployed` against this database's declared schema and apply the
|
|
430
|
-
* resulting plan through the driver's optional `migrate` hook.
|
|
431
|
-
*
|
|
432
|
-
* @param deployed - The schema currently deployed, as {@link TableSchema}s
|
|
433
|
-
* @param options - `{ signal }` to abort before the migration starts
|
|
434
|
-
* @returns The applied {@link Migration} plan
|
|
435
|
-
* @throws A `MIGRATION` {@link DatabaseError} when the driver does not
|
|
436
|
-
* implement `migrate`, or when a step references an unknown table
|
|
437
|
-
* (propagated from the driver)
|
|
438
|
-
* @throws An `ABORTED` {@link DatabaseError} when `options.signal` has
|
|
439
|
-
* already fired at entry
|
|
440
|
-
*/
|
|
441
|
-
migrate(deployed: readonly TableSchema[], options?: ReadOptions): Promise<Migration>;
|
|
333
|
+
transaction<R>(scope: (transaction: DatabaseStorageInterface<T>) => Promise<R>, options?: OperationOptions): Promise<R>;
|
|
334
|
+
migrate(deployed: readonly TableSchema[], options?: OperationOptions): Promise<Migration>;
|
|
442
335
|
}
|
|
443
336
|
|
|
444
337
|
/**
|
|
@@ -448,8 +341,8 @@ export declare class Database<T extends TablesShape = TablesShape> implements Da
|
|
|
448
341
|
* Carries a {@link DatabaseErrorCode} and an optional `context` bag naming the
|
|
449
342
|
* offending table / key. Thrown for: operating on a closed database (`CLOSED`), a
|
|
450
343
|
* `resolve` miss (`NOT_FOUND`), an `add` onto an existing key (`CONFLICT`), a
|
|
451
|
-
* row that fails its table's contract (`VALIDATION`),
|
|
452
|
-
* {@link
|
|
344
|
+
* row that fails its table's contract (`VALIDATION`), an aborted operation whose
|
|
345
|
+
* {@link OperationOptions.signal} aborted (`ABORTED`, carrying `signal.reason` in
|
|
453
346
|
* `context`), an inapplicable {@link Migration} plan (`MIGRATION`), a
|
|
454
347
|
* driver that violates a {@link DriverInterface} invariant, thrown by the
|
|
455
348
|
* `conformDriver` helper (`CONFORMANCE`), and an unexpected infrastructure
|
|
@@ -478,9 +371,10 @@ export declare type DatabaseErrorCode = 'CLOSED' | 'NOT_FOUND' | 'CONFLICT' | 'V
|
|
|
478
371
|
* listener throw is routed to the emitter's OWN `error` handler (the `error` option), never
|
|
479
372
|
* onto this domain map and never into the snapshot / commit / rollback flow — so a buggy
|
|
480
373
|
* observer can never reorder, throw into, or corrupt a transaction. Every emit sits AFTER the
|
|
481
|
-
* relevant transition: `commit` only after the scope succeeds, `rollback` only after
|
|
482
|
-
*
|
|
483
|
-
* propagates
|
|
374
|
+
* relevant transition: `commit` only after the scope succeeds, `rollback` only after the
|
|
375
|
+
* rollback operation completes (it OBSERVES the propagated scope error; that exact reason
|
|
376
|
+
* still propagates). A rollback failure propagates instead and emits no misleading
|
|
377
|
+
* `rollback` event. Subscribe via `database.emitter.on(...)`.
|
|
484
378
|
*
|
|
485
379
|
* Declared as a `type` alias (not `interface extends EventMap`, §4.5 — `EventMap` is a
|
|
486
380
|
* `type` kind): a type-literal satisfies the `EventMap` constraint
|
|
@@ -492,11 +386,11 @@ export declare type DatabaseEventMap = {
|
|
|
492
386
|
readonly open: readonly [];
|
|
493
387
|
/** The database was closed (the driver released). */
|
|
494
388
|
readonly close: readonly [];
|
|
495
|
-
/** A transaction scope began
|
|
389
|
+
/** A transaction scope began after its native boundary or fallback snapshot was acquired. */
|
|
496
390
|
readonly transaction: readonly [];
|
|
497
391
|
/** A transaction scope completed successfully (no rollback). */
|
|
498
392
|
readonly commit: readonly [];
|
|
499
|
-
/** A transaction scope
|
|
393
|
+
/** A transaction scope failed and rollback completed — the exact propagated scope error. */
|
|
500
394
|
readonly rollback: readonly [error: unknown];
|
|
501
395
|
/** A {@link Migration} plan was applied via `migrate` — the applied plan. */
|
|
502
396
|
readonly migrate: readonly [migration: Migration];
|
|
@@ -512,22 +406,21 @@ export declare type DatabaseEventMap = {
|
|
|
512
406
|
* created database is immediately usable. `import` defines more than one table
|
|
513
407
|
* from a shape map and returns a new typed view of **those** tables over the
|
|
514
408
|
* same driver and storage (so views can be split by concern and still share
|
|
515
|
-
* data); `export` produces a portable {@link
|
|
516
|
-
* schema between databases or environments. `transaction`
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
* IndexedDB's incompatible native transactions.
|
|
409
|
+
* data); `export` produces a portable {@link TableDefinition} per table for moving a
|
|
410
|
+
* schema between databases or environments. `transaction` runs a table-only
|
|
411
|
+
* scoped callback through a native driver transaction when available, otherwise
|
|
412
|
+
* through the universal whole-store snapshot floor.
|
|
520
413
|
*/
|
|
521
|
-
export declare interface DatabaseInterface<T extends
|
|
414
|
+
export declare interface DatabaseInterface<T extends TableMap = TableMap> {
|
|
522
415
|
readonly emitter: EmitterInterface<DatabaseEventMap>;
|
|
523
416
|
readonly name: string;
|
|
524
417
|
readonly status: DatabaseStatus;
|
|
525
418
|
table<K extends keyof T & string>(name: K): TableInterface<RowOf<T[K]>>;
|
|
526
|
-
import<U extends
|
|
527
|
-
export(): Readonly<Record<string,
|
|
419
|
+
import<U extends TableMap>(tables: U, primary?: PrimaryMap): DatabaseInterface<U>;
|
|
420
|
+
export(): Readonly<Record<string, TableDefinition>>;
|
|
528
421
|
open(): Promise<void>;
|
|
529
422
|
close(): Promise<void>;
|
|
530
|
-
transaction<R>(scope: () => Promise<R>, options?:
|
|
423
|
+
transaction<R>(scope: (transaction: DatabaseStorageInterface<T>) => Promise<R>, options?: OperationOptions): Promise<R>;
|
|
531
424
|
/**
|
|
532
425
|
* Diff a caller-supplied deployed schema against this database's declared
|
|
533
426
|
* schema (its `tables`, as configured) via `planMigration`, apply the
|
|
@@ -542,11 +435,20 @@ export declare interface DatabaseInterface<T extends TablesShape = TablesShape>
|
|
|
542
435
|
* Throws `DatabaseError` `MIGRATION` when the driver does not implement
|
|
543
436
|
* `migrate`, or when a step references an unknown table (propagated from
|
|
544
437
|
* the driver). Throws `ABORTED` when `options.signal` has already fired at
|
|
545
|
-
* entry.
|
|
546
|
-
*
|
|
547
|
-
*
|
|
438
|
+
* entry. For explicit `migrate`, driver open and migration apply form one
|
|
439
|
+
* readiness transition: status and the `open` event publish only after apply
|
|
440
|
+
* succeeds. A failed explicit apply blocks ordinary open/table work with that
|
|
441
|
+
* exact failure until a later explicit `migrate` succeeds; `close` remains
|
|
442
|
+
* available. Automatic versioned open has a separate lifecycle: successful
|
|
443
|
+
* physical driver open publishes `open` status and one `open` event before
|
|
444
|
+
* reconciliation. If reconciliation fails, its readiness Promise and table
|
|
445
|
+
* work reject until a later automatic retry succeeds on the same physical
|
|
446
|
+
* handle. Emits the `migrate` event after a successful apply. Explicit
|
|
447
|
+
* migration still accepts the caller's deployed schema; versioned open uses
|
|
448
|
+
* {@link DatabaseOptions.version} with a driver's paired `metadata` / `stamp`
|
|
449
|
+
* capabilities to persist and reconcile deployed version metadata.
|
|
548
450
|
*/
|
|
549
|
-
migrate(deployed: readonly TableSchema[], options?:
|
|
451
|
+
migrate(deployed: readonly TableSchema[], options?: OperationOptions): Promise<Migration>;
|
|
550
452
|
}
|
|
551
453
|
|
|
552
454
|
/**
|
|
@@ -554,37 +456,55 @@ export declare interface DatabaseInterface<T extends TablesShape = TablesShape>
|
|
|
554
456
|
*
|
|
555
457
|
* @remarks
|
|
556
458
|
* `driver` is the storage backend; `tables` declares each table's columns;
|
|
557
|
-
* `
|
|
459
|
+
* `primary` overrides the primary-key column per table ({@link DEFAULT_PRIMARY}
|
|
558
460
|
* otherwise); `indexes` declares secondary indexes per table (contracts don't
|
|
559
461
|
* express them) that flow into each derived {@link TableSchema}; `name` labels
|
|
560
462
|
* the database; `on` wires initial {@link DatabaseEventMap} listeners (§8); `error`
|
|
561
463
|
* is the emitter's listener-error handler (§13 — a listener throw routes here);
|
|
562
|
-
* `
|
|
563
|
-
*
|
|
564
|
-
*
|
|
464
|
+
* `generator` is the authoritative key-generation override a table uses when a
|
|
465
|
+
* written row's primary is exactly `undefined`. When omitted, the table uses
|
|
466
|
+
* global `crypto.randomUUID()`; numeric primary keys require a custom generator.
|
|
565
467
|
*/
|
|
566
|
-
export declare interface DatabaseOptions<T extends
|
|
468
|
+
export declare interface DatabaseOptions<T extends TableMap = TableMap> {
|
|
567
469
|
readonly on?: EmitterHooks<DatabaseEventMap>;
|
|
568
|
-
/**
|
|
470
|
+
/**
|
|
471
|
+
* The listener-error handler shared by the database and every table emitter.
|
|
472
|
+
*
|
|
473
|
+
* @remarks
|
|
474
|
+
* Listener throws from root, imported, and transaction-scoped handles route
|
|
475
|
+
* here as `(error, event)`, never to a domain event and never into the
|
|
476
|
+
* completed operation.
|
|
477
|
+
*/
|
|
569
478
|
readonly error?: EmitterErrorHandler;
|
|
570
479
|
readonly driver: DriverInterface;
|
|
571
480
|
readonly tables: T;
|
|
572
|
-
readonly
|
|
573
|
-
readonly indexes?:
|
|
481
|
+
readonly primary?: PrimaryMap;
|
|
482
|
+
readonly indexes?: IndexMap;
|
|
574
483
|
readonly name?: string;
|
|
575
|
-
|
|
484
|
+
/**
|
|
485
|
+
* The authoritative key-generation override for a keyless write.
|
|
486
|
+
*
|
|
487
|
+
* @remarks
|
|
488
|
+
* Omit it to use global `crypto.randomUUID()`. A numeric primary requires a
|
|
489
|
+
* custom generator. Explicit primary values never invoke this function. A
|
|
490
|
+
* custom generator throw is `VALIDATION`; a host
|
|
491
|
+
* `crypto.randomUUID()` failure is `DRIVER`. An invalid returned key is
|
|
492
|
+
* `VALIDATION`; neither branch falls back or retries.
|
|
493
|
+
*/
|
|
494
|
+
readonly generator?: KeyFunction;
|
|
576
495
|
/**
|
|
577
496
|
* The declared schema version.
|
|
578
497
|
*
|
|
579
498
|
* @remarks
|
|
580
|
-
* Only meaningful when the driver implements BOTH {@link DriverInterface.
|
|
499
|
+
* Only meaningful when the driver implements BOTH {@link DriverInterface.metadata}
|
|
581
500
|
* and {@link DriverInterface.stamp} (a versioning driver); unset, or a
|
|
582
501
|
* non-versioning driver, leaves `open()` unchanged from today's behavior.
|
|
583
502
|
* When set and the driver versions, `open()` reconciles against the
|
|
584
|
-
* driver's persisted {@link
|
|
585
|
-
* - **Fresh store** (`
|
|
586
|
-
*
|
|
587
|
-
*
|
|
503
|
+
* driver's persisted {@link DriverMetadata}:
|
|
504
|
+
* - **Fresh store** (`metadata()` returns `undefined` after the durable
|
|
505
|
+
* driver proves absence) — no migration is possible (there is nothing
|
|
506
|
+
* deployed to diff against), so `open()` simply `stamp`s
|
|
507
|
+
* `{ version, schema }` for next time.
|
|
588
508
|
* - **Stored version < `version`** — `planMigration(stored.schema, declared
|
|
589
509
|
* schema)` computes the upgrade plan, applied via the driver's optional
|
|
590
510
|
* `migrate` hook. If `migrate` is absent and the plan is non-empty,
|
|
@@ -592,13 +512,14 @@ export declare interface DatabaseOptions<T extends TablesShape = TablesShape> {
|
|
|
592
512
|
* `stamp`s the new `{ version, schema }` and emits the `migrate` event.
|
|
593
513
|
* - **Stored version > `version`** — the store is newer than the declared
|
|
594
514
|
* schema; `open()` throws `DatabaseError` `MIGRATION`.
|
|
595
|
-
* - **Stored version === `version`** —
|
|
515
|
+
* - **Stored version === `version`** — the persisted and declared schemas
|
|
516
|
+
* must still match. Schema drift throws `DatabaseError` `MIGRATION`;
|
|
517
|
+
* otherwise `open()` is a no-op.
|
|
596
518
|
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
* failure after a successful `migrate` can leave new data under old meta.
|
|
519
|
+
* A non-empty version upgrade passes both the plan and target metadata to
|
|
520
|
+
* {@link DriverInterface.migrate} as one {@link MigrationInput}; the driver
|
|
521
|
+
* settles both atomically. A zero-step version transition may use `stamp`
|
|
522
|
+
* directly because no schema or rows change.
|
|
602
523
|
*/
|
|
603
524
|
readonly version?: number;
|
|
604
525
|
}
|
|
@@ -607,45 +528,28 @@ export declare interface DatabaseOptions<T extends TablesShape = TablesShape> {
|
|
|
607
528
|
export declare type DatabaseStatus = 'idle' | 'open' | 'closed';
|
|
608
529
|
|
|
609
530
|
/**
|
|
610
|
-
*
|
|
611
|
-
*
|
|
531
|
+
* A database view valid only inside one {@link DatabaseInterface.transaction}
|
|
532
|
+
* scope.
|
|
612
533
|
*
|
|
613
534
|
* @remarks
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
* with a `deepEqual` value — so a key present with value `undefined` is NOT
|
|
619
|
-
* equal to that key being absent (both differ in `Object.keys` membership).
|
|
620
|
-
* Anything else (functions, class instances, mismatched shapes) falls through
|
|
621
|
-
* to `false`. There is no cycle detection — a cyclic input recurses forever;
|
|
622
|
-
* callers pass acyclic data (rows, plans, config).
|
|
623
|
-
*
|
|
624
|
-
* @param left - The left value
|
|
625
|
-
* @param right - The right value
|
|
626
|
-
* @returns Whether `left` and `right` are structurally equal
|
|
627
|
-
*
|
|
628
|
-
* @example
|
|
629
|
-
* ```ts
|
|
630
|
-
* deepEqual(Number.NaN, Number.NaN) // true
|
|
631
|
-
* deepEqual({ a: [1, { b: 2 }] }, { a: [1, { b: 2 }] }) // true
|
|
632
|
-
* deepEqual({ a: undefined }, {}) // false — present-undefined ≠ absent
|
|
633
|
-
* ```
|
|
535
|
+
* The view exposes only declared tables backed by the driver's scoped
|
|
536
|
+
* {@link StorageInterface}. It cannot open, close, import, migrate, or nest
|
|
537
|
+
* a transaction. The view and every table captured from it throw `CONFLICT`
|
|
538
|
+
* after the scope settles.
|
|
634
539
|
*/
|
|
635
|
-
export declare
|
|
540
|
+
export declare interface DatabaseStorageInterface<T extends TableMap = TableMap> {
|
|
541
|
+
table<K extends keyof T & string>(name: K): TableInterface<RowOf<T[K]>>;
|
|
542
|
+
}
|
|
636
543
|
|
|
637
544
|
/**
|
|
638
|
-
* The primary-key column assumed when {@link
|
|
545
|
+
* The primary-key column assumed when {@link PrimaryMap} does not name one.
|
|
639
546
|
*
|
|
640
547
|
* @remarks
|
|
641
548
|
* `id` is the convention IndexedDB (`keyPath: 'id'`) and SQL (`id` / rowid) both
|
|
642
|
-
* lean on, so a table
|
|
549
|
+
* lean on, so a table without a `primary` override keys its rows by `id`.
|
|
643
550
|
*/
|
|
644
551
|
export declare const DEFAULT_PRIMARY = "id";
|
|
645
552
|
|
|
646
|
-
/** A sort direction. */
|
|
647
|
-
export declare type Direction = 'ascending' | 'descending';
|
|
648
|
-
|
|
649
553
|
/**
|
|
650
554
|
* Run the driver-conformance battery against a fresh {@link DriverInterface}
|
|
651
555
|
* per phase, yielding one {@link ConformanceFinding} per violated invariant —
|
|
@@ -661,21 +565,24 @@ export declare type Direction = 'ascending' | 'descending';
|
|
|
661
565
|
* `read` of a missing key returns `undefined`; `write`/`read` round-trip with
|
|
662
566
|
* DEEP copy-in/copy-out isolation (mutating the caller's row — including a
|
|
663
567
|
* NESTED field — after `write`, or a row `read` returns, never perturbs
|
|
664
|
-
* stored state) and upsert-overwrite;
|
|
568
|
+
* stored state) and upsert-overwrite; simultaneous same-key `insert` calls
|
|
569
|
+
* produce exactly one commit and one `CONFLICT`; pre-aborted `write`,
|
|
570
|
+
* `insert`, and `delete` calls leave storage unchanged; `delete` returns
|
|
571
|
+
* `true` then `false`;
|
|
665
572
|
* `keys`/`scan` yield in ascending key order; `clear` empties only its target
|
|
666
573
|
* table; `snapshot`'s rollback thunk restores pre-snapshot state, including a
|
|
667
574
|
* NESTED field mutated in place on a read-back row between capture and
|
|
668
575
|
* restore; a scoped `snapshot(['users'])` rolls back only the named table,
|
|
669
576
|
* leaving a concurrent mutation to another table intact; a
|
|
670
577
|
* non-`id` primary key (`posts.slug`) round-trips; a nested-object row
|
|
671
|
-
* round-trips structurally (via {@link
|
|
578
|
+
* round-trips structurally (via {@link equalsValue}). The optional surface is
|
|
672
579
|
* presence-gated: when `migrate` exists, a `column.remove` plan strips the
|
|
673
580
|
* column from stored rows and a plan referencing an unknown table throws
|
|
674
581
|
* `DatabaseError` `MIGRATION`; when `stream` exists, it yields only
|
|
675
582
|
* condition-matching rows and honors `offset`/`limit`; when `transaction`
|
|
676
|
-
* exists, `commit` persists and `rollback` restores; when both `
|
|
677
|
-
* `stamp` exist, a fresh store's `
|
|
678
|
-
* `stamp({ version, schema })`, `
|
|
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.
|
|
679
586
|
*
|
|
680
587
|
* Each phase runs within a `try`/`catch`: an EXPECTED mismatch yields a
|
|
681
588
|
* finding built from the assertion, while an UNEXPECTED throw (a driver
|
|
@@ -706,107 +613,92 @@ export declare function driverFindings(factory: () => DriverInterface): AsyncIte
|
|
|
706
613
|
* The storage primitive every backend implements — the whole of the bridge.
|
|
707
614
|
*
|
|
708
615
|
* @remarks
|
|
709
|
-
* The REQUIRED surface is deliberately minimal: keyed read / write /
|
|
710
|
-
* ordered `scan`, a key listing, and a `snapshot` that backs
|
|
711
|
-
* irreducible primitive. There is **no** required query,
|
|
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
|
|
712
620
|
* here: all of that is one query engine in the core (`helpers.ts`) running over
|
|
713
621
|
* `scan`, so a new backend implements a handful of tiny methods rather than
|
|
714
622
|
* re-deriving WHERE compilation. `open` now receives a derived
|
|
715
623
|
* {@link TableSchema}`[]` (columns, types, primary, indexes) so a native backend
|
|
716
624
|
* can build real tables and indexes; a scan-only backend reads only `name`. The
|
|
717
|
-
* optional `records?` / `
|
|
625
|
+
* optional `records?` / `aggregate?` are native overrides the engine
|
|
718
626
|
* falls back from (AGENTS §21). The API is async (Promises) because IndexedDB is; synchronous
|
|
719
627
|
* backends resolve immediately. Lookups that may miss return `undefined` /
|
|
720
|
-
* `false` rather than throwing (AGENTS §12).
|
|
628
|
+
* `false` rather than throwing (AGENTS §12). Metadata has the same ownership
|
|
629
|
+
* boundary across every implementation: `stamp` and `migrate` snapshot
|
|
630
|
+
* {@link DriverMetadata} at entry, while `metadata` returns a distinct deeply frozen
|
|
631
|
+
* snapshot. A durable driver returns `undefined` only when it proves the
|
|
632
|
+
* metadata record or durable store is absent. Existing unreadable or malformed
|
|
633
|
+
* durable state fails `open` / `metadata` closed; it is never treated as fresh,
|
|
634
|
+
* rewritten, or repaired automatically.
|
|
721
635
|
*/
|
|
722
|
-
export declare interface DriverInterface {
|
|
636
|
+
export declare interface DriverInterface extends StorageInterface {
|
|
723
637
|
open(schema: readonly TableSchema[]): Promise<void>;
|
|
724
638
|
close(): Promise<void>;
|
|
725
|
-
read(table: string, key: Key): Promise<Row | undefined>;
|
|
726
|
-
write(table: string, key: Key, row: Row): Promise<void>;
|
|
727
|
-
delete(table: string, key: Key): Promise<boolean>;
|
|
728
|
-
keys(table: string): Promise<readonly Key[]>;
|
|
729
|
-
scan(table: string): AsyncIterable<Row>;
|
|
730
|
-
clear(table: string): Promise<void>;
|
|
731
639
|
/**
|
|
732
|
-
* Capture
|
|
733
|
-
*
|
|
734
|
-
* key buffer, a cloned map).
|
|
640
|
+
* Capture table rows and return a repeatable thunk that restores those rows —
|
|
641
|
+
* the primitive transactions are built on.
|
|
735
642
|
*
|
|
736
643
|
* @remarks
|
|
737
|
-
*
|
|
738
|
-
* `tables`
|
|
739
|
-
*
|
|
644
|
+
* Snapshots are row-only: they never restore schema or driver metadata.
|
|
645
|
+
* `tables` omitted captures every current table. `tables` provided captures
|
|
646
|
+
* only existing named tables. Rollback skips captured tables removed since
|
|
647
|
+
* capture and leaves every uncaptured or later-added table untouched.
|
|
740
648
|
*/
|
|
741
649
|
snapshot(tables?: readonly string[]): Promise<() => Promise<void>>;
|
|
742
650
|
/**
|
|
743
|
-
* Optional native
|
|
744
|
-
*
|
|
745
|
-
* implements this; `Table` prefers it and falls back to `applyCriteria` over
|
|
746
|
-
* `scan` when it is absent. Must honor the full criteria (filter, order, page).
|
|
747
|
-
*/
|
|
748
|
-
records?(table: string, criteria: Criteria): Promise<readonly Row[]>;
|
|
749
|
-
/**
|
|
750
|
-
* Optional native count (AGENTS §21). Counts rows matching the criteria's
|
|
751
|
-
* conditions (paging is irrelevant to a count); `Table` falls back to counting
|
|
752
|
-
* the engine-filtered `scan` when absent.
|
|
753
|
-
*/
|
|
754
|
-
count?(table: string, criteria: Criteria): Promise<number>;
|
|
755
|
-
/**
|
|
756
|
-
* Optional native aggregate (AGENTS §21). A backend that can compute an
|
|
757
|
-
* aggregate natively (SQL `COUNT`/`SUM`/`AVG`/`MIN`/`MAX`, an indexed count)
|
|
758
|
-
* implements this; `Table.aggregate` prefers it and otherwise falls back to
|
|
759
|
-
* `computeAggregate` over the native-filtered (or scanned) rows. Aggregates
|
|
760
|
-
* ignore paging, so `criteria` carries only conditions.
|
|
761
|
-
*/
|
|
762
|
-
aggregate?(table: string, operation: AggregateFunction, column: FieldPath, criteria: Criteria): Promise<number | undefined>;
|
|
763
|
-
/**
|
|
764
|
-
* Optional native transaction (BEGIN). When present, the engine uses the
|
|
765
|
-
* returned {@link TransactionInterface}'s `commit` / `rollback` instead of
|
|
766
|
-
* the snapshot-based rollback floor.
|
|
651
|
+
* Optional native transaction scope. The driver owns acquisition, commit or
|
|
652
|
+
* rollback, release, and invalidation of the scoped capability.
|
|
767
653
|
*/
|
|
768
|
-
transaction
|
|
769
|
-
/**
|
|
770
|
-
* Optional natively filtered lazy iteration — a {@link Criteria}-aware
|
|
771
|
-
* streaming read. Drivers without it are served by the core scan fallback
|
|
772
|
-
* (filtering `scan` lazily).
|
|
773
|
-
*/
|
|
774
|
-
stream?(table: string, criteria: Criteria): AsyncIterable<Row>;
|
|
775
|
-
/**
|
|
776
|
-
* Optional native migration — applies a {@link Migration} plan directly.
|
|
777
|
-
* Throws `DatabaseError` `MIGRATION` when a step references an unknown
|
|
778
|
-
* table.
|
|
779
|
-
*/
|
|
780
|
-
migrate?(plan: Migration): Promise<void>;
|
|
781
|
-
/**
|
|
782
|
-
* Optional persisted-metadata read (PAIRED with {@link stamp} — a driver
|
|
783
|
-
* implements both or neither). Returns the {@link DriverMeta} last stamped,
|
|
784
|
-
* or `undefined` when the store has never been stamped.
|
|
785
|
-
*/
|
|
786
|
-
meta?(): Promise<DriverMeta | undefined>;
|
|
787
|
-
/**
|
|
788
|
-
* Optional persisted-metadata write (PAIRED with {@link meta} — a driver
|
|
789
|
-
* implements both or neither). Persists `meta` verbatim for a later `meta()`
|
|
790
|
-
* to return.
|
|
791
|
-
*/
|
|
792
|
-
stamp?(meta: DriverMeta): Promise<void>;
|
|
654
|
+
transaction?<R>(scope: (storage: StorageInterface) => Promise<R>): Promise<R>;
|
|
793
655
|
}
|
|
794
656
|
|
|
795
657
|
/**
|
|
796
|
-
* Persisted schema metadata a versioning driver
|
|
797
|
-
* demand.
|
|
658
|
+
* Persisted schema metadata a versioning driver owns as an immutable snapshot.
|
|
798
659
|
*
|
|
799
660
|
* @remarks
|
|
800
|
-
*
|
|
801
|
-
*
|
|
802
|
-
*
|
|
803
|
-
*
|
|
661
|
+
* A driver snapshots metadata when it enters through `stamp` or a
|
|
662
|
+
* {@link MigrationInput}, so later caller mutation cannot alter stored version
|
|
663
|
+
* state. `metadata()` returns a distinct deeply frozen owned snapshot, never the
|
|
664
|
+
* driver's internal reference. `undefined` distinguishes a fresh store from an
|
|
665
|
+
* upgradable one: it means the store has never been stamped, not that it is at
|
|
666
|
+
* version zero.
|
|
804
667
|
*/
|
|
805
|
-
export declare interface
|
|
668
|
+
export declare interface DriverMetadata {
|
|
806
669
|
readonly version: number;
|
|
807
670
|
readonly schema: readonly TableSchema[];
|
|
808
671
|
}
|
|
809
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Structural equality by SameValueZero leaves — the comparator behind conformance
|
|
675
|
+
* checks and any test/fixture that needs "same data", not "same reference".
|
|
676
|
+
*
|
|
677
|
+
* @remarks
|
|
678
|
+
* Primitives compare by SameValueZero (`NaN` equals itself; `+0` equals `-0`).
|
|
679
|
+
* Arrays compare by index (same length, every element `equalsValue`). Plain
|
|
680
|
+
* records (via `isRecord`) compare by their OWN enumerable keys: same key
|
|
681
|
+
* COUNT and, for every key in `left`, `right` has that key (`Object.hasOwn`)
|
|
682
|
+
* with a `equalsValue` value — so a key present with value `undefined` is NOT
|
|
683
|
+
* equal to that key being absent (both differ in `Object.keys` membership).
|
|
684
|
+
* Anything else (functions, class instances, mismatched shapes) falls through
|
|
685
|
+
* to `false`. Container pairs are tracked iteratively, so self-referential and
|
|
686
|
+
* mutually cyclic arrays/records terminate without consuming the call stack.
|
|
687
|
+
* Hostile proxy traps and accessors are contained as a non-match.
|
|
688
|
+
*
|
|
689
|
+
* @param left - The left value
|
|
690
|
+
* @param right - The right value
|
|
691
|
+
* @returns Whether `left` and `right` are structurally equal
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* ```ts
|
|
695
|
+
* equalsValue(Number.NaN, Number.NaN) // true
|
|
696
|
+
* equalsValue({ a: [1, { b: 2 }] }, { a: [1, { b: 2 }] }) // true
|
|
697
|
+
* equalsValue({ a: undefined }, {}) // false — present-undefined ≠ absent
|
|
698
|
+
* ```
|
|
699
|
+
*/
|
|
700
|
+
export declare function equalsValue(left: unknown, right: unknown): boolean;
|
|
701
|
+
|
|
810
702
|
/**
|
|
811
703
|
* Read a row's primary key from a column, when it is a usable {@link Key}.
|
|
812
704
|
*
|
|
@@ -818,11 +710,11 @@ export declare function extractKey(row: Row, column: string): Key | undefined;
|
|
|
818
710
|
|
|
819
711
|
/**
|
|
820
712
|
* Filter rows by a list of conditions — the shared basis for a table's count
|
|
821
|
-
* and aggregate paths (no sort/page, unlike {@link
|
|
713
|
+
* and aggregate paths (no sort/page, unlike {@link applyQuery}).
|
|
822
714
|
*
|
|
823
715
|
* @remarks
|
|
824
716
|
* An empty condition list matches every row (returned as-is, no copy). Folds
|
|
825
|
-
* each row through {@link
|
|
717
|
+
* each row through {@link matchesQuery}.
|
|
826
718
|
*
|
|
827
719
|
* @param rows - The rows to filter
|
|
828
720
|
* @param conditions - The conditions to apply (empty matches everything)
|
|
@@ -839,35 +731,23 @@ export declare function extractKey(row: Row, column: string): Key | undefined;
|
|
|
839
731
|
export declare function filterRows(rows: readonly Row[], conditions: readonly Condition[]): readonly Row[];
|
|
840
732
|
|
|
841
733
|
/**
|
|
842
|
-
*
|
|
734
|
+
* Per-table secondary indexes — `{ [table]: groups }`, each group one
|
|
735
|
+
* (possibly compound) index of column names.
|
|
843
736
|
*
|
|
844
737
|
* @remarks
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
* `
|
|
848
|
-
* pass a seeded source (`seededRandom` from `@orkestrel/contract`) and reuse it
|
|
849
|
-
* across calls for reproducible sequences in tests and fixtures — production
|
|
850
|
-
* identifiers should keep the default source, whose engine entropy is far larger
|
|
851
|
-
* than a 32-bit seed. Each byte is floored and masked, so a source straying
|
|
852
|
-
* outside `[0, 1)` (negative, `>= 1`, `NaN`, `Infinity`) can never yield a
|
|
853
|
-
* malformed UUID. Suitable as a collision-resistant record identifier — not a
|
|
854
|
-
* cryptographic token; never use one as a secret.
|
|
855
|
-
*
|
|
856
|
-
* @param random - A number source returning values in the half-open range `[0, 1)` (defaults to `Math.random`)
|
|
857
|
-
* @returns A lowercase RFC 4122 version 4 UUID
|
|
858
|
-
*
|
|
859
|
-
* @example
|
|
860
|
-
* ```ts
|
|
861
|
-
* import { generateUUID } from '@orkestrel/database'
|
|
862
|
-
* import { seededRandom } from '@orkestrel/contract'
|
|
863
|
-
*
|
|
864
|
-
* generateUUID() // e.g. '9b2f7c1e-3d4a-4f6b-8e2d-5a1c0b9f8e7d'
|
|
865
|
-
* generateUUID(seededRandom(42)) // the same UUID on every run
|
|
866
|
-
* ```
|
|
738
|
+
* Contracts don't express indexes, so they're declared here on `createDatabase`
|
|
739
|
+
* and flow into each {@link TableSchema}'s `indexes` (SQLite `CREATE INDEX`,
|
|
740
|
+
* IndexedDB `createIndex`). Mirrors {@link PrimaryMap}.
|
|
867
741
|
*/
|
|
868
|
-
export declare
|
|
742
|
+
export declare type IndexMap = Readonly<Record<string, readonly (readonly string[])[]>>;
|
|
869
743
|
|
|
870
|
-
|
|
744
|
+
/**
|
|
745
|
+
* Test whether a value is a portable column schema.
|
|
746
|
+
*
|
|
747
|
+
* @param value - The value to test
|
|
748
|
+
* @returns Whether `value` is a complete {@link ColumnSchema}
|
|
749
|
+
*/
|
|
750
|
+
export declare function isColumnSchema(value: unknown): value is ColumnSchema;
|
|
871
751
|
|
|
872
752
|
/**
|
|
873
753
|
* Narrow an unknown caught value to a {@link DatabaseError}.
|
|
@@ -887,53 +767,82 @@ export declare function globMatch(value: string, pattern: string): boolean;
|
|
|
887
767
|
export declare function isDatabaseError(value: unknown): value is DatabaseError;
|
|
888
768
|
|
|
889
769
|
/**
|
|
890
|
-
*
|
|
891
|
-
* versioning driver's `meta()` narrows a stored (structured-clone or
|
|
892
|
-
* `JSON.parse`d) value through before trusting it, replacing the per-driver
|
|
893
|
-
* duplicated narrowing every backend used to hand-roll (AGENTS §14: never `as`).
|
|
770
|
+
* Test whether a value is persisted driver metadata.
|
|
894
771
|
*
|
|
895
|
-
* @
|
|
896
|
-
*
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
*
|
|
772
|
+
* @param value - The value to test
|
|
773
|
+
* @returns Whether `value` is complete {@link DriverMetadata}
|
|
774
|
+
*/
|
|
775
|
+
export declare function isDriverMetadata(value: unknown): value is DriverMetadata;
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Test whether a value is a complete portable driver schema.
|
|
902
779
|
*
|
|
903
780
|
* @param value - The value to test
|
|
904
|
-
* @returns
|
|
781
|
+
* @returns Whether `value` is a table-schema collection with unique table names
|
|
782
|
+
*/
|
|
783
|
+
export declare function isDriverSchema(value: unknown): value is readonly TableSchema[];
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Test whether a value is a usable database key.
|
|
905
787
|
*
|
|
906
|
-
* @
|
|
907
|
-
*
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
788
|
+
* @param value - The value to test
|
|
789
|
+
* @returns Whether `value` is a string or finite number
|
|
790
|
+
*/
|
|
791
|
+
export declare function isKey(value: unknown): value is Key;
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Test whether a value is an ordered migration plan.
|
|
795
|
+
*
|
|
796
|
+
* @param value - The value to test
|
|
797
|
+
* @returns Whether `value` is a complete {@link Migration}
|
|
798
|
+
*/
|
|
799
|
+
export declare function isMigration(value: unknown): value is Migration;
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Test whether a value is one atomic migration request.
|
|
803
|
+
*
|
|
804
|
+
* @param value - The value to test
|
|
805
|
+
* @returns Whether `value` is a complete {@link MigrationInput}
|
|
806
|
+
*/
|
|
807
|
+
export declare function isMigrationInput(value: unknown): value is MigrationInput;
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Test whether a value is one ordered migration step.
|
|
811
|
+
*
|
|
812
|
+
* @param value - The value to test
|
|
813
|
+
* @returns Whether `value` is a complete {@link MigrationStep}
|
|
814
|
+
*/
|
|
815
|
+
export declare function isMigrationStep(value: unknown): value is MigrationStep;
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Test whether a value is a portable table schema.
|
|
819
|
+
*
|
|
820
|
+
* @param value - The value to test
|
|
821
|
+
* @returns Whether `value` is a complete {@link TableSchema}
|
|
911
822
|
*/
|
|
912
|
-
export declare function
|
|
823
|
+
export declare function isTableSchema(value: unknown): value is TableSchema;
|
|
913
824
|
|
|
914
825
|
/**
|
|
915
826
|
* A primary key — the value identifying a row within its table.
|
|
916
827
|
*
|
|
917
828
|
* @remarks
|
|
918
829
|
* `string | number` is the intersection of what IndexedDB key ranges and SQL
|
|
919
|
-
* primary keys both express without coercion.
|
|
920
|
-
*
|
|
830
|
+
* primary keys both express without coercion. The default generated key is a
|
|
831
|
+
* UUID string; configure a custom generator for numeric primary keys.
|
|
921
832
|
*/
|
|
922
833
|
export declare type Key = string | number;
|
|
923
834
|
|
|
924
835
|
/**
|
|
925
|
-
* A
|
|
836
|
+
* A key-generating function.
|
|
926
837
|
*
|
|
927
838
|
* @remarks
|
|
928
|
-
*
|
|
929
|
-
*
|
|
930
|
-
*
|
|
931
|
-
* primary
|
|
839
|
+
* Supplied through {@link DatabaseOptions.generator} as an authoritative
|
|
840
|
+
* override when an application needs a non-UUID key or controlled generation.
|
|
841
|
+
* When omitted, a keyless write uses the global `crypto.randomUUID()`. Numeric
|
|
842
|
+
* primary keys therefore require a custom generator.
|
|
932
843
|
*/
|
|
933
844
|
export declare type KeyFunction = () => Key;
|
|
934
845
|
|
|
935
|
-
export declare function likeMatch(value: string, pattern: string): boolean;
|
|
936
|
-
|
|
937
846
|
/**
|
|
938
847
|
* Evaluate one {@link Condition} against a row — the per-operator predicate.
|
|
939
848
|
*
|
|
@@ -942,10 +851,10 @@ export declare function likeMatch(value: string, pattern: string): boolean;
|
|
|
942
851
|
* string is one column; an array descends a nested value) — and applies the
|
|
943
852
|
* operator. Range operators (`above` / `below` / `from` / `to` / `between`) use
|
|
944
853
|
* {@link compareValues}, the total order; the equality family (`equals` / `not`
|
|
945
|
-
* / `any` / `none`) uses {@link
|
|
854
|
+
* / `any` / `none`) uses {@link equalsValue} — STRUCTURAL equality, not the total
|
|
946
855
|
* order's rank-5-collapses-all-objects behavior, so `equals` on an object/array
|
|
947
856
|
* operand only matches a structurally-equal value, never every row holding any
|
|
948
|
-
* object. This is a semantics change from ranking: `
|
|
857
|
+
* object. This is a semantics change from ranking: `equalsValue` is SameValueZero
|
|
949
858
|
* on leaves, so `NaN` now equals `NaN` under `equals` / `any` (it never matched
|
|
950
859
|
* anything under the old rank-based comparison). `like` / `glob` / `starts` /
|
|
951
860
|
* `ends` match only strings; `absent` / `present` test nullishness. Total — a
|
|
@@ -957,6 +866,10 @@ export declare function likeMatch(value: string, pattern: string): boolean;
|
|
|
957
866
|
*/
|
|
958
867
|
export declare function matchesCondition(row: Row, condition: Condition): boolean;
|
|
959
868
|
|
|
869
|
+
export declare function matchesGlobPattern(value: string, pattern: string): boolean;
|
|
870
|
+
|
|
871
|
+
export declare function matchesLikePattern(value: string, pattern: string): boolean;
|
|
872
|
+
|
|
960
873
|
/**
|
|
961
874
|
* Fold a row through a list of conditions, joining each by its connector.
|
|
962
875
|
*
|
|
@@ -970,14 +883,47 @@ export declare function matchesCondition(row: Row, condition: Condition): boolea
|
|
|
970
883
|
* @param conditions - The conditions to fold
|
|
971
884
|
* @returns Whether the row satisfies the combined conditions
|
|
972
885
|
*/
|
|
973
|
-
export declare function
|
|
886
|
+
export declare function matchesQuery(row: Row, conditions: readonly Condition[]): boolean;
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Match a value against a wildcard pattern in LINEAR time — the shared, ReDoS-SAFE
|
|
890
|
+
* engine behind {@link matchesLikePattern} and {@link matchesGlobPattern}.
|
|
891
|
+
*
|
|
892
|
+
* @remarks
|
|
893
|
+
* A backtracking RegExp (`a%b%c` → `^a.*b.*c$`) is CATASTROPHIC on a hostile pattern:
|
|
894
|
+
* `.*` segments separated by literals, matched against a long non-matching input, blow
|
|
895
|
+
* up super-linearly — and JS has no atomic groups / possessive quantifiers to bound it
|
|
896
|
+
* (AGENTS §6.5, now that the authed server runs model-supplied `list` input over the
|
|
897
|
+
* wire). So this builds NO regex. It runs the classic GREEDY TWO-POINTER wildcard match:
|
|
898
|
+
* the `any` wildcard records its position and, on a later mismatch, backtracks ONLY to
|
|
899
|
+
* that last `any` (letting it absorb one more char) — so the work is O(value × pattern),
|
|
900
|
+
* never the exponential / polynomial backtracking a regex would do. The pattern length
|
|
901
|
+
* is capped at {@link MAX_PATTERN_LENGTH} (a `VALIDATION` {@link DatabaseError} over it),
|
|
902
|
+
* bounding the pattern factor so a match stays linear in the value length whatever the
|
|
903
|
+
* pattern.
|
|
904
|
+
*
|
|
905
|
+
* The `any` wildcard matches any run (including empty); `single` matches exactly one
|
|
906
|
+
* char; every other pattern char matches itself LITERALLY (a pattern `.` / `(` / `\` is
|
|
907
|
+
* a literal — the regex-metacharacter hazard is gone with the regex). `any` is tested
|
|
908
|
+
* BEFORE a literal match, so a value that literally contains the wildcard char never
|
|
909
|
+
* shadows the wildcard. Case folding is applied to BOTH sides when `fold` is set.
|
|
910
|
+
*
|
|
911
|
+
* @param value - The value to test
|
|
912
|
+
* @param pattern - The wildcard pattern
|
|
913
|
+
* @param any - The any-run wildcard char (`%` for `LIKE`, `*` for `GLOB`)
|
|
914
|
+
* @param single - The single-char wildcard char (`_` for `LIKE`, `?` for `GLOB`)
|
|
915
|
+
* @param fold - Whether to match case-INSENSITIVELY (`LIKE` folds; `GLOB` does not)
|
|
916
|
+
* @returns Whether `value` matches `pattern`
|
|
917
|
+
* @throws A `VALIDATION` {@link DatabaseError} when `pattern` exceeds {@link MAX_PATTERN_LENGTH}
|
|
918
|
+
*/
|
|
919
|
+
export declare function matchesWildcardPattern(value: string, pattern: string, any: string, single: string, fold: boolean): boolean;
|
|
974
920
|
|
|
975
921
|
/**
|
|
976
922
|
* The longest `LIKE` / `GLOB` pattern the wildcard matcher accepts before rejecting it.
|
|
977
923
|
*
|
|
978
924
|
* @remarks
|
|
979
925
|
* A ReDoS bound (AGENTS §6.5): the SA1–SA4 migration lets a model supply `list`
|
|
980
|
-
*
|
|
926
|
+
* input over the wire, so `matchesLikePattern` / `matchesGlobPattern` run attacker-controlled
|
|
981
927
|
* patterns. The matcher is the LINEAR greedy two-pointer wildcard match — never a
|
|
982
928
|
* backtracking regex (`.*`-segments-separated-by-literals against a long input is the
|
|
983
929
|
* catastrophic shape JS cannot bound without atomic groups), so it is O(value ×
|
|
@@ -998,21 +944,24 @@ export declare const MAX_PATTERN_LENGTH = 1024;
|
|
|
998
944
|
* snapshot capture and restore — so a caller mutating a nested field of an input
|
|
999
945
|
* row, a returned row, or a row mutated in place between snapshot and rollback
|
|
1000
946
|
* can never perturb stored state (AGENTS §11); a shallow `{ ...row }` spread
|
|
1001
|
-
* would still share nested object/array references.
|
|
947
|
+
* would still share nested object/array references. Metadata instead routes
|
|
948
|
+
* through `cloneDriverMetadata`: `stamp` and migration snapshot exact JSON at
|
|
949
|
+
* ingress, and `metadata` returns a distinct deeply frozen owned copy. `snapshot`
|
|
1002
950
|
* clones every table to give transactions an exact rollback point. `scan` and
|
|
1003
951
|
* `keys` yield in key order — sorted by the core {@link compareValues} total
|
|
1004
952
|
* order, the same contract the SQLite (`ORDER BY`) and IndexedDB (key-ordered
|
|
1005
953
|
* reads) backends honor, so an unordered read agrees across every backend rather
|
|
1006
954
|
* than leaking Map insertion order. A persistent backend (IndexedDB, SQLite)
|
|
1007
|
-
* implements the same
|
|
955
|
+
* implements the same required methods over real storage.
|
|
1008
956
|
*/
|
|
1009
957
|
export declare class MemoryDriver implements DriverInterface {
|
|
1010
958
|
#private;
|
|
1011
959
|
open(schema: readonly TableSchema[]): Promise<void>;
|
|
1012
960
|
close(): Promise<void>;
|
|
1013
961
|
read(table: string, key: Key): Promise<Row | undefined>;
|
|
1014
|
-
write(table: string, key: Key, row: Row): Promise<void>;
|
|
1015
|
-
|
|
962
|
+
write(table: string, key: Key, row: Row, options?: OperationOptions): Promise<void>;
|
|
963
|
+
insert(table: string, key: Key, row: Row, options?: OperationOptions): Promise<void>;
|
|
964
|
+
delete(table: string, key: Key, options?: OperationOptions): Promise<boolean>;
|
|
1016
965
|
keys(table: string): Promise<readonly Key[]>;
|
|
1017
966
|
scan(table: string): AsyncIterable<Row>;
|
|
1018
967
|
/**
|
|
@@ -1021,17 +970,17 @@ export declare class MemoryDriver implements DriverInterface {
|
|
|
1021
970
|
* @remarks
|
|
1022
971
|
* Iterates the table's keys in the same key order `scan` and `keys` yield
|
|
1023
972
|
* (sorted by {@link compareValues}), testing each row against
|
|
1024
|
-
* `
|
|
973
|
+
* `input.conditions` (via {@link matchesQuery}) before counting it
|
|
1025
974
|
* toward `offset` / `limit`. Both are applied lazily as matches are found —
|
|
1026
975
|
* `offset` matches are skipped without being yielded, and iteration stops the
|
|
1027
976
|
* instant `limit` yields have been produced, so a large table is never fully
|
|
1028
|
-
* walked for a small page. `
|
|
977
|
+
* walked for a small page. `input.order` is IGNORED (the same contract as
|
|
1029
978
|
* `TableInterface.scan` and `QueryInterface.stream`): streaming yields key
|
|
1030
979
|
* order, sorted output is `records()`'s job. Rows yield copy-out (AGENTS
|
|
1031
980
|
* §11), and an unknown table mirrors `scan`'s empty-yield behavior.
|
|
1032
981
|
*
|
|
1033
982
|
* @param table - The table to stream
|
|
1034
|
-
* @param
|
|
983
|
+
* @param input - The filter / offset / limit to apply lazily
|
|
1035
984
|
*
|
|
1036
985
|
* @example
|
|
1037
986
|
* ```ts
|
|
@@ -1040,49 +989,51 @@ export declare class MemoryDriver implements DriverInterface {
|
|
|
1040
989
|
* }
|
|
1041
990
|
* ```
|
|
1042
991
|
*/
|
|
1043
|
-
stream(table: string,
|
|
992
|
+
stream(table: string, input: QueryInput): AsyncIterable<Row>;
|
|
1044
993
|
clear(table: string): Promise<void>;
|
|
1045
994
|
/**
|
|
1046
995
|
* Capture the current state and return a thunk that rolls back to it.
|
|
1047
996
|
*
|
|
1048
997
|
* @remarks
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1052
|
-
*
|
|
998
|
+
* Capture owns rows, schema, and one session-local table identity. Replay
|
|
999
|
+
* adapts rows to each surviving same-identity table's current schema before
|
|
1000
|
+
* changing storage. Removed or replaced tables are skipped; uncaptured and
|
|
1001
|
+
* later-added tables retain their current rows. Schema and metadata are never
|
|
1002
|
+
* restored.
|
|
1053
1003
|
*
|
|
1054
1004
|
* @param tables - The table names to scope the snapshot to; omitted captures every table
|
|
1055
1005
|
* @returns A thunk that restores the captured tables
|
|
1056
1006
|
*/
|
|
1057
1007
|
snapshot(tables?: readonly string[]): Promise<() => Promise<void>>;
|
|
1058
1008
|
/**
|
|
1059
|
-
* Return the persisted {@link
|
|
1009
|
+
* Return the persisted {@link DriverMetadata}, or `undefined` when the store has
|
|
1060
1010
|
* never been stamped.
|
|
1061
1011
|
*
|
|
1062
1012
|
* @remarks
|
|
1063
1013
|
* In-process only — the metadata lives in this instance's memory, exactly
|
|
1064
|
-
* like the rest of this driver's storage.
|
|
1065
|
-
*
|
|
1014
|
+
* like the rest of this driver's storage. The returned value is a distinct
|
|
1015
|
+
* deeply frozen owned snapshot. A driver-conformance-valid implementation of
|
|
1016
|
+
* the optional `metadata` / `stamp` pair.
|
|
1066
1017
|
*
|
|
1067
|
-
* @returns The last-stamped {@link
|
|
1018
|
+
* @returns The last-stamped {@link DriverMetadata}, or `undefined`
|
|
1068
1019
|
*/
|
|
1069
|
-
|
|
1020
|
+
metadata(): Promise<DriverMetadata | undefined>;
|
|
1070
1021
|
/**
|
|
1071
|
-
* Persist
|
|
1022
|
+
* Persist an owned snapshot for a later `metadata()` to return.
|
|
1072
1023
|
*
|
|
1073
|
-
* @param
|
|
1024
|
+
* @param metadata - The {@link DriverMetadata} to persist
|
|
1074
1025
|
*/
|
|
1075
|
-
stamp(
|
|
1026
|
+
stamp(metadata: DriverMetadata): Promise<void>;
|
|
1076
1027
|
/**
|
|
1077
1028
|
* Apply a {@link Migration} plan's steps against the in-memory store.
|
|
1078
1029
|
*
|
|
1079
1030
|
* @remarks
|
|
1080
|
-
*
|
|
1081
|
-
*
|
|
1031
|
+
* Steps apply against an isolated candidate. Rows, schema changes, and
|
|
1032
|
+
* optional metadata publish together only after the whole request succeeds.
|
|
1082
1033
|
*
|
|
1083
|
-
* @param
|
|
1034
|
+
* @param input - The migration plan and optional metadata to settle atomically
|
|
1084
1035
|
*/
|
|
1085
|
-
migrate(
|
|
1036
|
+
migrate(input: MigrationInput): Promise<void>;
|
|
1086
1037
|
}
|
|
1087
1038
|
|
|
1088
1039
|
/**
|
|
@@ -1124,6 +1075,19 @@ export declare interface Migration {
|
|
|
1124
1075
|
readonly steps: readonly MigrationStep[];
|
|
1125
1076
|
}
|
|
1126
1077
|
|
|
1078
|
+
/**
|
|
1079
|
+
* One atomic migration request.
|
|
1080
|
+
*
|
|
1081
|
+
* @remarks
|
|
1082
|
+
* `plan` carries the schema changes. `metadata`, when present, is the snapshot that
|
|
1083
|
+
* must settle in the same atomic unit as those changes so a failed migration
|
|
1084
|
+
* cannot expose a new schema under stale version metadata.
|
|
1085
|
+
*/
|
|
1086
|
+
export declare interface MigrationInput {
|
|
1087
|
+
readonly plan: Migration;
|
|
1088
|
+
readonly metadata?: DriverMetadata;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1127
1091
|
/**
|
|
1128
1092
|
* One step of a {@link Migration} plan — a single schema change applied to one
|
|
1129
1093
|
* table.
|
|
@@ -1158,12 +1122,42 @@ export declare type MigrationStep = {
|
|
|
1158
1122
|
readonly index: readonly string[];
|
|
1159
1123
|
};
|
|
1160
1124
|
|
|
1125
|
+
/**
|
|
1126
|
+
* Canonicalize an unknown driver schema into a distinct deeply frozen snapshot.
|
|
1127
|
+
*
|
|
1128
|
+
* @remarks
|
|
1129
|
+
* Table and column lists are sorted by name. The index list is sorted by the
|
|
1130
|
+
* complete serialized tuple while column order inside each compound index is
|
|
1131
|
+
* preserved because it carries index semantics. Validation and ownership flow
|
|
1132
|
+
* through {@link cloneDriverSchema} before and after projection.
|
|
1133
|
+
*
|
|
1134
|
+
* @param value - Unknown driver schema
|
|
1135
|
+
* @returns A validated, owned canonical schema
|
|
1136
|
+
*/
|
|
1137
|
+
export declare function normalizeDriverSchema(value: unknown): readonly TableSchema[];
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Options for an abortable operation.
|
|
1141
|
+
*
|
|
1142
|
+
* @remarks
|
|
1143
|
+
* When `signal` aborts, the operation throws a {@link DatabaseError} with code
|
|
1144
|
+
* `ABORTED` carrying `signal.reason` in `context`. Reads check at their
|
|
1145
|
+
* documented boundaries; point mutations propagate the signal through the
|
|
1146
|
+
* driver to the backend commit point.
|
|
1147
|
+
*/
|
|
1148
|
+
export declare interface OperationOptions {
|
|
1149
|
+
readonly signal?: AbortSignal;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1161
1152
|
/** One ordering term — a column ({@link FieldPath}, flat or nested) and its direction. */
|
|
1162
1153
|
export declare interface Order {
|
|
1163
1154
|
readonly column: FieldPath;
|
|
1164
|
-
readonly direction:
|
|
1155
|
+
readonly direction: OrderDirection;
|
|
1165
1156
|
}
|
|
1166
1157
|
|
|
1158
|
+
/** A sort direction. */
|
|
1159
|
+
export declare type OrderDirection = 'ascending' | 'descending';
|
|
1160
|
+
|
|
1167
1161
|
/**
|
|
1168
1162
|
* Structurally diff a deployed and a declared table set into a {@link Migration}
|
|
1169
1163
|
* plan.
|
|
@@ -1177,11 +1171,12 @@ export declare interface Order {
|
|
|
1177
1171
|
* `column.remove` / `index.add` / `index.remove` steps. Step order is
|
|
1178
1172
|
* deterministic: every `table.remove`, then every `table.add`, then each
|
|
1179
1173
|
* shared table's column/index changes in `declared` order. `from` / `to` are
|
|
1180
|
-
* plan labels only
|
|
1181
|
-
*
|
|
1174
|
+
* plan labels only; versioning drivers persist and reconcile them through
|
|
1175
|
+
* {@link DriverMetadata}.
|
|
1182
1176
|
*
|
|
1183
1177
|
* A column present in BOTH schemas under the same name but with a different
|
|
1184
|
-
* `
|
|
1178
|
+
* `storage`, `optional`, or `nullable` value throws a `MIGRATION`
|
|
1179
|
+
* {@link DatabaseError} naming the
|
|
1185
1180
|
* table, the column, and the from→to difference — a name-only diff would
|
|
1186
1181
|
* otherwise silently produce NO step for the drift, and versioned
|
|
1187
1182
|
* reconciliation would stamp over it. There is no automatic in-place
|
|
@@ -1194,14 +1189,16 @@ export declare interface Order {
|
|
|
1194
1189
|
* @param from - The plan's source version label (defaults to `0`)
|
|
1195
1190
|
* @param to - The plan's target version label (defaults to `1`)
|
|
1196
1191
|
* @returns The migration plan moving `deployed` toward `declared`
|
|
1197
|
-
* @throws A `MIGRATION` {@link DatabaseError} when a shared
|
|
1198
|
-
*
|
|
1192
|
+
* @throws A `MIGRATION` {@link DatabaseError} when a shared table's primary or
|
|
1193
|
+
* a shared column's `storage`, `optional`, or `nullable` differs, or when a
|
|
1194
|
+
* required non-null column would be added to an existing table without a
|
|
1195
|
+
* portable backfill
|
|
1199
1196
|
*
|
|
1200
1197
|
* @example
|
|
1201
1198
|
* ```ts
|
|
1202
1199
|
* const plan = planMigration(
|
|
1203
|
-
* [{ name: 'users', primary: 'id', columns: [], indexes: [] }],
|
|
1204
|
-
* [{ name: 'users', primary: 'id', columns: [{ name: 'age',
|
|
1200
|
+
* [{ name: 'users', primary: 'id', columns: [{ name: 'id', storage: 'text', optional: false, nullable: false }], indexes: [] }],
|
|
1201
|
+
* [{ name: 'users', primary: 'id', columns: [{ name: 'id', storage: 'text', optional: false, nullable: false }, { name: 'age', storage: 'integer', optional: true, nullable: false }], indexes: [] }],
|
|
1205
1202
|
* )
|
|
1206
1203
|
* // plan.steps === [{ operation: 'column.add', table: 'users', column: { name: 'age', ... } }]
|
|
1207
1204
|
* ```
|
|
@@ -1209,74 +1206,62 @@ export declare interface Order {
|
|
|
1209
1206
|
export declare function planMigration(deployed: readonly TableSchema[], declared: readonly TableSchema[], from?: number, to?: number): Migration;
|
|
1210
1207
|
|
|
1211
1208
|
/**
|
|
1212
|
-
*
|
|
1209
|
+
* Per-table primary-key column overrides — `{ [table]: column }`.
|
|
1213
1210
|
*
|
|
1214
1211
|
* @remarks
|
|
1215
|
-
*
|
|
1216
|
-
*
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
* @param options - `signal` to cancel the iteration; checked before each yield
|
|
1247
|
-
* @returns An async iterable of matching rows
|
|
1248
|
-
*/
|
|
1249
|
-
stream(options?: ReadOptions): AsyncIterable<T>;
|
|
1250
|
-
aggregate(operation: AggregateFunction, column: FieldPath): Promise<number | undefined>;
|
|
1251
|
-
sum(column: FieldPath): Promise<number | undefined>;
|
|
1252
|
-
average(column: FieldPath): Promise<number | undefined>;
|
|
1253
|
-
minimum(column: FieldPath): Promise<number | undefined>;
|
|
1254
|
-
maximum(column: FieldPath): Promise<number | undefined>;
|
|
1212
|
+
* A table absent from this map keys its rows by {@link DEFAULT_PRIMARY} (`id`).
|
|
1213
|
+
* Kept separate from {@link TableMap} so the table map stays purely columns.
|
|
1214
|
+
*/
|
|
1215
|
+
export declare type PrimaryMap = Readonly<Record<string, string>>;
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* Sequentially project migration steps over a canonical validated owned schema.
|
|
1219
|
+
* Adding a required non-null column to an existing table rejects with
|
|
1220
|
+
* `MIGRATION`; optional-only and nullable-only additions remain portable.
|
|
1221
|
+
*
|
|
1222
|
+
* @param schema - The initial deployed schema
|
|
1223
|
+
* @param steps - The ordered migration steps
|
|
1224
|
+
* @returns A fresh owned final schema
|
|
1225
|
+
*/
|
|
1226
|
+
export declare function projectMigrationSchema(schema: readonly TableSchema[], steps: readonly MigrationStep[]): readonly TableSchema[];
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* A serializable read specification — everything a backend needs to compile one
|
|
1230
|
+
* read, free of JS callbacks so any backend can honor it.
|
|
1231
|
+
*
|
|
1232
|
+
* @remarks
|
|
1233
|
+
* The post-fetch `filter` predicate lives on {@link QueryInterface}, never here,
|
|
1234
|
+
* so `QueryInput` stays portable across backends. When present, `limit` and
|
|
1235
|
+
* `offset` must be finite nonnegative integers. Zero is valid: `limit: 0`
|
|
1236
|
+
* selects an empty page and `offset: 0` skips nothing.
|
|
1237
|
+
*/
|
|
1238
|
+
export declare interface QueryInput {
|
|
1239
|
+
readonly conditions?: readonly Condition[];
|
|
1240
|
+
readonly order?: readonly Order[];
|
|
1241
|
+
readonly limit?: number;
|
|
1242
|
+
readonly offset?: number;
|
|
1255
1243
|
}
|
|
1256
1244
|
|
|
1257
1245
|
/**
|
|
1258
1246
|
* A fluent query builder.
|
|
1259
1247
|
*
|
|
1260
1248
|
* @remarks
|
|
1261
|
-
* `
|
|
1262
|
-
*
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1249
|
+
* `condition` appends one portable condition and `order` appends one portable
|
|
1250
|
+
* ordering term. `filter` adds a post-fetch JavaScript predicate (applied after
|
|
1251
|
+
* the backend read, before paging). The terminals (`collect` / `find` / `count`
|
|
1252
|
+
* / `aggregate`) execute against the table; each
|
|
1265
1253
|
* call mutates and returns the same builder, so a chain reads as one statement.
|
|
1266
1254
|
* Every `column` is a {@link FieldPath} — a string is one column, an array
|
|
1267
1255
|
* descends a nested value.
|
|
1268
1256
|
*/
|
|
1269
1257
|
export declare interface QueryInterface<T = Row> {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
or(column: FieldPath): ClauseInterface<T>;
|
|
1258
|
+
condition(input: Condition): QueryInterface<T>;
|
|
1259
|
+
order(input: Order): QueryInterface<T>;
|
|
1273
1260
|
filter(predicate: (row: T) => boolean): QueryInterface<T>;
|
|
1274
|
-
ascending(column: FieldPath): QueryInterface<T>;
|
|
1275
|
-
descending(column: FieldPath): QueryInterface<T>;
|
|
1276
1261
|
limit(count: number): QueryInterface<T>;
|
|
1277
1262
|
offset(count: number): QueryInterface<T>;
|
|
1278
|
-
|
|
1279
|
-
|
|
1263
|
+
collect(): Promise<readonly T[]>;
|
|
1264
|
+
find(): Promise<T | undefined>;
|
|
1280
1265
|
count(): Promise<number>;
|
|
1281
1266
|
/**
|
|
1282
1267
|
* Lazy per-row evaluation of this query's conditions / filters / offset /
|
|
@@ -1288,51 +1273,43 @@ export declare interface QueryInterface<T = Row> {
|
|
|
1288
1273
|
* {@link TableInterface.scan}: the signal (if any) is checked before each
|
|
1289
1274
|
* yield, and breaking out early closes the underlying source.
|
|
1290
1275
|
*/
|
|
1291
|
-
stream(options?:
|
|
1292
|
-
|
|
1293
|
-
average(column: FieldPath): Promise<number | undefined>;
|
|
1294
|
-
minimum(column: FieldPath): Promise<number | undefined>;
|
|
1295
|
-
maximum(column: FieldPath): Promise<number | undefined>;
|
|
1296
|
-
aggregate(operation: AggregateFunction, column: FieldPath): Promise<number | undefined>;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
/**
|
|
1300
|
-
* Options for a cancellable read / iteration operation.
|
|
1301
|
-
*
|
|
1302
|
-
* @remarks
|
|
1303
|
-
* When `signal` aborts, the operation throws a {@link DatabaseError} with code
|
|
1304
|
-
* `ABORTED` carrying `signal.reason` in `context`. `TableInterface.scan` and
|
|
1305
|
-
* `QueryInterface.stream` check the signal before each yield; other read
|
|
1306
|
-
* methods check it at entry.
|
|
1307
|
-
*/
|
|
1308
|
-
export declare interface ReadOptions {
|
|
1309
|
-
readonly signal?: AbortSignal;
|
|
1276
|
+
stream(options?: OperationOptions): AsyncIterable<T>;
|
|
1277
|
+
aggregate(operation: AggregateOperation, column: FieldPath): Promise<number | undefined>;
|
|
1310
1278
|
}
|
|
1311
1279
|
|
|
1312
1280
|
/** A table row — a plain record of column values keyed by column name. */
|
|
1313
1281
|
export declare type Row = Record<string, unknown>;
|
|
1314
1282
|
|
|
1315
1283
|
/**
|
|
1316
|
-
* The row type a table's {@link
|
|
1284
|
+
* The row type a table's {@link ColumnMap} describe — `Infer` of the `objectShape`
|
|
1317
1285
|
* the database wraps them in.
|
|
1318
1286
|
*
|
|
1319
1287
|
* @remarks
|
|
1320
1288
|
* Contract 0.0.4's non-distributive `Infer` resolves the OPEN case (the broad
|
|
1321
|
-
* `
|
|
1322
|
-
* `RowOf<
|
|
1289
|
+
* `ColumnMap` — e.g. when a database is held at its default type) directly:
|
|
1290
|
+
* `RowOf<ColumnMap>` and {@link Row} are mutually assignable, so no short-circuit
|
|
1323
1291
|
* to `Row` and no `additionalProperties: false` pin are needed — `Infer` no
|
|
1324
1292
|
* longer trips TS's instantiation-depth guard over the open shape, and the
|
|
1325
1293
|
* inferred row matches the CLOSED object `objectShape(columns)` builds at
|
|
1326
1294
|
* runtime (its additional-properties parameter defaults to `false`) for every
|
|
1327
1295
|
* concrete column map.
|
|
1328
1296
|
*/
|
|
1329
|
-
export declare type RowOf<C extends
|
|
1297
|
+
export declare type RowOf<C extends ColumnMap> = Infer<{
|
|
1330
1298
|
readonly type: 'object';
|
|
1331
1299
|
readonly properties: C;
|
|
1332
1300
|
}>;
|
|
1333
1301
|
|
|
1334
1302
|
/**
|
|
1335
|
-
*
|
|
1303
|
+
* Project one contract shape into a portable column schema.
|
|
1304
|
+
*
|
|
1305
|
+
* @param name - The column name
|
|
1306
|
+
* @param shape - The column contract shape
|
|
1307
|
+
* @returns The portable storage and independent absence/null acceptance
|
|
1308
|
+
*/
|
|
1309
|
+
export declare function shapeToColumnSchema(name: string, shape: ContractShape): ColumnSchema;
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* Map a column's {@link ContractShape} to its portable {@link ColumnStorage} — the
|
|
1336
1313
|
* value a `TableSchema` carries so a native backend can declare a real column.
|
|
1337
1314
|
*
|
|
1338
1315
|
* @remarks
|
|
@@ -1349,13 +1326,13 @@ export declare type RowOf<C extends Columns> = Infer<{
|
|
|
1349
1326
|
*
|
|
1350
1327
|
* @example
|
|
1351
1328
|
* ```ts
|
|
1352
|
-
*
|
|
1353
|
-
*
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1329
|
+
* shapeToColumnStorage(stringShape()) // 'text'
|
|
1330
|
+
* shapeToColumnStorage(integerShape()) // 'integer'
|
|
1331
|
+
* shapeToColumnStorage(optionalShape(integerShape())) // 'integer'
|
|
1332
|
+
* shapeToColumnStorage(objectShape({ a: stringShape() })) // 'json'
|
|
1356
1333
|
* ```
|
|
1357
1334
|
*/
|
|
1358
|
-
export declare function
|
|
1335
|
+
export declare function shapeToColumnStorage(shape: ContractShape): ColumnStorage;
|
|
1359
1336
|
|
|
1360
1337
|
/**
|
|
1361
1338
|
* Sort rows by an ordering specification, leaving the input untouched.
|
|
@@ -1371,102 +1348,44 @@ export declare function shapeToColumnType(shape: ContractShape): ColumnType;
|
|
|
1371
1348
|
export declare function sortRows(rows: readonly Row[], order: readonly Order[]): readonly Row[];
|
|
1372
1349
|
|
|
1373
1350
|
/**
|
|
1374
|
-
*
|
|
1351
|
+
* The storage operations available only inside a driver's transaction scope.
|
|
1375
1352
|
*
|
|
1376
1353
|
* @remarks
|
|
1377
|
-
*
|
|
1378
|
-
*
|
|
1379
|
-
*
|
|
1380
|
-
*
|
|
1381
|
-
*
|
|
1382
|
-
|
|
1354
|
+
* A driver owns acquisition, commit or rollback, release, and lifetime. This
|
|
1355
|
+
* capability exposes storage work only: it has no public `commit` / `rollback`,
|
|
1356
|
+
* cannot start a nested transaction, and throws `CONFLICT` after its scope
|
|
1357
|
+
* settles. Optional native read and migration hooks mirror the owning driver;
|
|
1358
|
+
* callers fall back to `scan` when a native read hook is absent.
|
|
1359
|
+
*/
|
|
1360
|
+
export declare interface StorageInterface {
|
|
1361
|
+
read(table: string, key: Key): Promise<Row | undefined>;
|
|
1362
|
+
write(table: string, key: Key, row: Row, options?: OperationOptions): Promise<void>;
|
|
1363
|
+
insert(table: string, key: Key, row: Row, options?: OperationOptions): Promise<void>;
|
|
1364
|
+
delete(table: string, key: Key, options?: OperationOptions): Promise<boolean>;
|
|
1365
|
+
keys(table: string): Promise<readonly Key[]>;
|
|
1366
|
+
scan(table: string): AsyncIterable<Row>;
|
|
1367
|
+
clear(table: string): Promise<void>;
|
|
1368
|
+
records?(table: string, input: QueryInput): Promise<readonly Row[]>;
|
|
1369
|
+
aggregate?(table: string, operation: AggregateOperation, column: FieldPath, input: QueryInput): Promise<number | undefined>;
|
|
1370
|
+
stream?(table: string, input: QueryInput): AsyncIterable<Row>;
|
|
1371
|
+
migrate?(input: MigrationInput): Promise<void>;
|
|
1372
|
+
metadata?(): Promise<DriverMetadata | undefined>;
|
|
1373
|
+
stamp?(metadata: DriverMetadata): Promise<void>;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/**
|
|
1377
|
+
* One table's portable definition, produced by `export` — the unit of schema /
|
|
1378
|
+
* migration exchange across environments.
|
|
1383
1379
|
*
|
|
1384
1380
|
* @remarks
|
|
1385
|
-
*
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
*/
|
|
1394
|
-
export declare class Table<T = Row> implements TableInterface<T> {
|
|
1395
|
-
#private;
|
|
1396
|
-
constructor(ready: () => Promise<void>, driver: DriverInterface, name: string, key: string, contract: ContractInterface<T>, generate?: KeyFunction, on?: EmitterHooks<TableEventMap>, error?: EmitterErrorHandler);
|
|
1397
|
-
get emitter(): EmitterInterface<TableEventMap>;
|
|
1398
|
-
get name(): string;
|
|
1399
|
-
get primary(): string;
|
|
1400
|
-
get contract(): ContractInterface<T>;
|
|
1401
|
-
get(key: Key): Promise<T | undefined>;
|
|
1402
|
-
get(keys: readonly Key[]): Promise<readonly (T | undefined)[]>;
|
|
1403
|
-
resolve(key: Key): Promise<T>;
|
|
1404
|
-
resolve(keys: readonly Key[]): Promise<readonly T[]>;
|
|
1405
|
-
has(key: Key): Promise<boolean>;
|
|
1406
|
-
has(keys: readonly Key[]): Promise<readonly boolean[]>;
|
|
1407
|
-
keys(): Promise<readonly Key[]>;
|
|
1408
|
-
records(criteria?: Criteria, options?: ReadOptions): Promise<readonly T[]>;
|
|
1409
|
-
/**
|
|
1410
|
-
* Count rows matching `criteria`'s conditions.
|
|
1411
|
-
*
|
|
1412
|
-
* @remarks
|
|
1413
|
-
* Unlike {@link records}, which narrows every row through the table's
|
|
1414
|
-
* contract guard before returning it, `count` operates on STORED rows
|
|
1415
|
-
* WITHOUT that guard (both the native `driver.count` hook and the
|
|
1416
|
-
* `filterRows`-over-`#collect()` fallback count raw storage) — so it can
|
|
1417
|
-
* exceed `(await records(criteria)).length` when storage holds rows that
|
|
1418
|
-
* no longer conform to the table's contract (legacy or migrated data).
|
|
1419
|
-
*
|
|
1420
|
-
* @param criteria - Optional conditions to filter by (paging is ignored)
|
|
1421
|
-
* @param options - `{ signal }` to abort
|
|
1422
|
-
* @returns The count of matching stored rows
|
|
1423
|
-
*/
|
|
1424
|
-
count(criteria?: Criteria, options?: ReadOptions): Promise<number>;
|
|
1425
|
-
/**
|
|
1426
|
-
* Compute an aggregate over `column` across rows matching `criteria`'s
|
|
1427
|
-
* conditions.
|
|
1428
|
-
*
|
|
1429
|
-
* @remarks
|
|
1430
|
-
* Like {@link count}, `aggregate` operates on STORED rows WITHOUT the
|
|
1431
|
-
* contract guard {@link records} / {@link scan} apply — a non-conforming
|
|
1432
|
-
* stored row still contributes to the computed aggregate when it matches
|
|
1433
|
-
* the conditions, even though it would never appear in `records()`'s
|
|
1434
|
-
* output.
|
|
1435
|
-
*
|
|
1436
|
-
* @param operation - The aggregate to compute
|
|
1437
|
-
* @param column - The column to aggregate
|
|
1438
|
-
* @param criteria - Optional conditions to filter by (paging is ignored)
|
|
1439
|
-
* @param options - `{ signal }` to abort
|
|
1440
|
-
* @returns The aggregate value, or `undefined` when undefined for the inputs
|
|
1441
|
-
*/
|
|
1442
|
-
aggregate(operation: AggregateFunction, column: FieldPath, criteria?: Criteria, options?: ReadOptions): Promise<number | undefined>;
|
|
1443
|
-
/**
|
|
1444
|
-
* Stream the table's rows matching `criteria`, applying offset/limit paging.
|
|
1445
|
-
*
|
|
1446
|
-
* @remarks
|
|
1447
|
-
* `criteria.limit` counts rows that pass BOTH the criteria conditions AND the
|
|
1448
|
-
* table's contract guard (a stored row that fails the guard is skipped and
|
|
1449
|
-
* does not count toward `limit`) — this can differ from {@link records}'s
|
|
1450
|
-
* `limit`, which a driver's optional native `records` hook applies BEFORE
|
|
1451
|
-
* the contract guard runs, when storage holds rows that no longer conform
|
|
1452
|
-
* to the table's contract.
|
|
1453
|
-
*
|
|
1454
|
-
* @param criteria - Optional conditions plus offset/limit paging
|
|
1455
|
-
* @param options - `{ signal }` to abort mid-stream
|
|
1456
|
-
* @returns An async iterable of matching, guard-conforming rows
|
|
1457
|
-
*/
|
|
1458
|
-
scan(criteria?: Criteria, options?: ReadOptions): AsyncIterable<T>;
|
|
1459
|
-
set(row: T, options?: ReadOptions): Promise<Key>;
|
|
1460
|
-
set(rows: readonly T[], options?: ReadOptions): Promise<readonly Key[]>;
|
|
1461
|
-
add(row: T, options?: ReadOptions): Promise<Key>;
|
|
1462
|
-
add(rows: readonly T[], options?: ReadOptions): Promise<readonly Key[]>;
|
|
1463
|
-
update(key: Key, changes: Partial<T>, options?: ReadOptions): Promise<boolean>;
|
|
1464
|
-
update(keys: readonly Key[], changes: Partial<T>, options?: ReadOptions): Promise<readonly boolean[]>;
|
|
1465
|
-
remove(key: Key, options?: ReadOptions): Promise<boolean>;
|
|
1466
|
-
remove(keys: readonly Key[], options?: ReadOptions): Promise<readonly boolean[]>;
|
|
1467
|
-
clear(): Promise<void>;
|
|
1468
|
-
query(): QueryInterface<T>;
|
|
1469
|
-
cursor(): Promise<CursorInterface<T>>;
|
|
1381
|
+
* `schema` is the JSON Schema (universally portable, serializable); `columns` is
|
|
1382
|
+
* the source column map, which re-imports losslessly via `import` within a
|
|
1383
|
+
* TypeScript environment. `primary` is the primary-key column.
|
|
1384
|
+
*/
|
|
1385
|
+
export declare interface TableDefinition {
|
|
1386
|
+
readonly primary: string;
|
|
1387
|
+
readonly columns: ColumnMap;
|
|
1388
|
+
readonly schema: JSONSchema;
|
|
1470
1389
|
}
|
|
1471
1390
|
|
|
1472
1391
|
/**
|
|
@@ -1474,9 +1393,6 @@ export declare class Table<T = Row> implements TableInterface<T> {
|
|
|
1474
1393
|
* mutation moments a fire-and-forget observer (cache invalidation, sync, an audit log)
|
|
1475
1394
|
* subscribes to, ALONGSIDE the database-level {@link DatabaseEventMap}.
|
|
1476
1395
|
*
|
|
1477
|
-
* @typeParam TKey - The table's primary-key type (a {@link Key}); the events carry the
|
|
1478
|
-
* affected key so the map is `TableEventMap<TKey>`.
|
|
1479
|
-
*
|
|
1480
1396
|
* @remarks
|
|
1481
1397
|
* Events carry the affected KEY only — never the row value — to keep fan-out lean and
|
|
1482
1398
|
* avoid leaking row data through the observation channel; a consumer that needs the
|
|
@@ -1490,48 +1406,22 @@ export declare class Table<T = Row> implements TableInterface<T> {
|
|
|
1490
1406
|
* transaction. Subscribe via `table.emitter.on(...)`. Declared as a `type` alias (§4.5 —
|
|
1491
1407
|
* `EventMap` is a `type` kind).
|
|
1492
1408
|
*/
|
|
1493
|
-
export declare type TableEventMap
|
|
1409
|
+
export declare type TableEventMap = {
|
|
1494
1410
|
/** A row was written (set / added / updated) — the affected key (no value payload). */
|
|
1495
|
-
readonly write: readonly [key:
|
|
1411
|
+
readonly write: readonly [key: Key];
|
|
1496
1412
|
/** A row was removed — the affected key. */
|
|
1497
|
-
readonly remove: readonly [key:
|
|
1413
|
+
readonly remove: readonly [key: Key];
|
|
1498
1414
|
/** The table was cleared (every row removed). */
|
|
1499
1415
|
readonly clear: readonly [];
|
|
1500
1416
|
};
|
|
1501
1417
|
|
|
1502
|
-
/**
|
|
1503
|
-
* One table's portable definition, produced by `export` — the unit of schema /
|
|
1504
|
-
* migration exchange across environments.
|
|
1505
|
-
*
|
|
1506
|
-
* @remarks
|
|
1507
|
-
* `schema` is the JSON Schema (universally portable, serializable); `columns` is
|
|
1508
|
-
* the source column map, which re-imports losslessly via `import` within a
|
|
1509
|
-
* TypeScript environment. `key` is the primary-key column.
|
|
1510
|
-
*/
|
|
1511
|
-
export declare interface TableExport {
|
|
1512
|
-
readonly key: string;
|
|
1513
|
-
readonly columns: Columns;
|
|
1514
|
-
readonly schema: JSONSchema;
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
/**
|
|
1518
|
-
* Per-table secondary indexes — `{ [table]: groups }`, each group one
|
|
1519
|
-
* (possibly compound) index of column names.
|
|
1520
|
-
*
|
|
1521
|
-
* @remarks
|
|
1522
|
-
* Contracts don't express indexes, so they're declared here on `createDatabase`
|
|
1523
|
-
* and flow into each {@link TableSchema}'s `indexes` (SQLite `CREATE INDEX`,
|
|
1524
|
-
* IndexedDB `createIndex`). Mirrors {@link TableKeys}.
|
|
1525
|
-
*/
|
|
1526
|
-
export declare type TableIndexes = Readonly<Record<string, readonly (readonly string[])[]>>;
|
|
1527
|
-
|
|
1528
1418
|
/**
|
|
1529
1419
|
* A table — typed keyed CRUD plus fluent query and cursor access.
|
|
1530
1420
|
*
|
|
1531
1421
|
* @remarks
|
|
1532
1422
|
* Writes are coerced through the table's contract: a string input to a numeric
|
|
1533
1423
|
* column is normalized, and a row that cannot be coerced throws `VALIDATION`. A
|
|
1534
|
-
* row
|
|
1424
|
+
* row whose primary is `undefined` receives a generated key. `get` returns `undefined`
|
|
1535
1425
|
* when a key is absent; `resolve` throws `NOT_FOUND`. `set` upserts; `add`
|
|
1536
1426
|
* inserts and throws `CONFLICT` on a duplicate key. `contract` exposes the
|
|
1537
1427
|
* compiled contract for introspection (`schema`) and fixtures (`generate`).
|
|
@@ -1553,43 +1443,39 @@ export declare interface TableInterface<T = Row> {
|
|
|
1553
1443
|
has(key: Key): Promise<boolean>;
|
|
1554
1444
|
has(keys: readonly Key[]): Promise<readonly boolean[]>;
|
|
1555
1445
|
keys(): Promise<readonly Key[]>;
|
|
1556
|
-
records(
|
|
1446
|
+
records(input?: QueryInput, options?: OperationOptions): Promise<readonly T[]>;
|
|
1557
1447
|
/**
|
|
1558
|
-
* Count rows matching `
|
|
1448
|
+
* Count contract-valid rows matching `input`'s conditions.
|
|
1559
1449
|
*
|
|
1560
1450
|
* @remarks
|
|
1561
|
-
* `records()` / `scan()`
|
|
1562
|
-
*
|
|
1563
|
-
*
|
|
1564
|
-
* operates on STORED rows WITHOUT that guard — it counts whatever
|
|
1565
|
-
* conditions-matches in storage, guard-conforming or not. This means
|
|
1566
|
-
* `count()` CAN exceed `(await records(criteria)).length` when storage
|
|
1567
|
-
* holds rows that no longer conform to the table's contract.
|
|
1451
|
+
* Paging is ignored. Like `records()` / `scan()`, `count()` narrows every
|
|
1452
|
+
* candidate through the table contract, so a non-conforming stored row does
|
|
1453
|
+
* not consume the count.
|
|
1568
1454
|
*/
|
|
1569
|
-
count(
|
|
1455
|
+
count(input?: QueryInput, options?: OperationOptions): Promise<number>;
|
|
1570
1456
|
/**
|
|
1571
|
-
* Compute an aggregate over `column` across rows matching `
|
|
1457
|
+
* Compute an aggregate over `column` across rows matching `input`'s
|
|
1572
1458
|
* conditions.
|
|
1573
1459
|
*
|
|
1574
1460
|
* @remarks
|
|
1575
|
-
*
|
|
1576
|
-
*
|
|
1461
|
+
* Unlike {@link TableInterface.count}, `aggregate` operates on STORED rows
|
|
1462
|
+
* without the contract guard that `records()` / `scan()` apply — a
|
|
1577
1463
|
* non-conforming stored row still contributes to the aggregate (or to the
|
|
1578
1464
|
* `count` operation's tally) when it matches the conditions, even though
|
|
1579
1465
|
* it would never appear in `records()`'s output.
|
|
1580
1466
|
*/
|
|
1581
|
-
aggregate(operation:
|
|
1467
|
+
aggregate(operation: AggregateOperation, column: FieldPath, input?: QueryInput, options?: OperationOptions): Promise<number | undefined>;
|
|
1582
1468
|
/**
|
|
1583
1469
|
* Lazy filtered iteration over the table's rows.
|
|
1584
1470
|
*
|
|
1585
1471
|
* @remarks
|
|
1586
|
-
* `
|
|
1472
|
+
* `input`'s `conditions` / `offset` / `limit` are honored lazily as rows
|
|
1587
1473
|
* stream; `order` is intentionally IGNORED — streaming yields driver
|
|
1588
1474
|
* key-order, sorted output is `records()`'s job. Breaking out of the
|
|
1589
1475
|
* iteration early closes the underlying source. The signal (if any) is
|
|
1590
1476
|
* checked before each yield.
|
|
1591
1477
|
*/
|
|
1592
|
-
scan(
|
|
1478
|
+
scan(input?: QueryInput, options?: OperationOptions): AsyncIterable<T>;
|
|
1593
1479
|
/**
|
|
1594
1480
|
* Upsert one or more rows.
|
|
1595
1481
|
*
|
|
@@ -1597,7 +1483,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1597
1483
|
* @param options - Optional abort signal
|
|
1598
1484
|
* @returns The row's key
|
|
1599
1485
|
*/
|
|
1600
|
-
set(row: T, options?:
|
|
1486
|
+
set(row: T, options?: OperationOptions): Promise<Key>;
|
|
1601
1487
|
/**
|
|
1602
1488
|
* Upsert one or more rows.
|
|
1603
1489
|
*
|
|
@@ -1610,7 +1496,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1610
1496
|
* surfaces as `DatabaseError` `ABORTED`. Already-applied items stay
|
|
1611
1497
|
* applied — there is no rollback. Wrap in `transaction()` for atomicity.
|
|
1612
1498
|
*/
|
|
1613
|
-
set(rows: readonly T[], options?:
|
|
1499
|
+
set(rows: readonly T[], options?: OperationOptions): Promise<readonly Key[]>;
|
|
1614
1500
|
/**
|
|
1615
1501
|
* Insert one or more rows, throwing `CONFLICT` on a duplicate key.
|
|
1616
1502
|
*
|
|
@@ -1618,7 +1504,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1618
1504
|
* @param options - Optional abort signal
|
|
1619
1505
|
* @returns The row's key
|
|
1620
1506
|
*/
|
|
1621
|
-
add(row: T, options?:
|
|
1507
|
+
add(row: T, options?: OperationOptions): Promise<Key>;
|
|
1622
1508
|
/**
|
|
1623
1509
|
* Insert one or more rows, throwing `CONFLICT` on a duplicate key.
|
|
1624
1510
|
*
|
|
@@ -1631,7 +1517,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1631
1517
|
* surfaces as `DatabaseError` `ABORTED`. Already-applied items stay
|
|
1632
1518
|
* applied — there is no rollback. Wrap in `transaction()` for atomicity.
|
|
1633
1519
|
*/
|
|
1634
|
-
add(rows: readonly T[], options?:
|
|
1520
|
+
add(rows: readonly T[], options?: OperationOptions): Promise<readonly Key[]>;
|
|
1635
1521
|
/**
|
|
1636
1522
|
* Apply a partial change to one or more rows.
|
|
1637
1523
|
*
|
|
@@ -1640,7 +1526,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1640
1526
|
* @param options - Optional abort signal
|
|
1641
1527
|
* @returns `true` when the row existed and was updated
|
|
1642
1528
|
*/
|
|
1643
|
-
update(key: Key, changes: Partial<T>, options?:
|
|
1529
|
+
update(key: Key, changes: Partial<T>, options?: OperationOptions): Promise<boolean>;
|
|
1644
1530
|
/**
|
|
1645
1531
|
* Apply a partial change to one or more rows.
|
|
1646
1532
|
*
|
|
@@ -1654,7 +1540,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1654
1540
|
* surfaces as `DatabaseError` `ABORTED`. Already-applied items stay
|
|
1655
1541
|
* applied — there is no rollback. Wrap in `transaction()` for atomicity.
|
|
1656
1542
|
*/
|
|
1657
|
-
update(keys: readonly Key[], changes: Partial<T>, options?:
|
|
1543
|
+
update(keys: readonly Key[], changes: Partial<T>, options?: OperationOptions): Promise<readonly boolean[]>;
|
|
1658
1544
|
/**
|
|
1659
1545
|
* Delete one or more rows.
|
|
1660
1546
|
*
|
|
@@ -1662,7 +1548,7 @@ export declare interface TableInterface<T = Row> {
|
|
|
1662
1548
|
* @param options - Optional abort signal
|
|
1663
1549
|
* @returns `true` when the row existed and was removed
|
|
1664
1550
|
*/
|
|
1665
|
-
remove(key: Key, options?:
|
|
1551
|
+
remove(key: Key, options?: OperationOptions): Promise<boolean>;
|
|
1666
1552
|
/**
|
|
1667
1553
|
* Delete one or more rows.
|
|
1668
1554
|
*
|
|
@@ -1675,20 +1561,20 @@ export declare interface TableInterface<T = Row> {
|
|
|
1675
1561
|
* surfaces as `DatabaseError` `ABORTED`. Already-applied items stay
|
|
1676
1562
|
* applied — there is no rollback. Wrap in `transaction()` for atomicity.
|
|
1677
1563
|
*/
|
|
1678
|
-
remove(keys: readonly Key[], options?:
|
|
1564
|
+
remove(keys: readonly Key[], options?: OperationOptions): Promise<readonly boolean[]>;
|
|
1679
1565
|
clear(): Promise<void>;
|
|
1680
1566
|
query(): QueryInterface<T>;
|
|
1681
1567
|
cursor(): Promise<CursorInterface<T>>;
|
|
1682
1568
|
}
|
|
1683
1569
|
|
|
1684
1570
|
/**
|
|
1685
|
-
*
|
|
1571
|
+
* A database's table schema — a map of table name to its {@link ColumnMap}.
|
|
1686
1572
|
*
|
|
1687
1573
|
* @remarks
|
|
1688
|
-
*
|
|
1689
|
-
*
|
|
1574
|
+
* Each table's row type is `Infer` of its columns (see {@link RowOf}); primary-key
|
|
1575
|
+
* columns are named separately via {@link PrimaryMap}.
|
|
1690
1576
|
*/
|
|
1691
|
-
export declare type
|
|
1577
|
+
export declare type TableMap = Readonly<Record<string, ColumnMap>>;
|
|
1692
1578
|
|
|
1693
1579
|
/**
|
|
1694
1580
|
* A backend-agnostic description of one table — what `open` hands each driver so a
|
|
@@ -1696,7 +1582,7 @@ export declare type TableKeys = Readonly<Record<string, string>>;
|
|
|
1696
1582
|
*
|
|
1697
1583
|
* @remarks
|
|
1698
1584
|
* Derived by the database from its `tables` contract shapes ({@link ColumnSchema}
|
|
1699
|
-
* per column, via `
|
|
1585
|
+
* per column, via `shapeToColumnStorage`), its `primary`, and its `indexes` option
|
|
1700
1586
|
* (`indexes`, each entry one possibly-compound index of column names). A scan-only
|
|
1701
1587
|
* backend (the reference `MemoryDriver`) ignores everything but `name`.
|
|
1702
1588
|
*/
|
|
@@ -1708,64 +1594,17 @@ export declare interface TableSchema {
|
|
|
1708
1594
|
}
|
|
1709
1595
|
|
|
1710
1596
|
/**
|
|
1711
|
-
*
|
|
1712
|
-
*
|
|
1713
|
-
* @remarks
|
|
1714
|
-
* Each table's row type is `Infer` of its columns (see {@link RowOf}); primary-key
|
|
1715
|
-
* columns are named separately via {@link TableKeys}.
|
|
1716
|
-
*/
|
|
1717
|
-
export declare type TablesShape = Readonly<Record<string, Columns>>;
|
|
1718
|
-
|
|
1719
|
-
/**
|
|
1720
|
-
* The handle a driver's native `transaction` hook returns.
|
|
1597
|
+
* Validate the paging fields of a portable query.
|
|
1721
1598
|
*
|
|
1722
1599
|
* @remarks
|
|
1723
|
-
* `
|
|
1724
|
-
*
|
|
1725
|
-
*
|
|
1726
|
-
|
|
1727
|
-
export declare interface TransactionInterface {
|
|
1728
|
-
commit(): Promise<void>;
|
|
1729
|
-
rollback(): Promise<void>;
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
/** The number of bytes encoded by an RFC 4122 UUID. */
|
|
1733
|
-
export declare const UUID_BYTE_COUNT = 16;
|
|
1734
|
-
|
|
1735
|
-
/** The number of distinct values one UUID byte may hold. */
|
|
1736
|
-
export declare const UUID_BYTE_RANGE = 256;
|
|
1737
|
-
|
|
1738
|
-
/**
|
|
1739
|
-
* Match a value against a wildcard pattern in LINEAR time — the shared, ReDoS-SAFE
|
|
1740
|
-
* engine behind {@link likeMatch} and {@link globMatch}.
|
|
1741
|
-
*
|
|
1742
|
-
* @remarks
|
|
1743
|
-
* A backtracking RegExp (`a%b%c` → `^a.*b.*c$`) is CATASTROPHIC on a hostile pattern:
|
|
1744
|
-
* `.*` segments separated by literals, matched against a long non-matching input, blow
|
|
1745
|
-
* up super-linearly — and JS has no atomic groups / possessive quantifiers to bound it
|
|
1746
|
-
* (AGENTS §6.5, now that the authed server runs model-supplied `list` criteria over the
|
|
1747
|
-
* wire). So this builds NO regex. It runs the classic GREEDY TWO-POINTER wildcard match:
|
|
1748
|
-
* the `any` wildcard records its position and, on a later mismatch, backtracks ONLY to
|
|
1749
|
-
* that last `any` (letting it absorb one more char) — so the work is O(value × pattern),
|
|
1750
|
-
* never the exponential / polynomial backtracking a regex would do. The pattern length
|
|
1751
|
-
* is capped at {@link MAX_PATTERN_LENGTH} (a `VALIDATION` {@link DatabaseError} over it),
|
|
1752
|
-
* bounding the pattern factor so a match stays linear in the value length whatever the
|
|
1753
|
-
* pattern.
|
|
1600
|
+
* A present `limit` or `offset` must be a finite nonnegative integer; zero is
|
|
1601
|
+
* valid. Validation is deterministic (`limit` before `offset`). Non-finite
|
|
1602
|
+
* values are rendered as strings in error context so JSON serialization cannot
|
|
1603
|
+
* collapse `NaN` or infinity to `null`.
|
|
1754
1604
|
*
|
|
1755
|
-
*
|
|
1756
|
-
*
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
* shadows the wildcard. Case folding is applied to BOTH sides when `fold` is set.
|
|
1760
|
-
*
|
|
1761
|
-
* @param value - The value to test
|
|
1762
|
-
* @param pattern - The wildcard pattern
|
|
1763
|
-
* @param any - The any-run wildcard char (`%` for `LIKE`, `*` for `GLOB`)
|
|
1764
|
-
* @param single - The single-char wildcard char (`_` for `LIKE`, `?` for `GLOB`)
|
|
1765
|
-
* @param fold - Whether to match case-INSENSITIVELY (`LIKE` folds; `GLOB` does not)
|
|
1766
|
-
* @returns Whether `value` matches `pattern`
|
|
1767
|
-
* @throws A `VALIDATION` {@link DatabaseError} when `pattern` exceeds {@link MAX_PATTERN_LENGTH}
|
|
1768
|
-
*/
|
|
1769
|
-
export declare function wildcardMatch(value: string, pattern: string, any: string, single: string, fold: boolean): boolean;
|
|
1605
|
+
* @param input - The portable query whose paging fields to validate
|
|
1606
|
+
* @throws {@link DatabaseError} `VALIDATION` when a paging field is invalid
|
|
1607
|
+
*/
|
|
1608
|
+
export declare function validatePage(input?: QueryInput): void;
|
|
1770
1609
|
|
|
1771
|
-
export { }
|
|
1610
|
+
export { }
|