Skip to content

feat: WebAssembly build and browser entry - #4

Merged
pkurcx merged 1 commit into
mainfrom
feat/wasi-build
Sep 3, 2026
Merged

feat: WebAssembly build and browser entry#4
pkurcx merged 1 commit into
mainfrom
feat/wasi-build

Conversation

@pkurcx

@pkurcx pkurcx commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Ships the generator as a wasm32-wasip1-threads build published as @avsystem/openapi-ng-wasm32-wasi, and turns browser.js into a working browser entry backed by it. Groundwork for a paste-a-spec playground on the docs site (follow-up PR).

Changes

  • Error boundary: the native export generateNative returns { result, error } instead of throwing. The JS wrappers construct GenerateError, so codes, subcodes, paths and warnings are identical on native and WASI (under emnapi the thrown-Error decoration was lost). build.rs no longer propagates the error marker.
  • WASI target: added to napi.targets; emnapi/@emnapi/* 1.11.3 pinned through the workspace overrides (next to the existing tar/glob pins); build outputs gitignored; the unsupported-platform message in native.js mentions the wasm fallback.
  • Browser entry: browser.js re-exports lib/browser.js: inputContents only (inputPath/outputPathE_INVALID_OPTION), lazy import() of the wasm package, E_UNSUPPORTED_RUNTIME when it cannot load. New typed ./browser subpath exporting createGenerate for hosts that serve the wasm files themselves.
  • Node WASI fallback: WASI has no host cwd, so relative inputPath/outputPath and the banner now resolve through PWD (src/io/host_cwd.rs); native behaviour unchanged by cfg.
  • CI: wasm build matrix entry; test-wasm-binding job runs the full ava suite with NAPI_RS_FORCE_WASI=true and a native run with a fail-if-skipped WASI-vs-native cross-check; publish depends on it.
  • Toolchain pin: napi 3.11.0, napi-derive 3.5.10 and napi-build 2.3.2 are the last releases that link against the emnapi 1.x archive. napi-build 2.4 exports the emnapi v2 entry points (emnapi_create_env), which emnapi 1.11 does not define; rustc 1.98's linker rejects the missing export where 1.95 ignored it. Lifting the pin means moving the JS runtime to emnapi 2 once it is stable.
  • Docs: Runtime & platforms and Node API pages describe the browser path and the two response headers it needs.

Testing

  • cargo test --all-targets: 304 passed. cargo clippy, oxlint clean.
  • pnpm test native: 333 passed.
  • NAPI_RS_FORCE_WASI=true NODE_OPTIONS=--no-warnings pnpm test: 333 passed against the WASI binding (verified via require.cache).
  • OPENAPI_NG_EXPECT_WASI_BINDING=1 pnpm test: cross-check tests run against both bindings and produce identical artifacts.
  • Browser: the WASI loader was exercised in Chrome behind COOP/COEP headers (init ~200 ms, generate ~10–100 ms).

Notes

  • NAPI_RS_FORCE_WASI=error must not be used in this repo: it also forces @oxc-node/core (ava's TS loader) onto a WASI build it does not ship.
  • wasm cannot unwind, so a Rust panic under WASI surfaces as E_UNEXPECTED with a different message; the panic-message assertion is native-only.
  • PWD is absent on Windows, so a Windows WASI fallback still needs absolute paths.
  • Pre-existing, untouched: GenerateError's declared constructor requires a full payload while the runtime accepts a partial one.

Release

Intended to ship as v0.5.0 after this merges; the playground PR depends on the wasm package being on npm.

🤖 Generated with Claude Code

Ship the generator as a wasm32-wasip1-threads build published as
@avsystem/openapi-ng-wasm32-wasi, and make browser.js a working entry
backed by it.

- The native export returns a { result, error } outcome instead of
  throwing, so typed GenerateErrors are identical on native and WASI;
  the JS wrappers construct the error.
- wasm32-wasip1-threads joins the napi targets; emnapi pinned via the
  workspace overrides; CI builds it, runs the suite forced to WASI plus
  a native run with a WASI cross-check, and gates publish on it.
- browser.js re-exports lib/browser.js (inputContents only, lazy import
  of the wasm package, E_UNSUPPORTED_RUNTIME on load failure); a typed
  ./browser subpath exposes createGenerate.
- Under the Node WASI fallback, relative paths and the banner resolve
  against the host cwd via PWD.
- napi, napi-derive and napi-build are pinned to the last releases that
  link against the emnapi 1.x archive: napi-build 2.4 exports the
  emnapi v2 entry points, and rustc 1.98's linker rejects the missing
  symbols instead of ignoring them.
- Runtime and Node API docs describe the browser path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
@pkurcx
pkurcx merged commit 77bb264 into main Sep 3, 2026
37 checks passed
@pkurcx
pkurcx deleted the feat/wasi-build branch September 3, 2026 16:30
pkurcx added a commit that referenced this pull request Sep 4, 2026
Adds an interactive playground to the docs site at /playground/: paste or
open an OpenAPI spec, edit the generator config as JSONC, and see the
generated files in the browser. It runs the wasm32-wasi build shipped in
#4, so the site needs no backend.

- Three CodeMirror panes (spec, config, output) with a file tree, an
  "Open file…" button and drag-and-drop, and a console footer with
  diagnostics coloured by severity.
- JSONC config with a template listing mappedTypes, responseTypeMapping
  and naming at their defaults; enabling all of them leaves the output
  unchanged, which an e2e test checks.
- Editors painted from Starlight's Night Owl palette; highlight.js is gone.
- A Docs / Playground switch in the header replaces the sidebar entry.
- website joins the root pnpm workspace; the docs workflow type-checks and
  runs the unit and Playwright suites.
- ava is scoped to __test__/**/*.spec.ts so it skips the website's tests.
- The StackBlitz demo app is tracked in stackblitz/ and linked from the
  README next to the playground.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
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.

1 participant