Reference

API reference

Every export of persistence-drizzle, its signature and its failure behavior.

The package has three entry points: persistence-drizzle (engine-free), persistence-drizzle/capacitor and persistence-drizzle/node.

persistence-drizzle

openDatabase(options)

function openDatabase<TSchema extends Schema>(
  options: OpenDatabaseOptions<TSchema>,
): Promise<PersistenceDatabase<TSchema>>
OptionTypeMeaning
driverSqlDriverThe SQLite engine.
schemaTSchemaThe Drizzle schema (tables and relations), as passed to drizzle().
migrationsreadonly Migration[]The migrations in order.
now() => numberClock stamped on applied migrations. Default Date.now.

Migrations run under the lock before the promise resolves. A failure rejects with MigrationError and the database is left at the last good migration.

PersistenceDatabase<TSchema>:

MemberMeaning
dbA Drizzle SqliteRemoteDatabase<TSchema>. Each statement takes the lock; a write (run, or any INSERT, UPDATE, DELETE, REPLACE, CREATE, DROP, ALTER) is flushed before it resolves. db.transaction() and any raw BEGIN, COMMIT, ROLLBACK, SAVEPOINT or RELEASE reject with TransactionMisuseError.
transaction(fn)Runs BEGIN IMMEDIATE, then fn(tx), then COMMIT; on a throw it runs ROLLBACK and rethrows. The lock is held throughout and the result is flushed once. tx.transaction inside is a savepoint. A tx used after its transaction ended rejects.
exclusive(fn)Runs fn with the lock held, then flushes. For a writer outside Drizzle on the same connection.
migrationsMigrationReport: applied (every tag the database holds) and ran (the tags this open applied).
driverNameThe driver's name, for diagnostics.
close()Waits for pending work, then closes the driver. Later calls reject. Closing twice is a no-op.

SqlDriver

The seam between the Drizzle layer and a SQLite engine.

interface SqlDriver {
  readonly name: string
  run(sql: string, params: readonly SqlParam[]): Promise<void>
  query(sql: string, params: readonly SqlParam[]): Promise<unknown[][]>
  flush(): Promise<void>
  close(): Promise<void>
}
type SqlParam = string | number | bigint | null | Uint8Array

query returns rows as arrays in column order. flush makes writes durable (a no-op where they already are). A driver never wraps a statement in a transaction of its own. PersistenceUnavailableError is thrown when a driver's engine cannot be opened in the current environment.

Migrations

interface Migration { readonly tag: string; readonly sql: string }
function migrationsFromJournal(journal: DrizzleJournal, files: Record<string, string>): Migration[]
function applyMigrations(driver: SqlDriver, migrations: readonly Migration[], now: () => number): Promise<MigrationReport>
function splitStatements(sql: string): string[]
function migrationHash(sql: string): string
const MIGRATIONS_TABLE: string

tag matches ^[A-Za-z0-9_-]{1,128}$. files maps a path or a tag to the SQL text; the base name without .sql is matched against the journal. A journal entry without a file, or a file the journal does not list, throws MigrationError('invalid-list'). applyMigrations expects the caller to hold the connection's lock; openDatabase does.

MigrationError.reason is one of invalid-list, unknown-applied, drift, failed; tag names the migration involved when there is one.

Envelope

function createEnvelope<T>(target: EnvelopeTarget, version: number, data: T, exportedAt: number): string
function parseEnvelope(text: string, target: EnvelopeTarget & { currentVersion: number; maxChars?: number }): EnvelopeResult
function parseUntrustedJson(text: string, maxChars?: number): JsonResult
function stripForbiddenKeys(value: unknown): unknown
const ENVELOPE_FORMAT: 'persistence-drizzle.save'
const DEFAULT_MAX_CHARS: 2_000_000

EnvelopeTarget is { app: string; kind: string }. createEnvelope throws RangeError for a version that is not a positive safe integer. parseEnvelope never throws; failures are { ok: false, reason } with reason one of too-large, not-json, too-deep, not-envelope, wrong-app, wrong-kind, invalid-version, future-version. stripForbiddenKeys throws RangeError past 32 levels of nesting.

Preferences

function createTypedPreference<T>(options: { kv: StringKv; key: string; parse: (raw: unknown) => T | null; defaults: T; maxChars?: number }): TypedPreference<T>
function createMemoryKv(): StringKv & { readonly entries: Map<string, string> }

StringKv is { get(key): Promise<string | null>; set(key, value): Promise<void>; remove(key): Promise<void> }. TypedPreference<T> has load(), save(value) (returns { ok: true } or { ok: false, error }) and clear().

Lock

createLock() returns { run(task), held }: a first-in, first-out async lock. A rejection passes through to the caller and releases the lock.

persistence-drizzle/capacitor

function createCapacitorDriver(host: ConnectionHost): SqlDriver
function guardSnapshots<TFacade>(facade: TFacade, database: { exclusive }): Omit<TFacade, 'close'>
function rowsToArrays(values: readonly unknown[] | undefined): unknown[][]

ConnectionHost is { withConnection(operation, fallback), flush(), close() }; operation receives a CapacitorConnection (run and query of a Capacitor SQLite connection). rowsToArrays turns the plugin's keyed-object rows, including iOS's leading { ios_columns } row, into arrays in column order.

guardSnapshots wraps save, load, list, delete, withConnection and flush of a snapshot facade so each runs under the database's lock. close is removed from the result: the database closes the shared connection once.

persistence-drizzle/node

function createNodeSqliteDriver(location?: string): NodeSqliteDriver
function readMigrationsFolder(folder: string): Migration[]

location is a file path or :memory: (the default). The returned driver exposes raw, the underlying DatabaseSync, for assertions below Drizzle. This entry point requires Node.js 22.16.0 or later because it uses StatementSync.setReturnArrays() to preserve the SqlDriver row-array contract. readMigrationsFolder reads meta/_journal.json and every .sql file of a drizzle-kit output folder. The root and Capacitor entry points import no Node engine.