Problem
With record-adapter-sqljs removed, there is no adapter for non-Node runtimes. sql.js was meant to serve that case and could not: Workers forbid compiling WASM from bytes at runtime, and its whole-database-serialize-per-write persist model has no single-writer guarantee, so a Worker fleet would silently lose writes.
Cloudflare Durable Objects with SQLite storage is the shape that actually fits — and it fits this codebase specifically, not just the runtime:
ctx.storage.sql.exec() is synchronous. SqlExecutor (packages/sqlite-shared/src/executor.ts) is a synchronous interface and all of SharedSqlRecordLogic is built on it. A DO adapter drops onto it as-is. D1 and libsql-over-HTTP are both async and would force an async refactor of the entire shared layer — that cost is the reason to prefer this over either.
- DO supports FTS5, the same module
record-adapter-sqlite uses, so this rejoins the shared FTS path with no pluggable seam.
- A DO instance is a single owning writer.
docs/spec/adapters.md § Concurrency & storage ownership requires exactly one owning process per stack's storage; a Durable Object implements that constraint rather than delegating it to the host.
Work items
Notes
Not urgent, and worth confirming there's real demand before building it — it's listed as the answer if a Workers story is wanted, not a commitment to one. Explicitly not libsql: @libsql/client on Workers is a remote HTTP client to Turso, which is closer in kind to adapter-api than to an embedded storage adapter, and is async.
Cross-refs: #93 (optional-capability discovery). Blob storage in this topology would be R2 via #160.
Problem
With
record-adapter-sqljsremoved, there is no adapter for non-Node runtimes. sql.js was meant to serve that case and could not: Workers forbid compiling WASM from bytes at runtime, and its whole-database-serialize-per-write persist model has no single-writer guarantee, so a Worker fleet would silently lose writes.Cloudflare Durable Objects with SQLite storage is the shape that actually fits — and it fits this codebase specifically, not just the runtime:
ctx.storage.sql.exec()is synchronous.SqlExecutor(packages/sqlite-shared/src/executor.ts) is a synchronous interface and all ofSharedSqlRecordLogicis built on it. A DO adapter drops onto it as-is. D1 and libsql-over-HTTP are both async and would force an async refactor of the entire shared layer — that cost is the reason to prefer this over either.record-adapter-sqliteuses, so this rejoins the shared FTS path with no pluggable seam.docs/spec/adapters.md § Concurrency & storage ownershiprequires exactly one owning process per stack's storage; a Durable Object implements that constraint rather than delegating it to the host.Work items
record-adapter-do-sqlite—StackRecordAdapterwith aSqlExecutoroverctx.storage.sql, reusingSharedSqlRecordLogic, the FTS5 schema and strategy, the query builder, cursor codec, and row mappersctx,SqlStorageCursor→ row-object normalization, lifecycle (no lock file — the DO is the lock; nopersistcallback — storage is durable by construction)sqlite-shared/src/executor.ts(isForeignKeyViolation,isUniqueConstraintViolation) match what DO's SQLite build reports; they currently pin exact strings that happen to agree across sql.js andnode:sqlitedocs/spec/adapters.md § Concurrency & storage ownershipfor how this adapter honors single-writer@cloudflare/vitest-pool-workers) rather than underenvironment: 'node'— the sql.js package's tests only ever exercised Node, which is how a browser/Worker adapter shipped without ever running in a browser or a WorkerNotes
Not urgent, and worth confirming there's real demand before building it — it's listed as the answer if a Workers story is wanted, not a commitment to one. Explicitly not libsql:
@libsql/clienton Workers is a remote HTTP client to Turso, which is closer in kind toadapter-apithan to an embedded storage adapter, and is async.Cross-refs: #93 (optional-capability discovery). Blob storage in this topology would be R2 via #160.