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

@tegmentum/wasi-polyfill

v0.1.0

Published

Multi-version WASI polyfill for browser and JavaScript environments

Downloads

95

Readme

@tegmentum/wasi-polyfill

A multi-version WASI polyfill for browsers and JavaScript runtimes, plus a suite of browser Web API host imports for WebAssembly components.

Overview

This package lets you run WebAssembly modules and components in environments that don't natively support WASI — primarily the browser, but also Node.js, Deno, Bun, and other JS runtimes.

It provides four subsystems:

| Subsystem | Purpose | Entry point | |-----------|---------|-------------| | wasip1 | Standalone WASI Preview 1 implementation for core modules | @tegmentum/wasi-polyfill/wasip1 | | wasip2 | Capability-based plugin framework for WASI Preview 2 components | @tegmentum/wasi-polyfill/wasip2 | | wasip3 | Async-native primitives (streams, futures, tasks) targeting WASI Preview 3 | @tegmentum/wasi-polyfill/wasip3 | | browser | Host imports that expose Web Platform APIs (DOM, canvas, fetch, WebGPU, …) to guests | @tegmentum/wasi-polyfill/browser |

The bare @tegmentum/wasi-polyfill import still re-exports wasip2 as a back-compat alias, but it is deprecated — prefer the explicit /wasip2 subpath. The unprefixed ./plugins/*, ./runtime, ./build, ./testing, and ./proxy subpaths are likewise aliases for their /wasip2/... equivalents.

Key features

  • Plugin architecture — modular interface implementations with multiple backends per interface
  • Capability-based security — fine-grained policy control over what guests can access
  • Zero-config defaults — safe sandboxed defaults with explicit capability grants
  • Async-native — every interface is async-capable for browser compatibility
  • Tree-shakeable — import only the subpaths you need
  • Component-model native — works with jco-transpiled components or via runtime transpilation

Installation

npm install @tegmentum/wasi-polyfill

Optional peer dependencies (install only what you use):

npm install @bytecodealliance/jco  # runtime component transpilation
npm install ws                      # WebSocket gateway / Node-side sockets
npm install sql.js                  # SQL backend for wasi:sql
npm install onnxruntime-web         # ONNX backend for wasi:nn

Quick Start (WASI Preview 2)

import { createPolyfill, createCliPolicy } from '@tegmentum/wasi-polyfill/wasip2'
import { randomPlugin } from '@tegmentum/wasi-polyfill/wasip2/plugins/random'
import { monotonicClockPlugin } from '@tegmentum/wasi-polyfill/wasip2/plugins/clocks'

const polyfill = createPolyfill({
  policy: createCliPolicy({
    env: { NODE_ENV: 'production' },
    args: ['--verbose'],
  }),
})

polyfill.registerPlugin(randomPlugin)
polyfill.registerPlugin(monotonicClockPlugin)

const { imports } = await polyfill.forInterfaces([
  'wasi:random/[email protected]',
  'wasi:clocks/[email protected]',
])

const instance = await WebAssembly.instantiate(wasmBytes, imports)

Factories:

  • createPolyfill(config?) — explicit policy, safe by default
  • createDevPolyfill() — allows every interface (development only)
  • createJcoPolyfill(config?) — jco-compatible import shape

WASI Preview 2 Plugins

Each plugin satisfies one or more wasi:* interface versions. Register the plugins you need; the policy decides which the guest is actually allowed to import.

Core

| Interface | Plugin export | Description | |-----------|---------------|-------------| | wasi:random/random | randomPlugin | Cryptographic random number generation | | wasi:clocks/monotonic-clock | monotonicClockPlugin | High-resolution monotonic time | | wasi:clocks/wall-clock | wallClockPlugin | Wall-clock time | | wasi:io/streams | streamsPlugin | Input/output streams | | wasi:io/poll | pollPlugin | Polling for I/O readiness | | wasi:io/error | errorPlugin | Stream error resource |

Import path: @tegmentum/wasi-polyfill/wasip2/plugins/{random,clocks,io}

CLI

| Interface | Plugin export | Description | |-----------|---------------|-------------| | wasi:cli/environment | environmentPlugin | Env vars, args, initial CWD | | wasi:cli/stdin / stdout / stderr | (via environmentPlugin) | Standard streams | | wasi:cli/exit | (via environmentPlugin) | Process exit |

Import path: @tegmentum/wasi-polyfill/wasip2/plugins/cli

Filesystem

| Interface | Plugin export | Description | |-----------|---------------|-------------| | wasi:filesystem/types | filesystemTypesPlugin | Files, descriptors, metadata | | wasi:filesystem/preopens | filesystemPreopensPlugin | Pre-opened directory handles |

Backends: in-memory (default), OPFS (browser persistent), IndexedDB, overlay. Import path: @tegmentum/wasi-polyfill/wasip2/plugins/filesystem

Networking

| Interface | Plugin export | Description | |-----------|---------------|-------------| | wasi:sockets/network | networkPlugin | Network resource handle | | wasi:sockets/tcp | tcpPlugin | TCP sockets | | wasi:sockets/udp | udpPlugin | UDP sockets | | wasi:sockets/ip-name-lookup | ipNameLookupPlugin | DNS resolution (incl. DoH) | | wasi:http/types | httpTypesPlugin | HTTP request/response types | | wasi:http/outgoing-handler | httpOutgoingHandlerPlugin | HTTP client (fetch) | | wasi:http/incoming-handler | httpIncomingHandlerPlugin | HTTP server (Service Worker) |

For real sockets from a browser, see WebSocket Gateway. Import paths: @tegmentum/wasi-polyfill/wasip2/plugins/{sockets,http,ws-gateway}

Storage

| Interface | Plugin export | Description | |-----------|---------------|-------------| | wasi:keyvalue/store | keyvalueStorePlugin | Key-value storage | | wasi:keyvalue/atomics | keyvalueAtomicsPlugin | Atomic KV operations | | wasi:keyvalue/batch | keyvalueBatchPlugin | Batched KV operations | | wasi:blobstore/blobstore | blobstorePlugin | Object/blob storage root | | wasi:blobstore/container | blobstoreContainerPlugin | Blob container management |

Import paths: @tegmentum/wasi-polyfill/wasip2/plugins/{keyvalue,blobstore}

Configuration & Logging

| Interface | Plugin export | Description | |-----------|---------------|-------------| | wasi:config/store | configStorePlugin | Static configuration values | | wasi:config/runtime | configRuntimePlugin | Runtime / remote configuration | | wasi:logging/logging | loggingPlugin | Structured logging (console, buffer, NDJSON, OTLP) |

Import paths: @tegmentum/wasi-polyfill/wasip2/plugins/{config,logging}

Messaging & Compute

| Interface | Plugin export | Description | |-----------|---------------|-------------| | wasi:messaging/{types,producer,consumer,handler} | messagingTypesPlugin, messagingProducerPlugin, messagingConsumerPlugin, messagingHandlerPlugin | Pub/sub message queues (draft) | | wasi:nn/{tensor,graph,inference,errors} | nnTensorPlugin, nnGraphPlugin, nnInferencePlugin, nnErrorsPlugin | Neural-network inference via WebNN / ONNX | | wasi:sql/{types,connection,query,statement,transaction} | sqlTypesPlugin, sqlConnectionPlugin, sqlQueryPlugin, sqlStatementPlugin, sqlTransactionPlugin | SQL access (in-memory engine or sql.js) | | wasi:threads | threadsPlugin | Web Worker-based threads |

Import paths: @tegmentum/wasi-polyfill/wasip2/plugins/{messaging,nn,sql,threads}

Graphics (wasi-gfx)

| Interface | Plugin export | Description | |-----------|---------------|-------------| | wasi:frame-buffer/frame-buffer | frameBufferPlugin | Software-rendered framebuffers | | wasi:graphics-context/graphics-context | graphicsContextPlugin | Graphics context binding | | wasi:surface/surface | surfacePlugin | Windowing, pointer, keyboard events | | wasi:webgpu | webgpuPlugin | WebGPU compute and rendering |

Convenience bundle: wasiGfxPlugins registers all four. Import paths: @tegmentum/wasi-polyfill/wasip2/plugins/{frame-buffer,graphics-context,surface,webgpu,wasi-gfx}


Policies

Policies decide which interfaces a polyfill instance exposes and how each one is configured.

Built-in policies

import {
  createSafePolicy,    // deny-all default; opt in explicitly
  createCliPolicy,     // allows the CLI interface set
  AllowAllPolicy,      // development only — allows everything
  DenyAllPolicy,       // denies all interfaces
  mergePolicies,
} from '@tegmentum/wasi-polyfill/wasip2'

Custom policy

import { createPolicy } from '@tegmentum/wasi-polyfill/wasip2'

const policy = createPolicy({
  defaultAllow: false,
  allow: [
    'wasi:random/[email protected]',
    'wasi:clocks/[email protected]',
  ],
  deny: [
    'wasi:filesystem/[email protected]',
  ],
  preopens: ['/app/data'],
  env: { API_KEY: 'secret' },
  args: ['--config', 'prod.json'],
})

Runtime Component Loading

RuntimeBindgen and ComponentLoader instantiate .component.wasm files directly in the browser using jco's runtime transpilation.

npm install @bytecodealliance/jco
import { createRuntimeBindgen, registerCorePlugins } from '@tegmentum/wasi-polyfill/wasip2'

await registerCorePlugins()

const bindgen = createRuntimeBindgen({ devMode: true })
const result = await bindgen.instantiate<MyExports>(wasmBytes)
// or: bindgen.instantiateFromUrl('/my-component.wasm')

result.exports.myFunction()
result.destroy()

Async imports (JSPI)

Suspending imports — wasi:io/poll (pollable.block), wasi:http, wasi:sockets, or any host-async interface — require JSPI to actually block the guest. With the default sync transpilation, those imports cannot await the polyfill's async plugins.

const bindgen = createRuntimeBindgen({
  devMode: true,
  jcoOptions: {
    asyncMode: 'jspi',
    asyncImports: ['wasi:io/[email protected]#[method]pollable.block'],
    asyncExports: ['handle'],
  },
})

const result = await bindgen.instantiate<MyExports>(wasmBytes)
const value = await result.exports.handle(input) // promising export — await it

Requires WebAssembly.Suspending / promising (Chrome 137+, Node 22+).

ComponentLoader

A lighter API when you don't need full bindgen control:

import { createComponentLoader } from '@tegmentum/wasi-polyfill/wasip2/runtime'
import { randomPlugin } from '@tegmentum/wasi-polyfill/wasip2/plugins/random'

const loader = createComponentLoader({ devMode: true })
loader.getPolyfill().registerPlugin(randomPlugin)

const component = await loader.loadFromUrl('/my-component.wasm')
component.exports.myFunction()
component.destroy()

If jco is not installed, both loaders fall back to direct instantiation (works for pre-transpiled components or core modules).


WASI Preview 1

Use wasip1 when you need to run a classic WASI Preview 1 core .wasm (e.g. a Rust binary compiled to wasm32-wasi) rather than a Preview 2 component.

import { createWasip1 } from '@tegmentum/wasi-polyfill/wasip1'

const wasi = createWasip1({
  args: ['myprog', '--verbose'],
  env: { HOME: '/home/user' },
  // preopens: { '/': filesystem },  // optional
})

const { instance } = await WebAssembly.instantiate(wasmBytes, {
  wasi_snapshot_preview1: wasi.getImports(),
})

wasi.initialize(instance)
;(instance.exports._start as () => void)()

if (wasi.exited) console.log('exit code:', wasi.exitCode)

Supported capabilities: args, env, stdio (custom streams), filesystem (preopens), clocks (monotonic / realtime / CPU), random, poll (with optional blocking).

Node host filesystem

import { createNodeFilesystem } from '@tegmentum/wasi-polyfill/wasip1/hostfs-node'

const fs = createNodeFilesystem('/path/to/sandbox-root')
const wasi = createWasip1({ preopens: { '/': fs } })

createNodeFilesystem is sandboxed to its root and guards against path-escape attacks.


WASI Preview 3 (preview)

wasip3 provides the async primitives — streams, futures, tasks — that Preview 3 will be built on. Treat the binary-level component ABI as not yet implemented; the current instantiate() is a stub that targets jco-transpiled output. The API is stable enough to write tests against.

import {
  createWasip3,
  createStream,
  createFuture,
  delay,
} from '@tegmentum/wasi-polyfill/wasip3'

const wasi = createWasip3({
  args: ['myprog'],
  env: { LOG: 'debug' },
})

await wasi.execute(async (task) => {
  const { stream, writer } = createStream<Uint8Array>()
  writer.write(new TextEncoder().encode('hello'))
  writer.close()

  for await (const chunk of stream) {
    // …
  }
})

Re-exports from canonical-abi/: createStream, streamFromAsyncIterable, streamFromReadable, collectStream, pipeStream, mergeStreams, createFuture, futureFromPromise, delay, resolvedFuture, raceFutures, allFutures, Task, SubtaskManager.


Browser Host Imports

The browser/* subpaths expose Web Platform APIs as host imports for WebAssembly components — useful when you're shipping a guest module that wants to draw to a canvas, hit fetch, listen for DOM events, or read the clipboard.

import { getBrowserImports } from '@tegmentum/wasi-polyfill/browser'

const imports = getBrowserImports({
  capabilities: ['console', 'fetch', 'canvas', 'events'],
})

const instance = await WebAssembly.instantiate(wasmBytes, imports)

getBrowserImports is capability-gated. Each capability has a dedicated subpath if you want to load it individually or lazily.

| Capability | Subpath | What it exposes | |------------|---------|-----------------| | console | /browser/console | console.log/debug/info/warn/error/trace, time/timeEnd | | fetch | /browser/fetch | fetch() plus request/response builders | | storage | /browser/storage | localStorage, IndexedDB | | performance | /browser/performance | performance.now, mark, measure | | dom | /browser/dom | Node/element handles, query, traversal, mutation | | events | /browser/events | Mouse, keyboard, touch, wheel, focus event subscriptions | | canvas | /browser/canvas | 2D context, drawing primitives, images | | clipboard | /browser/clipboard | Read/write clipboard text | | geolocation | /browser/geolocation | getCurrentPosition() | | notifications | /browser/notifications | Permission + show | | media | /browser/media | getUserMedia, MediaStream, tracks | | service-worker | /browser/service-worker | Register, get registrations | | worker | /browser/worker | Spawn Web Workers, post/read messages | | websocket | /browser/websocket | WebSocket connect/send/read/close | | broadcast-channel | /browser/broadcast-channel | Cross-tab BroadcastChannel | | animation | /browser/animation | requestAnimationFrame, requestIdleCallback | | history | /browser/history | pushState, replaceState, navigation | | screen | /browser/screen | Screen info, orientation lock | | fullscreen | /browser/fullscreen | Request / exit fullscreen | | vibration | /browser/vibration | Vibration API | | webgpu | /browser/webgpu | Adapter, device, buffer, texture, pipeline, queue, canvas | | gc-enhanced | /browser/gc-enhanced | DOM/Events optimised for the WasmGC proposal | | (runtime) | /browser/runtime | Feature detection, user gesture, capability checks | | (types) | /browser/types | Shared error/result/header/URL/event types |

Other helpers exported from @tegmentum/wasi-polyfill/browser:

  • getMinimalBrowserImports() — just types + runtime
  • getCoreBrowserImports() — console, fetch, storage, performance
  • getWebGPUImportsLazy(), getCanvasImportsLazy(), getGcEnhancedImportsLazy() — dynamic imports for heavy modules

WebSocket Gateway

For real TCP/UDP/DNS from a browser, tunnel socket operations through a WebSocket proxy server:

import {
  wsGatewayTcpPlugin,
  wsGatewayUdpPlugin,
  wsGatewayDnsPlugin,
} from '@tegmentum/wasi-polyfill/wasip2/plugins/ws-gateway'

polyfill.registerPlugin(wsGatewayTcpPlugin, {
  gatewayUrl: 'wss://gateway.example.com/tunnel',
  authToken: 'your-auth-token',
})
polyfill.registerPlugin(wsGatewayUdpPlugin, { /* same gateway */ })
polyfill.registerPlugin(wsGatewayDnsPlugin, { /* same gateway */ })

Build-Time Tooling

Vite plugin

// vite.config.ts
import { wasiPolyfillPlugin } from '@tegmentum/wasi-polyfill/wasip2/build'

export default {
  plugins: [
    wasiPolyfillPlugin({ generateManifests: true }),
  ],
}

Component introspection

import { introspect, generateManifest } from '@tegmentum/wasi-polyfill/wasip2/build'

const result = await introspect(wasmBytes)
result.imports         // required WASI interfaces
result.wasiSubsystems  // ['random', 'clocks', ...]

const manifest = await generateManifest(wasmBytes)

Deterministic Testing

import {
  createTestHarness,
  withTestHarness,
} from '@tegmentum/wasi-polyfill/wasip2/testing'

const harness = createTestHarness({
  seed: 42n,
  initialTime: new Date('2024-01-01T00:00:00Z'),
})

const { imports } = await harness.getImports([
  { package: 'wasi:random', name: 'random', version: '0.2.0' },
  { package: 'wasi:clocks', name: 'monotonic-clock', version: '0.2.0' },
])

harness.advanceTimeSeconds(60)
harness.setWallTime(new Date('2024-06-15T12:00:00Z'))

const snapshot = harness.getSnapshot()
snapshot.monotonicTime  // 60_000_000_000n
snapshot.logs           // captured log entries

harness.destroy()

Bundle presets: deterministicBundle, browserTestBundle, minimalBundle.


API Reference (wasip2 core)

class Polyfill {
  getImports(required: WasiInterface[], options?): Promise<ImportResult>
  forInterfaces(interfaces: string[], options?): Promise<ImportResult>
  forManifest(manifest: ComponentManifest, options?): Promise<ImportResult>
  forComponent(bytes: ArrayBuffer, options?): Promise<ImportResult>
  registerPlugin(plugin: WasiPlugin, config?: PluginConfig): void
  isAllowed(iface: WasiInterface | string): boolean
  hasPlugin(iface: WasiInterface | string): boolean
  destroy(): void
}

interface ImportResult {
  imports: Record<string, Record<string, unknown>>
  loaded:  WasiInterface[]
  denied:  WasiInterface[]
  missing: WasiInterface[]
}

Manifest helpers: parseManifest, loadManifest, loadManifestForComponent, createManifest, serializeManifest, validateManifest, validateExports, verifyComponentHash.

Errors: WasiError, WasiErrorCode, PluginNotFoundError, PolicyDeniedError, ImplementationNotFoundError, ManifestError.


Examples

See examples/ for runnable demos:

  • basic-usage.ts — dev/safe polyfills, string specs, WASM integration
  • advanced-usage.ts — multi-plugin setup, security policies, lifecycle
  • filesystem-usage.ts — memory, OPFS, IndexedDB, overlay filesystems
  • http-usage.ts — HTTP client, server, test handler, service worker
  • storage-usage.ts — keyvalue store, atomics, batch
  • logging-usage.ts — console, buffer, NDJSON, OTLP sinks
  • config-usage.ts — static, layered, remote, manifest-driven config
  • wasip1-usage.ts — Preview 1 setup, custom I/O, virtual filesystem
  • wasip3-usage.ts — streams, futures, async executor, P3 loader
  • examples/browser/basic-usage.ts, opfs-filesystem.ts, dns-over-https.ts, service-worker-http.ts
  • examples/fractal-demo/ — full WASM fractal renderer

Development

npm install
npm run build         # tsup
npm test              # vitest
npm run test:e2e      # playwright
npm run typecheck     # tsc --noEmit
npm run lint

Browser Compatibility

  • Chrome / Edge 89+
  • Firefox 89+
  • Safari 15+

Feature-specific requirements:

  • OPFS filesystem — Chrome 86+, Firefox 111+, Safari 15.2+
  • Service Worker HTTP — all modern browsers
  • Web Workers (threads) — all modern browsers
  • JSPI async imports — Chrome 137+, Node 22+
  • WebGPU — Chrome 113+, Firefox Nightly, Safari 18+

License

MIT