@orkestrel/database 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,15 @@
1
1
  # @orkestrel/database
2
2
 
3
- A typed database abstraction for the `@orkestrel` line one public
4
- `Database` over internal table/query engines and pluggable storage drivers at
5
- the seams. Consumers use `TableInterface` and `QueryInterface`. Built to sit beside
6
- `@orkestrel/contract` (validation) and `@orkestrel/emitter` (observable
7
- lifecycle), reusing both directly. `TableInterface.cursor()` exposes the
8
- `CursorInterface` contract for serial bulk mutation.
3
+ > One typed database API for keyed rows, fluent queries, cursors, and
4
+ > whole-store transactions, running unchanged over an in-memory map, a JSON
5
+ > file, SQLite, or IndexedDB.
6
+
7
+ Declare your tables with the `createDatabase` function, hold each
8
+ `TableInterface` it hands back, and reach rows by key or through the fluent
9
+ `query()` builder. `TableInterface.cursor()` opens the `CursorInterface`
10
+ contract for serial bulk mutation. Built on `@orkestrel/contract` for
11
+ validation and `@orkestrel/emitter` for the observation surface, reusing both
12
+ directly. Part of the `@orkestrel` line.
9
13
 
10
14
  ## Install
11
15
 
@@ -1,19 +1,16 @@
1
- import { ColumnStorage } from '@orkestrel/database';
2
- import { Condition } from '@orkestrel/database';
1
+ import type { ColumnStorage } from '@orkestrel/database';
2
+ import type { Condition } from '@orkestrel/database';
3
3
  import { DatabaseError } from '@orkestrel/database';
4
- import { DriverInterface } from '@orkestrel/database';
5
- import { DriverInterface as DriverInterface_2 } from '@orkestrel/database';
6
- import { DriverMetadata } from '@orkestrel/database';
7
- import { IndexedDBError } from '@orkestrel/indexeddb';
8
- import { Key } from '@orkestrel/database';
9
- import { MigrationInput } from '@orkestrel/database';
10
- import { OperationOptions } from '@orkestrel/database';
11
- import { QueryInput } from '@orkestrel/database';
12
- import { QueryInput as QueryInput_2 } from '@orkestrel/database';
13
- import { Row } from '@orkestrel/database';
14
- import { StoreDefinition } from '@orkestrel/indexeddb';
15
- import { TableSchema } from '@orkestrel/database';
16
- import { TableSchema as TableSchema_2 } from '@orkestrel/database';
4
+ import type { DriverInterface } from '@orkestrel/database';
5
+ import type { DriverMetadata } from '@orkestrel/database';
6
+ import type { IndexedDBError } from '@orkestrel/indexeddb';
7
+ import type { Key } from '@orkestrel/database';
8
+ import type { MigrationInput } from '@orkestrel/database';
9
+ import type { OperationOptions } from '@orkestrel/database';
10
+ import type { QueryInput } from '@orkestrel/database';
11
+ import type { Row } from '@orkestrel/database';
12
+ import type { StoreDefinition } from '@orkestrel/indexeddb';
13
+ import type { TableSchema } from '@orkestrel/database';
17
14
 
18
15
  /**
19
16
  * Translates one {@link Condition} to the `IDBKeyRange` it maps to, when its
@@ -24,14 +21,14 @@ import { TableSchema as TableSchema_2 } from '@orkestrel/database';
24
21
  * Only the comparison operators (`equals`/`above`/`below`/`from`/`to`/`between`)
25
22
  * translate to a key range that a typed (string/number) column can back with an
26
23
  * IndexedDB store/index read — see {@link selectPlan} for the caveats that
27
- * decide WHICH of `below`/`to` may drive a SECONDARY-index read versus the
24
+ * decide which of `below`/`to` may drive a secondary-index read versus the
28
25
  * primary store only (a column-type / absent-row concern, not a range-shape
29
26
  * one). `starts` is excluded — its prefix range can miss strings past U+FFFF;
30
27
  * the membership / negation / pattern / existence operators (`not`/`like`/`glob`/
31
28
  * `ends`/`any`/`none`/`absent`/`present`) have no single exact range. The operand
32
29
  * guard (`typeof` string/number) rejects a non-scalar value (for example an array, a
33
30
  * boolean) that is not a usable key. `between` additionally guards against a
34
- * REVERSED pair (`first > second`): native `IDBKeyRange.bound` throws a raw
31
+ * reversed pair (`first > second`): native `IDBKeyRange.bound` throws a raw
35
32
  * `DataError` `DOMException` for a lower bound above the upper bound, so a
36
33
  * reversed pair returns `undefined` here (falls back to a full scan, which the
37
34
  * engine then correctly resolves to an empty result) rather than letting a
@@ -47,7 +44,7 @@ import { TableSchema as TableSchema_2 } from '@orkestrel/database';
47
44
  export declare function conditionToRange(condition: Condition): IDBKeyRange | undefined;
48
45
 
49
46
  /**
50
- * Creates a persistent IndexedDB {@link DriverInterface} for the core database layer.
47
+ * Creates a persistent IndexedDB {@link DriverInterface} for a browser database name.
51
48
  *
52
49
  * @remarks
53
50
  * Pass it to `createDatabase` from `@orkestrel/database` to run the typed database
@@ -74,7 +71,7 @@ export declare function conditionToRange(condition: Condition): IDBKeyRange | un
74
71
  * await db.table('users').set({ id: 'u1', name: 'Ada' }) // persisted to IndexedDB
75
72
  * ```
76
73
  */
77
- export declare function createIndexedDBDriver(name: string): DriverInterface_2;
74
+ export declare function createIndexedDBDriver(name: string): DriverInterface;
78
75
 
79
76
  /**
80
77
  * Derives an IndexedDB index name for a declared column group — a bare column
@@ -83,14 +80,14 @@ export declare function createIndexedDBDriver(name: string): DriverInterface_2;
83
80
  *
84
81
  * @remarks
85
82
  * Naming a compound index by joining its columns with `_` (`['a', 'b'] →
86
- * 'a_b'`) collides with a single-column index over a column LITERALLY named
83
+ * 'a_b'`) collides with a single-column index over a column literally named
87
84
  * `'a_b'` — the same name, two different key paths (`'a_b'` vs `['a', 'b']`),
88
85
  * which either throws a native `ConstraintError` from a duplicate
89
86
  * `createIndex` call at open, or (worse) lets {@link selectPlan}'s name-based
90
- * lookup match the wrong index. A single-column index keeps the BARE column
87
+ * lookup match the wrong index. A single-column index keeps the bare column
91
88
  * name — {@link selectPlan} matches `available.includes(condition.column)` by
92
89
  * that exact name, so a single-column index must stay named after its column
93
- * verbatim. A compound index instead encodes each column as a LENGTH-PREFIXED
90
+ * verbatim. A compound index instead encodes each column as a length-prefixed
94
91
  * segment (`'2#1:a1:b'`), so the boundary between columns is self-describing
95
92
  * and cannot be reconstructed by any other column list — including one
96
93
  * containing a column that happens to look like an encoded segment.
@@ -155,28 +152,28 @@ export declare const INDEXABLE_STORAGE: readonly ColumnStorage[];
155
152
  * {@link Migration} plan natively: IndexedDB schema DDL (creating/dropping a
156
153
  * store, creating/dropping an index) is legal only inside a versionchange
157
154
  * transaction (`onupgradeneeded`), so `migrate` closes the current connection
158
- * and opens a FRESH one at `version + 1` with an `upgrade` hook that walks the
155
+ * and opens a fresh one at `version + 1` with an `upgrade` hook that walks the
159
156
  * plan's steps — dropping stores, adding/removing indexes on the raw
160
157
  * `IDBTransaction`, and rewriting rows for `column.remove` through a cursor walk
161
158
  * (the one step needing to touch existing data; `column.add` is a no-op — this
162
159
  * driver stores whatever a row carries, so there is nothing to backfill). A
163
- * step referencing an unknown table is validated BEFORE the reconnect, so a
160
+ * step referencing an unknown table is validated before the reconnect, so a
164
161
  * `MIGRATION` `DatabaseError` never wastes a version bump.
165
162
  *
166
163
  * @remarks
167
- * This unit deliberately OMITS `aggregate` / `transaction`. There is no native
164
+ * This unit deliberately omits `aggregate` / `transaction`. There is no native
168
165
  * `aggregate` (IndexedDB has no native SUM/AVG); the engine over the narrowed
169
166
  * `records` covers it. `transaction` is impossible here: the wrapper
170
167
  * auto-commits an `IDBTransaction` when control yields outside its request
171
168
  * chain, so arbitrary callback awaits cannot remain inside one native
172
169
  * transaction. Every atomic multi-operation sequence in this driver
173
- * (`snapshot`'s rollback) instead runs entirely inside ONE `db.write(...)`
170
+ * (`snapshot`'s rollback) instead runs entirely inside one `db.write(...)`
174
171
  * scope.
175
172
  */
176
173
  export declare class IndexedDBDriver implements DriverInterface {
177
174
  #private;
178
175
  constructor(name: string);
179
- open(schema: readonly TableSchema_2[]): Promise<void>;
176
+ open(schema: readonly TableSchema[]): Promise<void>;
180
177
  close(): Promise<void>;
181
178
  read(table: string, key: Key): Promise<Row | undefined>;
182
179
  write(table: string, key: Key, row: Row, options?: OperationOptions): Promise<void>;
@@ -185,8 +182,8 @@ export declare class IndexedDBDriver implements DriverInterface {
185
182
  keys(table: string): Promise<readonly Key[]>;
186
183
  scan(table: string): AsyncIterable<Row>;
187
184
  clear(table: string): Promise<void>;
188
- records(table: string, input: QueryInput_2): Promise<readonly Row[]>;
189
- stream(table: string, input: QueryInput_2): AsyncIterable<Row>;
185
+ records(table: string, input: QueryInput): Promise<readonly Row[]>;
186
+ stream(table: string, input: QueryInput): AsyncIterable<Row>;
190
187
  snapshot(tables?: readonly string[]): Promise<() => Promise<void>>;
191
188
  /**
192
189
  * Returns the persisted {@link DriverMetadata}, or `undefined` when the store has
@@ -213,11 +210,11 @@ export declare class IndexedDBDriver implements DriverInterface {
213
210
  *
214
211
  * @remarks
215
212
  * IndexedDB schema DDL is legal only inside `onupgradeneeded`, so this closes
216
- * the current connection and opens a FRESH one at `version + 1`, declaring
217
- * every currently-known store (plus {@link METADATA_STORE}) so nothing is lost,
218
- * and applying `table.remove` / `index.add` / `index.remove` /
213
+ * the current connection and opens a fresh one at `version + 1`, declaring
214
+ * every store known at that point (plus {@link METADATA_STORE}) so nothing is
215
+ * lost, and applying `table.remove` / `index.add` / `index.remove` /
219
216
  * `column.remove` inside `upgrade`. Every step's `table` is validated against
220
- * the driver's own `#schema` BEFORE the reconnect — an unknown-table step
217
+ * the driver's own `#schema` before the reconnect — an unknown-table step
221
218
  * throws `DatabaseError` `MIGRATION` without ever bumping the version.
222
219
  * `table.add` / `column.add` need no upgrade-time action: `table.add` is
223
220
  * created by the wrapper's built-in create-missing-stores pass (its
@@ -260,7 +257,7 @@ export declare function mapIndexedDBError(error: IndexedDBError): DatabaseError;
260
257
 
261
258
  /**
262
259
  * Maps a backend {@link IndexedDBError} to the portable `DatabaseError` taxonomy
263
- * for use INSIDE `migrate()` — the one context where `UPGRADE` means the
260
+ * for use inside `migrate()` — the one context where `UPGRADE` means the
264
261
  * migration itself failed, not a generic driver fault.
265
262
  *
266
263
  * @remarks
@@ -277,13 +274,13 @@ export declare function mapIndexedDBError(error: IndexedDBError): DatabaseError;
277
274
  export declare function mapMigrationError(error: IndexedDBError): DatabaseError;
278
275
 
279
276
  /**
280
- * Names the reserved out-of-line store the {@link IndexedDBDriver} stamps its
281
- * {@link DriverMetadata} into.
277
+ * Names the reserved out-of-line store `__metadata__` the {@link IndexedDBDriver}
278
+ * stamps its {@link DriverMetadata} into.
282
279
  *
283
280
  * @remarks
284
- * Backs the driver's `metadata` / `stamp` hooks. A user table declared with this
285
- * exact name collides with the driver's own bookkeeping, so a caller must avoid
286
- * it; the collision is caught at `open`.
281
+ * Backs the driver's `metadata` / `stamp` hooks. A user table named `__metadata__`
282
+ * collides with the driver's own bookkeeping, so a caller must avoid it; the
283
+ * collision is caught at `open`.
287
284
  */
288
285
  export declare const METADATA_STORE = "__metadata__";
289
286
 
@@ -292,7 +289,8 @@ export declare const METADATA_STORE = "__metadata__";
292
289
  * a read. An omitted `index` selects the primary store; an omitted `range`
293
290
  * performs a full scan. The plan is always a superset of the matching rows;
294
291
  * the core engine refines it to the exact result. An empty plan (`{}`) is a
295
- * primary-store full scan.
292
+ * primary-store full scan. The `@orkestrel/database/browser` entry point exports
293
+ * this type.
296
294
  */
297
295
  export declare interface QueryPlan {
298
296
  readonly index?: string;
@@ -302,6 +300,11 @@ export declare interface QueryPlan {
302
300
  /**
303
301
  * Projects a table schema into the IndexedDB wrapper's store definition.
304
302
  *
303
+ * @remarks
304
+ * The definition is what an ordered versionchange migration creates the store
305
+ * from, carrying each declared index under the name {@link deriveIndexedDBIndexName}
306
+ * derives for its column group.
307
+ *
305
308
  * @param schema - Portable table schema
306
309
  * @returns Store definition with declared indexes
307
310
  */
@@ -312,7 +315,7 @@ export declare function schemaToStore(schema: TableSchema): StoreDefinition;
312
315
  * store) and {@link IDBKeyRange} to narrow by, falling back to a full scan.
313
316
  *
314
317
  * @remarks
315
- * Pushdown is sound ONLY when every condition is `and`-joined: the engine folds
318
+ * Pushdown is sound only when every condition is `and`-joined: the engine folds
316
319
  * conditions left-to-right (`c1 && c2 && … && cn`), so the result is a subset of
317
320
  * each — narrowing on any one is then a valid superset. A single `or` breaks that
318
321
  * (a row can match through a later condition the range would exclude), so any `or`
@@ -326,21 +329,21 @@ export declare function schemaToStore(schema: TableSchema): StoreDefinition;
326
329
  * type (`boolean`/`json`/`blob`), uses a non-comparison operator, or has a
327
330
  * non-scalar operand cannot push and is skipped.
328
331
  *
329
- * **`below`/`to` may drive a SECONDARY-index range only when the column has NO
332
+ * **`below`/`to` may drive a secondary-index range only when the column has no
330
333
  * absent/null rows to lose — which this planner cannot verify from the schema
331
- * alone, so it restricts them to the PRIMARY store, where that is always true.**
332
- * The engine's total order (`compareValues`, see `@src/core`) ranks
333
- * `undefined` (absent) and `null` BELOW every number/string, so
334
- * `matchesCondition('below' | 'to', …)` is TRUE for a row whose field is absent
335
- * or `null` — but a secondary IndexedDB index has NO ENTRY for a row whose
334
+ * alone, so it restricts them to the primary store, where that is always true.**
335
+ * The engine's total order (`compareValues`, see `@orkestrel/database`) ranks
336
+ * `undefined` (absent) and `null` below every number/string, so
337
+ * `matchesCondition('below' | 'to', …)` is true for a row whose field is absent
338
+ * or `null` — but a secondary IndexedDB index has no entry for a row whose
336
339
  * indexed field is absent/`null`, so a `below`/`to` range read against that
337
- * index would SILENTLY DROP those rows (they can never be over-fetched, only
340
+ * index would silently drop those rows (they can never be over-fetched, only
338
341
  * missed — the one shape of lossiness this planner must never produce). The
339
- * table's PRIMARY key is exempt: a row's primary-key value is always present
342
+ * table's primary key is exempt: a row's primary-key value is always present
340
343
  * and never `null` (it is the row's identity, enforced at write time), so a
341
344
  * `below`/`to` range against the primary store can never exclude an
342
345
  * absent/null-keyed row because no such row exists. `equals`/`above`/`from`/
343
- * `between` stay index-eligible on ANY orderable column, primary or secondary:
346
+ * `between` stay index-eligible on any orderable column, primary or secondary:
344
347
  * each is bounded below by a scalar (`equals`/`between`'s lower bound, `above`/
345
348
  * `from`'s lower bound), and every scalar strictly out-ranks `undefined`/`null`
346
349
  * in the total order, so an absent/null-valued row can never satisfy them — the
@@ -354,7 +357,7 @@ export declare function schemaToStore(schema: TableSchema): StoreDefinition;
354
357
  * metadata.
355
358
  *
356
359
  * When no condition qualifies the plan is a full scan (`{}`) and the engine
357
- * does everything. The plan is always a SUPERSET of the
360
+ * does everything. The plan is always a superset of the
358
361
  * matching rows — the only correctness contract — so the driver may safely run
359
362
  * the exact engine over it.
360
363
  *
@@ -19,13 +19,13 @@ var INDEXABLE_STORAGE = Object.freeze([
19
19
  "real"
20
20
  ]);
21
21
  /**
22
- * Names the reserved out-of-line store the {@link IndexedDBDriver} stamps its
23
- * {@link DriverMetadata} into.
22
+ * Names the reserved out-of-line store `__metadata__` the {@link IndexedDBDriver}
23
+ * stamps its {@link DriverMetadata} into.
24
24
  *
25
25
  * @remarks
26
- * Backs the driver's `metadata` / `stamp` hooks. A user table declared with this
27
- * exact name collides with the driver's own bookkeeping, so a caller must avoid
28
- * it; the collision is caught at `open`.
26
+ * Backs the driver's `metadata` / `stamp` hooks. A user table named `__metadata__`
27
+ * collides with the driver's own bookkeeping, so a caller must avoid it; the
28
+ * collision is caught at `open`.
29
29
  */
30
30
  var METADATA_STORE = "__metadata__";
31
31
  //#endregion
@@ -39,14 +39,14 @@ var METADATA_STORE = "__metadata__";
39
39
  * Only the comparison operators (`equals`/`above`/`below`/`from`/`to`/`between`)
40
40
  * translate to a key range that a typed (string/number) column can back with an
41
41
  * IndexedDB store/index read — see {@link selectPlan} for the caveats that
42
- * decide WHICH of `below`/`to` may drive a SECONDARY-index read versus the
42
+ * decide which of `below`/`to` may drive a secondary-index read versus the
43
43
  * primary store only (a column-type / absent-row concern, not a range-shape
44
44
  * one). `starts` is excluded — its prefix range can miss strings past U+FFFF;
45
45
  * the membership / negation / pattern / existence operators (`not`/`like`/`glob`/
46
46
  * `ends`/`any`/`none`/`absent`/`present`) have no single exact range. The operand
47
47
  * guard (`typeof` string/number) rejects a non-scalar value (for example an array, a
48
48
  * boolean) that is not a usable key. `between` additionally guards against a
49
- * REVERSED pair (`first > second`): native `IDBKeyRange.bound` throws a raw
49
+ * reversed pair (`first > second`): native `IDBKeyRange.bound` throws a raw
50
50
  * `DataError` `DOMException` for a lower bound above the upper bound, so a
51
51
  * reversed pair returns `undefined` here (falls back to a full scan, which the
52
52
  * engine then correctly resolves to an empty result) rather than letting a
@@ -85,7 +85,7 @@ function conditionToRange(condition) {
85
85
  * store) and {@link IDBKeyRange} to narrow by, falling back to a full scan.
86
86
  *
87
87
  * @remarks
88
- * Pushdown is sound ONLY when every condition is `and`-joined: the engine folds
88
+ * Pushdown is sound only when every condition is `and`-joined: the engine folds
89
89
  * conditions left-to-right (`c1 && c2 && … && cn`), so the result is a subset of
90
90
  * each — narrowing on any one is then a valid superset. A single `or` breaks that
91
91
  * (a row can match through a later condition the range would exclude), so any `or`
@@ -99,21 +99,21 @@ function conditionToRange(condition) {
99
99
  * type (`boolean`/`json`/`blob`), uses a non-comparison operator, or has a
100
100
  * non-scalar operand cannot push and is skipped.
101
101
  *
102
- * **`below`/`to` may drive a SECONDARY-index range only when the column has NO
102
+ * **`below`/`to` may drive a secondary-index range only when the column has no
103
103
  * absent/null rows to lose — which this planner cannot verify from the schema
104
- * alone, so it restricts them to the PRIMARY store, where that is always true.**
104
+ * alone, so it restricts them to the primary store, where that is always true.**
105
105
  * The engine's total order (`compareValues`, see `@src/core`) ranks
106
- * `undefined` (absent) and `null` BELOW every number/string, so
107
- * `matchesCondition('below' | 'to', …)` is TRUE for a row whose field is absent
108
- * or `null` — but a secondary IndexedDB index has NO ENTRY for a row whose
106
+ * `undefined` (absent) and `null` below every number/string, so
107
+ * `matchesCondition('below' | 'to', …)` is true for a row whose field is absent
108
+ * or `null` — but a secondary IndexedDB index has no entry for a row whose
109
109
  * indexed field is absent/`null`, so a `below`/`to` range read against that
110
- * index would SILENTLY DROP those rows (they can never be over-fetched, only
110
+ * index would silently drop those rows (they can never be over-fetched, only
111
111
  * missed — the one shape of lossiness this planner must never produce). The
112
- * table's PRIMARY key is exempt: a row's primary-key value is always present
112
+ * table's primary key is exempt: a row's primary-key value is always present
113
113
  * and never `null` (it is the row's identity, enforced at write time), so a
114
114
  * `below`/`to` range against the primary store can never exclude an
115
115
  * absent/null-keyed row because no such row exists. `equals`/`above`/`from`/
116
- * `between` stay index-eligible on ANY orderable column, primary or secondary:
116
+ * `between` stay index-eligible on any orderable column, primary or secondary:
117
117
  * each is bounded below by a scalar (`equals`/`between`'s lower bound, `above`/
118
118
  * `from`'s lower bound), and every scalar strictly out-ranks `undefined`/`null`
119
119
  * in the total order, so an absent/null-valued row can never satisfy them — the
@@ -127,7 +127,7 @@ function conditionToRange(condition) {
127
127
  * metadata.
128
128
  *
129
129
  * When no condition qualifies the plan is a full scan (`{}`) and the engine
130
- * does everything. The plan is always a SUPERSET of the
130
+ * does everything. The plan is always a superset of the
131
131
  * matching rows — the only correctness contract — so the driver may safely run
132
132
  * the exact engine over it.
133
133
  *
@@ -209,7 +209,7 @@ function mapIndexedDBError(error) {
209
209
  }
210
210
  /**
211
211
  * Maps a backend {@link IndexedDBError} to the portable `DatabaseError` taxonomy
212
- * for use INSIDE `migrate()` — the one context where `UPGRADE` means the
212
+ * for use inside `migrate()` — the one context where `UPGRADE` means the
213
213
  * migration itself failed, not a generic driver fault.
214
214
  *
215
215
  * @remarks
@@ -234,14 +234,14 @@ function mapMigrationError(error) {
234
234
  *
235
235
  * @remarks
236
236
  * Naming a compound index by joining its columns with `_` (`['a', 'b'] →
237
- * 'a_b'`) collides with a single-column index over a column LITERALLY named
237
+ * 'a_b'`) collides with a single-column index over a column literally named
238
238
  * `'a_b'` — the same name, two different key paths (`'a_b'` vs `['a', 'b']`),
239
239
  * which either throws a native `ConstraintError` from a duplicate
240
240
  * `createIndex` call at open, or (worse) lets {@link selectPlan}'s name-based
241
- * lookup match the wrong index. A single-column index keeps the BARE column
241
+ * lookup match the wrong index. A single-column index keeps the bare column
242
242
  * name — {@link selectPlan} matches `available.includes(condition.column)` by
243
243
  * that exact name, so a single-column index must stay named after its column
244
- * verbatim. A compound index instead encodes each column as a LENGTH-PREFIXED
244
+ * verbatim. A compound index instead encodes each column as a length-prefixed
245
245
  * segment (`'2#1:a1:b'`), so the boundary between columns is self-describing
246
246
  * and cannot be reconstructed by any other column list — including one
247
247
  * containing a column that happens to look like an encoded segment.
@@ -263,6 +263,11 @@ function deriveIndexedDBIndexName(columns) {
263
263
  /**
264
264
  * Projects a table schema into the IndexedDB wrapper's store definition.
265
265
  *
266
+ * @remarks
267
+ * The definition is what an ordered versionchange migration creates the store
268
+ * from, carrying each declared index under the name {@link deriveIndexedDBIndexName}
269
+ * derives for its column group.
270
+ *
266
271
  * @param schema - Portable table schema
267
272
  * @returns Store definition with declared indexes
268
273
  */
@@ -312,22 +317,22 @@ function schemaToStore(schema) {
312
317
  * {@link Migration} plan natively: IndexedDB schema DDL (creating/dropping a
313
318
  * store, creating/dropping an index) is legal only inside a versionchange
314
319
  * transaction (`onupgradeneeded`), so `migrate` closes the current connection
315
- * and opens a FRESH one at `version + 1` with an `upgrade` hook that walks the
320
+ * and opens a fresh one at `version + 1` with an `upgrade` hook that walks the
316
321
  * plan's steps — dropping stores, adding/removing indexes on the raw
317
322
  * `IDBTransaction`, and rewriting rows for `column.remove` through a cursor walk
318
323
  * (the one step needing to touch existing data; `column.add` is a no-op — this
319
324
  * driver stores whatever a row carries, so there is nothing to backfill). A
320
- * step referencing an unknown table is validated BEFORE the reconnect, so a
325
+ * step referencing an unknown table is validated before the reconnect, so a
321
326
  * `MIGRATION` `DatabaseError` never wastes a version bump.
322
327
  *
323
328
  * @remarks
324
- * This unit deliberately OMITS `aggregate` / `transaction`. There is no native
329
+ * This unit deliberately omits `aggregate` / `transaction`. There is no native
325
330
  * `aggregate` (IndexedDB has no native SUM/AVG); the engine over the narrowed
326
331
  * `records` covers it. `transaction` is impossible here: the wrapper
327
332
  * auto-commits an `IDBTransaction` when control yields outside its request
328
333
  * chain, so arbitrary callback awaits cannot remain inside one native
329
334
  * transaction. Every atomic multi-operation sequence in this driver
330
- * (`snapshot`'s rollback) instead runs entirely inside ONE `db.write(...)`
335
+ * (`snapshot`'s rollback) instead runs entirely inside one `db.write(...)`
331
336
  * scope.
332
337
  */
333
338
  var IndexedDBDriver = class {
@@ -568,11 +573,11 @@ var IndexedDBDriver = class {
568
573
  *
569
574
  * @remarks
570
575
  * IndexedDB schema DDL is legal only inside `onupgradeneeded`, so this closes
571
- * the current connection and opens a FRESH one at `version + 1`, declaring
572
- * every currently-known store (plus {@link METADATA_STORE}) so nothing is lost,
573
- * and applying `table.remove` / `index.add` / `index.remove` /
576
+ * the current connection and opens a fresh one at `version + 1`, declaring
577
+ * every store known at that point (plus {@link METADATA_STORE}) so nothing is
578
+ * lost, and applying `table.remove` / `index.add` / `index.remove` /
574
579
  * `column.remove` inside `upgrade`. Every step's `table` is validated against
575
- * the driver's own `#schema` BEFORE the reconnect — an unknown-table step
580
+ * the driver's own `#schema` before the reconnect — an unknown-table step
576
581
  * throws `DatabaseError` `MIGRATION` without ever bumping the version.
577
582
  * `table.add` / `column.add` need no upgrade-time action: `table.add` is
578
583
  * created by the wrapper's built-in create-missing-stores pass (its
@@ -824,7 +829,7 @@ var IndexedDBDriver = class {
824
829
  //#endregion
825
830
  //#region src/browser/factories.ts
826
831
  /**
827
- * Creates a persistent IndexedDB {@link DriverInterface} for the core database layer.
832
+ * Creates a persistent IndexedDB {@link DriverInterface} for a browser database name.
828
833
  *
829
834
  * @remarks
830
835
  * Pass it to `createDatabase` from `@orkestrel/database` to run the typed database