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?
JCO works with wasm components. wasip1 is based on plain wasm modules, not wasm components.
Right but can I wrap the WASI 0.1 module in a component or something?
Depending on what you need https://github.com/bjorn3/browser_wasi_shim/ may work.
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.
Does such an adapter already exist in wasm-tools?
Is that what that component new command does?
https://github.com/bytecodealliance/wasmtime/releases/tag/v36.0.2 has the latest version of the wasi_snapshot_preview1.reactor.wasm adapter.
It is in the wasmtime repo because wasmtime internally uses this adapter (compiled to native code rather than wasm) when running a wasip1 module.
Good to know, and promising since I have gotten my module running in wasmtime already
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.
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.
What’s the difference?
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
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
This does have a main function afaik, so the command approach should be fine
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
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
basically that looks like a CLI syntax mistake/bug, but I don't know the exact fix myself
you can also do
--adapt wasi_snapshot_preview1=wasi_snapshot_preview1.command.wasm
that worked, thank you
Where can I find more information about using jco from JS instead of from the command line?
Specifically so that I can work it into my build scripts
@Victor Adossi might be able to help out with that
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
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
Thanks - I'm using it from Node, not the browser, since I just want to integrate jco into my esbuild config
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
What am I to make of all these parameter types? It's hard to tell what type each param actually expects
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
component new and transpile, I think, but at the moment just component new
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;
Note that there's also a listing of the API in the README
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.
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());
I need to pass it command line args and env vars and get stdout, that kind of thing
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
Yes, I realize that, but with wasmtime it can just _run_ it, so what is the equivalent thing I need to do here
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
(I don't remember if jco calls it exports.run or directly .run, I have been working with wasip1 a lot again lately)
I am using @bytecodealliance/preview2-shim, all I am asking is how to actually do the thing that wasmtime is already doing
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
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
Why does the result of instantiate wrap up the exports the way it does?
image.png
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
After much hassle yesterday, I do finally have it up and running. Thanks for all your help.
The summary of what I'm now running is almost too shameful to admit
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
(All so that I can live-demo what the Ion JIT is doing with your JS code.)
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.
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
One last random thing: I'm getting duplicate object key warnings because of this:
image.png
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!
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
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?
I tried the jco book and various readmes but they all stop documenting things after instantiation
It’s all “TODO: add code to utilize the component”
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
which is entirely up to the shim afaik
AH! OK thank you for making that clear
Yeah we definitely need more work on the book -- would you mind creating an issue against Jco?
Well tbh I think this stuff should be in the shim readme, if documented at all? The filesystem stuff is really nasty.
And the shim readme is what shows up on NPM.
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
I can open an issue regardless
This is the filesystem stuff I'm talking about
image.png
Ah, so that sounds like a second issue
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)
I had to basically reverse engineer what the internal data format was for files
So this... I would have expected to be provided via the types, are they not showing up properly?
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
I am actually not using TypeScript for this at the moment
So perhaps that would have showed me
That said, I don't see TypeScript types for _setEnv and friends? But maybe I'm looking in the wrong place
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
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
Ah also a problem -- the types for setEnv are missing -- there's no JSdoc on that
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
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
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
yeah an isomorphic API would be ideal, but the browser implementation is very experimental (read incomplete)
Which is why they haven't been merged IIRC
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"
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?
and it's the latter that is platform-specific and undocumented and untyped
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
and there is the configuration of the host which should be less platform specific, but it depends completely on your implementation
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"
OK new version of jco is out that should fix that bindgen issue:
https://www.npmjs.com/package/@bytecodealliance/jco/v/1.15.0
Thanks!
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
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.
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/
Last updated: Dec 06 2025 at 05:03 UTC