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

@wasmer/sdk2

v0.1.3

Published

A portable, package-first sandbox SDK powered by Wasmer

Readme

@wasmer/sdk2

Run Wasmer packages in Node.js or the browser with one package-first sandbox API. The runtime is Wasmer + WASIX compiled to WebAssembly with wasm-bindgen; Node.js does not load a native addon.

@wasmer/sdk2 is the temporary package name for the next Wasmer JavaScript SDK.

Install

npm install @wasmer/sdk2

Run Python from Node.js

import { Wasmer } from "@wasmer/sdk2/node";

const wasmer = new Wasmer();
const sandbox = await wasmer.sandboxes.create({
  packages: ["python/python@=3.13.5"],
  files: {
    "main.py": "print(sum(number * number for number in range(10)))",
  },
});

const output = await sandbox
  .command("python", ["/workspace/main.py"])
  .run();

console.log(output.text());

new Wasmer() is synchronous. Package downloads, sandbox creation, commands, and shutdown are asynchronous.

run() throws ProcessExitError for a non-zero exit, termination, or timeout by default. Pass { check: false } when the outcome is expected and should be inspected as an Output.

For live processes, use spawn() and iterate the SDK streams directly:

const process = await sandbox
  .command("python", ["-u", "-c", "print('ready')"])
  .spawn({ stdin: "pipe", stdout: "pipe", stderr: "capture" });

for await (const line of process.stdout.lines()) {
  console.log(line);
}

const result = await process.wait({ check: true });

Unlike run(), process.wait() is unchecked by default.

For an interactive shell or REPL, attach one terminal to the entire process tree. Child packages inherit its TTY state and live streams:

const bash = await sandbox
  .command("bash", ["-i"])
  .spawn({ terminal: { columns: 100, rows: 30 } });

if (!bash.stdin || !bash.stdout || !bash.stderr) throw new Error("no terminal");
terminal.onData((data) => void bash.stdin.write(data));
terminal.onResize(({ cols, rows }) => bash.resizeTerminal(cols, rows));
const pump = async (stream) => {
  for await (const chunk of stream) terminal.write(chunk);
};
void pump(bash.stdout);
void pump(bash.stderr);

Call sandbox.close() and wasmer.close() when a long-lived application no longer needs them.

Node.js networking and caching

Use network: { mode: "host" } when a package needs TCP or DNS:

const sandbox = await wasmer.sandboxes.create({
  packages: ["wasmer/[email protected]"],
  network: { mode: "host" },
});

Node.js maps WASIX networking to node:net and node:dns. Its default .wasmer cache uses the same registry and package layout as the Rust and Python SDKs:

const wasmer = new Wasmer({
  cache: { directory: ".cache/wasmer" },
});

Compiled WebAssembly engine artifacts are intentionally not shared with native targets.

Browser

Import the browser entrypoint:

import { Wasmer } from "@wasmer/sdk2/browser";

const httpHost = "https://http.example.com";
const wasmer = new Wasmer();
const sandbox = await wasmer.sandboxes.create({
  packages: ["python/python@=3.13.5"],
});
const output = await sandbox
  .command("python", ["-c", "print('Hello from the browser')"])
  .run();

Worker-backed WASIX execution requires a cross-origin-isolated page so the browser can use SharedArrayBuffer. Serve the page with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.

Browser package data is persisted with browser storage rather than the Node filesystem cache.

Outbound networking through WISP

Browsers cannot open TCP sockets directly. Give a browser sandbox a WISP endpoint to multiplex its WASIX TCP and DNS traffic over one WebSocket:

import { Wasmer } from "@wasmer/sdk2/browser";

const wasmer = new Wasmer();
const sandbox = await wasmer.sandboxes.create({
  packages: ["wasmer/[email protected]"],
  network: { mode: "wisp", url: "wss://proxy.example/wisp/" },
});

const output = await sandbox
  .command("pnpm", ["add", "[email protected]", "--ignore-scripts"])
  .run();
console.log(output.text());

The SDK opens the WISP connection lazily when the guest first requests DNS or outbound TCP access. Browser applications can also supply or replace the endpoint at that point:

const sandbox = await wasmer.sandboxes.create({
  packages: ["curl/curl"],
  network: {
    mode: "wisp",
    requestUrl: async ({ url, error }) => {
      // Show application UI here. `url` and `error` are set after a failed
      // configured endpoint; return the WebSocket URL selected by the user.
      return await requestWispUrlFromUser({ url, error });
    },
  },
});

Concurrent guest connections share the same pending endpoint request. The SDK owns one WISP connection per sandbox and routes networking from every WASIX worker through it. Use a trusted, access-controlled proxy: it can observe connection metadata and decide which destinations and ports are allowed.

Change an active sandbox's endpoint without recreating its filesystem or process environment:

sandbox.network.setWispUrl("wss://another-proxy.example/");

Existing WISP streams are closed. The replacement connection is opened lazily on the next outbound network operation.

Run a web server in an iframe

Browser sandboxes expose one HTTP listener at the root of a dedicated static origin. That origin needs two small SDK entrypoints. Serve the worker as /wasmer-service-worker.js:

import "@wasmer/sdk2/service-worker";

Serve a control document at /.wasmer/host.html which imports:

import "@wasmer/sdk2/service-worker-host";

The application itself stays on a different origin. Start the guest server, then pass the static HTTP-host origin to ports.expose():

import { Wasmer } from "@wasmer/sdk2/browser";

const wasmer = new Wasmer();
const sandbox = await wasmer.sandboxes.create({
  packages: ["php/[email protected]"],
  network: { mode: "http" },
  files: { "index.php": "<h1><?php echo 'Hello from PHP'; ?></h1>" },
});

const php = await sandbox
  .command("php", ["-S", "0.0.0.0:8080", "-t", "/workspace"])
  .spawn({ stdout: "capture", stderr: "capture" });

const server = await sandbox.ports.expose(8080, {
  serviceWorker: httpHost,
});
document.body.append(server.createIframe({ title: "PHP preview" }));

Shell-style applications can discover ports instead of knowing them in advance:

const stopWatching = sandbox.ports.onListen((port) => {
  void sandbox.ports
    .expose(port, { serviceWorker: httpHost })
    .then((server) => document.body.append(server.createIframe()));
}, {
  onClose: (port) => console.log(`server on ${port} closed`),
});

server.url is the HTTP-host origin root. Requests are forwarded with their original path and body; the SDK does not rewrite HTML or add a path prefix. One service worker exposes exactly one guest server. A second call using the same origin fails until the first BrowserServer is closed. To serve guests concurrently, assign each one its own origin (for example with wildcard subdomains).

See wasmer-sh/service-worker in this repository for a complete static Vite build. The relay transfers the route directly to its local service worker; the Wasmer runtime remains in the application page.

network: { mode: "wisp", ... } includes this HTTP-listener support, so one sandbox can serve a browser preview while making outbound connections.

The worker needs scope / to route absolute guest URLs. The SDK-generated iframe uses the capabilities required for scripts and service worker control, but an iframe sandbox is not an origin boundary when both allow-scripts and allow-same-origin are enabled.

Examples

Run these from the repository root after npm run build in js/:

node js/examples/python.mjs
node js/examples/multiple_runtimes.mjs
node js/examples/edgejs_http.mjs
node js/examples/postgres_psql.mjs

The complete browser PHP preview is in examples/browser_php.

The PostgreSQL example requires psql on PATH; set PSQL or pass its path as the first argument otherwise. All examples reuse the programs in ../fixtures/.

Build and test locally

Install Node.js 20 or newer, Rust nightly with rust-src, and the matching wasm-bindgen CLI. npm ci installs the pinned Binaryen toolchain used to run wasm-opt -Oz as part of every JavaScript build:

rustup toolchain install nightly \
  --profile minimal \
  --component rust-src \
  --target wasm32-unknown-unknown
cargo +nightly install wasm-bindgen-cli --version 0.2.126 --locked

cd js
npm ci
npm run build
npm test

Run the network and browser regressions explicitly:

npm run test:edgejs
npm run test:postgres
npx playwright install chromium
npm run test:browser
npm run test:browser-http

npm run check type-checks the handwritten TypeScript API without rebuilding the wasm module.