Hi all, excited to share wcjs (https://github.com/jellevandenhooff/wcjs), an experimental component model / wasip3 runtime written in typescript and targeting the browser and node. Fair warning: it's lots of LLM written code, so feel free to ignore if that does not work for you.
wcjs currently passes the wasip3 tests from wasmtime, the component model spec tests, and can pass the go tests running my wasip3-targeting go fork. There's a demo that perhaps is most clear at https://jellevandenhooff.github.io/wcjs/ which runs a small async rust guest in the browser. You'll need a browser that supports JSPI to run it (ie a recent chrome). Source maps work so you can step through the rust, the bindgen code, the typescript runtime, or the host. You can use it both to run components (with npx @jellevdh/wcjs run <component>) or to generate javascript and types to embed in your application (with npx @jellevdh/wcjs generate).
This codebase is very inspired by and relies on the jco, wasmtime, and the component model repos. Compared to jco, this prototype has no dependencies on eg. wasm-parser, and the code generator and runtime are all written in typescript. I originally tried to get more wasip3 code running in jco, but I found it easier to work in a clean-slate codebase.
Would be curious to hear what y'all think. I am not yet committing to any future work or maintenance -- it's an experiment!
Impressive work @Jelle van den Hooff this is fantastic work, looking thorugh the code and it is certainly a lot cleaner and looks to match the spec pseudocode pretty much exactly!
Hey looking through the tests, one small change I think you could add is porting the upstream tests that test various P3 non-WASI scenarios:
https://github.com/bytecodealliance/wasmtime/tree/main/crates/test-programs/src/bin
Am I missing somewhere where you already pulled these tests? I see the copy-wasmtime-tests.sh script which cover WASI p3 stuff but I can't yet find the other tests if they're there.
They require slightly more porting because they require some composition (caller/callee) tests, but this would be basically another set of really valuable tests to add.
Thanks @Victor Adossi! You're right, those tests aren't ported yet, and it'd be nice to do (once I have tokens again, haha). The whole test runner infrastructure could use some love and (re-)organization. So far the composition logic is mostly tested by the component model wast tests.
yeah the range of the test suite is impressive -- IMO those are the only ones you're missing (I could very well be wrong!), and it's great that all the other ones work and pass w/ go as well.
Code looks clean (I see remnants of Jco bindgen'd code/idioms :) and I really like the dependency list (or lack thereof). Going to TS and splitting out a minimal runtime (in the p3 context but also generally so we could avoid writing JS-in-Rust) was something we've wanted to do for a while -- really great that you've pulled this forward.
At this point if WebAssembly hosts and CM + P2/P3 implementations are within range of LLMs, we're in a completely awesome new world -- we maybe we should have way more hosts than we do.
To come back to topic though, I might be a bit early here but I'm behind replacing/integrating as much of existing jco bindgen code as we can with wcjs's approach. Lack of support for Bun/Deno isn't ideal but that feels like it's prompts away. And with how easy to read and clear the current code is, it gives me confidence the codebase will continue to be maintainable by either humans or LLMs going forward.
jco's bindgen was super helpful. I originally tried to modify the jco codegen to generate code that would directly target a typescript runtime, but I ended up getting frustrated (or rather, not really understanding) the wasmtime-environ generated proxies. And it turns out doing the wasm parsing directly isn't so bad when you can use an LLM.
The areas that I think have tricky gaps are... host <-> guest bindings and their typings, I don't have much coverage or trust, and also the host <-> runtime infrastructure I think could use love (for example, the stream / handle / rep logic and perhaps typings).
(and I have been totally shocked how capable LLMs are at writing code like this)
I'd be happy to help get this to a place where it'd be useful to jco. I think it still feels pretty early to me; I haven't really audited a lot of the code myself or thought hard about APIs, so that's why there's the explicit prototype branding. At the same time, I'd be excited to try and get it in a more solid shape.
jco's bindgen was super helpful. I originally tried to modify the jco codegen to generate code that would directly target a typescript runtime, but I ended up getting frustrated (or rather, not really understanding) the wasmtime-environ generated proxies. And it turns out doing the wasm parsing directly isn't so bad when you can use an LLM.
Yeahhhh :( I've tried but unfortunately the Jco code still isn't easy to understand enough. I think the frustration has borne a completely different point on the design space though, so not sure if we should be happy it was that difficult.
Took a look at the Wasm parsing code and it's also quite well structured and looks great. Not sure how much sculpting you did but it certainly worked out.
The areas that I think have tricky gaps are... host <-> guest bindings and their typings, I don't have much coverage or trust, and also the host <-> runtime infrastructure I think could use love (for example, the stream / handle / rep logic and perhaps typings).
Yeah so this is a good point and is something we've struggled with so I understand at least -- enums for example IIRC get represented as strings and this is something that is really up to users/we kind of have to choose to accomodate/have an opinion on.
Currently Jco tries to be as close to idiomatic JS as possible (not TS), but it hasn't stopped there from being issues with it.
(and I have been totally shocked how capable LLMs are at writing code like this)
My shock increased today by at least one order of magnitude! Really appreciate the contribution
I'd be happy to help get this to a place where it'd be useful to jco. I think it still feels pretty early to me; I haven't really audited a lot of the code myself or thought hard about APIs, so that's why there's the explicit prototype branding. At the same time, I'd be excited to try and get it in a more solid shape.
Thanks I'm glad to hear that -- from what I've seen I can say I really like the shape of it.
We're moving very quickly towards a multi-engine world for the lower layers (StarlingMonkey and qjs via componentize-js and the recent componentize-qjs experiment respectively), and there's no reason why Jco can't be multi-runtime.
One thing that would be great would be to unify the p2/p3 shims and instantiation/virtualization logic so that different runtimes can use them, this way the multi-runtime world under Jco (or whatever project this morphs into) can be even more constrained/reusable.
okay, tiny follow-up with some more hacking... https://jellevandenhooff.github.io/wcjs/go/ has the go compiler built on wasip3 running in wcjs in the browser -- and because go today does all its own stack switching, this does not require JSPI and works on firefox/safari/chrome today.
I think I'll clean things up and not add more features now :joy:
(this is mostly standard wasi except I have a janky wasi:exec implementation because the go compiler really likes subprocesses)
BTW, JSPI is actually available in Safari Technology Preveiw 238
Demo is awesome, runs on my machine (compile takes a little bit) :tada: !
The compile was slow because go was reading the (pre-filled) build cache serially. The demo now prefetches part of the cache and it should be zippy!
I am really curious to see when JSPI lands... I saw a bunch of recent commits going into firefox also.
I think some tooling to bundle a readonly filesystem with a component and a wrapper around OPFS will be nice and should make this demo easy to reproduce.
Just tried it out, it was WAY faster
Build succeeded in 2.0s
Amazing work!
for me: Build succeeded in 1.1s
Last updated: Mar 23 2026 at 16:19 UTC