[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-75033":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":14,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":24,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":28,"readmeContent":29,"aiSummary":30,"trendingCount":16,"starSnapshotCount":16,"syncStatus":31,"lastSyncTime":32,"discoverSource":33},75033,"almostnode","macaly\u002Falmostnode","macaly","Node.js in your browser. Just like that.","http:\u002F\u002Falmostnode.dev\u002F",null,"TypeScript",1104,100,4,8,0,6,28,12,67.81,"MIT License",false,"main",true,[26,27],"nodejs","webcontainers","2026-06-12 04:01:17","# almostnode\n\n**Node.js in your browser. Just like that.**\n\nA lightweight, browser-native Node.js runtime environment. Run Node.js code, install npm packages, and develop with Vite or Next.js - all without a server.\n\n[![MIT License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-blue.svg)](LICENSE)\n[![TypeScript](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTypeScript-5.0-blue.svg)](https:\u002F\u002Fwww.typescriptlang.org\u002F)\n[![Node.js](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FNode.js-%3E%3D20-green.svg)](https:\u002F\u002Fnodejs.org\u002F)\n\nBuilt by the creators of [Macaly.com](https:\u002F\u002Fmacaly.com) — a tool that lets anyone build websites and web apps, even without coding experience. Think Claude Code for non-developers.\n\n> **Warning:** This project is experimental and may contain bugs. Use with caution in production environments.\n\n---\n\n## Features\n\n- **Virtual File System** - Full in-memory filesystem with Node.js-compatible API\n- **Node.js API Shims** - 40+ shimmed modules (`fs`, `path`, `http`, `events`, and more)\n- **npm Package Installation** - Install and run real npm packages in the browser with automatic bin stub creation\n- **Run Any CLI Tool** - npm packages with `bin` entries (vitest, eslint, tsc, etc.) work automatically\n- **Dev Servers** - Built-in Vite and Next.js development servers\n- **Hot Module Replacement** - React Refresh support for instant updates\n- **TypeScript Support** - First-class TypeScript\u002FTSX transformation via esbuild-wasm\n- **Service Worker Architecture** - Intercepts requests for seamless dev experience\n- **Optional Web Worker Support** - Offload code execution to a Web Worker for improved UI responsiveness\n- **Secure by Default** - Cross-origin sandbox support for running untrusted code safely\n\n---\n\n## Requirements\n\n- **Node.js 20+** - Required for development and building\n- **Modern browser** - Chrome, Firefox, Safari, or Edge with ES2020+ support\n\n> **Note:** almostnode runs in the browser and emulates Node.js 20 APIs. The Node.js requirement is only for development tooling (Vite, Vitest, TypeScript).\n\n---\n\n## Quick Start\n\n### Installation\n\n```bash\nnpm install almostnode\n```\n\n### Basic Usage\n\n```typescript\nimport { createContainer } from 'almostnode';\n\n\u002F\u002F Create a Node.js container in the browser\nconst container = createContainer();\n\n\u002F\u002F Execute JavaScript code directly\nconst result = container.execute(`\n  const path = require('path');\n  const fs = require('fs');\n\n  \u002F\u002F Use Node.js APIs in the browser!\n  fs.writeFileSync('\u002Fhello.txt', 'Hello from the browser!');\n  module.exports = fs.readFileSync('\u002Fhello.txt', 'utf8');\n`);\n\nconsole.log(result.exports); \u002F\u002F \"Hello from the browser!\"\n```\n\n> **⚠️ Security Warning:** The example above runs code on the main thread with full access to your page. **Do not use `createContainer()` or `container.execute()` with untrusted code.** For untrusted code, use `createRuntime()` with a cross-origin sandbox - see [Sandbox Setup](#sandbox-setup).\n\n### Running Untrusted Code Securely\n\n```typescript\nimport { createRuntime, VirtualFS } from 'almostnode';\n\nconst vfs = new VirtualFS();\n\n\u002F\u002F Create a secure runtime with cross-origin isolation\nconst runtime = await createRuntime(vfs, {\n  sandbox: 'https:\u002F\u002Fyour-sandbox.vercel.app', \u002F\u002F Deploy with generateSandboxFiles()\n});\n\n\u002F\u002F Now it's safe to run untrusted code\nconst result = await runtime.execute(untrustedCode);\n```\n\nSee [Sandbox Setup](#sandbox-setup) for deployment instructions.\n\n### Working with Virtual File System\n\n```typescript\nimport { createContainer } from 'almostnode';\n\nconst container = createContainer();\nconst { vfs } = container;\n\n\u002F\u002F Pre-populate the virtual filesystem\nvfs.writeFileSync('\u002Fsrc\u002Findex.js', `\n  const data = require('.\u002Fdata.json');\n  console.log('Users:', data.users.length);\n  module.exports = data;\n`);\n\nvfs.writeFileSync('\u002Fsrc\u002Fdata.json', JSON.stringify({\n  users: [{ name: 'Alice' }, { name: 'Bob' }]\n}));\n\n\u002F\u002F Run from the virtual filesystem\nconst result = container.runFile('\u002Fsrc\u002Findex.js');\n```\n\n### With npm Packages\n\n```typescript\nimport { createContainer } from 'almostnode';\n\nconst container = createContainer();\n\n\u002F\u002F Install a package\nawait container.npm.install('lodash');\n\n\u002F\u002F Use it in your code\ncontainer.execute(`\n  const _ = require('lodash');\n  console.log(_.capitalize('hello world'));\n`);\n\u002F\u002F Output: Hello world\n```\n\n### Running Shell Commands\n\n```typescript\nimport { createContainer } from 'almostnode';\n\nconst container = createContainer();\n\n\u002F\u002F Write a package.json with scripts\ncontainer.vfs.writeFileSync('\u002Fpackage.json', JSON.stringify({\n  name: 'my-app',\n  scripts: {\n    build: 'echo Building...',\n    test: 'vitest run'\n  }\n}));\n\n\u002F\u002F Run shell commands directly\nconst result = await container.run('npm run build');\nconsole.log(result.stdout); \u002F\u002F \"Building...\"\n\nawait container.run('npm test');\nawait container.run('echo hello && echo world');\nawait container.run('ls \u002F');\n```\n\nSupported npm commands: `npm run \u003Cscript>`, `npm start`, `npm test`, `npm install`, `npm ls`.\nPre\u002Fpost lifecycle scripts (`prebuild`, `postbuild`, etc.) run automatically.\n\n### Running CLI Tools\n\nAny npm package with a `bin` field works automatically after install — no configuration needed.\n\n```typescript\n\u002F\u002F Install a package that includes a CLI tool\nawait container.npm.install('vitest');\n\n\u002F\u002F Run it directly — bin stubs are created in \u002Fnode_modules\u002F.bin\u002F\nconst result = await container.run('vitest run');\nconsole.log(result.stdout); \u002F\u002F Test results\n```\n\nThis works because `npm install` reads each package's `bin` field and creates executable scripts in `\u002Fnode_modules\u002F.bin\u002F`. The shell's PATH includes `\u002Fnode_modules\u002F.bin`, so tools like `vitest`, `eslint`, `tsc`, etc. resolve automatically.\n\n### Streaming Output & Long-Running Commands\n\nFor commands that run continuously (like watch mode), use streaming callbacks and abort signals:\n\n```typescript\nconst controller = new AbortController();\n\nawait container.run('vitest --watch', {\n  onStdout: (data) => console.log(data),\n  onStderr: (data) => console.error(data),\n  signal: controller.signal,\n});\n\n\u002F\u002F Send input to the running process\ncontainer.sendInput('a'); \u002F\u002F Press 'a' to re-run all tests\n\n\u002F\u002F Stop the command\ncontroller.abort();\n```\n\n### With Next.js Dev Server\n\n```typescript\nimport { VirtualFS, NextDevServer, getServerBridge } from 'almostnode';\n\nconst vfs = new VirtualFS();\n\n\u002F\u002F Create a Next.js page\nvfs.mkdirSync('\u002Fpages', { recursive: true });\nvfs.writeFileSync('\u002Fpages\u002Findex.jsx', `\n  import { useState } from 'react';\n\n  export default function Home() {\n    const [count, setCount] = useState(0);\n    return (\n      \u003Cdiv>\n        \u003Ch1>Count: {count}\u003C\u002Fh1>\n        \u003Cbutton onClick={() => setCount(c => c + 1)}>+\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    );\n  }\n`);\n\n\u002F\u002F Start the dev server\nconst server = new NextDevServer(vfs, { port: 3000 });\nconst bridge = getServerBridge();\nawait bridge.initServiceWorker();\nbridge.registerServer(server, 3000);\n\n\u002F\u002F Access at: \u002F__virtual__\u002F3000\u002F\n```\n\n---\n\n## Service Worker Setup\n\nalmostnode uses a Service Worker to intercept HTTP requests and route them to virtual dev servers (e.g., `ViteDevServer`, `NextDevServer`).\n\n> **Note:** The service worker is only needed if you're using dev servers with URL access (e.g., `\u002F__virtual__\u002F3000\u002F`). If you're only executing code with `runtime.execute()`, you don't need the service worker.\n\n### Which Setup Do I Need?\n\n| Use Case | Setup Required |\n|----------|----------------|\n| Cross-origin sandbox (recommended for untrusted code) | `generateSandboxFiles()` - includes everything |\n| Same-origin with Vite | `almostnodePlugin` from `almostnode\u002Fvite` |\n| Same-origin with Next.js | `getServiceWorkerContent` from `almostnode\u002Fnext` |\n| Same-origin with other frameworks | Manual copy to public directory |\n\n---\n\n### Option 1: Cross-Origin Sandbox (Recommended)\n\nWhen using `createRuntime()` with a cross-origin `sandbox` URL, the service worker must be deployed **with the sandbox**, not your main app.\n\nThe `generateSandboxFiles()` helper generates all required files:\n\n```typescript\nimport { generateSandboxFiles } from 'almostnode';\nimport fs from 'fs';\n\nconst files = generateSandboxFiles();\n\n\u002F\u002F Creates: index.html, vercel.json, __sw__.js\nfs.mkdirSync('sandbox', { recursive: true });\nfor (const [filename, content] of Object.entries(files)) {\n  fs.writeFileSync(`sandbox\u002F${filename}`, content);\n}\n\n\u002F\u002F Deploy to a different origin:\n\u002F\u002F cd sandbox && vercel --prod\n```\n\n**Generated files:**\n| File | Purpose |\n|------|---------|\n| `index.html` | Sandbox page that loads almostnode and registers the service worker |\n| `vercel.json` | CORS headers for cross-origin iframe embedding |\n| `__sw__.js` | Service worker for intercepting dev server requests |\n\nSee [Sandbox Setup](#sandbox-setup) for full deployment instructions.\n\n---\n\n### Option 2: Same-Origin with Vite\n\nFor trusted code using `dangerouslyAllowSameOrigin: true`:\n\n```typescript\n\u002F\u002F vite.config.ts\nimport { defineConfig } from 'vite';\nimport { almostnodePlugin } from 'almostnode\u002Fvite';\n\nexport default defineConfig({\n  plugins: [almostnodePlugin()]\n});\n```\n\nThe plugin serves `\u002F__sw__.js` automatically during development.\n\n**Custom path:**\n\n```typescript\n\u002F\u002F vite.config.ts\nalmostnodePlugin({ swPath: '\u002Fcustom\u002F__sw__.js' })\n\n\u002F\u002F Then in your app:\nawait bridge.initServiceWorker({ swUrl: '\u002Fcustom\u002F__sw__.js' });\n```\n\n---\n\n### Option 3: Same-Origin with Next.js\n\nFor trusted code using `dangerouslyAllowSameOrigin: true`:\n\n**App Router:**\n\n```typescript\n\u002F\u002F app\u002F__sw__.js\u002Froute.ts\nimport { getServiceWorkerContent } from 'almostnode\u002Fnext';\n\nexport async function GET() {\n  return new Response(getServiceWorkerContent(), {\n    headers: {\n      'Content-Type': 'application\u002Fjavascript',\n      'Cache-Control': 'no-cache',\n    },\n  });\n}\n```\n\n**Pages Router:**\n\n```typescript\n\u002F\u002F pages\u002Fapi\u002F__sw__.ts\nimport { getServiceWorkerContent } from 'almostnode\u002Fnext';\nimport type { NextApiRequest, NextApiResponse } from 'next';\n\nexport default function handler(req: NextApiRequest, res: NextApiResponse) {\n  res.setHeader('Content-Type', 'application\u002Fjavascript');\n  res.setHeader('Cache-Control', 'no-cache');\n  res.send(getServiceWorkerContent());\n}\n```\n\n**Initialize with the correct path:**\n\n```typescript\n\u002F\u002F App Router (file-based route)\nawait bridge.initServiceWorker({ swUrl: '\u002F__sw__.js' });\n\n\u002F\u002F Pages Router (API route)\nawait bridge.initServiceWorker({ swUrl: '\u002Fapi\u002F__sw__' });\n```\n\n**Available exports from `almostnode\u002Fnext`:**\n\n| Export | Description |\n|--------|-------------|\n| `getServiceWorkerContent()` | Returns the service worker file content as a string |\n| `getServiceWorkerPath()` | Returns the absolute path to the service worker file |\n\n---\n\n### Option 4: Manual Setup (Other Frameworks)\n\nCopy the service worker to your public directory:\n\n```bash\ncp node_modules\u002Falmostnode\u002Fdist\u002F__sw__.js .\u002Fpublic\u002F\n```\n\nOr programmatically:\n\n```typescript\nimport { getServiceWorkerPath } from 'almostnode\u002Fnext';\nimport fs from 'fs';\n\nfs.copyFileSync(getServiceWorkerPath(), '.\u002Fpublic\u002F__sw__.js');\n```\n\n---\n\n## Comparison with WebContainers\n\n| Feature | almostnode | WebContainers |\n|---------|-----------|---------------|\n| **Bundle Size** | ~250KB gzipped | ~2MB |\n| **Startup Time** | Instant | 2-5 seconds |\n| **Execution Model** | Main thread or Web Worker (configurable) | Web Worker isolates |\n| **Shell** | `just-bash` (POSIX subset) | Full Linux kernel |\n| **Native Modules** | Stubs only | Full support |\n| **Networking** | Virtual ports | Real TCP\u002FIP |\n| **Use Case** | Lightweight playgrounds, demos | Full development environments |\n\n### When to use almostnode\n\n- Building code playgrounds or tutorials\n- Creating interactive documentation\n- Prototyping without server setup\n- Educational tools\n- Lightweight sandboxed execution\n\n### Example: Code Playground\n\n```typescript\nimport { createContainer } from 'almostnode';\n\nfunction createPlayground() {\n  const container = createContainer();\n\n  return {\n    run: (code: string) => {\n      try {\n        const result = container.execute(code);\n        return { success: true, result: result.exports };\n      } catch (error) {\n        return { success: false, error: error.message };\n      }\n    },\n    reset: () => container.runtime.clearCache(),\n  };\n}\n\n\u002F\u002F Usage\nconst playground = createPlayground();\nconst output = playground.run(`\n  const crypto = require('crypto');\n  module.exports = crypto.randomUUID();\n`);\nconsole.log(output); \u002F\u002F { success: true, result: \"550e8400-e29b-...\" }\n```\n\n### When to use WebContainers\n\n- Full-fidelity Node.js development\n- Running native modules\n- Complex build pipelines\n- Production-like environments\n\n---\n\n## API Reference\n\n### `createContainer(options?)`\n\nCreates a new container with all components initialized.\n\n```typescript\ninterface ContainerOptions {\n  cwd?: string;           \u002F\u002F Working directory (default: '\u002F')\n  env?: Record\u003Cstring, string>;  \u002F\u002F Environment variables\n  onConsole?: (method: string, args: any[]) => void;  \u002F\u002F Console hook\n}\n\nconst container = createContainer({\n  cwd: '\u002Fapp',\n  env: { NODE_ENV: 'development' },\n  onConsole: (method, args) => console.log(`[${method}]`, ...args),\n});\n```\n\nReturns:\n- `container.vfs` - VirtualFS instance\n- `container.runtime` - Runtime instance\n- `container.npm` - PackageManager instance\n- `container.serverBridge` - ServerBridge instance\n- `container.run(command, options?)` - Run a shell command (returns `Promise\u003CRunResult>`)\n- `container.sendInput(data)` - Send stdin data to the currently running process\n- `container.execute(code)` - Execute JavaScript code\n- `container.runFile(filename)` - Run a file from VirtualFS\n\n#### `container.run(command, options?)`\n\n```typescript\ninterface RunResult {\n  stdout: string;\n  stderr: string;\n  exitCode: number;\n}\n\ninterface RunOptions {\n  onStdout?: (data: string) => void;  \u002F\u002F Stream stdout in real-time\n  onStderr?: (data: string) => void;  \u002F\u002F Stream stderr in real-time\n  signal?: AbortSignal;                \u002F\u002F Cancel the command\n}\n```\n\n#### `container.sendInput(data)`\n\nSends data to the stdin of the currently running process. Emits both `data` and `keypress` events for compatibility with readline-based tools (e.g., vitest watch mode).\n\n### VirtualFS\n\nNode.js-compatible filesystem API.\n\n```typescript\n\u002F\u002F Synchronous operations\nvfs.writeFileSync(path, content);\nvfs.readFileSync(path, encoding?);\nvfs.mkdirSync(path, { recursive: true });\nvfs.readdirSync(path);\nvfs.statSync(path);\nvfs.unlinkSync(path);\nvfs.rmdirSync(path);\nvfs.existsSync(path);\nvfs.renameSync(oldPath, newPath);\n\n\u002F\u002F Async operations\nawait vfs.readFile(path, encoding?);\nawait vfs.stat(path);\n\n\u002F\u002F File watching\nvfs.watch(path, { recursive: true }, (event, filename) => {\n  console.log(`${event}: ${filename}`);\n});\n```\n\n### Runtime\n\nExecute JavaScript\u002FTypeScript code.\n\n```typescript\n\u002F\u002F Execute code string\nruntime.execute('console.log(\"Hello\")');\n\n\u002F\u002F Run a file from VirtualFS\nruntime.runFile('\u002Fpath\u002Fto\u002Ffile.js');\n\n\u002F\u002F Require a module\nconst module = runtime.require('\u002Fpath\u002Fto\u002Fmodule.js');\n```\n\n### createRuntime (Async Runtime Factory)\n\nFor advanced use cases, use `createRuntime` to create a runtime with security options:\n\n```typescript\nimport { createRuntime, VirtualFS } from 'almostnode';\n\nconst vfs = new VirtualFS();\n\n\u002F\u002F RECOMMENDED: Cross-origin sandbox (fully isolated)\nconst secureRuntime = await createRuntime(vfs, {\n  sandbox: 'https:\u002F\u002Fyour-sandbox.vercel.app',\n});\n\n\u002F\u002F For demos\u002Ftrusted code: Same-origin with explicit opt-in\nconst demoRuntime = await createRuntime(vfs, {\n  dangerouslyAllowSameOrigin: true,\n  useWorker: true,  \u002F\u002F Optional: run in Web Worker\n  cwd: '\u002Fproject',\n  env: { NODE_ENV: 'development' },\n});\n\n\u002F\u002F Both modes use the same async API\nconst result = await secureRuntime.execute('module.exports = 1 + 1;');\nconsole.log(result.exports); \u002F\u002F 2\n```\n\n#### Security Modes\n\n| Mode | Option | Security Level | Use Case |\n|------|--------|----------------|----------|\n| **Cross-origin sandbox** | `sandbox: 'https:\u002F\u002F...'` | Highest | Production, untrusted code |\n| **Same-origin Worker** | `dangerouslyAllowSameOrigin: true, useWorker: true` | Medium | Demos with trusted code |\n| **Same-origin main thread** | `dangerouslyAllowSameOrigin: true` | Lowest | Trusted code only |\n\n**Security by default:** `createRuntime()` throws an error if neither `sandbox` nor `dangerouslyAllowSameOrigin` is provided.\n\n---\n\n## Sandbox Setup\n\nFor running untrusted code securely, deploy a cross-origin sandbox. The key requirement is that the sandbox must be served from a **different origin** (different domain, subdomain, or port).\n\n### Quick Setup (Vercel)\n\n```typescript\nimport { generateSandboxFiles } from 'almostnode';\nimport fs from 'fs';\n\nconst files = generateSandboxFiles();\n\u002F\u002F Generates: index.html, vercel.json, __sw__.js\n\nfs.mkdirSync('sandbox', { recursive: true });\nfor (const [filename, content] of Object.entries(files)) {\n  fs.writeFileSync(`sandbox\u002F${filename}`, content);\n}\n\n\u002F\u002F Deploy: cd sandbox && vercel --prod\n```\n\nThe generated files include:\n- `index.html` - Sandbox page with service worker registration\n- `vercel.json` - CORS headers for cross-origin iframe embedding\n- `__sw__.js` - Service worker for dev server URL access\n\n### Manual Setup (Any Platform)\n\nThe sandbox requires two things:\n\n#### 1. The sandbox HTML page\n\nCreate an `index.html` that loads almostnode and handles postMessage:\n\n```html\n\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\u003Cmeta charset=\"UTF-8\">\u003C\u002Fhead>\n\u003Cbody>\n\u003Cscript type=\"module\">\n  import { VirtualFS, Runtime } from 'https:\u002F\u002Funpkg.com\u002Falmostnode\u002Fdist\u002Findex.js';\n\n  let vfs = null;\n  let runtime = null;\n\n  window.addEventListener('message', async (event) => {\n    const { type, id, code, filename, vfsSnapshot, options, path, content } = event.data;\n\n    try {\n      switch (type) {\n        case 'init':\n          vfs = VirtualFS.fromSnapshot(vfsSnapshot);\n          runtime = new Runtime(vfs, {\n            cwd: options?.cwd,\n            env: options?.env,\n            onConsole: (method, args) => {\n              parent.postMessage({ type: 'console', consoleMethod: method, consoleArgs: args }, '*');\n            },\n          });\n          break;\n        case 'execute':\n          const result = runtime.execute(code, filename);\n          parent.postMessage({ type: 'result', id, result }, '*');\n          break;\n        case 'runFile':\n          const runResult = runtime.runFile(filename);\n          parent.postMessage({ type: 'result', id, result: runResult }, '*');\n          break;\n        case 'syncFile':\n          if (content === null) { try { vfs.unlinkSync(path); } catch {} }\n          else { vfs.writeFileSync(path, content); }\n          break;\n        case 'clearCache':\n          runtime?.clearCache();\n          break;\n      }\n    } catch (error) {\n      if (id) parent.postMessage({ type: 'error', id, error: error.message }, '*');\n    }\n  });\n\n  parent.postMessage({ type: 'ready' }, '*');\n\u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n#### 2. Required HTTP headers\n\nThe sandbox server must include these headers:\n\n```\nAccess-Control-Allow-Origin: *\nCross-Origin-Resource-Policy: cross-origin\n```\n\n**Example configurations:**\n\n\u003Cdetails>\n\u003Csummary>Nginx\u003C\u002Fsummary>\n\n```nginx\nserver {\n    listen 3002;\n    root \u002Fpath\u002Fto\u002Fsandbox;\n\n    location \u002F {\n        add_header Access-Control-Allow-Origin *;\n        add_header Cross-Origin-Resource-Policy cross-origin;\n    }\n}\n```\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>Apache (.htaccess)\u003C\u002Fsummary>\n\n```apache\nHeader set Access-Control-Allow-Origin \"*\"\nHeader set Cross-Origin-Resource-Policy \"cross-origin\"\n```\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>Express.js\u003C\u002Fsummary>\n\n```javascript\napp.use((req, res, next) => {\n  res.setHeader('Access-Control-Allow-Origin', '*');\n  res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');\n  next();\n});\napp.use(express.static('sandbox'));\napp.listen(3002);\n```\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>Python (http.server)\u003C\u002Fsummary>\n\n```python\nfrom http.server import HTTPServer, SimpleHTTPRequestHandler\n\nclass CORSHandler(SimpleHTTPRequestHandler):\n    def end_headers(self):\n        self.send_header('Access-Control-Allow-Origin', '*')\n        self.send_header('Cross-Origin-Resource-Policy', 'cross-origin')\n        super().end_headers()\n\nHTTPServer(('', 3002), CORSHandler).serve_forever()\n```\n\u003C\u002Fdetails>\n\n### Use in your app\n\n```typescript\nconst runtime = await createRuntime(vfs, {\n  sandbox: 'https:\u002F\u002Fsandbox.yourdomain.com',  \u002F\u002F Must be different origin!\n});\n\n\u002F\u002F Code runs in isolated cross-origin iframe\nconst result = await runtime.execute(untrustedCode);\n```\n\n### Local Development\n\nFor local testing, run the sandbox on a different port:\n\n```bash\n# Terminal 1: Main app on port 5173\nnpm run dev\n\n# Terminal 2: Sandbox on port 3002\nnpm run sandbox\n```\n\nThen use `sandbox: 'http:\u002F\u002Flocalhost:3002\u002Fsandbox\u002F'` in your app.\n\n### What cross-origin sandbox protects\n\n| Threat | Status |\n|--------|--------|\n| Cookies | Blocked (different origin) |\n| localStorage | Blocked (different origin) |\n| IndexedDB | Blocked (different origin) |\n| DOM access | Blocked (cross-origin iframe) |\n\n**Note:** Network requests from the sandbox are still possible. Add CSP headers for additional protection.\n\n### PackageManager\n\nInstall npm packages.\n\n```typescript\n\u002F\u002F Install a package\nawait npm.install('react');\nawait npm.install('lodash@4.17.21');\n\n\u002F\u002F Install multiple packages\nawait npm.install(['react', 'react-dom']);\n```\n\n---\n\n## Supported Node.js APIs\n\n**967 compatibility tests** verify our Node.js API coverage.\n\n### Fully Shimmed Modules\n\n| Module | Tests | Coverage | Notes |\n|--------|-------|----------|-------|\n| `path` | 219 | High | POSIX paths (no Windows) |\n| `buffer` | 95 | High | All common operations |\n| `fs` | 76 | High | Sync + promises API |\n| `url` | 67 | High | WHATWG URL + legacy parser |\n| `util` | 77 | High | format, inspect, promisify |\n| `process` | 60 | High | env, cwd, hrtime, EventEmitter |\n| `events` | 50 | High | Full EventEmitter API |\n| `os` | 58 | High | Platform info (simulated) |\n| `crypto` | 57 | High | Hash, HMAC, random, sign\u002Fverify |\n| `querystring` | 52 | High | parse, stringify, escape |\n| `stream` | 44 | Medium | Readable, Writable, Transform |\n| `zlib` | 39 | High | gzip, deflate, brotli |\n| `tty` | 40 | High | ReadStream, WriteStream |\n| `perf_hooks` | 33 | High | Performance API |\n\n### Stubbed Modules\n\nThese modules export empty objects or no-op functions:\n- `net`, `tls`, `dns`, `dgram`\n- `cluster`, `worker_threads`\n- `vm`, `v8`, `inspector`\n- `async_hooks`\n\n---\n\n## Framework Support\n\n### Vite\n\n```typescript\nimport { VirtualFS, ViteDevServer, getServerBridge } from 'almostnode';\n\nconst vfs = new VirtualFS();\n\n\u002F\u002F Create a React app\nvfs.writeFileSync('\u002Findex.html', `\n  \u003C!DOCTYPE html>\n  \u003Chtml>\n    \u003Cbody>\n      \u003Cdiv id=\"root\">\u003C\u002Fdiv>\n      \u003Cscript type=\"module\" src=\"\u002Fsrc\u002Fmain.jsx\">\u003C\u002Fscript>\n    \u003C\u002Fbody>\n  \u003C\u002Fhtml>\n`);\n\nvfs.mkdirSync('\u002Fsrc', { recursive: true });\nvfs.writeFileSync('\u002Fsrc\u002Fmain.jsx', `\n  import React from 'react';\n  import ReactDOM from 'react-dom\u002Fclient';\n\n  function App() {\n    return \u003Ch1>Hello Vite!\u003C\u002Fh1>;\n  }\n\n  ReactDOM.createRoot(document.getElementById('root')).render(\u003CApp \u002F>);\n`);\n\n\u002F\u002F Start Vite dev server\nconst server = new ViteDevServer(vfs, { port: 5173 });\n```\n\n### Next.js\n\nSupports both **Pages Router** and **App Router**:\n\n#### Pages Router\n\n```\n\u002Fpages\n  \u002Findex.jsx      → \u002F\n  \u002Fabout.jsx      → \u002Fabout\n  \u002Fusers\u002F[id].jsx → \u002Fusers\u002F:id\n  \u002Fapi\u002Fhello.js   → \u002Fapi\u002Fhello\n```\n\n#### App Router\n\n```\n\u002Fapp\n  \u002Flayout.jsx           → Root layout\n  \u002Fpage.jsx             → \u002F\n  \u002Fabout\u002Fpage.jsx       → \u002Fabout\n  \u002Fusers\u002F[id]\u002Fpage.jsx  → \u002Fusers\u002F:id\n```\n\n---\n\n## Hot Module Replacement (HMR)\n\nalmostnode includes built-in Hot Module Replacement support for instant updates during development. When you edit files, changes appear immediately in the preview without a full page reload.\n\n### How It Works\n\nHMR is automatically enabled when using `NextDevServer` or `ViteDevServer`. The system uses:\n\n1. **VirtualFS file watching** - Detects file changes via `vfs.watch()`\n2. **postMessage API** - Communicates updates between the main page and preview iframe\n3. **React Refresh** - Preserves React component state during updates\n\n```typescript\n\u002F\u002F HMR works automatically - just edit files and save\nvfs.writeFileSync('\u002Fapp\u002Fpage.tsx', updatedContent);\n\u002F\u002F The preview iframe will automatically refresh with the new content\n```\n\n### Setup Requirements\n\nFor security, the preview iframe should be sandboxed. HMR uses `postMessage` for communication, which works correctly with sandboxed iframes:\n\n```typescript\n\u002F\u002F Create sandboxed iframe for security\nconst iframe = document.createElement('iframe');\niframe.src = '\u002F__virtual__\u002F3000\u002F';\n\u002F\u002F Sandbox restricts the iframe's capabilities - add only what you need\niframe.sandbox = 'allow-forms allow-scripts allow-same-origin allow-popups';\ncontainer.appendChild(iframe);\n\n\u002F\u002F Register the iframe as HMR target after it loads\niframe.onload = () => {\n  if (iframe.contentWindow) {\n    devServer.setHMRTarget(iframe.contentWindow);\n  }\n};\n```\n\n**Recommended sandbox permissions:**\n- `allow-scripts` - Required for JavaScript execution\n- `allow-same-origin` - Allows the iframe to access cookies, localStorage, and IndexedDB (only add if your app needs these; omit for better isolation)\n- `allow-forms` - If your app uses forms\n- `allow-popups` - If your app opens new windows\u002Ftabs\n\n> **Note:** The service worker intercepts `\u002F__virtual__\u002F` requests at the origin level, not the iframe level. The `allow-same-origin` attribute does NOT affect service worker functionality. For maximum security isolation, consider using **cross-origin sandbox mode** (see below) which doesn't use `allow-same-origin`.\n\n### Manual HMR Triggering\n\nIf you need to manually trigger HMR updates (e.g., after programmatic file changes):\n\n```typescript\nfunction triggerHMR(path: string, iframe: HTMLIFrameElement): void {\n  if (iframe.contentWindow) {\n    iframe.contentWindow.postMessage({\n      type: 'update',\n      path,\n      timestamp: Date.now(),\n      channel: 'next-hmr', \u002F\u002F Use 'vite-hmr' for Vite\n    }, '*');\n  }\n}\n\n\u002F\u002F After writing a file\nvfs.writeFileSync('\u002Fapp\u002Fpage.tsx', newContent);\ntriggerHMR('\u002Fapp\u002Fpage.tsx', iframe);\n```\n\n### Supported File Types\n\n| File Type | HMR Behavior |\n|-----------|--------------|\n| `.jsx`, `.tsx` | React Refresh (preserves state) |\n| `.js`, `.ts` | Full module reload |\n| `.css` | Style injection (no reload) |\n| `.json` | Full page reload |\n\n---\n\n## Demos\n\nStart the dev server with `npm run dev` and open any demo at `http:\u002F\u002Flocalhost:5173`:\n\n| Demo | Path | Description |\n|------|------|-------------|\n| **Next.js** | `\u002Fexamples\u002Fnext-demo.html` | Pages & App Router, CSS modules, route groups, API routes, HMR |\n| **Vite** | `\u002Fexamples\u002Fvite-demo.html` | Vite dev server with React and HMR |\n| **Vitest** | `\u002Fexamples\u002Fvitest-demo.html` | Real vitest execution with xterm.js terminal and watch mode |\n| **Express** | `\u002Fexamples\u002Fexpress-demo.html` | Express.js HTTP server running in the browser |\n| **Convex** | `\u002Fexamples\u002Fdemo-convex-app.html` | Real-time todo app with Convex cloud deployment |\n| **Vercel AI SDK** | `\u002Fexamples\u002Fdemo-vercel-ai-sdk.html` | Streaming AI chatbot with Next.js and OpenAI |\n| **Bash** | `\u002Fexamples\u002Fbash-demo.html` | Interactive POSIX shell emulator |\n\n---\n\n## Development\n\n### Setup\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fmacaly\u002Falmostnode.git\ncd almostnode\nnpm install\n```\n\n### Run Tests\n\n```bash\n# Unit tests\nnpm test\n\n# E2E tests (requires Playwright)\nnpm run test:e2e\n```\n\n### Development Server\n\n```bash\nnpm run dev\n```\n\nSee the [Demos](#demos) section for all available examples.\n\n---\n\n## Contributing\n\nContributions are welcome! Please:\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature\u002Famazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature\u002Famazing-feature`)\n5. Open a Pull Request\n\n---\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n\n---\n\n## Acknowledgments\n\n- [esbuild-wasm](https:\u002F\u002Fgithub.com\u002Fevanw\u002Fesbuild) - Lightning-fast JavaScript\u002FTypeScript transformation\n- [just-bash](https:\u002F\u002Fgithub.com\u002Fuser\u002Fjust-bash) - POSIX shell in WebAssembly\n- [React Refresh](https:\u002F\u002Fgithub.com\u002Ffacebook\u002Freact\u002Ftree\u002Fmain\u002Fpackages\u002Freact-refresh) - Hot module replacement for React\n- [Comlink](https:\u002F\u002Fgithub.com\u002FGoogleChromeLabs\u002Fcomlink) - Web Worker communication made simple\n\n---\n\n\u003Cp align=\"center\">\n  Built by the creators of \u003Ca href=\"https:\u002F\u002Fmacaly.com\">Macaly.com\u003C\u002Fa>\n\u003C\u002Fp>\n","almostnode 是一个在浏览器中运行 Node.js 代码的轻量级环境。它通过提供虚拟文件系统、Node.js API 的模拟实现以及直接在浏览器中安装和运行 npm 包的能力，支持开发者使用 Vite 或 Next.js 等工具进行前端开发而无需后端服务器的支持。此外，almostnode 还具备热模块替换、TypeScript 支持及服务工作者架构等特性，确保了开发过程中的高效与安全。此项目非常适合需要在纯前端环境下快速搭建或测试基于 Node.js 应用的场景，尤其适用于教育、原型设计或是小型项目的初期开发阶段。请注意，作为实验性项目，在生产环境中使用时需谨慎。",2,"2026-06-11 03:52:02","high_star"]