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

zova-wasm

v1.1.0

Published

Experimental Zova SQL and KV for browsers with memory and OPFS storage.

Readme

zova-wasm

Experimental Zova SQL and binary KV for browsers, in memory or named OPFS storage. The real Zova core and bundled SQLite run inside a dedicated worker as one WebAssembly module.

This experimental package follows the repository release (1.1.0, format 11). Browser API stability and full native compatibility are not promised. Memory databases lose their data when closed or when their worker/page terminates.

Try a local package

Build and pack using the instructions below, then install the resulting tarball with bun add /absolute/path/zova-wasm-1.1.0.tgz in your browser application. The package exports browser ESM and TypeScript declarations, with no native addon dependency. Serve over HTTP(S), allowing module workers and WebAssembly. Worker and WASM URLs resolve relative to the package; keep its files together when deploying. Bundler compatibility is not yet a tested guarantee.

import { Database, ZovaWasmError } from 'zova-wasm';

const db = await Database.createMemory();
try {
  await db.exec('CREATE TABLE tasks(id INTEGER, title TEXT)');
  await db.query('INSERT INTO tasks VALUES (?, ?)', [1n, 'Try browser Zova']);
  const result = await db.query('SELECT id, title FROM tasks WHERE id = ?', [1n]);
  console.log(result.columns); // ['id', 'title']
  console.log(result.rows);    // [[1n, 'Try browser Zova']]

  const encode = (text: string) => new TextEncoder().encode(text);
  await db.kv.put(encode('cache'), encode('greeting'), encode('hello'));
  const value = await db.kv.get(encode('cache'), encode('greeting'));
  console.log(value === null ? 'missing' : new TextDecoder().decode(value));
  console.log(await db.kv.delete(encode('cache'), encode('greeting'))); // true
} catch (error) {
  if (error instanceof ZovaWasmError) console.error(error.status, error.message);
  else throw error;
} finally {
  await db.close();
}

Named persistent databases

import { Database } from 'zova-wasm';

const encode = text => new TextEncoder().encode(text);
const db = await Database.openPersistent('my-app'); // create or reopen
try {
  await db.exec('CREATE TABLE IF NOT EXISTS tasks(id INTEGER PRIMARY KEY, title TEXT)');
  await db.query('INSERT OR REPLACE INTO tasks VALUES (?, ?)', [1n, 'Keep this task']);
  await db.kv.put(encode('settings'), encode('theme'), encode('sage'));
} finally {
  await db.close();
}

const reopened = await Database.openPersistent('my-app');
try {
  const result = await reopened.query('SELECT title FROM tasks WHERE id = ?', [1n]);
  const theme = await reopened.kv.get(encode('settings'), encode('theme'));
  console.log(result.rows[0][0]); // Keep this task
  console.log(new TextDecoder().decode(theme)); // sage
} finally {
  await reopened.close();
}

Names are case-sensitive: 1–64 ASCII letters, digits, underscores or hyphens, starting with a letter or digit. They are logical names, not filesystem paths. SQL and KV methods are unchanged. Existing files are validated before writable opening; incompatible or invalid databases reject without automatic migration. Missing OPFS support rejects rather than silently opening a memory database.

Persistent opening requires a secure context (HTTPS or localhost), dedicated workers, Web Locks, and OPFS synchronous access handles. Each name has one exclusive pool. An atomic, non-waiting Web Lock is acquired before storage initialization; a competing tab or worker rejects with ZOVA_BUSY (status code 10). No active owner is evicted. Different names can be open concurrently. Close releases storage handles before ownership; failed initialization and worker/tab termination also release ownership. Retry opening after the owner has closed or terminated. Storage belongs to the origin and browser profile. Clearing site data or browser eviction can remove it. There is no export/backup API yet. Broader crash, quota, and multi-tab recovery guarantees remain experimental.

Errors and recovery

Catch ZovaWasmError around opening and writes, inspecting status and statusCode. ZOVA_BUSY means another owner is active: wait for it to close, then retry with a bounded delay. Worker termination releases locks asynchronously; do not spin or try to steal them. ZOVA_CANT_OPEN can indicate missing storage or locking support. Invalid names produce ZOVA_INVALID_ARGUMENT.

Format/schema errors are not a request to recreate or delete the database. There is no automatic migration here. Storage write/flush failures reject rather than fall back to memory; quota failures may surface through SQLite's storage error mapping, not as a JavaScript QuotaExceededError. Do not assume a failed write succeeded or blindly replay a larger application operation. For an explicit SQL transaction, attempt rollback, close, and handle recovery at the application boundary. SQL and KV writes in the example are separate commits, not one combined transaction.

Browser storage is not a backup

The same name belongs to the same origin (scheme, host and port) and browser profile. It is neither a user-selected file nor synchronization with a server or another device. Changing the origin/profile gives different storage. Clearing site data can delete the database.

Browsers normally use best-effort storage and can evict it under storage pressure. Your application may call navigator.storage.persist() from its window context and inspect its boolean result; the browser may grant or deny it, with permission UI varying by browser. Zova does not request this permission automatically. openPersistent() names the disk-backed mode, not a grant of that permission. A grant protects against automatic eviction but is not unlimited capacity, a backup, or protection from user deletion. navigator.storage.estimate() provides an estimate, not reserved space. See browser quotas and eviction and persistent-storage permission.

The local packed-artifact gate is verified with Helium; Linux CI targets Playwright Chromium. Firefox, Safari, mobile browsers and private-browsing modes have not been qualified. Do not infer support from API presence alone. Private browsing can restrict storage and normally clears it when the private session ends; no private-mode persistence guarantee is made.

Tests cover reload, rollback, worker termination, and injected quota/write/flush errors. Real quota exhaustion, browser-process crashes, OS crashes and power loss remain unverified. There is no unload-time save requirement for completed commits, but these tests are not a general crash-durability or performance guarantee.

Experimental WASM capabilities (introduced in rc.3)

  • Adds experimental named OPFS SQL/KV storage alongside createMemory().
  • Adds exclusive per-name ownership with explicit busy errors and cleanup.
  • Adds packed-artifact persistence, rollback and injected-storage-fault coverage.
  • Defers export/import, shared multi-tab connections and automatic migration.
  • Does not promise native feature parity, stable browser APIs or performance.

Values and lifecycle

SQL accepts null, finite numbers, signed 64-bit bigint, strings, and Uint8Array. Numbers bind as floating-point values; use bigint for integer parameters. Integer result columns return bigint. Positional rows preserve column order even when names repeat. Text and blobs are copied out of native memory.

KV namespaces, keys, and values are Uint8Array values. Missing get returns null; an empty stored value returns an empty Uint8Array. Delete reports whether the key existed. Caller buffers remain attached and are copied when requests send.

Each database owns one worker. Concurrent calls execute in request order. Close follows earlier work, is idempotent, and marks closed immediately. Operations after close reject. Worker failures reject outstanding work and mark the database closed. Errors include status and statusCode.

Current boundaries

There is no import/export, public prepared-statement handle, transaction callback helper, graph/vector/object helper, native extension, bound-store API, migration, or shared access between workers. The native zova-js package is independent. There is no Node/CommonJS fallback.

There is no public bundled-extension lifecycle or application SQL callback API, and no .zovaext loader or extension-data upgrade API. SQL execution does not expose those native capabilities. See the extension capability matrix.

Memory databases use volatile storage; named databases use the bundled SQLite's OPFS SAH-pool adapter, not a second engine. The build is single-threaded and does not require SharedArrayBuffer. Safety traps become worker failures.

Build and test

Use Zig 0.16.0, Bun, and the pinned Emscripten version in emscripten-version.txt (6.0.9). Install development dependencies with bun install in bindings/wasm. From the repository root:

sh bindings/wasm/tools/build-package.sh /absolute/external/build-output
cd /absolute/external/build-output/package
npm pack --ignore-scripts

Build products and caches default to the chosen output directory. Existing EM_CACHE can be reused. The build downloads checksum-pinned SQLite adapter sources matching the bundled SQLite, or uses ZOVA_SQLITE_WASM_SOURCE when set. Its failed-initialization cleanup is patched to release handles without deleting the pool's stored files; the build rejects an unexpected upstream cleanup shape. Validate the tarball from the repository root:

bun bindings/wasm/tools/check-package.mjs /absolute/path/zova-wasm-1.1.0.tgz
bun test bindings/wasm/tests/api.test.ts bindings/wasm/tests/channel.test.mjs

Test the actual packed and installed artifact with Playwright driving Helium:

HELIUM_EXECUTABLE=/Applications/Helium.app/Contents/MacOS/Helium \
  sh bindings/wasm/tools/check-browser-package.sh /absolute/external/build-output

Local runs require an explicit Helium executable and use a temporary profile under the build output directory. They do not download Playwright's browser. Linux CI installs Playwright's bundled Chromium, builds once, and tests the installed npm tarball before uploading it. Playwright is pinned in the development lockfile. Native bindings remain independently tested.

Release publication requires a successful Release Artifacts run at the exact release commit, including the WASM browser job. WASM publication uses trusted publishing with npm's next tag for prereleases (such as 1.0.0-rc.3) and latest for stable versions (such as 1.1.0). The API's experimental status is independent of the package version or dist-tag. Publication remains independent of zova-js.

Both paths publish the exact versioned WASM tarball from the verified artifact run. A retry skips an already-published version only when its registry SHA-512 integrity matches that tarball; a mismatch or registry lookup error stops publication. Successful retries leave existing dist-tags unchanged, so rerunning an older release cannot move a tag backwards. No artifacts are overwritten. Before the first registry release, the npm package must exist and its trusted publisher must name this repository, publish-release.yml, and the release environment. This change does not publish the package or bump the repository version.

License

MIT; see LICENSE.