Skip to content

pavelsavara/jsco

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jsco - Browser polyfill for running WASM components

Demo

See live demo and browser demo sources

Usage

Browser usage

For most WASI components, instantiateWasiComponent is the simplest way:

import { instantiateWasiComponent } from '@pavelsavara/jsco';

// Works with both WASIp2 and WASIp3 components (auto-detected)
const instance = await instantiateWasiComponent('./my-component.wasm');
await instance.exports['wasi:cli/run@0.3.0'].run();

For advanced use cases (custom imports, non-WASI components), use createComponent directly:

import { createComponent } from '@pavelsavara/jsco';

const component = await createComponent('./my-component.wasm');
const instance = await component.instantiate({
    'my:app/logger@1.0.0': { log: console.log },
});
await instance.exports['my:app/greeter@1.0.0'].run({ name: 'World' });

Node.js usage

import { instantiateWasiComponent } from '@pavelsavara/jsco';

// Run a CLI component with real filesystem access
const instance = await instantiateWasiComponent('./my-cli-component.wasm', {
    args: ['input.txt'],
    env: [['HOME', '/home/user']],
    mounts: [{ hostPath: './data', guestPath: '/data' }],
});
await instance.exports['wasi:cli/run@0.3.0'].run();

To serve an HTTP handler component:

import { createComponent, loadWasiP3Host, loadWasiP3Serve } from '@pavelsavara/jsco';

const { createWasiP3Host } = await loadWasiP3Host();
const host = createWasiP3Host();
const component = await createComponent('./my-http-component.wasm');
const instance = await component.instantiate(host);
const handler = instance.exports['wasi:http/incoming-handler@0.2.0'];
const { serve } = await loadWasiP3Serve();
const handle = await serve(handler, { network: { httpRequestTimeoutMs: 30_000 } });
console.log(`Listening on port ${handle.port}`);

Configuration

createWasiP3Host() accepts an optional HostConfig:

Option Type Description
env [string, string][] Environment variables
args string[] Command-line arguments
cwd string Initial working directory
stdin ReadableStream<Uint8Array> Stdin input stream
stdout WritableStream<Uint8Array> Stdout output stream
stderr WritableStream<Uint8Array> Stderr output stream
fs Map<string, Uint8Array | string> In-memory VFS files
mounts MountConfig[] Host filesystem mounts (Node.js only)
network NetworkConfig Network limits and timeouts
limits AllocationLimits Allocation and size limits
enabledInterfaces string[] Whitelist of WASI interface prefixes

WASI interfaces provided

Each WASI interface is implemented for one or more preview generations (P1 = wasi_snapshot_preview1, P2 = wasi:*@0.2.x, P3 = wasi:*@0.3.x), and may behave differently in the browser vs. Node.js.

Interface Browser Node.js P3 P2 P1
cli/* (environment, exit, stdio) ✅ via adapter
clocks/* (monotonic, system, timezone) ✅ via adapter
random/* (secure, insecure, seed) ✅ via adapter
filesystem/* (VFS, preopens) ✅ in-memory ✅ + real mounts ✅ via adapter
http/client (Fetch API)
http/handler (server) ❌ not-supported ✅ via serve()
http/handler (middleware / chain) ❌ not-supported ✅ via linkHandler()
sockets/* (TCP, UDP, DNS) ❌ not-supported

P1 modules are served by the wasip1-via-wasip3 adapter (wasi_snapshot_preview1 shim layered on top of the P3 host); same configuration, same VFS/mount/network options apply.

CLI

jsco follows a command-based CLI similar to wasmtime. If no subcommand is provided, run is used by default.

# Run a component (default command)
jsco run ./integration-tests/hello-p2-world-wat/hello.wasm
# or without the subcommand
jsco ./integration-tests/hello-p2-world-wat/hello.wasm

# Serve an HTTP proxy component
jsco serve --addr 0.0.0.0:8080 ./my-http-component.wasm

# Show help
jsco --help
jsco run --help
jsco serve --help

When installed locally or via npx:

npx @pavelsavara/jsco run ./integration-tests/hello-p2-world-wat/hello.wasm

Common Options

Option Default Description
--dir <HOST[::GUEST[::ro]]> Mount a host directory into the guest. --dir . maps cwd, --dir /data::/mnt remaps.
--env <NAME[=VAL]> Set (--env FOO=bar) or inherit (--env FOO) an environment variable.
--env-inherit Inherit all host environment variables.
--cwd <PATH> Set the working directory for the component.
--enable <PREFIX> all Enable only WASI interfaces matching prefix (e.g. --enable wasi:http).
--use-number-for-int64 false Use number instead of bigint for i64.
--no-jspi false Disable JSPI wrapping of exports.
--validate-types true Validate export/import type annotations.

serve Options

Option Default Description
--addr <HOST:PORT> 0.0.0.0:8080 Socket address for the HTTP server to bind to.

Resource Limits

DOS-mitigation budgets enforced by the runtime. Each can be tuned via CLI flag or programmatically via instantiate(..., { limits: { ... } }).

Option Default Description
--max-allocation-size <N> 16777216 Max single allocation (bytes).
--max-handles <N> 10000 Max live resource handles per table.
--max-path-length <N> 4096 Max filesystem path length (bytes).
--max-memory-bytes <N> 268435456 Max WASM linear-memory size (bytes); 0 disables.
--max-canon-ops-without-yield <N> 1000000 Max canon built-in calls between JSPI yields; 0 disables. Mitigates stream.read → stream.cancel-read spin DOS.
--max-blocking-time-ms <N> 0 (off) Max ms any single JSPI suspension may block. 0 disables. Recommended for CI: 10000. Off by default so legitimately slow host I/O isn't killed.
--max-heap-growth-per-yield <N> 0 (off) Max host heap growth (bytes) between JSPI yields; 0 disables. Catches host-side state DOS that stays inside --max-canon-ops-without-yield.

Networking Options

HTTP/socket budgets enforced by the host. Run jsco run --help for the full list.

Option Default Description
--max-http-body-bytes <N> host default Max HTTP body size in bytes.
--max-http-headers-bytes <N> host default Max HTTP headers size in bytes.
--socket-buffer-bytes <N> host default Per-connection socket buffer in bytes.
--max-tcp-pending <N> host default Max pending TCP connections.
--tcp-idle-timeout-ms <N> host default TCP idle timeout (ms).
--http-request-timeout-ms <N> host default HTTP request timeout (ms).
--max-udp-datagrams <N> host default Max queued UDP datagrams.
--dns-timeout-ms <N> host default DNS lookup timeout (ms).
--max-concurrent-dns <N> host default Max concurrent DNS lookups.
--max-http-connections <N> host default Max concurrent HTTP server connections.
--max-request-url-bytes <N> host default Max request URL length (bytes).
--http-headers-timeout-ms <N> host default Slowloris protection: headers timeout (ms).
--http-keep-alive-timeout-ms <N> host default HTTP keep-alive timeout (ms).

Contribute

Goals

  • browser polyfill for running WASM/WASI components.
  • streaming parser of binary WIT
  • streaming compilation of WASM core modules during .wasm file download
  • in-the-browser creation of instances and necessary JavaScript interop
  • keep download size small enough to be practical for browser use

Why

  • to provide host which could do the binding in the browser
  • browsers currently don't implement built-in WASM component model host
  • because independent implementation will help the WASM/WIT/WASI to make progress
  • JCO is great alternative, really.
    • But it is too large to use as dynamic host, because download size matters to browser folks.
    • When you have all your components available at dev machine, JCO transpiler could be better choice.

How

Status

🚧 Preview quality 🚧

The WASIp1/p2/p3 hosts are implemented and pass an extensive test suite, but the API surface is not yet stable and behavior may change between preview releases. Not recommended for production use yet. Also this is one man + 🤖 show, come and help with testing.

test

See ./CHANGELOG.md for release notes and ./TODO.md for the roadmap. Contributors are welcome!

JSPI

See ./jspi.md for more details about JSPI - synchronous calls to JS APIs which are blocking, like I/O.

License

This project is licensed under the Apache License, Version 2.0, with the LLVM exception. See LICENSE for details, and THIRD-PARTY-NOTICES.TXT for attribution of code adapted from upstream projects.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

About

Browser polyfill for running WASM components

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors