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

@gadgetinc/substrate

v0.1.0-rc.9

Published

Browser-native JavaScript runtime with Node.js compatibility, powered by a Rust/WASM kernel.

Readme

Substrate

Browser-native JavaScript runtime with Node.js compatibility, powered by a Rust/WASM kernel.

Substrate runs Node-style code entirely in the browser. It provides a virtual filesystem, process model, and 39 Node.js builtin modules so that tools like Vite, npm, and pnpm work without a server.

Install

pnpm add @gadgetinc/substrate

Quick Start

import { bootSubstrate } from "@gadgetinc/substrate";

const substrate = await bootSubstrate();

await substrate.mount({
  "server.js": {
    file: {
      contents: `
        const http = require("http");
        http.createServer((req, res) => {
          res.writeHead(200, { "Content-Type": "text/plain" });
          res.end("Hello from Substrate!");
        }).listen(8080);
      `,
    },
  },
});

const proc = substrate.spawn("node", ["server.js"]);
await substrate.waitForPort(8080);

const response = await fetch("http://localhost:8080/");
console.log(await response.text()); // "Hello from Substrate!"

Boot Configuration

bootSubstrate accepts an optional config object:

const substrate = await bootSubstrate({
  // Multi-tab coordination — all tabs with the same name share a kernel
  name: "my-app",

  // Initial working directory (default: "/")
  cwd: "/project",

  // Environment variables
  env: { NODE_ENV: "development" },

  // Package managers to install (npm enabled by default, others opt-in)
  binaries: {
    pnpm: true,
    yarn: true,
  },

  // CORS proxy for external requests
  proxyUrl: "https://cors-proxy.example.com",
  neverProxy: ["registry.npmjs.org"],

  // Persist the virtual filesystem across page reloads via OPFS
  persistence: { storeName: "my-project" },

  // Runtime logging
  logging: { level: "info" },

  // Callbacks
  onConsole: (level, args) => console.log(`[${level}]`, ...args),
  onPortListen: (port) => console.log(`Server listening on :${port}`),
  onPortClose: (port) => console.log(`Server closed on :${port}`),
});

Mounting Files

mount() writes a file tree to the virtual filesystem. You can mount source code, node_modules, config files — anything the guest process needs.

await substrate.mount({
  "package.json": {
    file: { contents: JSON.stringify({ name: "my-app", type: "module" }) },
  },
  src: {
    directory: {
      "index.js": {
        file: { contents: `console.log("hello")` },
      },
    },
  },
});

Mount accepts an optional second argument for the mount point (default "/"):

await substrate.mount(tree, "/project");

Spawning Processes

const proc = substrate.spawn("node", ["src/index.js"], {
  cwd: "/project",
  env: { DEBUG: "true" },
});

// Stream stdout/stderr
const reader = proc.stdout.getReader();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log(value);
}

// Wait for exit
const exitCode = await proc.exit;

// Or kill the process
proc.kill();

Package manager commands work the same way:

const install = substrate.spawn("pnpm", ["install"]);
await install.exit;

const dev = substrate.spawn("pnpm", ["run", "dev"]);
await substrate.waitForPort(5173);

Virtual Networking

Servers started inside Substrate are accessible via fetch() from the host page. The Service Worker intercepts requests to localhost and routes them to the virtual kernel.

substrate.spawn("node", ["server.js"]); // Listens on :3000
await substrate.waitForPort(3000);

// fetch() just works — the Service Worker handles routing
const res = await fetch("http://localhost:3000/api/data");

WebSocket connections are also routed through the virtual network automatically:

const ws = new WebSocket("ws://localhost:3000");
ws.onopen = () => ws.send("hello");
ws.onmessage = (e) => console.log(e.data);

Base Path Routing

For embedding in iframes or serving from a subpath:

const substrate = await bootSubstrate({
  basePath: "/__preview__",
  basePathPort: 5173,
});

// Navigating to /__preview__/ routes to the virtual server on :5173

Filesystem Operations

Read and write files directly without going through a process:

await substrate.writeFile("/project/config.json", JSON.stringify(config));
const contents = await substrate.readFile("/project/config.json");

Persistence

Persist the virtual filesystem to OPFS so it survives page reloads:

// Enable at boot
const substrate = await bootSubstrate({
  persistence: { storeName: "my-project" },
});

// ... mount files, run processes ...

// Save current state
await substrate.persist();

// On next page load, the VFS is restored automatically from the store

You can also manage stores after boot:

await substrate.mountStore("another-project");
const stores = await substrate.listStores();
await substrate.deleteStore("old-project");

WASM Package Interception

When native Node.js addons are detected during mount(), Substrate automatically fetches WASM alternatives from npm. Built-in support is included for rollup, esbuild, and rolldown.

You can register additional mappings:

const substrate = await bootSubstrate({
  wasmPackages: [
    {
      packageName: "my-native-tool",
      wasmPackage: "my-native-tool-wasm",
      nativeBindingPatterns: ["^@my-native-tool/binding-"],
      wasmExportPath: "dist/index.js",
    },
  ],
});

Multi-Tab Coordination

When multiple tabs boot Substrate with the same name, one tab becomes the leader (owns the kernel) and others become followers. Leadership changes are communicated via events:

const substrate = await bootSubstrate({ name: "my-app" });

console.log(substrate.isLeader); // true or false

substrate.on("leadership", (isLeader) => {
  console.log(isLeader ? "Became leader" : "Lost leadership");
});

Limitations

See docs/limitations.md for a full list. Key points:

  • Native Node.js addons (.node files) don't work unless a WASM alternative is available
  • crypto supports hashing and HMAC but not ciphers, signing, or key exchange
  • dns, tls, http2 are stubbed — the browser handles these natively
  • Brotli compression is not supported (deflate/gzip work)
  • Integration tests run against Chromium; Firefox and Safari are untested

Development

Requires Nix (via direnv) for the development environment.

pnpm build              # Build kernel + JS bundles
pnpm test               # Unit tests (vitest)
pnpm test:integration   # Integration tests (Playwright)
pnpm typecheck          # Type check (tsgo)
pnpm lint               # Lint (oxlint + oxfmt)
pnpm dev                # Dev server (Vite playground)

Repository structure

crates/kernel/          — Rust WASM kernel (filesystem, process table, syscalls)
packages/substrate/     — TypeScript runtime, node compat, boot API
packages/playground/    — Dev playgrounds (Vite, Express, Fastify, React Router)
scripts/                — Build and release tooling
docs/architecture/      — Architecture deep dives