@xylabs/indexed-db 4.4.32 → 4.4.34

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.
Files changed (83) hide show
  1. package/dist/browser/IndexDescription.d.ts +30 -0
  2. package/dist/browser/IndexDescription.d.ts.map +1 -0
  3. package/dist/browser/IndexedDbKeyValueStore.d.ts +17 -0
  4. package/dist/browser/IndexedDbKeyValueStore.d.ts.map +1 -0
  5. package/dist/browser/ObjectStore.d.ts +5 -0
  6. package/dist/browser/ObjectStore.d.ts.map +1 -0
  7. package/dist/browser/checkDbNeedsUpgrade.d.ts +7 -0
  8. package/dist/browser/checkDbNeedsUpgrade.d.ts.map +1 -0
  9. package/dist/browser/createStoreDuringUpgrade.d.ts +5 -0
  10. package/dist/browser/createStoreDuringUpgrade.d.ts.map +1 -0
  11. package/dist/{neutral/createStore.d.ts → browser/getExistingIndexes.d.ts} +2 -2
  12. package/dist/browser/getExistingIndexes.d.ts.map +1 -0
  13. package/dist/browser/index.d.ts +11 -0
  14. package/dist/browser/index.d.ts.map +1 -0
  15. package/dist/browser/index.mjs +233 -0
  16. package/dist/browser/index.mjs.map +1 -0
  17. package/dist/browser/withDb.d.ts +6 -0
  18. package/dist/browser/withDb.d.ts.map +1 -0
  19. package/dist/browser/withReadOnlyStore.d.ts +6 -0
  20. package/dist/browser/withReadOnlyStore.d.ts.map +1 -0
  21. package/dist/browser/withReadWriteStore.d.ts +6 -0
  22. package/dist/browser/withReadWriteStore.d.ts.map +1 -0
  23. package/dist/browser/withStore.d.ts +8 -0
  24. package/dist/browser/withStore.d.ts.map +1 -0
  25. package/dist/neutral/IndexDescription.d.ts +0 -4
  26. package/dist/neutral/IndexDescription.d.ts.map +1 -1
  27. package/dist/neutral/IndexedDbKeyValueStore.d.ts +3 -3
  28. package/dist/neutral/IndexedDbKeyValueStore.d.ts.map +1 -1
  29. package/dist/neutral/checkDbNeedsUpgrade.d.ts +7 -0
  30. package/dist/neutral/checkDbNeedsUpgrade.d.ts.map +1 -0
  31. package/dist/neutral/createStoreDuringUpgrade.d.ts +5 -0
  32. package/dist/neutral/createStoreDuringUpgrade.d.ts.map +1 -0
  33. package/dist/neutral/getExistingIndexes.d.ts +2 -1
  34. package/dist/neutral/getExistingIndexes.d.ts.map +1 -1
  35. package/dist/neutral/index.d.ts +3 -1
  36. package/dist/neutral/index.d.ts.map +1 -1
  37. package/dist/neutral/index.mjs +131 -33
  38. package/dist/neutral/index.mjs.map +1 -1
  39. package/dist/neutral/withDb.d.ts +3 -1
  40. package/dist/neutral/withDb.d.ts.map +1 -1
  41. package/dist/neutral/withReadOnlyStore.d.ts +2 -1
  42. package/dist/neutral/withReadOnlyStore.d.ts.map +1 -1
  43. package/dist/neutral/withReadWriteStore.d.ts +2 -1
  44. package/dist/neutral/withReadWriteStore.d.ts.map +1 -1
  45. package/dist/neutral/withStore.d.ts +8 -0
  46. package/dist/neutral/withStore.d.ts.map +1 -0
  47. package/dist/node/IndexDescription.d.ts +30 -0
  48. package/dist/node/IndexDescription.d.ts.map +1 -0
  49. package/dist/node/IndexedDbKeyValueStore.d.ts +17 -0
  50. package/dist/node/IndexedDbKeyValueStore.d.ts.map +1 -0
  51. package/dist/node/ObjectStore.d.ts +5 -0
  52. package/dist/node/ObjectStore.d.ts.map +1 -0
  53. package/dist/node/checkDbNeedsUpgrade.d.ts +7 -0
  54. package/dist/node/checkDbNeedsUpgrade.d.ts.map +1 -0
  55. package/dist/node/createStoreDuringUpgrade.d.ts +5 -0
  56. package/dist/node/createStoreDuringUpgrade.d.ts.map +1 -0
  57. package/dist/node/getExistingIndexes.d.ts +7 -0
  58. package/dist/node/getExistingIndexes.d.ts.map +1 -0
  59. package/dist/node/index.d.ts +11 -0
  60. package/dist/node/index.d.ts.map +1 -0
  61. package/dist/node/index.mjs +233 -0
  62. package/dist/node/index.mjs.map +1 -0
  63. package/dist/node/withDb.d.ts +6 -0
  64. package/dist/node/withDb.d.ts.map +1 -0
  65. package/dist/node/withReadOnlyStore.d.ts +6 -0
  66. package/dist/node/withReadOnlyStore.d.ts.map +1 -0
  67. package/dist/node/withReadWriteStore.d.ts +6 -0
  68. package/dist/node/withReadWriteStore.d.ts.map +1 -0
  69. package/dist/node/withStore.d.ts +8 -0
  70. package/dist/node/withStore.d.ts.map +1 -0
  71. package/package.json +23 -9
  72. package/src/IndexDescription.ts +0 -4
  73. package/src/IndexedDbKeyValueStore.ts +3 -3
  74. package/src/checkDbNeedsUpgrade.ts +37 -0
  75. package/src/{createStore.ts → createStoreDuringUpgrade.ts} +17 -10
  76. package/src/getExistingIndexes.ts +39 -20
  77. package/src/index.ts +3 -1
  78. package/src/withDb.ts +50 -4
  79. package/src/withReadOnlyStore.ts +5 -8
  80. package/src/withReadWriteStore.ts +5 -8
  81. package/src/withStore.ts +34 -0
  82. package/xy.config.ts +2 -2
  83. package/dist/neutral/createStore.d.ts.map +0 -1
@@ -0,0 +1,233 @@
1
+ // src/checkDbNeedsUpgrade.ts
2
+ import { exists } from "@xylabs/exists";
3
+
4
+ // src/withDb.ts
5
+ import { openDB } from "idb";
6
+
7
+ // src/IndexDescription.ts
8
+ var IndexSeparator = "-";
9
+ var buildStandardIndexName = (index) => {
10
+ const { key, unique } = index;
11
+ const prefix = unique ? "UX" : "IX";
12
+ const indexKeys = Object.keys(key);
13
+ return `${prefix}_${indexKeys.join(IndexSeparator)}`;
14
+ };
15
+
16
+ // src/createStoreDuringUpgrade.ts
17
+ function createStoreDuringUpgrade(db, storeName, indexes, logger) {
18
+ logger?.log(`Creating store ${storeName}`);
19
+ let store;
20
+ try {
21
+ store = db.createObjectStore(storeName, {
22
+ // If it isn't explicitly set, create a value by auto incrementing.
23
+ autoIncrement: true
24
+ });
25
+ } catch {
26
+ logger?.warn(`Failed to create store: ${storeName} already exists`);
27
+ return;
28
+ }
29
+ logger?.log(`Creating store: created ${storeName}`);
30
+ store.name = storeName;
31
+ for (const {
32
+ key,
33
+ multiEntry,
34
+ unique
35
+ } of indexes) {
36
+ logger?.log(`Creating store: index ${key}`);
37
+ const indexKeys = Object.keys(key);
38
+ const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys;
39
+ const indexName = buildStandardIndexName({ key, unique });
40
+ console.log("createIndex", indexName, keys, { multiEntry, unique });
41
+ store.createIndex(indexName, keys, { multiEntry, unique });
42
+ }
43
+ }
44
+
45
+ // src/withDb.ts
46
+ async function withDb(dbName, callback, expectedIndexes, logger) {
47
+ const versionToOpen = expectedIndexes === void 0 ? void 0 : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger);
48
+ const db = await openDB(dbName, versionToOpen, {
49
+ blocked(currentVersion, blockedVersion, event) {
50
+ logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event);
51
+ },
52
+ blocking(currentVersion, blockedVersion, event) {
53
+ logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event);
54
+ },
55
+ terminated() {
56
+ logger?.log("IndexedDb: Terminated");
57
+ },
58
+ upgrade(db2, oldVersion, newVersion, transaction) {
59
+ if (oldVersion !== newVersion) {
60
+ logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`);
61
+ const objectStores = transaction.objectStoreNames;
62
+ for (const name of objectStores) {
63
+ try {
64
+ db2.deleteObjectStore(name);
65
+ } catch {
66
+ console.log(`IndexedDb: Failed to delete existing object store ${name}`);
67
+ }
68
+ }
69
+ }
70
+ if (expectedIndexes) {
71
+ for (const [storeName, indexes] of Object.entries(expectedIndexes)) {
72
+ if (db2.objectStoreNames.contains(storeName)) {
73
+ continue;
74
+ }
75
+ const indexesToCreate = indexes.map((idx) => ({
76
+ ...idx,
77
+ name: buildStandardIndexName(idx)
78
+ // eslint-disable-next-line unicorn/no-array-reduce
79
+ })).reduce((acc, idx) => acc.set(idx.name, idx), /* @__PURE__ */ new Map()).values();
80
+ createStoreDuringUpgrade(db2, storeName, [...indexesToCreate], logger);
81
+ }
82
+ }
83
+ }
84
+ });
85
+ try {
86
+ return await callback(db);
87
+ } finally {
88
+ db.close();
89
+ }
90
+ }
91
+
92
+ // src/withStore.ts
93
+ async function withStore(db, storeName, callback, mode, logger) {
94
+ logger?.log("withStore", storeName, mode);
95
+ let transaction = void 0;
96
+ logger?.log("withStore:transaction");
97
+ let store = null;
98
+ try {
99
+ transaction = db.transaction(storeName, mode);
100
+ store = transaction.objectStore(storeName);
101
+ } catch (ex) {
102
+ logger?.log("withStore:catch", ex);
103
+ }
104
+ try {
105
+ return await callback(store);
106
+ } finally {
107
+ logger?.log("withStore:finally");
108
+ await transaction?.done;
109
+ }
110
+ }
111
+
112
+ // src/withReadOnlyStore.ts
113
+ async function withReadOnlyStore(db, storeName, callback, logger) {
114
+ return await withStore(db, storeName, callback, "readonly", logger);
115
+ }
116
+
117
+ // src/getExistingIndexes.ts
118
+ async function getExistingIndexesInternal(db, storeName, logger) {
119
+ logger?.log("getExistingIndexesInternal", storeName);
120
+ return await withReadOnlyStore(db, storeName, (store) => {
121
+ return store ? [...store.indexNames].map((indexName) => {
122
+ const index = store.index(indexName);
123
+ const key = {};
124
+ if (Array.isArray(index.keyPath)) {
125
+ for (const keyPath of index.keyPath) {
126
+ key[keyPath] = 1;
127
+ }
128
+ } else {
129
+ key[index.keyPath] = 1;
130
+ }
131
+ const desc = {
132
+ key,
133
+ unique: index.unique,
134
+ multiEntry: index.multiEntry
135
+ };
136
+ return desc;
137
+ }) : null;
138
+ }, logger);
139
+ }
140
+ async function getExistingIndexes(db, storeName, logger) {
141
+ logger?.log("getExistingIndexes", storeName);
142
+ if (typeof db === "string") {
143
+ return await withDb(db, async (db2) => {
144
+ return await getExistingIndexesInternal(db2, storeName, logger);
145
+ });
146
+ }
147
+ return await getExistingIndexesInternal(db, storeName, logger);
148
+ }
149
+
150
+ // src/checkDbNeedsUpgrade.ts
151
+ async function checkStoreNeedsUpgrade(db, storeName, indexes, logger) {
152
+ logger?.log("checkStoreNeedsUpgrade", storeName, indexes);
153
+ const existingIndexes = await getExistingIndexes(db, storeName, logger);
154
+ if (existingIndexes === null) {
155
+ return true;
156
+ }
157
+ const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists));
158
+ for (const { key, unique } of indexes) {
159
+ const indexName = buildStandardIndexName({ key, unique });
160
+ if (!existingIndexNames.has(indexName)) {
161
+ return true;
162
+ }
163
+ }
164
+ return false;
165
+ }
166
+ async function checkDbNeedsUpgrade(dbName, stores, logger) {
167
+ logger?.log("checkDbNeedsUpgrade", dbName, stores);
168
+ return await withDb(dbName, async (db) => {
169
+ for (const [storeName, indexes] of Object.entries(stores)) {
170
+ if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {
171
+ return db.version + 1;
172
+ }
173
+ }
174
+ return db.version;
175
+ });
176
+ }
177
+
178
+ // src/IndexedDbKeyValueStore.ts
179
+ var IndexedDbKeyValueStore = class {
180
+ constructor(dbName, storeName) {
181
+ this.dbName = dbName;
182
+ this.storeName = storeName;
183
+ }
184
+ async clear() {
185
+ return await this.withDb((db) => {
186
+ return db.clear(this.storeName);
187
+ });
188
+ }
189
+ async delete(key) {
190
+ return await this.withDb((db) => {
191
+ return db.delete(this.storeName, key);
192
+ });
193
+ }
194
+ async get(key) {
195
+ return await this.withDb((db) => {
196
+ return db.get(this.storeName, key) ?? void 0;
197
+ });
198
+ }
199
+ async keys() {
200
+ return await this.withDb((db) => {
201
+ return db.getAllKeys(this.storeName);
202
+ });
203
+ }
204
+ async set(key, value) {
205
+ return await this.withDb((db) => {
206
+ return db.put(this.storeName, value, key);
207
+ });
208
+ }
209
+ async withDb(callback) {
210
+ return await withDb(this.dbName, (db) => {
211
+ return callback(db);
212
+ });
213
+ }
214
+ };
215
+
216
+ // src/withReadWriteStore.ts
217
+ async function withReadWriteStore(db, storeName, callback, logger) {
218
+ return await withStore(db, storeName, callback, "readwrite", logger);
219
+ }
220
+ export {
221
+ IndexSeparator,
222
+ IndexedDbKeyValueStore,
223
+ buildStandardIndexName,
224
+ checkDbNeedsUpgrade,
225
+ checkStoreNeedsUpgrade,
226
+ createStoreDuringUpgrade,
227
+ getExistingIndexes,
228
+ withDb,
229
+ withReadOnlyStore,
230
+ withReadWriteStore,
231
+ withStore
232
+ };
233
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/checkDbNeedsUpgrade.ts","../../src/withDb.ts","../../src/IndexDescription.ts","../../src/createStoreDuringUpgrade.ts","../../src/withStore.ts","../../src/withReadOnlyStore.ts","../../src/getExistingIndexes.ts","../../src/IndexedDbKeyValueStore.ts","../../src/withReadWriteStore.ts"],"sourcesContent":["import { exists } from '@xylabs/exists'\nimport type { Logger } from '@xylabs/logger'\nimport type { IDBPDatabase } from 'idb'\n\nimport { getExistingIndexes } from './getExistingIndexes.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDb } from './withDb.ts'\n\nexport async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {\n logger?.log('checkStoreNeedsUpgrade', storeName, indexes)\n const existingIndexes = await getExistingIndexes(db, storeName, logger)\n if (existingIndexes === null) {\n // the store does not exist, so we need to trigger upgrade (no existing indexes)\n return true\n }\n const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))\n for (const { key, unique } of indexes) {\n const indexName = buildStandardIndexName({ key, unique })\n if (!existingIndexNames.has(indexName)) {\n return true\n }\n }\n return false\n}\n\nexport async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {\n logger?.log('checkDbNeedsUpgrade', dbName, stores)\n return await withDb<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 })\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n DBSchema, IDBPDatabase, StoreNames,\n} from 'idb'\nimport { openDB } from 'idb'\n\nimport { checkDbNeedsUpgrade } from './checkDbNeedsUpgrade.ts'\nimport { createStoreDuringUpgrade } from './createStoreDuringUpgrade.ts'\nimport { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'\n\nexport async function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(\n dbName: string,\n callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R,\n expectedIndexes?: Record<string, IndexDescription[]>,\n logger?: Logger,\n): Promise<R> {\n const versionToOpen = expectedIndexes === undefined ? undefined : await checkDbNeedsUpgrade(dbName, expectedIndexes, logger)\n const db = await openDB<DBTypes>(dbName, versionToOpen, {\n blocked(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocked from upgrading from ${currentVersion} to ${blockedVersion}`, event)\n },\n blocking(currentVersion, blockedVersion, event) {\n logger?.warn(`IndexedDb: Blocking upgrade from ${currentVersion} to ${blockedVersion}`, event)\n },\n terminated() {\n logger?.log('IndexedDb: Terminated')\n },\n upgrade(db, oldVersion, newVersion, transaction) {\n if (oldVersion !== newVersion) {\n logger?.log(`IndexedDb: Upgrading from ${oldVersion} to ${newVersion}`)\n const objectStores = transaction.objectStoreNames\n for (const name of objectStores) {\n try {\n db.deleteObjectStore(name)\n } catch {\n console.log(`IndexedDb: Failed to delete existing object store ${name}`)\n }\n }\n }\n if (expectedIndexes) {\n for (const [storeName, indexes] of Object.entries(expectedIndexes)) {\n if (db.objectStoreNames.contains(storeName as StoreNames<DBTypes>)) {\n continue\n }\n const indexesToCreate = indexes.map(idx => ({\n ...idx,\n name: buildStandardIndexName(idx),\n // eslint-disable-next-line unicorn/no-array-reduce\n })).reduce((acc, idx) => acc.set(idx.name, idx), new Map<string, IndexDescription>()).values()\n createStoreDuringUpgrade(db, storeName as StoreNames<DBTypes>, [...indexesToCreate], logger)\n }\n }\n },\n })\n try {\n return await callback(db)\n } finally {\n db.close()\n }\n}\n","/**\n * The index direction (1 for ascending, -1 for descending)\n */\nexport type IndexDirection = -1 | 1\n\n/**\n * Description of index(es) to be created on a store\n */\nexport type IndexDescription = {\n /**\n * The key(s) to index\n */\n key: Record<string, IndexDirection>\n /**\n * Is the indexed value an array\n */\n multiEntry?: boolean\n /**\n * If true, the index must enforce uniqueness on the key\n */\n unique?: boolean\n}\n\nexport const IndexSeparator = '-'\n\n/**\n * Given an index description, this will build the index\n * name in standard form\n * @param index The index description\n * @returns The index name in standard form\n */\nexport const buildStandardIndexName = (index: IndexDescription) => {\n const { key, unique } = index\n const prefix = unique ? 'UX' : 'IX'\n const indexKeys = Object.keys(key)\n return `${prefix}_${indexKeys.join(IndexSeparator)}`\n}\n","import type { Logger } from '@xylabs/logger'\nimport type {\n DBSchema,\n IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,\n} from 'idb'\n\nimport {\n buildStandardIndexName,\n type IndexDescription,\n} from './IndexDescription.ts'\n\nexport function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(\n db: IDBPDatabase<DBTypes>,\n storeName: StoreNames<DBTypes>,\n indexes: IndexDescription[],\n logger?: Logger,\n) {\n logger?.log(`Creating store ${storeName}`)\n // Create the store\n let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined\n try {\n store = db.createObjectStore(storeName, {\n // If it isn't explicitly set, create a value by auto incrementing.\n autoIncrement: true,\n })\n } catch {\n logger?.warn(`Failed to create store: ${storeName} already exists`)\n return\n }\n logger?.log(`Creating store: created ${storeName}`)\n // Name the store\n store.name = storeName\n // Create an index on the hash\n for (const {\n key, multiEntry, unique,\n } of indexes) {\n logger?.log(`Creating store: index ${key}`)\n const indexKeys = Object.keys(key)\n const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys\n const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>\n console.log('createIndex', indexName, keys, { multiEntry, unique })\n store.createIndex(indexName, keys, { multiEntry, unique })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, IDBPTransaction, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\n\nexport async function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>,\n [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R,\n mode: M,\n logger?: Logger,\n): Promise<R> {\n logger?.log('withStore', storeName, mode)\n let transaction: IDBPTransaction<ObjectStore<T>, [StoreNames<ObjectStore<T>>], M> | undefined = undefined\n logger?.log('withStore:transaction')\n let store = null\n try {\n transaction = db.transaction(storeName, mode)\n // we do this in a try/catch because the store might not exist\n store = transaction.objectStore(storeName)\n } catch (ex) {\n logger?.log('withStore:catch', ex)\n }\n try {\n return await callback(store)\n } finally {\n logger?.log('withStore:finally')\n await transaction?.done\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readonly', logger)\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type { IDBPDatabase, StoreNames } from 'idb'\n\nimport {\n type IndexDescription,\n type IndexDirection,\n} from './IndexDescription.ts'\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withDb } from './withDb.ts'\nimport { withReadOnlyStore } from './withReadOnlyStore.ts'\n\nasync function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexesInternal', storeName)\n return await withReadOnlyStore(db, storeName, (store) => {\n return store\n ? [...store.indexNames].map((indexName) => {\n const index = store.index(indexName)\n const key: Record<string, IndexDirection> = {}\n if (Array.isArray(index.keyPath)) {\n for (const keyPath of index.keyPath) {\n key[keyPath] = 1\n }\n } else {\n key[index.keyPath] = 1\n }\n const desc: IndexDescription = {\n key,\n unique: index.unique,\n multiEntry: index.multiEntry,\n }\n return desc\n })\n : null\n }, logger)\n}\n\nexport async function getExistingIndexes<T extends EmptyObject = EmptyObject>(\n db: IDBPDatabase<ObjectStore<T>> | string,\n storeName: StoreNames<ObjectStore<T>>,\n logger?: Logger,\n): Promise<IndexDescription[] | null> {\n logger?.log('getExistingIndexes', storeName)\n if (typeof db === 'string') {\n return await withDb<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {\n return await getExistingIndexesInternal(db, storeName, logger)\n })\n }\n return await getExistingIndexesInternal(db, storeName, logger)\n}\n","import type { 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 constructor(readonly dbName: string, readonly storeName: S) {}\n\n async clear?(): Promise<void> {\n return await this.withDb((db) => {\n return db.clear(this.storeName)\n })\n }\n\n async delete(key: StoreKey<T, S>): Promise<void> {\n return await this.withDb((db) => {\n return db.delete(this.storeName, key)\n })\n }\n\n async get(key: StoreKey<T, S>) {\n return await this.withDb((db) => {\n return db.get(this.storeName, key) ?? undefined\n })\n }\n\n async keys?(): Promise<StoreKey<T, S>[]> {\n return await this.withDb((db) => {\n return db.getAllKeys(this.storeName)\n })\n }\n\n async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<StoreKey<T, S>> {\n return await this.withDb((db) => {\n return db.put(this.storeName, value, key)\n })\n }\n\n async withDb<R = StoreValue<T, S>>(\n callback: (db: IDBPDatabase<T>) =>\n Promise<R> | R,\n ) {\n return await withDb<T, R>(this.dbName, (db) => {\n return callback(db)\n })\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { EmptyObject } from '@xylabs/object'\nimport type {\n IDBPDatabase, IDBPObjectStore, StoreNames,\n} from 'idb'\n\nimport type { ObjectStore } from './ObjectStore.ts'\nimport { withStore } from './withStore.ts'\n\nexport async function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(\n db: IDBPDatabase<ObjectStore<T>>,\n storeName: StoreNames<ObjectStore<T>>,\n callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R,\n logger?: Logger,\n): Promise<R> {\n return await withStore(db, storeName, callback, 'readwrite', logger)\n}\n"],"mappings":"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","names":["db","db"]}
@@ -0,0 +1,6 @@
1
+ import type { Logger } from '@xylabs/logger';
2
+ import type { EmptyObject } from '@xylabs/object';
3
+ import type { DBSchema, IDBPDatabase } from 'idb';
4
+ import { type IndexDescription } from './IndexDescription.ts';
5
+ export declare function withDb<DBTypes extends DBSchema | unknown = unknown, R = EmptyObject>(dbName: string, callback: (db: IDBPDatabase<DBTypes>) => Promise<R> | R, expectedIndexes?: Record<string, IndexDescription[]>, logger?: Logger): Promise<R>;
6
+ //# sourceMappingURL=withDb.d.ts.map
@@ -0,0 +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;AACjD,OAAO,KAAK,EACV,QAAQ,EAAE,YAAY,EACvB,MAAM,KAAK,CAAA;AAKZ,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAErF,wBAAsB,MAAM,CAAC,OAAO,SAAS,QAAQ,GAAG,OAAO,GAAG,OAAO,EAAE,CAAC,GAAG,WAAW,EACxF,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,GACd,OAAO,CAAC,CAAC,CAAC,CA4CZ"}
@@ -0,0 +1,6 @@
1
+ import type { Logger } from '@xylabs/logger';
2
+ import type { EmptyObject } from '@xylabs/object';
3
+ import type { IDBPDatabase, IDBPObjectStore, StoreNames } from 'idb';
4
+ import type { ObjectStore } from './ObjectStore.ts';
5
+ export declare function withReadOnlyStore<T extends EmptyObject = EmptyObject, R = T>(db: IDBPDatabase<ObjectStore<T>>, storeName: StoreNames<ObjectStore<T>>, callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readonly'> | null) => Promise<R> | R, logger?: Logger): Promise<R>;
6
+ //# sourceMappingURL=withReadOnlyStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withReadOnlyStore.d.ts","sourceRoot":"","sources":["../../src/withReadOnlyStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EACV,YAAY,EAAE,eAAe,EAAE,UAAU,EAC1C,MAAM,KAAK,CAAA;AAEZ,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnD,wBAAsB,iBAAiB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,CAAC,GAAG,CAAC,EAChF,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAChC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACjJ,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAEZ"}
@@ -0,0 +1,6 @@
1
+ import type { Logger } from '@xylabs/logger';
2
+ import type { EmptyObject } from '@xylabs/object';
3
+ import type { IDBPDatabase, IDBPObjectStore, StoreNames } from 'idb';
4
+ import type { ObjectStore } from './ObjectStore.ts';
5
+ export declare function withReadWriteStore<T extends EmptyObject = EmptyObject, R = T>(db: IDBPDatabase<ObjectStore<T>>, storeName: StoreNames<ObjectStore<T>>, callback: (store: IDBPObjectStore<ObjectStore<T>, [StoreNames<ObjectStore<T>>], StoreNames<ObjectStore<T>>, 'readwrite'> | null) => Promise<R> | R, logger?: Logger): Promise<R>;
6
+ //# sourceMappingURL=withReadWriteStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withReadWriteStore.d.ts","sourceRoot":"","sources":["../../src/withReadWriteStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EACV,YAAY,EAAE,eAAe,EAAE,UAAU,EAC1C,MAAM,KAAK,CAAA;AAEZ,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnD,wBAAsB,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,CAAC,GAAG,CAAC,EACjF,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAChC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAClJ,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAEZ"}
@@ -0,0 +1,8 @@
1
+ import type { Logger } from '@xylabs/logger';
2
+ import type { EmptyObject } from '@xylabs/object';
3
+ import type { IDBPDatabase, IDBPObjectStore, StoreNames } from 'idb';
4
+ import type { ObjectStore } from './ObjectStore.ts';
5
+ export declare function withStore<T extends EmptyObject = EmptyObject, R = T, M extends 'readonly' | 'readwrite' = 'readonly'>(db: IDBPDatabase<ObjectStore<T>>, storeName: StoreNames<ObjectStore<T>>, callback: (store: IDBPObjectStore<ObjectStore<T>, [
6
+ StoreNames<ObjectStore<T>>
7
+ ], StoreNames<ObjectStore<T>>, M> | null) => Promise<R> | R, mode: M, logger?: Logger): Promise<R>;
8
+ //# sourceMappingURL=withStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withStore.d.ts","sourceRoot":"","sources":["../../src/withStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EACV,YAAY,EAAE,eAAe,EAAmB,UAAU,EAC3D,MAAM,KAAK,CAAA;AAEZ,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,wBAAsB,SAAS,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,UAAU,GAAG,WAAW,GAAG,UAAU,EACzH,EAAE,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAChC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,EAC9C;IAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACxF,IAAI,EAAE,CAAC,EACP,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAkBZ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xylabs/indexed-db",
3
- "version": "4.4.32",
3
+ "version": "4.4.34",
4
4
  "description": "Base functionality used throughout XY Labs TypeScript/JavaScript libraries",
5
5
  "keywords": [
6
6
  "hex",
@@ -28,26 +28,40 @@
28
28
  "type": "module",
29
29
  "exports": {
30
30
  ".": {
31
- "types": "./dist/neutral/index.d.ts",
32
- "default": "./dist/neutral/index.mjs"
31
+ "node": {
32
+ "types": "./dist/node/index.d.ts",
33
+ "default": "./dist/node/index.mjs"
34
+ },
35
+ "browser": {
36
+ "types": "./dist/browser/index.d.ts",
37
+ "default": "./dist/browser/index.mjs"
38
+ },
39
+ "neutral": {
40
+ "types": "./dist/neutral/index.d.ts",
41
+ "default": "./dist/neutral/index.mjs"
42
+ }
33
43
  },
34
44
  "./package.json": "./package.json"
35
45
  },
36
- "module": "./dist/neutral/index.mjs",
37
- "types": "./dist/neutral/index.d.ts",
46
+ "module": "./dist/browser/index.mjs",
47
+ "types": "./dist/browser/index.d.ts",
38
48
  "workspaces": [
39
49
  "packages/**/*"
40
50
  ],
41
51
  "dependencies": {
42
- "@xylabs/logger": "^4.4.32",
43
- "@xylabs/object": "^4.4.32",
44
- "@xylabs/storage": "^4.4.32",
52
+ "@xylabs/exists": "^4.4.34",
53
+ "@xylabs/logger": "^4.4.34",
54
+ "@xylabs/object": "^4.4.34",
55
+ "@xylabs/storage": "^4.4.34",
45
56
  "idb": "^8.0.1"
46
57
  },
47
58
  "devDependencies": {
48
59
  "@xylabs/ts-scripts-yarn3": "^4.2.6",
49
60
  "@xylabs/tsconfig": "^4.2.6",
50
- "@xylabs/vitest-extended": "^4.4.32",
61
+ "@xylabs/tsconfig-dom": "^4.2.6",
62
+ "@xylabs/vitest-extended": "^4.4.34",
63
+ "fake-indexeddb": "^6.0.0",
64
+ "jsdom": "^25.0.1",
51
65
  "typescript": "^5.7.2",
52
66
  "vitest": "^2.1.8"
53
67
  },
@@ -15,10 +15,6 @@ export type IndexDescription = {
15
15
  * Is the indexed value an array
16
16
  */
17
17
  multiEntry?: boolean
18
- /**
19
- * The name of the index
20
- */
21
- name?: string
22
18
  /**
23
19
  * If true, the index must enforce uniqueness on the key
24
20
  */
@@ -18,13 +18,13 @@ export class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>>
18
18
  })
19
19
  }
20
20
 
21
- async delete(key: string): Promise<void> {
21
+ async delete(key: StoreKey<T, S>): Promise<void> {
22
22
  return await this.withDb((db) => {
23
23
  return db.delete(this.storeName, key)
24
24
  })
25
25
  }
26
26
 
27
- async get(key: string) {
27
+ async get(key: StoreKey<T, S>) {
28
28
  return await this.withDb((db) => {
29
29
  return db.get(this.storeName, key) ?? undefined
30
30
  })
@@ -36,7 +36,7 @@ export class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>>
36
36
  })
37
37
  }
38
38
 
39
- async set(key: string, value: StoreValue<T, S>): Promise<void> {
39
+ async set(key: StoreKey<T, S>, value: StoreValue<T, S>): Promise<StoreKey<T, S>> {
40
40
  return await this.withDb((db) => {
41
41
  return db.put(this.storeName, value, key)
42
42
  })
@@ -0,0 +1,37 @@
1
+ import { exists } from '@xylabs/exists'
2
+ import type { Logger } from '@xylabs/logger'
3
+ import type { IDBPDatabase } from 'idb'
4
+
5
+ import { getExistingIndexes } from './getExistingIndexes.ts'
6
+ import { buildStandardIndexName, type IndexDescription } from './IndexDescription.ts'
7
+ import type { ObjectStore } from './ObjectStore.ts'
8
+ import { withDb } from './withDb.ts'
9
+
10
+ export async function checkStoreNeedsUpgrade(db: IDBPDatabase<ObjectStore<object>>, storeName: string, indexes: IndexDescription[], logger?: Logger) {
11
+ logger?.log('checkStoreNeedsUpgrade', storeName, indexes)
12
+ const existingIndexes = await getExistingIndexes(db, storeName, logger)
13
+ if (existingIndexes === null) {
14
+ // the store does not exist, so we need to trigger upgrade (no existing indexes)
15
+ return true
16
+ }
17
+ const existingIndexNames = new Set(existingIndexes.map(({ key, unique }) => buildStandardIndexName({ key, unique })).filter(exists))
18
+ for (const { key, unique } of indexes) {
19
+ const indexName = buildStandardIndexName({ key, unique })
20
+ if (!existingIndexNames.has(indexName)) {
21
+ return true
22
+ }
23
+ }
24
+ return false
25
+ }
26
+
27
+ export async function checkDbNeedsUpgrade(dbName: string, stores: Record<string, IndexDescription[]>, logger?: Logger) {
28
+ logger?.log('checkDbNeedsUpgrade', dbName, stores)
29
+ return await withDb<ObjectStore, number>(dbName, async (db) => {
30
+ for (const [storeName, indexes] of Object.entries(stores)) {
31
+ if (await checkStoreNeedsUpgrade(db, storeName, indexes, logger)) {
32
+ return db.version + 1
33
+ }
34
+ }
35
+ return db.version
36
+ })
37
+ }
@@ -1,36 +1,43 @@
1
1
  import type { Logger } from '@xylabs/logger'
2
- import type { EmptyObject } from '@xylabs/object'
3
2
  import type {
4
- IDBPDatabase, IndexNames, StoreNames,
3
+ DBSchema,
4
+ IDBPDatabase, IDBPObjectStore, IndexNames, StoreNames,
5
5
  } from 'idb'
6
6
 
7
7
  import {
8
8
  buildStandardIndexName,
9
9
  type IndexDescription,
10
10
  } from './IndexDescription.ts'
11
- import type { ObjectStore } from './ObjectStore.ts'
12
11
 
13
- export function createStore<T extends EmptyObject = EmptyObject>(
14
- db: IDBPDatabase<ObjectStore<T>>,
15
- storeName: StoreNames<ObjectStore<T>>,
12
+ export function createStoreDuringUpgrade<DBTypes extends DBSchema | unknown = unknown>(
13
+ db: IDBPDatabase<DBTypes>,
14
+ storeName: StoreNames<DBTypes>,
16
15
  indexes: IndexDescription[],
17
16
  logger?: Logger,
18
17
  ) {
19
18
  logger?.log(`Creating store ${storeName}`)
20
19
  // Create the store
21
- const store = db.createObjectStore(storeName, {
20
+ let store: IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, StoreNames<DBTypes>, 'versionchange'> | undefined
21
+ try {
22
+ store = db.createObjectStore(storeName, {
22
23
  // If it isn't explicitly set, create a value by auto incrementing.
23
- autoIncrement: true,
24
- })
24
+ autoIncrement: true,
25
+ })
26
+ } catch {
27
+ logger?.warn(`Failed to create store: ${storeName} already exists`)
28
+ return
29
+ }
30
+ logger?.log(`Creating store: created ${storeName}`)
25
31
  // Name the store
26
32
  store.name = storeName
27
33
  // Create an index on the hash
28
34
  for (const {
29
35
  key, multiEntry, unique,
30
36
  } of indexes) {
37
+ logger?.log(`Creating store: index ${key}`)
31
38
  const indexKeys = Object.keys(key)
32
39
  const keys = indexKeys.length === 1 ? indexKeys[0] : indexKeys
33
- const indexName = buildStandardIndexName({ key, unique }) as IndexNames<ObjectStore<T>, StoreNames<ObjectStore<T>>>
40
+ const indexName = buildStandardIndexName({ key, unique }) as IndexNames<DBTypes, StoreNames<DBTypes>>
34
41
  console.log('createIndex', indexName, keys, { multiEntry, unique })
35
42
  store.createIndex(indexName, keys, { multiEntry, unique })
36
43
  }
@@ -1,3 +1,4 @@
1
+ import type { Logger } from '@xylabs/logger'
1
2
  import type { EmptyObject } from '@xylabs/object'
2
3
  import type { IDBPDatabase, StoreNames } from 'idb'
3
4
 
@@ -6,30 +7,48 @@ import {
6
7
  type IndexDirection,
7
8
  } from './IndexDescription.ts'
8
9
  import type { ObjectStore } from './ObjectStore.ts'
10
+ import { withDb } from './withDb.ts'
9
11
  import { withReadOnlyStore } from './withReadOnlyStore.ts'
10
12
 
11
- export async function getExistingIndexes<T extends EmptyObject = EmptyObject>(
13
+ async function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(
12
14
  db: IDBPDatabase<ObjectStore<T>>,
13
15
  storeName: StoreNames<ObjectStore<T>>,
14
- ): Promise<IndexDescription[]> {
16
+ logger?: Logger,
17
+ ): Promise<IndexDescription[] | null> {
18
+ logger?.log('getExistingIndexesInternal', storeName)
15
19
  return await withReadOnlyStore(db, storeName, (store) => {
16
- return [...store.indexNames].map((indexName) => {
17
- const index = store.index(indexName)
18
- const key: Record<string, IndexDirection> = {}
19
- if (Array.isArray(index.keyPath)) {
20
- for (const keyPath of index.keyPath) {
21
- key[keyPath] = 1
22
- }
23
- } else {
24
- key[index.keyPath] = 1
25
- }
26
- const desc: IndexDescription = {
27
- name: indexName as string,
28
- key,
29
- unique: index.unique,
30
- multiEntry: index.multiEntry,
31
- }
32
- return desc
20
+ return store
21
+ ? [...store.indexNames].map((indexName) => {
22
+ const index = store.index(indexName)
23
+ const key: Record<string, IndexDirection> = {}
24
+ if (Array.isArray(index.keyPath)) {
25
+ for (const keyPath of index.keyPath) {
26
+ key[keyPath] = 1
27
+ }
28
+ } else {
29
+ key[index.keyPath] = 1
30
+ }
31
+ const desc: IndexDescription = {
32
+ key,
33
+ unique: index.unique,
34
+ multiEntry: index.multiEntry,
35
+ }
36
+ return desc
37
+ })
38
+ : null
39
+ }, logger)
40
+ }
41
+
42
+ export async function getExistingIndexes<T extends EmptyObject = EmptyObject>(
43
+ db: IDBPDatabase<ObjectStore<T>> | string,
44
+ storeName: StoreNames<ObjectStore<T>>,
45
+ logger?: Logger,
46
+ ): Promise<IndexDescription[] | null> {
47
+ logger?.log('getExistingIndexes', storeName)
48
+ if (typeof db === 'string') {
49
+ return await withDb<ObjectStore<T>, IndexDescription[] | null>(db, async (db) => {
50
+ return await getExistingIndexesInternal(db, storeName, logger)
33
51
  })
34
- })
52
+ }
53
+ return await getExistingIndexesInternal(db, storeName, logger)
35
54
  }
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- export * from './createStore.ts'
1
+ export * from './checkDbNeedsUpgrade.ts'
2
+ export * from './createStoreDuringUpgrade.ts'
2
3
  export * from './getExistingIndexes.ts'
3
4
  export * from './IndexDescription.ts'
4
5
  export * from './IndexedDbKeyValueStore.ts'
@@ -6,3 +7,4 @@ export * from './ObjectStore.ts'
6
7
  export * from './withDb.ts'
7
8
  export * from './withReadOnlyStore.ts'
8
9
  export * from './withReadWriteStore.ts'
10
+ export * from './withStore.ts'