contributor-context
Contributor context for coding agents working in this repository. Human contributors should start with `README.md` and…
<!-- GENERATED from docs/api/threading.md by scripts/build-agent-skill.mjs. Do not edit. -->
$ npx -y skills add crossbind/crossbind --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
Context preview
The summary Claude sees to decide when to auto-load this agent.
<!-- GENERATED from docs/api/threading.md by scripts/build-agent-skill.mjs. Do not edit. -->
<!-- GENERATED from docs/api/threading.md by scripts/build-agent-skill.mjs. Do not edit. -->
> Two orthogonal axes: **threading** (single vs multi-thread Wasm) and **`useWorker`** (whether the Wasm module runs in a Web Worker). Don't confuse them. > > **`useWorker` is on by default in a browser** (the runtime enables it wherever `globalThis.Worker` exists), so browser code awaits its calls, construction included. Pass `useWorker: false` to opt out.
│ runtime: 'st' │ runtime: 'mt'
────────────────────┼──────────────────────┼─────────────────────────
useWorker: false │ Opt-out: main thread │ Wasm runs main-thread,
│ Wasm. Smallest setup.│ pthreads via SharedArray-
│ │ Buffer. Needs COOP/COEP.
────────────────────┼──────────────────────┼─────────────────────────
useWorker: true │ Wasm in 1 Web Worker.│ Wasm in 1 Web Worker;
│ Comlink bridge. Main │ pthreads spawn ADDITIONAL
│ thread free. Required│ workers from there. Needs
│ for OPFS persistence.│ COOP/COEP + Worker support.| You want | Pick | |----------|------| | Quickest path to "C++ in browser" | `runtime: 'st'`, no `useWorker` | | Persistent storage in browser | `runtime: 'st'`, `useWorker: true` | | CPU-bound parallelism (image / geo / crypto) | `runtime: 'mt'` | | Both: persistent storage AND parallelism | `runtime: 'mt'`, `useWorker: true` | | Cloudflare Worker / Deno Deploy / Vercel Edge | `runtime: 'st'` only — `mt` and `useWorker` not supported | | React Native | `runtime: 'mt'` if perf-sensitive (pthreads via JSI; no COOP/COEP needed, except on an Expo app's web build) |
In `crossbind.config.js`:
export default {
general: { name: 'myapp' },
paths: { config: import.meta.url },
target: { runtime: 'mt' }, // ← here
}Two things happen at build time:
1. The Wasm is compiled with `-pthread` (Emscripten flag). 2. Any transitive dependency that's already `mt` keeps the project on `mt`. Conversely, if any dep is `mt`, this project auto-promotes to `mt` (you can't downgrade).
Multi-threaded Wasm uses `SharedArrayBuffer`, which browsers gate behind **cross-origin isolation**. Your hosting layer must send these response headers:
Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp
Without them, `SharedArrayBuffer` is `undefined` and the Wasm init silently fails.
console.log(crossOriginIsolated) // must be true for `mt` console.log(typeof SharedArrayBuffer) // must be 'function'
| Host | Config | |------|--------| | Vite dev / preview | Auto-injected by `@crossbind/plugin-vite` | | Webpack / Rspack dev server | Auto-injected by `@crossbind/plugin-webpack` | | Vercel | Add to `vercel.json`: `{ "headers": [{ "source": "/(.*)", "headers": [{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }, { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }] }] }` | | Netlify | Add to `_headers`: `/*\n Cross-Origin-Opener-Policy: same-origin\n Cross-Origin-Embedder-Policy: require-corp` | | Cloudflare Pages | `_headers` file (same syntax as Netlify) | | nginx | `add_header Cross-Origin-Opener-Policy same-origin; add_header Cross-Origin-Embedder-Policy require-corp;` | | Express / Next.js custom server | Set headers via middleware on every response |
`require-corp` blocks cross-origin resources unless they explicitly opt in (`Cross-Origin-Resource-Policy: cross-origin` on the response, or `crossorigin` attribute on `<img>` / `<script>` tags). If your page loads third-party images / fonts / scripts, you'll either need to:
In Playwright's bundled WebKit, `navigator.storage.getDirectory()` exists but always rejects with `UnknownError: The operation failed for an unknown transient reason`. Threads themselves are fine there — pthreads, SharedArrayBuffer, wasm exceptions and SIMD all work (verified 2026-07-12 with per-worker instrumentation; real Safari 26.5 works fully, OPFS included). Before the runtime preflight existed this broke mt boots: the WASMFS OPFS proxy thread swallowed the rejection and `init` deadlocked with no console or page error. The runtime now probes `getDirectory()` before mounting and falls back to `/memfs` with a logged error, so mt modules boot on all three Playwright engines and the mt playground e2e specs run WebKit again. If you ever see an mt init hang with zero signals, suspect a blocked storage backend first — presence of the OPFS API does not mean it works.
Wasm runs in a single dedicated Web Worker; main thread receives a Comlink-bridged proxy.
const m = await initNative({ useWorker: true })
// m looks identical, but every call is async.
const result = await m.add(2, 3)You want this when:
You don't need it when:
| Aspect | Without worker | With worker | |--------|------------
Import C++ and Rust like JavaScript modules (WebAssembly & React Native)
Contributor context for coding agents working in this repository. Human contributors should start with `README.md` and…
<!-- GENERATED from docs/api/app.md by scripts/build-agent-skill.mjs. Do not edit. -->
<!-- GENERATED from docs/api/build-state.md by scripts/build-agent-skill.mjs. Do not edit. -->
<!-- GENERATED from docs/api/conan.md by scripts/build-agent-skill.mjs. Do not edit. -->
<!-- GENERATED from docs/api/cpp-binding-rules.md by scripts/build-agent-skill.mjs. Do not edit. -->