@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 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.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.0.0",
75
- "@sqlite.org/sqlite-wasm": "^3.51.1-build2",
76
- "@tanstack/db": "^0.5.15",
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.2.1"
81
+ "zod": "^4.3.5"
82
82
  },
83
83
  "devDependencies": {
84
84
  "@standard-schema/spec": "^1.1.0",
85
- "@types/react": "^19.2.7"
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 (write) => {
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
- for (const item of items) {
356
- write(item);
357
- }
355
+ return items as unknown as InferSchemaOutput<SelectSchema<TTable>>[];
358
356
  },
359
357
 
360
- loadSubset: async (options, write) => {
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
- for (const item of items) {
480
- write(item);
481
- }
477
+ return items as unknown as InferSchemaOutput<SelectSchema<TTable>>[];
482
478
  },
483
479
 
484
- handleInsert: async (mutations) => {
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 mutation of mutations) {
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`,
@@ -1,4 +1,4 @@
1
- import type { Database } from "@sqlite.org/sqlite-wasm";
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";
@@ -4,7 +4,7 @@ import {
4
4
  success,
5
5
  type MaybeError,
6
6
  } from "@firtoz/maybe-error";
7
- import type { Database } from "@sqlite.org/sqlite-wasm";
7
+ import type { Database } from "../types";
8
8
 
9
9
  export const handleRemoteCallback = async ({
10
10
  sqliteDb,
package/src/types.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type init from "@sqlite.org/sqlite-wasm";
2
+
3
+ export type Sqlite3Static = Awaited<ReturnType<typeof init>>;
4
+ export type Database = InstanceType<Sqlite3Static["oo1"]["DB"]>;
@@ -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<DbId, { db: Database; initialized: boolean }>();
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
  },