Skip to main content

Upgrading from v2

v3 is a rewrite, not an incremental release. The schema builder keeps its shape; almost everything else is new. Plan the upgrade as a port.

v2v3
DeclarativeDatabaseDatabase.open<Rows, SyncedTables>({ schema, adapter })
AdapterFactoryopenAdapter({ name, wasmDir })
SchemaBuilderSame fluent API. Drop .lww(), add .synced({ key, scope }) to server tables
.lww(), __hlc columns, HlcGone. The server decides: last change to arrive wins
dirtyRowStoreThe built-in outbox table
db.update(...) on a server tablesync.outbox.record({ table, systemId, changes })
db.stream(...), RxJS streamsdb.live(spec) / useLiveQuery(spec)
bulkLoad(...)sync.pull.pull(table, scope)
Push / debounced syncAutomatic after record; sync.push.pushNow() to force
Realtime change handlersync.ticks.notify({ table, seq, scopes })
File management (fileset)Removed from the package

Steps​

  1. Update the schema: remove .lww(), mark server-owned tables .synced(), and give every notNull column a default.
  2. Replace database setup with openAdapter + Database.open + createSyncRuntime.
  3. Write a SyncTransport for your API (Server protocol). The server must provide sequence numbers and batch idempotency.
  4. Replace writes to synced tables with sync.outbox.record. The type checker will point at every call site: synced tables have no write methods on db.tables.
  5. Replace streams with live queries, and form inputs with useDraftField.

Existing data​

v3 uses its own tables (outbox, sync_cursor) and the sync_seq column. The simplest safe upgrade is a fresh database file under a new name, filled by a full pull. Before switching, push any unsent v2 changes, or they'll be lost.

Not in v3​

  • Creating or deleting server rows from the client. The push format carries column changes to existing rows only.
  • File storage.
  • Client-side conflict resolution (HLC / LWW).

The full, app-specific migration notes are in MIGRATION-v2-to-v3.md.