Skip to content

ScelarOrg/Nodepod

Repository files navigation

nodepod

npm version license

Browser-native Node.js runtime. Run real Node.js code — filesystem, modules, require(), npm packages, HTTP servers — entirely inside the browser.

No backend. No containers. No WASM Node binary. Just polyfills, an in-memory filesystem, and a JavaScript execution engine.

Built by @R1ck404 — powering Scelar, the AI app builder that actually COMPLETELY builds your apps, from idea to production in a matter of minutes.

Features

  • Virtual Filesystem — Full in-memory fs API (read, write, watch, streams, symlinks, glob)
  • Module Systemrequire(), import, module.exports, package.json resolution, conditional exports
  • npm Packages — Install real packages from the npm registry, extracted and resolved in-browser
  • HTTP Servers — Run Express, Hono, Elysia, vite and other frameworks with real request/response routing
  • Shell — Built-in bash-like shell with 35+ commands (ls, cat, grep, find, sed, etc.), pipes, redirections, variable expansion
  • Process Model — Web Worker-based processes with child_process.exec/spawn/fork, worker_threads, and IPC
  • Terminal — Drop-in xterm.js integration with line editing, history, raw mode, Ctrl+C
  • Preview — Service Worker-based iframe preview with script injection and WebSocket bridging
  • Snapshots — Save and restore the entire filesystem state

Install

npm install @scelar/nodepod

Quick Start

import { Nodepod } from '@scelar/nodepod';

// Boot a nodepod instance with some files
const nodepod = await Nodepod.boot({
  files: {
    '/index.js': 'console.log("Hello from the browser!")',
  },
});

// Run a script
const proc = await nodepod.spawn('node', ['index.js']);
proc.on('output', (text) => console.log(text));
await proc.completion;

// Read/write files
await nodepod.fs.writeFile('/data.json', JSON.stringify({ hello: 'world' }));
const content = await nodepod.fs.readFile('/data.json', 'utf8');

Terminal Integration

nodepod provides built-in xterm.js terminal support:

import { Nodepod } from '@scelar/nodepod';
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';

const nodepod = await Nodepod.boot();
const terminal = nodepod.createTerminal({ Terminal, FitAddon });
terminal.attach('#terminal-container');

The terminal handles line editing, command history, prompt rendering, raw/cooked mode, and streaming output out of the box.

Running an Express Server

const nodepod = await Nodepod.boot({
  files: {
    '/server.js': `
      const express = require('express');
      const app = express();
      app.get('/', (req, res) => res.json({ status: 'ok' }));
      app.listen(3000, () => console.log('Server running on port 3000'));
    `,
  },
});

// Install express
await nodepod.install(['express']);

// Run the server
const proc = await nodepod.spawn('node', ['server.js']);

// Dispatch requests to the virtual server
const response = await nodepod.request(3000, 'GET', '/');
console.log(response.body); // { status: 'ok' }

Preview Script Injection

Inject scripts into preview iframes before any page content loads — useful for setting up bridges between the main window and the iframe:

await nodepod.setPreviewScript(`
  window.__bridge = {
    sendToParent(msg) { window.parent.postMessage(msg, '*'); }
  };
`);

// Remove it later
await nodepod.clearPreviewScript();

SDK API

Nodepod.boot(options?)

Creates a fully wired nodepod instance.

Option Type Default Description
files Record<string, string> Initial files to populate the filesystem
workdir string "/" Working directory
env Record<string, string> Environment variables
swUrl string Service Worker URL (enables preview iframes)
watermark boolean true Show a small "nodepod" badge in preview iframes
onServerReady (port, url) => void Callback when a virtual server starts listening
allowedFetchDomains string[] | null npm/github defaults Extra domains allowed through the CORS proxy. Pass null to allow all

Instance Methods

Method Description
spawn(cmd, args?) Run a command, returns NodepodProcess
createTerminal(opts) Create an interactive terminal
install(packages) Install npm packages
fs.readFile(path, enc?) Read a file
fs.writeFile(path, data) Write a file
fs.readdir(path) List directory contents
fs.stat(path) Get file stats
fs.mkdir(path, opts?) Create a directory
fs.rm(path, opts?) Remove files/directories
snapshot() Capture filesystem state
restore(snapshot) Restore filesystem from snapshot
setPreviewScript(script) Inject JS into preview iframes
clearPreviewScript() Remove injected preview script
port(num) Get preview URL for a virtual server port

NodepodProcess

Returned by spawn(). An EventEmitter with:

Event Payload Description
output string stdout data
error string stderr data
exit number exit code

Property: completion — a Promise<void> that resolves when the process exits.

Security

nodepod includes several security measures for running untrusted code:

  • CORS proxy domain whitelist — Proxied fetch requests only go through to whitelisted domains (npm registry, GitHub, esm.sh, etc by default). Extend via the allowedFetchDomains boot option or pass null to disable
  • Service Worker auth — Control messages to the SW require a random token generated at boot, so other scripts on the same origin can't inject preview content
  • WebSocket bridge auth — The BroadcastChannel used for WS bridging between preview iframes and the main thread is token-authenticated
  • Package integrity — Downloaded npm tarballs are checked against the registry's shasum before extraction
  • Iframe sandbox — The cross-origin iframe mode uses sandbox="allow-scripts" to prevent top-frame navigation, popups, and form submissions
  • Origin-checked messaging — The sandbox page validates event.origin on incoming messages and only responds to the configured parent origin

Architecture

nodepod
├── src/
│   ├── script-engine.ts       # JavaScript execution engine (require, ESM→CJS, REPL)
│   ├── memory-volume.ts       # In-memory virtual filesystem
│   ├── syntax-transforms.ts   # ESM-to-CJS conversion via acorn
│   ├── module-transformer.ts  # esbuild-wasm code transforms
│   ├── polyfills/             # Node.js built-in module polyfills
│   │   ├── fs.ts              #   Filesystem (read, write, watch, streams, glob)
│   │   ├── http.ts            #   HTTP server/client
│   │   ├── stream.ts          #   Readable, Writable, Transform, Duplex
│   │   ├── events.ts          #   EventEmitter
│   │   ├── path.ts            #   Path operations
│   │   ├── crypto.ts          #   Hashing, randomBytes, randomUUID
│   │   ├── child_process.ts   #   exec, spawn, fork, execSync
│   │   ├── net.ts             #   TCP Socket, Server
│   │   └── ...                #   40+ more polyfills
│   ├── shell/                 # Bash-like shell interpreter
│   │   ├── shell-parser.ts    #   Tokenizer + recursive-descent parser
│   │   ├── shell-builtins.ts  #   35+ built-in commands
│   │   └── shell-interpreter.ts # AST executor, pipes, redirections
│   ├── packages/              # npm package management
│   │   ├── installer.ts       #   Package installer
│   │   ├── registry-client.ts #   npm registry client
│   │   └── version-resolver.ts #  Semver resolution
│   ├── threading/             # Worker-based process model
│   │   ├── process-manager.ts #   Process lifecycle management
│   │   └── process-handle.ts  #   Process I/O handle
│   └── sdk/                   # Public SDK layer
│       ├── nodepod.ts          #   Nodepod.boot() entry point
│       ├── nodepod-fs.ts       #   Async filesystem facade
│       ├── nodepod-process.ts  #   Process handle
│       └── nodepod-terminal.ts #   xterm.js terminal integration
└── static/
    └── __sw__.js              # Service Worker for HTTP request interception

Supported Node.js Modules

Full implementations: fs, path, events, stream, buffer, process, http, https, net, crypto, zlib, url, querystring, util, os, tty, child_process, assert, readline, module, timers, string_decoder, perf_hooks, constants, punycode

Shims/stubs: dns, worker_threads, vm, v8, tls, dgram, cluster, http2, inspector, domain, diagnostics_channel, async_hooks

CDN Usage

Once published to npm, nodepod is automatically available on CDNs:

<!-- unpkg -->
<script src="https://unpkg.com/@scelar/nodepod"></script>

<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@scelar/nodepod"></script>

Development

git clone https://github.com/ScelarOrg/Nodepod.git
cd Nodepod
npm install
npm run type-check    # TypeScript validation
npm run build:lib     # Build ESM + CJS bundles
npm test              # Run tests

Contributing

See CONTRIBUTING.md for setup instructions and guidelines.

Author

Created by @R1ck404. Part of the Scelar ecosystem.

License

MIT + Commons Clause

This project uses the MIT license with the Commons Clause restriction. In plain terms:

  • Use it freely in your own projects, including commercial ones
  • Modify it, fork it, learn from it
  • Ship products built with nodepod — that's totally fine
  • Don't resell nodepod itself as a standalone product or hosted service

Basically, build whatever you want with it — just don't take nodepod, rebrand it, and sell it as your own thing.

About

Node.js for inside the browser. An open-source, lightweight WebContainer alternative.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors