@xylabs/indexed-db 6.1.1 → 6.1.3

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.
@@ -7,5 +7,5 @@ import { type IndexDescription } from './IndexDescription.ts';
7
7
  * @param logger Optional logger for diagnostics
8
8
  * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)
9
9
  */
10
- export declare function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger): Promise<number>;
10
+ export declare function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger, lock?: boolean): Promise<number>;
11
11
  //# sourceMappingURL=checkDbNeedsUpgrade.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkDbNeedsUpgrade.d.ts","sourceRoot":"","sources":["../../src/checkDbNeedsUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAG5C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAI7D;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,mBAUpH"}
1
+ {"version":3,"file":"checkDbNeedsUpgrade.d.ts","sourceRoot":"","sources":["../../src/checkDbNeedsUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAG5C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAI7D;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EAC1C,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,mBAWZ"}
@@ -0,0 +1,14 @@
1
+ import { Mutex } from 'async-mutex';
2
+ /**
3
+ * Returns the shared mutex for a database name, creating one if needed.
4
+ * @param dbName The IndexedDB database name
5
+ */
6
+ export declare function getDbMutex(dbName: string): Mutex;
7
+ /**
8
+ * Runs a handler under the shared database mutex when locking is enabled.
9
+ * @param dbName The IndexedDB database name
10
+ * @param lock Whether to acquire the mutex before running the handler
11
+ * @param handler The async function to execute
12
+ */
13
+ export declare function withDbLock<R>(dbName: string, lock: boolean, handler: () => Promise<R>): Promise<R>;
14
+ //# sourceMappingURL=dbMutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbMutex.d.ts","sourceRoot":"","sources":["../../src/dbMutex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAInC;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,SAGxC;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,cAK3F"}
@@ -2,7 +2,6 @@
2
2
  import { exists } from "@xylabs/exists";
3
3
 
4
4
  // src/withDbByVersion.ts
5
- import { Mutex } from "async-mutex";
6
5
  import { openDB } from "idb";
7
6
 
8
7
  // src/IndexDescription.ts
@@ -43,12 +42,24 @@ function createStoreDuringUpgrade(db, storeName, indexes, logger) {
43
42
  }
44
43
  }
45
44
 
46
- // src/withDbByVersion.ts
45
+ // src/dbMutex.ts
46
+ import { Mutex } from "async-mutex";
47
47
  var dbMutexes = {};
48
- async function withDbByVersion(dbName, callback, version, expectedIndexes, logger, lock = true) {
48
+ function getDbMutex(dbName) {
49
49
  dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex();
50
- const handler = async () => {
51
- const db2 = await openDB(dbName, version, {
50
+ return dbMutexes[dbName];
51
+ }
52
+ async function withDbLock(dbName, lock, handler) {
53
+ if (!lock) {
54
+ return await handler();
55
+ }
56
+ return await getDbMutex(dbName).runExclusive(handler);
57
+ }
58
+
59
+ // src/withDbByVersion.ts
60
+ async function withDbByVersion(dbName, callback, version, expectedIndexes, logger, lock = true) {
61
+ const db = await withDbLock(dbName, lock, async () => {
62
+ return await openDB(dbName, version, {
52
63
  /* v8 ignore next 2 */
53
64
  blocked(currentVersion, blockedVersion, event) {
54
65
  logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event);
@@ -61,10 +72,10 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
61
72
  terminated() {
62
73
  logger?.log("IndexedDb: Terminated");
63
74
  },
64
- upgrade(db3, _oldVersion, _newVersion, _transaction) {
75
+ upgrade(db2, _oldVersion, _newVersion, _transaction) {
65
76
  if (expectedIndexes) {
66
77
  for (const [storeName, indexes] of Object.entries(expectedIndexes)) {
67
- if (db3.objectStoreNames.contains(storeName)) {
78
+ if (db2.objectStoreNames.contains(storeName)) {
68
79
  continue;
69
80
  }
70
81
  const indexesToCreate = indexes.map((idx) => ({
@@ -72,14 +83,12 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
72
83
  name: buildStandardIndexName(idx)
73
84
  // eslint-disable-next-line unicorn/no-array-reduce
74
85
  })).reduce((acc, idx) => acc.set(idx.name, idx), /* @__PURE__ */ new Map()).values();
75
- createStoreDuringUpgrade(db3, storeName, [...indexesToCreate], logger);
86
+ createStoreDuringUpgrade(db2, storeName, [...indexesToCreate], logger);
76
87
  }
77
88
  }
78
89
  }
79
90
  });
80
- return db2;
81
- };
82
- const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler();
91
+ });
83
92
  try {
84
93
  return await callback(db);
85
94
  } finally {
@@ -164,7 +173,7 @@ async function checkStoreNeedsUpgrade(db, storeName, indexes, logger) {
164
173
  }
165
174
 
166
175
  // src/checkDbNeedsUpgrade.ts
167
- async function checkDbNeedsUpgrade(dbName, stores, logger) {
176
+ async function checkDbNeedsUpgrade(dbName, stores, logger, lock = true) {
168
177
  logger?.log("checkDbNeedsUpgrade", dbName, stores);
169
178
  return await withDbByVersion(dbName, async (db) => {
170
179
  for (const [storeName, indexes] of Object.entries(stores)) {
@@ -173,21 +182,17 @@ async function checkDbNeedsUpgrade(dbName, stores, logger) {
173
182
  }
174
183
  }
175
184
  return db.version;
176
- }, void 0, stores, logger);
185
+ }, void 0, stores, logger, lock);
177
186
  }
178
187
 
179
188
  // src/withDb.ts
180
- import { Mutex as Mutex2 } from "async-mutex";
181
- var dbMutexes2 = {};
182
189
  async function withDb(dbName, callback, expectedIndexes, logger, lock = true) {
183
- dbMutexes2[dbName] = dbMutexes2[dbName] ?? new Mutex2();
184
- const handler = async () => {
185
- const versionToOpen = expectedIndexes === void 0 ? void 0 : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger);
190
+ return await withDbLock(dbName, lock, async () => {
191
+ const versionToOpen = expectedIndexes === void 0 ? void 0 : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger, false);
186
192
  return await withDbByVersion(dbName, async (db) => {
187
193
  return await callback(db);
188
- }, versionToOpen, expectedIndexes, logger, lock);
189
- };
190
- return lock ? await dbMutexes2[dbName].runExclusive(handler) : await handler();
194
+ }, versionToOpen, expectedIndexes, logger, false);
195
+ });
191
196
  }
192
197
 
193
198
  // src/IndexedDbKeyValueStore.ts
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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"],
4
- "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'\n\n/**\n * Checks whether a store needs an upgrade by comparing its existing indexes against expected indexes.\n * @param db The IndexedDB database instance\n * @param storeName The name of the store to check\n * @param indexes The expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns True if the store is missing or has missing indexes\n */\nexport async function checkStoreNeedsUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\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 { IDBPDatabase, StoreNames } 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\n/**\n * Opens an IndexedDB database at a specific version, handling upgrade events, and passes it to the callback.\n * The database is automatically closed after the callback completes.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param version Optional specific version to open (undefined for latest)\n * @param expectedIndexes Optional map of store names to indexes to create during upgrade\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDbByVersion<DBTypes = 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 interface 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\n/** Separator used between key names when building standard index names. */\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 IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\n/**\n * Creates an object store with the specified indexes during a version upgrade transaction.\n * @param db The IndexedDB database instance (during upgrade)\n * @param storeName The name of the store to create\n * @param indexes The index descriptions to create on the store\n * @param logger Optional logger for diagnostics\n */\nexport function createStoreDuringUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${String(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: ${String(storeName)} already exists`)\n return\n }\n logger?.log(`Creating store: created ${String(storeName)}`)\n // Name the store\n store.name = String(storeName)\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${JSON.stringify(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\n/**\n * Opens a transaction on the specified store with the given mode and passes the store to the callback.\n * If the store does not exist, the callback receives null.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the store (or null if it doesn't exist)\n * @param mode The transaction mode ('readonly' or 'readwrite')\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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\n/**\n * Opens a read-only transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-only store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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 { 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<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n const typedStoreName = storeName as StoreNames<DBTypes>\n return await withReadOnlyStore(db as unknown as IDBPDatabase<ObjectStore>, typedStoreName as StoreNames<ObjectStore>, (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\n/**\n * Retrieves the existing index descriptions for a store. Accepts either a database instance or a database name.\n * @param db The IndexedDB database instance or database name\n * @param storeName The name of the store to inspect\n * @param logger Optional logger for diagnostics\n * @returns An array of index descriptions, or null if the store does not exist\n */\nexport async function getExistingIndexes<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes> | string,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<DBTypes, 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\n/**\n * Checks whether any store in the database needs an upgrade and returns the appropriate version number.\n * @param dbName The name of the database to check\n * @param stores Map of store names to their expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)\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 { 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\n/**\n * Opens an IndexedDB database, automatically upgrading if needed, and passes it to the callback.\n * Uses a mutex to serialize access to the same database by default.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param expectedIndexes Optional map of store names to their expected indexes (triggers upgrade check)\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDb<DBTypes = 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 /** The name of the IndexedDB database. */\n readonly dbName: string\n /** The name of the object store within the database. */\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n /** Removes all entries from the store. */\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n /**\n * Deletes the entry with the given key.\n * @param key The key of the entry to delete\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 /**\n * Retrieves the value associated with the given key.\n * @param key The key to look up\n * @returns The value, or undefined if not found\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 /** Returns all keys in the store. */\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n /**\n * Sets a value for the given key, creating or updating the entry.\n * @param key The key to set\n * @param value The value to store\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 /**\n * Opens the underlying IndexedDB database and passes it to the callback.\n * @param callback Function to execute with the database\n * @returns The result of the callback\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\n/**\n * Opens a read-write transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-write store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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"],
5
- "mappings": "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",
6
- "names": ["db", "db", "Mutex", "dbMutexes", "Mutex"]
3
+ "sources": ["../../src/checkStoreNeedsUpgrade.ts", "../../src/withDbByVersion.ts", "../../src/IndexDescription.ts", "../../src/createStoreDuringUpgrade.ts", "../../src/dbMutex.ts", "../../src/withStore.ts", "../../src/withReadOnlyStore.ts", "../../src/getExistingIndexes.ts", "../../src/checkDbNeedsUpgrade.ts", "../../src/withDb.ts", "../../src/IndexedDbKeyValueStore.ts", "../../src/withReadWriteStore.ts"],
4
+ "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'\n\n/**\n * Checks whether a store needs an upgrade by comparing its existing indexes against expected indexes.\n * @param db The IndexedDB database instance\n * @param storeName The name of the store to check\n * @param indexes The expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns True if the store is missing or has missing indexes\n */\nexport async function checkStoreNeedsUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\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 type { IDBPDatabase, StoreNames } from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { withDbLock } from './dbMutex.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\n/**\n * Opens an IndexedDB database at a specific version, handling upgrade events, and passes it to the callback.\n * The database is automatically closed after the callback completes.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param version Optional specific version to open (undefined for latest)\n * @param expectedIndexes Optional map of store names to indexes to create during upgrade\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDbByVersion<DBTypes = 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 const db = await withDbLock(dbName, lock, async () => {\n return 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 })\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 interface 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\n/** Separator used between key names when building standard index names. */\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 IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\n/**\n * Creates an object store with the specified indexes during a version upgrade transaction.\n * @param db The IndexedDB database instance (during upgrade)\n * @param storeName The name of the store to create\n * @param indexes The index descriptions to create on the store\n * @param logger Optional logger for diagnostics\n */\nexport function createStoreDuringUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${String(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: ${String(storeName)} already exists`)\n return\n }\n logger?.log(`Creating store: created ${String(storeName)}`)\n // Name the store\n store.name = String(storeName)\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${JSON.stringify(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 { Mutex } from 'async-mutex'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\n/**\n * Returns the shared mutex for a database name, creating one if needed.\n * @param dbName The IndexedDB database name\n */\nexport function getDbMutex(dbName: string) {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n return dbMutexes[dbName]\n}\n\n/**\n * Runs a handler under the shared database mutex when locking is enabled.\n * @param dbName The IndexedDB database name\n * @param lock Whether to acquire the mutex before running the handler\n * @param handler The async function to execute\n */\nexport async function withDbLock<R>(dbName: string, lock: boolean, handler: () => Promise<R>) {\n if (!lock) {\n return await handler()\n }\n return await getDbMutex(dbName).runExclusive(handler)\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\n/**\n * Opens a transaction on the specified store with the given mode and passes the store to the callback.\n * If the store does not exist, the callback receives null.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the store (or null if it doesn't exist)\n * @param mode The transaction mode ('readonly' or 'readwrite')\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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\n/**\n * Opens a read-only transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-only store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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 { 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<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n const typedStoreName = storeName as StoreNames<DBTypes>\n return await withReadOnlyStore(db as unknown as IDBPDatabase<ObjectStore>, typedStoreName as StoreNames<ObjectStore>, (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\n/**\n * Retrieves the existing index descriptions for a store. Accepts either a database instance or a database name.\n * @param db The IndexedDB database instance or database name\n * @param storeName The name of the store to inspect\n * @param logger Optional logger for diagnostics\n * @returns An array of index descriptions, or null if the store does not exist\n */\nexport async function getExistingIndexes<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes> | string,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<DBTypes, 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\n/**\n * Checks whether any store in the database needs an upgrade and returns the appropriate version number.\n * @param dbName The name of the database to check\n * @param stores Map of store names to their expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)\n */\nexport async function checkDbNeedsUpgrade(\n dbName: string,\n stores: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n) {\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, lock)\n}\n", "import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { withDbLock } from './dbMutex.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\n/**\n * Opens an IndexedDB database, automatically upgrading if needed, and passes it to the callback.\n * Uses a mutex to serialize access to the same database by default.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param expectedIndexes Optional map of store names to their expected indexes (triggers upgrade check)\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDb<DBTypes = 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 return await withDbLock(dbName, lock, async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger, false)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, false)\n })\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 /** The name of the IndexedDB database. */\n readonly dbName: string\n /** The name of the object store within the database. */\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n /** Removes all entries from the store. */\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n /**\n * Deletes the entry with the given key.\n * @param key The key of the entry to delete\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 /**\n * Retrieves the value associated with the given key.\n * @param key The key to look up\n * @returns The value, or undefined if not found\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 /** Returns all keys in the store. */\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n /**\n * Sets a value for the given key, creating or updating the entry.\n * @param key The key to set\n * @param value The value to store\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 /**\n * Opens the underlying IndexedDB database and passes it to the callback.\n * @param callback Function to execute with the database\n * @returns The result of the callback\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\n/**\n * Opens a read-write transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-write store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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"],
5
+ "mappings": "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",
6
+ "names": ["db", "db"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"withDb.d.ts","sourceRoot":"","sources":["../../src/withDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAGvC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAK7D;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EAC7D,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CASZ"}
1
+ {"version":3,"file":"withDb.d.ts","sourceRoot":"","sources":["../../src/withDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAIvC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EAC7D,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CAOZ"}
@@ -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,EAAE,YAAY,EAAc,MAAM,KAAK,CAAA;AAInD,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACtE,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
+ {"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;AACjD,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,KAAK,CAAA;AAKnD,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAErF;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACtE,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,CAgDZ"}
@@ -7,5 +7,5 @@ import { type IndexDescription } from './IndexDescription.ts';
7
7
  * @param logger Optional logger for diagnostics
8
8
  * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)
9
9
  */
10
- export declare function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger): Promise<number>;
10
+ export declare function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger, lock?: boolean): Promise<number>;
11
11
  //# sourceMappingURL=checkDbNeedsUpgrade.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkDbNeedsUpgrade.d.ts","sourceRoot":"","sources":["../../src/checkDbNeedsUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAG5C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAI7D;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,mBAUpH"}
1
+ {"version":3,"file":"checkDbNeedsUpgrade.d.ts","sourceRoot":"","sources":["../../src/checkDbNeedsUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAG5C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAI7D;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EAC1C,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,mBAWZ"}
@@ -0,0 +1,14 @@
1
+ import { Mutex } from 'async-mutex';
2
+ /**
3
+ * Returns the shared mutex for a database name, creating one if needed.
4
+ * @param dbName The IndexedDB database name
5
+ */
6
+ export declare function getDbMutex(dbName: string): Mutex;
7
+ /**
8
+ * Runs a handler under the shared database mutex when locking is enabled.
9
+ * @param dbName The IndexedDB database name
10
+ * @param lock Whether to acquire the mutex before running the handler
11
+ * @param handler The async function to execute
12
+ */
13
+ export declare function withDbLock<R>(dbName: string, lock: boolean, handler: () => Promise<R>): Promise<R>;
14
+ //# sourceMappingURL=dbMutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbMutex.d.ts","sourceRoot":"","sources":["../../src/dbMutex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAInC;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,SAGxC;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,cAK3F"}
@@ -2,7 +2,6 @@
2
2
  import { exists } from "@xylabs/exists";
3
3
 
4
4
  // src/withDbByVersion.ts
5
- import { Mutex } from "async-mutex";
6
5
  import { openDB } from "idb";
7
6
 
8
7
  // src/IndexDescription.ts
@@ -43,12 +42,24 @@ function createStoreDuringUpgrade(db, storeName, indexes, logger) {
43
42
  }
44
43
  }
45
44
 
46
- // src/withDbByVersion.ts
45
+ // src/dbMutex.ts
46
+ import { Mutex } from "async-mutex";
47
47
  var dbMutexes = {};
48
- async function withDbByVersion(dbName, callback, version, expectedIndexes, logger, lock = true) {
48
+ function getDbMutex(dbName) {
49
49
  dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex();
50
- const handler = async () => {
51
- const db2 = await openDB(dbName, version, {
50
+ return dbMutexes[dbName];
51
+ }
52
+ async function withDbLock(dbName, lock, handler) {
53
+ if (!lock) {
54
+ return await handler();
55
+ }
56
+ return await getDbMutex(dbName).runExclusive(handler);
57
+ }
58
+
59
+ // src/withDbByVersion.ts
60
+ async function withDbByVersion(dbName, callback, version, expectedIndexes, logger, lock = true) {
61
+ const db = await withDbLock(dbName, lock, async () => {
62
+ return await openDB(dbName, version, {
52
63
  /* v8 ignore next 2 */
53
64
  blocked(currentVersion, blockedVersion, event) {
54
65
  logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event);
@@ -61,10 +72,10 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
61
72
  terminated() {
62
73
  logger?.log("IndexedDb: Terminated");
63
74
  },
64
- upgrade(db3, _oldVersion, _newVersion, _transaction) {
75
+ upgrade(db2, _oldVersion, _newVersion, _transaction) {
65
76
  if (expectedIndexes) {
66
77
  for (const [storeName, indexes] of Object.entries(expectedIndexes)) {
67
- if (db3.objectStoreNames.contains(storeName)) {
78
+ if (db2.objectStoreNames.contains(storeName)) {
68
79
  continue;
69
80
  }
70
81
  const indexesToCreate = indexes.map((idx) => ({
@@ -72,14 +83,12 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
72
83
  name: buildStandardIndexName(idx)
73
84
  // eslint-disable-next-line unicorn/no-array-reduce
74
85
  })).reduce((acc, idx) => acc.set(idx.name, idx), /* @__PURE__ */ new Map()).values();
75
- createStoreDuringUpgrade(db3, storeName, [...indexesToCreate], logger);
86
+ createStoreDuringUpgrade(db2, storeName, [...indexesToCreate], logger);
76
87
  }
77
88
  }
78
89
  }
79
90
  });
80
- return db2;
81
- };
82
- const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler();
91
+ });
83
92
  try {
84
93
  return await callback(db);
85
94
  } finally {
@@ -164,7 +173,7 @@ async function checkStoreNeedsUpgrade(db, storeName, indexes, logger) {
164
173
  }
165
174
 
166
175
  // src/checkDbNeedsUpgrade.ts
167
- async function checkDbNeedsUpgrade(dbName, stores, logger) {
176
+ async function checkDbNeedsUpgrade(dbName, stores, logger, lock = true) {
168
177
  logger?.log("checkDbNeedsUpgrade", dbName, stores);
169
178
  return await withDbByVersion(dbName, async (db) => {
170
179
  for (const [storeName, indexes] of Object.entries(stores)) {
@@ -173,21 +182,17 @@ async function checkDbNeedsUpgrade(dbName, stores, logger) {
173
182
  }
174
183
  }
175
184
  return db.version;
176
- }, void 0, stores, logger);
185
+ }, void 0, stores, logger, lock);
177
186
  }
178
187
 
179
188
  // src/withDb.ts
180
- import { Mutex as Mutex2 } from "async-mutex";
181
- var dbMutexes2 = {};
182
189
  async function withDb(dbName, callback, expectedIndexes, logger, lock = true) {
183
- dbMutexes2[dbName] = dbMutexes2[dbName] ?? new Mutex2();
184
- const handler = async () => {
185
- const versionToOpen = expectedIndexes === void 0 ? void 0 : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger);
190
+ return await withDbLock(dbName, lock, async () => {
191
+ const versionToOpen = expectedIndexes === void 0 ? void 0 : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger, false);
186
192
  return await withDbByVersion(dbName, async (db) => {
187
193
  return await callback(db);
188
- }, versionToOpen, expectedIndexes, logger, lock);
189
- };
190
- return lock ? await dbMutexes2[dbName].runExclusive(handler) : await handler();
194
+ }, versionToOpen, expectedIndexes, logger, false);
195
+ });
191
196
  }
192
197
 
193
198
  // src/IndexedDbKeyValueStore.ts
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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"],
4
- "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'\n\n/**\n * Checks whether a store needs an upgrade by comparing its existing indexes against expected indexes.\n * @param db The IndexedDB database instance\n * @param storeName The name of the store to check\n * @param indexes The expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns True if the store is missing or has missing indexes\n */\nexport async function checkStoreNeedsUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\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 { IDBPDatabase, StoreNames } 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\n/**\n * Opens an IndexedDB database at a specific version, handling upgrade events, and passes it to the callback.\n * The database is automatically closed after the callback completes.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param version Optional specific version to open (undefined for latest)\n * @param expectedIndexes Optional map of store names to indexes to create during upgrade\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDbByVersion<DBTypes = 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 interface 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\n/** Separator used between key names when building standard index names. */\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 IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\n/**\n * Creates an object store with the specified indexes during a version upgrade transaction.\n * @param db The IndexedDB database instance (during upgrade)\n * @param storeName The name of the store to create\n * @param indexes The index descriptions to create on the store\n * @param logger Optional logger for diagnostics\n */\nexport function createStoreDuringUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${String(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: ${String(storeName)} already exists`)\n return\n }\n logger?.log(`Creating store: created ${String(storeName)}`)\n // Name the store\n store.name = String(storeName)\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${JSON.stringify(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\n/**\n * Opens a transaction on the specified store with the given mode and passes the store to the callback.\n * If the store does not exist, the callback receives null.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the store (or null if it doesn't exist)\n * @param mode The transaction mode ('readonly' or 'readwrite')\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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\n/**\n * Opens a read-only transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-only store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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 { 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<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n const typedStoreName = storeName as StoreNames<DBTypes>\n return await withReadOnlyStore(db as unknown as IDBPDatabase<ObjectStore>, typedStoreName as StoreNames<ObjectStore>, (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\n/**\n * Retrieves the existing index descriptions for a store. Accepts either a database instance or a database name.\n * @param db The IndexedDB database instance or database name\n * @param storeName The name of the store to inspect\n * @param logger Optional logger for diagnostics\n * @returns An array of index descriptions, or null if the store does not exist\n */\nexport async function getExistingIndexes<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes> | string,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<DBTypes, 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\n/**\n * Checks whether any store in the database needs an upgrade and returns the appropriate version number.\n * @param dbName The name of the database to check\n * @param stores Map of store names to their expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)\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 { 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\n/**\n * Opens an IndexedDB database, automatically upgrading if needed, and passes it to the callback.\n * Uses a mutex to serialize access to the same database by default.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param expectedIndexes Optional map of store names to their expected indexes (triggers upgrade check)\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDb<DBTypes = 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 /** The name of the IndexedDB database. */\n readonly dbName: string\n /** The name of the object store within the database. */\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n /** Removes all entries from the store. */\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n /**\n * Deletes the entry with the given key.\n * @param key The key of the entry to delete\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 /**\n * Retrieves the value associated with the given key.\n * @param key The key to look up\n * @returns The value, or undefined if not found\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 /** Returns all keys in the store. */\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n /**\n * Sets a value for the given key, creating or updating the entry.\n * @param key The key to set\n * @param value The value to store\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 /**\n * Opens the underlying IndexedDB database and passes it to the callback.\n * @param callback Function to execute with the database\n * @returns The result of the callback\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\n/**\n * Opens a read-write transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-write store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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"],
5
- "mappings": "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",
6
- "names": ["db", "db", "Mutex", "dbMutexes", "Mutex"]
3
+ "sources": ["../../src/checkStoreNeedsUpgrade.ts", "../../src/withDbByVersion.ts", "../../src/IndexDescription.ts", "../../src/createStoreDuringUpgrade.ts", "../../src/dbMutex.ts", "../../src/withStore.ts", "../../src/withReadOnlyStore.ts", "../../src/getExistingIndexes.ts", "../../src/checkDbNeedsUpgrade.ts", "../../src/withDb.ts", "../../src/IndexedDbKeyValueStore.ts", "../../src/withReadWriteStore.ts"],
4
+ "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'\n\n/**\n * Checks whether a store needs an upgrade by comparing its existing indexes against expected indexes.\n * @param db The IndexedDB database instance\n * @param storeName The name of the store to check\n * @param indexes The expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns True if the store is missing or has missing indexes\n */\nexport async function checkStoreNeedsUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\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 type { IDBPDatabase, StoreNames } from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { withDbLock } from './dbMutex.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\n/**\n * Opens an IndexedDB database at a specific version, handling upgrade events, and passes it to the callback.\n * The database is automatically closed after the callback completes.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param version Optional specific version to open (undefined for latest)\n * @param expectedIndexes Optional map of store names to indexes to create during upgrade\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDbByVersion<DBTypes = 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 const db = await withDbLock(dbName, lock, async () => {\n return 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 })\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 interface 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\n/** Separator used between key names when building standard index names. */\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 IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\n/**\n * Creates an object store with the specified indexes during a version upgrade transaction.\n * @param db The IndexedDB database instance (during upgrade)\n * @param storeName The name of the store to create\n * @param indexes The index descriptions to create on the store\n * @param logger Optional logger for diagnostics\n */\nexport function createStoreDuringUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${String(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: ${String(storeName)} already exists`)\n return\n }\n logger?.log(`Creating store: created ${String(storeName)}`)\n // Name the store\n store.name = String(storeName)\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${JSON.stringify(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 { Mutex } from 'async-mutex'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\n/**\n * Returns the shared mutex for a database name, creating one if needed.\n * @param dbName The IndexedDB database name\n */\nexport function getDbMutex(dbName: string) {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n return dbMutexes[dbName]\n}\n\n/**\n * Runs a handler under the shared database mutex when locking is enabled.\n * @param dbName The IndexedDB database name\n * @param lock Whether to acquire the mutex before running the handler\n * @param handler The async function to execute\n */\nexport async function withDbLock<R>(dbName: string, lock: boolean, handler: () => Promise<R>) {\n if (!lock) {\n return await handler()\n }\n return await getDbMutex(dbName).runExclusive(handler)\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\n/**\n * Opens a transaction on the specified store with the given mode and passes the store to the callback.\n * If the store does not exist, the callback receives null.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the store (or null if it doesn't exist)\n * @param mode The transaction mode ('readonly' or 'readwrite')\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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\n/**\n * Opens a read-only transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-only store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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 { 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<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n const typedStoreName = storeName as StoreNames<DBTypes>\n return await withReadOnlyStore(db as unknown as IDBPDatabase<ObjectStore>, typedStoreName as StoreNames<ObjectStore>, (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\n/**\n * Retrieves the existing index descriptions for a store. Accepts either a database instance or a database name.\n * @param db The IndexedDB database instance or database name\n * @param storeName The name of the store to inspect\n * @param logger Optional logger for diagnostics\n * @returns An array of index descriptions, or null if the store does not exist\n */\nexport async function getExistingIndexes<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes> | string,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<DBTypes, 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\n/**\n * Checks whether any store in the database needs an upgrade and returns the appropriate version number.\n * @param dbName The name of the database to check\n * @param stores Map of store names to their expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)\n */\nexport async function checkDbNeedsUpgrade(\n dbName: string,\n stores: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n) {\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, lock)\n}\n", "import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { withDbLock } from './dbMutex.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\n/**\n * Opens an IndexedDB database, automatically upgrading if needed, and passes it to the callback.\n * Uses a mutex to serialize access to the same database by default.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param expectedIndexes Optional map of store names to their expected indexes (triggers upgrade check)\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDb<DBTypes = 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 return await withDbLock(dbName, lock, async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger, false)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, false)\n })\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 /** The name of the IndexedDB database. */\n readonly dbName: string\n /** The name of the object store within the database. */\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n /** Removes all entries from the store. */\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n /**\n * Deletes the entry with the given key.\n * @param key The key of the entry to delete\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 /**\n * Retrieves the value associated with the given key.\n * @param key The key to look up\n * @returns The value, or undefined if not found\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 /** Returns all keys in the store. */\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n /**\n * Sets a value for the given key, creating or updating the entry.\n * @param key The key to set\n * @param value The value to store\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 /**\n * Opens the underlying IndexedDB database and passes it to the callback.\n * @param callback Function to execute with the database\n * @returns The result of the callback\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\n/**\n * Opens a read-write transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-write store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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"],
5
+ "mappings": "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",
6
+ "names": ["db", "db"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"withDb.d.ts","sourceRoot":"","sources":["../../src/withDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAGvC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAK7D;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EAC7D,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CASZ"}
1
+ {"version":3,"file":"withDb.d.ts","sourceRoot":"","sources":["../../src/withDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAIvC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EAC7D,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CAOZ"}
@@ -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,EAAE,YAAY,EAAc,MAAM,KAAK,CAAA;AAInD,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACtE,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
+ {"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;AACjD,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,KAAK,CAAA;AAKnD,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAErF;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACtE,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,CAgDZ"}
@@ -7,5 +7,5 @@ import { type IndexDescription } from './IndexDescription.ts';
7
7
  * @param logger Optional logger for diagnostics
8
8
  * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)
9
9
  */
10
- export declare function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger): Promise<number>;
10
+ export declare function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger, lock?: boolean): Promise<number>;
11
11
  //# sourceMappingURL=checkDbNeedsUpgrade.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkDbNeedsUpgrade.d.ts","sourceRoot":"","sources":["../../src/checkDbNeedsUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAG5C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAI7D;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,mBAUpH"}
1
+ {"version":3,"file":"checkDbNeedsUpgrade.d.ts","sourceRoot":"","sources":["../../src/checkDbNeedsUpgrade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAG5C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAI7D;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EAC1C,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,mBAWZ"}
@@ -0,0 +1,14 @@
1
+ import { Mutex } from 'async-mutex';
2
+ /**
3
+ * Returns the shared mutex for a database name, creating one if needed.
4
+ * @param dbName The IndexedDB database name
5
+ */
6
+ export declare function getDbMutex(dbName: string): Mutex;
7
+ /**
8
+ * Runs a handler under the shared database mutex when locking is enabled.
9
+ * @param dbName The IndexedDB database name
10
+ * @param lock Whether to acquire the mutex before running the handler
11
+ * @param handler The async function to execute
12
+ */
13
+ export declare function withDbLock<R>(dbName: string, lock: boolean, handler: () => Promise<R>): Promise<R>;
14
+ //# sourceMappingURL=dbMutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbMutex.d.ts","sourceRoot":"","sources":["../../src/dbMutex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAInC;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,SAGxC;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,cAK3F"}
@@ -2,7 +2,6 @@
2
2
  import { exists } from "@xylabs/exists";
3
3
 
4
4
  // src/withDbByVersion.ts
5
- import { Mutex } from "async-mutex";
6
5
  import { openDB } from "idb";
7
6
 
8
7
  // src/IndexDescription.ts
@@ -43,12 +42,24 @@ function createStoreDuringUpgrade(db, storeName, indexes, logger) {
43
42
  }
44
43
  }
45
44
 
46
- // src/withDbByVersion.ts
45
+ // src/dbMutex.ts
46
+ import { Mutex } from "async-mutex";
47
47
  var dbMutexes = {};
48
- async function withDbByVersion(dbName, callback, version, expectedIndexes, logger, lock = true) {
48
+ function getDbMutex(dbName) {
49
49
  dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex();
50
- const handler = async () => {
51
- const db2 = await openDB(dbName, version, {
50
+ return dbMutexes[dbName];
51
+ }
52
+ async function withDbLock(dbName, lock, handler) {
53
+ if (!lock) {
54
+ return await handler();
55
+ }
56
+ return await getDbMutex(dbName).runExclusive(handler);
57
+ }
58
+
59
+ // src/withDbByVersion.ts
60
+ async function withDbByVersion(dbName, callback, version, expectedIndexes, logger, lock = true) {
61
+ const db = await withDbLock(dbName, lock, async () => {
62
+ return await openDB(dbName, version, {
52
63
  /* v8 ignore next 2 */
53
64
  blocked(currentVersion, blockedVersion, event) {
54
65
  logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event);
@@ -61,10 +72,10 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
61
72
  terminated() {
62
73
  logger?.log("IndexedDb: Terminated");
63
74
  },
64
- upgrade(db3, _oldVersion, _newVersion, _transaction) {
75
+ upgrade(db2, _oldVersion, _newVersion, _transaction) {
65
76
  if (expectedIndexes) {
66
77
  for (const [storeName, indexes] of Object.entries(expectedIndexes)) {
67
- if (db3.objectStoreNames.contains(storeName)) {
78
+ if (db2.objectStoreNames.contains(storeName)) {
68
79
  continue;
69
80
  }
70
81
  const indexesToCreate = indexes.map((idx) => ({
@@ -72,14 +83,12 @@ async function withDbByVersion(dbName, callback, version, expectedIndexes, logge
72
83
  name: buildStandardIndexName(idx)
73
84
  // eslint-disable-next-line unicorn/no-array-reduce
74
85
  })).reduce((acc, idx) => acc.set(idx.name, idx), /* @__PURE__ */ new Map()).values();
75
- createStoreDuringUpgrade(db3, storeName, [...indexesToCreate], logger);
86
+ createStoreDuringUpgrade(db2, storeName, [...indexesToCreate], logger);
76
87
  }
77
88
  }
78
89
  }
79
90
  });
80
- return db2;
81
- };
82
- const db = lock ? await dbMutexes[dbName].runExclusive(handler) : await handler();
91
+ });
83
92
  try {
84
93
  return await callback(db);
85
94
  } finally {
@@ -164,7 +173,7 @@ async function checkStoreNeedsUpgrade(db, storeName, indexes, logger) {
164
173
  }
165
174
 
166
175
  // src/checkDbNeedsUpgrade.ts
167
- async function checkDbNeedsUpgrade(dbName, stores, logger) {
176
+ async function checkDbNeedsUpgrade(dbName, stores, logger, lock = true) {
168
177
  logger?.log("checkDbNeedsUpgrade", dbName, stores);
169
178
  return await withDbByVersion(dbName, async (db) => {
170
179
  for (const [storeName, indexes] of Object.entries(stores)) {
@@ -173,21 +182,17 @@ async function checkDbNeedsUpgrade(dbName, stores, logger) {
173
182
  }
174
183
  }
175
184
  return db.version;
176
- }, void 0, stores, logger);
185
+ }, void 0, stores, logger, lock);
177
186
  }
178
187
 
179
188
  // src/withDb.ts
180
- import { Mutex as Mutex2 } from "async-mutex";
181
- var dbMutexes2 = {};
182
189
  async function withDb(dbName, callback, expectedIndexes, logger, lock = true) {
183
- dbMutexes2[dbName] = dbMutexes2[dbName] ?? new Mutex2();
184
- const handler = async () => {
185
- const versionToOpen = expectedIndexes === void 0 ? void 0 : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger);
190
+ return await withDbLock(dbName, lock, async () => {
191
+ const versionToOpen = expectedIndexes === void 0 ? void 0 : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger, false);
186
192
  return await withDbByVersion(dbName, async (db) => {
187
193
  return await callback(db);
188
- }, versionToOpen, expectedIndexes, logger, lock);
189
- };
190
- return lock ? await dbMutexes2[dbName].runExclusive(handler) : await handler();
194
+ }, versionToOpen, expectedIndexes, logger, false);
195
+ });
191
196
  }
192
197
 
193
198
  // src/IndexedDbKeyValueStore.ts
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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"],
4
- "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'\n\n/**\n * Checks whether a store needs an upgrade by comparing its existing indexes against expected indexes.\n * @param db The IndexedDB database instance\n * @param storeName The name of the store to check\n * @param indexes The expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns True if the store is missing or has missing indexes\n */\nexport async function checkStoreNeedsUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\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 { IDBPDatabase, StoreNames } 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\n/**\n * Opens an IndexedDB database at a specific version, handling upgrade events, and passes it to the callback.\n * The database is automatically closed after the callback completes.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param version Optional specific version to open (undefined for latest)\n * @param expectedIndexes Optional map of store names to indexes to create during upgrade\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDbByVersion<DBTypes = 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 interface 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\n/** Separator used between key names when building standard index names. */\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 IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\n/**\n * Creates an object store with the specified indexes during a version upgrade transaction.\n * @param db The IndexedDB database instance (during upgrade)\n * @param storeName The name of the store to create\n * @param indexes The index descriptions to create on the store\n * @param logger Optional logger for diagnostics\n */\nexport function createStoreDuringUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${String(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: ${String(storeName)} already exists`)\n return\n }\n logger?.log(`Creating store: created ${String(storeName)}`)\n // Name the store\n store.name = String(storeName)\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${JSON.stringify(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\n/**\n * Opens a transaction on the specified store with the given mode and passes the store to the callback.\n * If the store does not exist, the callback receives null.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the store (or null if it doesn't exist)\n * @param mode The transaction mode ('readonly' or 'readwrite')\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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\n/**\n * Opens a read-only transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-only store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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 { 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<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n const typedStoreName = storeName as StoreNames<DBTypes>\n return await withReadOnlyStore(db as unknown as IDBPDatabase<ObjectStore>, typedStoreName as StoreNames<ObjectStore>, (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\n/**\n * Retrieves the existing index descriptions for a store. Accepts either a database instance or a database name.\n * @param db The IndexedDB database instance or database name\n * @param storeName The name of the store to inspect\n * @param logger Optional logger for diagnostics\n * @returns An array of index descriptions, or null if the store does not exist\n */\nexport async function getExistingIndexes<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes> | string,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<DBTypes, 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\n/**\n * Checks whether any store in the database needs an upgrade and returns the appropriate version number.\n * @param dbName The name of the database to check\n * @param stores Map of store names to their expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)\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 { 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\n/**\n * Opens an IndexedDB database, automatically upgrading if needed, and passes it to the callback.\n * Uses a mutex to serialize access to the same database by default.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param expectedIndexes Optional map of store names to their expected indexes (triggers upgrade check)\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDb<DBTypes = 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 /** The name of the IndexedDB database. */\n readonly dbName: string\n /** The name of the object store within the database. */\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n /** Removes all entries from the store. */\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n /**\n * Deletes the entry with the given key.\n * @param key The key of the entry to delete\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 /**\n * Retrieves the value associated with the given key.\n * @param key The key to look up\n * @returns The value, or undefined if not found\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 /** Returns all keys in the store. */\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n /**\n * Sets a value for the given key, creating or updating the entry.\n * @param key The key to set\n * @param value The value to store\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 /**\n * Opens the underlying IndexedDB database and passes it to the callback.\n * @param callback Function to execute with the database\n * @returns The result of the callback\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\n/**\n * Opens a read-write transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-write store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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"],
5
- "mappings": "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",
6
- "names": ["db", "db", "Mutex", "dbMutexes", "Mutex"]
3
+ "sources": ["../../src/checkStoreNeedsUpgrade.ts", "../../src/withDbByVersion.ts", "../../src/IndexDescription.ts", "../../src/createStoreDuringUpgrade.ts", "../../src/dbMutex.ts", "../../src/withStore.ts", "../../src/withReadOnlyStore.ts", "../../src/getExistingIndexes.ts", "../../src/checkDbNeedsUpgrade.ts", "../../src/withDb.ts", "../../src/IndexedDbKeyValueStore.ts", "../../src/withReadWriteStore.ts"],
4
+ "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'\n\n/**\n * Checks whether a store needs an upgrade by comparing its existing indexes against expected indexes.\n * @param db The IndexedDB database instance\n * @param storeName The name of the store to check\n * @param indexes The expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns True if the store is missing or has missing indexes\n */\nexport async function checkStoreNeedsUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\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 type { IDBPDatabase, StoreNames } from 'idb'\nimport { openDB } from 'idb'\n\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { withDbLock } from './dbMutex.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\n/**\n * Opens an IndexedDB database at a specific version, handling upgrade events, and passes it to the callback.\n * The database is automatically closed after the callback completes.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param version Optional specific version to open (undefined for latest)\n * @param expectedIndexes Optional map of store names to indexes to create during upgrade\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDbByVersion<DBTypes = 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 const db = await withDbLock(dbName, lock, async () => {\n return 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 })\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 interface 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\n/** Separator used between key names when building standard index names. */\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 IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\n/**\n * Creates an object store with the specified indexes during a version upgrade transaction.\n * @param db The IndexedDB database instance (during upgrade)\n * @param storeName The name of the store to create\n * @param indexes The index descriptions to create on the store\n * @param logger Optional logger for diagnostics\n */\nexport function createStoreDuringUpgrade<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${String(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: ${String(storeName)} already exists`)\n return\n }\n logger?.log(`Creating store: created ${String(storeName)}`)\n // Name the store\n store.name = String(storeName)\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${JSON.stringify(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 { Mutex } from 'async-mutex'\n\nconst dbMutexes: Record<string, Mutex> = {}\n\n/**\n * Returns the shared mutex for a database name, creating one if needed.\n * @param dbName The IndexedDB database name\n */\nexport function getDbMutex(dbName: string) {\n dbMutexes[dbName] = dbMutexes[dbName] ?? new Mutex()\n return dbMutexes[dbName]\n}\n\n/**\n * Runs a handler under the shared database mutex when locking is enabled.\n * @param dbName The IndexedDB database name\n * @param lock Whether to acquire the mutex before running the handler\n * @param handler The async function to execute\n */\nexport async function withDbLock<R>(dbName: string, lock: boolean, handler: () => Promise<R>) {\n if (!lock) {\n return await handler()\n }\n return await getDbMutex(dbName).runExclusive(handler)\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\n/**\n * Opens a transaction on the specified store with the given mode and passes the store to the callback.\n * If the store does not exist, the callback receives null.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the store (or null if it doesn't exist)\n * @param mode The transaction mode ('readonly' or 'readwrite')\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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\n/**\n * Opens a read-only transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-only store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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 { 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<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n const typedStoreName = storeName as StoreNames<DBTypes>\n return await withReadOnlyStore(db as unknown as IDBPDatabase<ObjectStore>, typedStoreName as StoreNames<ObjectStore>, (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\n/**\n * Retrieves the existing index descriptions for a store. Accepts either a database instance or a database name.\n * @param db The IndexedDB database instance or database name\n * @param storeName The name of the store to inspect\n * @param logger Optional logger for diagnostics\n * @returns An array of index descriptions, or null if the store does not exist\n */\nexport async function getExistingIndexes<DBTypes = unknown>(\n db: IDBPDatabase<DBTypes> | string,\n storeName: string,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDbByVersion<DBTypes, 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\n/**\n * Checks whether any store in the database needs an upgrade and returns the appropriate version number.\n * @param dbName The name of the database to check\n * @param stores Map of store names to their expected index descriptions\n * @param logger Optional logger for diagnostics\n * @returns The version to open (current version + 1 if upgrade needed, otherwise current version)\n */\nexport async function checkDbNeedsUpgrade(\n dbName: string,\n stores: Record<string, IndexDescription[]>,\n logger?: Logger,\n lock = true,\n) {\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, lock)\n}\n", "import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { withDbLock } from './dbMutex.ts'\nimport { type IndexDescription } from './IndexDescription.ts'\nimport { withDbByVersion } from './withDbByVersion.ts'\n\n/**\n * Opens an IndexedDB database, automatically upgrading if needed, and passes it to the callback.\n * Uses a mutex to serialize access to the same database by default.\n * @param dbName The name of the database to open\n * @param callback Function to execute with the opened database\n * @param expectedIndexes Optional map of store names to their expected indexes (triggers upgrade check)\n * @param logger Optional logger for diagnostics\n * @param lock Whether to use a mutex to serialize access (defaults to true)\n * @returns The result of the callback\n */\nexport async function withDb<DBTypes = 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 return await withDbLock(dbName, lock, async () => {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger, false)\n return await withDbByVersion<DBTypes, R>(dbName, async (db) => {\n return await callback(db)\n }, versionToOpen, expectedIndexes, logger, false)\n })\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 /** The name of the IndexedDB database. */\n readonly dbName: string\n /** The name of the object store within the database. */\n readonly storeName: S\n\n constructor(dbName: string, storeName: S) {\n this.dbName = dbName\n this.storeName = storeName\n }\n\n /** Removes all entries from the store. */\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n /**\n * Deletes the entry with the given key.\n * @param key The key of the entry to delete\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 /**\n * Retrieves the value associated with the given key.\n * @param key The key to look up\n * @returns The value, or undefined if not found\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 /** Returns all keys in the store. */\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n /**\n * Sets a value for the given key, creating or updating the entry.\n * @param key The key to set\n * @param value The value to store\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 /**\n * Opens the underlying IndexedDB database and passes it to the callback.\n * @param callback Function to execute with the database\n * @returns The result of the callback\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\n/**\n * Opens a read-write transaction on the specified store and passes it to the callback.\n * @param db The IndexedDB database instance\n * @param storeName The name of the object store to open\n * @param callback Function to execute with the read-write store\n * @param logger Optional logger for diagnostics\n * @returns The result of the callback\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"],
5
+ "mappings": "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",
6
+ "names": ["db", "db"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"withDb.d.ts","sourceRoot":"","sources":["../../src/withDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAGvC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAK7D;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EAC7D,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CASZ"}
1
+ {"version":3,"file":"withDb.d.ts","sourceRoot":"","sources":["../../src/withDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAIvC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EAC7D,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,UAAO,GACV,OAAO,CAAC,CAAC,CAAC,CAOZ"}
@@ -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,EAAE,YAAY,EAAc,MAAM,KAAK,CAAA;AAInD,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAIrF;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACtE,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
+ {"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;AACjD,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,KAAK,CAAA;AAKnD,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAErF;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACtE,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,CAgDZ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xylabs/indexed-db",
3
- "version": "6.1.1",
3
+ "version": "6.1.3",
4
4
  "description": "Base functionality used throughout XY Labs TypeScript/JavaScript libraries",
5
5
  "keywords": [
6
6
  "hex",
@@ -65,10 +65,10 @@
65
65
  "README.md"
66
66
  ],
67
67
  "dependencies": {
68
- "@xylabs/exists": "~6.1.1",
69
- "@xylabs/logger": "~6.1.1",
70
- "@xylabs/object": "~6.1.1",
71
- "@xylabs/storage": "~6.1.1"
68
+ "@xylabs/object": "~6.1.3",
69
+ "@xylabs/exists": "~6.1.3",
70
+ "@xylabs/logger": "~6.1.3",
71
+ "@xylabs/storage": "~6.1.3"
72
72
  },
73
73
  "devDependencies": {
74
74
  "@xylabs/toolchain": "^8.1.20",