Stream: general

Topic: Run WASI 0.1 module with jco?


view this post on Zulip Ben Visness (Sep 10 2025 at 12:53):

I’m really struggling to get a WASI 0.1 module running on the web because the available JS libraries seem like a total disaster. jco seems like by far the more up to date tool, but is there any way to use it with WASI 0.1 instead of 0.2?

view this post on Zulip bjorn3 (Sep 10 2025 at 13:07):

JCO works with wasm components. wasip1 is based on plain wasm modules, not wasm components.

view this post on Zulip Ben Visness (Sep 10 2025 at 13:08):

Right but can I wrap the WASI 0.1 module in a component or something?

view this post on Zulip bjorn3 (Sep 10 2025 at 13:08):

Depending on what you need https://github.com/bjorn3/browser_wasi_shim/ may work.

A WASI shim for in the browser. Contribute to bjorn3/browser_wasi_shim development by creating an account on GitHub.

view this post on Zulip bjorn3 (Sep 10 2025 at 13:09):

Ben Visness said:

Right but can I wrap the WASI 0.1 module in a component or something?

That would also be an option. You can use wasm-tools to wrap a wasip1 module with a wasip1 -> wasip2 adapter.

CLI and Rust libraries for low-level manipulation of WebAssembly modules - GitHub - bytecodealliance/wasm-tools: CLI and Rust libraries for low-level manipulation of WebAssembly modules

view this post on Zulip Ben Visness (Sep 10 2025 at 13:10):

Does such an adapter already exist in wasm-tools?

view this post on Zulip Ben Visness (Sep 10 2025 at 13:10):

Is that what that component new command does?

view this post on Zulip bjorn3 (Sep 10 2025 at 13:11):

https://github.com/bytecodealliance/wasmtime/releases/tag/v36.0.2 has the latest version of the wasi_snapshot_preview1.reactor.wasm adapter.

36.0.2 Released 2025-08-26. Fixed Wasmtime will no longer panic in the pooling allocator when in near-OOM conditions related to resetting the linear memory of a slot. #11510

view this post on Zulip bjorn3 (Sep 10 2025 at 13:11):

It is in the wasmtime repo because wasmtime internally uses this adapter (compiled to native code rather than wasm) when running a wasip1 module.

view this post on Zulip Ben Visness (Sep 10 2025 at 13:12):

Good to know, and promising since I have gotten my module running in wasmtime already

view this post on Zulip bjorn3 (Sep 10 2025 at 13:12):

wasm-tools component new my-core.wasm -o my-component.wasm --adapt wasi_snapshot_preview1.reactor.wasm is the suggested command in the wasm-tools readme.

view this post on Zulip bjorn3 (Sep 10 2025 at 13:14):

Also you may want to use the .command.wasm rather than .reactor.wasm version if you have a program whose main function should be called at startup rather than a library-like module that exports functions you can call.

view this post on Zulip Ben Visness (Sep 10 2025 at 13:14):

What’s the difference?

view this post on Zulip Ramon Klass (Sep 10 2025 at 13:20):

command exports a main function and nothing else, reactor may export an optional init function and as many functions as it wants
command is like an executable that reads command line arguments, env and stdin and writes stdout and stderr
reactor is like a dll other programs load to call functions in

view this post on Zulip Alex Crichton (Sep 10 2025 at 14:40):

You'll likely want the "command" adapter which reexports the WASI main function entrypoint as a WASIp2 function. If you use a "reactor" adapter it would require that you have component metadata for some other export. Otherwise while you'll likely get a component out of the process it won't have any exports to call because the reactor doesn't export anything nor does your module which wasn't intended to be a component.

If you're building something that's not something with a main function, however, then creating a component won't work without some sort of integration with WIT or similar. Basically you'll have to describe the exported functions somehow with WIT. That can be done after-the-fact once the core module is built, and it can also be done with wasm-tools, but depends on your use case

view this post on Zulip Ben Visness (Sep 10 2025 at 14:42):

This does have a main function afaik, so the command approach should be fine

view this post on Zulip Ben Visness (Sep 10 2025 at 14:43):

But what to make of this...

$ npx jco new js.wasm -o js.component.wasm --adapt wasi_snapshot_preview1.command.wasm
(jco componentNew) ComponentError: failed to encode a component from module
$failed to decode world from module

Caused by:
    0: module was not valid
    1: failed to resolve import `wasi_snapshot_preview1::fd_write`
    2: module requires an import interface named `wasi_snapshot_preview1`
    at componentNew (file:///home/bvisness/Developer/spidermonkey.dev/_support/iongraph-article/node_modules/@bytecodealliance/jco/obj/wasm-tools.js:4663:11)
    at componentNew (file:///home/bvisness/Developer/spidermonkey.dev/_support/iongraph-article/node_modules/@bytecodealliance/jco/src/cmd/wasm-tools.js:100:20)
    at async file:///home/bvisness/Developer/spidermonkey.dev/_support/iongraph-article/node_modules/@bytecodealliance/jco/src/jco.js:467:17

view this post on Zulip Alex Crichton (Sep 10 2025 at 14:45):

That looks like a failure in the CLI invocation to communicate to jco's new subcommand "the wasi_snapshot_preview1 adapter is in this wasm file". I'm not familiar with jco's CLI so I can't say for certain what the issue is, but you might want to try to rename that as wasi_snapshot_preview1.wasm and see if that helps. Another option is that if jco new's syntax is inspired by wasm-tools you can also do --adapt wasi_snapshot_preview1=wasi_snapshot_preview1.command.wasm, although I'm not sure if this is supported in jco

view this post on Zulip Alex Crichton (Sep 10 2025 at 14:46):

basically that looks like a CLI syntax mistake/bug, but I don't know the exact fix myself

view this post on Zulip Ben Visness (Sep 10 2025 at 14:46):

you can also do --adapt wasi_snapshot_preview1=wasi_snapshot_preview1.command.wasm

that worked, thank you

view this post on Zulip Ben Visness (Sep 10 2025 at 14:50):

Where can I find more information about using jco from JS instead of from the command line?

view this post on Zulip Ben Visness (Sep 10 2025 at 14:50):

Specifically so that I can work it into my build scripts

view this post on Zulip Alex Crichton (Sep 10 2025 at 15:02):

@Victor Adossi might be able to help out with that

view this post on Zulip Victor Adossi (Sep 10 2025 at 15:11):

Hey @Ben Visness so in general I think where you'll want to start is the jco code base here:

For example if you wanted to investigate how transpile works:
https://github.com/bytecodealliance/jco/blob/main/packages/jco/src/cmd/transpile.js

In general the API is the surface you can use without the CLI (i.e. directly from JS):
https://github.com/bytecodealliance/jco/blob/main/packages/jco/src/api.js

JavaScript toolchain for working with WebAssembly Components - bytecodealliance/jco
JavaScript toolchain for working with WebAssembly Components - bytecodealliance/jco

view this post on Zulip Victor Adossi (Sep 10 2025 at 15:12):

Unfortunately the browser bindings are still very experimental and not complete, so there's a bunch there but you may very well run into more missing pieces

view this post on Zulip Ben Visness (Sep 10 2025 at 15:12):

Thanks - I'm using it from Node, not the browser, since I just want to integrate jco into my esbuild config

view this post on Zulip Victor Adossi (Sep 10 2025 at 15:14):

Ah OK, great in that case the support should be much better, and like I said you should be able to use the code in api.js

view this post on Zulip Ben Visness (Sep 10 2025 at 15:19):

What am I to make of all these parameter types? It's hard to tell what type each param actually expects

view this post on Zulip Victor Adossi (Sep 10 2025 at 15:33):

Which part of the API are yout rying to use? Also, do you have the project built locally? obj/wasm-tools.d.tsis actually present and helps decode types and should help

view this post on Zulip Ben Visness (Sep 10 2025 at 15:34):

component new and transpile, I think, but at the moment just component new

view this post on Zulip Ben Visness (Sep 10 2025 at 15:34):

digging around in my local node_modules, I did finally turn this up, which is the thing I actually wanted

export function componentNew(binary: Uint8Array, adapters: Array<[string, Uint8Array]> | undefined): Uint8Array;

view this post on Zulip Victor Adossi (Sep 10 2025 at 15:48):

Note that there's also a listing of the API in the README

JavaScript toolchain for working with WebAssembly Components - bytecodealliance/jco

view this post on Zulip Victor Adossi (Sep 10 2025 at 15:49):

Usage in tests/code can also be useful to look at!

We really should have some better types, but once we switch to TS they should be much better.

view this post on Zulip Ben Visness (Sep 10 2025 at 17:41):

So, I've gotten as far as this, but how do I actually run the module now?

const component = await instantiate(loader, new WASIShim().getImportObject());

view this post on Zulip Ben Visness (Sep 10 2025 at 17:41):

I need to pass it command line args and env vars and get stdout, that kind of thing

view this post on Zulip Victor Adossi (Sep 10 2025 at 17:46):

So if you've gotten to that point, component is a module that contains what you component exports, so you should be able to call the relevant imports.

The exports that your component exports should be available on the component object

view this post on Zulip Ben Visness (Sep 10 2025 at 17:48):

Yes, I realize that, but with wasmtime it can just _run_ it, so what is the equivalent thing I need to do here

view this post on Zulip Ramon Klass (Sep 10 2025 at 17:51):

component.exports.run() is the default main function of a command world, and you provide env, files and cli args via the wasi shim you use

view this post on Zulip Ramon Klass (Sep 10 2025 at 17:52):

(I don't remember if jco calls it exports.run or directly .run, I have been working with wasip1 a lot again lately)

view this post on Zulip Ben Visness (Sep 10 2025 at 17:53):

I am using @bytecodealliance/preview2-shim, all I am asking is how to actually do the thing that wasmtime is already doing

view this post on Zulip Ben Visness (Sep 10 2025 at 17:57):

I'd be happy to follow an example, but I can't find a single example in the jco repo of actually running any kind of wasi component

view this post on Zulip Ramon Klass (Sep 10 2025 at 17:59):

I think right now you need to call these exports in preview2-shim to set things like env vars and commandline args: https://github.com/bytecodealliance/jco/blob/main/packages/preview2-shim/lib/nodejs/cli.js#L11-L28

and then you call the run() export of your module

JavaScript toolchain for working with WebAssembly Components - bytecodealliance/jco

view this post on Zulip Ben Visness (Sep 10 2025 at 18:11):

Why does the result of instantiate wrap up the exports the way it does?
image.png

view this post on Zulip Ramon Klass (Sep 10 2025 at 18:13):

https://github.com/WebAssembly/wasi-cli/blob/main/wit/run.wit

interface run { run: func() } }

run is actually an interface that defines a single method run on itself

Command-Line Interface (CLI) World for WASI. Contribute to WebAssembly/wasi-cli development by creating an account on GitHub.

view this post on Zulip Ben Visness (Sep 11 2025 at 16:07):

After much hassle yesterday, I do finally have it up and running. Thanks for all your help.

view this post on Zulip Ben Visness (Sep 11 2025 at 16:07):

The summary of what I'm now running is almost too shameful to admit

view this post on Zulip Ben Visness (Sep 11 2025 at 16:08):

I am compiling SpiderMonkey to WASM as a WASI 0.1 module, using wasm-tools to convert it to a WASI 0.2 module using that adapter, then transpiling it to JS with jco, all so that I can JIT JS to arm32 machine code and then run an arm32 emulator to run the code

view this post on Zulip Ben Visness (Sep 11 2025 at 16:09):

(All so that I can live-demo what the Ion JIT is doing with your JS code.)

view this post on Zulip Ben Visness (Sep 11 2025 at 16:11):

I gotta say, using the preview2-shim is a pretty miserable experience. Especially the filesystem stuff. Some docs would go a long way, but also, it seems quite rough as an API and I basically had to reverse engineer how it was all supposed to work by rooting around in the source and honestly just guessing sometimes.

view this post on Zulip Ben Visness (Sep 11 2025 at 16:17):

I also wrote an esbuild plugin along the way to handle the transpiling and stuff. It's too rough around the edges to publish as a library but here's a gist in case it helps anyone else do something similar: https://gist.github.com/bvisness/66e4b65af09a40b6c3bd4bd316d2d7e4

An esbuild plugin to get WASI 0.1 modules running in the browser. - esbuild-wasi.mjs

view this post on Zulip Ben Visness (Sep 11 2025 at 16:18):

One last random thing: I'm getting duplicate object key warnings because of this:
image.png

view this post on Zulip Victor Adossi (Sep 11 2025 at 17:17):

Ben Visness said:

After much hassle yesterday, I do finally have it up and running. Thanks for all your help.

Thanks to @Ramon Klass for doing a great job answering questions!

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:27):

Ben Visness said:

One last random thing: I'm getting duplicate object key warnings because of this:

Ah, thank you for pointing that out, that definitely shouldn't be there, new Jco version should be out soon with that fix

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:30):

Ben Visness said:

I gotta say, using the preview2-shim is a pretty miserable experience. Especially the filesystem stuff. Some docs would go a long way, but also, it seems quite rough as an API and I basically had to reverse engineer how it was all supposed to work by rooting around in the source and honestly just guessing sometimes.

Would really appreciate any PRs you want to make against the documentation, but where did you look first? Did you look at the Jco books or the examples? Which resource let you down the most/where should we add the docs first?

Does this page in the Jco book need an update that would have helped you do filesystem stuff much easier?

view this post on Zulip Ben Visness (Sep 11 2025 at 18:32):

I tried the jco book and various readmes but they all stop documenting things after instantiation

view this post on Zulip Ben Visness (Sep 11 2025 at 18:32):

It’s all “TODO: add code to utilize the component”

view this post on Zulip Ben Visness (Sep 11 2025 at 18:33):

the key thing, I think, is that I don’t actually need to see how to call the WASI exports, what I need is to see how to do _setEnv and so on

view this post on Zulip Ben Visness (Sep 11 2025 at 18:33):

which is entirely up to the shim afaik

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:34):

AH! OK thank you for making that clear

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:34):

Yeah we definitely need more work on the book -- would you mind creating an issue against Jco?

view this post on Zulip Ben Visness (Sep 11 2025 at 18:35):

Well tbh I think this stuff should be in the shim readme, if documented at all? The filesystem stuff is really nasty.

view this post on Zulip Ben Visness (Sep 11 2025 at 18:35):

And the shim readme is what shows up on NPM.

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:35):

If you could be incredibly liberal (no frustration is too small), this is something we can actually get to and fix/make better.

The context here is that there are actually ~2+ starting points for Jco/JS component work -- there's the component book (it stops short of async instantiation after transpile) and the Jco book, and clearly the latter is the right place but doesn't have enough detail

view this post on Zulip Ben Visness (Sep 11 2025 at 18:36):

I can open an issue regardless

view this post on Zulip Ben Visness (Sep 11 2025 at 18:36):

This is the filesystem stuff I'm talking about
image.png

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:36):

Ah, so that sounds like a second issue

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:36):

Feel free to make them distinct issues if you want, instantiation broadly being better documented, and the Preview2 README pointing to the documentation (and containing more examples)

view this post on Zulip Ben Visness (Sep 11 2025 at 18:36):

I had to basically reverse engineer what the internal data format was for files

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:37):

So this... I would have expected to be provided via the types, are they not showing up properly?

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:37):

is this a third issue? IIRC I get completion locally when using the exports of preview2-shim but if that's not working then there is clearly an issue

view this post on Zulip Ben Visness (Sep 11 2025 at 18:38):

I am actually not using TypeScript for this at the moment

view this post on Zulip Ben Visness (Sep 11 2025 at 18:38):

So perhaps that would have showed me

view this post on Zulip Ben Visness (Sep 11 2025 at 18:39):

That said, I don't see TypeScript types for _setEnv and friends? But maybe I'm looking in the wrong place

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:39):

AHHHH yes, unfortunately that's the problem, all of the types are specified that way... OK, but we can at least improve this with JSDoc

view this post on Zulip Ben Visness (Sep 11 2025 at 18:40):

like, I can find @bytecodealliance/preview2-shim/lib/browser/filesystem.js, which has the _setFileData function, but unless I'm missing something, I don't see any TypeScript definitions for _setFileData

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:40):

Ah also a problem -- the types for setEnv are missing -- there's no JSdoc on that

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:41):

Yeah at this point not even sure if the "add jsdoc everywhere" should be lumped into the problems with preview2 issue, but either way, that's probably the quickest way to fix the huge DX hole

view this post on Zulip Ben Visness (Sep 11 2025 at 18:41):

and like I'm sure it's because it's specific to the browser implementation of the shim, but really it's kind of gross on its own that I have to import and use browser-specific things instead of having a unified API that I could use in any environment

view this post on Zulip Ben Visness (Sep 11 2025 at 18:43):

and if I understand correctly, the things I need (env vars, cmd args, files) are entirely outside of the WASI component and are implemented in some specific way on the host - that is, in this case, by the shim

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:43):

yeah an isomorphic API would be ideal, but the browser implementation is very experimental (read incomplete)

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:43):

Which is why they haven't been merged IIRC

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:43):

So the thing is that the instantiation is also JS so there's some overlap there, but in general yes, the p2 implementation is "host code"

view this post on Zulip Ben Visness (Sep 11 2025 at 18:44):

sure, but part of it is satisfying the semantics of the WASI interface (glue), and part of it is actually acting as the host, right?

view this post on Zulip Ben Visness (Sep 11 2025 at 18:44):

and it's the latter that is platform-specific and undocumented and untyped

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:46):

If I'm understanding you correctly it's both that are platform specific -- because what is available/usable from Node differs from the browser environment

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:47):

and there is the configuration of the host which should be less platform specific, but it depends completely on your implementation

view this post on Zulip Victor Adossi (Sep 11 2025 at 18:48):

Like for example TextEncoder is obviously available in both environments so there's nothing inherently platform specific about your screenshot above, though it's part of the configuration for "acting as the host"

view this post on Zulip Victor Adossi (Sep 11 2025 at 19:39):

OK new version of jco is out that should fix that bindgen issue:

https://www.npmjs.com/package/@bytecodealliance/jco/v/1.15.0

JavaScript tooling for working with WebAssembly Components. Latest version: 1.15.0, last published: 6 minutes ago. Start using @bytecodealliance/jco in your project by running `npm i @bytecodealliance/jco`. There are 10 other projects in the npm registry using @bytecodealliance/jco.

view this post on Zulip Ben Visness (Sep 11 2025 at 19:41):

Thanks!

view this post on Zulip Milan (rajsite) (Sep 11 2025 at 21:28):

It doesn't address the API concerns (and don't think it's trying to) but I have found the WASI p2 browser shim made by @Catherine (whitequark) easier to extend / hack on:
https://github.com/YoWASP/runtime-js/blob/develop/lib/api.js#L42
https://github.com/YoWASP/runtime-js/blob/develop/lib/wasi-virt.js

Common runtime for YoWASP packages deployed to NPM, interfacing with Node.js and the browser - YoWASP/runtime-js
Common runtime for YoWASP packages deployed to NPM, interfacing with Node.js and the browser - YoWASP/runtime-js

view this post on Zulip Catherine (whitequark) (Sep 11 2025 at 22:35):

o/ Happy to answer questions!

And yeah, I found the "official" shim kind of burdensome to work with, and it didn't support my use case (virtual FS) to start with.

view this post on Zulip Bakkot (Sep 16 2025 at 19:27):

For just the basic "get a WASI 0.1 module running on the web", I have found https://github.com/taybenlor/runno to be useful; there's a demo at https://runno.dev/wasi/

Sandboxed runtime for programming languages and WASI binaries. Works in the browser or on your server. - taybenlor/runno

Last updated: Dec 06 2025 at 05:03 UTC