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

@knockdata/sqlite

v3.51.3-r.2

Published

A minimal SQLite build for browsing database files — native addon and wasm from one source

Readme

@knockdata/sqlite

SQLite as a library you can actually ship: SQL in, rows out. One source builds two engines — a native N-API addon and a wasm module — and they answer identically, so the same code runs in Node, in a browser and inside a single-file binary.

native addon   1.3 MB     the fast path, one prebuilt binary per platform
wasm glue       72 KB     bundles with your app
wasm binary    1.2 MB     served once and cached, not inlined

The companion package is @knockdata/duckdb, built the same way for duckdb files.

Install

npm install @knockdata/sqlite

The native addon is not built on install. It arrives as one of six platform packages named in optionalDependencies — npm's os/cpu fields make the resolver skip every one that does not match, so exactly one binary is downloaded, no compiler is involved, and there is no install script. If no addon matches, the wasm is used instead and everything still works.

Use

import Sqlite from '@knockdata/sqlite'

const db = await Sqlite('/data/app.db')            // a path in Node, a File or bytes in the browser

await db.query('SELECT id, name FROM users LIMIT 2')
// → [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' } ]

await db.query('SELECT name FROM users WHERE age < ?', [30])
// → [ { name: 'Bob' } ]

await db.close()

query(sql, params) returns rows, exec(sql) runs a statement for its effect, run(sql, params) returns { changes, lastInsertRowid }, and close() releases the file. A path is read in place; bytes (a database nested inside an archive) are spilled to a temp file and cleaned up on close().

Databases are opened read only. Not just the connection — the file descriptor too. Reading a database never rewrites it, never leaves a -journal or -wal beside it, and works on a file the user cannot write at all. For a database you own and want to write:

const cache = await Sqlite('/data/cache.db', { readOnly: false })
await cache.exec('CREATE TABLE IF NOT EXISTS pairs (key TEXT PRIMARY KEY, value TEXT)')
await cache.run('INSERT INTO pairs VALUES (?, ?)', ['a', '1'])
await cache.close()

TEXT comes back as a string, REAL as a number, BLOB as an ArrayBuffer, NULL as null. An INTEGER is a number while that is lossless and a bigint past 2^53-1 — always returning a number would silently corrupt large ids, and always returning a bigint would make every ordinary row awkward and unserialisable. Both engines draw that line in the same place, which the test suite checks by type, not just by value.

Listing what is in a database

There is no getEntries here, on purpose. "A table looks like this, a view looks like that, here is a page of rows" is an application's shape, not SQLite's — bake one app's answer into the engine package and the next app spends its time fighting it. Ask SQLite directly; it is three queries:

await db.query("SELECT name, type FROM sqlite_master WHERE type IN ('table','view') ORDER BY name")
await db.query('PRAGMA table_info("users")')
await db.query('SELECT * FROM "users" LIMIT 100 OFFSET 0')

Identifiers cannot be bound as parameters, so a table name is interpolated — double any embedded quote (name.replaceAll('"', '""')) before you do.

In the browser

The wasm binary is 1.2 MB, so this package does not decide how you get it — serve it, cache it, however suits your app — you just hand over the bytes once:

import { setWasmBinary } from '@knockdata/sqlite/browser.js'

setWasmBinary(await (await fetch('/sqlite/sqlite.wasm')).arrayBuffer())

The file to serve is node_modules/@knockdata/sqlite/wasm/sqlite.wasm. The 72 KB of glue next to it bundles with your app normally. The engine runs in a dedicated worker with an OPFS VFS, because that is the only place FileSystemSyncAccessHandle exists; the build is single-threaded, so it needs no SharedArrayBuffer and no COOP/COEP headers.

In a single-file bundle

An SEA or an esbuild bundle has no node_modules to resolve against. Unpack the two files wherever you like and point the package at them:

import { setEngineDir } from '@knockdata/sqlite/engineDir.js'

setEngineDir('/somewhere/sqlite')   // holding sqlite_napi.node and wasm/sqlite.wasm

engineDir.js is a separate entry, not part of the main one, because it reads the filesystem — importing it from the root would pull node:fs into every browser bundle. wasmPath() from the same module answers where the wasm is, which is what a server serving /sqlite/sqlite.wasm needs.

Build it yourself

bash build.sh          native  -> build/Release/sqlite_napi.node
bash build.sh wasm     wasm    -> wasm/sqlite.js + wasm/sqlite.wasm

Both start from the same amalgamation, which build.sh downloads from sqlite.org and unpacks to a fixed directory name — two files, no ./configure, no tclsh. A build is a couple of minutes. Only the N-API wrapper (napi/sqlite_napi.c), the JS layer and the VFSes are ours; the engine is upstream SQLite, unmodified.

Versioning

<upstream>-r.<revision>3.51.3-r.1 is our first build of SQLite 3.51.3. The revision moves when only our build or JS changed; the base moves when SQLite does. Pin exactly: the platform packages are pinned to the exact version too, so a range would let them drift apart.

License

MIT for this repository. SQLite itself is in the public domain.