@firtoz/drizzle-indexeddb 0.5.0 → 0.6.0
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 +21 -2
- package/README.md +2 -2
- package/package.json +9 -9
- package/src/collections/indexeddb-collection.ts +31 -26
- package/src/context/DrizzleIndexedDBProvider.tsx +1 -1
- package/src/index.ts +1 -1
- package/src/proxy/idb-proxy-client.ts +3 -7
- package/src/proxy/idb-proxy-server.ts +1 -1
- package/src/proxy/idb-proxy-types.ts +1 -1
- package/src/proxy/idb-sync-adapter.ts +1 -1
- package/src/proxy/index.ts +1 -1
- package/src/standalone-collection.ts +15 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @firtoz/drizzle-indexeddb
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`a08a986`](https://github.com/firtoz/fullstack-toolkit/commit/a08a986cc5161b20c9c875328e49565c15417ffc) Thanks [@firtoz](https://github.com/firtoz)! - Slight refactor
|
|
8
|
+
|
|
9
|
+
- Renamed `createProxyDbCreator` to `createProxyIDbCreator` for consistency across the codebase.
|
|
10
|
+
- Updated server sync message type from `sync:clear` to `sync:truncate` to better reflect its functionality.
|
|
11
|
+
- Adjusted related documentation and test cases to align with these changes.
|
|
12
|
+
|
|
13
|
+
## 0.5.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [`8abab0a`](https://github.com/firtoz/fullstack-toolkit/commit/8abab0ae7a99320a4254cb128c0fd823726e58e0) Thanks [@firtoz](https://github.com/firtoz)! - Add cursor-based and offset-based pagination support to `loadSubset` operations, enabling efficient navigation through large datasets with consistent behavior across collection backends.
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [[`8abab0a`](https://github.com/firtoz/fullstack-toolkit/commit/8abab0ae7a99320a4254cb128c0fd823726e58e0)]:
|
|
20
|
+
- @firtoz/drizzle-utils@0.3.1
|
|
21
|
+
|
|
3
22
|
## 0.5.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
|
@@ -52,12 +71,12 @@
|
|
|
52
71
|
- **`IDBProxyServer`** - Server that manages database lifecycle, migrations, and broadcasts mutations to connected clients
|
|
53
72
|
- **`IDBProxyClient`** - Client implementing `IDBDatabaseLike`, routing operations through a transport layer
|
|
54
73
|
- **`createMultiClientTransport()`** - In-memory transport for testing N clients connected to one server
|
|
55
|
-
- **`
|
|
74
|
+
- **`createProxyIDbCreator()`** - Factory to create `dbCreator` for `DrizzleIndexedDBProvider`
|
|
56
75
|
- **`createCollectionSyncHandler()`** - Adapter connecting proxy sync messages to collection's external sync
|
|
57
76
|
|
|
58
77
|
**Real-time Multi-Client Sync**:
|
|
59
78
|
|
|
60
|
-
- Server broadcasts `sync:add`, `sync:put`, `sync:delete`, `sync:
|
|
79
|
+
- Server broadcasts `sync:add`, `sync:put`, `sync:delete`, `sync:truncate` messages to all clients (excluding initiator)
|
|
61
80
|
- All mutations automatically sync across connected clients
|
|
62
81
|
|
|
63
82
|
**Provider Enhancements**:
|
package/README.md
CHANGED
|
@@ -460,7 +460,7 @@ For scenarios where IndexedDB needs to be accessed over a messaging layer (e.g.,
|
|
|
460
460
|
import {
|
|
461
461
|
createMultiClientTransport,
|
|
462
462
|
createProxyServer,
|
|
463
|
-
|
|
463
|
+
createProxyIDbCreator,
|
|
464
464
|
migrateIndexedDBWithFunctions,
|
|
465
465
|
DrizzleIndexedDBProvider,
|
|
466
466
|
} from "@firtoz/drizzle-indexeddb";
|
|
@@ -478,7 +478,7 @@ const server = createProxyServer({
|
|
|
478
478
|
|
|
479
479
|
// Create client
|
|
480
480
|
const clientTransport = createClientTransport();
|
|
481
|
-
const dbCreator =
|
|
481
|
+
const dbCreator = createProxyIDbCreator(clientTransport);
|
|
482
482
|
|
|
483
483
|
// Use with React provider
|
|
484
484
|
function App() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firtoz/drizzle-indexeddb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "IndexedDB migrations powered by Drizzle ORM",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"module": "./src/index.ts",
|
|
@@ -68,20 +68,20 @@
|
|
|
68
68
|
"access": "public"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@firtoz/drizzle-utils": ">=0.3.
|
|
72
|
-
"@tanstack/db": ">=0.5.
|
|
73
|
-
"drizzle-orm": ">=0.
|
|
71
|
+
"@firtoz/drizzle-utils": ">=0.3.1",
|
|
72
|
+
"@tanstack/db": ">=0.5.16",
|
|
73
|
+
"drizzle-orm": ">=0.45.1",
|
|
74
74
|
"drizzle-valibot": ">=0.4.0",
|
|
75
|
-
"react": ">=
|
|
75
|
+
"react": ">=19.2.3",
|
|
76
76
|
"valibot": ">=1.0.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@firtoz/drizzle-utils": "^0.3.
|
|
80
|
-
"@tanstack/db": "^0.5.
|
|
79
|
+
"@firtoz/drizzle-utils": "^0.3.1",
|
|
80
|
+
"@tanstack/db": "^0.5.16",
|
|
81
81
|
"@types/react": "^19.2.7",
|
|
82
|
-
"drizzle-orm": "^0.45.
|
|
82
|
+
"drizzle-orm": "^0.45.1",
|
|
83
83
|
"drizzle-valibot": "^0.4.2",
|
|
84
|
-
"react": "^19.2.
|
|
84
|
+
"react": "^19.2.3",
|
|
85
85
|
"valibot": "^1.2.0"
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
@@ -381,14 +381,30 @@ export function indexedDBCollectionOptions<const TTable extends Table>(
|
|
|
381
381
|
|
|
382
382
|
let items: IndexedDBSyncItem[];
|
|
383
383
|
|
|
384
|
+
// Combine where with cursor expressions if present
|
|
385
|
+
// The cursor.whereFrom gives us rows after the cursor position
|
|
386
|
+
let combinedWhere = options.where;
|
|
387
|
+
if (options.cursor?.whereFrom) {
|
|
388
|
+
if (combinedWhere) {
|
|
389
|
+
// Combine main where with cursor expression using AND
|
|
390
|
+
combinedWhere = {
|
|
391
|
+
type: "func",
|
|
392
|
+
name: "and",
|
|
393
|
+
args: [combinedWhere, options.cursor.whereFrom],
|
|
394
|
+
} as IR.Func;
|
|
395
|
+
} else {
|
|
396
|
+
combinedWhere = options.cursor.whereFrom;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
384
400
|
// Try to use an index for efficient querying
|
|
385
|
-
const indexedQuery =
|
|
386
|
-
? tryExtractIndexedQuery(
|
|
401
|
+
const indexedQuery = combinedWhere
|
|
402
|
+
? tryExtractIndexedQuery(combinedWhere, discoveredIndexes, config.debug)
|
|
387
403
|
: null;
|
|
388
404
|
|
|
389
405
|
if (indexedQuery) {
|
|
390
406
|
// Use indexed query for better performance
|
|
391
|
-
|
|
407
|
+
// Index returns exact results for single-field queries, no additional filtering needed
|
|
392
408
|
items = await db.getAllByIndex<IndexedDBSyncItem>(
|
|
393
409
|
config.storeName,
|
|
394
410
|
indexedQuery.indexName,
|
|
@@ -398,9 +414,9 @@ export function indexedDBCollectionOptions<const TTable extends Table>(
|
|
|
398
414
|
// Fall back to getting all items
|
|
399
415
|
items = await db.getAll<IndexedDBSyncItem>(config.storeName);
|
|
400
416
|
|
|
401
|
-
// Apply where filter in memory
|
|
402
|
-
if (
|
|
403
|
-
const whereExpression =
|
|
417
|
+
// Apply combined where filter in memory
|
|
418
|
+
if (combinedWhere) {
|
|
419
|
+
const whereExpression = combinedWhere;
|
|
404
420
|
items = items.filter((item) =>
|
|
405
421
|
evaluateExpression(
|
|
406
422
|
whereExpression,
|
|
@@ -436,6 +452,11 @@ export function indexedDBCollectionOptions<const TTable extends Table>(
|
|
|
436
452
|
});
|
|
437
453
|
}
|
|
438
454
|
|
|
455
|
+
// Apply offset (skip first N items for pagination)
|
|
456
|
+
if (options.offset !== undefined && options.offset > 0) {
|
|
457
|
+
items = items.slice(options.offset);
|
|
458
|
+
}
|
|
459
|
+
|
|
439
460
|
// Apply limit
|
|
440
461
|
if (options.limit !== undefined) {
|
|
441
462
|
items = items.slice(0, options.limit);
|
|
@@ -446,32 +467,16 @@ export function indexedDBCollectionOptions<const TTable extends Table>(
|
|
|
446
467
|
}
|
|
447
468
|
},
|
|
448
469
|
|
|
449
|
-
handleInsert: async (
|
|
470
|
+
handleInsert: async (itemsToInsert) => {
|
|
450
471
|
const db = config.indexedDBRef.current;
|
|
451
472
|
if (!db) {
|
|
452
473
|
throw new Error("Database not ready");
|
|
453
474
|
}
|
|
454
475
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
try {
|
|
458
|
-
const itemsToInsert: IndexedDBSyncItem[] = [];
|
|
459
|
-
|
|
460
|
-
for (const mutation of mutations) {
|
|
461
|
-
const itemToInsert = mutation.modified;
|
|
462
|
-
results.push(itemToInsert);
|
|
463
|
-
itemsToInsert.push(itemToInsert as IndexedDBSyncItem);
|
|
464
|
-
}
|
|
476
|
+
// Add all items in a single batch operation
|
|
477
|
+
await db.add(config.storeName, itemsToInsert);
|
|
465
478
|
|
|
466
|
-
|
|
467
|
-
await db.add(config.storeName, itemsToInsert);
|
|
468
|
-
} catch (error) {
|
|
469
|
-
// Clear results on error so nothing gets written to reactive store
|
|
470
|
-
results.length = 0;
|
|
471
|
-
throw error;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return results;
|
|
479
|
+
return itemsToInsert;
|
|
475
480
|
},
|
|
476
481
|
|
|
477
482
|
handleUpdate: async (mutations) => {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
IDBDatabaseLike,
|
|
3
3
|
IDBCreator,
|
|
4
|
-
IDBOpenOptions,
|
|
5
4
|
IndexInfo,
|
|
6
5
|
CreateStoreOptions,
|
|
7
6
|
CreateIndexOptions,
|
|
@@ -294,13 +293,13 @@ export class IDBProxyClient implements IDBDatabaseLike {
|
|
|
294
293
|
* @param onSync Optional handler called when any sync message is received
|
|
295
294
|
*
|
|
296
295
|
* @example
|
|
297
|
-
* const dbCreator =
|
|
296
|
+
* const dbCreator = createProxyIDbCreator(transport, (msg) => {
|
|
298
297
|
* console.log('Sync:', msg.type, msg.storeName);
|
|
299
298
|
* });
|
|
300
299
|
*
|
|
301
300
|
* <DrizzleIndexedDBProvider dbCreator={dbCreator} ... />
|
|
302
301
|
*/
|
|
303
|
-
export function
|
|
302
|
+
export function createProxyIDbCreator(
|
|
304
303
|
transport: IDBProxyClientTransport,
|
|
305
304
|
onSync?: SyncHandler,
|
|
306
305
|
): IDBCreator {
|
|
@@ -308,10 +307,7 @@ export function createProxyDbCreator(
|
|
|
308
307
|
const clientCache = new Map<string, IDBProxyClient>();
|
|
309
308
|
const connectingCache = new Map<string, Promise<IDBProxyClient>>();
|
|
310
309
|
|
|
311
|
-
return async (
|
|
312
|
-
name: string,
|
|
313
|
-
_options?: IDBOpenOptions,
|
|
314
|
-
): Promise<IDBDatabaseLike> => {
|
|
310
|
+
return async (name: string): Promise<IDBDatabaseLike> => {
|
|
315
311
|
// Return cached client if already connected
|
|
316
312
|
const cached = clientCache.get(name);
|
|
317
313
|
if (cached) {
|
package/src/proxy/index.ts
CHANGED
|
@@ -251,8 +251,8 @@ export function createStandaloneCollection<TTable extends Table>(
|
|
|
251
251
|
if (debug) {
|
|
252
252
|
console.log(`[StandaloneCollection] Database "${dbName}" initialized`);
|
|
253
253
|
}
|
|
254
|
-
|
|
255
|
-
resolveReady
|
|
254
|
+
|
|
255
|
+
resolveReady();
|
|
256
256
|
} catch (error) {
|
|
257
257
|
console.error(
|
|
258
258
|
`[StandaloneCollection] Failed to initialize database "${dbName}":`,
|
|
@@ -294,16 +294,15 @@ export function createStandaloneCollection<TTable extends Table>(
|
|
|
294
294
|
const ready = Promise.all([readyPromise, collectionReady]).then(() => {});
|
|
295
295
|
|
|
296
296
|
// Helper to wait for transaction to persist
|
|
297
|
-
const waitForPersist = (
|
|
298
|
-
|
|
299
|
-
transaction:
|
|
300
|
-
// biome-ignore lint/suspicious/noExplicitAny: Transaction types are complex, runtime is correct
|
|
301
|
-
callback?: (transaction: any) => void,
|
|
297
|
+
const waitForPersist = async (
|
|
298
|
+
transaction: MutationTransaction<TTable>,
|
|
299
|
+
callback?: (transaction: MutationTransaction<TTable>) => void,
|
|
302
300
|
): Promise<MutationTransaction<TTable>> => {
|
|
303
301
|
if (callback) {
|
|
304
302
|
callback(transaction);
|
|
305
303
|
}
|
|
306
|
-
|
|
304
|
+
await transaction.isPersisted.promise;
|
|
305
|
+
return transaction;
|
|
307
306
|
};
|
|
308
307
|
|
|
309
308
|
return {
|
|
@@ -325,9 +324,12 @@ export function createStandaloneCollection<TTable extends Table>(
|
|
|
325
324
|
data: InsertInput<TTable> | InsertInput<TTable>[],
|
|
326
325
|
callback?: (transaction: MutationTransaction<TTable>) => void,
|
|
327
326
|
): Promise<MutationTransaction<TTable>> {
|
|
328
|
-
const items = Array.isArray(data) ? data : [data]
|
|
329
|
-
|
|
330
|
-
|
|
327
|
+
const items = (Array.isArray(data) ? data : [data]) as InferSchemaOutput<
|
|
328
|
+
SelectSchema<TTable>
|
|
329
|
+
>;
|
|
330
|
+
const transaction = collection.insert(
|
|
331
|
+
items,
|
|
332
|
+
) as MutationTransaction<TTable>;
|
|
331
333
|
return waitForPersist(transaction, callback);
|
|
332
334
|
},
|
|
333
335
|
|
|
@@ -338,8 +340,8 @@ export function createStandaloneCollection<TTable extends Table>(
|
|
|
338
340
|
): Promise<MutationTransaction<TTable>> {
|
|
339
341
|
const transaction = collection.update(
|
|
340
342
|
key,
|
|
341
|
-
updater
|
|
342
|
-
)
|
|
343
|
+
updater,
|
|
344
|
+
) as MutationTransaction<TTable>;
|
|
343
345
|
return waitForPersist(transaction, callback);
|
|
344
346
|
},
|
|
345
347
|
|