Skip to main content

cpp.js — Runtime & Config API Reference

For AI agents and humans alike. Every option, every default, every constraint — in one place. If you're integrating cpp.js into a project, start with init.md. If you're authoring a cppjs.config.js, see cppjs-config.md.

cpp.js has two API surfaces that get confused often. Keep them straight:

SurfaceWhenAuthored byDocumented in
initCppJs(opts)Runtime — at the moment your app calls into WasmEvery consumerinit.md
cppjs.config.jsBuild-time — read by the cppjs build CLIEvery consumercppjs-config.md
cppjs.build.jsBuild-time — only inside cppjs-package-* source foldersPackage authors onlycppjs-build.md

Cross-cutting topics:

  • filesystem.md — How files persist (or don't) across browser, Node, and edge runtimes. Covers OPFS, memfs, the useWorker requirement, and the auto-fallback chain.
  • threading.md — Single-thread vs multi-thread Wasm, the COOP/COEP requirement, why useWorker is a separate axis from threading, and what edge runtimes can't do.

C++ binding & build authoring:

  • cpp-binding-rules.md — Rules for writing C++ that cpp.js can auto-bind (no raw pointers, C++11+, wrapper pattern, JSPI advanced).
  • swig-escape.md — Manual SWIG .i files when auto-generation isn't enough.
  • build-state.mdstate and target object shapes passed to cppjs.build.js hooks; full inventory of 20 built-in build targets.
  • overrides.md — Catalog of 20 override mechanisms ordered least → most invasive.
  • troubleshooting.md — Common errors mapped to the right override; tribal-knowledge gotchas from real packages.
  • performance.md — Default Emscripten + CMake flags reference; what's safe to override and what to leave alone.
  • lifecycle-and-types.md — Why JS-side m.delete() isn't a thing in cpp.js + TypeScript .d.ts notes.

The 30-second mental model

┌─────────────────────────────────────────────────────────────┐
│ Build time: cppjs build CLI │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ cppjs.config.js │ + │ cppjs.build.js │ │
│ │ (consumer-side) │ │ (package author │ │
│ │ deps, paths, flags │ │ only — wraps a │ │
│ │ runtime: st|mt │ │ C++ library) │ │
│ └────────────────────┘ └────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ Emcc / NDK / Xcode produces .wasm / .a / .xcframework │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ Runtime: your app │
│ ┌────────────────────────────────────────────────────────┐│
│ │ const m = await initCppJs({ ││
│ │ useWorker: true, // for OPFS persistent storage ││
│ │ fs: { opfs: true }, // browser default ││
│ │ env: { ... }, ││
│ │ onRuntimeInitialized: (m) => {...}, ││
│ │ }) ││
│ │ // m.FS, m.toVector, m.autoMountFiles, ... ││
│ └────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

Common pitfalls (read these even if you skip the rest)

  1. cppjs.config.js is NOT runtime config. It's read once by the cppjs build CLI. Putting useWorker: true here does nothing — that's a runtime option for initCppJs(opts).
  2. OPFS persistent storage in browser requires useWorker: true. OPFS API is only exposed in Worker scope. Mounting /opfs/... from the main thread throws.
  3. runtime: 'mt' in production silently fails without COOP/COEP headers. Dev plugins inject them; prod hosts (Vercel, Netlify, nginx, Cloudflare Pages, …) need explicit configuration.
  4. Edge runtimes (Cloudflare Workers, Deno Deploy, Vercel Edge) don't support Web Workers. That means no useWorker, no OPFS, no multithread — only single-thread + in-memory fs.
  5. paths.native is an array. Not a string. fs.existsSync(paths.native) is a bug.

See also