Skip to main content

0) Prerequisites

  • Node.js 18+ and npm installed
  • A terminal on macOS/Linux/WSL (Windows PowerShell also works)
Tip: Use nvm to manage Node versions:
# macOS/Linux example
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install --lts
nvm use --lts

1) Create a project folder

mkdir my_project
cd my_project

2) Install CLI

npm install -g @bytecodealliance/componentize-js

3) Add the WIT world

Create newton-provider.wit in the project root:
package newton:provider@0.1.0;

interface http {
    record http-request {
        url: string,
        method: string,
        headers: list<tuple<string, string>>,
        body: option<list<u8>>,
    }

    record http-response {
        status: u16,
        headers: list<tuple<string, string>>,
        body: list<u8>,
    }

    fetch: func(request: http-request) -> result<http-response, string>;
}

world newton-provider {
    import http;
    export run: func(input: string) -> result<string, string>;
}

4) Implement your component logic

Create app.js:
// Import the WIT import exactly like the ComponentizeJS usage pattern:
// e.g. import { log } from 'local:hello/logger' in their docs.
// Here our package is newton:provider and interface is http.
import { fetch as httpFetch } from "newton:provider/[email protected]";

// WIT: export run: func(input: string) -> result<string, string>
// We return a JSON string on success AND on "errors"
// (i.e., we don't surface WIT Err<string> — we encode error info in JSON)
export function run(input) {
  const req = JSON.parse(input);
  // do stuff
  const output = { result: "example" }; // replace with your logic
  return JSON.stringify(output);
}
Note: Keep this import at the top level of your module; ComponentizeJS resolves virtual specifiers like 'newton:provider/[email protected]' when you build the component.

5) Build the component

componentize-js --wit newton-provider.wit -o policy.wasm app.js -d stdio random clocks http fetch-event
This produces policy.wasm in the project root — a component that:
  • Imports newton:provider/http.fetch from the host
  • Exports run(input: string) -> result<string, string>
Resulting project tree:
my_project/
├─ policy.wasm
├─ newton-provider.wit
└─ app.js

6) (Alternative) Programmatic build

Create componentize.mjs:
import { componentize } from "@bytecodealliance/componentize-js";
import { readFile, writeFile } from "node:fs/promises";

const { component, imports } = await componentize({
  sourcePath: "./app.js", // your JS file
  witPath: "./newton-provider.wit", // WIT file path
  worldName: "newton-provider", // the world in your WIT
});

await writeFile("policy.wasm", component);
console.log("guest imports:", imports);
Run it with:
node componentize.mjs

7) Next steps

  • Port the op-sim directory (in its entirety) from: op-sim GitHub into your project root.
  • Test your WASM binary by running op-sim:
cd op-sim
cargo run --release -- ../policy.wasm '<your_input_string>'
# Example:
# cargo run --release -- ../policy.wasm '{"inquiry_id": "inq_xRZrQFKg7rqZ5UZGLhnvb2ympshE"}'