@firtoz/drizzle-indexeddb 0.2.0 → 0.3.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/CHANGELOG.md +32 -26
- package/README.md +112 -51
- package/package.json +13 -5
- package/src/bin/generate-migrations.ts +288 -0
- package/src/collections/indexeddb-collection.ts +95 -243
- package/src/context/useDrizzleIndexedDB.ts +2 -1
- package/src/function-migrator.ts +190 -170
- package/src/index.ts +16 -7
- package/src/utils.ts +517 -7
- package/src/snapshot-migrator.ts +0 -420
package/src/utils.ts
CHANGED
|
@@ -1,15 +1,525 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Minimal IDB Interface - High-Level Async API
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// These interfaces define a simple, high-level async API for IndexedDB operations.
|
|
5
|
+
// This makes it easy to:
|
|
6
|
+
// - Create mock implementations for testing
|
|
7
|
+
// - Implement alternative backends (e.g., Chrome extension message-based IDB)
|
|
8
|
+
// - Use with any async storage that supports similar operations
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
|
-
*
|
|
12
|
+
* Index information returned by getStoreIndexes
|
|
13
|
+
*/
|
|
14
|
+
export interface IndexInfo {
|
|
15
|
+
name: string;
|
|
16
|
+
keyPath: string | string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Options for creating an object store
|
|
3
21
|
*/
|
|
4
|
-
export
|
|
22
|
+
export interface CreateStoreOptions {
|
|
23
|
+
keyPath?: string;
|
|
24
|
+
autoIncrement?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Options for creating an index
|
|
29
|
+
*/
|
|
30
|
+
export interface CreateIndexOptions {
|
|
31
|
+
unique?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Key range specification for index queries
|
|
36
|
+
*/
|
|
37
|
+
export interface KeyRangeSpec {
|
|
38
|
+
type: "only" | "lowerBound" | "upperBound" | "bound";
|
|
39
|
+
value?: unknown;
|
|
40
|
+
lower?: unknown;
|
|
41
|
+
upper?: unknown;
|
|
42
|
+
lowerOpen?: boolean;
|
|
43
|
+
upperOpen?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Minimal database interface with high-level async operations.
|
|
48
|
+
* This is the interface that custom implementations (mocks, Chrome extension proxies, etc.) need to implement.
|
|
49
|
+
*
|
|
50
|
+
* All operations are simple async functions - no transactions, requests, or callbacks to deal with.
|
|
51
|
+
*/
|
|
52
|
+
export interface IDBDatabaseLike {
|
|
53
|
+
/** Database version number */
|
|
54
|
+
readonly version: number;
|
|
55
|
+
|
|
56
|
+
// =========================================================================
|
|
57
|
+
// Schema Operations (for migrations)
|
|
58
|
+
// =========================================================================
|
|
59
|
+
|
|
60
|
+
/** Check if a store exists */
|
|
61
|
+
hasStore(storeName: string): boolean;
|
|
62
|
+
|
|
63
|
+
/** Get list of all store names */
|
|
64
|
+
getStoreNames(): string[];
|
|
65
|
+
|
|
66
|
+
/** Create an object store (only valid during migrations) */
|
|
67
|
+
createStore(storeName: string, options?: CreateStoreOptions): void;
|
|
68
|
+
|
|
69
|
+
/** Delete an object store (only valid during migrations) */
|
|
70
|
+
deleteStore(storeName: string): void;
|
|
71
|
+
|
|
72
|
+
/** Create an index on a store (only valid during migrations) */
|
|
73
|
+
createIndex(
|
|
74
|
+
storeName: string,
|
|
75
|
+
indexName: string,
|
|
76
|
+
keyPath: string | string[],
|
|
77
|
+
options?: CreateIndexOptions,
|
|
78
|
+
): void;
|
|
79
|
+
|
|
80
|
+
/** Delete an index from a store (only valid during migrations) */
|
|
81
|
+
deleteIndex(storeName: string, indexName: string): void;
|
|
82
|
+
|
|
83
|
+
/** Get all indexes for a store (for index discovery) */
|
|
84
|
+
getStoreIndexes(storeName: string): IndexInfo[];
|
|
85
|
+
|
|
86
|
+
// =========================================================================
|
|
87
|
+
// Data Operations (all async, handle transactions internally)
|
|
88
|
+
// =========================================================================
|
|
89
|
+
|
|
90
|
+
/** Get all items from a store */
|
|
91
|
+
getAll<T = unknown>(storeName: string): Promise<T[]>;
|
|
92
|
+
|
|
93
|
+
/** Get items from a store using an index with optional key range */
|
|
94
|
+
getAllByIndex<T = unknown>(
|
|
95
|
+
storeName: string,
|
|
96
|
+
indexName: string,
|
|
97
|
+
keyRange?: KeyRangeSpec,
|
|
98
|
+
): Promise<T[]>;
|
|
99
|
+
|
|
100
|
+
/** Get a single item by key */
|
|
101
|
+
get<T = unknown>(storeName: string, key: IDBValidKey): Promise<T | undefined>;
|
|
102
|
+
|
|
103
|
+
/** Add items to a store (batch operation) */
|
|
104
|
+
add(storeName: string, items: unknown[]): Promise<void>;
|
|
105
|
+
|
|
106
|
+
/** Update items in a store (batch operation, uses put) */
|
|
107
|
+
put(storeName: string, items: unknown[]): Promise<void>;
|
|
108
|
+
|
|
109
|
+
/** Delete items from a store by keys (batch operation) */
|
|
110
|
+
delete(storeName: string, keys: IDBValidKey[]): Promise<void>;
|
|
111
|
+
|
|
112
|
+
/** Clear all items from a store */
|
|
113
|
+
clear(storeName: string): Promise<void>;
|
|
114
|
+
|
|
115
|
+
// =========================================================================
|
|
116
|
+
// Lifecycle
|
|
117
|
+
// =========================================================================
|
|
118
|
+
|
|
119
|
+
/** Close the database connection */
|
|
120
|
+
close(): void;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Default Implementation (wraps native IndexedDB)
|
|
125
|
+
// ============================================================================
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Creates a KeyRange from a KeyRangeSpec
|
|
129
|
+
*/
|
|
130
|
+
function createKeyRange(spec: KeyRangeSpec): IDBKeyRange {
|
|
131
|
+
switch (spec.type) {
|
|
132
|
+
case "only":
|
|
133
|
+
return IDBKeyRange.only(spec.value);
|
|
134
|
+
case "lowerBound":
|
|
135
|
+
return IDBKeyRange.lowerBound(spec.lower, spec.lowerOpen);
|
|
136
|
+
case "upperBound":
|
|
137
|
+
return IDBKeyRange.upperBound(spec.upper, spec.upperOpen);
|
|
138
|
+
case "bound":
|
|
139
|
+
return IDBKeyRange.bound(
|
|
140
|
+
spec.lower,
|
|
141
|
+
spec.upper,
|
|
142
|
+
spec.lowerOpen,
|
|
143
|
+
spec.upperOpen,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Default implementation that wraps native IndexedDB
|
|
150
|
+
*/
|
|
151
|
+
class NativeIDBDatabase implements IDBDatabaseLike {
|
|
152
|
+
constructor(private db: IDBDatabase) {
|
|
153
|
+
// Listen for version change events - close connection when another tab/process
|
|
154
|
+
// wants to upgrade the database. This prevents blocking issues.
|
|
155
|
+
this.db.onversionchange = () => {
|
|
156
|
+
this.db.close();
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
get version(): number {
|
|
161
|
+
return this.db.version;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
hasStore(storeName: string): boolean {
|
|
165
|
+
return this.db.objectStoreNames.contains(storeName);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getStoreNames(): string[] {
|
|
169
|
+
return Array.from(this.db.objectStoreNames);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
createStore(storeName: string, options?: CreateStoreOptions): void {
|
|
173
|
+
this.db.createObjectStore(storeName, options);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
deleteStore(storeName: string): void {
|
|
177
|
+
this.db.deleteObjectStore(storeName);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
createIndex(
|
|
181
|
+
storeName: string,
|
|
182
|
+
indexName: string,
|
|
183
|
+
keyPath: string | string[],
|
|
184
|
+
options?: CreateIndexOptions,
|
|
185
|
+
): void {
|
|
186
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
187
|
+
const store = transaction.objectStore(storeName);
|
|
188
|
+
store.createIndex(indexName, keyPath, options);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
deleteIndex(storeName: string, indexName: string): void {
|
|
192
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
193
|
+
const store = transaction.objectStore(storeName);
|
|
194
|
+
store.deleteIndex(indexName);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
getStoreIndexes(storeName: string): IndexInfo[] {
|
|
198
|
+
if (!this.hasStore(storeName)) {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
203
|
+
const store = transaction.objectStore(storeName);
|
|
204
|
+
const indexes: IndexInfo[] = [];
|
|
205
|
+
|
|
206
|
+
for (const indexName of Array.from(store.indexNames)) {
|
|
207
|
+
const index = store.index(indexName);
|
|
208
|
+
indexes.push({
|
|
209
|
+
name: indexName,
|
|
210
|
+
keyPath: index.keyPath,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return indexes;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async getAll<T = unknown>(storeName: string): Promise<T[]> {
|
|
218
|
+
if (!this.hasStore(storeName)) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return new Promise((resolve, reject) => {
|
|
223
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
224
|
+
const store = transaction.objectStore(storeName);
|
|
225
|
+
const request = store.getAll();
|
|
226
|
+
|
|
227
|
+
request.onsuccess = () => resolve(request.result as T[]);
|
|
228
|
+
request.onerror = () => reject(request.error);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async getAllByIndex<T = unknown>(
|
|
233
|
+
storeName: string,
|
|
234
|
+
indexName: string,
|
|
235
|
+
keyRange?: KeyRangeSpec,
|
|
236
|
+
): Promise<T[]> {
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
239
|
+
const store = transaction.objectStore(storeName);
|
|
240
|
+
const index = store.index(indexName);
|
|
241
|
+
const range = keyRange ? createKeyRange(keyRange) : undefined;
|
|
242
|
+
const request = index.getAll(range);
|
|
243
|
+
|
|
244
|
+
request.onsuccess = () => resolve(request.result as T[]);
|
|
245
|
+
request.onerror = () => reject(request.error);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async get<T = unknown>(
|
|
250
|
+
storeName: string,
|
|
251
|
+
key: IDBValidKey,
|
|
252
|
+
): Promise<T | undefined> {
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
255
|
+
const store = transaction.objectStore(storeName);
|
|
256
|
+
const request = store.get(key);
|
|
257
|
+
|
|
258
|
+
request.onsuccess = () => resolve(request.result as T | undefined);
|
|
259
|
+
request.onerror = () => reject(request.error);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async add(storeName: string, items: unknown[]): Promise<void> {
|
|
264
|
+
return new Promise((resolve, reject) => {
|
|
265
|
+
const transaction = this.db.transaction(storeName, "readwrite");
|
|
266
|
+
const store = transaction.objectStore(storeName);
|
|
267
|
+
|
|
268
|
+
for (const item of items) {
|
|
269
|
+
store.add(item);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
transaction.oncomplete = () => resolve();
|
|
273
|
+
transaction.onerror = () => reject(transaction.error);
|
|
274
|
+
transaction.onabort = () => reject(new Error("Transaction aborted"));
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async put(storeName: string, items: unknown[]): Promise<void> {
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
const transaction = this.db.transaction(storeName, "readwrite");
|
|
281
|
+
const store = transaction.objectStore(storeName);
|
|
282
|
+
|
|
283
|
+
for (const item of items) {
|
|
284
|
+
store.put(item);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
transaction.oncomplete = () => resolve();
|
|
288
|
+
transaction.onerror = () => reject(transaction.error);
|
|
289
|
+
transaction.onabort = () => reject(new Error("Transaction aborted"));
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async delete(storeName: string, keys: IDBValidKey[]): Promise<void> {
|
|
294
|
+
return new Promise((resolve, reject) => {
|
|
295
|
+
const transaction = this.db.transaction(storeName, "readwrite");
|
|
296
|
+
const store = transaction.objectStore(storeName);
|
|
297
|
+
|
|
298
|
+
for (const key of keys) {
|
|
299
|
+
store.delete(key);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
transaction.oncomplete = () => resolve();
|
|
303
|
+
transaction.onerror = () => reject(transaction.error);
|
|
304
|
+
transaction.onabort = () => reject(new Error("Transaction aborted"));
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async clear(storeName: string): Promise<void> {
|
|
309
|
+
return new Promise((resolve, reject) => {
|
|
310
|
+
const transaction = this.db.transaction(storeName, "readwrite");
|
|
311
|
+
const store = transaction.objectStore(storeName);
|
|
312
|
+
const request = store.clear();
|
|
313
|
+
|
|
314
|
+
request.onsuccess = () => resolve();
|
|
315
|
+
request.onerror = () => reject(request.error);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
close(): void {
|
|
320
|
+
this.db.close();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ============================================================================
|
|
325
|
+
// IDB Creator / Opener
|
|
326
|
+
// ============================================================================
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Options for opening a database with version upgrade support.
|
|
330
|
+
*/
|
|
331
|
+
export interface IDBOpenOptions {
|
|
332
|
+
/** Target version for the database. If higher than current, triggers upgrade. */
|
|
333
|
+
version?: number;
|
|
334
|
+
/** Called during version upgrade - this is where schema changes (createStore, createIndex) are allowed. */
|
|
335
|
+
onUpgrade?: (db: IDBDatabaseLike) => void;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Function type for creating/opening an IndexedDB-like database.
|
|
340
|
+
* Custom implementations can use this to provide proxy/mock/alternative backends.
|
|
341
|
+
*/
|
|
342
|
+
export type IDBCreator = (
|
|
343
|
+
name: string,
|
|
344
|
+
options?: IDBOpenOptions,
|
|
345
|
+
) => Promise<IDBDatabaseLike>;
|
|
346
|
+
|
|
347
|
+
const defaultIDBCreator: IDBCreator = (
|
|
348
|
+
name: string,
|
|
349
|
+
options?: IDBOpenOptions,
|
|
350
|
+
): Promise<IDBDatabaseLike> => {
|
|
5
351
|
return new Promise((resolve, reject) => {
|
|
6
|
-
const request =
|
|
352
|
+
const request = options?.version
|
|
353
|
+
? indexedDB.open(name, options.version)
|
|
354
|
+
: indexedDB.open(name);
|
|
355
|
+
|
|
7
356
|
request.onerror = () => reject(request.error);
|
|
8
|
-
|
|
357
|
+
|
|
9
358
|
request.onblocked = () => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
);
|
|
359
|
+
setTimeout(() => {
|
|
360
|
+
reject(new Error("Database upgrade blocked - close other tabs"));
|
|
361
|
+
}, 3000);
|
|
13
362
|
};
|
|
363
|
+
|
|
364
|
+
request.onupgradeneeded = (event) => {
|
|
365
|
+
if (options?.onUpgrade) {
|
|
366
|
+
const db = request.result;
|
|
367
|
+
const transaction = (event.target as IDBOpenDBRequest).transaction;
|
|
368
|
+
if (!transaction) {
|
|
369
|
+
reject(new Error("No transaction during upgrade"));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
// Create an upgrade-mode database wrapper
|
|
373
|
+
const upgradeDb = new UpgradeModeDatabase(db, transaction);
|
|
374
|
+
try {
|
|
375
|
+
options.onUpgrade(upgradeDb);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
transaction.abort();
|
|
378
|
+
reject(error);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
request.onsuccess = () => resolve(new NativeIDBDatabase(request.result));
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Upgrade-mode database wrapper used during version changes.
|
|
389
|
+
* Provides IDBDatabaseLike interface with schema modification capabilities.
|
|
390
|
+
*/
|
|
391
|
+
class UpgradeModeDatabase implements IDBDatabaseLike {
|
|
392
|
+
private createdStores: Map<string, IDBObjectStore> = new Map();
|
|
393
|
+
|
|
394
|
+
constructor(
|
|
395
|
+
private db: IDBDatabase,
|
|
396
|
+
private transaction: IDBTransaction,
|
|
397
|
+
) {}
|
|
398
|
+
|
|
399
|
+
get version(): number {
|
|
400
|
+
return this.db.version;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
hasStore(storeName: string): boolean {
|
|
404
|
+
return this.db.objectStoreNames.contains(storeName);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
getStoreNames(): string[] {
|
|
408
|
+
return Array.from(this.db.objectStoreNames);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
createStore(storeName: string, options?: CreateStoreOptions): void {
|
|
412
|
+
const store = this.db.createObjectStore(storeName, options);
|
|
413
|
+
this.createdStores.set(storeName, store);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
deleteStore(storeName: string): void {
|
|
417
|
+
this.db.deleteObjectStore(storeName);
|
|
418
|
+
this.createdStores.delete(storeName);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
createIndex(
|
|
422
|
+
storeName: string,
|
|
423
|
+
indexName: string,
|
|
424
|
+
keyPath: string | string[],
|
|
425
|
+
options?: CreateIndexOptions,
|
|
426
|
+
): void {
|
|
427
|
+
let store = this.createdStores.get(storeName);
|
|
428
|
+
if (!store) {
|
|
429
|
+
try {
|
|
430
|
+
store = this.transaction.objectStore(storeName);
|
|
431
|
+
} catch {
|
|
432
|
+
throw new Error(`Cannot create index - store "${storeName}" not found`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
store.createIndex(indexName, keyPath, options);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
deleteIndex(storeName: string, indexName: string): void {
|
|
439
|
+
let store = this.createdStores.get(storeName);
|
|
440
|
+
if (!store) {
|
|
441
|
+
try {
|
|
442
|
+
store = this.transaction.objectStore(storeName);
|
|
443
|
+
} catch {
|
|
444
|
+
throw new Error(`Cannot delete index - store "${storeName}" not found`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
store.deleteIndex(indexName);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
getStoreIndexes(storeName: string): IndexInfo[] {
|
|
451
|
+
if (!this.hasStore(storeName)) return [];
|
|
452
|
+
let store = this.createdStores.get(storeName);
|
|
453
|
+
if (!store) {
|
|
454
|
+
try {
|
|
455
|
+
store = this.transaction.objectStore(storeName);
|
|
456
|
+
} catch {
|
|
457
|
+
return [];
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return Array.from(store.indexNames).map((name) => ({
|
|
461
|
+
name,
|
|
462
|
+
keyPath: store.index(name).keyPath,
|
|
463
|
+
}));
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Data operations not available during upgrade
|
|
467
|
+
async getAll<T = unknown>(): Promise<T[]> {
|
|
468
|
+
throw new Error("getAll not available during upgrade");
|
|
469
|
+
}
|
|
470
|
+
async getAllByIndex<T = unknown>(): Promise<T[]> {
|
|
471
|
+
throw new Error("getAllByIndex not available during upgrade");
|
|
472
|
+
}
|
|
473
|
+
async get<T = unknown>(): Promise<T | undefined> {
|
|
474
|
+
throw new Error("get not available during upgrade");
|
|
475
|
+
}
|
|
476
|
+
async add(): Promise<void> {
|
|
477
|
+
throw new Error("add not available during upgrade");
|
|
478
|
+
}
|
|
479
|
+
async put(): Promise<void> {
|
|
480
|
+
throw new Error("put not available during upgrade");
|
|
481
|
+
}
|
|
482
|
+
async delete(): Promise<void> {
|
|
483
|
+
throw new Error("delete not available during upgrade");
|
|
484
|
+
}
|
|
485
|
+
async clear(): Promise<void> {
|
|
486
|
+
throw new Error("clear not available during upgrade");
|
|
487
|
+
}
|
|
488
|
+
close(): void {
|
|
489
|
+
this.db.close();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export async function openIndexedDb(
|
|
494
|
+
name: string,
|
|
495
|
+
dbCreator?: IDBCreator,
|
|
496
|
+
options?: IDBOpenOptions,
|
|
497
|
+
): Promise<IDBDatabaseLike> {
|
|
498
|
+
const dbCreatorToUse = dbCreator ?? defaultIDBCreator;
|
|
499
|
+
return dbCreatorToUse(name, options);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// ============================================================================
|
|
503
|
+
// IDB Deleter
|
|
504
|
+
// ============================================================================
|
|
505
|
+
|
|
506
|
+
export type IDBDeleter = (name: string) => Promise<void>;
|
|
507
|
+
|
|
508
|
+
const defaultIDBDeleter: IDBDeleter = (name: string): Promise<void> => {
|
|
509
|
+
return new Promise((resolve, reject) => {
|
|
510
|
+
const request = indexedDB.deleteDatabase(name);
|
|
511
|
+
request.onerror = () => reject(request.error);
|
|
512
|
+
request.onsuccess = () => resolve();
|
|
14
513
|
});
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Deletes the database (useful for testing)
|
|
518
|
+
*/
|
|
519
|
+
export async function deleteIndexedDB(
|
|
520
|
+
dbName: string,
|
|
521
|
+
dbDeleter?: IDBDeleter,
|
|
522
|
+
): Promise<void> {
|
|
523
|
+
const dbDeleterToUse = dbDeleter ?? defaultIDBDeleter;
|
|
524
|
+
return dbDeleterToUse(dbName);
|
|
15
525
|
}
|