@firtoz/drizzle-sqlite-wasm 0.2.2 → 0.2.3
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 +9 -0
- package/README.md +73 -0
- package/package.json +6 -6
- package/src/collections/sqlite-collection.ts +6 -14
- package/src/drizzle/direct.ts +1 -1
- package/src/drizzle/handle-callback.ts +1 -1
- package/src/types.ts +4 -0
- package/src/worker/sqlite.worker.ts +9 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @firtoz/drizzle-sqlite-wasm
|
|
2
2
|
|
|
3
|
+
## 0.2.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`d97681f`](https://github.com/firtoz/fullstack-toolkit/commit/d97681f56e103d033292005d31f298b03b4fa7ca) Thanks [@firtoz](https://github.com/firtoz)! - Add comprehensive Vite configuration documentation for OPFS support. Includes required COOP/COEP headers and a custom plugin to fix the sqlite-wasm 3.51.x OPFS proxy worker module format issue ("Unexpected token 'export'" error).
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`b0f7893`](https://github.com/firtoz/fullstack-toolkit/commit/b0f789314c4ee85d8c08466b968baad2977a2581)]:
|
|
10
|
+
- @firtoz/worker-helper@1.1.0
|
|
11
|
+
|
|
3
12
|
## 0.2.2
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -111,6 +111,79 @@ import SqliteWorker from "@firtoz/drizzle-sqlite-wasm/worker/sqlite.worker?worke
|
|
|
111
111
|
const { drizzle } = useDrizzleSqliteDb(SqliteWorker, "mydb", schema, migrations);
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
#### Required Vite Configuration for OPFS Support
|
|
115
|
+
|
|
116
|
+
To enable OPFS (Origin Private File System) persistence with SQLite WASM, you need to configure Vite properly. Add the following to your `vite.config.ts`:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { defineConfig } from "vite";
|
|
120
|
+
|
|
121
|
+
export default defineConfig({
|
|
122
|
+
plugins: [
|
|
123
|
+
// Required: Set COOP/COEP headers for SharedArrayBuffer support
|
|
124
|
+
{
|
|
125
|
+
name: "configure-response-headers",
|
|
126
|
+
configureServer: (server) => {
|
|
127
|
+
server.middlewares.use((_req, res, next) => {
|
|
128
|
+
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
|
|
129
|
+
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
|
|
130
|
+
next();
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
// Required: Fix for sqlite-wasm OPFS proxy worker module format
|
|
135
|
+
{
|
|
136
|
+
name: "sqlite-wasm-opfs-fix",
|
|
137
|
+
enforce: "pre",
|
|
138
|
+
transform(code, id) {
|
|
139
|
+
// Transform worker creation calls to use module type
|
|
140
|
+
if (
|
|
141
|
+
id.includes("@sqlite.org/sqlite-wasm") &&
|
|
142
|
+
code.includes("new Worker")
|
|
143
|
+
) {
|
|
144
|
+
// This fixes the "Unexpected token 'export'" error in OPFS proxy worker
|
|
145
|
+
let transformed = code;
|
|
146
|
+
const workerRegex =
|
|
147
|
+
/new\s+Worker\s*\(\s*(new\s+URL\s*\([^)]+,[^)]+\))\s*\)/g;
|
|
148
|
+
transformed = transformed.replace(
|
|
149
|
+
workerRegex,
|
|
150
|
+
"new Worker($1, { type: 'module' })",
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
if (transformed !== code) {
|
|
154
|
+
return {
|
|
155
|
+
code: transformed,
|
|
156
|
+
map: null,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
server: {
|
|
164
|
+
headers: {
|
|
165
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
|
166
|
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
optimizeDeps: {
|
|
170
|
+
exclude: ["@sqlite.org/sqlite-wasm"],
|
|
171
|
+
},
|
|
172
|
+
worker: {
|
|
173
|
+
format: "es",
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Why is this needed?**
|
|
179
|
+
|
|
180
|
+
1. **COOP/COEP Headers**: Required for `SharedArrayBuffer` support, which OPFS needs for synchronous file operations
|
|
181
|
+
2. **Worker Module Fix**: sqlite-wasm 3.51.x creates workers without specifying `{ type: 'module' }`, causing syntax errors when the OPFS proxy worker (which uses ES module syntax) is loaded
|
|
182
|
+
3. **Worker Format**: Ensures all workers are treated as ES modules
|
|
183
|
+
4. **Exclude from Optimization**: Prevents Vite from pre-bundling sqlite-wasm, which can break worker creation
|
|
184
|
+
|
|
185
|
+
For a complete example, see [`tests/test-playground/vite.config.ts`](../../tests/test-playground/vite.config.ts) in the repository.
|
|
186
|
+
|
|
114
187
|
### Webpack 5+
|
|
115
188
|
|
|
116
189
|
Use `new URL()` with `import.meta.url`:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firtoz/drizzle-sqlite-wasm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Drizzle SQLite WASM bindings",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"module": "./src/index.ts",
|
|
@@ -71,17 +71,17 @@
|
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@firtoz/drizzle-utils": "^0.3.1",
|
|
73
73
|
"@firtoz/maybe-error": "^1.5.2",
|
|
74
|
-
"@firtoz/worker-helper": "^1.
|
|
75
|
-
"@sqlite.org/sqlite-wasm": "^3.51.
|
|
76
|
-
"@tanstack/db": "^0.5.
|
|
74
|
+
"@firtoz/worker-helper": "^1.1.0",
|
|
75
|
+
"@sqlite.org/sqlite-wasm": "^3.51.2-build3",
|
|
76
|
+
"@tanstack/db": "^0.5.20",
|
|
77
77
|
"drizzle-orm": "^0.45.1",
|
|
78
78
|
"drizzle-valibot": "^0.4.2",
|
|
79
79
|
"react": "^19.2.3",
|
|
80
80
|
"valibot": "^1.2.0",
|
|
81
|
-
"zod": "^4.
|
|
81
|
+
"zod": "^4.3.5"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@standard-schema/spec": "^1.1.0",
|
|
85
|
-
"@types/react": "^19.2.
|
|
85
|
+
"@types/react": "^19.2.8"
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -323,7 +323,7 @@ export function sqliteCollectionOptions<
|
|
|
323
323
|
|
|
324
324
|
// Create backend-specific implementation
|
|
325
325
|
const backend: SyncBackend<TTable> = {
|
|
326
|
-
initialLoad: async (
|
|
326
|
+
initialLoad: async () => {
|
|
327
327
|
const items = (await config.drizzle
|
|
328
328
|
.select()
|
|
329
329
|
.from(table)) as unknown as InferSchemaOutput<SelectSchema<TTable>>[];
|
|
@@ -352,12 +352,10 @@ export function sqliteCollectionOptions<
|
|
|
352
352
|
});
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
|
|
356
|
-
write(item);
|
|
357
|
-
}
|
|
355
|
+
return items as unknown as InferSchemaOutput<SelectSchema<TTable>>[];
|
|
358
356
|
},
|
|
359
357
|
|
|
360
|
-
loadSubset: async (options
|
|
358
|
+
loadSubset: async (options) => {
|
|
361
359
|
// Build the query with optional where, orderBy, limit, and offset
|
|
362
360
|
// Use $dynamic() to enable dynamic query building
|
|
363
361
|
let query = config.drizzle.select().from(table).$dynamic();
|
|
@@ -476,22 +474,16 @@ export function sqliteCollectionOptions<
|
|
|
476
474
|
});
|
|
477
475
|
}
|
|
478
476
|
|
|
479
|
-
|
|
480
|
-
write(item);
|
|
481
|
-
}
|
|
477
|
+
return items as unknown as InferSchemaOutput<SelectSchema<TTable>>[];
|
|
482
478
|
},
|
|
483
479
|
|
|
484
|
-
handleInsert: async (
|
|
480
|
+
handleInsert: async (items) => {
|
|
485
481
|
const results: Array<InferSchemaOutput<SelectSchema<TTable>>> = [];
|
|
486
482
|
|
|
487
483
|
// Queue the transaction to serialize SQLite operations
|
|
488
484
|
await queueTransaction(async () => {
|
|
489
485
|
await config.drizzle.transaction(async (tx) => {
|
|
490
|
-
for (const
|
|
491
|
-
// TanStack DB applies schema transform (including ID default) before calling this listener
|
|
492
|
-
// So mutation.modified already has the ID from insertSchemaWithIdDefault
|
|
493
|
-
const itemToInsert = mutation.modified;
|
|
494
|
-
|
|
486
|
+
for (const itemToInsert of items) {
|
|
495
487
|
if (config.debug) {
|
|
496
488
|
console.log(
|
|
497
489
|
`[${new Date().toISOString()}] insertListener inserting`,
|
package/src/drizzle/direct.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Database } from "
|
|
1
|
+
import type { Database } from "../types";
|
|
2
2
|
import type { DrizzleConfig } from "drizzle-orm";
|
|
3
3
|
import { drizzle as drizzleSqliteProxy } from "drizzle-orm/sqlite-proxy";
|
|
4
4
|
import { handleRemoteCallback } from "./handle-callback";
|
package/src/types.ts
ADDED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
// type BindingSpec,
|
|
3
|
-
Database,
|
|
4
|
-
Sqlite3Static,
|
|
5
|
-
} from "@sqlite.org/sqlite-wasm";
|
|
6
|
-
|
|
7
1
|
import { WorkerHelper } from "@firtoz/worker-helper";
|
|
8
2
|
import {
|
|
9
3
|
SqliteWorkerClientMessageSchema,
|
|
@@ -18,6 +12,7 @@ import {
|
|
|
18
12
|
} from "./schema";
|
|
19
13
|
import { handleRemoteCallback } from "../drizzle/handle-callback";
|
|
20
14
|
import { exhaustiveGuard } from "@firtoz/maybe-error";
|
|
15
|
+
import type { Sqlite3Static, Database } from "../types";
|
|
21
16
|
|
|
22
17
|
// Declare self as DedicatedWorkerGlobalScope for TypeScript
|
|
23
18
|
declare var self: DedicatedWorkerGlobalScope;
|
|
@@ -27,7 +22,13 @@ class SqliteWorkerHelper extends WorkerHelper<
|
|
|
27
22
|
SqliteWorkerServerMessage
|
|
28
23
|
> {
|
|
29
24
|
private initPromise: Promise<Sqlite3Static>;
|
|
30
|
-
private databases = new Map<
|
|
25
|
+
private databases = new Map<
|
|
26
|
+
DbId,
|
|
27
|
+
{
|
|
28
|
+
db: Database;
|
|
29
|
+
initialized: boolean;
|
|
30
|
+
}
|
|
31
|
+
>();
|
|
31
32
|
|
|
32
33
|
constructor() {
|
|
33
34
|
super(self, SqliteWorkerClientMessageSchema, sqliteWorkerServerMessage, {
|
|
@@ -50,10 +51,7 @@ class SqliteWorkerHelper extends WorkerHelper<
|
|
|
50
51
|
|
|
51
52
|
this.initPromise = import("@sqlite.org/sqlite-wasm").then(
|
|
52
53
|
async ({ default: sqlite3InitModule }) => {
|
|
53
|
-
const result = await sqlite3InitModule(
|
|
54
|
-
print: this.log.bind(this),
|
|
55
|
-
printErr: this.error.bind(this),
|
|
56
|
-
});
|
|
54
|
+
const result = await sqlite3InitModule();
|
|
57
55
|
|
|
58
56
|
return result;
|
|
59
57
|
},
|