npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

persistence-drizzle

v0.1.3

Published

A typed Drizzle ORM database over any SQLite driver: one lock serializing every statement, BEGIN IMMEDIATE transactions, drizzle-kit migrations at open, typed Preferences and a hardened export envelope. Drivers for Capacitor SQLite and node:sqlite.

Readme

persistence-drizzle

CI npm License: MIT

A typed Drizzle ORM database over any SQLite driver, built for an app that owns one SQLite connection and several writers: a Capacitor app on a phone, a browser app on sql.js, a Node test suite.

One SQLite connection runs one statement stream. This package puts a first-in, first-out lock in front of it so that every statement, every transaction and every foreign writer is serialized, and adds what a relational save needs on top:

  • a Drizzle sqlite-proxy database whose every statement takes the lock, and whose writes are flushed before they resolve;
  • BEGIN IMMEDIATE transactions that hold the lock throughout, so nothing can land inside one;
  • drizzle-kit migrations applied at open, each in its own transaction, with tag and hash bookkeeping that refuses a database written by a newer build and a shipped migration edited after it ran;
  • typed Preferences for small settings over any string key-value store;
  • a hardened export envelope for moving a save between devices as text;
  • drivers for Capacitor SQLite (persistence-drizzle/capacitor) and node:sqlite (persistence-drizzle/node), over a one-method-per-operation SqlDriver seam you can implement for any other engine.

Install

npm install persistence-drizzle drizzle-orm

drizzle-orm (>=0.45.2 <1) is the only peer dependency. Node.js 22.16.0 or later is supported, including the maintained 22, 24 and 26 lines. The root and persistence-drizzle/capacitor imports are engine-free; the optional persistence-drizzle/node entry point requires that Node floor for node:sqlite.

Quick start

import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { openDatabase } from 'persistence-drizzle'
import { createNodeSqliteDriver } from 'persistence-drizzle/node'

const players = sqliteTable('players', {
  id: integer('id').primaryKey(),
  name: text('name').notNull(),
})

const database = await openDatabase({
  driver: createNodeSqliteDriver(), // in memory; pass a file path to persist
  schema: { players },
  migrations: [
    { tag: '0000_players', sql: 'CREATE TABLE players (id integer PRIMARY KEY NOT NULL, name text NOT NULL);' },
  ],
})

await database.transaction(async (tx) => {
  await tx.insert(players).values({ id: 1, name: 'Player One' })
})
console.log(await database.db.select().from(players))
await database.close()

With a drizzle-kit output folder, read the migrations from disk in Node, or bundle the .sql files and the journal with your build tool:

import { migrationsFromJournal } from 'persistence-drizzle'
import journal from './drizzle/meta/_journal.json'

const migrations = migrationsFromJournal(
  journal,
  import.meta.glob('./drizzle/*.sql', { query: '?raw', import: 'default', eager: true }),
)

On a device or the web

persistence-drizzle/capacitor drives any host shaped like the persistence-save connection manager: it provides withConnection(operation, fallback), flush() and close(), where operation receives a Capacitor SQLite connection. The package depends on neither, so a host from any version that has those three methods fits.

import { openDatabase } from 'persistence-drizzle'
import { createCapacitorDriver, guardSnapshots } from 'persistence-drizzle/capacitor'

const database = await openDatabase({ driver: createCapacitorDriver(host), schema, migrations })
// Snapshot saves go through the same lock, so they never land inside a transaction.
const snapshots = guardSnapshots(snapshotFacade, database)

API

| Export | What it does | | --- | --- | | openDatabase({ driver, schema, migrations, now? }) | Applies the migrations, then returns { db, transaction, exclusive, migrations, driverName, close } | | db | A Drizzle SqliteRemoteDatabase<schema>. Each statement takes the lock; a write is flushed before it resolves. db.transaction() is refused (TransactionMisuseError) | | transaction(fn) | BEGIN IMMEDIATE, then COMMIT, or ROLLBACK on a throw, with the lock held; one flush on commit. Use the tx given to fn. A tx kept past its transaction refuses to run | | exclusive(fn) | Holds the lock for a writer outside Drizzle on the same connection, then flushes | | migrationsFromJournal(journal, files) | The migrations in journal order from bundled files | | MigrationError | reason: invalid-list, unknown-applied (a newer build wrote this database), drift (an applied migration changed), failed (rolled back whole) | | createEnvelope(target, version, data, exportedAt) and parseEnvelope(text, target) | Export and import of a domain value. Reading is bounded, strips __proto__, constructor and prototype at every depth, refuses other apps, other kinds and newer versions, and never throws | | parseUntrustedJson, stripForbiddenKeys | The same hygiene for any stored or imported JSON | | createTypedPreference({ kv, key, parse, defaults }) | load (defaults on anything unusable), save (refuses what parse rejects), clear. createMemoryKv() for tests | | persistence-drizzle/capacitor: createCapacitorDriver(host) | The device and web driver over a withConnection / flush / close host | | persistence-drizzle/capacitor: guardSnapshots(facade, database) | A snapshot facade with every call under the database's lock | | persistence-drizzle/node: createNodeSqliteDriver(location?), readMigrationsFolder(dir) | node:sqlite for tests and tools; a drizzle-kit output folder from disk |

Full signatures are in docs/API.md, the invariants in docs/ARCHITECTURE.md.

Rules

  • One connection, one lock. Every writer on the connection goes through the database: Drizzle through db or transaction, snapshot saves through guardSnapshots, anything else through exclusive.
  • Inside transaction(fn), use tx. db would wait for the lock fn holds.
  • Alias clashing column names in joins. Capacitor SQLite returns rows as objects, so two result columns with one name collapse into one key.
  • No RETURNING on the device. The plugin's query is for reads; insert, then select.
  • Never edit a shipped migration. Generate a new one; the runner refuses drift.
  • Export the domain value, not rows. Rows change shape with every migration; the domain value carries its own version.

Compatibility

| | | | --- | --- | | Node | Node.js 22.16.0 or later; CI runs the maintained 22, 24 and 26 major lines on Linux and 26 on Windows. The root and Capacitor entries are engine-free; persistence-drizzle/node uses node:sqlite. | | drizzle-orm | >=0.45.2 <1 | | Module formats | ESM and CommonJS, with types for both |

Links

License

MIT