test(turbo-tasks): run the wasm tests with a Node wasi host (#97789)
### What?
Adds a Node host so `wasm32-wasip1-threads` test binaries can run, and
wires the `turbo-rcstr`, `turbo-tasks`, and `turbo-tasks-backend`
library tests into the WASI CI job.
It also provides the WASI allocation symbols expected by the `lz4-sys`
dependency introduced by the base PR, allowing those test binaries to
link.
### Why?
The Rust test binaries require host functionality that stock WASI
runners do not provide. Turbopack stores its link-time task registries
in WebAssembly custom sections and imports a host callback to read them.
Its multithreaded runtime also requires shared memory and thread
spawning through Node Workers.
The base PR replaces `lzzzz` with `lz4`. `lz4-sys 1.11.1` compiles its C
sources against custom allocation shims on `wasm32-wasi*`, but excludes
the Rust implementations when the target OS is WASI. Without equivalent
symbols supplied by the consuming crate, test links fail on unresolved
malloc, calloc, and free shims.
Running these suites in CI gives the remaining WASI layers direct
regression coverage instead of relying on native-only tests.
### How?
The Node cargo target runner provides WASI preview1, imported shared
memory, and the complete two-phase [`link-section` custom-section
protocol](https://github.com/mmastrac/linktime/blob/ae29e51d94a955df2442ed6418b8a712c1f2bfb3/link-section/docs/PREAMBLE.md#wasm).
[`@emnapi/wasi-threads`](https://github.com/toyobayashi/emnapi/tree/main/packages/wasi-threads)
supplies Worker lifecycle, compiled-module transfer, load/start
ordering, and cleanup for the
[`wasi.thread-spawn`](https://github.com/WebAssembly/wasi-threads)
import that Node's WASI runtime does not provide. The module is compiled
once, then the structured-cloneable `WebAssembly.Module` and shared
memory are sent to Workers. A small adapter around the package's
lower-level `ThreadManager` and `ThreadMessageHandler` APIs preserves
Rust's original one-argument ABI: the high-level `WASIThreads` wrapper
requires guest `malloc`/`free` exports for that ABI, and Rust test
binaries do not export them. Worker identifiers come from a shared
atomic counter because Tokio Workers can spawn additional Workers while
the main WebAssembly thread is parked.
On WASI, `turbo-persistence` supplies the three allocation symbols
required by liblz4 and forwards them to WASI libc, keeping allocation
and deallocation within the same allocator. Native builds do not compile
these wrappers.
Tests that depend on unavailable platform behavior remain visible as
WASI-only ignored tests with explicit reasons. Native runs keep the full
test set enabled. A following stacked PR replaces the unsupported
`parking_lot` parker and re-enables the corresponding contention tests.
The workflow installs and builds the workspace before running the host
because both the package dependency and compiled production support used
by a later stack layer must be available.
### Verification
- Node 20 host unit tests: 9/9
- Exact combined WASI CI command: `turbo-rcstr` 12 passed / 1 ignored;
`turbo-tasks` 68 / 44; `turbo-tasks-backend` 65 / 35
- Full 111-test `turbo-tasks` binary through the package-managed host:
95 passed / 16 ignored
- No Worker-side `WebAssembly.compile`; the compiled module is
transferred by `ThreadManager`
- `pnpm install --lockfile-only --offline` with a frozen lockfile
<!-- NEXT_JS_LLM -->
<!-- fleet b6d0486f-97c7-42a7-bdaf-3490774cdec3 -->
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>