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

web-sqlite-js

v1.1.2

Published

web-sqlite-js is a friendly, out-of-the-box SQLite database for the web that makes persistent client-side storage simple for every developer.

Readme

web-sqlite-js is a friendly, out-of-the-box SQLite database for the web that makes persistent client-side storage simple for every developer.

Designed to be truly effortless, it allows you to get a high-performance relational database running in the browser in seconds. Just install, set your HTTP headers, and start querying—no complex infrastructure required.

Table of contents

Features

  • Persistent Storage: Uses OPFS for high-performance, persistent file storage.
  • Non-Blocking: Runs in a Web Worker, keeping your UI responsive.
  • Concurrency Safe: Built-in mutex ensures safe, sequential execution of commands.
  • Type-Safe: Written in TypeScript with full type definitions.
  • Transactions: Supports atomic transactions with automatic rollback on error.

Quick start

Pick the path that fits your setup:

Option A: npm / bundler

# npm
npm install web-sqlite-js
import openDB from "web-sqlite-js";
// ...

Option B: CDN / script tag (no build step)

For quick demos or plain HTML pages you can load the prebuilt module directly:

<script type="module">
    import openDB from "https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js";
    // ...
</script>

See samples/cdn.html for a copy/paste page you can serve .

Heads up: SharedArrayBuffer requires COOP/COEP headers; see the section below.

Setup http headers

Pick your stack below to set the headers:

This library depends on SharedArrayBuffer for high performance, which requires your server to send the following HTTP headers:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Update your vite.config.ts:

import { defineConfig } from "vite";

export default defineConfig({
    server: {
        headers: {
            "Cross-Origin-Opener-Policy": "same-origin",
            "Cross-Origin-Embedder-Policy": "require-corp",
        },
    },
    preview: {
        headers: {
            "Cross-Origin-Opener-Policy": "same-origin",
            "Cross-Origin-Embedder-Policy": "require-corp",
        },
    },
});

Update your next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
    async headers() {
        return [
            {
                source: "/(.*)",
                headers: [
                    {
                        key: "Cross-Origin-Opener-Policy",
                        value: "same-origin",
                    },
                    {
                        key: "Cross-Origin-Embedder-Policy",
                        value: "require-corp",
                    },
                ],
            },
        ];
    },
};

module.exports = nextConfig;

Update your webpack.config.js:

module.exports = {
    // ...
    devServer: {
        headers: {
            "Cross-Origin-Opener-Policy": "same-origin",
            "Cross-Origin-Embedder-Policy": "require-corp",
        },
    },
};

Add the headers to your server block:

server {
    # ...
    add_header Cross-Origin-Opener-Policy "same-origin";
    add_header Cross-Origin-Embedder-Policy "require-corp";
    # ...
}

Use a middleware:

const express = require("express");
const app = express();

app.use((req, res, next) => {
    res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
    res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
    next();
});

// ...

Most modern React/Vue setups use Vite. Please refer to the Vite section above.

If you are using an older webpack-based setup (like CRA react-scripts), you technically need to configure the underlying webpack-dev-server, but CRA doesn't expose this easily without ejecting or using tools like craco or react-app-rewired to modify the dev server configuration as shown in the Webpack section.

Usage

Basic Usage

// 1. Open the database (creates 'my-database.sqlite3' in OPFS)
const db = await openDB("local.sqlite3");

// 2. Initialize schema
await db.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT,
    email TEXT
  );
`);

// 3. Insert data (Parameterized)
await db.exec("INSERT INTO users (name, email) VALUES (?, ?)", [
    "Alice",
    "[email protected]",
]);
await db.exec("INSERT INTO users (name, email) VALUES ($name, $email)", {
    $name: "Bob",
    $email: "[email protected]",
});

// 4. Query data

const users = await db.query("SELECT * FROM users");
console.log(users);
// Output: [{ id: 1, name: 'Alice', ... }, { id: 2, name: 'Bob', ... }]

// 5. Close when done
await db.close();

Debug mode

Add { debug: true } when opening the database to stream worker-side SQL logs (including bind values and timings) to your browser's console.debug. This is useful for profiling and verifying queries during development.

const db = await openDB("local.sqlite3", { debug: true });

await db.exec("CREATE TABLE IF NOT EXISTS notes (body TEXT)");
await db.query("SELECT * FROM notes WHERE id = ?", [1]);

The console output highlights SQL keywords and shows how long each statement took (click to preview):

Debug console output

Transactions

Transactions are atomic. If any command inside the callback fails, the entire transaction is rolled back.

await db.transaction(async (tx) => {
    await tx.exec("INSERT INTO users (name) VALUES (?)", ["Charlie"]);

    // You can perform multiple operations safely
    await tx.exec("INSERT INTO logs (action) VALUES (?)", ["User Created"]);

    // If you throw an error here, both INSERTs will be rolled back!
    // throw new Error('Something went wrong');
});

Star History