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

nacelle

v1.0.6

Published

Self-contained Node.js execution engine and virtual runtime in the browser via WebAssembly, Web Workers, and Virtual Filesystem

Downloads

1,228

Readme

nacelle

Self-contained Node.js execution engine and virtual runtime in the browser.

Powered by WebAssembly, Web Workers, Virtual Filesystems (VFS), and in-browser networking, nacelle lets you run Node.js code, standard libraries, and npm packages directly inside client browsers with zero server execution.


Node 22 Alpha

The alpha ships one target: Node 22, referenced against native Node 22.23.2 (module ABI 127, Node-API 10). latest, lts, n22, and the nacelle/v22 subpath all select that same runtime. Other majors and the current alias are rejected instead of silently falling back.

Installation & Release Channels

Install nacelle using your preferred package manager and release channel:

# Install the Node 22 major channel
npm install nacelle@n22

# Or install the latest default release
npm install nacelle@latest

CDN / Direct Browser Import (ESM)

You can import nacelle directly in browser scripts via modern CDNs:

<script type="module">
  import { Nacelle } from 'https://esm.sh/nacelle@n22';

  const node = await Nacelle.create({
    files: {
      '/app/index.js': `console.log('Hello from Node ' + process.version + ' in your browser!');`
    }
  });

  const proc = await node.run({ entry: '/app/index.js' });
  console.log(await proc.stdoutText());

  // Run shell lines directly in the virtual filesystem
  const shell = await node.bash('NODE_ENV=production echo "$NODE_ENV" | sed \'s/production/ready/\'');
  console.log(await shell.stdoutText());
</script>

Quick Start

1. High-Level API (Nacelle)

import { Nacelle } from 'nacelle';

// Initialize an in-browser Node engine instance
const node = await Nacelle.create({
  cwd: '/workspace',
  env: { NODE_ENV: 'development' },
  files: {
    '/workspace/server.js': `
      const http = require('http');
      const server = http.createServer((req, res) => {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ message: 'Hello from in-browser HTTP server!' }));
      });
      server.listen(3000, () => {
        console.log('Server listening on http://localhost:3000');
      });
    `
  }
});

// Run a script in an isolated Web Worker child process (browser runtimes)
const proc = await node.run({ entry: '/workspace/server.js' });

// Read stdout
console.log(await proc.stdoutText());

// File System (VFS) Operations
await node.fs.writeFile('/workspace/data.txt', 'Hello VFS');
const content = await node.fs.readFile('/workspace/data.txt', 'utf8');

// In-Browser NPM package installation
await node.npm.install('express');

Features

  • 🌐 60+ Node Built-in Modules: node:fs, node:http, node:http2, node:tls, node:crypto, node:buffer, node:stream, node:events, node:path, node:process, node:child_process, node:worker_threads, node:zlib, node:sqlite, node:net, node:dgram, node:dns, node:test, node:vm, node:v8, node:os, node:assert, and more.
  • WebAssembly Artifact Loader: Integrity-checked, export-validated low-level sqlite, zlib, and standard Node-API bridge artifacts. A listed artifact is not described as a working Node binding until a subsystem parity test wires and verifies it.
  • 📁 Virtual POSIX Filesystem (VFS): Isolated in-memory filesystem with synchronous and asynchronous operations, streams, and file descriptors.
  • 🧵 Web Worker Isolation: Optional true multi-threaded process execution via Web Workers with IPC; browser Nacelle+ requests fail closed when the boundary is unavailable.
  • 📦 In-Browser NPM Installer: Direct npm package resolution, tarball downloading, and untarring right inside the browser VFS.
  • 🔒 Run-Scoped Capability Policy: Immutable grants for VFS, workers, network, npm, preview, persistence, host bridges, and named secrets, with auditable grant deltas.
  • 🧰 Bounded Process Output: Streaming callbacks plus byte limits, tail retention, and dropped-byte accounting for untrusted workloads.
  • Content-Addressed Checkpoints: Workspace snapshots with metadata, diffs, rollback, and deterministic content identities.
  • 🔎 Structured Failure Traces: Stable error codes, bounded event history, and secret-redacted run diagnostics.
  • 🔑 Named Secret Broker: Origin-bound request signatures without exposing raw secret material to guest code.
  • 🔒 Security & Isolation: Strict capability boundary architecture with zero server-side dependencies.

Subpath Exports

  • nacelle -> Main bundle (Nacelle, createRuntime, runtime)
  • nacelle/v22 -> Explicit Node v22 runtime entry
  • nacelle/latest / nacelle/lts -> Alpha aliases for the v22 entry
  • nacelle/runtime -> Low-level runtime assembly and module loader
  • nacelle/worker -> Dedicated Web Worker process script
  • nacelle/sw -> Virtual network gateway Service Worker
  • nacelle/wasm/* -> Integrity-checked WebAssembly artifacts listed by the selected release manifest
  • nacelle/support / nacelle/version -> Shipped aliases, profile hashes, and WASM artifact-set identity

WASM adapters load lazily from the selected v22 manifest. A custom CDN or application path can be supplied with wasmBaseUrl:

const node = await Nacelle.create({
  wasmBaseUrl: new URL('/nacelle/v22/wasm/', location.href).href,
});
const artifact = await node.wasm.load('node_addon_napi');
console.log(artifact.path, artifact.bytes);

Examples & Demos

Try out the interactive in-browser examples included in examples/:

# Start local examples server (Express in-browser IDE)
npm run examples

# Test the CDN-origin direct iframe flow with a local esm.sh simulation
npm run examples:cdn

# Run Vite + React in-browser IDE example
npm run examples:vite-react

# Inspect the shipped WASM artifacts and export contracts
npm run check:wasm

# Build and inspect every published package export
npm run verify:package

# Run Native WASM Addons example
npm run examples:wasm

# Try the inline bash compatibility demo
npm run examples:bash

# Try the TypeScript strip-and-run demo
npm run examples:typescript

# Try the browser proxy configuration demo
npm run examples:proxy

# Run the native Node-compatible proxy example without a browser
npm run examples:proxy-native

Each example page includes a navigation menu (☰) at the top to easily switch between examples.

Optional Nacelle+ transport

For APIs that reject ordinary browser requests because of CORS, the optional nacelle-plus/extension companion provides a capability-gated HTTP transport for Chrome and Firefox. Nacelle remains the only runtime: native page fetch is attempted first only for replay-safe GET and HEAD requests. Unsafe methods are sent to the privileged adapter before any page fetch, because a browser failure does not prove that a non-idempotent request was not sent. The Nacelle run must explicitly grant its proxy capability; the extension's per-origin permission is a separate check. See nacelle-plus/README.md for setup, streaming, and permission handling.

The supported Node release-line audit and upgrade policy are in docs/node-version-support.md.

Run policy and recovery

Capabilities are validated once when a runtime is created and are available as node.capabilities. Network methods are selected before a request is issued: GET and HEAD may try the page fetch before a privileged fallback, while unsafe methods go directly to the privileged adapter. Process output is bounded by the run manifest; handle.stats('stdout') reports retained and dropped bytes. Checkpoints are exposed through node.checkpoint(), node.diff(), and node.rollback(). Secrets are available through node.secretBroker only as named, origin-bound signatures.

Alpha build and release commands

npm run versions
npm run build
npm run validate:versions
npm run check:wasm
npm run verify:package
npm run parity
npm run test:full

The release gate covers the JavaScript runtime, builds, artifacts, and browser workloads. Python harness tests run separately with npm run test:python.

npm run publish:n22 -- --dry-run exercises the release path without uploading. The live command publishes the major-scoped n22 tag first and promotes latest and lts only after the explicit release gate.

Inline shell execution

node.bash(command, options) runs the supported POSIX shell subset against the virtual filesystem and returns a normal ProcessHandle. It supports npm-style command lists, environment assignments, PATH lookup, pipes, redirects, globbing, and common commands including mv, cp, ls, ps, grep, cat, find, cut, tr, sort, uniq, tee, head, tail, wc, mkdir, rm, and touch:

const proc = await node.bash(`
  mkdir -p dist &&
  echo "built in $NODE_ENV" > dist/status.txt &&
  cat dist/status.txt
`, { env: { NODE_ENV: 'production' } });

console.log(await proc.stdoutText());

The TypeScript demo showcases three conversion pipelines from inline bash build scripts: Node.js 22 built-in module.stripTypeScriptTypes(), Vite's fast transform pipeline (vite build), and the official Microsoft TypeScript compiler (tsc), then executes the emitted JavaScript.

Run CITGM in Chromium or Firefox

The Playwright adapter preloads the pinned CITGM CLI and the candidate package's registry metadata, tarballs, and dependency graph into Nacelle's browser-side npm cache, then executes CITGM itself as a Nacelle child process. Preloading does not run candidate tests. Playwright only hosts the browser page and selects the browser engine; CITGM, the candidate package, npm commands, and package tests stay inside the browser runtime.

Install the adapter dependencies and browser binaries once:

npm --prefix dev/adapters/playwright install
npx --prefix dev/adapters/playwright playwright install chromium firefox

Generate the host-side artifact once per CITGM version, candidate package, and registry. This uses host npm only to fetch package data with lifecycle scripts disabled; it does not run the candidate. The generated cache is ignored by git and is consumed as local assets by the browser page:

npm run citgm:precache -- express

Run one registry package through either engine:

npm run citgm:browser:chromium -- express
npm run citgm:browser:firefox -- express

CITGM agent workflow and gates

Use Node 22.23.2 for every native CITGM, browser CITGM, build, and test command. Find the first unprocessed table row marked BLOCKED or GATE-BLOCKED in CITGM-TOP-100-STATUS.md, and work forward in rank order. The status table is the source of truth for the next candidate; re-check it before each run because the cursor changes after every committed CITGM result.

Prepare a Node 22 shell first. The temporary npm link matters for native CITGM: CITGM launches package-manager scripts as node <npm-path>, so a shell-wrapper npm can otherwise produce a false native failure before the package test runs.

export NODE22_HOME="$(mise where [email protected])"
export PATH="$NODE22_HOME/bin:$PATH"
export NATIVE_NPM_DIR="$(mktemp -d)"
ln -s "$NODE22_HOME/lib/node_modules/npm/bin/npm-cli.js" "$NATIVE_NPM_DIR/npm"
ln -s "$NODE22_HOME/lib/node_modules/npm/bin/npx-cli.js" "$NATIVE_NPM_DIR/npx"
export NATIVE_PATH="$NATIVE_NPM_DIR:$PATH"
node --version # v22.23.2
PATH="$NATIVE_PATH" node "$NODE22_HOME/lib/node_modules/npm/bin/npm-cli.js" \
  exec --yes [email protected] -- sh -c \
  'unset npm_config_package npm_command npm_lifecycle_event npm_lifecycle_script; exec citgm "$@"' \
  sh <package> --tmpDir /tmp
# Keep the candidate checkout under the POSIX /tmp root, matching native CITGM.
# This prevents legacy tools from resolving unrelated /node ancestor packages.
npm run citgm:browser:chromium -- <package> --tmpDir /tmp
npm run citgm:browser:firefox -- <package> --tmpDir /tmp

The full repository Playwright suites are separate from the two browser CITGM runs. Follow these rules exactly:

  1. Always commit the completed CITGM run's artifacts, logs, and truthful status update before moving on, whether the candidate passed, failed, or was classified as blocked. Never start another CITGM with a dirty worktree from the previous run.

  2. If the candidate passes without any source, runtime, harness, or regression test changes, the native and two browser CITGM results are the gate. Do not run the full Chromium and Firefox Playwright suites for that candidate; commit the artifacts and advance.

  3. If any source, runtime, harness, or regression-test changes are made, stop before running a different CITGM candidate. Rebuild and run the complete repository gate set first:

    npm run build:v22
    npm run check:wasm
    node --test --test-concurrency=1 \
      dev/tests/runtime/runtime/patch-regressions.mjs \
      dev/tests/runtime/runtime/patch-source-regressions.mjs \
      dev/tests/runtime/runtime/patch-messaging-regressions.mjs \
      dev/tests/runtime/runtime/patch-crypto-regressions.mjs
    npm test
    npm run test:browser:chromium
    npm run test:browser:firefox

    Only after every gate passes may the next CITGM candidate start. Commit the fix, permanent regression tests, gate logs, and CITGM artifacts before advancing. The artifact corpus is ignored by default, so force-add the authoritative run directory explicitly, for example:

    git add -f artifacts/citgm-top-100/rank-<rank>-<package>/<run-dir>
    git commit -m "record <package> CITGM run"

The full repository Playwright suites are never a substitute for the candidate CITGM: when changes are present, run them only after that candidate has passed the real upstream CITGM in both Chromium and Firefox, then use their results as the final repository gate.

Treat a browser-only failure as an ours-side runtime or harness failure until the same package, git revision, and failing path have been reproduced under native Node 22. A package may be recorded as an upstream or dependency blocker only when that external oracle supports the classification. Preserve complete logs and explain the classification in the status record. Temporary tracing, diagnostic hooks, and scratch directories must be removed before the final commit; retain only general runtime fixes and permanent regression tests.

If no matching artifact exists, the runner falls back to direct browser registry fetches. Arguments after the module are forwarded to CITGM. The runner currently targets registry packages; local-directory and native-addon cases need an explicit browser-safe fixture or capability before they can be meaningful. Use NACELLE_CITGM_VERSION to select a different CITGM release and NACELLE_CITGM_TIMEOUT_MS to change the outer browser-run timeout. Set NACELLE_NPM_REGISTRY when generating and consuming an alternate registry artifact.

Next.js 16 App Router

Nacelle runs full Next.js App Router applications entirely in-browser without host subprocesses. The Next.js demo (npm run examples:next) demonstrates Server-Side Rendering (SSR), file-system routing (app/page.tsx, app/about/page.tsx, app/dashboard/page.tsx), Server Actions & API endpoints (app/api/hello/route.ts), and Next.js CLI orchestration (next dev, next build, next start). Nacelle advertises the WebContainer runtime signal that makes stock Next.js select its own official SWC WebAssembly fallback (@next/swc-wasm-nodejs) instead of its platform .node package.

The demo's next start button builds the current sources when needed, then starts the production server. next build can also run separately. Production builds use one worker with worker threads to fit the browser's memory budget, and generated files remain available between commands.

Proxy configuration

createProxyConfig() provides one small, explicit configuration for HTTP(S) environment proxy routing. It normalizes proxy URLs, mirrors upper- and lower-case environment keys, supports NO_PROXY, and enables routing for the virtual Node process:

import { Nacelle, createProxyConfig } from 'nacelle';

const node = await Nacelle.create({
  proxy: createProxyConfig({
    httpProxy: 'http://127.0.0.1:3128',
    httpsProxy: 'http://127.0.0.1:3128',
    noProxy: ['localhost', '127.0.0.1:3000'],
  }),
});

The native example uses standard node:http createServer() and get() calls inside Nacelle. The first request is routed to the proxy, while the proxy's upstream request temporarily disables environment routing to avoid a loop.


License

MIT © Nacelle Contributors

Virtual HTTP previews: Service Worker or direct iframe

Nacelle.create({ gateway: true }) selects a gateway from the loaded module's import.meta.url and the host page's origin. Same-origin deployments use the Service Worker when available. Cross-origin imports (including esm.sh), missing Service Worker support, and failed automatic worker registration use a direct iframe/message-channel gateway. Cross-origin automatic selection does not try /runtime/gateway-sw.js first. Explicit worker failures reject creation.

// CDN / no worker asset:
const node = await Nacelle.create({ gateway: { mode: 'direct-iframe' } });
// Same-origin worker deployment:
// const node = await Nacelle.create({
//   gateway: { mode: 'service-worker', swPath: '/runtime/gateway-sw.js', scope: '/' },
// });

node.on('gateway-diagnostic', error => console.error(error.code, error.message));
console.log(node.gateway.mode, node.gateway.reason, node.gateway.policy);

// Start a virtual HTTP server before connecting.
const disconnect = node.connectIframe(document.querySelector('iframe'), {
  port: 3000,
  path: '/hello?name=Developer',
  onNavigate: cleanPath => { addressInput.value = cleanPath; },
  onError: error => { errorOutput.textContent = `${error.code}: ${error.message}`; },
});
// Additional methods on a direct connection:
await disconnect.navigate?.('/another-page');
disconnect.back?.();
disconnect();                    // idempotent; cancels requests and sockets
await node.shutdown();           // closes every connection and process
// node.reset() also closes direct connections before resetting the runtime.

gateway: false disables iframe gateway setup. The read-only node.gateway diagnostic retains up to 128 errors and exposes active sessions, clean virtual paths, request counts, Blob ownership, and the resolved policies. The existing getVirtualUrl() helper is for Service Worker navigation; do not assign its result to a direct iframe's src. Use connectIframe and its direct navigation handle instead.

Direct mode uses sandbox="allow-scripts allow-forms", an opaque origin, fresh nonces and MessagePorts, and a deny-by-default resource CSP. The parent never uses host fetch for virtual HTTP, assets or sockets, and a missing virtual listener cannot fall through to a configured Nacelle+ proxy. Request credentials, authorization and cookie headers are not forwarded. The session's preview port must be granted by the runtime's capability manifest.

Defaults are a 30-second request/navigation timeout, 16 MiB per request or response body and aggregate document assets, 32 combined HTTP/WebSocket operations, 20 redirects, and 1,024 document resources. Set requestTimeoutMs, maxBodyBytes, and maxConcurrentRequests in the gateway options to change the first three. WebSockets have a handshake timeout; open sockets count against the concurrency cap until closed, and each message is size-limited.

HTTP fetch bodies stream over the channel; XHR buffers its eventual result and HTML navigation is buffered within the limit for inert parsing and resource rewriting. Scripts, literal module dependencies, stylesheets, recursive CSS URLs/imports, images, media, links, forms and virtual history are supported. The parent owns the resource catalog; the opaque document creates local Blob mirrors because it cannot reliably fetch another origin's Blob URLs. Both sets are revoked on navigation/close. Classic scripts run in document order after staging the DOM; deferred/module scripts run afterward. This is not a complete emulation of parser-blocking HTML execution.

Nested frames, external resources, custom rewriters, import maps, computed or cyclic module imports, encoded responses, integrity metadata, srcset, escaped CSS URLs, and unsupported browser networking APIs produce structured errors. Application CSP/framing headers that cannot survive origin/resource rewriting reject navigation instead of being silently weakened. A restrictive host CSP can also block the fixed inline bootstrap; direct mode does not bypass it.

Security boundary: the opaque iframe prevents access to parent DOM/storage; it is not a general network-egress sandbox for arbitrary hostile JavaScript. Browser sandbox/CSP do not prevent every self-navigation through location. Intercepted APIs never fall back to host networking, but an unmanaged navigation is detected and closed only on load, potentially after its network request. Deploy a browser-level network restriction when complete egress isolation is required. The existing same-origin Service Worker mode has a different trust boundary and is not made opaque by this change. Neither transport changes the runtime's separate inline versus worker guest execution isolation policy.

See examples/direct-iframe-cdn.html for an esm.sh/Express example. Run npm run examples:cdn to serve that page on one origin and the built Nacelle package behind an esm.sh-shaped static origin on another. The fixture also proxies npm registry requests needed by the example's browser install. See docs/direct-iframe-gateway-validation.md for the test commands, coverage and outstanding live-environment acceptance checks. Pin a release containing this implementation before deploying the CDN example.