@firtoz/drizzle-sqlite-wasm 0.1.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 +230 -0
- package/README.md +602 -0
- package/package.json +89 -0
- package/src/collections/sqlite-collection.ts +532 -0
- package/src/collections/websocket-collection.ts +271 -0
- package/src/context/useDrizzleSqlite.ts +35 -0
- package/src/drizzle/direct.ts +27 -0
- package/src/drizzle/handle-callback.ts +113 -0
- package/src/drizzle/worker.ts +24 -0
- package/src/hooks/useDrizzleSqliteDb.ts +139 -0
- package/src/index.ts +32 -0
- package/src/migration/migrator.ts +148 -0
- package/src/worker/client.ts +11 -0
- package/src/worker/global-manager.ts +78 -0
- package/src/worker/manager.ts +339 -0
- package/src/worker/schema.ts +111 -0
- package/src/worker/sqlite.worker.ts +253 -0
package/README.md
ADDED
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
# @firtoz/drizzle-sqlite-wasm
|
|
2
|
+
|
|
3
|
+
TanStack DB collections backed by SQLite WASM running in Web Workers, with full Drizzle ORM integration. Build reactive, type-safe SQLite applications in the browser with non-blocking database operations.
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @firtoz/drizzle-sqlite-wasm @firtoz/drizzle-utils drizzle-orm @tanstack/db
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- 📦 **TanStack DB collections** - Reactive collections with type safety (primary feature)
|
|
16
|
+
- 🔄 **Web Worker support** - Non-blocking SQLite in a dedicated worker
|
|
17
|
+
- ⚡ **Drizzle ORM** - Full type-safe query builder
|
|
18
|
+
- 🎯 **Type-safe** - Full TypeScript support with automatic type inference
|
|
19
|
+
- 🔍 **Query optimization** - Leverage SQLite indexes for fast queries
|
|
20
|
+
- ⚛️ **React hooks** - Provider and hooks for easy integration
|
|
21
|
+
- 🔄 **Migrations** - Automatic schema migrations with Drizzle snapshots
|
|
22
|
+
- 🔌 **Bundler agnostic** - Works with Vite, Webpack, Parcel, and more
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Define Your Schema
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// schema.ts
|
|
30
|
+
import { syncableTable } from "@firtoz/drizzle-utils";
|
|
31
|
+
import { text, integer } from "drizzle-orm/sqlite-core";
|
|
32
|
+
|
|
33
|
+
export const todoTable = syncableTable("todos", {
|
|
34
|
+
title: text("title").notNull(),
|
|
35
|
+
completed: integer("completed", { mode: "boolean" }).notNull().default(false),
|
|
36
|
+
description: text("description"),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const schema = { todoTable };
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Generate Migrations
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Generate Drizzle migrations
|
|
46
|
+
drizzle-kit generate
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Setup Worker (Vite Example)
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// App.tsx
|
|
53
|
+
import { DrizzleSqliteProvider } from "@firtoz/drizzle-sqlite-wasm";
|
|
54
|
+
import SqliteWorker from "@firtoz/drizzle-sqlite-wasm/worker/sqlite.worker?worker";
|
|
55
|
+
import * as schema from "./schema";
|
|
56
|
+
import migrations from "./migrations";
|
|
57
|
+
|
|
58
|
+
function App() {
|
|
59
|
+
return (
|
|
60
|
+
<DrizzleSqliteProvider
|
|
61
|
+
worker={SqliteWorker}
|
|
62
|
+
dbName="my-app"
|
|
63
|
+
schema={schema}
|
|
64
|
+
migrations={migrations}
|
|
65
|
+
>
|
|
66
|
+
<TodoApp />
|
|
67
|
+
</DrizzleSqliteProvider>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 4. Use in Components
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// TodoList.tsx
|
|
76
|
+
import { useDrizzleSqlite, useSqliteCollection } from "@firtoz/drizzle-sqlite-wasm";
|
|
77
|
+
import { todoTable } from "./schema";
|
|
78
|
+
|
|
79
|
+
function TodoList() {
|
|
80
|
+
const { drizzle } = useDrizzleSqlite();
|
|
81
|
+
|
|
82
|
+
// Option 1: Use Drizzle ORM directly
|
|
83
|
+
const loadTodos = async () => {
|
|
84
|
+
const todos = await drizzle.select().from(todoTable).all();
|
|
85
|
+
return todos;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Option 2: Use TanStack DB collection
|
|
89
|
+
const collection = useSqliteCollection("todos");
|
|
90
|
+
const [todos] = collection.useStore();
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<ul>
|
|
94
|
+
{todos.map(todo => (
|
|
95
|
+
<li key={todo.id}>{todo.title}</li>
|
|
96
|
+
))}
|
|
97
|
+
</ul>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Bundler Support
|
|
103
|
+
|
|
104
|
+
### Vite
|
|
105
|
+
|
|
106
|
+
Vite has built-in support for Web Workers with the `?worker` suffix:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import SqliteWorker from "@firtoz/drizzle-sqlite-wasm/worker/sqlite.worker?worker";
|
|
110
|
+
|
|
111
|
+
const { drizzle } = useDrizzleSqliteDb(SqliteWorker, "mydb", schema, migrations);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Webpack 5+
|
|
115
|
+
|
|
116
|
+
Use `new URL()` with `import.meta.url`:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const SqliteWorker = class extends Worker {
|
|
120
|
+
constructor() {
|
|
121
|
+
super(
|
|
122
|
+
new URL("@firtoz/drizzle-sqlite-wasm/worker/sqlite.worker", import.meta.url),
|
|
123
|
+
{ type: "module" }
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const { drizzle } = useDrizzleSqliteDb(SqliteWorker, "mydb", schema, migrations);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Parcel 2+
|
|
132
|
+
|
|
133
|
+
Similar to Webpack:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const SqliteWorker = class extends Worker {
|
|
137
|
+
constructor() {
|
|
138
|
+
super(
|
|
139
|
+
new URL("@firtoz/drizzle-sqlite-wasm/worker/sqlite.worker", import.meta.url)
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const { drizzle } = useDrizzleSqliteDb(SqliteWorker, "mydb", schema, migrations);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## TanStack DB Collections
|
|
148
|
+
|
|
149
|
+
The primary feature of this package: Create reactive, type-safe collections backed by SQLite WASM.
|
|
150
|
+
|
|
151
|
+
### Basic Usage
|
|
152
|
+
|
|
153
|
+
Create TanStack DB collections backed by SQLite:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { createCollection } from "@tanstack/db";
|
|
157
|
+
import { drizzleCollectionOptions } from "@firtoz/drizzle-sqlite-wasm/drizzleCollectionOptions";
|
|
158
|
+
|
|
159
|
+
const collection = createCollection(
|
|
160
|
+
drizzleCollectionOptions({
|
|
161
|
+
drizzle,
|
|
162
|
+
tableName: "todos",
|
|
163
|
+
readyPromise,
|
|
164
|
+
syncMode: "on-demand", // or "realtime"
|
|
165
|
+
})
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// CRUD operations
|
|
169
|
+
await collection.insert({ title: "Buy milk", completed: false });
|
|
170
|
+
await collection.update(todoId, { completed: true });
|
|
171
|
+
await collection.delete(todoId); // Soft delete (sets deletedAt)
|
|
172
|
+
|
|
173
|
+
// Query with filters
|
|
174
|
+
const completed = await collection.find({
|
|
175
|
+
where: { completed: true },
|
|
176
|
+
orderBy: { createdAt: "desc" },
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Subscribe to changes
|
|
180
|
+
collection.subscribe((todos) => {
|
|
181
|
+
console.log("Todos updated:", todos);
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Collection Options
|
|
186
|
+
|
|
187
|
+
**Config:**
|
|
188
|
+
- `drizzle: DrizzleDB` - Drizzle instance
|
|
189
|
+
- `tableName: string` - Table name
|
|
190
|
+
- `readyPromise: Promise<void>` - Database ready promise
|
|
191
|
+
- `syncMode?: "on-demand" | "realtime"` - Sync mode
|
|
192
|
+
|
|
193
|
+
## API Reference
|
|
194
|
+
|
|
195
|
+
### React Hooks
|
|
196
|
+
|
|
197
|
+
#### `DrizzleSqliteProvider`
|
|
198
|
+
|
|
199
|
+
Context provider for SQLite WASM:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
<DrizzleSqliteProvider
|
|
203
|
+
worker={SqliteWorker} // Worker constructor
|
|
204
|
+
dbName="my-app" // Database name
|
|
205
|
+
schema={schema} // Drizzle schema
|
|
206
|
+
migrations={migrations} // Migration config
|
|
207
|
+
>
|
|
208
|
+
{children}
|
|
209
|
+
</DrizzleSqliteProvider>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Props:**
|
|
213
|
+
- `worker: new () => Worker` - Worker constructor (bundler-specific)
|
|
214
|
+
- `dbName: string` - Name of the SQLite database
|
|
215
|
+
- `schema: TSchema` - Drizzle schema object
|
|
216
|
+
- `migrations: DurableSqliteMigrationConfig` - Migration configuration
|
|
217
|
+
|
|
218
|
+
#### `useDrizzleSqliteDb(worker, dbName, schema, migrations)`
|
|
219
|
+
|
|
220
|
+
Hook to create a Drizzle instance with Web Worker:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
function MyComponent() {
|
|
224
|
+
const { drizzle, readyPromise } = useDrizzleSqliteDb(
|
|
225
|
+
SqliteWorker,
|
|
226
|
+
"my-app",
|
|
227
|
+
schema,
|
|
228
|
+
migrations
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
readyPromise.then(() => {
|
|
233
|
+
console.log("Database ready!");
|
|
234
|
+
});
|
|
235
|
+
}, [readyPromise]);
|
|
236
|
+
|
|
237
|
+
// Use drizzle...
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Returns:**
|
|
242
|
+
- `drizzle: DrizzleDB` - Drizzle ORM instance
|
|
243
|
+
- `readyPromise: Promise<void>` - Resolves when database is ready
|
|
244
|
+
|
|
245
|
+
#### `useDrizzleSqlite()`
|
|
246
|
+
|
|
247
|
+
Access the Drizzle context:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
function MyComponent() {
|
|
251
|
+
const { drizzle, getCollection } = useDrizzleSqlite();
|
|
252
|
+
|
|
253
|
+
// Use drizzle or get collections...
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Returns:**
|
|
258
|
+
- `drizzle: DrizzleDB` - Drizzle ORM instance
|
|
259
|
+
- `getCollection: (tableName) => Collection` - Get TanStack DB collection
|
|
260
|
+
|
|
261
|
+
#### `useSqliteCollection(tableName)`
|
|
262
|
+
|
|
263
|
+
Hook for a specific collection with automatic ref counting:
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
function TodoList() {
|
|
267
|
+
const collection = useSqliteCollection("todos");
|
|
268
|
+
const [todos] = collection.useStore();
|
|
269
|
+
|
|
270
|
+
// Collection is automatically cleaned up on unmount
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Worker API
|
|
275
|
+
|
|
276
|
+
#### `SqliteWorkerManager`
|
|
277
|
+
|
|
278
|
+
Manages multiple SQLite databases in a single worker:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import { getSqliteWorkerManager, initializeSqliteWorker } from "@firtoz/drizzle-sqlite-wasm";
|
|
282
|
+
|
|
283
|
+
// Initialize global worker
|
|
284
|
+
await initializeSqliteWorker(SqliteWorker, true);
|
|
285
|
+
|
|
286
|
+
// Get manager
|
|
287
|
+
const manager = getSqliteWorkerManager();
|
|
288
|
+
|
|
289
|
+
// Get database instance
|
|
290
|
+
const db = manager.getDatabase("mydb");
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Global Functions:**
|
|
294
|
+
- `initializeSqliteWorker(Worker, debug?)` - Initialize the global worker
|
|
295
|
+
- `getSqliteWorkerManager()` - Get the global manager instance
|
|
296
|
+
- `isSqliteWorkerInitialized()` - Check if worker is initialized
|
|
297
|
+
- `resetSqliteWorkerManager()` - Reset the global manager
|
|
298
|
+
|
|
299
|
+
### Direct SQLite (Non-Worker)
|
|
300
|
+
|
|
301
|
+
#### `drizzleSqliteWasm(sqliteDb, config, debug?)`
|
|
302
|
+
|
|
303
|
+
Use SQLite WASM directly in the main thread (for testing or synchronous contexts):
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
import { drizzleSqliteWasm } from "@firtoz/drizzle-sqlite-wasm";
|
|
307
|
+
import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
|
|
308
|
+
|
|
309
|
+
const sqlite3 = await sqlite3InitModule();
|
|
310
|
+
const sqliteDb = new sqlite3.oo1.DB("/mydb.db", "ct");
|
|
311
|
+
|
|
312
|
+
const drizzle = drizzleSqliteWasm(sqliteDb, { schema }, true);
|
|
313
|
+
|
|
314
|
+
// Use drizzle normally
|
|
315
|
+
const todos = await drizzle.select().from(todoTable).all();
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Migrations
|
|
319
|
+
|
|
320
|
+
#### `customSqliteMigrate(config)`
|
|
321
|
+
|
|
322
|
+
Run custom SQLite migrations:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { customSqliteMigrate } from "@firtoz/drizzle-sqlite-wasm/sqlite-wasm-migrator";
|
|
326
|
+
|
|
327
|
+
await customSqliteMigrate({
|
|
328
|
+
database: sqliteDb,
|
|
329
|
+
journal: journalData,
|
|
330
|
+
migrations: {
|
|
331
|
+
"0000_init": "CREATE TABLE todos (id TEXT PRIMARY KEY, title TEXT NOT NULL);",
|
|
332
|
+
"0001_add_completed": "ALTER TABLE todos ADD COLUMN completed INTEGER DEFAULT 0;",
|
|
333
|
+
},
|
|
334
|
+
debug: true,
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Config:**
|
|
339
|
+
- `database: Database` - SQLite WASM database instance
|
|
340
|
+
- `journal: Journal` - Drizzle journal
|
|
341
|
+
- `migrations: Record<string, string>` - SQL migration strings
|
|
342
|
+
- `debug?: boolean` - Enable debug logging
|
|
343
|
+
|
|
344
|
+
## Advanced Usage
|
|
345
|
+
|
|
346
|
+
### Multiple Databases
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
function App() {
|
|
350
|
+
return (
|
|
351
|
+
<>
|
|
352
|
+
<DrizzleSqliteProvider
|
|
353
|
+
worker={SqliteWorker}
|
|
354
|
+
dbName="app-data"
|
|
355
|
+
schema={appSchema}
|
|
356
|
+
migrations={appMigrations}
|
|
357
|
+
>
|
|
358
|
+
<AppContent />
|
|
359
|
+
</DrizzleSqliteProvider>
|
|
360
|
+
|
|
361
|
+
<DrizzleSqliteProvider
|
|
362
|
+
worker={SqliteWorker}
|
|
363
|
+
dbName="cache-data"
|
|
364
|
+
schema={cacheSchema}
|
|
365
|
+
migrations={cacheMigrations}
|
|
366
|
+
>
|
|
367
|
+
<CacheManager />
|
|
368
|
+
</DrizzleSqliteProvider>
|
|
369
|
+
</>
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Custom Worker Configuration
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { initializeSqliteWorker, getSqliteWorkerManager } from "@firtoz/drizzle-sqlite-wasm";
|
|
378
|
+
|
|
379
|
+
// Initialize with custom worker
|
|
380
|
+
await initializeSqliteWorker(MyCustomWorker, true);
|
|
381
|
+
|
|
382
|
+
// Get manager for manual control
|
|
383
|
+
const manager = getSqliteWorkerManager();
|
|
384
|
+
|
|
385
|
+
// Access databases
|
|
386
|
+
const db1 = manager.getDatabase("app-data");
|
|
387
|
+
const db2 = manager.getDatabase("cache-data");
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Complex Queries with Drizzle
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
import { eq, and, or, gt, like, desc } from "drizzle-orm";
|
|
394
|
+
import { todoTable } from "./schema";
|
|
395
|
+
|
|
396
|
+
function TodoComponent() {
|
|
397
|
+
const { drizzle } = useDrizzleSqlite();
|
|
398
|
+
|
|
399
|
+
const searchTodos = async (searchTerm: string) => {
|
|
400
|
+
return await drizzle
|
|
401
|
+
.select()
|
|
402
|
+
.from(todoTable)
|
|
403
|
+
.where(
|
|
404
|
+
and(
|
|
405
|
+
like(todoTable.title, `%${searchTerm}%`),
|
|
406
|
+
eq(todoTable.completed, false),
|
|
407
|
+
gt(todoTable.createdAt, yesterday)
|
|
408
|
+
)
|
|
409
|
+
)
|
|
410
|
+
.orderBy(desc(todoTable.createdAt))
|
|
411
|
+
.limit(10)
|
|
412
|
+
.all();
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Transaction Support
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
function TodoComponent() {
|
|
421
|
+
const { drizzle } = useDrizzleSqlite();
|
|
422
|
+
|
|
423
|
+
const createTodoWithCategory = async (title: string, category: string) => {
|
|
424
|
+
return await drizzle.transaction(async (tx) => {
|
|
425
|
+
// Insert category if it doesn't exist
|
|
426
|
+
const [existingCategory] = await tx
|
|
427
|
+
.select()
|
|
428
|
+
.from(categoryTable)
|
|
429
|
+
.where(eq(categoryTable.name, category))
|
|
430
|
+
.limit(1)
|
|
431
|
+
.all();
|
|
432
|
+
|
|
433
|
+
let categoryId = existingCategory?.id;
|
|
434
|
+
|
|
435
|
+
if (!categoryId) {
|
|
436
|
+
const [newCategory] = await tx
|
|
437
|
+
.insert(categoryTable)
|
|
438
|
+
.values({ name: category })
|
|
439
|
+
.returning();
|
|
440
|
+
categoryId = newCategory.id;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Insert todo
|
|
444
|
+
const [todo] = await tx
|
|
445
|
+
.insert(todoTable)
|
|
446
|
+
.values({ title, categoryId })
|
|
447
|
+
.returning();
|
|
448
|
+
|
|
449
|
+
return todo;
|
|
450
|
+
});
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Performance Best Practices
|
|
456
|
+
|
|
457
|
+
### 1. Use Indexes
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
const todoTable = syncableTable("todos", {
|
|
461
|
+
title: text("title").notNull(),
|
|
462
|
+
completed: integer("completed", { mode: "boolean" }).notNull(),
|
|
463
|
+
userId: text("userId").notNull(),
|
|
464
|
+
}, (table) => [
|
|
465
|
+
index("completed_idx").on(table.completed),
|
|
466
|
+
index("user_id_idx").on(table.userId),
|
|
467
|
+
index("user_completed_idx").on(table.userId, table.completed),
|
|
468
|
+
]);
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### 2. Use Web Workers
|
|
472
|
+
|
|
473
|
+
Always use the Worker mode for production to keep the UI responsive:
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
// ✅ Good - Non-blocking
|
|
477
|
+
const { drizzle } = useDrizzleSqliteDb(SqliteWorker, "mydb", schema, migrations);
|
|
478
|
+
|
|
479
|
+
// ❌ Bad - Blocks UI thread
|
|
480
|
+
const drizzle = drizzleSqliteWasm(sqliteDb, { schema });
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### 3. Batch Operations
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
// ✅ Good - Single transaction
|
|
487
|
+
await drizzle.insert(todoTable).values([
|
|
488
|
+
{ title: "Todo 1" },
|
|
489
|
+
{ title: "Todo 2" },
|
|
490
|
+
{ title: "Todo 3" },
|
|
491
|
+
]);
|
|
492
|
+
|
|
493
|
+
// ❌ Bad - Multiple transactions
|
|
494
|
+
for (const title of titles) {
|
|
495
|
+
await drizzle.insert(todoTable).values({ title });
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### 4. Use Collections for Reactive Data
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
// ✅ Good - Reactive updates
|
|
503
|
+
const collection = useSqliteCollection("todos");
|
|
504
|
+
const [todos] = collection.useStore(); // Automatically updates
|
|
505
|
+
|
|
506
|
+
// ❌ Bad - Manual polling
|
|
507
|
+
useEffect(() => {
|
|
508
|
+
const interval = setInterval(loadTodos, 1000);
|
|
509
|
+
return () => clearInterval(interval);
|
|
510
|
+
}, []);
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## Troubleshooting
|
|
514
|
+
|
|
515
|
+
### Worker Not Loading
|
|
516
|
+
|
|
517
|
+
**Vite:** Make sure you're using the `?worker` suffix:
|
|
518
|
+
```typescript
|
|
519
|
+
import SqliteWorker from "path/to/sqlite.worker?worker";
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Webpack:** Check that worker-loader is configured or use `new URL()` pattern
|
|
523
|
+
|
|
524
|
+
### Database Not Ready
|
|
525
|
+
|
|
526
|
+
Always wait for the ready promise:
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
const { drizzle, readyPromise } = useDrizzleSqliteDb(/* ... */);
|
|
530
|
+
|
|
531
|
+
useEffect(() => {
|
|
532
|
+
readyPromise.then(() => {
|
|
533
|
+
// Now safe to query
|
|
534
|
+
loadData();
|
|
535
|
+
});
|
|
536
|
+
}, [readyPromise]);
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Performance Issues
|
|
540
|
+
|
|
541
|
+
- Enable debug mode to see timing: `useDrizzleSqliteDb(Worker, dbName, schema, migrations, true)`
|
|
542
|
+
- Check Performance tab in DevTools
|
|
543
|
+
- Add indexes to frequently queried columns
|
|
544
|
+
|
|
545
|
+
### Migration Errors
|
|
546
|
+
|
|
547
|
+
Check the console for specific errors. Common issues:
|
|
548
|
+
- Missing migration files
|
|
549
|
+
- Invalid SQL in migrations
|
|
550
|
+
- Schema conflicts
|
|
551
|
+
|
|
552
|
+
Enable debug mode for detailed logs:
|
|
553
|
+
```typescript
|
|
554
|
+
<DrizzleSqliteProvider
|
|
555
|
+
worker={SqliteWorker}
|
|
556
|
+
dbName="mydb"
|
|
557
|
+
schema={schema}
|
|
558
|
+
migrations={migrations}
|
|
559
|
+
debug={true} // Enable debug logging
|
|
560
|
+
>
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## Type Utilities
|
|
564
|
+
|
|
565
|
+
Re-exported from `@firtoz/drizzle-utils`:
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
import {
|
|
569
|
+
syncableTable,
|
|
570
|
+
makeId,
|
|
571
|
+
type IdOf,
|
|
572
|
+
type TableId,
|
|
573
|
+
type Branded,
|
|
574
|
+
type SelectSchema,
|
|
575
|
+
type InsertSchema,
|
|
576
|
+
} from "@firtoz/drizzle-sqlite-wasm";
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## Examples
|
|
580
|
+
|
|
581
|
+
Check out the test playground for complete examples:
|
|
582
|
+
- `tests/test-playground/e2e/` - E2E tests
|
|
583
|
+
|
|
584
|
+
## Dependencies
|
|
585
|
+
|
|
586
|
+
- `@firtoz/drizzle-utils`
|
|
587
|
+
- `@firtoz/maybe-error`
|
|
588
|
+
- `@firtoz/worker-helper`
|
|
589
|
+
- `@sqlite.org/sqlite-wasm`
|
|
590
|
+
- `drizzle-orm`
|
|
591
|
+
- `@tanstack/db`
|
|
592
|
+
- `react`
|
|
593
|
+
- `zod`
|
|
594
|
+
|
|
595
|
+
## License
|
|
596
|
+
|
|
597
|
+
MIT
|
|
598
|
+
|
|
599
|
+
## Author
|
|
600
|
+
|
|
601
|
+
Firtina Ozbalikchi <firtoz@github.com>
|
|
602
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@firtoz/drizzle-sqlite-wasm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Drizzle SQLite WASM bindings",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"module": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./src/index.ts",
|
|
12
|
+
"import": "./src/index.ts",
|
|
13
|
+
"require": "./src/index.ts"
|
|
14
|
+
},
|
|
15
|
+
"./drizzle-sqlite-wasm-worker": {
|
|
16
|
+
"types": "./src/drizzle/worker.ts",
|
|
17
|
+
"import": "./src/drizzle/worker.ts",
|
|
18
|
+
"require": "./src/drizzle/worker.ts"
|
|
19
|
+
},
|
|
20
|
+
"./sqlite-wasm-migrator": {
|
|
21
|
+
"types": "./src/migration/migrator.ts",
|
|
22
|
+
"import": "./src/migration/migrator.ts",
|
|
23
|
+
"require": "./src/migration/migrator.ts"
|
|
24
|
+
},
|
|
25
|
+
"./drizzleCollectionOptions": {
|
|
26
|
+
"types": "./src/collections/sqlite-collection.ts",
|
|
27
|
+
"import": "./src/collections/sqlite-collection.ts",
|
|
28
|
+
"require": "./src/collections/sqlite-collection.ts"
|
|
29
|
+
},
|
|
30
|
+
"./*": {
|
|
31
|
+
"types": "./src/*.ts",
|
|
32
|
+
"import": "./src/*.ts",
|
|
33
|
+
"require": "./src/*.ts"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"src/**/*.ts",
|
|
38
|
+
"!src/**/*.test.ts",
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"typecheck": "tsc --noEmit -p ./tsconfig.json",
|
|
44
|
+
"lint": "biome check --write src",
|
|
45
|
+
"lint:ci": "biome ci src",
|
|
46
|
+
"format": "biome format src --write",
|
|
47
|
+
"test": "bun test --pass-with-no-tests",
|
|
48
|
+
"test:watch": "bun test --watch"
|
|
49
|
+
},
|
|
50
|
+
"keywords": [
|
|
51
|
+
"typescript",
|
|
52
|
+
"sqlite",
|
|
53
|
+
"wasm",
|
|
54
|
+
"drizzle"
|
|
55
|
+
],
|
|
56
|
+
"author": "Firtina Ozbalikchi <firtoz@github.com>",
|
|
57
|
+
"license": "MIT",
|
|
58
|
+
"homepage": "https://github.com/firtoz/fullstack-toolkit#readme",
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "https://github.com/firtoz/fullstack-toolkit.git",
|
|
62
|
+
"directory": "packages/drizzle-sqlite-wasm"
|
|
63
|
+
},
|
|
64
|
+
"bugs": {
|
|
65
|
+
"url": "https://github.com/firtoz/fullstack-toolkit/issues"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=18.0.0"
|
|
69
|
+
},
|
|
70
|
+
"publishConfig": {
|
|
71
|
+
"access": "public"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@firtoz/drizzle-utils": "^0.1.0",
|
|
75
|
+
"@firtoz/maybe-error": "^1.5.1",
|
|
76
|
+
"@firtoz/worker-helper": "^1.0.0",
|
|
77
|
+
"@sqlite.org/sqlite-wasm": "^3.51.1-build1",
|
|
78
|
+
"@tanstack/db": "^0.5.0",
|
|
79
|
+
"drizzle-orm": "^0.44.7",
|
|
80
|
+
"drizzle-valibot": "^0.4.2",
|
|
81
|
+
"react": "^19.2.0",
|
|
82
|
+
"valibot": "^1.1.0",
|
|
83
|
+
"zod": "^4.1.12"
|
|
84
|
+
},
|
|
85
|
+
"devDependencies": {
|
|
86
|
+
"@standard-schema/spec": "^1.0.0",
|
|
87
|
+
"@types/react": "^19.2.5"
|
|
88
|
+
}
|
|
89
|
+
}
|