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

@nickstrad/prototyper

v0.1.0

Published

SQLite-only browser prototyping with Effect, a terminal, and portable TypeScript contracts

Readme

@nickstrad/prototyper

An ESM-only toolkit for SQLite browser prototypes, with first-class TypeScript types, an Effect database service, a reusable React database editor, an application terminal, a Fetch API explorer, and native SQLite for Node and Deno. A generator creates standalone CLI/API/web/combined prototypes. The source repository includes a root workbench and four examples. Package publication is not part of this repository workflow.

Browser SQLite and editor

During your build, copy the self-hosted assets into a static public directory:

import { copySqliteAssets } from "@nickstrad/prototyper/assets";
await copySqliteAssets("public/sqlite");

Create one session for your prototype, then share it with the application and editor. React 19.3+ is required when using the editor:

import { Effect } from "effect";
import { createBrowserSqliteSession } from "@nickstrad/prototyper/sqlite";
import { DatabaseEditor } from "@nickstrad/prototyper/database-editor";
import "@nickstrad/prototyper/database-editor/style.css";

const session = await createBrowserSqliteSession({
  assetBaseUrl: new URL("sqlite/", document.baseURI),
  persistence: "memory",
  schema: "CREATE TABLE notes(id INTEGER PRIMARY KEY, title TEXT NOT NULL);",
  seed: "INSERT INTO notes(title) VALUES ('First note');",
});
console.log(
  await Effect.runPromise(session.database.execute("SELECT * FROM notes")),
);
// Render inside your React application:
<DatabaseEditor session={session} />;
// When the prototype closes, unmount the UI and await session.dispose().

For cancellable startup, pass signal: controller.signal to createBrowserSqliteSession. Abort it if the host closes before the promise resolves. After successful acquisition, the signal no longer controls the session; call session.dispose() when closing. This startup cancellation does not enable interruption of a running SQLite query.

The editor runs the actual upstream SQLite WASM shell. .help, .tables, .schema, .mode table, .mode csv, .mode json and .headers use SQLite's own interpreter and output. Application queries and console commands share one live connection, including uncommitted transaction state. .open and .connection are blocked because they would replace that connection.

Use the correct asset base URL below a hosting subdirectory. Serve all four runtime files together. No runtime CDN or application server is needed. The service defaults to memory. Request opfs-sahpool with a stable storageKey to persist across reloads without special headers. A second tab using that key or unavailable OPFS falls back to memory with a visible reason; inspect session.database.persistence.actual. Reset restores the initial schema/seed, and import retains persistence. Export/import reject open transactions. Cancellation of a running query is unavailable. The /sqlite/transfer entry provides exportSqlite(database), importSqlite(database, bytes) and validateSqliteImage(bytes) as typed Effects. Transfer errors distinguish validation failures, preserved data, and unavailable recovery. The editor uses these helpers and keeps the shared connection alive after import.

session.database exposes typed Effect operations and a change stream. session.subscribeChanges(listener) synchronously registers an observer and returns an unsubscribe function. Notifications include partial-write failures and rollback invalidation; observer exceptions do not fail committed writes. Scoped Effect acquisition and layers are exported alongside the session factory. For direct worker access, /sqlite/worker still exports createSqliteWorker.

Node and Deno SQLite

Native code is a separate entry point and is never bundled into the browser:

import { Effect } from "effect";
import { createNativeSqliteSession } from "@nickstrad/prototyper/sqlite/native";
const session = await createNativeSqliteSession({
  schema: "CREATE TABLE notes(title TEXT);",
  seed: "INSERT INTO notes VALUES ('Native SQLite');",
});
try {
  console.log(
    await Effect.runPromise(session.database.execute("SELECT * FROM notes")),
  );
} finally {
  await session.dispose();
}

This uses node:sqlite in Node 22.18+ or Node 24, and Deno 2.7.14. The current factory defaults to memory; pass filename: "./prototype.sqlite" for persistent local storage. Existing files retain their data; reset restores the original schema/seed and persists. Both adapters enable foreign keys before seeding and resetting. On Deno, result rows are materialized to preserve correct SQL errors before applying maxRows; the limit bounds returned rows, not transient allocation. /node and /deno export createNativeHost, runNativeCli, and their respective serveNode/serveDeno HTTP listener. One host shares its application and database across CLI and Fetch requests. Close the listener before disposing the host. The browser editor requires a browser session with the upstream shell.

Application terminal

import {
  createDemoCommands,
  createShell,
} from "@nickstrad/prototyper/terminal";
const shell = createShell(createDemoCommands());
console.log(await shell.exec('hello "Ada Lovelace" --json | jq -r .greeting'));

attachShell(terminal, shell) connects an existing xterm instance; dispose the binding when unmounting. Load @xterm/xterm/css/xterm.css in your application. Portable contracts are exported from @nickstrad/prototyper. JavaScript entry points have declarations and package-local source maps. There is no CommonJS build. Styles are an explicit CSS subpath.

MIT for toolkit code. SQLite and Emscripten provenance and notices are included in assets/sqlite/ and remain applicable to their vendored files.

Fetch API explorer

The portable /api entry exports ApiHandler and ApiRoute types; the React explorer and its props type are exported from /api-explorer. Supply your own Fetch handler and keep its identity stable while the prototype is open:

import type { ApiHandler, ApiRoute } from "@nickstrad/prototyper/api";
import { ApiExplorer } from "@nickstrad/prototyper/api-explorer";
import "@nickstrad/prototyper/api-explorer/style.css";

const handler: ApiHandler = async (_request) =>
  new Response(JSON.stringify({ message: "Hello" }), {
    headers: { "Content-Type": "application/json" },
  });
const routes: readonly ApiRoute[] = [
  { method: "GET", path: "/hello", description: "Say hello" },
];
<ApiExplorer handler={handler} routes={routes} />;

The explorer constructs a Request and calls the handler directly. It displays status, headers and body; it does not start a server or call fetch. It aborts obsolete requests on edit/replacement/unmount. A handler must honor its request signal if it performs cancellable work. The host retains ownership of the application runtime and database.

createDatabaseCommands(database, runtime) from /terminal registers structured db tables, db schema [table], db query <sql> [--json], db reset and db seed commands for the supplied service. The runner provides runPromise (e.g. an Effect ManagedRuntime). Reset and seed both restore original seeds. encodeCell from the package root preserves unsafe integers as { $type: "bigint", value: "..." } and blobs as { $type: "blob", base64: "..." } when encoding query results for JSON.

Generate a prototype

Once the package is available, run:

npx --package @nickstrad/[email protected] prototyper-new my-idea --type combined
cd my-idea
npm install
npm run dev

Choose cli, api, web, or combined; every type includes DatabaseEditor. --directory selects a parent directory. Existing destinations are never overwritten. The /generator entry exports generatePrototype(name, options) and typed options. For local tarball development, pass its absolute path as packageSpecifier to the API before installing the generated project.

Scaffolds include ESM TypeScript, npm/Deno tasks, Vite asset copying, deterministic task data and one shared runtime. BASE_PATH=/nested/ npm run build supports a static subdirectory. Edit src/prototype-config.ts to request persistent browser storage; the prototype name is its storage key.