Releases: eagerpatch/durable-db
Release list
durable-db@0.1.11
Patch Changes
-
512cc62: fix:
validateno longer reports false schema drift for tables altered withALTER TABLE ... ADD COLUMNThe drift check compared raw
sqlite_masterCREATE text between the migration-replay database and the from-scratch schema database. SQLite rewrites a table's stored CREATE statement when a column is added via ALTER (the column is spliced in before the closing paren, always last), so any generated column-add migration could never text-match the fresh CREATE — every such migration failed validation and blocked deploys.Tables are now compared structurally via PRAGMA introspection (
pragma_table_info,pragma_index_list/pragma_index_infofor UNIQUE constraints,pragma_foreign_key_list), which reflects what SQLite actually enforces regardless of how the table was built. Column order is intentionally ignored. Indexes, views, and triggers are still compared textually. Known gap: CHECK constraints, column collations, and WITHOUT ROWID/STRICT options are not exposed by PRAGMAs and are no longer part of the comparison.
durable-db@0.1.9
Patch Changes
-
The generated Durable Object now carries the database's Drizzle schema and builds its Kysely with the full schema-aware plugin chain.
Previously the DO used the base class's schema-less Kysely (
createKyselyFromSql(sql)with only CamelCase + heuristic date handling), so the schema-aware plugins —DrizzleDefaultsPlugin(defaultFn/onUpdateFn),SchemaPlugin(exact column-name mapping), schema-gated date deserialization, and the newBooleanDeserializePlugin— never ran on the production action path. Most visibly:integer({ mode: 'boolean' })columns read back as raw storage values instead of booleans.The Vite plugin now emits an aliased import of the schema tables into the generated module and a
schema = { … }class property;SqliteDurableObjectbuildsdblazily (subclass field initializers run after the constructor) with the schema when present. Subclassing without a schema keeps the old behavior.
durable-db@0.1.8
Patch Changes
-
Two Kysely-over-SqlStorage driver fixes, both found in the field and both invisible to the node-based unit suites:
- Statements with more than 100 bound parameters no longer throw
too many SQL variables. workerd's SQLite is compiled withSQLITE_MAX_VARIABLE_NUMBER = 100, so a modest multi-row insert (24 rows × 7 columns = 168 params) or a largeWHERE … IN (…)list blew up. Past the limit, the driver now inlines parameters as escaped SQL literals (a real scanner that never touches?inside string literals, quoted identifiers, or comments) — still a single statement with identical semantics. - Boolean columns round-trip as booleans. workerd has no boolean binding type and silently stringified JS
trueinto TEXT'true'(which then failed'boolean'arg validation downstream, and made a stored'false'truthy). The driver now normalizes boolean bindings to SQLite's 1/0 (andundefined→ NULL), and a new schema-awareBooleanDeserializePluginmapsinteger({ mode: 'boolean' })columns back to real booleans on read — including legacy TEXT'true'/'false'rows written by the old driver.
Covered by a new workerd E2E suite (
tests/workers/kysely-e2e.test.ts) that runs the production plugin chain against a real DO SqlStorage. - Statements with more than 100 bound parameters no longer throw
durable-db@0.1.10
Patch Changes
-
Migration chunks now commit atomically with their journal row (
storage.transactionSync).Previously each chunk's statements and the
__migrationsjournal insert ran as separate writes. A crash — or a dev server killed mid-boot — between aCREATE TABLEand the journal insert left the schema applied but unrecorded: storage that throwsMigrationSchemaConflictError("table already exists ... journal has no record") on every subsequent boot. Production is shielded by the PITR bookmark restore, but local dev has no PITR, so the only way out wasdb reset --purge-local-storage.With
transactionSync, a failed or interrupted chunk rolls back completely — either the whole chunk plus its journal row lands, or nothing does. Mocked/test storages withouttransactionSyncfall back to the old inline execution.
durable-db@0.1.7
Patch Changes
- 02bf9f2: Register all actions in the Durable Object's isolate at startup. The action registry is a module singleton populated when an action file loads, but the generated DO module didn't import action files — so it relied on the request path having loaded them. That holds in dev (one shared isolate) and via in-app navigation, but a Durable Object is a separate, persistent isolate in production: a freshly deep-linked route whose action the DO never loaded threw
Unknown action "X" (was it imported?). The generated DO module now side-effect-imports every action-defining file (discovered recursively under the databases dir), so all actions register at DO startup regardless of entry route. - 02bf9f2: Fix
EvalError: Code generation from strings disallowed(500) in production Cloudflare Workers. arktype compiles validators withnew Function(runtime codegen), which Workers allow at startup but block during request handling — so a route's actionargsschema that compiles at request time crashed. arktype'senvHasCsp()auto-probe mis-detects this (it runs at startup, where codegen still works). durable-db now re-exportstypefrom an explicitly jitless (eval-free) arktype scope, so everytype({...})is interpreted rather than compiled — independent of bundle init order. Dev was unaffected because its workerd allows codegen.
durable-db@0.1.6
Patch Changes
- 052c049: vite: keep Drizzle base classes through Rolldown (Vite 8) production tree-shaking.
drizzle-ormshipssideEffects:false, and Rolldown fails to count a cyclicclass Sub extends Baseas a use ofBase— so it deletes the apparently-unused base-class module entirely while keeping theextendsreference, crashing the worker at boot with e.g.TypedQueryBuilder is not defined.databasePlugin()now tagsdrizzle-ormmodules side-effectful (a plugin hook'smoduleSideEffectsoutranks the package'ssideEffectsfield), forcing Rolldown to retain them. Fixes the bug at the source for any durable-db + Vite 8 consumer.
durable-db@0.1.5
Patch Changes
-
597c275: Strip the Cloudflare RPC
Symbol.disposefrom action results in the generated
page→DO stub too.0.1.4 stripped it in
callAction(the action-to-action cross-DB path), but the
common case — an app page /"use server"function calling an action, which goes
through the Vite-plugin-generated action proxy (stub.rpc(...)) — still
returned the symbol-keyed RPC object, so passing the result to a client component
still threw "Objects with symbol properties like Symbol.dispose are not supported".
The generated proxy's out-of-DO path now returnsstub.rpc(...).then(structuredClone),
yielding a plain clone (symbol keys dropped, Date/Map/Set preserved). Verified
end-to-end in a ShopLayer app.
durable-db@0.1.4
Patch Changes
-
fd6a6c5: Strip the Cloudflare RPC
Symbol.disposefrom cross-DB action results.Action results returned over the Durable Object RPC boundary carry a top-level
Symbol.disposethat Cloudflare attaches for explicit resource management. React
Server Components refuse to serialize symbol-keyed props ("Objects with symbol
properties like Symbol.dispose are not supported"), so returning an action result
from a"use server"function — or passing it straight to a client component —
threw at runtime.callActionnowstructuredClones the RPC result, dropping all
symbol keys while preserving value types (Date/Map/Set/etc., unlike a JSON
round-trip). The same-DB direct-call and WebSocket transport paths already returned
plain values and are unchanged.
durable-db@0.1.3
Patch Changes
- c1f1c85: Replace the
debugdependency with a tiny internal logger.debug's entry is a
runtime-conditionalmodule.exports = require(isBrowser ? './browser' : './node'),
which Rolldown can't statically interop. Under Vite 8 thedatabase:vite/cli/migrations
namespaces get pulled into the worker runtime viacontext, and the worker
module-runner evaluates that CJS with nomodulein scope → crash at boot
("module is not defined"). The internal logger keeps the samecreateDebug(namespace)
API andDEBUG-env gating, with zero dependencies.
durable-db@0.1.2
Patch Changes
- Alias the injected
typeimport so it never collides with an app's owntype
import. The action transform injectsimport { …, type } from "durable-db/registry"
into the user's file; if the app already doesimport { type } from '…'(e.g.
arktype'stypere-exported by a framework), the two top-leveltypebindings are a
duplicate declaration. Rollup tolerated it, but rolldown (Vite 8+) rejects it with
Identifier 'type' has already been declared. The injectedtypeis now imported
under an internal alias (__ddType) and the generated validators reference that,
so the transform still routes to durable-db's own arktype instance with no clash.