@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.
- package/dist/browser/IndexDescription.d.ts +30 -0
- package/dist/browser/IndexDescription.d.ts.map +1 -0
- package/dist/browser/IndexedDbKeyValueStore.d.ts +17 -0
- package/dist/browser/IndexedDbKeyValueStore.d.ts.map +1 -0
- package/dist/browser/ObjectStore.d.ts +5 -0
- package/dist/browser/ObjectStore.d.ts.map +1 -0
- package/dist/browser/checkDbNeedsUpgrade.d.ts +7 -0
- package/dist/browser/checkDbNeedsUpgrade.d.ts.map +1 -0
- package/dist/browser/createStoreDuringUpgrade.d.ts +5 -0
- package/dist/browser/createStoreDuringUpgrade.d.ts.map +1 -0
- package/dist/{neutral/createStore.d.ts → browser/getExistingIndexes.d.ts} +2 -2
- package/dist/browser/getExistingIndexes.d.ts.map +1 -0
- package/dist/browser/index.d.ts +11 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.mjs +233 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/browser/withDb.d.ts +6 -0
- package/dist/browser/withDb.d.ts.map +1 -0
- package/dist/browser/withReadOnlyStore.d.ts +6 -0
- package/dist/browser/withReadOnlyStore.d.ts.map +1 -0
- package/dist/browser/withReadWriteStore.d.ts +6 -0
- package/dist/browser/withReadWriteStore.d.ts.map +1 -0
- package/dist/browser/withStore.d.ts +8 -0
- package/dist/browser/withStore.d.ts.map +1 -0
- package/dist/neutral/IndexDescription.d.ts +0 -4
- package/dist/neutral/IndexDescription.d.ts.map +1 -1
- package/dist/neutral/IndexedDbKeyValueStore.d.ts +3 -3
- package/dist/neutral/IndexedDbKeyValueStore.d.ts.map +1 -1
- package/dist/neutral/checkDbNeedsUpgrade.d.ts +7 -0
- package/dist/neutral/checkDbNeedsUpgrade.d.ts.map +1 -0
- package/dist/neutral/createStoreDuringUpgrade.d.ts +5 -0
- package/dist/neutral/createStoreDuringUpgrade.d.ts.map +1 -0
- package/dist/neutral/getExistingIndexes.d.ts +2 -1
- package/dist/neutral/getExistingIndexes.d.ts.map +1 -1
- package/dist/neutral/index.d.ts +3 -1
- package/dist/neutral/index.d.ts.map +1 -1
- package/dist/neutral/index.mjs +131 -33
- package/dist/neutral/index.mjs.map +1 -1
- package/dist/neutral/withDb.d.ts +3 -1
- package/dist/neutral/withDb.d.ts.map +1 -1
- package/dist/neutral/withReadOnlyStore.d.ts +2 -1
- package/dist/neutral/withReadOnlyStore.d.ts.map +1 -1
- package/dist/neutral/withReadWriteStore.d.ts +2 -1
- package/dist/neutral/withReadWriteStore.d.ts.map +1 -1
- package/dist/neutral/withStore.d.ts +8 -0
- package/dist/neutral/withStore.d.ts.map +1 -0
- package/dist/node/IndexDescription.d.ts +30 -0
- package/dist/node/IndexDescription.d.ts.map +1 -0
- package/dist/node/IndexedDbKeyValueStore.d.ts +17 -0
- package/dist/node/IndexedDbKeyValueStore.d.ts.map +1 -0
- package/dist/node/ObjectStore.d.ts +5 -0
- package/dist/node/ObjectStore.d.ts.map +1 -0
- package/dist/node/checkDbNeedsUpgrade.d.ts +7 -0
- package/dist/node/checkDbNeedsUpgrade.d.ts.map +1 -0
- package/dist/node/createStoreDuringUpgrade.d.ts +5 -0
- package/dist/node/createStoreDuringUpgrade.d.ts.map +1 -0
- package/dist/node/getExistingIndexes.d.ts +7 -0
- package/dist/node/getExistingIndexes.d.ts.map +1 -0
- package/dist/node/index.d.ts +11 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.mjs +233 -0
- package/dist/node/index.mjs.map +1 -0
- package/dist/node/withDb.d.ts +6 -0
- package/dist/node/withDb.d.ts.map +1 -0
- package/dist/node/withReadOnlyStore.d.ts +6 -0
- package/dist/node/withReadOnlyStore.d.ts.map +1 -0
- package/dist/node/withReadWriteStore.d.ts +6 -0
- package/dist/node/withReadWriteStore.d.ts.map +1 -0
- package/dist/node/withStore.d.ts +8 -0
- package/dist/node/withStore.d.ts.map +1 -0
- package/package.json +23 -9
- package/src/IndexDescription.ts +0 -4
- package/src/IndexedDbKeyValueStore.ts +3 -3
- package/src/checkDbNeedsUpgrade.ts +37 -0
- package/src/{createStore.ts → createStoreDuringUpgrade.ts} +17 -10
- package/src/getExistingIndexes.ts +39 -20
- package/src/index.ts +3 -1
- package/src/withDb.ts +50 -4
- package/src/withReadOnlyStore.ts +5 -8
- package/src/withReadWriteStore.ts +5 -8
- package/src/withStore.ts +34 -0
- package/xy.config.ts +2 -2
- 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.
|
|
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
|
-
"
|
|
32
|
-
|
|
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/
|
|
37
|
-
"types": "./dist/
|
|
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/
|
|
43
|
-
"@xylabs/
|
|
44
|
-
"@xylabs/
|
|
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/
|
|
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
|
},
|
package/src/IndexDescription.ts
CHANGED
|
@@ -18,13 +18,13 @@ export class IndexedDbKeyValueStore<T extends DBSchema, S extends StoreNames<T>>
|
|
|
18
18
|
})
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
async delete(key:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
14
|
-
db: IDBPDatabase<
|
|
15
|
-
storeName: StoreNames<
|
|
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
|
-
|
|
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
|
-
|
|
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<
|
|
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
|
-
|
|
13
|
+
async function getExistingIndexesInternal<T extends EmptyObject = EmptyObject>(
|
|
12
14
|
db: IDBPDatabase<ObjectStore<T>>,
|
|
13
15
|
storeName: StoreNames<ObjectStore<T>>,
|
|
14
|
-
|
|
16
|
+
logger?: Logger,
|
|
17
|
+
): Promise<IndexDescription[] | null> {
|
|
18
|
+
logger?.log('getExistingIndexesInternal', storeName)
|
|
15
19
|
return await withReadOnlyStore(db, storeName, (store) => {
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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 './
|
|
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'
|