@xylabs/indexed-db 5.0.80 → 5.0.81
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 +4 -4
- package/dist/browser/IndexedDbKeyValueStore.d.ts.map +1 -1
- package/dist/browser/ObjectStore.d.ts.map +1 -1
- package/dist/browser/index.mjs +3 -0
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/withDbByVersion.d.ts.map +1 -1
- package/dist/neutral/IndexedDbKeyValueStore.d.ts.map +1 -1
- package/dist/neutral/ObjectStore.d.ts.map +1 -1
- package/dist/neutral/index.mjs +3 -0
- package/dist/neutral/index.mjs.map +1 -1
- package/dist/neutral/withDbByVersion.d.ts.map +1 -1
- package/dist/node/IndexedDbKeyValueStore.d.ts.map +1 -1
- package/dist/node/ObjectStore.d.ts.map +1 -1
- package/dist/node/index.mjs +3 -0
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/withDbByVersion.d.ts.map +1 -1
- package/package.json +10 -14
- package/src/IndexDescription.ts +0 -37
- package/src/IndexedDbKeyValueStore.ts +0 -59
- package/src/ObjectStore.ts +0 -5
- package/src/checkDbNeedsUpgrade.ts +0 -18
- package/src/checkStoreNeedsUpgrade.ts +0 -24
- package/src/createStoreDuringUpgrade.ts +0 -44
- package/src/getExistingIndexes.ts +0 -54
- package/src/index.ts +0 -11
- package/src/withDb.ts +0 -27
- package/src/withDbByVersion.ts +0 -69
- package/src/withReadOnlyStore.ts +0 -17
- package/src/withReadWriteStore.ts +0 -17
- package/src/withStore.ts +0 -34
package/README.md
CHANGED
|
@@ -159,7 +159,7 @@ KeyValueStore.delete
|
|
|
159
159
|
### get()
|
|
160
160
|
|
|
161
161
|
```ts
|
|
162
|
-
get(key): Promise<
|
|
162
|
+
get(key): Promise<StoreValue<T, S> | undefined>;
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
Returns a promise that resolves to the value for the given key.
|
|
@@ -174,7 +174,7 @@ The key to get the value for.
|
|
|
174
174
|
|
|
175
175
|
### Returns
|
|
176
176
|
|
|
177
|
-
`Promise`\<`
|
|
177
|
+
`Promise`\<`StoreValue`\<`T`, `S`\> \| `undefined`\>
|
|
178
178
|
|
|
179
179
|
### Implementation of
|
|
180
180
|
|
|
@@ -366,7 +366,7 @@ function createStoreDuringUpgrade<DBTypes>(
|
|
|
366
366
|
function getExistingIndexes<T>(
|
|
367
367
|
db,
|
|
368
368
|
storeName,
|
|
369
|
-
logger?): Promise<
|
|
369
|
+
logger?): Promise<IndexDescription[] | null>;
|
|
370
370
|
```
|
|
371
371
|
|
|
372
372
|
## Type Parameters
|
|
@@ -391,7 +391,7 @@ logger?): Promise<null | IndexDescription[]>;
|
|
|
391
391
|
|
|
392
392
|
## Returns
|
|
393
393
|
|
|
394
|
-
`Promise
|
|
394
|
+
`Promise`\<[`IndexDescription`](#../type-aliases/IndexDescription)[] \| `null`\>
|
|
395
395
|
|
|
396
396
|
### <a id="withDb"></a>withDb
|
|
397
397
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IndexedDbKeyValueStore.d.ts","sourceRoot":"","sources":["../../src/IndexedDbKeyValueStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAC/C,MAAM,KAAK,CAAA;AAIZ;;GAEG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzI,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAA;gBAET,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAKlC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAMvB,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"IndexedDbKeyValueStore.d.ts","sourceRoot":"","sources":["../../src/IndexedDbKeyValueStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAC/C,MAAM,KAAK,CAAA;AAIZ;;GAEG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzI,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAA;gBAET,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAKlC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAMvB,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;IAQvB,IAAI,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAMlC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhE,MAAM,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,KAC5B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;CAMnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectStore.d.ts","sourceRoot":"","sources":["../../src/ObjectStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ObjectStore.d.ts","sourceRoot":"","sources":["../../src/ObjectStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IAC9D,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAA;CACf"}
|
package/dist/browser/index.mjs
CHANGED
|
@@ -49,12 +49,15 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
|
|
|
49
49
|
dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex();
|
|
50
50
|
const handler = async () => {
|
|
51
51
|
const db2 = await openDB(dbName, version, {
|
|
52
|
+
/* v8 ignore next 2 */
|
|
52
53
|
blocked(currentVersion, blockedVersion, event) {
|
|
53
54
|
logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event);
|
|
54
55
|
},
|
|
56
|
+
/* v8 ignore next 2 */
|
|
55
57
|
blocking(currentVersion, blockedVersion, event) {
|
|
56
58
|
logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event);
|
|
57
59
|
},
|
|
60
|
+
/* v8 ignore next 2 */
|
|
58
61
|
terminated() {
|
|
59
62
|
logger?.log("IndexedDb: Terminated");
|
|
60
63
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/checkStoreNeedsUpgrade.ts","../../src/withDbByVersion.ts","../../src/IndexDescription.ts","../../src/createStoreDuringUpgrade.ts","../../src/withStore.ts","../../src/withReadOnlyStore.ts","../../src/getExistingIndexes.ts","../../src/checkDbNeedsUpgrade.ts","../../src/withDb.ts","../../src/IndexedDbKeyValueStore.ts","../../src/withReadWriteStore.ts"],"sourcesContent":["import { exists } from '@xylabs/exists'\nimport type { Logger } from '@xylabs/logger'\nimport type { IDBPDatabase } from 'idb'\n\nimport { getExistingIndexes } from './getExistingIndexes.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {\n logger?.log('checkStoreNeedsUpgrade', storeName, indexes)\n const existingIndexes = await getExistingIndexes(db, storeName, logger)\n if (existingIndexes === null) {\n // the store does not exist, so we need to trigger upgrade (no existing indexes)\n return true\n }\n const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))\n for (const { key, unique } of indexes) {\n const indexName = buildStandardIndexName({ key, unique })\n if (!existingIndexNames.has(indexName)) {\n return true\n }\n }\n return false\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type {\n DBSchema, IDBPDatabase, StoreNames,\n} from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDbByVersion<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n version?: number,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const db = await openDB<DBTypes>(dbName, version, {\n blocked(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event)\n },\n blocking(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event)\n },\n terminated() {\n logger?.log('IndexedDb: Terminated')\n },\n upgrade(db, _oldVersion, _newVersion, _transaction) {\n /* if (oldVersion !== newVersion) {\n logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`)\n const objectStores = transaction.objectStoreNames\n for (const name of objectStores) {\n try {\n db.deleteObjectStore(name)\n } catch {\n console.log(`IndexedDb: Failed to delete existing object store ${name}`)\n }\n }\n } */\n if (expectedIndexes) {\n for (const [storeName, indexes] of Object.entries(expectedIndexes)) {\n if (db.objectStoreNames.contains(storeName as StoreNames<DBTypes>)) {\n continue\n }\n const indexesToCreate = indexes.map(idx => ({\n ...idx,\n name: buildStandardIndexName(idx),\n // eslint-disable-next-line unicorn/no-array-reduce\n })).reduce((acc, idx) => acc.set(idx.name, idx), new Map<string, IndexDescription>()).values()\n createStoreDuringUpgrade(db, storeName as StoreNames<DBTypes>, [...indexesToCreate], logger)\n }\n }\n },\n })\n return db\n }\n const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n try {\n return await callback(db)\n } finally {\n db.close()\n }\n}\n","/**\n * The index direction (1 for ascending, -1 for descending)\n */\nexport type IndexDirection = -1 | 1\n\n/**\n * Description of index(es) to be created on a store\n */\nexport type IndexDescription = {\n /**\n * The key(s) to index\n */\n key: Record<string, IndexDirection>\n /**\n * Is the indexed value an array\n */\n multiEntry?: boolean\n /**\n * If true, the index must enforce uniqueness on the key\n */\n unique?: boolean\n}\n\nexport const IndexSeparator = '-'\n\n/**\n * Given an index description, this will build the index\n * name in standard form\n * @param index The index description\n * @returns The index name in standard form\n */\nexport const buildStandardIndexName = (index: IndexDescription) => {\n const { key, unique } = index\n const prefix = unique ? 'UX' : 'IX'\n const indexKeys = Object.keys(key)\n return `${prefix}_${indexKeys.join(IndexSeparator)}`\n}\n","import type { Logger } from '@xylabs/logger'\nimport type {\n DBSchema,\n IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\nexport function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${storeName}`)\n // Create the store\n let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined\n try {\n store = db.createObjectStore(storeName, {\n // If it isn't explicitly set, create a value by auto incrementing.\n autoIncrement: true,\n })\n } catch {\n logger?.warn(`Failed to create store: ${storeName} already exists`)\n return\n }\n logger?.log(`Creating store: created ${storeName}`)\n // Name the store\n store.name = storeName\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${key}`)\n const indexKeys = Object.keys(key)\n const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys\n const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>\n logger?.log('createIndex', indexName, keys, { multiEntry, unique })\n store.createIndex(indexName, keys, { multiEntry, unique })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, IDBPTransaction, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>,\n [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R,\n mode: M,\n logger?: Logger,\n): Promise<R> {\n logger?.log('withStore', storeName, mode)\n let transaction: IDBPTransaction<ObjectStore<T>, [StoreNames<ObjectStore<T>>], M> | undefined = undefined\n logger?.log('withStore:transaction')\n let store = null\n try {\n transaction = db.transaction(storeName, mode)\n // we do this in a try/catch because the store might not exist\n store = transaction.objectStore(storeName)\n } catch (ex) {\n logger?.log('withStore:catch', ex)\n }\n try {\n return await callback(store)\n } finally {\n logger?.log('withStore:finally')\n await transaction?.done\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readonly', logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase, StoreNames } from 'idb'\n\nimport {\n type IndexDescription,\n type IndexDirection,\n} from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\nimport { withReadOnlyStore } from './withReadOnlyStore.ts'\n\nasync function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n return await withReadOnlyStore(db, storeName, (store) => {\n return store\n ? [...store.indexNames].map((indexName) => {\n const index = store.index(indexName)\n const key: Record<string, IndexDirection> = {}\n if (Array.isArray(index.keyPath)) {\n for (const keyPath of index.keyPath) {\n key[keyPath] = 1\n }\n } else {\n key[index.keyPath] = 1\n }\n const desc: IndexDescription = {\n key,\n unique: index.unique,\n multiEntry: index.multiEntry,\n }\n return desc\n })\n : null\n }, logger)\n}\n\nexport async function getExistingIndexes<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>> | string,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {\n return await getExistingIndexesInternal(db, storeName, logger)\n })\n }\n return await getExistingIndexesInternal(db, storeName, logger)\n}\n","import type { Logger } from '@xylabs/logger'\n\nimport { checkStoreNeedsUpgrade } from './checkStoreNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nexport async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {\n logger?.log('checkDbNeedsUpgrade', dbName, stores)\n return await withDbByVersion<ObjectStore, number>(dbName, async (db) => {\n for (const [storeName, indexes] of Object.entries(stores)) {\n if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {\n return db.version + 1\n }\n }\n return db.version\n }, undefined, stores, logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type { DBSchema, IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, lock)\n }\n return lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n}\n","import type { KeyValueStore } from '@xylabs/storage'\nimport type {\n DBSchema,\n IDBPDatabase, StoreKey, StoreNames, StoreValue,\n} from 'idb'\n\nimport { withDb } from './withDb.ts'\n\n/**\n * An IndexedDB key/value store.\n */\nexport class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>> implements KeyValueStore<StoreValue<T, S>, StoreKey<T, S>> {\n readonly dbName: string\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n async delete(key: StoreKey<T, S>): Promise<void> {\n return await this.withDb((db) => {\n return db.delete(this.storeName, key)\n })\n }\n\n async get(key: StoreKey<T, S>) {\n return await this.withDb((db) => {\n return db.get(this.storeName, key) ?? undefined\n })\n }\n\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<void> {\n return await this.withDb(async (db) => {\n await db.put(this.storeName, value, key)\n })\n }\n\n async withDb<R = StoreValue<T, S>>(\n callback: (db: IDBPDatabase<T>) =>\n Promise<R> | R,\n ) {\n return await withDb<T, R>(this.dbName, (db) => {\n return callback(db)\n })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readwrite', logger)\n}\n"],"mappings":"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","names":["db","db","Mutex","dbMutexes","Mutex"]}
|
|
1
|
+
{"version":3,"sources":["../../src/checkStoreNeedsUpgrade.ts","../../src/withDbByVersion.ts","../../src/IndexDescription.ts","../../src/createStoreDuringUpgrade.ts","../../src/withStore.ts","../../src/withReadOnlyStore.ts","../../src/getExistingIndexes.ts","../../src/checkDbNeedsUpgrade.ts","../../src/withDb.ts","../../src/IndexedDbKeyValueStore.ts","../../src/withReadWriteStore.ts"],"sourcesContent":["import { exists } from '@xylabs/exists'\nimport type { Logger } from '@xylabs/logger'\nimport type { IDBPDatabase } from 'idb'\n\nimport { getExistingIndexes } from './getExistingIndexes.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {\n logger?.log('checkStoreNeedsUpgrade', storeName, indexes)\n const existingIndexes = await getExistingIndexes(db, storeName, logger)\n if (existingIndexes === null) {\n // the store does not exist, so we need to trigger upgrade (no existing indexes)\n return true\n }\n const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))\n for (const { key, unique } of indexes) {\n const indexName = buildStandardIndexName({ key, unique })\n if (!existingIndexNames.has(indexName)) {\n return true\n }\n }\n return false\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type {\n DBSchema, IDBPDatabase, StoreNames,\n} from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDbByVersion<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n version?: number,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const db = await openDB<DBTypes>(dbName, version, {\n /* v8 ignore next 2 */\n blocked(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event)\n },\n /* v8 ignore next 2 */\n blocking(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event)\n },\n /* v8 ignore next 2 */\n terminated() {\n logger?.log('IndexedDb: Terminated')\n },\n upgrade(db, _oldVersion, _newVersion, _transaction) {\n /* if (oldVersion !== newVersion) {\n logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`)\n const objectStores = transaction.objectStoreNames\n for (const name of objectStores) {\n try {\n db.deleteObjectStore(name)\n } catch {\n console.log(`IndexedDb: Failed to delete existing object store ${name}`)\n }\n }\n } */\n if (expectedIndexes) {\n for (const [storeName, indexes] of Object.entries(expectedIndexes)) {\n if (db.objectStoreNames.contains(storeName as StoreNames<DBTypes>)) {\n continue\n }\n const indexesToCreate = indexes.map(idx => ({\n ...idx,\n name: buildStandardIndexName(idx),\n // eslint-disable-next-line unicorn/no-array-reduce\n })).reduce((acc, idx) => acc.set(idx.name, idx), new Map<string, IndexDescription>()).values()\n createStoreDuringUpgrade(db, storeName as StoreNames<DBTypes>, [...indexesToCreate], logger)\n }\n }\n },\n })\n return db\n }\n const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n try {\n return await callback(db)\n } finally {\n db.close()\n }\n}\n","/**\n * The index direction (1 for ascending, -1 for descending)\n */\nexport type IndexDirection = -1 | 1\n\n/**\n * Description of index(es) to be created on a store\n */\nexport type IndexDescription = {\n /**\n * The key(s) to index\n */\n key: Record<string, IndexDirection>\n /**\n * Is the indexed value an array\n */\n multiEntry?: boolean\n /**\n * If true, the index must enforce uniqueness on the key\n */\n unique?: boolean\n}\n\nexport const IndexSeparator = '-'\n\n/**\n * Given an index description, this will build the index\n * name in standard form\n * @param index The index description\n * @returns The index name in standard form\n */\nexport const buildStandardIndexName = (index: IndexDescription) => {\n const { key, unique } = index\n const prefix = unique ? 'UX' : 'IX'\n const indexKeys = Object.keys(key)\n return `${prefix}_${indexKeys.join(IndexSeparator)}`\n}\n","import type { Logger } from '@xylabs/logger'\nimport type {\n DBSchema,\n IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\nexport function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${storeName}`)\n // Create the store\n let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined\n try {\n store = db.createObjectStore(storeName, {\n // If it isn't explicitly set, create a value by auto incrementing.\n autoIncrement: true,\n })\n } catch {\n logger?.warn(`Failed to create store: ${storeName} already exists`)\n return\n }\n logger?.log(`Creating store: created ${storeName}`)\n // Name the store\n store.name = storeName\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${key}`)\n const indexKeys = Object.keys(key)\n const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys\n const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>\n logger?.log('createIndex', indexName, keys, { multiEntry, unique })\n store.createIndex(indexName, keys, { multiEntry, unique })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, IDBPTransaction, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>,\n [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R,\n mode: M,\n logger?: Logger,\n): Promise<R> {\n logger?.log('withStore', storeName, mode)\n let transaction: IDBPTransaction<ObjectStore<T>, [StoreNames<ObjectStore<T>>], M> | undefined = undefined\n logger?.log('withStore:transaction')\n let store = null\n try {\n transaction = db.transaction(storeName, mode)\n // we do this in a try/catch because the store might not exist\n store = transaction.objectStore(storeName)\n } catch (ex) {\n logger?.log('withStore:catch', ex)\n }\n try {\n return await callback(store)\n } finally {\n logger?.log('withStore:finally')\n await transaction?.done\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readonly', logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase, StoreNames } from 'idb'\n\nimport {\n type IndexDescription,\n type IndexDirection,\n} from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\nimport { withReadOnlyStore } from './withReadOnlyStore.ts'\n\nasync function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n return await withReadOnlyStore(db, storeName, (store) => {\n return store\n ? [...store.indexNames].map((indexName) => {\n const index = store.index(indexName)\n const key: Record<string, IndexDirection> = {}\n if (Array.isArray(index.keyPath)) {\n for (const keyPath of index.keyPath) {\n key[keyPath] = 1\n }\n } else {\n key[index.keyPath] = 1\n }\n const desc: IndexDescription = {\n key,\n unique: index.unique,\n multiEntry: index.multiEntry,\n }\n return desc\n })\n : null\n }, logger)\n}\n\nexport async function getExistingIndexes<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>> | string,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {\n return await getExistingIndexesInternal(db, storeName, logger)\n })\n }\n return await getExistingIndexesInternal(db, storeName, logger)\n}\n","import type { Logger } from '@xylabs/logger'\n\nimport { checkStoreNeedsUpgrade } from './checkStoreNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nexport async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {\n logger?.log('checkDbNeedsUpgrade', dbName, stores)\n return await withDbByVersion<ObjectStore, number>(dbName, async (db) => {\n for (const [storeName, indexes] of Object.entries(stores)) {\n if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {\n return db.version + 1\n }\n }\n return db.version\n }, undefined, stores, logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type { DBSchema, IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, lock)\n }\n return lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n}\n","import type { KeyValueStore } from '@xylabs/storage'\nimport type {\n DBSchema,\n IDBPDatabase, StoreKey, StoreNames, StoreValue,\n} from 'idb'\n\nimport { withDb } from './withDb.ts'\n\n/**\n * An IndexedDB key/value store.\n */\nexport class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>> implements KeyValueStore<StoreValue<T, S>, StoreKey<T, S>> {\n readonly dbName: string\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n async delete(key: StoreKey<T, S>): Promise<void> {\n return await this.withDb((db) => {\n return db.delete(this.storeName, key)\n })\n }\n\n async get(key: StoreKey<T, S>) {\n return await this.withDb((db) => {\n /* v8 ignore start */\n return db.get(this.storeName, key) ?? undefined\n /* v8 ignore stop */\n })\n }\n\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<void> {\n return await this.withDb(async (db) => {\n await db.put(this.storeName, value, key)\n })\n }\n\n async withDb<R = StoreValue<T, S>>(\n callback: (db: IDBPDatabase<T>) =>\n Promise<R> | R,\n ) {\n return await withDb<T, R>(this.dbName, (db) => {\n return callback(db)\n })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readwrite', logger)\n}\n"],"mappings":";AAAA,SAAS,cAAc;;;ACEvB,SAAS,aAAa;AAItB,SAAS,cAAc;;;ACiBhB,IAAM,iBAAiB;AAQvB,IAAM,yBAAyB,CAAC,UAA4B;AACjE,QAAM,EAAE,KAAK,OAAO,IAAI;AACxB,QAAM,SAAS,SAAS,OAAO;AAC/B,QAAM,YAAY,OAAO,KAAK,GAAG;AACjC,SAAO,GAAG,MAAM,IAAI,UAAU,KAAK,cAAc,CAAC;AACpD;;;ACzBO,SAAS,yBACd,IACA,WACA,SACA,QACA;AACA,UAAQ,IAAI,kBAAkB,SAAS,EAAE;AAEzC,MAAI;AACJ,MAAI;AACF,YAAQ,GAAG,kBAAkB,WAAW;AAAA;AAAA,MAEtC,eAAe;AAAA,IACjB,CAAC;AAAA,EACH,QAAQ;AACN,YAAQ,KAAK,2BAA2B,SAAS,iBAAiB;AAClE;AAAA,EACF;AACA,UAAQ,IAAI,2BAA2B,SAAS,EAAE;AAElD,QAAM,OAAO;AAEb,aAAW;AAAA,IACT;AAAA,IAAK;AAAA,IAAY;AAAA,EACnB,KAAK,SAAS;AACZ,YAAQ,IAAI,yBAAyB,GAAG,EAAE;AAC1C,UAAM,YAAY,OAAO,KAAK,GAAG;AACjC,UAAM,OAAO,UAAU,WAAW,IAAI,UAAU,CAAC,IAAI;AACrD,UAAM,YAAY,uBAAuB,EAAE,KAAK,OAAO,CAAC;AACxD,YAAQ,IAAI,eAAe,WAAW,MAAM,EAAE,YAAY,OAAO,CAAC;AAClE,UAAM,YAAY,WAAW,MAAM,EAAE,YAAY,OAAO,CAAC;AAAA,EAC3D;AACF;;;AFhCA,IAAM,YAAmC,CAAC;AAE1C,eAAsB,gBACpB,QACA,UACA,SACA,iBACA,QACA,OAAO,MACK;AACZ,YAAU,MAAM,IAAI,UAAU,MAAM,KAAK,IAAI,MAAM;AACnD,QAAM,UAAU,YAAY;AAC1B,UAAMA,MAAK,MAAM,OAAgB,QAAQ,SAAS;AAAA;AAAA,MAEhD,QAAQ,gBAAgB,gBAAgB,OAAO;AAC7C,gBAAQ,KAAK,0CAA0C,cAAc,OAAO,cAAc,IAAI,KAAK;AAAA,MACrG;AAAA;AAAA,MAEA,SAAS,gBAAgB,gBAAgB,OAAO;AAC9C,gBAAQ,KAAK,oCAAoC,cAAc,OAAO,cAAc,IAAI,KAAK;AAAA,MAC/F;AAAA;AAAA,MAEA,aAAa;AACX,gBAAQ,IAAI,uBAAuB;AAAA,MACrC;AAAA,MACA,QAAQA,KAAI,aAAa,aAAa,cAAc;AAYlD,YAAI,iBAAiB;AACnB,qBAAW,CAAC,WAAW,OAAO,KAAK,OAAO,QAAQ,eAAe,GAAG;AAClE,gBAAIA,IAAG,iBAAiB,SAAS,SAAgC,GAAG;AAClE;AAAA,YACF;AACA,kBAAM,kBAAkB,QAAQ,IAAI,UAAQ;AAAA,cAC1C,GAAG;AAAA,cACH,MAAM,uBAAuB,GAAG;AAAA;AAAA,YAElC,EAAE,EAAE,OAAO,CAAC,KAAK,QAAQ,IAAI,IAAI,IAAI,MAAM,GAAG,GAAG,oBAAI,IAA8B,CAAC,EAAE,OAAO;AAC7F,qCAAyBA,KAAI,WAAkC,CAAC,GAAG,eAAe,GAAG,MAAM;AAAA,UAC7F;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AACD,WAAOA;AAAA,EACT;AACA,QAAM,KAAK,OAAO,MAAM,UAAU,MAAM,EAAE,aAAa,OAAO,IAAI,MAAM,QAAQ;AAChF,MAAI;AACF,WAAO,MAAM,SAAS,EAAE;AAAA,EAC1B,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;;;AG/DA,eAAsB,UACpB,IACA,WACA,UAEA,MACA,QACY;AACZ,UAAQ,IAAI,aAAa,WAAW,IAAI;AACxC,MAAI,cAA4F;AAChG,UAAQ,IAAI,uBAAuB;AACnC,MAAI,QAAQ;AACZ,MAAI;AACF,kBAAc,GAAG,YAAY,WAAW,IAAI;AAE5C,YAAQ,YAAY,YAAY,SAAS;AAAA,EAC3C,SAAS,IAAI;AACX,YAAQ,IAAI,mBAAmB,EAAE;AAAA,EACnC;AACA,MAAI;AACF,WAAO,MAAM,SAAS,KAAK;AAAA,EAC7B,UAAE;AACA,YAAQ,IAAI,mBAAmB;AAC/B,UAAM,aAAa;AAAA,EACrB;AACF;;;ACxBA,eAAsB,kBACpB,IACA,WACA,UACA,QACY;AACZ,SAAO,MAAM,UAAU,IAAI,WAAW,UAAU,YAAY,MAAM;AACpE;;;ACJA,eAAe,2BACb,IACA,WACA,QACoC;AACpC,UAAQ,IAAI,8BAA8B,SAAS;AACnD,SAAO,MAAM,kBAAkB,IAAI,WAAW,CAAC,UAAU;AACvD,WAAO,QACH,CAAC,GAAG,MAAM,UAAU,EAAE,IAAI,CAAC,cAAc;AACvC,YAAM,QAAQ,MAAM,MAAM,SAAS;AACnC,YAAM,MAAsC,CAAC;AAC7C,UAAI,MAAM,QAAQ,MAAM,OAAO,GAAG;AAChC,mBAAW,WAAW,MAAM,SAAS;AACnC,cAAI,OAAO,IAAI;AAAA,QACjB;AAAA,MACF,OAAO;AACL,YAAI,MAAM,OAAO,IAAI;AAAA,MACvB;AACA,YAAM,OAAyB;AAAA,QAC7B;AAAA,QACA,QAAQ,MAAM;AAAA,QACd,YAAY,MAAM;AAAA,MACpB;AACA,aAAO;AAAA,IACT,CAAC,IACD;AAAA,EACN,GAAG,MAAM;AACX;AAEA,eAAsB,mBACpB,IACA,WACA,QACoC;AACpC,UAAQ,IAAI,sBAAsB,SAAS;AAC3C,MAAI,OAAO,OAAO,UAAU;AAC1B,WAAO,MAAM,gBAA2D,IAAI,OAAOC,QAAO;AACxF,aAAO,MAAM,2BAA2BA,KAAI,WAAW,MAAM;AAAA,IAC/D,CAAC;AAAA,EACH;AACA,SAAO,MAAM,2BAA2B,IAAI,WAAW,MAAM;AAC/D;;;AN7CA,eAAsB,uBAAuB,IAAuC,WAAmB,SAA6B,QAAiB;AACnJ,UAAQ,IAAI,0BAA0B,WAAW,OAAO;AACxD,QAAM,kBAAkB,MAAM,mBAAmB,IAAI,WAAW,MAAM;AACtE,MAAI,oBAAoB,MAAM;AAE5B,WAAO;AAAA,EACT;AACA,QAAM,qBAAqB,IAAI,IAAI,gBAAgB,IAAI,CAAC,EAAE,KAAK,OAAO,MAAM,uBAAuB,EAAE,KAAK,OAAO,CAAC,CAAC,EAAE,OAAO,MAAM,CAAC;AACnI,aAAW,EAAE,KAAK,OAAO,KAAK,SAAS;AACrC,UAAM,YAAY,uBAAuB,EAAE,KAAK,OAAO,CAAC;AACxD,QAAI,CAAC,mBAAmB,IAAI,SAAS,GAAG;AACtC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AOhBA,eAAsB,oBAAoB,QAAgB,QAA4C,QAAiB;AACrH,UAAQ,IAAI,uBAAuB,QAAQ,MAAM;AACjD,SAAO,MAAM,gBAAqC,QAAQ,OAAO,OAAO;AACtE,eAAW,CAAC,WAAW,OAAO,KAAK,OAAO,QAAQ,MAAM,GAAG;AACzD,UAAI,MAAM,uBAAuB,IAAI,WAAW,SAAS,MAAM,GAAG;AAChE,eAAO,GAAG,UAAU;AAAA,MACtB;AAAA,IACF;AACA,WAAO,GAAG;AAAA,EACZ,GAAG,QAAW,QAAQ,MAAM;AAC9B;;;ACfA,SAAS,SAAAC,cAAa;AAOtB,IAAMC,aAAmC,CAAC;AAE1C,eAAsB,OACpB,QACA,UACA,iBACA,QACA,OAAO,MACK;AACZ,EAAAA,WAAU,MAAM,IAAIA,WAAU,MAAM,KAAK,IAAIC,OAAM;AACnD,QAAM,UAAU,YAAY;AAC1B,UAAM,gBAAgB,oBAAoB,SAAY,SAAY,MAAM,oBAAoB,QAAQ,iBAAiB,MAAM;AAC3H,WAAO,MAAM,gBAA4B,QAAQ,OAAO,OAAO;AAC7D,aAAO,MAAM,SAAS,EAAE;AAAA,IAC1B,GAAG,eAAe,iBAAiB,QAAQ,IAAI;AAAA,EACjD;AACA,SAAO,OAAO,MAAMD,WAAU,MAAM,EAAE,aAAa,OAAO,IAAI,MAAM,QAAQ;AAC9E;;;ACfO,IAAM,yBAAN,MAAqI;AAAA,EACjI;AAAA,EACA;AAAA,EAET,YAAY,QAAgB,WAAc;AACxC,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,QAAwB;AAC5B,WAAO,MAAM,KAAK,OAAO,CAAC,OAAO;AAC/B,aAAO,GAAG,MAAM,KAAK,SAAS;AAAA,IAChC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,KAAoC;AAC/C,WAAO,MAAM,KAAK,OAAO,CAAC,OAAO;AAC/B,aAAO,GAAG,OAAO,KAAK,WAAW,GAAG;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,KAAqB;AAC7B,WAAO,MAAM,KAAK,OAAO,CAAC,OAAO;AAE/B,aAAO,GAAG,IAAI,KAAK,WAAW,GAAG,KAAK;AAAA,IAExC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAmC;AACvC,WAAO,MAAM,KAAK,OAAO,CAAC,OAAO;AAC/B,aAAO,GAAG,WAAW,KAAK,SAAS;AAAA,IACrC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,KAAqB,OAAwC;AACrE,WAAO,MAAM,KAAK,OAAO,OAAO,OAAO;AACrC,YAAM,GAAG,IAAI,KAAK,WAAW,OAAO,GAAG;AAAA,IACzC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OACJ,UAEA;AACA,WAAO,MAAM,OAAa,KAAK,QAAQ,CAAC,OAAO;AAC7C,aAAO,SAAS,EAAE;AAAA,IACpB,CAAC;AAAA,EACH;AACF;;;ACnDA,eAAsB,mBACpB,IACA,WACA,UACA,QACY;AACZ,SAAO,MAAM,UAAU,IAAI,WAAW,UAAU,aAAa,MAAM;AACrE;","names":["db","db","Mutex","dbMutexes","Mutex"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withDbByVersion.d.ts","sourceRoot":"","sources":["../../src/withDbByVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EACV,QAAQ,EAAE,YAAY,EACvB,MAAM,KAAK,CAAA;AAIZ,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF,wBAAsB,eAAe,CAAC,OAAO,SAAS,QAAQ,GAAG,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACjG,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"withDbByVersion.d.ts","sourceRoot":"","sources":["../../src/withDbByVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EACV,QAAQ,EAAE,YAAY,EACvB,MAAM,KAAK,CAAA;AAIZ,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF,wBAAsB,eAAe,CAAC,OAAO,SAAS,QAAQ,GAAG,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACjG,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CAmDZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IndexedDbKeyValueStore.d.ts","sourceRoot":"","sources":["../../src/IndexedDbKeyValueStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAC/C,MAAM,KAAK,CAAA;AAIZ;;GAEG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzI,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAA;gBAET,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAKlC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAMvB,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"IndexedDbKeyValueStore.d.ts","sourceRoot":"","sources":["../../src/IndexedDbKeyValueStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAC/C,MAAM,KAAK,CAAA;AAIZ;;GAEG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzI,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAA;gBAET,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAKlC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAMvB,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;IAQvB,IAAI,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAMlC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhE,MAAM,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,KAC5B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;CAMnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectStore.d.ts","sourceRoot":"","sources":["../../src/ObjectStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ObjectStore.d.ts","sourceRoot":"","sources":["../../src/ObjectStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IAC9D,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAA;CACf"}
|
package/dist/neutral/index.mjs
CHANGED
|
@@ -49,12 +49,15 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
|
|
|
49
49
|
dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex();
|
|
50
50
|
const handler = async () => {
|
|
51
51
|
const db2 = await openDB(dbName, version, {
|
|
52
|
+
/* v8 ignore next 2 */
|
|
52
53
|
blocked(currentVersion, blockedVersion, event) {
|
|
53
54
|
logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event);
|
|
54
55
|
},
|
|
56
|
+
/* v8 ignore next 2 */
|
|
55
57
|
blocking(currentVersion, blockedVersion, event) {
|
|
56
58
|
logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event);
|
|
57
59
|
},
|
|
60
|
+
/* v8 ignore next 2 */
|
|
58
61
|
terminated() {
|
|
59
62
|
logger?.log("IndexedDb: Terminated");
|
|
60
63
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/checkStoreNeedsUpgrade.ts","../../src/withDbByVersion.ts","../../src/IndexDescription.ts","../../src/createStoreDuringUpgrade.ts","../../src/withStore.ts","../../src/withReadOnlyStore.ts","../../src/getExistingIndexes.ts","../../src/checkDbNeedsUpgrade.ts","../../src/withDb.ts","../../src/IndexedDbKeyValueStore.ts","../../src/withReadWriteStore.ts"],"sourcesContent":["import { exists } from '@xylabs/exists'\nimport type { Logger } from '@xylabs/logger'\nimport type { IDBPDatabase } from 'idb'\n\nimport { getExistingIndexes } from './getExistingIndexes.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {\n logger?.log('checkStoreNeedsUpgrade', storeName, indexes)\n const existingIndexes = await getExistingIndexes(db, storeName, logger)\n if (existingIndexes === null) {\n // the store does not exist, so we need to trigger upgrade (no existing indexes)\n return true\n }\n const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))\n for (const { key, unique } of indexes) {\n const indexName = buildStandardIndexName({ key, unique })\n if (!existingIndexNames.has(indexName)) {\n return true\n }\n }\n return false\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type {\n DBSchema, IDBPDatabase, StoreNames,\n} from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDbByVersion<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n version?: number,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const db = await openDB<DBTypes>(dbName, version, {\n blocked(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event)\n },\n blocking(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event)\n },\n terminated() {\n logger?.log('IndexedDb: Terminated')\n },\n upgrade(db, _oldVersion, _newVersion, _transaction) {\n /* if (oldVersion !== newVersion) {\n logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`)\n const objectStores = transaction.objectStoreNames\n for (const name of objectStores) {\n try {\n db.deleteObjectStore(name)\n } catch {\n console.log(`IndexedDb: Failed to delete existing object store ${name}`)\n }\n }\n } */\n if (expectedIndexes) {\n for (const [storeName, indexes] of Object.entries(expectedIndexes)) {\n if (db.objectStoreNames.contains(storeName as StoreNames<DBTypes>)) {\n continue\n }\n const indexesToCreate = indexes.map(idx => ({\n ...idx,\n name: buildStandardIndexName(idx),\n // eslint-disable-next-line unicorn/no-array-reduce\n })).reduce((acc, idx) => acc.set(idx.name, idx), new Map<string, IndexDescription>()).values()\n createStoreDuringUpgrade(db, storeName as StoreNames<DBTypes>, [...indexesToCreate], logger)\n }\n }\n },\n })\n return db\n }\n const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n try {\n return await callback(db)\n } finally {\n db.close()\n }\n}\n","/**\n * The index direction (1 for ascending, -1 for descending)\n */\nexport type IndexDirection = -1 | 1\n\n/**\n * Description of index(es) to be created on a store\n */\nexport type IndexDescription = {\n /**\n * The key(s) to index\n */\n key: Record<string, IndexDirection>\n /**\n * Is the indexed value an array\n */\n multiEntry?: boolean\n /**\n * If true, the index must enforce uniqueness on the key\n */\n unique?: boolean\n}\n\nexport const IndexSeparator = '-'\n\n/**\n * Given an index description, this will build the index\n * name in standard form\n * @param index The index description\n * @returns The index name in standard form\n */\nexport const buildStandardIndexName = (index: IndexDescription) => {\n const { key, unique } = index\n const prefix = unique ? 'UX' : 'IX'\n const indexKeys = Object.keys(key)\n return `${prefix}_${indexKeys.join(IndexSeparator)}`\n}\n","import type { Logger } from '@xylabs/logger'\nimport type {\n DBSchema,\n IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\nexport function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${storeName}`)\n // Create the store\n let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined\n try {\n store = db.createObjectStore(storeName, {\n // If it isn't explicitly set, create a value by auto incrementing.\n autoIncrement: true,\n })\n } catch {\n logger?.warn(`Failed to create store: ${storeName} already exists`)\n return\n }\n logger?.log(`Creating store: created ${storeName}`)\n // Name the store\n store.name = storeName\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${key}`)\n const indexKeys = Object.keys(key)\n const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys\n const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>\n logger?.log('createIndex', indexName, keys, { multiEntry, unique })\n store.createIndex(indexName, keys, { multiEntry, unique })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, IDBPTransaction, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>,\n [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R,\n mode: M,\n logger?: Logger,\n): Promise<R> {\n logger?.log('withStore', storeName, mode)\n let transaction: IDBPTransaction<ObjectStore<T>, [StoreNames<ObjectStore<T>>], M> | undefined = undefined\n logger?.log('withStore:transaction')\n let store = null\n try {\n transaction = db.transaction(storeName, mode)\n // we do this in a try/catch because the store might not exist\n store = transaction.objectStore(storeName)\n } catch (ex) {\n logger?.log('withStore:catch', ex)\n }\n try {\n return await callback(store)\n } finally {\n logger?.log('withStore:finally')\n await transaction?.done\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readonly', logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase, StoreNames } from 'idb'\n\nimport {\n type IndexDescription,\n type IndexDirection,\n} from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\nimport { withReadOnlyStore } from './withReadOnlyStore.ts'\n\nasync function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n return await withReadOnlyStore(db, storeName, (store) => {\n return store\n ? [...store.indexNames].map((indexName) => {\n const index = store.index(indexName)\n const key: Record<string, IndexDirection> = {}\n if (Array.isArray(index.keyPath)) {\n for (const keyPath of index.keyPath) {\n key[keyPath] = 1\n }\n } else {\n key[index.keyPath] = 1\n }\n const desc: IndexDescription = {\n key,\n unique: index.unique,\n multiEntry: index.multiEntry,\n }\n return desc\n })\n : null\n }, logger)\n}\n\nexport async function getExistingIndexes<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>> | string,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {\n return await getExistingIndexesInternal(db, storeName, logger)\n })\n }\n return await getExistingIndexesInternal(db, storeName, logger)\n}\n","import type { Logger } from '@xylabs/logger'\n\nimport { checkStoreNeedsUpgrade } from './checkStoreNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nexport async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {\n logger?.log('checkDbNeedsUpgrade', dbName, stores)\n return await withDbByVersion<ObjectStore, number>(dbName, async (db) => {\n for (const [storeName, indexes] of Object.entries(stores)) {\n if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {\n return db.version + 1\n }\n }\n return db.version\n }, undefined, stores, logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type { DBSchema, IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, lock)\n }\n return lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n}\n","import type { KeyValueStore } from '@xylabs/storage'\nimport type {\n DBSchema,\n IDBPDatabase, StoreKey, StoreNames, StoreValue,\n} from 'idb'\n\nimport { withDb } from './withDb.ts'\n\n/**\n * An IndexedDB key/value store.\n */\nexport class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>> implements KeyValueStore<StoreValue<T, S>, StoreKey<T, S>> {\n readonly dbName: string\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n async delete(key: StoreKey<T, S>): Promise<void> {\n return await this.withDb((db) => {\n return db.delete(this.storeName, key)\n })\n }\n\n async get(key: StoreKey<T, S>) {\n return await this.withDb((db) => {\n return db.get(this.storeName, key) ?? undefined\n })\n }\n\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<void> {\n return await this.withDb(async (db) => {\n await db.put(this.storeName, value, key)\n })\n }\n\n async withDb<R = StoreValue<T, S>>(\n callback: (db: IDBPDatabase<T>) =>\n Promise<R> | R,\n ) {\n return await withDb<T, R>(this.dbName, (db) => {\n return callback(db)\n })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readwrite', logger)\n}\n"],"mappings":"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","names":["db","db","Mutex","dbMutexes","Mutex"]}
|
|
1
|
+
{"version":3,"sources":["../../src/checkStoreNeedsUpgrade.ts","../../src/withDbByVersion.ts","../../src/IndexDescription.ts","../../src/createStoreDuringUpgrade.ts","../../src/withStore.ts","../../src/withReadOnlyStore.ts","../../src/getExistingIndexes.ts","../../src/checkDbNeedsUpgrade.ts","../../src/withDb.ts","../../src/IndexedDbKeyValueStore.ts","../../src/withReadWriteStore.ts"],"sourcesContent":["import { exists } from '@xylabs/exists'\nimport type { Logger } from '@xylabs/logger'\nimport type { IDBPDatabase } from 'idb'\n\nimport { getExistingIndexes } from './getExistingIndexes.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {\n logger?.log('checkStoreNeedsUpgrade', storeName, indexes)\n const existingIndexes = await getExistingIndexes(db, storeName, logger)\n if (existingIndexes === null) {\n // the store does not exist, so we need to trigger upgrade (no existing indexes)\n return true\n }\n const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))\n for (const { key, unique } of indexes) {\n const indexName = buildStandardIndexName({ key, unique })\n if (!existingIndexNames.has(indexName)) {\n return true\n }\n }\n return false\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type {\n DBSchema, IDBPDatabase, StoreNames,\n} from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDbByVersion<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n version?: number,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const db = await openDB<DBTypes>(dbName, version, {\n /* v8 ignore next 2 */\n blocked(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event)\n },\n /* v8 ignore next 2 */\n blocking(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event)\n },\n /* v8 ignore next 2 */\n terminated() {\n logger?.log('IndexedDb: Terminated')\n },\n upgrade(db, _oldVersion, _newVersion, _transaction) {\n /* if (oldVersion !== newVersion) {\n logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`)\n const objectStores = transaction.objectStoreNames\n for (const name of objectStores) {\n try {\n db.deleteObjectStore(name)\n } catch {\n console.log(`IndexedDb: Failed to delete existing object store ${name}`)\n }\n }\n } */\n if (expectedIndexes) {\n for (const [storeName, indexes] of Object.entries(expectedIndexes)) {\n if (db.objectStoreNames.contains(storeName as StoreNames<DBTypes>)) {\n continue\n }\n const indexesToCreate = indexes.map(idx => ({\n ...idx,\n name: buildStandardIndexName(idx),\n // eslint-disable-next-line unicorn/no-array-reduce\n })).reduce((acc, idx) => acc.set(idx.name, idx), new Map<string, IndexDescription>()).values()\n createStoreDuringUpgrade(db, storeName as StoreNames<DBTypes>, [...indexesToCreate], logger)\n }\n }\n },\n })\n return db\n }\n const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n try {\n return await callback(db)\n } finally {\n db.close()\n }\n}\n","/**\n * The index direction (1 for ascending, -1 for descending)\n */\nexport type IndexDirection = -1 | 1\n\n/**\n * Description of index(es) to be created on a store\n */\nexport type IndexDescription = {\n /**\n * The key(s) to index\n */\n key: Record<string, IndexDirection>\n /**\n * Is the indexed value an array\n */\n multiEntry?: boolean\n /**\n * If true, the index must enforce uniqueness on the key\n */\n unique?: boolean\n}\n\nexport const IndexSeparator = '-'\n\n/**\n * Given an index description, this will build the index\n * name in standard form\n * @param index The index description\n * @returns The index name in standard form\n */\nexport const buildStandardIndexName = (index: IndexDescription) => {\n const { key, unique } = index\n const prefix = unique ? 'UX' : 'IX'\n const indexKeys = Object.keys(key)\n return `${prefix}_${indexKeys.join(IndexSeparator)}`\n}\n","import type { Logger } from '@xylabs/logger'\nimport type {\n DBSchema,\n IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\nexport function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${storeName}`)\n // Create the store\n let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined\n try {\n store = db.createObjectStore(storeName, {\n // If it isn't explicitly set, create a value by auto incrementing.\n autoIncrement: true,\n })\n } catch {\n logger?.warn(`Failed to create store: ${storeName} already exists`)\n return\n }\n logger?.log(`Creating store: created ${storeName}`)\n // Name the store\n store.name = storeName\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${key}`)\n const indexKeys = Object.keys(key)\n const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys\n const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>\n logger?.log('createIndex', indexName, keys, { multiEntry, unique })\n store.createIndex(indexName, keys, { multiEntry, unique })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, IDBPTransaction, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>,\n [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R,\n mode: M,\n logger?: Logger,\n): Promise<R> {\n logger?.log('withStore', storeName, mode)\n let transaction: IDBPTransaction<ObjectStore<T>, [StoreNames<ObjectStore<T>>], M> | undefined = undefined\n logger?.log('withStore:transaction')\n let store = null\n try {\n transaction = db.transaction(storeName, mode)\n // we do this in a try/catch because the store might not exist\n store = transaction.objectStore(storeName)\n } catch (ex) {\n logger?.log('withStore:catch', ex)\n }\n try {\n return await callback(store)\n } finally {\n logger?.log('withStore:finally')\n await transaction?.done\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readonly', logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase, StoreNames } from 'idb'\n\nimport {\n type IndexDescription,\n type IndexDirection,\n} from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\nimport { withReadOnlyStore } from './withReadOnlyStore.ts'\n\nasync function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n return await withReadOnlyStore(db, storeName, (store) => {\n return store\n ? [...store.indexNames].map((indexName) => {\n const index = store.index(indexName)\n const key: Record<string, IndexDirection> = {}\n if (Array.isArray(index.keyPath)) {\n for (const keyPath of index.keyPath) {\n key[keyPath] = 1\n }\n } else {\n key[index.keyPath] = 1\n }\n const desc: IndexDescription = {\n key,\n unique: index.unique,\n multiEntry: index.multiEntry,\n }\n return desc\n })\n : null\n }, logger)\n}\n\nexport async function getExistingIndexes<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>> | string,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {\n return await getExistingIndexesInternal(db, storeName, logger)\n })\n }\n return await getExistingIndexesInternal(db, storeName, logger)\n}\n","import type { Logger } from '@xylabs/logger'\n\nimport { checkStoreNeedsUpgrade } from './checkStoreNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nexport async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {\n logger?.log('checkDbNeedsUpgrade', dbName, stores)\n return await withDbByVersion<ObjectStore, number>(dbName, async (db) => {\n for (const [storeName, indexes] of Object.entries(stores)) {\n if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {\n return db.version + 1\n }\n }\n return db.version\n }, undefined, stores, logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type { DBSchema, IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, lock)\n }\n return lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n}\n","import type { KeyValueStore } from '@xylabs/storage'\nimport type {\n DBSchema,\n IDBPDatabase, StoreKey, StoreNames, StoreValue,\n} from 'idb'\n\nimport { withDb } from './withDb.ts'\n\n/**\n * An IndexedDB key/value store.\n */\nexport class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>> implements KeyValueStore<StoreValue<T, S>, StoreKey<T, S>> {\n readonly dbName: string\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n async delete(key: StoreKey<T, S>): Promise<void> {\n return await this.withDb((db) => {\n return db.delete(this.storeName, key)\n })\n }\n\n async get(key: StoreKey<T, S>) {\n return await this.withDb((db) => {\n /* v8 ignore start */\n return db.get(this.storeName, key) ?? undefined\n /* v8 ignore stop */\n })\n }\n\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<void> {\n return await this.withDb(async (db) => {\n await db.put(this.storeName, value, key)\n })\n }\n\n async withDb<R = StoreValue<T, S>>(\n callback: (db: IDBPDatabase<T>) =>\n Promise<R> | R,\n ) {\n return await withDb<T, R>(this.dbName, (db) => {\n return callback(db)\n })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readwrite', logger)\n}\n"],"mappings":"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","names":["db","db","Mutex","dbMutexes","Mutex"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withDbByVersion.d.ts","sourceRoot":"","sources":["../../src/withDbByVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EACV,QAAQ,EAAE,YAAY,EACvB,MAAM,KAAK,CAAA;AAIZ,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF,wBAAsB,eAAe,CAAC,OAAO,SAAS,QAAQ,GAAG,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACjG,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"withDbByVersion.d.ts","sourceRoot":"","sources":["../../src/withDbByVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EACV,QAAQ,EAAE,YAAY,EACvB,MAAM,KAAK,CAAA;AAIZ,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF,wBAAsB,eAAe,CAAC,OAAO,SAAS,QAAQ,GAAG,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACjG,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CAmDZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IndexedDbKeyValueStore.d.ts","sourceRoot":"","sources":["../../src/IndexedDbKeyValueStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAC/C,MAAM,KAAK,CAAA;AAIZ;;GAEG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzI,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAA;gBAET,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAKlC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAMvB,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"IndexedDbKeyValueStore.d.ts","sourceRoot":"","sources":["../../src/IndexedDbKeyValueStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAC/C,MAAM,KAAK,CAAA;AAIZ;;GAEG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzI,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAA;gBAET,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAKlC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAMvB,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;IAQvB,IAAI,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAMlC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhE,MAAM,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,KAC5B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;CAMnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectStore.d.ts","sourceRoot":"","sources":["../../src/ObjectStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ObjectStore.d.ts","sourceRoot":"","sources":["../../src/ObjectStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IAC9D,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAA;CACf"}
|
package/dist/node/index.mjs
CHANGED
|
@@ -49,12 +49,15 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
|
|
|
49
49
|
dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex();
|
|
50
50
|
const handler = async () => {
|
|
51
51
|
const db2 = await openDB(dbName, version, {
|
|
52
|
+
/* v8 ignore next 2 */
|
|
52
53
|
blocked(currentVersion, blockedVersion, event) {
|
|
53
54
|
logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event);
|
|
54
55
|
},
|
|
56
|
+
/* v8 ignore next 2 */
|
|
55
57
|
blocking(currentVersion, blockedVersion, event) {
|
|
56
58
|
logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event);
|
|
57
59
|
},
|
|
60
|
+
/* v8 ignore next 2 */
|
|
58
61
|
terminated() {
|
|
59
62
|
logger?.log("IndexedDb: Terminated");
|
|
60
63
|
},
|
package/dist/node/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/checkStoreNeedsUpgrade.ts","../../src/withDbByVersion.ts","../../src/IndexDescription.ts","../../src/createStoreDuringUpgrade.ts","../../src/withStore.ts","../../src/withReadOnlyStore.ts","../../src/getExistingIndexes.ts","../../src/checkDbNeedsUpgrade.ts","../../src/withDb.ts","../../src/IndexedDbKeyValueStore.ts","../../src/withReadWriteStore.ts"],"sourcesContent":["import { exists } from '@xylabs/exists'\nimport type { Logger } from '@xylabs/logger'\nimport type { IDBPDatabase } from 'idb'\n\nimport { getExistingIndexes } from './getExistingIndexes.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {\n logger?.log('checkStoreNeedsUpgrade', storeName, indexes)\n const existingIndexes = await getExistingIndexes(db, storeName, logger)\n if (existingIndexes === null) {\n // the store does not exist, so we need to trigger upgrade (no existing indexes)\n return true\n }\n const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))\n for (const { key, unique } of indexes) {\n const indexName = buildStandardIndexName({ key, unique })\n if (!existingIndexNames.has(indexName)) {\n return true\n }\n }\n return false\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type {\n DBSchema, IDBPDatabase, StoreNames,\n} from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDbByVersion<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n version?: number,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const db = await openDB<DBTypes>(dbName, version, {\n blocked(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event)\n },\n blocking(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event)\n },\n terminated() {\n logger?.log('IndexedDb: Terminated')\n },\n upgrade(db, _oldVersion, _newVersion, _transaction) {\n /* if (oldVersion !== newVersion) {\n logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`)\n const objectStores = transaction.objectStoreNames\n for (const name of objectStores) {\n try {\n db.deleteObjectStore(name)\n } catch {\n console.log(`IndexedDb: Failed to delete existing object store ${name}`)\n }\n }\n } */\n if (expectedIndexes) {\n for (const [storeName, indexes] of Object.entries(expectedIndexes)) {\n if (db.objectStoreNames.contains(storeName as StoreNames<DBTypes>)) {\n continue\n }\n const indexesToCreate = indexes.map(idx => ({\n ...idx,\n name: buildStandardIndexName(idx),\n // eslint-disable-next-line unicorn/no-array-reduce\n })).reduce((acc, idx) => acc.set(idx.name, idx), new Map<string, IndexDescription>()).values()\n createStoreDuringUpgrade(db, storeName as StoreNames<DBTypes>, [...indexesToCreate], logger)\n }\n }\n },\n })\n return db\n }\n const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n try {\n return await callback(db)\n } finally {\n db.close()\n }\n}\n","/**\n * The index direction (1 for ascending, -1 for descending)\n */\nexport type IndexDirection = -1 | 1\n\n/**\n * Description of index(es) to be created on a store\n */\nexport type IndexDescription = {\n /**\n * The key(s) to index\n */\n key: Record<string, IndexDirection>\n /**\n * Is the indexed value an array\n */\n multiEntry?: boolean\n /**\n * If true, the index must enforce uniqueness on the key\n */\n unique?: boolean\n}\n\nexport const IndexSeparator = '-'\n\n/**\n * Given an index description, this will build the index\n * name in standard form\n * @param index The index description\n * @returns The index name in standard form\n */\nexport const buildStandardIndexName = (index: IndexDescription) => {\n const { key, unique } = index\n const prefix = unique ? 'UX' : 'IX'\n const indexKeys = Object.keys(key)\n return `${prefix}_${indexKeys.join(IndexSeparator)}`\n}\n","import type { Logger } from '@xylabs/logger'\nimport type {\n DBSchema,\n IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\nexport function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${storeName}`)\n // Create the store\n let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined\n try {\n store = db.createObjectStore(storeName, {\n // If it isn't explicitly set, create a value by auto incrementing.\n autoIncrement: true,\n })\n } catch {\n logger?.warn(`Failed to create store: ${storeName} already exists`)\n return\n }\n logger?.log(`Creating store: created ${storeName}`)\n // Name the store\n store.name = storeName\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${key}`)\n const indexKeys = Object.keys(key)\n const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys\n const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>\n logger?.log('createIndex', indexName, keys, { multiEntry, unique })\n store.createIndex(indexName, keys, { multiEntry, unique })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, IDBPTransaction, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>,\n [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R,\n mode: M,\n logger?: Logger,\n): Promise<R> {\n logger?.log('withStore', storeName, mode)\n let transaction: IDBPTransaction<ObjectStore<T>, [StoreNames<ObjectStore<T>>], M> | undefined = undefined\n logger?.log('withStore:transaction')\n let store = null\n try {\n transaction = db.transaction(storeName, mode)\n // we do this in a try/catch because the store might not exist\n store = transaction.objectStore(storeName)\n } catch (ex) {\n logger?.log('withStore:catch', ex)\n }\n try {\n return await callback(store)\n } finally {\n logger?.log('withStore:finally')\n await transaction?.done\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readonly', logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase, StoreNames } from 'idb'\n\nimport {\n type IndexDescription,\n type IndexDirection,\n} from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\nimport { withReadOnlyStore } from './withReadOnlyStore.ts'\n\nasync function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n return await withReadOnlyStore(db, storeName, (store) => {\n return store\n ? [...store.indexNames].map((indexName) => {\n const index = store.index(indexName)\n const key: Record<string, IndexDirection> = {}\n if (Array.isArray(index.keyPath)) {\n for (const keyPath of index.keyPath) {\n key[keyPath] = 1\n }\n } else {\n key[index.keyPath] = 1\n }\n const desc: IndexDescription = {\n key,\n unique: index.unique,\n multiEntry: index.multiEntry,\n }\n return desc\n })\n : null\n }, logger)\n}\n\nexport async function getExistingIndexes<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>> | string,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {\n return await getExistingIndexesInternal(db, storeName, logger)\n })\n }\n return await getExistingIndexesInternal(db, storeName, logger)\n}\n","import type { Logger } from '@xylabs/logger'\n\nimport { checkStoreNeedsUpgrade } from './checkStoreNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nexport async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {\n logger?.log('checkDbNeedsUpgrade', dbName, stores)\n return await withDbByVersion<ObjectStore, number>(dbName, async (db) => {\n for (const [storeName, indexes] of Object.entries(stores)) {\n if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {\n return db.version + 1\n }\n }\n return db.version\n }, undefined, stores, logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type { DBSchema, IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, lock)\n }\n return lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n}\n","import type { KeyValueStore } from '@xylabs/storage'\nimport type {\n DBSchema,\n IDBPDatabase, StoreKey, StoreNames, StoreValue,\n} from 'idb'\n\nimport { withDb } from './withDb.ts'\n\n/**\n * An IndexedDB key/value store.\n */\nexport class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>> implements KeyValueStore<StoreValue<T, S>, StoreKey<T, S>> {\n readonly dbName: string\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n async delete(key: StoreKey<T, S>): Promise<void> {\n return await this.withDb((db) => {\n return db.delete(this.storeName, key)\n })\n }\n\n async get(key: StoreKey<T, S>) {\n return await this.withDb((db) => {\n return db.get(this.storeName, key) ?? undefined\n })\n }\n\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<void> {\n return await this.withDb(async (db) => {\n await db.put(this.storeName, value, key)\n })\n }\n\n async withDb<R = StoreValue<T, S>>(\n callback: (db: IDBPDatabase<T>) =>\n Promise<R> | R,\n ) {\n return await withDb<T, R>(this.dbName, (db) => {\n return callback(db)\n })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readwrite', logger)\n}\n"],"mappings":"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","names":["db","db","Mutex","dbMutexes","Mutex"]}
|
|
1
|
+
{"version":3,"sources":["../../src/checkStoreNeedsUpgrade.ts","../../src/withDbByVersion.ts","../../src/IndexDescription.ts","../../src/createStoreDuringUpgrade.ts","../../src/withStore.ts","../../src/withReadOnlyStore.ts","../../src/getExistingIndexes.ts","../../src/checkDbNeedsUpgrade.ts","../../src/withDb.ts","../../src/IndexedDbKeyValueStore.ts","../../src/withReadWriteStore.ts"],"sourcesContent":["import { exists } from '@xylabs/exists'\nimport type { Logger } from '@xylabs/logger'\nimport type { IDBPDatabase } from 'idb'\n\nimport { getExistingIndexes } from './getExistingIndexes.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {\n logger?.log('checkStoreNeedsUpgrade', storeName, indexes)\n const existingIndexes = await getExistingIndexes(db, storeName, logger)\n if (existingIndexes === null) {\n // the store does not exist, so we need to trigger upgrade (no existing indexes)\n return true\n }\n const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))\n for (const { key, unique } of indexes) {\n const indexName = buildStandardIndexName({ key, unique })\n if (!existingIndexNames.has(indexName)) {\n return true\n }\n }\n return false\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type {\n DBSchema, IDBPDatabase, StoreNames,\n} from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDbByVersion<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n version?: number,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const db = await openDB<DBTypes>(dbName, version, {\n /* v8 ignore next 2 */\n blocked(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event)\n },\n /* v8 ignore next 2 */\n blocking(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event)\n },\n /* v8 ignore next 2 */\n terminated() {\n logger?.log('IndexedDb: Terminated')\n },\n upgrade(db, _oldVersion, _newVersion, _transaction) {\n /* if (oldVersion !== newVersion) {\n logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`)\n const objectStores = transaction.objectStoreNames\n for (const name of objectStores) {\n try {\n db.deleteObjectStore(name)\n } catch {\n console.log(`IndexedDb: Failed to delete existing object store ${name}`)\n }\n }\n } */\n if (expectedIndexes) {\n for (const [storeName, indexes] of Object.entries(expectedIndexes)) {\n if (db.objectStoreNames.contains(storeName as StoreNames<DBTypes>)) {\n continue\n }\n const indexesToCreate = indexes.map(idx => ({\n ...idx,\n name: buildStandardIndexName(idx),\n // eslint-disable-next-line unicorn/no-array-reduce\n })).reduce((acc, idx) => acc.set(idx.name, idx), new Map<string, IndexDescription>()).values()\n createStoreDuringUpgrade(db, storeName as StoreNames<DBTypes>, [...indexesToCreate], logger)\n }\n }\n },\n })\n return db\n }\n const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n try {\n return await callback(db)\n } finally {\n db.close()\n }\n}\n","/**\n * The index direction (1 for ascending, -1 for descending)\n */\nexport type IndexDirection = -1 | 1\n\n/**\n * Description of index(es) to be created on a store\n */\nexport type IndexDescription = {\n /**\n * The key(s) to index\n */\n key: Record<string, IndexDirection>\n /**\n * Is the indexed value an array\n */\n multiEntry?: boolean\n /**\n * If true, the index must enforce uniqueness on the key\n */\n unique?: boolean\n}\n\nexport const IndexSeparator = '-'\n\n/**\n * Given an index description, this will build the index\n * name in standard form\n * @param index The index description\n * @returns The index name in standard form\n */\nexport const buildStandardIndexName = (index: IndexDescription) => {\n const { key, unique } = index\n const prefix = unique ? 'UX' : 'IX'\n const indexKeys = Object.keys(key)\n return `${prefix}_${indexKeys.join(IndexSeparator)}`\n}\n","import type { Logger } from '@xylabs/logger'\nimport type {\n DBSchema,\n IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\nexport function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${storeName}`)\n // Create the store\n let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined\n try {\n store = db.createObjectStore(storeName, {\n // If it isn't explicitly set, create a value by auto incrementing.\n autoIncrement: true,\n })\n } catch {\n logger?.warn(`Failed to create store: ${storeName} already exists`)\n return\n }\n logger?.log(`Creating store: created ${storeName}`)\n // Name the store\n store.name = storeName\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${key}`)\n const indexKeys = Object.keys(key)\n const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys\n const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>\n logger?.log('createIndex', indexName, keys, { multiEntry, unique })\n store.createIndex(indexName, keys, { multiEntry, unique })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, IDBPTransaction, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>,\n [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R,\n mode: M,\n logger?: Logger,\n): Promise<R> {\n logger?.log('withStore', storeName, mode)\n let transaction: IDBPTransaction<ObjectStore<T>, [StoreNames<ObjectStore<T>>], M> | undefined = undefined\n logger?.log('withStore:transaction')\n let store = null\n try {\n transaction = db.transaction(storeName, mode)\n // we do this in a try/catch because the store might not exist\n store = transaction.objectStore(storeName)\n } catch (ex) {\n logger?.log('withStore:catch', ex)\n }\n try {\n return await callback(store)\n } finally {\n logger?.log('withStore:finally')\n await transaction?.done\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readonly', logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase, StoreNames } from 'idb'\n\nimport {\n type IndexDescription,\n type IndexDirection,\n} from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\nimport { withReadOnlyStore } from './withReadOnlyStore.ts'\n\nasync function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n return await withReadOnlyStore(db, storeName, (store) => {\n return store\n ? [...store.indexNames].map((indexName) => {\n const index = store.index(indexName)\n const key: Record<string, IndexDirection> = {}\n if (Array.isArray(index.keyPath)) {\n for (const keyPath of index.keyPath) {\n key[keyPath] = 1\n }\n } else {\n key[index.keyPath] = 1\n }\n const desc: IndexDescription = {\n key,\n unique: index.unique,\n multiEntry: index.multiEntry,\n }\n return desc\n })\n : null\n }, logger)\n}\n\nexport async function getExistingIndexes<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>> | string,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {\n return await getExistingIndexesInternal(db, storeName, logger)\n })\n }\n return await getExistingIndexesInternal(db, storeName, logger)\n}\n","import type { Logger } from '@xylabs/logger'\n\nimport { checkStoreNeedsUpgrade } from './checkStoreNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nexport async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {\n logger?.log('checkDbNeedsUpgrade', dbName, stores)\n return await withDbByVersion<ObjectStore, number>(dbName, async (db) => {\n for (const [storeName, indexes] of Object.entries(stores)) {\n if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {\n return db.version + 1\n }\n }\n return db.version\n }, undefined, stores, logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport { Mutex } from 'async-mutex'\nimport type { DBSchema, IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\nexport async function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n): Promise<R> {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n const handler = async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, lock)\n }\n return lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()\n}\n","import type { KeyValueStore } from '@xylabs/storage'\nimport type {\n DBSchema,\n IDBPDatabase, StoreKey, StoreNames, StoreValue,\n} from 'idb'\n\nimport { withDb } from './withDb.ts'\n\n/**\n * An IndexedDB key/value store.\n */\nexport class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>> implements KeyValueStore<StoreValue<T, S>, StoreKey<T, S>> {\n readonly dbName: string\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n async delete(key: StoreKey<T, S>): Promise<void> {\n return await this.withDb((db) => {\n return db.delete(this.storeName, key)\n })\n }\n\n async get(key: StoreKey<T, S>) {\n return await this.withDb((db) => {\n /* v8 ignore start */\n return db.get(this.storeName, key) ?? undefined\n /* v8 ignore stop */\n })\n }\n\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<void> {\n return await this.withDb(async (db) => {\n await db.put(this.storeName, value, key)\n })\n }\n\n async withDb<R = StoreValue<T, S>>(\n callback: (db: IDBPDatabase<T>) =>\n Promise<R> | R,\n ) {\n return await withDb<T, R>(this.dbName, (db) => {\n return callback(db)\n })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readwrite', logger)\n}\n"],"mappings":"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","names":["db","db","Mutex","dbMutexes","Mutex"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withDbByVersion.d.ts","sourceRoot":"","sources":["../../src/withDbByVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EACV,QAAQ,EAAE,YAAY,EACvB,MAAM,KAAK,CAAA;AAIZ,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF,wBAAsB,eAAe,CAAC,OAAO,SAAS,QAAQ,GAAG,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACjG,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"withDbByVersion.d.ts","sourceRoot":"","sources":["../../src/withDbByVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EACV,QAAQ,EAAE,YAAY,EACvB,MAAM,KAAK,CAAA;AAIZ,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF,wBAAsB,eAAe,CAAC,OAAO,SAAS,QAAQ,GAAG,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACjG,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CAmDZ"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xylabs/indexed-db",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.81",
|
|
4
4
|
"description": "Base functionality used throughout XY Labs TypeScript/JavaScript libraries",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hex",
|
|
@@ -30,45 +30,41 @@
|
|
|
30
30
|
".": {
|
|
31
31
|
"node": {
|
|
32
32
|
"types": "./dist/node/index.d.ts",
|
|
33
|
-
"source": "./src/index.ts",
|
|
34
33
|
"default": "./dist/node/index.mjs"
|
|
35
34
|
},
|
|
36
35
|
"browser": {
|
|
37
36
|
"types": "./dist/browser/index.d.ts",
|
|
38
|
-
"source": "./src/index.ts",
|
|
39
37
|
"default": "./dist/browser/index.mjs"
|
|
40
38
|
},
|
|
41
39
|
"neutral": {
|
|
42
40
|
"types": "./dist/neutral/index.d.ts",
|
|
43
|
-
"source": "./src/index.ts",
|
|
44
41
|
"default": "./dist/neutral/index.mjs"
|
|
45
42
|
}
|
|
46
43
|
},
|
|
47
44
|
"./package.json": "./package.json"
|
|
48
45
|
},
|
|
49
46
|
"module": "./dist/browser/index.mjs",
|
|
50
|
-
"source": "./src/index.ts",
|
|
51
47
|
"types": "./dist/browser/index.d.ts",
|
|
52
48
|
"files": [
|
|
53
49
|
"dist",
|
|
54
|
-
"src",
|
|
55
50
|
"!**/*.bench.*",
|
|
56
51
|
"!**/*.spec.*",
|
|
57
52
|
"!**/*.test.*"
|
|
58
53
|
],
|
|
59
54
|
"dependencies": {
|
|
60
|
-
"@xylabs/exists": "~5.0.
|
|
61
|
-
"@xylabs/logger": "~5.0.
|
|
62
|
-
"@xylabs/object": "~5.0.
|
|
63
|
-
"@xylabs/storage": "~5.0.
|
|
55
|
+
"@xylabs/exists": "~5.0.81",
|
|
56
|
+
"@xylabs/logger": "~5.0.81",
|
|
57
|
+
"@xylabs/object": "~5.0.81",
|
|
58
|
+
"@xylabs/storage": "~5.0.81",
|
|
64
59
|
"async-mutex": "~0.5.0",
|
|
60
|
+
"fake-indexeddb": "~6.2.5",
|
|
65
61
|
"idb": "~8.0.3"
|
|
66
62
|
},
|
|
67
63
|
"devDependencies": {
|
|
68
|
-
"@xylabs/ts-scripts-yarn3": "~7.
|
|
69
|
-
"@xylabs/tsconfig
|
|
70
|
-
"
|
|
71
|
-
"jsdom": "~28.
|
|
64
|
+
"@xylabs/ts-scripts-yarn3": "~7.4.11",
|
|
65
|
+
"@xylabs/tsconfig": "~7.4.11",
|
|
66
|
+
"@xylabs/tsconfig-dom": "~7.4.11",
|
|
67
|
+
"jsdom": "~28.1.0",
|
|
72
68
|
"typescript": "~5.9.3",
|
|
73
69
|
"vitest": "~4.0.18"
|
|
74
70
|
},
|
package/src/IndexDescription.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The index direction (1 for ascending, -1 for descending)
|
|
3
|
-
*/
|
|
4
|
-
export type IndexDirection = -1 | 1
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Description of index(es) to be created on a store
|
|
8
|
-
*/
|
|
9
|
-
export type IndexDescription = {
|
|
10
|
-
/**
|
|
11
|
-
* The key(s) to index
|
|
12
|
-
*/
|
|
13
|
-
key: Record<string, IndexDirection>
|
|
14
|
-
/**
|
|
15
|
-
* Is the indexed value an array
|
|
16
|
-
*/
|
|
17
|
-
multiEntry?: boolean
|
|
18
|
-
/**
|
|
19
|
-
* If true, the index must enforce uniqueness on the key
|
|
20
|
-
*/
|
|
21
|
-
unique?: boolean
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const IndexSeparator = '-'
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Given an index description, this will build the index
|
|
28
|
-
* name in standard form
|
|
29
|
-
* @param index The index description
|
|
30
|
-
* @returns The index name in standard form
|
|
31
|
-
*/
|
|
32
|
-
export const buildStandardIndexName = (index: IndexDescription) => {
|
|
33
|
-
const { key, unique } = index
|
|
34
|
-
const prefix = unique ? 'UX' : 'IX'
|
|
35
|
-
const indexKeys = Object.keys(key)
|
|
36
|
-
return `${prefix}_${indexKeys.join(IndexSeparator)}`
|
|
37
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { KeyValueStore } from '@xylabs/storage'
|
|
2
|
-
import type {
|
|
3
|
-
DBSchema,
|
|
4
|
-
IDBPDatabase, StoreKey, StoreNames, StoreValue,
|
|
5
|
-
} from 'idb'
|
|
6
|
-
|
|
7
|
-
import { withDb } from './withDb.ts'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* An IndexedDB key/value store.
|
|
11
|
-
*/
|
|
12
|
-
export class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>> implements KeyValueStore<StoreValue<T, S>, StoreKey<T, S>> {
|
|
13
|
-
readonly dbName: string
|
|
14
|
-
readonly storeName: S
|
|
15
|
-
|
|
16
|
-
constructor(dbName: string, storeName: S) {
|
|
17
|
-
this.dbName = dbName
|
|
18
|
-
this.storeName = storeName
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async clear?(): Promise<void> {
|
|
22
|
-
return await this.withDb((db) => {
|
|
23
|
-
return db.clear(this.storeName)
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async delete(key: StoreKey<T, S>): Promise<void> {
|
|
28
|
-
return await this.withDb((db) => {
|
|
29
|
-
return db.delete(this.storeName, key)
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async get(key: StoreKey<T, S>) {
|
|
34
|
-
return await this.withDb((db) => {
|
|
35
|
-
return db.get(this.storeName, key) ?? undefined
|
|
36
|
-
})
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async keys?(): Promise<StoreKey<T, S>[]> {
|
|
40
|
-
return await this.withDb((db) => {
|
|
41
|
-
return db.getAllKeys(this.storeName)
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<void> {
|
|
46
|
-
return await this.withDb(async (db) => {
|
|
47
|
-
await db.put(this.storeName, value, key)
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async withDb<R = StoreValue<T, S>>(
|
|
52
|
-
callback: (db: IDBPDatabase<T>) =>
|
|
53
|
-
Promise<R> | R,
|
|
54
|
-
) {
|
|
55
|
-
return await withDb<T, R>(this.dbName, (db) => {
|
|
56
|
-
return callback(db)
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
}
|
package/src/ObjectStore.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@xylabs/logger'
|
|
2
|
-
|
|
3
|
-
import { checkStoreNeedsUpgrade } from './checkStoreNeedsUpgrade.ts'
|
|
4
|
-
import { type IndexDescription } from './IndexDescription.ts'
|
|
5
|
-
import type { ObjectStore } from './ObjectStore.ts'
|
|
6
|
-
import { withDbByVersion } from './withDbByVersion.ts'
|
|
7
|
-
|
|
8
|
-
export async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {
|
|
9
|
-
logger?.log('checkDbNeedsUpgrade', dbName, stores)
|
|
10
|
-
return await withDbByVersion<ObjectStore, number>(dbName, async (db) => {
|
|
11
|
-
for (const [storeName, indexes] of Object.entries(stores)) {
|
|
12
|
-
if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {
|
|
13
|
-
return db.version + 1
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return db.version
|
|
17
|
-
}, undefined, stores, logger)
|
|
18
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { exists } from '@xylabs/exists'
|
|
2
|
-
import type { Logger } from '@xylabs/logger'
|
|
3
|
-
import type { IDBPDatabase } from 'idb'
|
|
4
|
-
|
|
5
|
-
import { getExistingIndexes } from './getExistingIndexes.ts'
|
|
6
|
-
import { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'
|
|
7
|
-
import type { ObjectStore } from './ObjectStore.ts'
|
|
8
|
-
|
|
9
|
-
export async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {
|
|
10
|
-
logger?.log('checkStoreNeedsUpgrade', storeName, indexes)
|
|
11
|
-
const existingIndexes = await getExistingIndexes(db, storeName, logger)
|
|
12
|
-
if (existingIndexes === null) {
|
|
13
|
-
// the store does not exist, so we need to trigger upgrade (no existing indexes)
|
|
14
|
-
return true
|
|
15
|
-
}
|
|
16
|
-
const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))
|
|
17
|
-
for (const { key, unique } of indexes) {
|
|
18
|
-
const indexName = buildStandardIndexName({ key, unique })
|
|
19
|
-
if (!existingIndexNames.has(indexName)) {
|
|
20
|
-
return true
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return false
|
|
24
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@xylabs/logger'
|
|
2
|
-
import type {
|
|
3
|
-
DBSchema,
|
|
4
|
-
IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,
|
|
5
|
-
} from 'idb'
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
buildStandardIndexName,
|
|
9
|
-
type IndexDescription,
|
|
10
|
-
} from './IndexDescription.ts'
|
|
11
|
-
|
|
12
|
-
export function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(
|
|
13
|
-
db: IDBPDatabase<DBTypes>,
|
|
14
|
-
storeName: StoreNames<DBTypes>,
|
|
15
|
-
indexes: IndexDescription[],
|
|
16
|
-
logger?: Logger,
|
|
17
|
-
) {
|
|
18
|
-
logger?.log(`Creating store ${storeName}`)
|
|
19
|
-
// Create the store
|
|
20
|
-
let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined
|
|
21
|
-
try {
|
|
22
|
-
store = db.createObjectStore(storeName, {
|
|
23
|
-
// If it isn't explicitly set, create a value by auto incrementing.
|
|
24
|
-
autoIncrement: true,
|
|
25
|
-
})
|
|
26
|
-
} catch {
|
|
27
|
-
logger?.warn(`Failed to create store: ${storeName} already exists`)
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
logger?.log(`Creating store: created ${storeName}`)
|
|
31
|
-
// Name the store
|
|
32
|
-
store.name = storeName
|
|
33
|
-
// Create an index on the hash
|
|
34
|
-
for (const {
|
|
35
|
-
key, multiEntry, unique,
|
|
36
|
-
} of indexes) {
|
|
37
|
-
logger?.log(`Creating store: index ${key}`)
|
|
38
|
-
const indexKeys = Object.keys(key)
|
|
39
|
-
const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys
|
|
40
|
-
const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>
|
|
41
|
-
logger?.log('createIndex', indexName, keys, { multiEntry, unique })
|
|
42
|
-
store.createIndex(indexName, keys, { multiEntry, unique })
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@xylabs/logger'
|
|
2
|
-
import type { EmptyObject } from '@xylabs/object'
|
|
3
|
-
import type { IDBPDatabase, StoreNames } from 'idb'
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
type IndexDescription,
|
|
7
|
-
type IndexDirection,
|
|
8
|
-
} from './IndexDescription.ts'
|
|
9
|
-
import type { ObjectStore } from './ObjectStore.ts'
|
|
10
|
-
import { withDbByVersion } from './withDbByVersion.ts'
|
|
11
|
-
import { withReadOnlyStore } from './withReadOnlyStore.ts'
|
|
12
|
-
|
|
13
|
-
async function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(
|
|
14
|
-
db: IDBPDatabase<ObjectStore<T>>,
|
|
15
|
-
storeName: StoreNames<ObjectStore<T>>,
|
|
16
|
-
logger?: Logger,
|
|
17
|
-
): Promise<IndexDescription[] | null> {
|
|
18
|
-
logger?.log('getExistingIndexesInternal', storeName)
|
|
19
|
-
return await withReadOnlyStore(db, storeName, (store) => {
|
|
20
|
-
return store
|
|
21
|
-
? [...store.indexNames].map((indexName) => {
|
|
22
|
-
const index = store.index(indexName)
|
|
23
|
-
const key: Record<string, IndexDirection> = {}
|
|
24
|
-
if (Array.isArray(index.keyPath)) {
|
|
25
|
-
for (const keyPath of index.keyPath) {
|
|
26
|
-
key[keyPath] = 1
|
|
27
|
-
}
|
|
28
|
-
} else {
|
|
29
|
-
key[index.keyPath] = 1
|
|
30
|
-
}
|
|
31
|
-
const desc: IndexDescription = {
|
|
32
|
-
key,
|
|
33
|
-
unique: index.unique,
|
|
34
|
-
multiEntry: index.multiEntry,
|
|
35
|
-
}
|
|
36
|
-
return desc
|
|
37
|
-
})
|
|
38
|
-
: null
|
|
39
|
-
}, logger)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function getExistingIndexes<T extends EmptyObject = EmptyObject>(
|
|
43
|
-
db: IDBPDatabase<ObjectStore<T>> | string,
|
|
44
|
-
storeName: StoreNames<ObjectStore<T>>,
|
|
45
|
-
logger?: Logger,
|
|
46
|
-
): Promise<IndexDescription[] | null> {
|
|
47
|
-
logger?.log('getExistingIndexes', storeName)
|
|
48
|
-
if (typeof db === 'string') {
|
|
49
|
-
return await withDbByVersion<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {
|
|
50
|
-
return await getExistingIndexesInternal(db, storeName, logger)
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
return await getExistingIndexesInternal(db, storeName, logger)
|
|
54
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export * from './checkDbNeedsUpgrade.ts'
|
|
2
|
-
export * from './createStoreDuringUpgrade.ts'
|
|
3
|
-
export * from './getExistingIndexes.ts'
|
|
4
|
-
export * from './IndexDescription.ts'
|
|
5
|
-
export * from './IndexedDbKeyValueStore.ts'
|
|
6
|
-
export * from './ObjectStore.ts'
|
|
7
|
-
export * from './withDb.ts'
|
|
8
|
-
export * from './withDbByVersion.ts'
|
|
9
|
-
export * from './withReadOnlyStore.ts'
|
|
10
|
-
export * from './withReadWriteStore.ts'
|
|
11
|
-
export * from './withStore.ts'
|
package/src/withDb.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@xylabs/logger'
|
|
2
|
-
import type { EmptyObject } from '@xylabs/object'
|
|
3
|
-
import { Mutex } from 'async-mutex'
|
|
4
|
-
import type { DBSchema, IDBPDatabase } from 'idb'
|
|
5
|
-
|
|
6
|
-
import { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'
|
|
7
|
-
import { type IndexDescription } from './IndexDescription.ts'
|
|
8
|
-
import { withDbByVersion } from './withDbByVersion.ts'
|
|
9
|
-
|
|
10
|
-
const dbMutexes: Record<string, Mutex> = {}
|
|
11
|
-
|
|
12
|
-
export async function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(
|
|
13
|
-
dbName: string,
|
|
14
|
-
callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,
|
|
15
|
-
expectedIndexes?: Record<string, IndexDescription[]>,
|
|
16
|
-
logger?: Logger,
|
|
17
|
-
lock = true,
|
|
18
|
-
): Promise<R> {
|
|
19
|
-
dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()
|
|
20
|
-
const handler = async () => {
|
|
21
|
-
const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger)
|
|
22
|
-
return await withDbByVersion<DBTypes, R>(dbName, async (db) => {
|
|
23
|
-
return await callback(db)
|
|
24
|
-
}, versionToOpen, expectedIndexes, logger, lock)
|
|
25
|
-
}
|
|
26
|
-
return lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()
|
|
27
|
-
}
|
package/src/withDbByVersion.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@xylabs/logger'
|
|
2
|
-
import type { EmptyObject } from '@xylabs/object'
|
|
3
|
-
import { Mutex } from 'async-mutex'
|
|
4
|
-
import type {
|
|
5
|
-
DBSchema, IDBPDatabase, StoreNames,
|
|
6
|
-
} from 'idb'
|
|
7
|
-
import { openDB } from 'idb'
|
|
8
|
-
|
|
9
|
-
import { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'
|
|
10
|
-
import { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'
|
|
11
|
-
|
|
12
|
-
const dbMutexes: Record<string, Mutex> = {}
|
|
13
|
-
|
|
14
|
-
export async function withDbByVersion<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(
|
|
15
|
-
dbName: string,
|
|
16
|
-
callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,
|
|
17
|
-
version?: number,
|
|
18
|
-
expectedIndexes?: Record<string, IndexDescription[]>,
|
|
19
|
-
logger?: Logger,
|
|
20
|
-
lock = true,
|
|
21
|
-
): Promise<R> {
|
|
22
|
-
dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()
|
|
23
|
-
const handler = async () => {
|
|
24
|
-
const db = await openDB<DBTypes>(dbName, version, {
|
|
25
|
-
blocked(currentVersion, blockedVersion, event) {
|
|
26
|
-
logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event)
|
|
27
|
-
},
|
|
28
|
-
blocking(currentVersion, blockedVersion, event) {
|
|
29
|
-
logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event)
|
|
30
|
-
},
|
|
31
|
-
terminated() {
|
|
32
|
-
logger?.log('IndexedDb: Terminated')
|
|
33
|
-
},
|
|
34
|
-
upgrade(db, _oldVersion, _newVersion, _transaction) {
|
|
35
|
-
/* if (oldVersion !== newVersion) {
|
|
36
|
-
logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`)
|
|
37
|
-
const objectStores = transaction.objectStoreNames
|
|
38
|
-
for (const name of objectStores) {
|
|
39
|
-
try {
|
|
40
|
-
db.deleteObjectStore(name)
|
|
41
|
-
} catch {
|
|
42
|
-
console.log(`IndexedDb: Failed to delete existing object store ${name}`)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
} */
|
|
46
|
-
if (expectedIndexes) {
|
|
47
|
-
for (const [storeName, indexes] of Object.entries(expectedIndexes)) {
|
|
48
|
-
if (db.objectStoreNames.contains(storeName as StoreNames<DBTypes>)) {
|
|
49
|
-
continue
|
|
50
|
-
}
|
|
51
|
-
const indexesToCreate = indexes.map(idx => ({
|
|
52
|
-
...idx,
|
|
53
|
-
name: buildStandardIndexName(idx),
|
|
54
|
-
// eslint-disable-next-line unicorn/no-array-reduce
|
|
55
|
-
})).reduce((acc, idx) => acc.set(idx.name, idx), new Map<string, IndexDescription>()).values()
|
|
56
|
-
createStoreDuringUpgrade(db, storeName as StoreNames<DBTypes>, [...indexesToCreate], logger)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
})
|
|
61
|
-
return db
|
|
62
|
-
}
|
|
63
|
-
const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler()
|
|
64
|
-
try {
|
|
65
|
-
return await callback(db)
|
|
66
|
-
} finally {
|
|
67
|
-
db.close()
|
|
68
|
-
}
|
|
69
|
-
}
|
package/src/withReadOnlyStore.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@xylabs/logger'
|
|
2
|
-
import type { EmptyObject } from '@xylabs/object'
|
|
3
|
-
import type {
|
|
4
|
-
IDBPDatabase, IDBPObjectStore, StoreNames,
|
|
5
|
-
} from 'idb'
|
|
6
|
-
|
|
7
|
-
import type { ObjectStore } from './ObjectStore.ts'
|
|
8
|
-
import { withStore } from './withStore.ts'
|
|
9
|
-
|
|
10
|
-
export async function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(
|
|
11
|
-
db: IDBPDatabase<ObjectStore<T>>,
|
|
12
|
-
storeName: StoreNames<ObjectStore<T>>,
|
|
13
|
-
callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R,
|
|
14
|
-
logger?: Logger,
|
|
15
|
-
): Promise<R> {
|
|
16
|
-
return await withStore(db, storeName, callback, 'readonly', logger)
|
|
17
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@xylabs/logger'
|
|
2
|
-
import type { EmptyObject } from '@xylabs/object'
|
|
3
|
-
import type {
|
|
4
|
-
IDBPDatabase, IDBPObjectStore, StoreNames,
|
|
5
|
-
} from 'idb'
|
|
6
|
-
|
|
7
|
-
import type { ObjectStore } from './ObjectStore.ts'
|
|
8
|
-
import { withStore } from './withStore.ts'
|
|
9
|
-
|
|
10
|
-
export async function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(
|
|
11
|
-
db: IDBPDatabase<ObjectStore<T>>,
|
|
12
|
-
storeName: StoreNames<ObjectStore<T>>,
|
|
13
|
-
callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R,
|
|
14
|
-
logger?: Logger,
|
|
15
|
-
): Promise<R> {
|
|
16
|
-
return await withStore(db, storeName, callback, 'readwrite', logger)
|
|
17
|
-
}
|
package/src/withStore.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Logger } from '@xylabs/logger'
|
|
2
|
-
import type { EmptyObject } from '@xylabs/object'
|
|
3
|
-
import type {
|
|
4
|
-
IDBPDatabase, IDBPObjectStore, IDBPTransaction, StoreNames,
|
|
5
|
-
} from 'idb'
|
|
6
|
-
|
|
7
|
-
import type { ObjectStore } from './ObjectStore.ts'
|
|
8
|
-
|
|
9
|
-
export async function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(
|
|
10
|
-
db: IDBPDatabase<ObjectStore<T>>,
|
|
11
|
-
storeName: StoreNames<ObjectStore<T>>,
|
|
12
|
-
callback: (store: IDBPObjectStore<ObjectStore<T>,
|
|
13
|
-
[StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R,
|
|
14
|
-
mode: M,
|
|
15
|
-
logger?: Logger,
|
|
16
|
-
): Promise<R> {
|
|
17
|
-
logger?.log('withStore', storeName, mode)
|
|
18
|
-
let transaction: IDBPTransaction<ObjectStore<T>, [StoreNames<ObjectStore<T>>], M> | undefined = undefined
|
|
19
|
-
logger?.log('withStore:transaction')
|
|
20
|
-
let store = null
|
|
21
|
-
try {
|
|
22
|
-
transaction = db.transaction(storeName, mode)
|
|
23
|
-
// we do this in a try/catch because the store might not exist
|
|
24
|
-
store = transaction.objectStore(storeName)
|
|
25
|
-
} catch (ex) {
|
|
26
|
-
logger?.log('withStore:catch', ex)
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
return await callback(store)
|
|
30
|
-
} finally {
|
|
31
|
-
logger?.log('withStore:finally')
|
|
32
|
-
await transaction?.done
|
|
33
|
-
}
|
|
34
|
-
}
|