|
1 | | -import { clear, createStore, del, entries, getMany, setMany } from 'idb-keyval' |
2 | | -import { DBDriver, DBKey, DBValue } from './abstract' |
| 1 | +import { openDB, IDBPDatabase } from 'idb' |
| 2 | +import { DBDriver, DBKey } from './abstract' |
3 | 3 |
|
4 | | -export default (dbName: string, storeName: string): IndexedDBDriver => new IndexedDBDriver(dbName, storeName) |
| 4 | +export default function ( |
| 5 | + dbName: string, |
| 6 | + storeName: string, |
| 7 | +): DBDriver { |
| 8 | + return new IndexedDB(() => getDBWithStore(dbName, storeName), storeName) |
| 9 | +} |
| 10 | + |
| 11 | +const dbs: Record<string, Promise<IDBPDatabase>> = {} |
| 12 | + |
| 13 | +export async function resetConnections(): Promise<void> { |
| 14 | + for(const [name, db] of Object.entries(dbs)) { |
| 15 | + (await db).close() |
| 16 | + delete dbs[name] |
| 17 | + } |
| 18 | +} |
5 | 19 |
|
6 | | -class IndexedDBDriver implements DBDriver { |
7 | | - constructor(dbName: string, storeName: string) { |
8 | | - this.store = createStore(dbName, storeName) |
| 20 | +export async function getDBWithStore( |
| 21 | + dbName: string, |
| 22 | + storeName: string, |
| 23 | +): Promise<IDBPDatabase> { |
| 24 | + const db = await (dbs[dbName] ?? open(dbName, storeName)) |
| 25 | + |
| 26 | + if (!db.objectStoreNames.contains(storeName)) { |
| 27 | + return open(dbName, storeName, db.version + 1) |
9 | 28 | } |
10 | | - store: ReturnType<typeof createStore> |
11 | 29 |
|
12 | | - clear(): Promise<void> { |
13 | | - return clear(this.store) |
| 30 | + return db |
| 31 | +} |
| 32 | + |
| 33 | +async function open(dbName: string, storeName: string, version: number | undefined = undefined) { |
| 34 | + if (dbName in dbs) { |
| 35 | + (await dbs[dbName]).close() |
14 | 36 | } |
| 37 | + return dbs[dbName] = openDB(dbName, version, { |
| 38 | + upgrade(db) { |
| 39 | + db.createObjectStore(storeName) |
| 40 | + }, |
| 41 | + }) |
| 42 | +} |
15 | 43 |
|
16 | | - del(key: DBKey): Promise<void> { |
17 | | - return del(key, this.store) |
| 44 | +export class IndexedDB { |
| 45 | + private getDB: () => Promise<IDBPDatabase> |
| 46 | + private storeName: string |
| 47 | + constructor(getDB: () => Promise<IDBPDatabase>, storeName: string) { |
| 48 | + this.getDB = getDB |
| 49 | + this.storeName = storeName |
18 | 50 | } |
19 | 51 |
|
20 | | - entries<T extends unknown>(): Promise<[DBKey, NonNullable<DBValue<T>>][]> { |
21 | | - return entries(this.store) |
| 52 | + async clear(): Promise<void> { |
| 53 | + return (await this.getDB()).clear(this.storeName) |
22 | 54 | } |
23 | 55 |
|
24 | | - getMany<T extends unknown>(keys: DBKey[]): Promise<DBValue<T>[]> { |
25 | | - return getMany(keys, this.store) |
| 56 | + async del(key: DBKey): Promise<void> { |
| 57 | + return (await this.getDB()).delete(this.storeName, key) |
26 | 58 | } |
27 | 59 |
|
28 | | - setMany<T extends unknown>(entries: [DBKey, DBValue<T>][]): Promise<void> { |
29 | | - return setMany(entries, this.store) |
| 60 | + async entries<T>(): Promise<Array<[IDBValidKey, T]>> { |
| 61 | + const items: Array<[IDBValidKey, T]> = [] |
| 62 | + const transaction = (await this.getDB()).transaction(this.storeName, 'readonly') |
| 63 | + |
| 64 | + let cursor = await transaction.store.openCursor() |
| 65 | + while(cursor) { |
| 66 | + items.push([cursor.key, cursor.value]) |
| 67 | + cursor = await cursor.continue() |
| 68 | + } |
| 69 | + |
| 70 | + await transaction.done |
| 71 | + return items |
| 72 | + } |
| 73 | + |
| 74 | + async getMany<T>(keys: DBKey[]): Promise<Array<T>> { |
| 75 | + const transaction = (await this.getDB()).transaction(this.storeName, 'readonly') |
| 76 | + |
| 77 | + const r = await Promise.all(keys.map(k => transaction.store.get(k))) |
| 78 | + |
| 79 | + await transaction.done |
| 80 | + return r |
| 81 | + } |
| 82 | + |
| 83 | + async setMany(entries: [DBKey, unknown][]): Promise<void> { |
| 84 | + const transaction = (await this.getDB()).transaction(this.storeName, 'readwrite') |
| 85 | + |
| 86 | + await Promise.all<unknown>(entries.map(([key, value]) => ( |
| 87 | + value !== undefined |
| 88 | + ? transaction.store.put(value, key) |
| 89 | + : transaction.store.delete(key) |
| 90 | + ))) |
| 91 | + |
| 92 | + transaction.commit() |
| 93 | + return transaction.done |
30 | 94 | } |
31 | 95 | } |
0 commit comments