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

@yydb/sqlite

v0.2.0

Published

TypeScript surface over the SQLite format-3 native binding

Readme

@yydb/sqlite

Sync Node facade over a SQLite format-3 engine: new SQLite() opens a usable connection, a fail-closed SQL subset runs immediately, and storage classes map faithfully (INTEGER → bigint, BLOB → Uint8Array). Matching @yydb/sqlite-<platform> optional dependencies supply the .node binding — app code never imports them.

This is not full SQLite feature parity and not a browser package. For WebAssembly use @yydb/sqlite-unknown-wasm32. Product overview: repository README.

Install

Requires Node.js 18+. Install the facade only — platform packages resolve as optional dependencies on supported hosts:

pnpm add @yydb/sqlite
# npm i @yydb/sqlite · yarn add @yydb/sqlite

If the host has no matching optional package (unsupported os/cpu, or optional deps disabled), loading fails at import time. Override the binary path with SQLITE_NATIVE=/absolute/path/to/sqlite.<platform>.node when you stage a binding yourself.

Quick start

import {SQLite} from "@yydb/sqlite";

const db = new SQLite({filename: "app.sqlite"});
db.exec("create table account (id integer primary key, label text not null)");
db.run("insert into account (id, label) values (?, ?)", [1n, "alpha"]);
const row = db.get<{ id: bigint; label: string }>(
    "select id, label from account where id = ?",
    [1n],
);
db.close();

In-memory: new SQLite(). Restore a private copy from bytes: new SQLite({ data }) (does not write back into the buffer).

Exports

| Import | Role | |------------------------|-----------------------------------------------------------------------| | @yydb/sqlite | SQLite, Statement, TransactionScope, SQLiteError, value types | | @yydb/sqlite/inspect | Read-only format-3 inspection + snapshot/writer helpers |

Transitional JSON primitives (createEngine, executeSql, bindings, …) are still re-exported from the root for ORM adapters and harnesses that have not migrated. New application code should use SQLite and @yydb/sqlite/inspect only.

Core API

SQLite connection

| Member | Notes | |--------------------------------------------------------------------|----------------------------------------------------------------------| | new SQLite(options?) | Opens immediately (sync). See options below. | | prepare / run / get / all / iterate | One statement; params optional when the SQL has no placeholders | | exec(script) | Multi-statement script, returns void (no implicit transaction) | | transaction(cb) | Sync closure: success commits, throw rolls back; nested → savepoints | | pragma(name, { scalar?, value? }) | Read or set; { scalar: true } returns the first cell | | serialize() | Consistent format-3 image (Uint8Array) | | backup(path) | Storage-layer file backup; refuses inside a transaction | | close() / [Symbol.dispose]() | Idempotent close | | filename, isOpen, readOnly, inTransaction, sqliteVersion | Connection state |

const db = new SQLite({filename: "app.sqlite", foreignKeys: true});
db.pragma("user_version", {value: 3});
console.log(db.pragma("user_version", {scalar: true})); // 3n or number from engine
const image = db.serialize();
db.backup("app.backup.sqlite");

Options (SQLiteOptions):

| Option | Default | Behavior | |----------------------------|--------------|--------------------------------------------------------------| | filename | — | Path or URL; mutually exclusive with data | | data | — | Open a private in-memory copy of a format-3 image | | create | true | Create file when missing (false requires an existing file) | | readOnly | false | Reject writes; never mutate the file | | foreignKeys | true | Applied via PRAGMA foreign_keys on open | | pageSize | 4096 | Page size for new databases | | synchronous | true | false skips durable flush on commit | | journalMode | "preserve" | "preserve" / "delete" / "truncate"; "wal" throws unsupported | | busyTimeoutMs | 0 | Milliseconds to retry the cross-process write lock (busy) | | limits | — | Unsupported — passing it throws unsupported |

A file write connection takes an OS advisory lock on <db>.lock and holds it until close(), a finalizer close, or process exit. Another writer from this package on the same file is rejected with code: "busy" and a SQLITE_BUSY message; busyTimeoutMs retries for that many milliseconds first. 0 attempts once. Read-only and in-memory connections never take the write lock. A connection garbage-collected without close() is closed by a finalizer, which releases the lock, but explicit close() (or using) remains the recommended contract. External engines (sqlite3, node:sqlite, better-sqlite3) do not create or check this lock, so concurrent writes from another engine are outside the contract — keep a single writer per file, including across engines.

Statement

Prepared handles share one native statement. Views (raw / pluck) share lifecycle with the parent.

const insert = db.prepare("insert into account (id, label) values (@id, @label)");
insert.run({id: 2n, label: "beta"}); // bare keys or :/@/$ prefixes all match

const byId = db.prepare<{ id: bigint; label: string }, readonly [bigint]>(
    "select id, label from account where id = ?",
);
byId.get([2n]);
byId.raw().get([2n]);     // SQLiteValue[]
byId.pluck().get([2n]);   // first column only
byId.close();
  • Positional ? placeholders require an array; named (:name / @name / $name) require an object.
  • undefined is not bindable — use explicit null for SQL NULL.
  • boolean, Date, plain objects, and nested arrays are rejected (param-type); convert at the call site.
  • bigint must fit signed 64-bit INTEGER.
  • columns() returns the result header captured at prepare time for row-set statements; statements without a result set return [], and columns-pending only surfaces for a row-set statement whose header the engine cannot resolve until the first execution.
  • An active iterate() cursor blocks re-execution until exhausted, return()d, or closed (cursor-active).

Transactions

db.transaction((tx) => {
    tx.run("insert into account (id, label) values (?, ?)", [3n, "gamma"]);
    tx.exec("update account set label = 'gamma!' where id = 3");
});

Callbacks must be synchronous — a returned Promise rolls back and throws async-callback. Inside transaction(), do not issue BEGIN / COMMIT / ROLLBACK by hand (transaction-ownership). Nesting uses savepoints; only mode: "deferred" is accepted.

Value mapping

| SQLite | JavaScript | |---------|----------------------------------------------------| | NULL | null | | INTEGER | bigint (including changes / lastInsertRowid) | | REAL | number | | TEXT | string | | BLOB | Uint8Array |

There is no automatic Date / boolean coercion and no “integers as number” mode.

@yydb/sqlite/inspect

SQL-free format-3 readers and builders. Prefer these when you need schema/table dumps without opening a SQL connection.

import {openDatabase, openSnapshot, createWriter} from "@yydb/sqlite/inspect";

const reader = openDatabase("app.sqlite");
console.log(reader.schema(), reader.table("account"));

const snap = openSnapshot(db.serialize());
console.log(snap.tableDecoded("account"));

const writer = createWriter(4096);
writer.createTable("account", JSON.stringify([
    {name: "id", declaredType: "INTEGER", primaryKey: true},
    {name: "label", declaredType: "TEXT", notNull: true},
]));
writer.insert("account", JSON.stringify([1, "alpha"]));
writer.save("built.sqlite");

Also: openInMemoryDatabase(), openWriter(path), decodeRecord(payload).

Platform matrix

| Package | Host | Role | |-------------------------------|---------------------|----------------------------------| | @yydb/sqlite-win32-x64 | Windows x64 | Native .node sidecar | | @yydb/sqlite-linux-x64 | Linux x64 | Native .node sidecar | | @yydb/sqlite-darwin-x64 | macOS Intel | Native .node sidecar | | @yydb/sqlite-darwin-arm64 | macOS Apple Silicon | Native .node sidecar | | @yydb/sqlite-unknown-wasm32 | Browser / WASM | MemoryDatabase + .wasm asset |

Native sidecars are not public import targets. The facade resolves @yydb/sqlite-${platform}-${arch} at load time. Other OS/arch combinations are unsupported unless you set SQLITE_NATIVE.

Interop

Written files are ordinary SQLite format-3 databases:

  • Images from serialize() / backup() / inspect writers open in stock SQLite, better-sqlite3, Node node:sqlite, and CPython sqlite3.
  • External engines can write fixtures that this package’s readers and SQL engine consume (integrity and corpus suites do exactly that).
  • WASM MemoryDatabase.snapshot() produces the same main-file bytes; feed them to new SQLite({ data }) or openSnapshot on Node.

ORM demos (adapter-level, transitional primitives): with-drizzle-orm, with-prisma, with-type-orm.

Errors

Failures surface as SQLiteError with a stable code string and optional cause. Engine rejections arrive as a JSON envelope on the native error message and are unpacked into code / message.

| Code (examples) | When | |--------------------------------------------------------------------------------|-----------------------------------------------------------------------| | unsupported | Requested option/API not implemented (wal, UDF, limits, …) | | busy | The file write lock is held elsewhere past busyTimeoutMs | | option-invalid / option-conflict | Bad pragma name, invalid busyTimeoutMs, or conflicting options | | param-* | Missing / extra / wrong-shape / wrong-type / out-of-range binds | | db-closed / statement-closed / cursor-active | Lifecycle misuse | | transaction-ownership / async-callback / commit-failed / scope-escaped | Transaction control flow | | backup-in-transaction | backup() while inTransaction | | duplicate-column | Object-row projection hit duplicate labels — use AS or .raw() | | engine | Unstructured binding/native failure |

import {SQLite, SQLiteError} from "@yydb/sqlite";

try {
    new SQLite({filename: "app.sqlite", journalMode: "wal"});
} catch (error) {
    if (error instanceof SQLiteError) console.error(error.code, error.message);
}

Limitations

  • Fail-closed SQL: the implemented surface covers CREATE TABLE (WITHOUT ROWID, STRICT, foreign keys), indexes (composite, partial, expression and COLLATE), views, triggers, INSERT / UPDATE / DELETE with RETURNING and UPSERT, joins, subqueries, recursive CTEs, set operators, aggregates, window functions, JSON, date/time and math functions, transactions and savepoints, ATTACH / DETACH, PRAGMA and VACUUM / VACUUM INTO. Statements outside that surface reject with a structured SQLiteError instead of being approximated.
  • Journal / locking: journalMode supports "preserve", "delete" and "truncate"; there is still no WAL write surface. "delete" removes <db>-journal after a commit and "truncate" leaves it at zero bytes; both write the rollback journal before publishing, so a reopen can restore the previous image. File writers using this package serialize on a cross-process lock with busyTimeoutMs retry. That lock is not SQLite's byte-range/journal lock and is invisible to external engines, so it does not make concurrent external SQLite writes safe.
  • UDF / aggregate / window registration: function() / aggregate() / window() always throw unsupported. Window functions written in SQL are supported; the host registration APIs are not.
  • Result materialization: query results are fully materialized arrays, not a streaming cursor. iterate() is an Iterator protocol over an already-materialized native result, so peak memory grows with the result row count.
  • Hosts: Node native sidecars for the four platforms above; browsers use the WASM package. No published binding for other arches yet.
  • Sync-only API: no Promise-based query surface; transaction callbacks cannot be async.

Links