@asaidimu/utils-database 1.1.0 → 2.0.0
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 +2 -3
- package/index.d.mts +548 -280
- package/index.d.ts +548 -280
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/package.json +2 -3
package/index.d.mts
CHANGED
|
@@ -1,16 +1,79 @@
|
|
|
1
1
|
import { QueryFilter, PaginationOptions } from '@asaidimu/query';
|
|
2
|
-
import { SchemaDefinition, SchemaChange, DataTransform, PredicateMap } from '@asaidimu/anansi';
|
|
3
|
-
import { EventBus } from '@asaidimu/events';
|
|
2
|
+
import { IndexDefinition, SchemaDefinition, SchemaChange, DataTransform, PredicateMap } from '@asaidimu/anansi';
|
|
4
3
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
5
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Buffers write operations across one or more stores and commits them atomically.
|
|
7
|
+
*
|
|
8
|
+
* ## How atomicity works
|
|
9
|
+
*
|
|
10
|
+
* ### IndexedDB stores (same database)
|
|
11
|
+
* At commit time, TransactionContext collects the names of every IDB store that
|
|
12
|
+
* received operations, then opens a **single** `IDBTransaction` spanning all of
|
|
13
|
+
* them via `ConnectionManager.openTransaction`. Each store's `executeInTransaction`
|
|
14
|
+
* receives that shared transaction object and performs its writes against it
|
|
15
|
+
* without opening a new transaction of its own. IDB commits or aborts the
|
|
16
|
+
* entire multi-store transaction as one unit.
|
|
17
|
+
*
|
|
18
|
+
* ### MemoryStore
|
|
19
|
+
* MemoryStore's `executeInTransaction` receives `null` for the shared transaction.
|
|
20
|
+
* It applies ops against an internal staging map and returns. If a later
|
|
21
|
+
* participant fails, TransactionContext calls `rollbackMemory` on each
|
|
22
|
+
* MemoryStore that already applied its staged ops. MemoryStore restores its
|
|
23
|
+
* pre-transaction snapshot.
|
|
24
|
+
*
|
|
25
|
+
* ### Mixed (IDB + Memory in the same transaction)
|
|
26
|
+
* All IDB stores are committed first as a single atomic IDB transaction, then
|
|
27
|
+
* each MemoryStore is committed. If a MemoryStore fails after IDB has already
|
|
28
|
+
* committed, the IDB side cannot be rolled back — this is an inherent limitation
|
|
29
|
+
* of mixing two different storage engines. In practice the schema store is
|
|
30
|
+
* always MemoryStore-or-IDB consistently, so mixed transactions should not arise
|
|
31
|
+
* in normal usage.
|
|
32
|
+
*/
|
|
6
33
|
declare class TransactionContext {
|
|
7
34
|
readonly id: string;
|
|
8
|
-
|
|
9
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Flat list of every operation staged so far, in the order they were added.
|
|
37
|
+
* We keep the store reference alongside the op so commit() can group them.
|
|
38
|
+
*/
|
|
39
|
+
private staged;
|
|
40
|
+
private done;
|
|
10
41
|
constructor();
|
|
11
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Stages a single write operation against a store.
|
|
44
|
+
* Does NOT touch the store — no I/O happens until commit().
|
|
45
|
+
*/
|
|
46
|
+
addOp<T extends Record<string, any>>(store: Store<T>, type: "put" | "delete" | "add", data: any): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Commits all staged operations atomically.
|
|
49
|
+
*
|
|
50
|
+
* For IDB stores: opens one shared IDBTransaction across all participating
|
|
51
|
+
* stores, then dispatches ops to each store's executeInTransaction.
|
|
52
|
+
* For MemoryStores: dispatches sequentially; rolls back on failure.
|
|
53
|
+
*/
|
|
12
54
|
commit(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Discards all staged operations. No I/O has occurred so there is nothing
|
|
57
|
+
* to undo — we simply clear the buffer.
|
|
58
|
+
*/
|
|
13
59
|
rollback(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Opens ONE IDBTransaction across all participating IDB stores and lets
|
|
62
|
+
* each store execute its ops against the shared transaction handle.
|
|
63
|
+
*
|
|
64
|
+
* We obtain the IDBDatabase from the first store (they all share the same
|
|
65
|
+
* ConnectionManager / database) and open the transaction ourselves so that
|
|
66
|
+
* the commit/abort lifecycle belongs entirely to this method.
|
|
67
|
+
*/
|
|
68
|
+
private commitIDB;
|
|
69
|
+
/**
|
|
70
|
+
* Commits MemoryStore groups sequentially.
|
|
71
|
+
* Maintains a list of stores that have already applied their ops; if any
|
|
72
|
+
* store throws, all previously-applied stores are rolled back via the
|
|
73
|
+
* store-level `_rollbackMemory(snapshot)` escape hatch.
|
|
74
|
+
*/
|
|
75
|
+
private commitMemory;
|
|
76
|
+
completed(): boolean;
|
|
14
77
|
}
|
|
15
78
|
|
|
16
79
|
declare const DEFAULT_KEYPATH = "$id";
|
|
@@ -30,8 +93,9 @@ interface CursorCallbackResult<T> {
|
|
|
30
93
|
* @template T - The type of records stored.
|
|
31
94
|
* @param value - The current record value (cloned, not a live reference).
|
|
32
95
|
* @param key - The key (ID) of the current record.
|
|
33
|
-
* @param cursor - The underlying cursor object (implementation
|
|
34
|
-
* @returns A promise
|
|
96
|
+
* @param cursor - The underlying cursor object (implementation-specific; may be `null` in memory adapters).
|
|
97
|
+
* @returns A promise resolving to an object indicating whether iteration should stop,
|
|
98
|
+
* and an optional offset to advance.
|
|
35
99
|
*/
|
|
36
100
|
type CursorCallback<T> = (value: T, key: string | number, cursor: any) => Promise<CursorCallbackResult<T>>;
|
|
37
101
|
/**
|
|
@@ -43,123 +107,100 @@ interface StoreKeyRange {
|
|
|
43
107
|
lowerOpen?: boolean;
|
|
44
108
|
upperOpen?: boolean;
|
|
45
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* A single buffered operation staged inside a TransactionContext.
|
|
112
|
+
* Kept intentionally minimal — the context only needs to know what to
|
|
113
|
+
* replay against a store during commit.
|
|
114
|
+
*/
|
|
115
|
+
type BufferedOperation<T> = {
|
|
116
|
+
type: "add" | "put";
|
|
117
|
+
data: T | T[];
|
|
118
|
+
} | {
|
|
119
|
+
type: "delete";
|
|
120
|
+
data: string | number | (string | number)[];
|
|
121
|
+
};
|
|
46
122
|
/**
|
|
47
123
|
* Storage adapter interface for a single object store (collection).
|
|
48
124
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
125
|
+
* Stores own their indexes. Index lifecycle (create, drop) and index-aware reads
|
|
126
|
+
* (findByIndex) are part of this contract so that both MemoryStore and IndexedDBStore
|
|
127
|
+
* implement them natively — MemoryStore via in-memory index maps, IndexedDB via its
|
|
128
|
+
* native index mechanism.
|
|
53
129
|
*
|
|
54
|
-
* @template T - The type of objects stored
|
|
130
|
+
* @template T - The type of objects stored. Must include the key path property.
|
|
55
131
|
*/
|
|
56
|
-
interface Store<T
|
|
132
|
+
interface Store<T extends Record<string, any> = Record<string, any>> {
|
|
133
|
+
/**
|
|
134
|
+
* Returns the name of this store (the IDB object store / collection name).
|
|
135
|
+
* Used by TransactionContext to group operations and open a correctly-scoped
|
|
136
|
+
* multi-store IDB transaction at commit time.
|
|
137
|
+
*/
|
|
138
|
+
name(): string;
|
|
139
|
+
/**
|
|
140
|
+
* Opens the store, ensuring underlying storage structures exist.
|
|
141
|
+
*/
|
|
142
|
+
open(): Promise<void>;
|
|
57
143
|
/**
|
|
58
144
|
* Adds one or more records to the store.
|
|
59
|
-
*
|
|
60
145
|
* If a record does not have a value for the store's key path, an automatic
|
|
61
|
-
* key
|
|
62
|
-
* property is then updated on the added record(s).
|
|
63
|
-
*
|
|
64
|
-
* @param data - A single record or an array of records to add.
|
|
65
|
-
* @returns A promise that resolves to:
|
|
66
|
-
* - the key(s) of the added record(s) – a single key if `data` was a single record,
|
|
67
|
-
* or an array of keys if `data` was an array.
|
|
68
|
-
* @throws {Error} If any record lacks the key path property and auto‑keying is not supported,
|
|
69
|
-
* or if a record with the same key already exists.
|
|
146
|
+
* key may be assigned. Throws if a record with the same key already exists.
|
|
70
147
|
*/
|
|
71
148
|
add(data: T | T[]): Promise<string | number | (string | number)[]>;
|
|
72
149
|
/**
|
|
73
|
-
* Removes all records from the store.
|
|
74
|
-
*
|
|
75
|
-
* @returns A promise that resolves when the store is cleared.
|
|
76
|
-
* @throws {Error} If the operation fails (e.g., store is closed).
|
|
150
|
+
* Removes all records from the store without destroying index structures.
|
|
77
151
|
*/
|
|
78
152
|
clear(): Promise<void>;
|
|
79
153
|
/**
|
|
80
154
|
* Returns the total number of records in the store.
|
|
81
|
-
*
|
|
82
|
-
* @returns A promise that resolves to the record count.
|
|
83
|
-
* @throws {Error} If the operation fails.
|
|
84
155
|
*/
|
|
85
156
|
count(): Promise<number>;
|
|
86
157
|
/**
|
|
87
158
|
* Deletes one or more records by their keys.
|
|
88
|
-
*
|
|
89
|
-
* @param id - A single key or an array of keys to delete.
|
|
90
|
-
* @returns A promise that resolves when the records are deleted.
|
|
91
|
-
* @throws {Error} If any key is `undefined` or the operation fails.
|
|
92
159
|
*/
|
|
93
160
|
delete(id: string | number | (string | number)[]): Promise<void>;
|
|
94
161
|
/**
|
|
95
162
|
* Retrieves a single record by its primary key.
|
|
96
|
-
*
|
|
97
|
-
* @param id - The key of the record to retrieve.
|
|
98
|
-
* @returns A promise that resolves to the record (cloned) if found, otherwise `undefined`.
|
|
99
|
-
* @throws {Error} If the key is `undefined` or the operation fails.
|
|
100
163
|
*/
|
|
101
164
|
getById(id: string | number): Promise<T | undefined>;
|
|
102
165
|
/**
|
|
103
|
-
* Retrieves
|
|
166
|
+
* Retrieves the first record matching an exact index key (point lookup).
|
|
167
|
+
* Useful for unique indexes — returns the single matching record or undefined.
|
|
104
168
|
*
|
|
105
|
-
* @param
|
|
106
|
-
* @param key - The exact key value to look up
|
|
107
|
-
* @returns A promise that resolves to the first matching record (cloned), or `undefined` if none.
|
|
108
|
-
* @throws {Error} If the index does not exist, the key is `undefined`, or the operation fails.
|
|
169
|
+
* @param indexName - The name of the index to query.
|
|
170
|
+
* @param key - The exact key value to look up.
|
|
109
171
|
*/
|
|
110
|
-
getByIndex(
|
|
172
|
+
getByIndex(indexName: string, key: any): Promise<T | undefined>;
|
|
111
173
|
/**
|
|
112
|
-
* Retrieves
|
|
174
|
+
* Retrieves all records from a named index, optionally filtered by a key range.
|
|
175
|
+
* Use this for range scans over an index (e.g. all records where age >= 18).
|
|
113
176
|
*
|
|
114
|
-
* @param
|
|
115
|
-
* @param keyRange - Optional
|
|
116
|
-
* @returns A promise that resolves to an array of matching records (each cloned).
|
|
117
|
-
* @throws {Error} If the index does not exist or the operation fails.
|
|
177
|
+
* @param indexName - The name of the index to query.
|
|
178
|
+
* @param keyRange - Optional range to filter results.
|
|
118
179
|
*/
|
|
119
|
-
getByKeyRange(
|
|
180
|
+
getByKeyRange(indexName: string, keyRange?: StoreKeyRange): Promise<T[]>;
|
|
120
181
|
/**
|
|
121
|
-
* Retrieves all records from the store.
|
|
122
|
-
*
|
|
123
|
-
* @returns A promise that resolves to an array of all records (each cloned).
|
|
124
|
-
* @throws {Error} If the operation fails.
|
|
182
|
+
* Retrieves all records from the store without index involvement.
|
|
125
183
|
*/
|
|
126
184
|
getAll(): Promise<T[]>;
|
|
127
185
|
/**
|
|
128
|
-
* Inserts or replaces a record.
|
|
129
|
-
*
|
|
130
|
-
* If a record with the same key already exists, it is replaced.
|
|
131
|
-
* The record must contain the store's key path property.
|
|
132
|
-
*
|
|
133
|
-
* @param data - The record to store.
|
|
134
|
-
* @returns A promise that resolves to the key of the stored record.
|
|
135
|
-
* @throws {Error} If the record lacks the key path property or the operation fails.
|
|
186
|
+
* Inserts or replaces a record. Validates OCC if a record with the same key exists.
|
|
136
187
|
*/
|
|
137
188
|
put(data: T): Promise<string | number>;
|
|
138
189
|
/**
|
|
139
190
|
* Iterates over records using a cursor, allowing early termination and skipping.
|
|
140
191
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* @param callback - Function called for each record.
|
|
146
|
-
* @param direction - Iteration direction: `"forward"` (ascending keys) or `"backward"` (descending keys).
|
|
147
|
-
* @param keyRange - An optional StoreKeyRange to start from specific points.
|
|
148
|
-
* @returns A promise that resolves to the last record processed (or `null` if none).
|
|
149
|
-
* @throws {Error} If the callback throws or the operation fails.
|
|
192
|
+
* @param callback - Invoked for each record; return `{ done: true }` to stop,
|
|
193
|
+
* `{ offset: n }` to skip ahead n records.
|
|
194
|
+
* @param direction - Iteration order.
|
|
195
|
+
* @param keyRange - Optional range to restrict iteration.
|
|
150
196
|
*/
|
|
151
197
|
cursor(callback: CursorCallback<T>, direction?: "forward" | "backward", keyRange?: StoreKeyRange): Promise<T | null>;
|
|
152
198
|
/**
|
|
153
|
-
* Executes a batch of write operations atomically.
|
|
154
|
-
*
|
|
155
|
-
* All operations in the batch succeed or fail together. This is useful for
|
|
156
|
-
* maintaining consistency when multiple writes are required.
|
|
199
|
+
* Executes a batch of write operations atomically within this store.
|
|
200
|
+
* All operations succeed or fail together.
|
|
157
201
|
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* - `{ type: "delete", data: string | number | (string | number)[] }`
|
|
161
|
-
* @returns A promise that resolves when the batch is committed.
|
|
162
|
-
* @throws {Error} If any operation fails or the batch cannot be completed.
|
|
202
|
+
* Used for standalone (single-store) atomic writes. For cross-store atomicity,
|
|
203
|
+
* use executeInTransaction instead.
|
|
163
204
|
*/
|
|
164
205
|
batch(operations: Array<{
|
|
165
206
|
type: "add" | "put";
|
|
@@ -168,44 +209,81 @@ interface Store<T = any> {
|
|
|
168
209
|
type: "delete";
|
|
169
210
|
data: string | number | (string | number)[];
|
|
170
211
|
}>): Promise<void>;
|
|
171
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Registers a new index on the store. Idempotent — no-op if the index already exists.
|
|
214
|
+
* For IndexedDB, this triggers a database version upgrade.
|
|
215
|
+
* For MemoryStore, this builds the index map from existing records.
|
|
216
|
+
*
|
|
217
|
+
* @param definition - The full index definition from the schema.
|
|
218
|
+
*/
|
|
219
|
+
createIndex(definition: IndexDefinition): Promise<void>;
|
|
220
|
+
/**
|
|
221
|
+
* Removes a named index from the store.
|
|
222
|
+
* For IndexedDB, this triggers a database version upgrade.
|
|
223
|
+
* For MemoryStore, this drops the in-memory index map.
|
|
224
|
+
*
|
|
225
|
+
* @param name - The index name as declared in IndexDefinition.name.
|
|
226
|
+
*/
|
|
227
|
+
dropIndex(name: string): Promise<void>;
|
|
228
|
+
/**
|
|
229
|
+
* Returns all records matching an exact index key.
|
|
230
|
+
* Unlike getByIndex (which returns only the first match), this returns all matches —
|
|
231
|
+
* essential for non-unique indexes where multiple records share the same indexed value.
|
|
232
|
+
*
|
|
233
|
+
* @param indexName - The name of the index to query.
|
|
234
|
+
* @param value - The exact value to look up.
|
|
235
|
+
*/
|
|
236
|
+
findByIndex(indexName: string, value: any): Promise<T[]>;
|
|
237
|
+
/**
|
|
238
|
+
* Executes a set of buffered operations as part of a cross-store atomic transaction.
|
|
239
|
+
*
|
|
240
|
+
* For IndexedDBStore: `sharedTx` is the single IDBTransaction opened across all
|
|
241
|
+
* participating stores. Operations are applied directly to `sharedTx.objectStore(name)`
|
|
242
|
+
* without opening a new transaction — IDB commits or aborts the whole thing atomically.
|
|
243
|
+
*
|
|
244
|
+
* For MemoryStore: `sharedTx` is null. The store applies ops against its own staging
|
|
245
|
+
* area. The caller (TransactionContext) is responsible for coordinating rollback across
|
|
246
|
+
* all MemoryStores if any participant fails.
|
|
247
|
+
*
|
|
248
|
+
* This method must NOT open, commit, or abort any transaction itself.
|
|
249
|
+
*
|
|
250
|
+
* @param ops - The buffered operations to apply.
|
|
251
|
+
* @param sharedTx - The shared IDBTransaction (IndexedDB only), or null (MemoryStore).
|
|
252
|
+
*/
|
|
253
|
+
executeInTransaction(ops: BufferedOperation<T>[], sharedTx: IDBTransaction | null): Promise<void>;
|
|
172
254
|
}
|
|
173
255
|
interface Collection<T> {
|
|
174
256
|
/**
|
|
175
257
|
* Finds a single document matching the query.
|
|
176
|
-
* @param query - The query to execute.
|
|
177
|
-
* @returns A promise resolving to the matching document or `null` if not found.
|
|
178
258
|
*/
|
|
179
259
|
find: (query: QueryFilter<T>) => Promise<Document<T> | null>;
|
|
180
260
|
/**
|
|
181
|
-
* Lists documents
|
|
182
|
-
*
|
|
183
|
-
* @returns A promise resolving to an array of documents.
|
|
261
|
+
* Lists documents with pagination. Returns an AsyncIterator so consumers can
|
|
262
|
+
* wrap it in their own iteration protocol (e.g. for-await-of via AsyncIterable).
|
|
184
263
|
*/
|
|
185
264
|
list: (query: PaginationOptions) => Promise<AsyncIterator<Document<T>[]>>;
|
|
186
265
|
/**
|
|
187
|
-
* Filters documents
|
|
188
|
-
* @param query - The query to filter documents.
|
|
189
|
-
* @returns A promise resolving to an array of matching documents.
|
|
266
|
+
* Filters all documents matching the query.
|
|
190
267
|
*/
|
|
191
268
|
filter: (query: QueryFilter<T>) => Promise<Document<T>[]>;
|
|
192
269
|
/**
|
|
193
|
-
* Creates a new document in the
|
|
270
|
+
* Creates a new document in the collection.
|
|
271
|
+
*
|
|
272
|
+
* When a TransactionContext is provided the initial store.add is buffered into
|
|
273
|
+
* the transaction rather than written immediately. The document is returned in
|
|
274
|
+
* its fully initialised in-memory state regardless — callers can use it before
|
|
275
|
+
* the transaction commits.
|
|
276
|
+
*
|
|
194
277
|
* @param initial - The initial data for the document.
|
|
195
|
-
* @
|
|
278
|
+
* @param tx - Optional transaction to buffer the write into.
|
|
196
279
|
*/
|
|
197
|
-
create: (initial: T) => Promise<Document<T>>;
|
|
280
|
+
create: (initial: T, tx?: TransactionContext) => Promise<Document<T>>;
|
|
198
281
|
/**
|
|
199
|
-
* Subscribes to
|
|
200
|
-
* @param event - The event type to subscribe to.
|
|
201
|
-
* @param callback - The function to call when the event occurs.
|
|
202
|
-
* @returns A promise resolving to an unsubscribe function.
|
|
282
|
+
* Subscribes to collection-level events.
|
|
203
283
|
*/
|
|
204
|
-
subscribe: (event: CollectionEventType | TelemetryEventType, callback: (event: CollectionEvent<T> | TelemetryEvent) => void) =>
|
|
284
|
+
subscribe: (event: CollectionEventType | TelemetryEventType, callback: (event: CollectionEvent<T> | TelemetryEvent) => void) => () => void;
|
|
205
285
|
/**
|
|
206
|
-
*
|
|
207
|
-
* @param data - The data to validate
|
|
208
|
-
* @returns An object containing validation results
|
|
286
|
+
* Validates data against the collection's schema.
|
|
209
287
|
*/
|
|
210
288
|
validate(data: Record<string, any>): Promise<{
|
|
211
289
|
value?: any;
|
|
@@ -214,9 +292,10 @@ interface Collection<T> {
|
|
|
214
292
|
path: Array<string>;
|
|
215
293
|
}>;
|
|
216
294
|
}>;
|
|
295
|
+
invalidate(): void;
|
|
217
296
|
}
|
|
218
297
|
/**
|
|
219
|
-
* Event payload for
|
|
298
|
+
* Event payload for Collection events.
|
|
220
299
|
*/
|
|
221
300
|
type CollectionEventType = "document:create" | "collection:read" | "migration:start" | "migration:end";
|
|
222
301
|
type CollectionEvent<T> = {
|
|
@@ -229,91 +308,49 @@ type CollectionEvent<T> = {
|
|
|
229
308
|
};
|
|
230
309
|
interface Database {
|
|
231
310
|
/**
|
|
232
|
-
*
|
|
233
|
-
* @param schemaName - The name of the schema to access.
|
|
234
|
-
* @returns A promise resolving to the schema's DocumentCursor.
|
|
235
|
-
* @throws DatabaseError
|
|
311
|
+
* Opens an existing collection by name.
|
|
236
312
|
*/
|
|
237
313
|
collection: <T>(schemaName: string) => Promise<Collection<T>>;
|
|
238
314
|
/**
|
|
239
|
-
* Creates a new schema
|
|
240
|
-
* @param schema - The schema definition.
|
|
241
|
-
* @returns A promise resolving to the created schema's DocumentCursor.
|
|
242
|
-
* @throws DatabaseError
|
|
315
|
+
* Creates a new collection from a schema definition.
|
|
243
316
|
*/
|
|
244
317
|
createCollection: <T>(schema: SchemaDefinition) => Promise<Collection<T>>;
|
|
245
318
|
/**
|
|
246
|
-
* Deletes a schema
|
|
247
|
-
* @param schemaName - The name of the schema to delete.
|
|
248
|
-
* @returns A promise resolving to `true` if successful, or `false` if an error occurs.
|
|
249
|
-
* @throws DatabaseError
|
|
319
|
+
* Deletes a collection and its schema record.
|
|
250
320
|
*/
|
|
251
321
|
deleteCollection: (schemaName: string) => Promise<boolean>;
|
|
252
322
|
/**
|
|
253
|
-
* Updates an existing schema.
|
|
254
|
-
* @param schema - The updated schema definition.
|
|
255
|
-
* @returns A promise resolving to `true` if successful, or `false` if an error occurs.
|
|
256
|
-
* @throws DatabaseError
|
|
323
|
+
* Updates an existing collection's schema record.
|
|
257
324
|
*/
|
|
258
325
|
updateCollection: (schema: SchemaDefinition) => Promise<boolean>;
|
|
259
326
|
/**
|
|
260
|
-
* Migrates an existing collection's data and
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
* It will:
|
|
265
|
-
* 1. Verify the target collection exists.
|
|
266
|
-
* 2. Retrieve the collection's current schema definition from a metadata store ($index).
|
|
267
|
-
* 3. Initialize a `MigrationEngine` with this schema.
|
|
268
|
-
* 4. Allow a callback to define specific data transformations using the `MigrationEngine`.
|
|
269
|
-
* 5. **Crucially, it uses `migrationEngine.dryRun()` to get the `newSchema` that results**
|
|
270
|
-
* **from the transformations defined in the callback.**
|
|
271
|
-
* 6. Execute these transformations by streaming data from the collection,
|
|
272
|
-
* through the `MigrationEngine`, and back into the same collection.
|
|
273
|
-
* 7. Finally, update the schema definition for the collection in the `$index` metadata store
|
|
274
|
-
* to reflect this `newSchema`.
|
|
275
|
-
* All these steps for data and metadata updates happen within a single atomic IndexedDB transaction.
|
|
276
|
-
*
|
|
277
|
-
* Note: This function focuses solely on *data transformation* and *metadata updates*.
|
|
278
|
-
* It does NOT handle structural IndexedDB changes like adding/removing physical indexes or object stores,
|
|
279
|
-
* which still require an `onupgradeneeded` event (i.e., a database version upgrade).
|
|
280
|
-
*
|
|
281
|
-
* @param name - The name of the collection (IndexedDB object store) to migrate.
|
|
282
|
-
* @param {Object} opts - Options for the new migration
|
|
283
|
-
* @param {SchemaChange<any>[]} opts.changes - Array of schema changes
|
|
284
|
-
* @param {string} opts.description - Description of the migration
|
|
285
|
-
* @param {SchemaChange<any>[]} [opts.rollback] - Optional rollback changes
|
|
286
|
-
* @param {DataTransform<any, any>} [opts.transform] - Optional data transform
|
|
287
|
-
* @returns A Promise resolving to `true` if the migration completes successfully,
|
|
288
|
-
* @throws {DatabaseError} If the collection does not exist, its schema metadata is missing,
|
|
289
|
-
* or any IndexedDB operation/streaming fails critically.
|
|
327
|
+
* Migrates an existing collection's data and schema definition.
|
|
328
|
+
* Processes data in a streaming fashion to avoid loading the full collection
|
|
329
|
+
* into memory.
|
|
290
330
|
*/
|
|
291
|
-
migrateCollection: (name: string, opts: CollectionMigrationOptions, batchSize?: number) => Promise<
|
|
331
|
+
migrateCollection: <T>(name: string, opts: CollectionMigrationOptions, batchSize?: number) => Promise<Collection<T>>;
|
|
292
332
|
/**
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* @returns A promise resolving to an unsubscribe function.
|
|
333
|
+
* Executes a callback within a TransactionContext.
|
|
334
|
+
* Writes buffered inside the callback are flushed atomically on commit.
|
|
335
|
+
* If the callback throws, the buffer is discarded (no writes are flushed).
|
|
297
336
|
*/
|
|
298
|
-
|
|
337
|
+
transaction: (callback: (tx: TransactionContext) => Promise<void>) => Promise<void>;
|
|
299
338
|
/**
|
|
300
|
-
*
|
|
339
|
+
* Subscribes to database-level events.
|
|
340
|
+
*/
|
|
341
|
+
subscribe: (event: DatabaseEventType | "telemetry", callback: (event: DatabaseEvent | TelemetryEvent) => void) => () => void;
|
|
342
|
+
/**
|
|
343
|
+
* Releases in-memory references and event bus subscriptions.
|
|
344
|
+
* Does not delete any persisted data.
|
|
301
345
|
*/
|
|
302
346
|
close: () => void;
|
|
347
|
+
clear: () => Promise<void>;
|
|
303
348
|
/**
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
* @param schema - The schema definition for the collection.
|
|
307
|
-
* @returns A promise that resolves when the collection exists.
|
|
308
|
-
* @throws {DatabaseError} If validation fails or an unexpected error occurs.
|
|
309
|
-
*/
|
|
349
|
+
* Ensures a collection exists; creates it if it doesn't. Idempotent.
|
|
350
|
+
*/
|
|
310
351
|
ensureCollection: (schema: SchemaDefinition) => Promise<void>;
|
|
311
352
|
/**
|
|
312
|
-
* Ensures multiple collections exist; creates any that don't.
|
|
313
|
-
* Idempotent – safe to call multiple times.
|
|
314
|
-
* @param schemas - An array of schema definitions.
|
|
315
|
-
* @returns A promise that resolves when all collections exist.
|
|
316
|
-
* @throws {DatabaseError} If any schema validation fails or an unexpected error occurs.
|
|
353
|
+
* Ensures multiple collections exist; creates any that don't. Idempotent.
|
|
317
354
|
*/
|
|
318
355
|
setupCollections: (schemas: SchemaDefinition[]) => Promise<void>;
|
|
319
356
|
}
|
|
@@ -323,9 +360,6 @@ type CollectionMigrationOptions = {
|
|
|
323
360
|
rollback?: SchemaChange<any>[];
|
|
324
361
|
transform?: string | DataTransform<any, any>;
|
|
325
362
|
};
|
|
326
|
-
/**
|
|
327
|
-
* Event payload for Database events.
|
|
328
|
-
*/
|
|
329
363
|
type DatabaseEventType = "collection:create" | "collection:delete" | "collection:update" | "collection:read" | "migrate";
|
|
330
364
|
type DatabaseEvent = {
|
|
331
365
|
type: DatabaseEventType;
|
|
@@ -335,60 +369,17 @@ type DatabaseEvent = {
|
|
|
335
369
|
type Document<T> = {
|
|
336
370
|
readonly [K in keyof T]: T[K];
|
|
337
371
|
} & {
|
|
338
|
-
/**
|
|
339
|
-
* Unique identifier for the document (assigned automatically if not provided).
|
|
340
|
-
*/
|
|
341
372
|
$id?: string;
|
|
342
|
-
/**
|
|
343
|
-
* Timestamp of document creation (ISO string or Date).
|
|
344
|
-
*/
|
|
345
373
|
$created?: string | Date;
|
|
346
|
-
/**
|
|
347
|
-
* Timestamp of last document update (ISO string or Date).
|
|
348
|
-
*/
|
|
349
374
|
$updated?: string | Date;
|
|
350
|
-
/**
|
|
351
|
-
* Version number incremented on each change (used for optimistic concurrency control).
|
|
352
|
-
*/
|
|
353
375
|
$version?: number;
|
|
354
|
-
/**
|
|
355
|
-
* Fetches the latest data from the database and updates the document instance.
|
|
356
|
-
* @returns A promise resolving to `true` if successful, or `false` if an error occurs.
|
|
357
|
-
*/
|
|
358
376
|
read: () => Promise<boolean>;
|
|
359
|
-
/**
|
|
360
|
-
* Saves the current document state to the database.
|
|
361
|
-
* Normally called automatically by `update()` or `delete()`, but can be used manually.
|
|
362
|
-
* @returns A promise resolving to `true` if successful, or `false` if an error occurs.
|
|
363
|
-
*/
|
|
364
377
|
save: (tx?: TransactionContext) => Promise<boolean>;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
* @param props - Partial object containing the fields to update.
|
|
368
|
-
* @returns A promise resolving to `true` if successful, or `false` if an error occurs.
|
|
369
|
-
*/
|
|
370
|
-
update: (props: Partial<T>) => Promise<boolean>;
|
|
371
|
-
/**
|
|
372
|
-
* Deletes the document from the database.
|
|
373
|
-
* @returns A promise resolving to `true` if successful, or `false` if an error occurs.
|
|
374
|
-
*/
|
|
375
|
-
delete: () => Promise<boolean>;
|
|
376
|
-
/**
|
|
377
|
-
* Subscribes to document events (e.g., "update", "delete", "access").
|
|
378
|
-
* @param event - The event type to subscribe to.
|
|
379
|
-
* @param callback - The function to call when the event occurs.
|
|
380
|
-
* @returns An unsubscribe function.
|
|
381
|
-
*/
|
|
378
|
+
update: (props: Partial<T>, tx?: TransactionContext) => Promise<boolean>;
|
|
379
|
+
delete: (tx?: TransactionContext) => Promise<boolean>;
|
|
382
380
|
subscribe: (event: DocumentEventType | TelemetryEventType, callback: (event: DocumentEvent<T> | TelemetryEvent) => void) => () => void;
|
|
383
|
-
/**
|
|
384
|
-
* Returns a plain object containing only the user-defined data (without system fields like $id, $version, etc.).
|
|
385
|
-
* @returns The current user data.
|
|
386
|
-
*/
|
|
387
381
|
state(): T;
|
|
388
382
|
};
|
|
389
|
-
/**
|
|
390
|
-
* Event payload for DocumentModel events.
|
|
391
|
-
*/
|
|
392
383
|
type DocumentEventType = "document:create" | "document:write" | "document:update" | "document:delete" | "document:read";
|
|
393
384
|
type DocumentEvent<T> = {
|
|
394
385
|
type: DocumentEventType;
|
|
@@ -412,7 +403,7 @@ type TelemetryEvent = {
|
|
|
412
403
|
document?: string;
|
|
413
404
|
};
|
|
414
405
|
result?: {
|
|
415
|
-
type:
|
|
406
|
+
type: "array" | string;
|
|
416
407
|
size?: number;
|
|
417
408
|
};
|
|
418
409
|
error: {
|
|
@@ -423,22 +414,88 @@ type TelemetryEvent = {
|
|
|
423
414
|
};
|
|
424
415
|
};
|
|
425
416
|
interface DatabaseConfig {
|
|
426
|
-
|
|
417
|
+
database: string;
|
|
427
418
|
keyPath?: string;
|
|
428
419
|
schemasStoreName?: string;
|
|
429
420
|
enableTelemetry?: boolean;
|
|
430
421
|
predicates?: PredicateMap;
|
|
431
422
|
validate?: boolean;
|
|
432
423
|
}
|
|
424
|
+
type StoreConfig = DatabaseConfig & {
|
|
425
|
+
collection: string;
|
|
426
|
+
};
|
|
433
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Internal structure for a single maintained index.
|
|
430
|
+
*/
|
|
431
|
+
interface IndexEntry {
|
|
432
|
+
definition: IndexDefinition;
|
|
433
|
+
/** Maps a composite/single index key → set of record primary keys ($id) */
|
|
434
|
+
map: Map<string, Set<string | number>>;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Snapshot of the store's mutable state, used for cross-store transaction rollback.
|
|
438
|
+
*/
|
|
439
|
+
interface MemoryStoreSnapshot {
|
|
440
|
+
data: Map<string | number, Readonly<Record<string, any>>>;
|
|
441
|
+
indexes: Map<string, IndexEntry>;
|
|
442
|
+
nextId: number;
|
|
443
|
+
}
|
|
434
444
|
declare class MemoryStore<T extends Record<string, any>> implements Store<T> {
|
|
445
|
+
private readonly storeName;
|
|
435
446
|
private readonly keyPath;
|
|
436
447
|
private data;
|
|
448
|
+
private indexes;
|
|
437
449
|
private nextId;
|
|
438
|
-
|
|
450
|
+
/**
|
|
451
|
+
* @param storeName - The logical name of this store (matches the collection name).
|
|
452
|
+
* @param keyPath - The primary key field name (default: "$id").
|
|
453
|
+
* @param indexDefs - Index definitions from the schema. The store will maintain
|
|
454
|
+
* these indexes on every write operation.
|
|
455
|
+
*/
|
|
456
|
+
constructor(storeName: string, keyPath?: string, indexDefs?: IndexDefinition[]);
|
|
457
|
+
name(): string;
|
|
439
458
|
open(): Promise<void>;
|
|
440
|
-
|
|
441
|
-
|
|
459
|
+
/**
|
|
460
|
+
* Returns a deep snapshot of the store's mutable state.
|
|
461
|
+
* Called by TransactionContext immediately before executeInTransaction so
|
|
462
|
+
* that if a later store in the same transaction fails, this store can be
|
|
463
|
+
* fully restored via _rollbackMemory.
|
|
464
|
+
*
|
|
465
|
+
* Prefixed with underscore to signal it is an internal contract between
|
|
466
|
+
* MemoryStore and TransactionContext — not part of the public Store API.
|
|
467
|
+
*/
|
|
468
|
+
_snapshotMemory(): MemoryStoreSnapshot;
|
|
469
|
+
/**
|
|
470
|
+
* Restores the store to a previously snapshotted state.
|
|
471
|
+
* Called by TransactionContext when a later participant in the same
|
|
472
|
+
* cross-store transaction fails, requiring all already-applied stores to
|
|
473
|
+
* be unwound.
|
|
474
|
+
*/
|
|
475
|
+
_rollbackMemory(snapshot: MemoryStoreSnapshot): void;
|
|
476
|
+
/**
|
|
477
|
+
* Registers a new index. Idempotent — no-op if the name already exists.
|
|
478
|
+
* Immediately indexes all existing records so the index is consistent.
|
|
479
|
+
*/
|
|
480
|
+
createIndex(definition: IndexDefinition): Promise<void>;
|
|
481
|
+
/**
|
|
482
|
+
* Removes a named index. No-op if the index does not exist.
|
|
483
|
+
*/
|
|
484
|
+
dropIndex(name: string): Promise<void>;
|
|
485
|
+
/**
|
|
486
|
+
* Returns the first record whose indexed value exactly matches `value`.
|
|
487
|
+
* Intended for unique indexes — for non-unique indexes use findByIndex.
|
|
488
|
+
*/
|
|
489
|
+
getByIndex(indexName: string, value: any): Promise<T | undefined>;
|
|
490
|
+
/**
|
|
491
|
+
* Returns all records whose indexed value exactly matches `value`.
|
|
492
|
+
* O(k) where k is the result set size — avoids a full table scan.
|
|
493
|
+
*/
|
|
494
|
+
findByIndex(indexName: string, value: any): Promise<T[]>;
|
|
495
|
+
/**
|
|
496
|
+
* Returns all records from a named index within an optional key range.
|
|
497
|
+
*/
|
|
498
|
+
getByKeyRange(indexName: string, keyRange?: StoreKeyRange): Promise<T[]>;
|
|
442
499
|
add(data: T | T[]): Promise<string | number | (string | number)[]>;
|
|
443
500
|
put(data: T): Promise<string | number>;
|
|
444
501
|
batch(operations: Array<{
|
|
@@ -448,15 +505,47 @@ declare class MemoryStore<T extends Record<string, any>> implements Store<T> {
|
|
|
448
505
|
type: "delete";
|
|
449
506
|
data: string | number | (string | number)[];
|
|
450
507
|
}>): Promise<void>;
|
|
451
|
-
getById(id: string | number): Promise<T | undefined>;
|
|
452
508
|
delete(id: string | number | (string | number)[]): Promise<void>;
|
|
453
509
|
clear(): Promise<void>;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
510
|
+
/**
|
|
511
|
+
* Applies buffered ops directly to the live data and index maps.
|
|
512
|
+
*
|
|
513
|
+
* `sharedTx` is always null for MemoryStore — there is no shared transaction
|
|
514
|
+
* object. Atomicity across multiple MemoryStores is managed by the caller
|
|
515
|
+
* (TransactionContext), which takes a snapshot via _snapshotMemory() before
|
|
516
|
+
* calling this method and calls _rollbackMemory(snapshot) if a later store
|
|
517
|
+
* in the same transaction fails.
|
|
518
|
+
*
|
|
519
|
+
* This method applies ops eagerly (no staging) because the snapshot already
|
|
520
|
+
* guards against partial failure at the cross-store level.
|
|
521
|
+
*/
|
|
522
|
+
executeInTransaction(ops: BufferedOperation<T>[], sharedTx: IDBTransaction | null): Promise<void>;
|
|
523
|
+
getById(id: string | number): Promise<T | undefined>;
|
|
458
524
|
getAll(): Promise<T[]>;
|
|
525
|
+
count(): Promise<number>;
|
|
459
526
|
cursor(callback: CursorCallback<T>, direction?: "forward" | "backward", keyRange?: StoreKeyRange): Promise<T | null>;
|
|
527
|
+
private getKey;
|
|
528
|
+
private clone;
|
|
529
|
+
/**
|
|
530
|
+
* Adds a document to all index maps. Skips indexes where the document
|
|
531
|
+
* does not satisfy partial conditions or is missing indexed fields.
|
|
532
|
+
*/
|
|
533
|
+
private indexDocument;
|
|
534
|
+
private indexOne;
|
|
535
|
+
/**
|
|
536
|
+
* Removes a document from all index maps.
|
|
537
|
+
*/
|
|
538
|
+
private unindexDocument;
|
|
539
|
+
/**
|
|
540
|
+
* Enforces unique index constraints before a write.
|
|
541
|
+
* Throws CONFLICT if another record (other than `existing`) already holds the
|
|
542
|
+
* same indexed value for any unique index.
|
|
543
|
+
*
|
|
544
|
+
* @param incoming - The record about to be written.
|
|
545
|
+
* @param existing - The record currently stored at this key (undefined for new records).
|
|
546
|
+
*/
|
|
547
|
+
private enforceUniqueIndexes;
|
|
548
|
+
private isInKeyRange;
|
|
460
549
|
}
|
|
461
550
|
|
|
462
551
|
declare function createEphemeralStore<T extends Record<string, any>>(config: DatabaseConfig): MemoryStore<T>;
|
|
@@ -469,77 +558,125 @@ declare class ConnectionManager {
|
|
|
469
558
|
private readonly config;
|
|
470
559
|
private connectionInitializer;
|
|
471
560
|
private readonly schemasStoreName;
|
|
561
|
+
constructor(config: DatabaseConfig);
|
|
562
|
+
private openDatabase;
|
|
472
563
|
/**
|
|
473
|
-
*
|
|
564
|
+
* Bumps the database version and runs the provided upgrade callback.
|
|
565
|
+
* The callback receives both the IDBDatabase and the active IDBTransaction
|
|
566
|
+
* so callers can access existing object stores via tx.objectStore(name).
|
|
474
567
|
*
|
|
475
|
-
*
|
|
568
|
+
* Note: IDB only allows structural changes (createObjectStore, createIndex,
|
|
569
|
+
* deleteIndex) inside an onupgradeneeded handler. This method is the single
|
|
570
|
+
* entry point for all such changes.
|
|
476
571
|
*/
|
|
477
|
-
|
|
572
|
+
private upgradeDatabase;
|
|
478
573
|
/**
|
|
479
|
-
*
|
|
574
|
+
* Ensures a collection object store exists, creating it (and its indexes) if absent.
|
|
575
|
+
* Triggers an upgrade only when the store does not yet exist; if it already exists,
|
|
576
|
+
* this is a fast no-op.
|
|
480
577
|
*
|
|
481
|
-
* @
|
|
482
|
-
* @
|
|
578
|
+
* @param collection - Name of the IDB object store.
|
|
579
|
+
* @param keyPath - Primary key field (default: "$id").
|
|
580
|
+
* @param indexes - Index definitions to create alongside the store.
|
|
581
|
+
* These are only applied during the initial store creation.
|
|
582
|
+
* Use createStoreIndex / dropStoreIndex for post-creation changes.
|
|
483
583
|
*/
|
|
484
|
-
|
|
584
|
+
ensureStore(collection: string, keyPath?: string, indexes?: IndexDefinition[]): Promise<void>;
|
|
485
585
|
/**
|
|
486
|
-
*
|
|
586
|
+
* Adds a named index to an existing object store.
|
|
587
|
+
* Triggers a database version upgrade.
|
|
487
588
|
*
|
|
488
|
-
* @param
|
|
489
|
-
* @param
|
|
490
|
-
* @returns A promise that resolves to the newly upgraded IDBDatabase instance.
|
|
491
|
-
* @throws {Error} If the database upgrade fails.
|
|
589
|
+
* @param collection - The object store to add the index to.
|
|
590
|
+
* @param definition - The index definition.
|
|
492
591
|
*/
|
|
493
|
-
|
|
592
|
+
createStoreIndex(collection: string, definition: IndexDefinition): Promise<void>;
|
|
494
593
|
/**
|
|
495
|
-
*
|
|
496
|
-
* Triggers
|
|
594
|
+
* Removes a named index from an existing object store.
|
|
595
|
+
* Triggers a database version upgrade.
|
|
497
596
|
*
|
|
498
|
-
* @param
|
|
499
|
-
* @param
|
|
500
|
-
* @returns A promise that resolves when the store is confirmed to exist.
|
|
597
|
+
* @param storeName - The object store to remove the index from.
|
|
598
|
+
* @param indexName - The name of the index to remove.
|
|
501
599
|
*/
|
|
502
|
-
|
|
600
|
+
dropStoreIndex(storeName: string, indexName: string): Promise<void>;
|
|
503
601
|
/**
|
|
504
602
|
* Retrieves or opens the active database connection.
|
|
505
|
-
* Handles connection loss and version changes automatically.
|
|
506
|
-
*
|
|
507
|
-
* @returns A promise resolving to the active IDBDatabase connection.
|
|
508
|
-
* @throws {DatabaseError} If the connection initialization fails.
|
|
509
603
|
*/
|
|
510
604
|
getConnection: () => Promise<IDBDatabase>;
|
|
511
605
|
/**
|
|
512
|
-
*
|
|
513
|
-
* Resets the internal initialization state so subsequent callers wait for the new version.
|
|
606
|
+
* Opens a readwrite IDBTransaction spanning the given store names.
|
|
514
607
|
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
608
|
+
* This is the entry point for all cross-store atomic writes. The returned
|
|
609
|
+
* transaction is NOT managed here — the caller (TransactionContext) owns the
|
|
610
|
+
* commit/abort lifecycle by wiring oncomplete / onerror / onabort handlers.
|
|
611
|
+
*
|
|
612
|
+
* All named stores must already exist (i.e. ensureStore must have been called
|
|
613
|
+
* for each before this point). Requesting a store that doesn't exist will cause
|
|
614
|
+
* IDB to throw a DOMException synchronously when the transaction is opened.
|
|
615
|
+
*
|
|
616
|
+
* @param storeNames - Object store names to include in the transaction.
|
|
617
|
+
* @param mode - IDB transaction mode (default: "readwrite").
|
|
518
618
|
*/
|
|
519
|
-
|
|
619
|
+
openTransaction(storeNames: string[], mode?: IDBTransactionMode): Promise<IDBTransaction>;
|
|
520
620
|
/**
|
|
521
|
-
*
|
|
621
|
+
* Performs a version upgrade. Resets the internal initialiser so concurrent
|
|
622
|
+
* callers wait for the upgraded connection rather than using the stale one.
|
|
623
|
+
*
|
|
624
|
+
* The callback receives both `db` (for creating new stores) and `tx`
|
|
625
|
+
* (for accessing existing stores to add/remove indexes).
|
|
626
|
+
*/
|
|
627
|
+
upgrade(upgradeLogic: (db: IDBDatabase, tx: IDBTransaction) => void): Promise<IDBDatabase>;
|
|
628
|
+
/**
|
|
629
|
+
* Closes the active connection and resets the initialiser.
|
|
630
|
+
* Does not delete any persisted data.
|
|
522
631
|
*/
|
|
523
632
|
close(): void;
|
|
524
633
|
}
|
|
525
634
|
|
|
526
635
|
declare class IndexedDBStore<T extends Record<string, any>> implements Store<T> {
|
|
527
|
-
private readonly
|
|
528
|
-
private readonly
|
|
636
|
+
private readonly connectionManager;
|
|
637
|
+
private readonly collection;
|
|
529
638
|
private readonly keyPath;
|
|
530
|
-
private readonly
|
|
531
|
-
constructor(
|
|
639
|
+
private readonly indexes;
|
|
640
|
+
constructor(connectionManager: ConnectionManager, collection: string, keyPath?: string, indexes?: IndexDefinition[]);
|
|
641
|
+
name(): string;
|
|
642
|
+
/**
|
|
643
|
+
* Internal escape hatch used by TransactionContext to obtain the shared
|
|
644
|
+
* IDBDatabase so it can open a single multi-store IDBTransaction.
|
|
645
|
+
*
|
|
646
|
+
* Prefixed with underscore to signal that nothing outside of
|
|
647
|
+
* TransactionContext should call this directly.
|
|
648
|
+
*/
|
|
649
|
+
_getIDBConnection(): Promise<IDBDatabase>;
|
|
650
|
+
/**
|
|
651
|
+
* Ensures the underlying IDB object store (and its declared indexes) exist.
|
|
652
|
+
* Safe to call multiple times — no-op if the store is already present.
|
|
653
|
+
*/
|
|
532
654
|
open(): Promise<void>;
|
|
533
655
|
/**
|
|
534
|
-
*
|
|
656
|
+
* Adds a new index to the IDB object store. Triggers a database version upgrade.
|
|
657
|
+
* Idempotent — no-op if the index already exists.
|
|
535
658
|
*/
|
|
536
|
-
|
|
659
|
+
createIndex(definition: IndexDefinition): Promise<void>;
|
|
537
660
|
/**
|
|
538
|
-
*
|
|
539
|
-
*
|
|
661
|
+
* Removes a named index from the IDB object store. Triggers a version upgrade.
|
|
662
|
+
* No-op if the index does not exist.
|
|
540
663
|
*/
|
|
541
|
-
|
|
542
|
-
|
|
664
|
+
dropIndex(name: string): Promise<void>;
|
|
665
|
+
/**
|
|
666
|
+
* Returns the first record matching an exact index key.
|
|
667
|
+
* Use for unique index point lookups.
|
|
668
|
+
*/
|
|
669
|
+
getByIndex(indexName: string, key: any): Promise<T | undefined>;
|
|
670
|
+
/**
|
|
671
|
+
* Returns all records from a named index within an optional key range.
|
|
672
|
+
*/
|
|
673
|
+
getByKeyRange(indexName: string, keyRange?: StoreKeyRange): Promise<T[]>;
|
|
674
|
+
/**
|
|
675
|
+
* Returns all records whose indexed value exactly matches `value`.
|
|
676
|
+
* Unlike getByIndex, this uses index.getAll(IDBKeyRange.only(value)) so it
|
|
677
|
+
* correctly returns multiple records for non-unique indexes.
|
|
678
|
+
*/
|
|
679
|
+
findByIndex(indexName: string, value: any): Promise<T[]>;
|
|
543
680
|
put(data: T): Promise<string | number>;
|
|
544
681
|
add(data: T | T[]): Promise<string | number | (string | number)[]>;
|
|
545
682
|
batch(operations: Array<{
|
|
@@ -549,29 +686,98 @@ declare class IndexedDBStore<T extends Record<string, any>> implements Store<T>
|
|
|
549
686
|
type: "delete";
|
|
550
687
|
data: string | number | (string | number)[];
|
|
551
688
|
}>): Promise<void>;
|
|
552
|
-
getById(id: string | number): Promise<T | undefined>;
|
|
553
689
|
delete(id: string | number | (string | number)[]): Promise<void>;
|
|
554
690
|
clear(): Promise<void>;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
691
|
+
/**
|
|
692
|
+
* Executes buffered ops against a shared IDBTransaction opened by
|
|
693
|
+
* TransactionContext. This method MUST NOT open, commit, or abort a
|
|
694
|
+
* transaction — the caller owns the transaction lifecycle entirely.
|
|
695
|
+
*
|
|
696
|
+
* @param ops - Operations to apply.
|
|
697
|
+
* @param sharedTx - The IDBTransaction shared across all participating stores.
|
|
698
|
+
* Never null for IndexedDBStore.
|
|
699
|
+
*/
|
|
700
|
+
executeInTransaction(ops: BufferedOperation<T>[], sharedTx: IDBTransaction | null): Promise<void>;
|
|
701
|
+
getById(id: string | number): Promise<T | undefined>;
|
|
558
702
|
getAll(): Promise<T[]>;
|
|
703
|
+
count(): Promise<number>;
|
|
559
704
|
cursor(callback: CursorCallback<T>, direction?: "forward" | "backward", keyRange?: StoreKeyRange): Promise<T | null>;
|
|
705
|
+
private mapError;
|
|
706
|
+
/**
|
|
707
|
+
* Opens a fresh single-store IDB transaction for standalone (non-atomic)
|
|
708
|
+
* operations. Not used by executeInTransaction — that receives an externally
|
|
709
|
+
* managed shared transaction.
|
|
710
|
+
*/
|
|
711
|
+
private withTx;
|
|
712
|
+
private requestToPromise;
|
|
560
713
|
}
|
|
561
714
|
|
|
562
|
-
|
|
715
|
+
/**
|
|
716
|
+
* Retrieves or creates an IndexedDB store instance.
|
|
717
|
+
* * This function utilizes a synchronous execution path to retrieve the
|
|
718
|
+
* ConnectionManager. If the connection is currently being initialized
|
|
719
|
+
* asynchronously, or if the lock is contended, it throws a DatabaseError.
|
|
720
|
+
*
|
|
721
|
+
* @template T - The schema type for the collection.
|
|
722
|
+
* @param config - The database and collection configuration.
|
|
723
|
+
* @returns A functional Store instance.
|
|
724
|
+
* @throws {DatabaseError} CONNECTION_FAILED if the manager is busy or fails to init.
|
|
725
|
+
*/
|
|
726
|
+
declare const createIndexedDbStore: <T extends Record<string, any>>(config: StoreConfig) => Store<T>;
|
|
563
727
|
|
|
564
|
-
declare function DatabaseConnection(config: Omit<DatabaseConfig, "keyPath">, createStore: <T extends Record<string, any>>(config:
|
|
728
|
+
declare function DatabaseConnection(config: Omit<DatabaseConfig, "keyPath">, createStore: <T extends Record<string, any>>(config: StoreConfig, indexes: IndexDefinition[]) => Store<T>): Promise<Database>;
|
|
565
729
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
730
|
+
/**
|
|
731
|
+
* Interface defining the shape of the EventBus.
|
|
732
|
+
* @template TEventMap - A record mapping event names to their respective payload types.
|
|
733
|
+
*/
|
|
734
|
+
interface EventBus<TEventMap extends Record<string, any>> {
|
|
735
|
+
/**
|
|
736
|
+
* Subscribes to a specific event by name.
|
|
737
|
+
* @param eventName - The name of the event to subscribe to.
|
|
738
|
+
* @param callback - The function to call when the event is emitted.
|
|
739
|
+
* @returns A function to unsubscribe from the event.
|
|
740
|
+
*/
|
|
741
|
+
subscribe: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void) => () => void;
|
|
742
|
+
/**
|
|
743
|
+
* Subscribes to an event and automatically unsubscribes after it fires once.
|
|
744
|
+
* @param eventName - The name of the event to subscribe to.
|
|
745
|
+
* @param callback - The function to call when the event is emitted.
|
|
746
|
+
* @returns A function to cancel the one-shot subscription before it fires.
|
|
747
|
+
*/
|
|
748
|
+
once: <TEventName extends keyof TEventMap>(eventName: TEventName, callback: (payload: TEventMap[TEventName]) => void) => () => void;
|
|
749
|
+
/**
|
|
750
|
+
* Emits an event with a payload to all subscribed listeners.
|
|
751
|
+
* @param event - An object containing the event name and payload.
|
|
752
|
+
*/
|
|
753
|
+
emit: <TEventName extends keyof TEventMap>(event: {
|
|
754
|
+
name: TEventName;
|
|
755
|
+
payload: TEventMap[TEventName];
|
|
756
|
+
}) => void;
|
|
757
|
+
/**
|
|
758
|
+
* Retrieves metrics about event bus usage.
|
|
759
|
+
* @returns An object containing various metrics.
|
|
760
|
+
*/
|
|
761
|
+
metrics: () => EventMetrics;
|
|
762
|
+
/**
|
|
763
|
+
* Clears all subscriptions and resets metrics.
|
|
764
|
+
* After calling clear(), the bus is fully reset and can be reused —
|
|
765
|
+
* cross-tab communication is re-established if it was previously enabled.
|
|
766
|
+
*/
|
|
767
|
+
clear: () => void;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Interface defining the metrics tracked by the EventBus.
|
|
771
|
+
*/
|
|
772
|
+
interface EventMetrics {
|
|
773
|
+
/** Total number of events emitted (both sync and deferred paths). */
|
|
774
|
+
totalEvents: number;
|
|
775
|
+
/** Number of active subscriptions across all event names. */
|
|
776
|
+
activeSubscriptions: number;
|
|
777
|
+
/** Map of event names to their emission counts. */
|
|
778
|
+
eventCounts: Map<string, number>;
|
|
779
|
+
/** Average duration of event dispatch in milliseconds. */
|
|
780
|
+
averageEmitDuration: number;
|
|
575
781
|
}
|
|
576
782
|
|
|
577
783
|
interface MiddlewareContext {
|
|
@@ -599,6 +805,11 @@ declare class Mutex {
|
|
|
599
805
|
}
|
|
600
806
|
|
|
601
807
|
interface DocumentOptions<T extends Record<string, any>> {
|
|
808
|
+
/**
|
|
809
|
+
* The already-persisted initial state of the document.
|
|
810
|
+
* createDocument does NOT call store.add — the caller is responsible
|
|
811
|
+
* for having written this record before constructing the document proxy.
|
|
812
|
+
*/
|
|
602
813
|
initial: Partial<T>;
|
|
603
814
|
collection: string;
|
|
604
815
|
validator?: StandardSchemaV1;
|
|
@@ -606,8 +817,18 @@ interface DocumentOptions<T extends Record<string, any>> {
|
|
|
606
817
|
bus: EventBus<Record<DocumentEventType | TelemetryEventType, DocumentEvent<T> | TelemetryEvent>>;
|
|
607
818
|
pipeline: Pipeline;
|
|
608
819
|
lockManager: Mutex;
|
|
609
|
-
indexManager: IndexManager<T>;
|
|
610
820
|
}
|
|
821
|
+
/**
|
|
822
|
+
* Constructs an in-memory Document proxy around an already-persisted record.
|
|
823
|
+
*
|
|
824
|
+
* Responsibility split:
|
|
825
|
+
* - createDocument: validates, initialises in-memory state, wires operations. NO I/O.
|
|
826
|
+
* - openCollection.create: owns the initial store.add (or tx.addOp for transactional creates).
|
|
827
|
+
*
|
|
828
|
+
* This separation ensures that transactional creates are correctly buffered:
|
|
829
|
+
* the document object is available immediately in-memory, while the actual
|
|
830
|
+
* store write is deferred until transaction commit.
|
|
831
|
+
*/
|
|
611
832
|
declare function createDocument<T extends Record<string, any>>(opts: DocumentOptions<T>): Promise<Document<T & {
|
|
612
833
|
$id: string | number;
|
|
613
834
|
}>>;
|
|
@@ -620,4 +841,51 @@ declare function openCollection<T extends Record<string, any>>({ collection: sch
|
|
|
620
841
|
validate: boolean;
|
|
621
842
|
}): Promise<Collection<T>>;
|
|
622
843
|
|
|
623
|
-
|
|
844
|
+
/**
|
|
845
|
+
* Error types for Database operations.
|
|
846
|
+
*/
|
|
847
|
+
declare enum DatabaseErrorType {
|
|
848
|
+
/** The schema does not exist. */
|
|
849
|
+
SCHEMA_NOT_FOUND = "SCHEMA_NOT_FOUND",
|
|
850
|
+
/** The schema already exists. */
|
|
851
|
+
SCHEMA_ALREADY_EXISTS = "SCHEMA_ALREADY_EXISTS",
|
|
852
|
+
/** The schema name is invalid. */
|
|
853
|
+
INVALID_SCHEMA_NAME = "INVALID_SCHEMA_NAME",
|
|
854
|
+
/** The schema definition is invalid. */
|
|
855
|
+
INVALID_SCHEMA_DEFINITION = "INVALID_SCHEMA_DEFINITION",
|
|
856
|
+
/** The database subscription failed. */
|
|
857
|
+
SUBSCRIPTION_FAILED = "SUBSCRIPTION_FAILED",
|
|
858
|
+
/** The database operation failed due to an internal error. */
|
|
859
|
+
INTERNAL_ERROR = "INTERNAL_ERROR",
|
|
860
|
+
/** Data being entered into a collection does not satisfy its schema definition. */
|
|
861
|
+
INVALID_DATA = "INVALID_DATA",
|
|
862
|
+
/** Optimistic Concurrency Control version mismatches */
|
|
863
|
+
CONFLICT = "CONFLICT",
|
|
864
|
+
/** For retryable store locks */
|
|
865
|
+
TRANSIENT_ERROR = "TRANSIENT_ERROR",
|
|
866
|
+
TRANSACTION_FAILED = "TRANSACTION_FAILED",
|
|
867
|
+
CONNECTION_FAILED = "CONNECTION_FAILED",
|
|
868
|
+
INVALID_OPERATION = "INVALID_OPERATION"
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Error class for Database operations.
|
|
872
|
+
*/
|
|
873
|
+
declare class DatabaseError extends Error {
|
|
874
|
+
/**
|
|
875
|
+
* The type of error that occurred.
|
|
876
|
+
*/
|
|
877
|
+
type: DatabaseErrorType;
|
|
878
|
+
/**
|
|
879
|
+
* The schema associated with the error, if applicable.
|
|
880
|
+
*/
|
|
881
|
+
schema?: SchemaDefinition;
|
|
882
|
+
/**
|
|
883
|
+
* Constructs a new DatabaseErrorClass instance.
|
|
884
|
+
* @param type - The type of error that occurred.
|
|
885
|
+
* @param message - A human-readable message describing the error.
|
|
886
|
+
* @param schema - The schema associated with the error, if applicable.
|
|
887
|
+
*/
|
|
888
|
+
constructor(type: DatabaseErrorType, message: string, schema?: SchemaDefinition, cause?: unknown);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
export { type BufferedOperation, type Collection, type CollectionEvent, type CollectionEventType, type CollectionMigrationOptions, ConnectionManager, type CursorCallback, type CursorCallbackResult, type CursorPaginationOptions, DEFAULT_KEYPATH, type Database, type DatabaseConfig, DatabaseConnection, DatabaseError, DatabaseErrorType, type DatabaseEvent, type DatabaseEventType, type Document, type DocumentEvent, type DocumentEventType, IndexedDBStore, type Store, type StoreConfig, type StoreKeyRange, type TelemetryEvent, type TelemetryEventType, createDocument, createEphemeralStore, createIndexedDbStore, openCollection };
|