@firtoz/drizzle-indexeddb 0.1.0 → 0.3.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 CHANGED
@@ -1,5 +1,44 @@
1
1
  # @firtoz/drizzle-indexeddb
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`5e854a6`](https://github.com/firtoz/fullstack-toolkit/commit/5e854a62236a811918a47037a59df23329856614) Thanks [@firtoz](https://github.com/firtoz)! - ### Breaking Changes
8
+
9
+ - Removed `migrateIndexedDB` and `IndexedDBMigrationConfig` exports - use `migrateIndexedDBWithFunctions` instead
10
+ - Removed snapshot-based migration system in favor of function-based migrations
11
+
12
+ ### New Features
13
+
14
+ - Added `drizzle-indexeddb-generate` CLI tool to generate IndexedDB migration functions from Drizzle snapshots
15
+ - Added `generateIndexedDBMigrations` export for programmatic migration generation
16
+ - Added `./generate` export path
17
+
18
+ ### Migration Guide
19
+
20
+ Instead of importing snapshots directly and using `migrateIndexedDB`, you now:
21
+
22
+ 1. Run `bun drizzle-indexeddb-generate` (or `npx drizzle-indexeddb-generate`) after `drizzle-kit generate`
23
+ 2. Import the generated migrations and use `migrateIndexedDBWithFunctions`
24
+
25
+ ## 0.2.0
26
+
27
+ ### Minor Changes
28
+
29
+ - [`58d2cba`](https://github.com/firtoz/fullstack-toolkit/commit/58d2cbac8ea4e540b5460b7088b6b62e50357558) Thanks [@firtoz](https://github.com/firtoz)! - Add sync mode functionality for IndexedDB and SQLite collections
30
+
31
+ - Introduced support for both eager and on-demand sync modes in Drizzle providers
32
+ - Implemented operation tracking via interceptors to monitor database operations during queries
33
+ - Enhanced DrizzleIndexedDBProvider and DrizzleSqliteProvider to accept interceptors for debugging and testing purposes
34
+ - Added createInsertSchemaWithDefaults and createInsertSchemaWithIdDefault utilities for better schema management
35
+ - Refactored collection utilities to improve data handling and consistency across collections
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies [[`58d2cba`](https://github.com/firtoz/fullstack-toolkit/commit/58d2cbac8ea4e540b5460b7088b6b62e50357558)]:
40
+ - @firtoz/drizzle-utils@0.2.0
41
+
3
42
  ## 0.1.0
4
43
 
5
44
  ### Minor Changes
@@ -24,18 +63,6 @@
24
63
  - Sync configuration for real-time updates
25
64
  - Works seamlessly with React hooks
26
65
 
27
- ### Snapshot-Based Migration
28
-
29
- **`migrateIndexedDB(dbName, config, debug?)`** - Automatically migrates IndexedDB databases using Drizzle snapshot files:
30
-
31
- - Reads Drizzle journal and snapshot files
32
- - Tracks applied migrations in `__drizzle_migrations` store
33
- - Creates/updates object stores and indexes based on schema changes
34
- - Handles table deletion, index changes, and schema evolution
35
- - Incremental migrations - only applies pending changes
36
- - Validates primary key structure changes (requires manual migration if keys change)
37
- - Performance logging when debug mode is enabled
38
-
39
66
  ### Function-Based Migration
40
67
 
41
68
  **`migrateIndexedDBWithFunctions(dbName, migrations, debug?)`** - Run migrations using custom migration functions:
@@ -70,19 +97,15 @@
70
97
  ## Example
71
98
 
72
99
  ```typescript
73
- import { migrateIndexedDB } from "@firtoz/drizzle-indexeddb";
74
- import journal from "./drizzle/journal.json";
75
- import * as snapshots from "./drizzle/snapshots";
100
+ import { migrateIndexedDBWithFunctions } from "@firtoz/drizzle-indexeddb";
101
+ import migrations from "./drizzle/indexeddb-migrations";
76
102
 
77
- // Migrate database using Drizzle snapshots
78
- const db = await migrateIndexedDB(
103
+ // Migrate database using function-based migrations
104
+ const db = await migrateIndexedDBWithFunctions(
79
105
  "my-app-db",
80
- {
81
- journal,
82
- snapshots,
83
- },
84
- true
85
- ); // debug mode
106
+ migrations,
107
+ true // debug mode
108
+ );
86
109
 
87
110
  // Use with TanStack DB
88
111
  import { createCollection } from "@tanstack/db";
@@ -119,9 +142,9 @@
119
142
 
120
143
  ## Migration Workflow
121
144
 
122
- 1. Generate Drizzle snapshots: `drizzle-kit generate`
123
- 2. Import journal and snapshots
124
- 3. Call `migrateIndexedDB()` on app startup
145
+ 1. Generate Drizzle migrations: `drizzle-kit generate`
146
+ 2. Generate IndexedDB migrations: `bun drizzle-indexeddb-generate`
147
+ 3. Import migrations and call `migrateIndexedDBWithFunctions()` on app startup
125
148
  4. Database automatically updates to latest schema
126
149
 
127
150
  ## Dependencies
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # @firtoz/drizzle-indexeddb
2
2
 
3
- TanStack DB collections backed by IndexedDB with automatic migrations powered by Drizzle ORM snapshots. Build reactive, type-safe IndexedDB applications with the power of Drizzle's schema management.
3
+ TanStack DB collections backed by IndexedDB with automatic migrations powered by Drizzle ORM. Build reactive, type-safe IndexedDB applications with the power of Drizzle's schema management.
4
4
 
5
5
  > **⚠️ Early WIP Notice:** This package is in very early development and is **not production-ready**. It is TypeScript-only and may have breaking changes. While I (the maintainer) have limited time, I'm open to PRs for features, bug fixes, or additional support (like JS builds). Please feel free to try it out and contribute! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for details.
6
6
 
7
- > **Note:** This package currently builds on top of Drizzle's SQLite integration (using `drizzle-orm/sqlite-core` types and snapshots) until Drizzle adds native IndexedDB support. The migration system reads Drizzle's SQLite snapshots and translates them into IndexedDB object stores and indexes.
7
+ > **Note:** This package currently builds on top of Drizzle's SQLite integration (using `drizzle-orm/sqlite-core` types) until Drizzle adds native IndexedDB support. The migration system uses function-based migrations generated from Drizzle's SQLite migrations to create IndexedDB object stores and indexes.
8
8
 
9
9
  ## Installation
10
10
 
@@ -19,8 +19,7 @@ npm install @firtoz/drizzle-indexeddb @firtoz/drizzle-utils drizzle-orm @tanstac
19
19
  - 🔍 **Query optimization** - Leverage IndexedDB indexes for fast queries
20
20
  - 📦 **Soft deletes** - Built-in support for `deletedAt` column
21
21
  - ⚛️ **React hooks** - Provider and hooks for easy React integration
22
- - 🔄 **Snapshot-based migrations** - Use Drizzle's generated snapshots to migrate IndexedDB
23
- - 📝 **Function-based migrations** - Write custom migration functions for complex changes
22
+ - 📝 **Function-based migrations** - Generated migration functions from Drizzle schema changes
24
23
 
25
24
  ## Quick Start
26
25
 
@@ -40,22 +39,25 @@ export const todoTable = syncableTable("todos", {
40
39
  ### 2. Generate Migrations
41
40
 
42
41
  ```bash
43
- # Generate Drizzle snapshots
42
+ # Generate Drizzle migrations
44
43
  drizzle-kit generate
44
+
45
+ # Generate IndexedDB migration functions
46
+ bun drizzle-indexeddb-generate
45
47
  ```
46
48
 
47
49
  ### 3. Migrate IndexedDB
48
50
 
49
51
  ```typescript
50
52
  // db.ts
51
- import { migrateIndexedDB } from "@firtoz/drizzle-indexeddb";
52
- import journal from "./drizzle/meta/_journal.json";
53
- import * as snapshots from "./drizzle/snapshots";
54
-
55
- export const db = await migrateIndexedDB("my-app", {
56
- journal,
57
- snapshots,
58
- }, true); // Enable debug logging
53
+ import { migrateIndexedDBWithFunctions } from "@firtoz/drizzle-indexeddb";
54
+ import migrations from "./drizzle/indexeddb-migrations";
55
+
56
+ export const db = await migrateIndexedDBWithFunctions(
57
+ "my-app",
58
+ migrations,
59
+ true // Enable debug logging
60
+ );
59
61
  ```
60
62
 
61
63
  ### 4. Use with React
@@ -181,19 +183,19 @@ collection.find({
181
183
 
182
184
  ## Migration Methods
183
185
 
184
- ### Snapshot-Based Migration
186
+ ### Function-Based Migration
185
187
 
186
- Use Drizzle's snapshot files to automatically migrate your IndexedDB schema:
188
+ Use generated migration functions to migrate your IndexedDB schema:
187
189
 
188
190
  ```typescript
189
- import { migrateIndexedDB } from "@firtoz/drizzle-indexeddb";
190
- import journal from "./drizzle/meta/_journal.json";
191
- import * as snapshots from "./drizzle/snapshots";
191
+ import { migrateIndexedDBWithFunctions } from "@firtoz/drizzle-indexeddb";
192
+ import migrations from "./drizzle/indexeddb-migrations";
192
193
 
193
- const db = await migrateIndexedDB("my-app-db", {
194
- journal,
195
- snapshots,
196
- }, true); // debug flag
194
+ const db = await migrateIndexedDBWithFunctions(
195
+ "my-app-db",
196
+ migrations,
197
+ true // debug flag
198
+ );
197
199
 
198
200
  console.log("Database migrated successfully!");
199
201
  ```
@@ -219,40 +221,49 @@ interface MigrationRecord {
219
221
  }
220
222
  ```
221
223
 
222
- ### Function-Based Migration
224
+ ### Custom Migration Functions
223
225
 
224
- For complex migrations that require custom logic:
226
+ For complex migrations that require custom logic, you can write migration functions directly:
225
227
 
226
228
  ```typescript
227
229
  import { migrateIndexedDBWithFunctions } from "@firtoz/drizzle-indexeddb";
228
230
 
229
231
  const migrations = [
230
232
  // Migration 0: Initial schema
231
- async (db: IDBDatabase, transaction: IDBTransaction) => {
232
- const store = db.createObjectStore("todos", { keyPath: "id" });
233
- store.createIndex("title", "title", { unique: false });
233
+ {
234
+ tag: "0000_initial",
235
+ migrate: async (db: IDBDatabase, transaction: IDBTransaction) => {
236
+ const store = db.createObjectStore("todos", { keyPath: "id" });
237
+ store.createIndex("title", "title", { unique: false });
238
+ },
234
239
  },
235
240
 
236
241
  // Migration 1: Add completed index
237
- async (db: IDBDatabase, transaction: IDBTransaction) => {
238
- const store = transaction.objectStore("todos");
239
- store.createIndex("completed", "completed", { unique: false });
242
+ {
243
+ tag: "0001_add_completed",
244
+ migrate: async (db: IDBDatabase, transaction: IDBTransaction) => {
245
+ const store = transaction.objectStore("todos");
246
+ store.createIndex("completed", "completed", { unique: false });
247
+ },
240
248
  },
241
249
 
242
250
  // Migration 2: Transform data
243
- async (db: IDBDatabase, transaction: IDBTransaction) => {
244
- const store = transaction.objectStore("todos");
245
- const todos = await new Promise<any[]>((resolve, reject) => {
246
- const req = store.getAll();
247
- req.onsuccess = () => resolve(req.result);
248
- req.onerror = () => reject(req.error);
249
- });
250
-
251
- // Transform data
252
- for (const todo of todos) {
253
- todo.priority = todo.priority || "medium";
254
- store.put(todo);
255
- }
251
+ {
252
+ tag: "0002_add_priority",
253
+ migrate: async (db: IDBDatabase, transaction: IDBTransaction) => {
254
+ const store = transaction.objectStore("todos");
255
+ const todos = await new Promise<any[]>((resolve, reject) => {
256
+ const req = store.getAll();
257
+ req.onsuccess = () => resolve(req.result);
258
+ req.onerror = () => reject(req.error);
259
+ });
260
+
261
+ // Transform data
262
+ for (const todo of todos) {
263
+ todo.priority = todo.priority || "medium";
264
+ store.put(todo);
265
+ }
266
+ },
256
267
  },
257
268
  ];
258
269
 
@@ -338,6 +349,56 @@ Useful for:
338
349
  - Clearing user data on logout
339
350
  - Testing scenarios
340
351
 
352
+ ### generateIndexedDBMigrations
353
+
354
+ Generate IndexedDB migration files from Drizzle snapshots programmatically:
355
+
356
+ ```typescript
357
+ import { generateIndexedDBMigrations } from "@firtoz/drizzle-indexeddb";
358
+
359
+ generateIndexedDBMigrations({
360
+ drizzleDir: "./drizzle", // Path to Drizzle directory (default: ./drizzle)
361
+ outputDir: "./drizzle/indexeddb-migrations", // Output directory (default: ./drizzle/indexeddb-migrations)
362
+ });
363
+ ```
364
+
365
+ ## CLI
366
+
367
+ The package includes a CLI tool to generate IndexedDB migrations from Drizzle schema snapshots.
368
+
369
+ ### Usage
370
+
371
+ ```bash
372
+ # Generate migrations (run after drizzle-kit generate)
373
+ bun drizzle-indexeddb-generate
374
+
375
+ # With custom paths
376
+ bun drizzle-indexeddb-generate --drizzle-dir ./db/drizzle
377
+ bun drizzle-indexeddb-generate --output-dir ./src/migrations
378
+
379
+ # Show help
380
+ bun drizzle-indexeddb-generate --help
381
+ ```
382
+
383
+ ### Options
384
+
385
+ | Option | Description | Default |
386
+ |--------|-------------|---------|
387
+ | `--drizzle-dir <path>` | Path to Drizzle directory | `./drizzle` |
388
+ | `--output-dir <path>` | Path to output directory | `./drizzle/indexeddb-migrations` |
389
+
390
+ ### npm scripts
391
+
392
+ Add to your `package.json`:
393
+
394
+ ```json
395
+ {
396
+ "scripts": {
397
+ "db:generate": "bun drizzle-kit generate && bun drizzle-indexeddb-generate"
398
+ }
399
+ }
400
+ ```
401
+
341
402
  ## Advanced Usage
342
403
 
343
404
  ### Custom Sync Configuration
@@ -359,13 +420,13 @@ const collection = createCollection(
359
420
 
360
421
  ```typescript
361
422
  try {
362
- const db = await migrateIndexedDB("my-app", config, true);
423
+ const db = await migrateIndexedDBWithFunctions("my-app", migrations, true);
363
424
  } catch (error) {
364
425
  console.error("Migration failed:", error);
365
426
 
366
427
  // Option 1: Delete and start fresh
367
428
  await deleteIndexedDB("my-app");
368
- const db = await migrateIndexedDB("my-app", config, true);
429
+ const db = await migrateIndexedDBWithFunctions("my-app", migrations, true);
369
430
 
370
431
  // Option 2: Handle specific errors
371
432
  if (error.message.includes("Primary key structure changed")) {
@@ -378,10 +439,10 @@ try {
378
439
 
379
440
  ```typescript
380
441
  // Enable debug mode to see performance metrics
381
- const db = await migrateIndexedDB("my-app", config, true);
442
+ const db = await migrateIndexedDBWithFunctions("my-app", migrations, true);
382
443
 
383
444
  // Output shows:
384
- // [PERF] IndexedDB snapshot migrator start for my-app
445
+ // [PERF] IndexedDB function migrator start for my-app
385
446
  // [PERF] Latest applied migration index: 5 (checked 5 migrations)
386
447
  // [PERF] Found 2 pending migrations to apply: ["add_priority", "add_category"]
387
448
  // [PERF] Upgrade started: v5 → v7
@@ -432,9 +493,9 @@ const todoTable = syncableTable("todos", {
432
493
 
433
494
  ### Renaming a Column
434
495
 
435
- Drizzle snapshots don't track renames directly, but you can:
496
+ Drizzle migrations don't track renames directly, but you can:
436
497
 
437
- 1. Use function-based migrations to handle data transformation
498
+ 1. Modify the generated migration function to handle data transformation
438
499
  2. Or: Add new column, copy data, delete old column (3 separate migrations)
439
500
 
440
501
  ### Deleting a Table
@@ -455,8 +516,8 @@ This happens when you change the primary key of a table. IndexedDB doesn't suppo
455
516
 
456
517
  ### Migrations Not Applying
457
518
 
458
- - Check that journal and snapshots are correctly imported
459
- - Verify the snapshot files exist in `drizzle/snapshots/`
519
+ - Check that migrations are correctly imported from `drizzle/indexeddb-migrations/`
520
+ - Verify the migration files exist - run `bun drizzle-indexeddb-generate` to regenerate
460
521
  - Enable debug mode to see what's happening
461
522
  - Check browser DevTools → Application → IndexedDB
462
523
 
package/package.json CHANGED
@@ -1,17 +1,25 @@
1
1
  {
2
2
  "name": "@firtoz/drizzle-indexeddb",
3
- "version": "0.1.0",
4
- "description": "IndexedDB migrations powered by Drizzle ORM snapshots",
3
+ "version": "0.3.0",
4
+ "description": "IndexedDB migrations powered by Drizzle ORM",
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
7
7
  "types": "./src/index.ts",
8
8
  "type": "module",
9
+ "bin": {
10
+ "drizzle-indexeddb-generate": "./src/bin/generate-migrations.ts"
11
+ },
9
12
  "exports": {
10
13
  ".": {
11
14
  "types": "./src/index.ts",
12
15
  "import": "./src/index.ts",
13
16
  "require": "./src/index.ts"
14
17
  },
18
+ "./generate": {
19
+ "types": "./src/bin/generate-migrations.ts",
20
+ "import": "./src/bin/generate-migrations.ts",
21
+ "require": "./src/bin/generate-migrations.ts"
22
+ },
15
23
  "./*": {
16
24
  "types": "./src/*.ts",
17
25
  "import": "./src/*.ts",
@@ -56,16 +64,16 @@
56
64
  "access": "public"
57
65
  },
58
66
  "dependencies": {
59
- "@firtoz/drizzle-utils": "^0.1.0",
60
- "@tanstack/db": "^0.5.0",
67
+ "@firtoz/drizzle-utils": "^0.2.0",
68
+ "@tanstack/db": "^0.5.10",
61
69
  "drizzle-orm": "^0.44.7",
62
70
  "drizzle-valibot": "^0.4.2",
63
- "valibot": "^1.0.0"
71
+ "valibot": "^1.2.0"
64
72
  },
65
73
  "peerDependencies": {
66
74
  "react": "^19.2.0"
67
75
  },
68
76
  "devDependencies": {
69
- "@types/react": "^19.2.5"
77
+ "@types/react": "^19.2.7"
70
78
  }
71
79
  }