@firtoz/drizzle-indexeddb 0.4.1 → 0.4.2
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @firtoz/drizzle-indexeddb
|
|
2
2
|
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`58afa0a`](https://github.com/firtoz/fullstack-toolkit/commit/58afa0a5365f55f536e50194a73f847293102e7f) Thanks [@firtoz](https://github.com/firtoz)! - Hopefully this should work
|
|
8
|
+
|
|
3
9
|
## 0.4.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firtoz/drizzle-indexeddb",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "IndexedDB migrations powered by Drizzle ORM",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"module": "./src/index.ts",
|
|
@@ -42,9 +42,7 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit -p ./tsconfig.json",
|
|
43
43
|
"lint": "biome check --write src",
|
|
44
44
|
"lint:ci": "biome ci src",
|
|
45
|
-
"format": "biome format src --write"
|
|
46
|
-
"test": "bun test --pass-with-no-tests",
|
|
47
|
-
"test:watch": "bun test --watch"
|
|
45
|
+
"format": "biome format src --write"
|
|
48
46
|
},
|
|
49
47
|
"keywords": [
|
|
50
48
|
"typescript",
|
|
@@ -69,17 +67,21 @@
|
|
|
69
67
|
"publishConfig": {
|
|
70
68
|
"access": "public"
|
|
71
69
|
},
|
|
72
|
-
"
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"@firtoz/drizzle-utils": ">=0.3.0",
|
|
72
|
+
"@tanstack/db": ">=0.5.0",
|
|
73
|
+
"drizzle-orm": ">=0.44.0",
|
|
74
|
+
"drizzle-valibot": ">=0.4.0",
|
|
75
|
+
"react": ">=18.0.0",
|
|
76
|
+
"valibot": ">=1.0.0"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
73
79
|
"@firtoz/drizzle-utils": "^0.3.0",
|
|
74
80
|
"@tanstack/db": "^0.5.11",
|
|
81
|
+
"@types/react": "^19.2.7",
|
|
75
82
|
"drizzle-orm": "^0.45.0",
|
|
76
83
|
"drizzle-valibot": "^0.4.2",
|
|
84
|
+
"react": "^19.2.1",
|
|
77
85
|
"valibot": "^1.2.0"
|
|
78
|
-
},
|
|
79
|
-
"peerDependencies": {
|
|
80
|
-
"react": "^19.2.1"
|
|
81
|
-
},
|
|
82
|
-
"devDependencies": {
|
|
83
|
-
"@types/react": "^19.2.7"
|
|
84
86
|
}
|
|
85
87
|
}
|
|
@@ -134,10 +134,9 @@ export function DrizzleIndexedDBProvider<
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
useEffect(() => {
|
|
137
|
+
let db: IDBDatabaseLike | null = null;
|
|
137
138
|
const initDB = async () => {
|
|
138
139
|
try {
|
|
139
|
-
let db: IDBDatabaseLike;
|
|
140
|
-
|
|
141
140
|
if (migrations.length === 0) {
|
|
142
141
|
// Open database directly without migration logic
|
|
143
142
|
db = await openIndexedDb(dbName, dbCreator);
|
|
@@ -161,11 +160,11 @@ export function DrizzleIndexedDBProvider<
|
|
|
161
160
|
|
|
162
161
|
// Cleanup on unmount
|
|
163
162
|
return () => {
|
|
164
|
-
if (
|
|
165
|
-
|
|
163
|
+
if (db) {
|
|
164
|
+
db.close();
|
|
166
165
|
}
|
|
167
166
|
};
|
|
168
|
-
}, [dbName, migrations, migrateFunction, debug, readyPromise]);
|
|
167
|
+
}, [dbName, migrations, migrateFunction, debug, readyPromise, dbCreator]);
|
|
169
168
|
|
|
170
169
|
// Collection cache with ref counting
|
|
171
170
|
const collections = useMemo<Map<string, CollectionCacheEntry>>(
|
|
@@ -219,15 +218,7 @@ export function DrizzleIndexedDBProvider<
|
|
|
219
218
|
return collections.get(cacheKey)!
|
|
220
219
|
.collection as unknown as IndexedDbCollection<TSchema, TTableName>;
|
|
221
220
|
},
|
|
222
|
-
[
|
|
223
|
-
indexedDBRef,
|
|
224
|
-
collections,
|
|
225
|
-
schema,
|
|
226
|
-
readyPromise.promise,
|
|
227
|
-
debug,
|
|
228
|
-
dbName,
|
|
229
|
-
syncMode,
|
|
230
|
-
],
|
|
221
|
+
[collections, schema, readyPromise.promise, debug, dbName, syncMode],
|
|
231
222
|
);
|
|
232
223
|
|
|
233
224
|
const incrementRefCount: DrizzleIndexedDBContextValue<TSchema>["incrementRefCount"] =
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext } from "react";
|
|
1
|
+
import { useCallback, useContext } from "react";
|
|
2
2
|
import {
|
|
3
3
|
DrizzleIndexedDBContext,
|
|
4
4
|
useIndexedDBCollection,
|
|
@@ -28,10 +28,15 @@ export function useDrizzleIndexedDB<
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
const useCollection = useCallback(
|
|
32
|
+
<TTableName extends keyof TSchema & string>(tableName: TTableName) =>
|
|
33
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: This is on purpose.
|
|
34
|
+
useIndexedDBCollection(context, tableName),
|
|
35
|
+
[context],
|
|
36
|
+
);
|
|
37
|
+
|
|
31
38
|
return {
|
|
32
|
-
useCollection
|
|
33
|
-
tableName: TTableName,
|
|
34
|
-
) => useIndexedDBCollection(context, tableName),
|
|
39
|
+
useCollection,
|
|
35
40
|
indexedDB: context.indexedDB,
|
|
36
41
|
};
|
|
37
42
|
}
|
package/src/idb-operations.ts
CHANGED
|
@@ -23,9 +23,14 @@ export async function openIndexedDb(
|
|
|
23
23
|
*/
|
|
24
24
|
const defaultIDBDeleter: IDBDeleter = (name: string): Promise<void> => {
|
|
25
25
|
return new Promise((resolve, reject) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
try {
|
|
27
|
+
const request = indexedDB.deleteDatabase(name);
|
|
28
|
+
request.onerror = () => reject(request.error);
|
|
29
|
+
request.onsuccess = () => resolve();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("Error deleting database", error);
|
|
32
|
+
reject(error);
|
|
33
|
+
}
|
|
29
34
|
});
|
|
30
35
|
};
|
|
31
36
|
|
|
@@ -104,12 +104,17 @@ class NativeIDBDatabase implements IDBDatabaseLike {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
return new Promise((resolve, reject) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
try {
|
|
108
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
109
|
+
const store = transaction.objectStore(storeName);
|
|
110
|
+
const request = store.getAll();
|
|
111
|
+
|
|
112
|
+
request.onsuccess = () => resolve(request.result as T[]);
|
|
113
|
+
request.onerror = () => reject(request.error);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error("Error getting all items", error);
|
|
116
|
+
reject(error);
|
|
117
|
+
}
|
|
113
118
|
});
|
|
114
119
|
}
|
|
115
120
|
|
|
@@ -119,14 +124,19 @@ class NativeIDBDatabase implements IDBDatabaseLike {
|
|
|
119
124
|
keyRange?: KeyRangeSpec,
|
|
120
125
|
): Promise<T[]> {
|
|
121
126
|
return new Promise((resolve, reject) => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
try {
|
|
128
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
129
|
+
const store = transaction.objectStore(storeName);
|
|
130
|
+
const index = store.index(indexName);
|
|
131
|
+
const range = keyRange ? createKeyRange(keyRange) : undefined;
|
|
132
|
+
const request = index.getAll(range);
|
|
133
|
+
|
|
134
|
+
request.onsuccess = () => resolve(request.result as T[]);
|
|
135
|
+
request.onerror = () => reject(request.error);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error("Error getting all items by index", error);
|
|
138
|
+
reject(error);
|
|
139
|
+
}
|
|
130
140
|
});
|
|
131
141
|
}
|
|
132
142
|
|
|
@@ -135,72 +145,98 @@ class NativeIDBDatabase implements IDBDatabaseLike {
|
|
|
135
145
|
key: IDBValidKey,
|
|
136
146
|
): Promise<T | undefined> {
|
|
137
147
|
return new Promise((resolve, reject) => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
try {
|
|
149
|
+
const transaction = this.db.transaction(storeName, "readonly");
|
|
150
|
+
const store = transaction.objectStore(storeName);
|
|
151
|
+
const request = store.get(key);
|
|
152
|
+
|
|
153
|
+
request.onsuccess = () => resolve(request.result as T | undefined);
|
|
154
|
+
request.onerror = () => reject(request.error);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error("Error getting item", error);
|
|
157
|
+
reject(error);
|
|
158
|
+
}
|
|
144
159
|
});
|
|
145
160
|
}
|
|
146
161
|
|
|
147
162
|
async add(storeName: string, items: unknown[]): Promise<void> {
|
|
148
163
|
return new Promise((resolve, reject) => {
|
|
149
|
-
|
|
150
|
-
|
|
164
|
+
try {
|
|
165
|
+
const transaction = this.db.transaction(storeName, "readwrite");
|
|
166
|
+
const store = transaction.objectStore(storeName);
|
|
151
167
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
168
|
+
for (const item of items) {
|
|
169
|
+
store.add(item);
|
|
170
|
+
}
|
|
155
171
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
172
|
+
transaction.oncomplete = () => resolve();
|
|
173
|
+
transaction.onerror = () => reject(transaction.error);
|
|
174
|
+
transaction.onabort = () => reject(new Error("Transaction aborted"));
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error("Error adding items", error);
|
|
177
|
+
reject(error);
|
|
178
|
+
}
|
|
159
179
|
});
|
|
160
180
|
}
|
|
161
181
|
|
|
162
182
|
async put(storeName: string, items: unknown[]): Promise<void> {
|
|
163
183
|
return new Promise((resolve, reject) => {
|
|
164
|
-
|
|
165
|
-
|
|
184
|
+
try {
|
|
185
|
+
const transaction = this.db.transaction(storeName, "readwrite");
|
|
186
|
+
const store = transaction.objectStore(storeName);
|
|
166
187
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
188
|
+
for (const item of items) {
|
|
189
|
+
store.put(item);
|
|
190
|
+
}
|
|
170
191
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
192
|
+
transaction.oncomplete = () => resolve();
|
|
193
|
+
transaction.onerror = () => reject(transaction.error);
|
|
194
|
+
transaction.onabort = () => reject(new Error("Transaction aborted"));
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error("Error putting items", error);
|
|
197
|
+
reject(error);
|
|
198
|
+
}
|
|
174
199
|
});
|
|
175
200
|
}
|
|
176
201
|
|
|
177
202
|
async delete(storeName: string, keys: IDBValidKey[]): Promise<void> {
|
|
178
203
|
return new Promise((resolve, reject) => {
|
|
179
|
-
|
|
180
|
-
|
|
204
|
+
try {
|
|
205
|
+
const transaction = this.db.transaction(storeName, "readwrite");
|
|
206
|
+
const store = transaction.objectStore(storeName);
|
|
181
207
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
208
|
+
for (const key of keys) {
|
|
209
|
+
store.delete(key);
|
|
210
|
+
}
|
|
185
211
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
212
|
+
transaction.oncomplete = () => resolve();
|
|
213
|
+
transaction.onerror = () => reject(transaction.error);
|
|
214
|
+
transaction.onabort = () => reject(new Error("Transaction aborted"));
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error("Error deleting items", error);
|
|
217
|
+
reject(error);
|
|
218
|
+
}
|
|
189
219
|
});
|
|
190
220
|
}
|
|
191
221
|
|
|
192
222
|
async clear(storeName: string): Promise<void> {
|
|
193
223
|
return new Promise((resolve, reject) => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
224
|
+
try {
|
|
225
|
+
const transaction = this.db.transaction(storeName, "readwrite");
|
|
226
|
+
const store = transaction.objectStore(storeName);
|
|
227
|
+
const request = store.clear();
|
|
228
|
+
|
|
229
|
+
request.onsuccess = () => resolve();
|
|
230
|
+
request.onerror = () => reject(request.error);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error("Error clearing store", error);
|
|
233
|
+
reject(error);
|
|
234
|
+
}
|
|
200
235
|
});
|
|
201
236
|
}
|
|
202
237
|
|
|
203
238
|
close(): void {
|
|
239
|
+
console.log("Closing database");
|
|
204
240
|
this.db.close();
|
|
205
241
|
}
|
|
206
242
|
}
|
|
@@ -319,37 +355,42 @@ export const defaultIDBCreator: IDBCreator = (
|
|
|
319
355
|
options?: IDBOpenOptions,
|
|
320
356
|
): Promise<IDBDatabaseLike> => {
|
|
321
357
|
return new Promise((resolve, reject) => {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
request.onerror = () => reject(request.error);
|
|
358
|
+
try {
|
|
359
|
+
const request = options?.version
|
|
360
|
+
? indexedDB.open(name, options.version)
|
|
361
|
+
: indexedDB.open(name);
|
|
327
362
|
|
|
328
|
-
|
|
329
|
-
setTimeout(() => {
|
|
330
|
-
reject(new Error("Database upgrade blocked - close other tabs"));
|
|
331
|
-
}, 3000);
|
|
332
|
-
};
|
|
363
|
+
request.onerror = () => reject(request.error);
|
|
333
364
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
365
|
+
request.onblocked = () => {
|
|
366
|
+
setTimeout(() => {
|
|
367
|
+
reject(new Error("Database upgrade blocked - close other tabs"));
|
|
368
|
+
}, 3000);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
request.onupgradeneeded = (event) => {
|
|
372
|
+
if (options?.onUpgrade) {
|
|
373
|
+
const db = request.result;
|
|
374
|
+
const transaction = (event.target as IDBOpenDBRequest).transaction;
|
|
375
|
+
if (!transaction) {
|
|
376
|
+
reject(new Error("No transaction during upgrade"));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
// Create an upgrade-mode database wrapper
|
|
380
|
+
const upgradeDb = new UpgradeModeDatabase(db, transaction);
|
|
381
|
+
try {
|
|
382
|
+
options.onUpgrade(upgradeDb);
|
|
383
|
+
} catch (error) {
|
|
384
|
+
transaction.abort();
|
|
385
|
+
reject(error);
|
|
386
|
+
}
|
|
341
387
|
}
|
|
342
|
-
|
|
343
|
-
const upgradeDb = new UpgradeModeDatabase(db, transaction);
|
|
344
|
-
try {
|
|
345
|
-
options.onUpgrade(upgradeDb);
|
|
346
|
-
} catch (error) {
|
|
347
|
-
transaction.abort();
|
|
348
|
-
reject(error);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
};
|
|
388
|
+
};
|
|
352
389
|
|
|
353
|
-
|
|
390
|
+
request.onsuccess = () => resolve(new NativeIDBDatabase(request.result));
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error("Error creating database", error);
|
|
393
|
+
reject(error);
|
|
394
|
+
}
|
|
354
395
|
});
|
|
355
396
|
};
|