foundry-node-engine
v0.3.0
Published
FoundryNodeEngine — a clean-room, browser-native Node.js runtime: in-memory VFS, CJS/ESM script engine, ~53 Node polyfills, SharedArrayBuffer worker threading, an in-browser npm installer, a git/npm shell, and service-worker preview routing. The engine be
Downloads
847
Readme
FoundryNodeEngine is a browser-native Node.js runtime. It runs real Node.js code — plus a real in-browser npm install and a git client — entirely inside a browser tab, with no server:
- an in-memory virtual filesystem and a CJS/ESM script engine with on-the-fly TypeScript/JSX transforms (esbuild-wasm), live ESM bindings, export conditions and single-instance resolution;
- Node's standard library — real implementations of the core modules
(
fs,http/https,net,crypto,stream,events,buffer,util,os,path,url,zlib,vm,assert,worker_threads,child_process,wasi,ws,readline,module, …), plus a module registry that resolves the rest so anyrequire/importsucceeds; SharedArrayBuffer-backed threading —worker_threads, real spawned sub-processes, a synchronous cross-thread VFS + server bridge, and napi-rs WASM addons (emnapi / wasi-threads, e.g.@node-rs/argon2);- an in-browser npm installer (real registry, tarball-integrity checked,
native-binary packages auto-resolved to their
wasm32-wasibuilds) and a git client (clone/fetch over HTTP, packfiles, commit/diff); - an on-device build toolchain — esbuild, Rollup (
@rollup/browser) and Lightning CSS — so real framework dev servers and production builds run in the tab (Vite, Nuxt, React/Vue, Tailwind, SCSS); - a service-worker request proxy for live preview (HTTP + a WebSocket/HMR bridge), and a WebContainer-parity SDK (snapshot/restore, IndexedDB persistence, zip/json/binary export, an xterm terminal).
It is FlowFoundry's own clean-room implementation, written from a behavioural spec (not a fork of any Commons-Clause project). It powers Vibecode Studio in FlowFoundry Hub (the AI vibecode IDE) and is the live-preview runtime for the apps built there.
License at a glance: the source is public and free for noncommercial use (personal projects, study, research, evaluation) under the PolyForm Noncommercial 1.0.0 license. Commercial use requires a separate license — contact FlowFoundry. See License below.
Install
pnpm add foundry-node-engine
# optional, for in-browser TS/JSX transforms:
pnpm add esbuild-wasmQuick start
import { FoundryNodeEngine } from "foundry-node-engine";
// Must run in a cross-origin-isolated context (COEP: require-corp + COOP:
// same-origin) so SharedArrayBuffer is available — boot() throws loudly otherwise.
const fp = await FoundryNodeEngine.boot({
files: {
"/app/server.js": `require('http').createServer((_, res) => res.end('hi')).listen(3000);`,
},
});
await fp.install(["left-pad"]); // in-browser npm install
fp.run("/app/server.js"); // start a server in the runtime
const res = await fp.request(3000, { path: "/" }); // hit it through the proxy
await fp.save("my-workspace"); // persist (IndexedDB + LRU)Run a framework dev server
Install Vite + React, spawn the dev server in its own worker, and preview it live (with HMR), all in the tab:
import { FoundryNodeEngine, createInlineWorker } from "foundry-node-engine";
const fp = await FoundryNodeEngine.boot({
swUrl: "/__sw__.js",
files: viteProjectFiles,
engineWorkerFactory: () => createInlineWorker(engineWorkerSrc), // required for spawn
});
await fp.install(["vite", "@vitejs/plugin-react", "react", "react-dom"]);
// The dev server runs off the main thread; its listen() is bridged to the preview.
fp.spawn("node", ["/app/dev.js"], { env: esbuildEnv });
fp.on("server-ready", (port, url) => { iframe.src = url; });See docs/api.md §5
for the full wiring (the SW proxy + WS/HMR bridge, esbuild-wasm env). Production
builds (vite build, Nuxt) run the same way.
SDK surface
FoundryNodeEngine.boot({ files, workdir, env, swUrl, onServerReady, allowedFetchDomains, registry, fetchImpl, engineWorkerFactory, workerThreadFactory, previewScript, snapshotStore, snapshot })
fp.fs.*— read/write the VFSfp.run(entry)— run an entry in the runtime engine, in-process (simple long-running servers)fp.spawn('node', [args])— a real sub-process (worker + shared VFS) →ProcessHandle; a server it hosts is exposed to the preview via the cross-thread bridges (how framework dev servers run off the main thread)fp.install(['pkg'], { dev })— in-browser npm installfp.request(port, { path, … })/fp.port()/fp.ports()— preview routingfp.snapshot()/fp.restore(snap)— the portable workspace formatfp.save(key)/fp.load(key)— IndexedDB + LRU persistencefp.createTerminal({ prompt, history })— an xterm-bindable terminal session;term.registerCommand(name, handler)adds your own commands (async-capable — they canawaitafetch/fp.request, e.g. aping)fp.export({ format: 'json'|'binary'|'zip' })·fp.reloadPreview(iframe)·fp.deliverPreviewMessage(data)— WebContainer-parity surface (P14d)- events:
output·error·server-ready·port·preview-message·exit
Lower-level building blocks (MemoryVolume, ScriptEngine, Shell,
ProcessManager, RequestProxy, the polyfills, the git/npm engine, the
isolation header helpers) are all exported from the package root.
Environment variables & .env
process.env (and the shell env) comes from the env boot option, merged over a
few defaults (NODE_ENV, PATH, HOME):
await FoundryNodeEngine.boot({ files, env: { API_BASE: "https://api.example.com" } });.env files are not loaded automatically (no --env-file / loadEnvFile).
Use them the normal way: server-side install dotenv and
require('dotenv').config({ path: '/app/.env', override: true }) (it reads the VFS
and writes process.env — override: true so it can replace the pre-seeded
defaults); client-side Vite reads .env from the project root itself and
exposes VITE_-prefixed keys as import.meta.env. See
docs/api.md § Environment variables
and the runnable env-express / env-react / env-vue / env-svelte examples.
Requirements
- A cross-origin-isolated document (the COEP + COOP headers below) so
SharedArrayBufferis available;boot()throws loudly otherwise. - A modern browser; Chromium-based is recommended (some previews, e.g. WebGL and certain WASM features, work best there).
- Node 20+ for the build/test tooling.
esbuild-wasmis an optional peer, add it for in-browser TypeScript/JSX transforms.
Cross-origin isolation
The runtime's synchronous cross-thread machinery needs SharedArrayBuffer,
which the browser only grants a cross-origin-isolated document. Serve the
hosting route with:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-originThe package exports studioHeaderRules, ISOLATION_HEADERS, the
canUseSyncChannel() guard, the serveFoundryNodeEngine() fetch-server, and a Vite
plugin to wire this up.
Build
pnpm build # tsup → dist/ (ESM + .d.ts)
pnpm test # vitest
pnpm typecheck # tsc --noEmitDocumentation & examples
- docs/api.md — full API reference: every method, boot option, event, the WebContainer-parity API (P14d), and the framework dev-server (worker-spawn) workflow.
- docs/commands.md — shell command reference: every built-in command (coreutils +
node/npm/git) with a one-line description. - docs/how-it-works.md — architecture (VFS, engine, threading, shell, npm, preview).
- docs/service-worker-setup.md — serving
/__sw__.js+ cross-origin isolation (Vite / Next / custom) + the page↔SW preview bridge. - docs/CHANGELOG.md — recent engine work (UI-framework matrix + styling pipelines, terminal networking, dynamic-
import()interop; Nuxt bring-up, in-browser build pipeline, napi-rs WASM addons, cross-thread hardening). The examples/ directory is a runnable, branded browser gallery of 35 demos:
| Category | Examples |
|---|---|
| Core runtime | basic · terminal (xterm, with a ping command + a spawned Express worker) · custom-commands · npm-install · git (clean-room git on the VFS) · typescript (real .ts, no build step) · napi-argon2 (Rust→WASM napi-rs addon) · worker-threads · multi-boot · child-process · process-stream |
| Servers & networking | express · http-preview · sse-stream · ws-echo |
| Frameworks & build | react / vue / svelte (Vite 7 dev + HMR) · react-vite-tailwind-radix-shadcn (Tailwind 3 + real Radix UI + shadcn/ui) · react-vite-material-ui (Material UI + Emotion) · nuxt (Nuxt 4, Nitro + Vite, built & served) · three-app (live WebGL) · scss · hmr · vite-build (production build) |
| Configuration & .env | env-express (dotenv + dotenv-expand → process.env) · env-react / env-vue / env-svelte (Vite .env → import.meta.env) |
| Full-stack & data | html+express+sqlite · vite+react+express+sqlite / vite+vue+express+sqlite (front-end + Express/SQLite across two worker threads) · sqlite-wasm · snapshot-persistence · workspace-export |
Every preview example shares the same browser chrome: a fullscreen toggle (maximize the iframe to fill the viewport, Esc or the button to return) and a black pre-load background so there's no white flash before the app paints.
Build the bundle, then serve the gallery:
pnpm build && node examples/serve.js # → http://localhost:4400License
PolyForm Noncommercial License 1.0.0 — © FlowFoundry.
The source is public: you may read, download, modify, and use it for any noncommercial purpose (personal projects, study, research, evaluation, nonprofit/educational use). Any commercial use requires a separate commercial license from FlowFoundry — reach out to arrange one.
