Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

Run a portable reducer in a browser

This guide builds the core WebAssembly adapter and runs the standalone reducer in a dedicated browser worker. It does not depend on the interactive app stack.

Prerequisites

  • the repository setup from make setup
  • a current Node.js installation
  • a current Chrome installation for the automated worker test

The Make targets install pinned wasm-pack and wasm-tools binaries plus the Rust Wasm target outside the checkout. Mutable Cargo and Wasm outputs remain worktree-local.

Build and verify

From the repository root:

make wasm-check

This validates the WIT projection, builds the release wasm32-unknown-unknown module, rejects unreviewed imports, and runs the conformance corpus in a real headless Chrome dedicated worker. It does not substitute Node tests for the browser path.

Open the reducer

Start the cross-platform demo server:

make wasm-demo

Open http://127.0.0.1:8765. The page loads demo/worker.js; the worker loads the generated ES module, compiles demo/reducer.harn, and retains reducer state between typed events. “Restore saved state” sends a structured clone back to the worker to demonstrate serializable application state.

The page owns HTML, controls, and presentation. Harn owns reducer policy. The Wasm kernel owns deterministic execution. No DOM or canvas object crosses the worker boundary.

The adapter does not require SharedArrayBuffer or Wasm threads. For parallel dispatch, create multiple dedicated workers, reuse the same artifact bytes, and keep each worker's reducer state independent. Do not run CPU-heavy transitions on the browser main thread.

Use the generated adapter

The worker path is intentionally small:

import init, { compile, start } from "../pkg/harn_wasm.js"

await init()
const compiled = compile(source, "reduce", "function")
if (!compiled.ok) throw new Error(compiled.diagnosticsJson())

const result = start(
  compiled.artifactBytes(),
  JSON.stringify({ state, event }),
  "[]",
)

if (result.status === "completed") {
  state = JSON.parse(result.valueJson())
}

Do not silently ignore suspended or failed. A privileged reducer must pass an exact grant document with a host-generated snapshot key, retain the returned snapshot bytes, perform the typed request outside Wasm, and call resume with the matching result.

For native and browser latency measurements, receipt fields, and a recorded worker procedure, see Benchmark the portable kernel.