Skip to content

A WASM Plugin in JavaScript (jco / ComponentizeJS)

A fidius WASM plugin is an ordinary WebAssembly component that satisfies an interface's WIT contract — the language is irrelevant to the host. This guide implements the same greeter interface as Your First WASM Plugin (Rust) and the Python guide, this time in JavaScript, and loads it through the identical host path. Three languages, one host, one descriptor — that is the polyglot payoff of the Component Model (ADR FIDIUS-A-0003, "Path B").

The worked example is the committed fixture tests/wasm-fixtures/greeter-js/, verified by polyglot_js_guest_behaves_identically in crates/fidius-host.

Prerequisites

There is no fidius dependency in the guest — it only has to satisfy the WIT (the same contract the Rust and Python guests implement).

1. Implement it in JavaScript

jco maps the exported interface to an ESM named export matching the interface (greeter). Kebab-case WIT names become lowerCamelCase (echo-bytesechoBytes). Type mapping: s64/u64BigInt, list<u8>Uint8Array, result<T, _> → return T (throw for the error arm):

// greeter.js
export const greeter = {
  greet(name) {
    return `Hello, ${name}!`;
  },
  add(a, b) {
    return a + b; // BigInt + BigInt — the Ok arm of result<s64, plugin-error>
  },
  echoBytes(data) {
    return new Uint8Array(Array.from(data).reverse());
  },
  fidiusInterfaceHash() {
    return 0x0102030405060708n; // MUST equal the interface hash (BigInt / u64)
  },
};

The interface hash must match

fidius-interface-hash is an integrity check — the host compares it to the descriptor's interface_hash and refuses a mismatch at load. The interface author publishes the expected value (the Rust macros compute it from the signatures). It is not a security boundary — signing is.

2. Build the component

npx -y @bytecodealliance/jco componentize greeter.js \
  --wit path/to/wit --world-name greeter-plugin \
  --disable http fetch-event \
  --out greeter_js.wasm

wasm-tools validate --features component-model greeter_js.wasm

Disable features the sandbox won't grant

ComponentizeJS embeds a JS engine (StarlingMonkey) that, by default, imports wasi:http for fetch. fidius's WASM sandbox is deny-all and does not wire wasi:http into the linker, so an HTTP-enabled component fails to instantiate with "component imports instance wasi:http/types, but a matching implementation was not found". --disable http fetch-event drops those imports; clocks/random/stdio are fine (the host provides them). The result is a component exporting fidius:greeter/greeter — the same artifact shape as the Rust and Python guests (larger, since it embeds the JS engine: ~12 MB).

3. Package, sign, and load

Identical to any fidius package — the [wasm] section names the component and its capability allow-list:

# greeter-js-pkg/package.toml
[package]
name = "greeter-js-pkg"
version = "0.1.0"
interface = "greeter"
interface_version = 1
runtime = "wasm"

[metadata]
category = "demo"

[wasm]
component = "greeter_js.wasm"
capabilities = []
// The host loads it through the SAME API as the Rust and Python guests.
let handle = host.load_wasm("greeter-js-pkg", &Greeter_WASM_DESCRIPTOR)?;
let greeting: String = handle.call_method(0, &("Ada".to_string(),))?;
assert_eq!(greeting, "Hello, Ada!");

The host neither knows nor cares that the component is JavaScript — it loads, sandboxes, and dispatches it exactly like the Rust and Python guests. That is the polyglot guarantee.

See also