Skip to content

fix(js): build bashkit-js for wasm32-wasip1-threads#2141

Open
a0preetham wants to merge 2 commits into
everruns:mainfrom
a0preetham:fix-js-wasip1-threads
Open

fix(js): build bashkit-js for wasm32-wasip1-threads#2141
a0preetham wants to merge 2 commits into
everruns:mainfrom
a0preetham:fix-js-wasip1-threads

Conversation

@a0preetham

Copy link
Copy Markdown
Contributor

Problem

The wasm32-wasip1-threads target has been commented out in publish-js.yml with:

TODO: WASM disabled — tokio "full" features (from bashkit core) are unsupported on wasm32. Needs architectural fix to gate tokio features.

This PR is that fix: @everruns/bashkit now builds for wasm32-wasip1-threads, which makes the npm package runnable in browsers (napi-rs WASI runtime + Web Workers).

Changes

  • Cargo.toml: split the bashkit dependency by target. wasm32 drops four features, each for a concrete reason (documented inline):
    • interop/sqlite force tokio/rt-multi-thread → mio → socket2, which does not compile on any wasm target
    • http_client: reqwest needs tokio::net (real sockets) or its wasm-bindgen browser backend; neither exists on wasip1
    • realfs: mounting the host filesystem is meaningless in a browser sandbox
  • src/lib.rs: cfg-gate the interop fs-handle ABI, sqlite, and network code paths. Methods inside #[napi] impl blocks branch in the body instead of gating the item — napi's macro expands the whole block before cfg stripping and otherwise leaves dangling trampoline references. callback_runtime() uses tokio's current-thread runtime on wasm.
  • format_support.rs (bashkit core): OsStrExt lives in std::os::wasi::ffi on WASI targets, not std::os::unix::ffi; the old cfg(any(unix, target_os = "wasi")) import only compiled on unix.
  • package.json: add wasm32-wasip1-threads to napi targets (generates the browser/worker JS glue), asyncInit: true (the wasm binary exceeds Chromium's 8 MB sync-compile limit), and the @emnapi/core devDependency the napi CLI needs for wasm builds.
  • publish-js.yml: enable the wasm build matrix entry (the artifact upload paths were already wired for bashkit.*.wasm; with the target listed in package.json, napi prepublish expects the artifact, so these must land together).
  • js.yml: new PR-time debug build of the wasm target, so breakage surfaces in PR CI instead of at release time.

The test-js-wasi publish job stays commented: the build blocker is gone, but running the ava suite under NAPI_RS_FORCE_WASI=1 is unverified — noted in the updated comment.

Verification

  • napi build --target wasm32-wasip1-threads succeeds warning-free for the bashkit-js crate (one remaining warning comes from bashkit core and is fixed by the companion wasm32-unknown-unknown time PR; the two are otherwise independent)
  • native build unaffected: cargo check/clippy clean, ava suite passes
  • verified end-to-end: the built package runs a full interactive bash session in headless Chromium (echo/uname/cat/loops) behind COOP/COEP headers

Lifts the publish-js.yml TODO ("WASM disabled — tokio 'full' features are
unsupported on wasm32") by gating the features that force incompatible
deps onto wasm builds:

- Cargo.toml: split the bashkit dependency by target. wasm32 drops
  interop/sqlite (force tokio/rt-multi-thread -> mio -> socket2, which
  does not compile on wasm), http_client (reqwest needs tokio::net or a
  wasm-bindgen browser backend, neither exists on wasip1), and realfs
  (host filesystem is meaningless in a browser sandbox).
- src/lib.rs: cfg-gate the interop fs-handle ABI, sqlite, and network
  code paths. Methods inside #[napi] impl blocks branch in the body
  rather than gating the item — napi's macro expands the whole block
  before cfg stripping and leaves dangling trampoline callbacks
  otherwise. callback_runtime() uses the current-thread tokio runtime
  on wasm.
- format_support.rs: OsStrExt lives in std::os::wasi::ffi on WASI
  targets, not std::os::unix::ffi — the old cfg(any(unix, wasi)) import
  only worked on unix.
- package.json: add wasm32-wasip1-threads to napi targets (generates the
  browser/worker glue), asyncInit (the ~11MB wasm binary exceeds
  Chromium's 8MB sync-compile limit), and the @emnapi/core devDependency
  the napi cli requires for wasm builds.
- publish-js.yml: enable the wasm32-wasip1-threads build matrix entry.
- js.yml: add a PR-time debug build of the wasm target so it cannot rot
  between releases (publish-js.yml only exercises it at release time).

Verified locally: napi build --target wasm32-wasip1-threads succeeds
warning-free, and the built package runs a full interactive bash session
in headless Chromium (echo/uname/cat/loops) served with COOP/COEP
headers.

@chaliy chaliy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two inline findings from local verification against latest origin/main.

}
},
"devDependencies": {
"@emnapi/core": "1.10.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasm build path still fails in CI as submitted. Reproduced with the workflow's pinned pnpm@10.33.0: pnpm install --frozen-lockfile succeeds, then pnpm exec napi build --platform --target wasm32-wasip1-threads dies with Cannot find module '@emnapi/runtime'. The napi CLI resolves both @emnapi/core and @emnapi/runtime from the project root for WASI builds, so this needs a matching @emnapi/runtime: 1.10.0 devDependency and lockfile update.

"x86_64-pc-windows-msvc"
]
"x86_64-pc-windows-msvc",
"wasm32-wasip1-threads"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the wasm target makes the package's existing browser export (./bashkit.wasi-browser.js) active for browser consumers, but the generated file exports the raw napi bindings only. It does not expose the wrapper API advertised by wrapper.d.ts/README, such as FileSystem, Bash.create, or the wrapper custom-builtin adaptation. Please either route the browser condition through a wrapper that re-exports the public API, or expose this as a separate raw wasm entrypoint with matching types/docs.

napi build --target wasm32-wasip1-threads requires both @emnapi/core and
@emnapi/runtime from the project's scope (createRequire against the
project package.json). runtime is an *optional* peer of @napi-rs/cli, so
pnpm never auto-installs it, and @emnapi/core does not depend on it —
only @emnapi/wasi-threads. With only core declared, a clean frozen
install has no @emnapi/runtime at the root and the build dies in
Builder.setWasiEnv with "Cannot find module '@emnapi/runtime'".

Verified from the committed state: pnpm install --frozen-lockfile, then
napi build --platform --target wasm32-wasip1-threads succeeds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants