Filesystem
C++ wants paths. What a path means depends on where the module runs: real disk in Node, an origin-private store or plain memory in the browser, per-invocation memory on the edge. This page maps every combination, including how a file from an <input> element becomes a path your C++ can open.
The two browser roots
| Mount | Backed by | Survives a reload | Available when |
|---|---|---|---|
/opfs/<app>/ | the Origin Private File System | yes | useWorker: true, fs.opfs not disabled, browser support |
/memfs/<app>/ | in-memory | no - tab session only | always |
<app> is general.name from cppjs.config.js. OPFS is a Worker-scope-only API, which is the single most important consequence on this page:
/opfs/... from the main thread throws. If the browser has no OPFS support - or the backend is blocked - the path is redirected to /memfs/ and the reason is logged, so writes keep working but stop persisting.Module helpers
| Helper | Does |
|---|---|
m.FS | the standard virtual filesystem: mkdirTree, writeFile, readFile, … |
m.getDefaultPath() | returns /opfs or /memfs for the current configuration |
m.getFinalPath(path) | validates a path, falling back when OPFS is unavailable |
m.getRandomPath(startPath?) | creates <start>/<app>/automounted/<random> and returns it |
m.autoMountFiles(files, parentPath?) | streams File[] into the filesystem, returns the mounted paths |
m.getFileBytes(path) | file contents as a Uint8Array |
m.getFileList(startPath?) | recursive listing as [{ path, size }] |
initNative(...) promise (or use the onRuntimeInitialized hook) before touching it.Files the user picked
A File from an <input type=file> never has a path your C++ can open. autoMountFiles streams it in and hands back paths that do - no size limit beyond storage, so multi-gigabyte inputs are fine.
Pass a second argument to mount into a known directory instead of a random one: await m.autoMountFiles(files, '/opfs/myapp/uploads').
Getting results back to JavaScript
C++ writes to a path; JavaScript reads the bytes and does something browser-shaped with them.
Per-runtime cheat sheet
| Runtime | /opfs/... | /memfs/... | Notes |
|---|---|---|---|
| Browser, no worker | throws | yes | tab-session memory only |
Browser + useWorker: true | yes, or falls back to /memfs/ | yes | the persistent option |
| Node.js | n/a | n/a | m.FS reads and writes real disk |
| Cloudflare Workers / edge | n/a | yes | per-invocation memory, no persistence |
| React Native | n/a | n/a | the app sandbox, through the platform APIs |
Pitfalls
- Mounting `/opfs` without a worker throws inside
getFinalPath(). Either setuseWorker: trueor write under/memfs/. - Setting `fs: { opfs: false }` and then using `/opfs/...` throws too - it is disabled, not missing.
- Dropping the `<app>` segment. Writes to
/memfs/foowork but sit outside the tree that gets cleaned up on terminate. - Expecting OPFS to cross origins. It does not: files written by one origin are invisible to another.
- Needing durable files on the edge. There is no persistent store there; read bytes from R2/KV/S3 and write them in.