@bytecodealliance/jco
v1.35.0
Published
JavaScript tooling for working with WebAssembly Components
Readme
A Bytecode Alliance project
Overview
Jco provides a fully native JS toolchain for working with WebAssembly Components in JavaScript.
Features include:
- "Transpiling" Wasm Component binaries into ES modules that can run in any JS environment.
- WASI Preview2 support in Node.js & browsers.
- Component builds of Wasm Tools helpers, available for use as a library or CLI commands for use in native JS environments, as well as optimization helper for Components via Binaryen.
- Run and serve commands like Wasmtime, as JS implementations of the Command and HTTP Proxy worlds.
- "Componentize" command to easily create components written in JavaScript (wrapper of ComponentizeJS).
For creating components in other languages, see the Component Model Book and Wit Bindgen for various guest bindgen helpers.
Installation
pnpm install @bytecodealliance/jcoJco can be used as either a library import or as a CLI via the jco command.
Example
See the Example Workflow page for a full usage example.
CLI
Usage: jco <command> [options]
jco - WebAssembly JS Component Tools
JS Component Transpilation Bindgen & Wasm Tools for JS
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
scaffold [options] <project-directory> Create a JavaScript or TypeScript WebAssembly component or host-plugin project
componentize [options] <source> Create a component from a JavaScript or TypeScript module
transpile [options] <component-path> Transpile a WebAssembly Component to JS + core Wasm for JavaScript execution
types [options] <wit-path> Generate types for the given WIT
guest-types [options] <wit-path> (experimental) Generate guest types for the given WIT
run [options] <command> [args...] Run a WASI Command component
serve [options] <server> [args...] Serve a WASI HTTP component
opt [options] <component-file> optimizes a Wasm component, including running wasm-opt Binaryen optimizations
wit [options] <component-path> extract the WIT from a WebAssembly Component [wasm-tools component wit]
print [options] <input> print the WebAssembly WAT text for a binary file [wasm-tools print]
metadata-show [options] [module] extract the producer metadata for a Wasm binary [wasm-tools metadata show]
metadata-add [options] [module] add producer metadata for a Wasm binary [wasm-tools metadata add]
parse [options] <input> parses the Wasm text format into a binary file [wasm-tools parse]
new [options] <core-module> create a WebAssembly component adapted from a component core Wasm [wasm-tools component new]
tool Low-level WebAssembly conversion utilities
embed [options] [core-module] embed the component typing section into a core Wasm module [wasm-tools component embed]
help [command] display help for commandFor help with individual command options, use jco <cmd> --help.
Scaffold a new component project
jco scaffold creates a regular Node.js project whose source skeleton matches an existing WIT world. By default,
Typescript, pnpm, and both NodeJS and Web targets are used:
jco scaffold my-component --wit path/to/wit--wit accepts a self-contained .wit file or WIT package directory and copies it into the project. If dealing with a
WIT file that contains multiple worlds, supply the --world option as well.
It can also pull a versioned WASI package from the Bytecode Alliance OCI registry. The package and its embedded dependencies are
expanded into the generated project's wit/ directory, so subsequent builds do not access the registry:
jco scaffold my-command --wit wasi:[email protected] --world wasi:cli/[email protected]Use an oci:// reference to scaffold from an arbitrary OCI registry package, including WIT-only components:
jco scaffold my-adder --wit oci://ghcr.io/bytecodealliance/docs/adder:0.1.0 --world docs:adder/[email protected]For a quick WASI starting point, use one of Jco's bundled WIT packages:
jco scaffold my-command --wit builtin:wasi-command
jco scaffold my-http-service --wit builtin:wasi-proxy
jco scaffold my-reactor --wit builtin:wasi-reactorThese aliases default to WASI 0.3.0. Use the @0.2.x suffix, for example
builtin:[email protected], to select the latest bundled WASI 0.2 release (currently 0.2.12). Jco copies the bundled
official WASI sources into the generated project's wit/ directory, so scaffolding does not require network access.
Immediately, you should be able to install and build the component:
cd my-component
pnpm install
pnpm check
pnpm test
pnpm buildYou can configure scaffolding in various ways:
--hostto generate a plugin that provides the selected world's imports for use withinstantiate(the default generates a guest that implements its exports)--language javascriptto generate a Javscript scaffold (the default is Typescript)--package-manager npm/--package-manager yarnto use a separate package manager (pnpmis the default)--target nodejs/--target web(can be repeated) to explicitly enable targets (by default both targets are supported)
Transpile
See the Transpiling Docs for more background and info.
Bindgen Crate
To directly call into the transpilation in Rust, the bindgen used in Jco is also available on crates.io as js-component-bindgen.
Run & Serve
For Wasm components that implement the WASI Command world, a jco run utility is provided to run these applications in Node.js.
jco run cowasy.component.wasm helloBy default, jco run retains its historical behavior and gives the component access to the host
filesystem, environment, and network. Pass --sandbox to deny those capabilities, then grant
only those the component needs:
jco run command.wasm --sandbox \
--sandbox-env-set HOME=/guest \
--sandbox-fs-preopen ./data::/data \
--sandbox-net-inherit--sandbox-env-set NAME inherits one variable from the host, while --sandbox-env-inherit inherits
them all. --sandbox-fs-preopen HOST[::GUEST] exposes a directory (using the host path as the guest
path when it is omitted). The environment and preopen options may be repeated; values are applied
in command-line order.
Using the preview2-shim WASI implementation, full access to the underlying system primitives is provided, including filesystem and environment variable permissions.
For HTTP Proxy components, jco serve provides a JS server implementation:
Warning:
jco serveis intended for development and testing only. It is not production ready.
jco serve --port 8080 server.wasmBy default, the server reuses one component instance for all requests. Pass --isolate-requests
to run every request in a fresh Node.js worker thread (equivalent to
--isolate-requests=worker):
jco serve --isolate-requests --port 8080 server.wasmWorker isolation gives each request a separate V8 isolate, JavaScript global scope, module cache, and
component instance. Request and response bodies are streamed through a local HTTP proxy. For lower
overhead, --isolate-requests=instance creates fresh component memories, tables, globals, and resource
tables while sharing the JavaScript isolate and module cache. Neither mode is a security sandbox or
isolates external filesystem and network side effects. Both modes have a significant performance cost,
especially worker isolation. Instance isolation is generally the faster isolation mechanism; worker
isolation trades additional worker startup and HTTP proxy overhead for separate JavaScript globals and
module caches. To reduce per-request startup latency without reusing JavaScript state, worker mode
prewarms 50 one-shot workers by default; configure this with --isolate-worker-pool-size. See the
development-use jco serve benchmark
page for benchmark results and reproduction instructions.
Wasmtime generally provides the most performant implementation for executing command and proxy worlds to use. These implementations are rather for when JS virtualization is required or the most convenient approach.
Componentize
Note:
jco componentizeis considered experimental, and breaking changes may be made without notice.
To componentize a JavaScript file run:
jco componentize app.js --wit wit -n world-name -o component.wasmBy default, StarlingMonkey is the default componentization backend, but alternative backends can be
selected with the --backend option, for example QuickJS-NG via componentize-qjs
(i.e. --backend quickjs or --backend qjs):
jco componentize app.js --wit wit -n world-name -o component.wasm --backend qjsQuickJS uses an async-capable runtime by default, even for synchronous WIT worlds. For synchronous
components targeting hosts without async support, such as Node.js 22 when running Jco-generated
JavaScript, add --backend-qjs-disable-async to select the non-async runtime.
The accepted backend names are starlingmonkey/sm and quickjs/qjs. The existing --engine <path>
option supplies a custom StarlingMonkey build and is valid only with the StarlingMonkey backend.
TypeScript entry modules are transformed and bundled automatically:
jco componentize app.ts --wit wit -n world-name -o component.wasmThe TypeScript transform erases types but does not perform semantic type checking. Run tsc --noEmit
separately when required.
Experimental Node.js built-in compatibility
Warning: Node.js builtin compatibility in components is experimental. Its APIs and generated component requirements may change incompatibly before the feature is stabilized.
Bundled source can keep ordinary supported node: imports. See the Jco book's
Node.js built-in compatibility guide
for the current API inventory, implementation choices, WIT requirements, host
adapter policy, limitations, and examples.
See ComponentizeJS and componentize-qjs for backend-specific details.
API
transpile(component: Uint8Array, opts?): Promise<{ files: Record<string, Uint8Array> }>
Transpile a Component to JS.
opt(component: Uint8Array, opts?): Promise<{ component: Uint8Array }>
Optimize a Component with the Binaryen Wasm-opt project.
componentWit(component: Uint8Array, document?: string): string
Extract the WIT world from a component binary.
print(component: Uint8Array): string
Print the WAT for a Component binary.
metadataShow(wasm: Uint8Array): Metadata
Extract the producer toolchain metadata for a component and its nested modules.
parse(wat: string): Uint8Array
Parse a compoment WAT to output a Component binary.
componentNew(coreWasm: Uint8Array | null, adapters?: [String, Uint8Array][]): Uint8Array
"WIT Component" Component creation tool, optionally providing a set of named adapter binaries.
componentEmbed(coreWasm: Uint8Array | null, wit: String, opts?: { stringEncoding?, dummy?, world?, metadata? }): Uint8Array
"WIT Component" Component embedding tool, for embedding component types into core binaries, as an advanced use case of component generation.
metadataAdd(wasm: Uint8Array, metadata): Uint8Array
Add new producer metadata to a component or core Wasm binary.
Contributing
See the Contributing chapter of the Jco book.
License
This project is licensed under the Apache 2.0 license with the LLVM exception. See LICENSE for more details.
Contribution
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.
