Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f0595fc
Fix all TypeScript strict type errors in shims and tests
petrbrzek Feb 9, 2026
2816d4c
Add npm run support, vitest testing, xterm.js terminal, and watch mode
petrbrzek Feb 11, 2026
703eb2a
Generic bin stubs, streaming API, watch mode restart, and platform sh…
petrbrzek Feb 12, 2026
906909f
Fix TypeScript strict type errors in child_process and process shims
petrbrzek Feb 12, 2026
6cfccdc
Fix node-compat workflow: add permissions for PR comments
petrbrzek Feb 12, 2026
91aaea8
Merge pull request #6 from macaly/feature/vitest-xterm-watch-mode
petrbrzek Feb 12, 2026
82c9137
Remove library-specific code, centralize CDN config, fix docs — v0.2.13
petrbrzek Feb 12, 2026
4fe5f50
Merge pull request #8 from macaly/feature/platform-cleanup-v0.2.13
petrbrzek Feb 12, 2026
0d044f5
Add missing example pages to Vite build inputs
petrbrzek Feb 12, 2026
2bdc0a0
Remove vite-plugin-top-level-await, use native TLA instead
petrbrzek Feb 12, 2026
72c37ff
Fix demos to use platform properly, harden E2E tests, fix route group…
petrbrzek Feb 14, 2026
3a1a776
Fix TypeScript type errors
petrbrzek Feb 14, 2026
3323864
Add Agent Workbench to homepage demos, remove file editing guardrails
petrbrzek Feb 14, 2026
c9944a3
Exclude convex-deploy E2E test from CI
petrbrzek Feb 14, 2026
02c5a35
Remove flaky HTTP API mutation test from convex-deploy E2E
petrbrzek Feb 14, 2026
1e960b0
Merge pull request #11 from macaly/feature/platform-fixes-e2e-hardening
petrbrzek Feb 14, 2026
c01e918
Bump version to 0.2.14, update changelog
petrbrzek Feb 14, 2026
6ab61f3
Switch CORS proxy to self-hosted Cloudflare Worker
petrbrzek Feb 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,32 @@ jobs:
run: npm run build:lib

- name: Run type check
run: npm run build:types
run: npm run type-check

- name: Run tests
run: npm run test:run

e2e:
runs-on: ubuntu-latest
needs: test

steps:
- uses: actions/checkout@v4

- name: Use Node.js 22
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Install Playwright browsers
run: npx playwright install --with-deps chromium

- name: Run E2E tests
run: npx playwright test e2e/
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CONVEX_DEPLOY_KEY: ${{ secrets.CONVEX_DEPLOY_KEY }}
4 changes: 4 additions & 0 deletions .github/workflows/node-compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ on:
- 'src/virtual-fs.ts'
- '.github/workflows/node-compat.yml'

permissions:
contents: read
pull-requests: write

jobs:
test:
name: Node.js Compatibility
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ npm-debug.log*

# Temp folders for testing
temp/

# Scratch files
e2e/deploy-debug.spec.ts
examples/macaly-demo.html
67 changes: 67 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,73 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.14] - 2026-02-14

### Added
- **Agent Workbench demo**: AI coding agent that builds Next.js pages live with file editing, bash execution, and HMR preview. Added to homepage demos grid.
- **Vercel AI SDK demo**: Streaming AI chatbot with Next.js, OpenAI, and real-time token streaming via Pages Router API route
- **Express demo E2E tests**: New Playwright tests for the Express server demo
- **`vfs-require` module** (`src/frameworks/vfs-require.ts`): Shared require system extracted for reuse across entry points
- **`npm-serve` module** (`src/frameworks/npm-serve.ts`): Shared `/_npm/` package bundling endpoint with nested exports support
- **CI E2E pipeline**: GitHub Actions now runs Playwright E2E tests after unit tests with Chromium
- **CLAUDE.md**: Project instructions file for AI-assisted development

### Fixed
- **Route group client-side navigation**: Pages inside route groups (e.g. `(marketing)/about`) now render correctly during client-side navigation. Replaced local path construction with server-based `resolveRoute()` using extended `/_next/route-info` endpoint that returns actual `page` and `layouts` paths.
- **`convertToModelMessages` import**: Vercel AI SDK demo now imports from `ai` package instead of non-existent `@ai-sdk/ui-utils`
- **npm-serve nested exports**: Packages with nested `exports` field entries (e.g. `ai/react`, `@ai-sdk/openai`) now resolve correctly
- **TypeScript type errors**: Fixed duplicate `setEnv` method, `executeApiHandler` return type, `cpExec` callback types

### Changed
- **Agent Workbench guardrails removed**: AI agent can now modify any project file including root page (`/app/page.tsx`), `package.json`, and `tsconfig.json`. Only `/pages/api/chat.ts` remains protected.
- **E2E tests hardened**: Removed try/catch fallbacks across all E2E tests for strict assertions; collect page errors for better debugging
- **Convex and Vite demos refactored**: Use platform's `vfs-require` and `npm-serve` modules instead of inline implementations

## [0.2.13] - 2026-02-12

### Added
- **Centralized CDN configuration** (`src/config/cdn.ts`): Single source of truth for esm.sh, unpkg, and other CDN URLs used across the codebase
- **esm.sh version resolution**: `redirectNpmImports` now reads `package.json` dependencies and includes the major version in esm.sh URLs (e.g. `ai@4/react`), fixing 404s on subpath imports
- **Setup overlay dialogs**: Convex and Vercel AI SDK demos now show an API key setup dialog on load with privacy notice ("your key stays in your browser")
- **New tests**: `tests/cdn-config.test.ts` (12 tests) and `tests/code-transforms.test.ts` (11 tests)

### Changed
- Renamed AI chatbot demo files: `demo-ai-chatbot.html` → `demo-vercel-ai-sdk.html`, `ai-chatbot-demo.ts` → `vercel-ai-sdk-demo.ts`
- Replaced hardcoded CDN URLs throughout codebase with imports from `src/config/cdn.ts`

### Removed
- **`sentry` shim** (`src/shims/sentry.ts`): Was a no-op stub for a non-existent Node.js built-in
- **Custom `convex` command** in `child_process.ts`: Convex now runs through the generic bin stub system like any other CLI tool
- **Convex-specific path remaps** in `fs.ts`: `path.resolve()` with correct `cwd` handles this generically
- **`vfs:` prefix stripping** in `fs.ts`: Moved to esbuild shim where the artifact originates

## [0.2.12] - 2026-02-12

### Added

- **Generic bin stubs:** `npm install` now reads each package's `bin` field and creates executable scripts in `/node_modules/.bin/`. CLI tools like `vitest`, `eslint`, `tsc`, etc. work automatically via the `node` command — no custom commands needed.
- **Streaming `container.run()` API:** Long-running commands support `onStdout`/`onStderr` callbacks and `AbortController` signal for cancellation.
- **`container.sendInput()`:** Send stdin data to running processes (emits both `data` and `keypress` events for readline compatibility).
- **Vitest demo with xterm.js:** New `examples/vitest-demo.html` showcasing real vitest execution in the browser with watch mode, syntax-highlighted terminal output, and file editing.
- **E2E tests for vitest demo:** 5 Playwright tests covering install, test execution, tab switching, failure detection, and watch mode restart.
- **`rollup` shim:** Stub module so vitest's dependency chain resolves without errors.
- **`fs.realpathSync.native`:** Added as alias for `realpathSync` (used by vitest internals).
- **`fs.createReadStream` / `fs.createWriteStream`:** Basic implementations using VirtualFS.
- **`path.delimiter` and `path.win32`:** Added missing path module properties.
- **`process.getuid()`, `process.getgid()`, `process.umask()`:** Added missing process methods used by npm packages.
- **`util.deprecate()`:** Returns the original function with a no-op deprecation warning.

### Changed

- **`Object.defineProperty` patch on `globalThis`:** Forces `configurable: true` for properties defined on `globalThis`, so libraries that define non-configurable globals (like vitest's `__vitest_index__`) can be re-run without errors.
- **VFS adapter executable mode:** Files in `/node_modules/.bin/` now return `0o755` mode so just-bash treats them as executable.
- **`Runtime.clearCache()` clears in-place:** Previously created a new empty object, leaving closures referencing the stale cache. Now deletes keys in-place.
- **Watch mode uses restart pattern:** Vitest caches modules internally (Vite's ModuleRunner), so file changes require a full vitest restart (abort + re-launch) rather than stdin-triggered re-runs.

### Removed

- **Custom vitest command:** Deleted `src/shims/vitest-command.ts` and removed vitest-specific handling from `child_process.ts`. Vitest now runs through the generic bin stub + `node` command like any other CLI tool.

## [0.2.11] - 2026-02-09

### Fixed
Expand Down
68 changes: 68 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# almostnode

## What This Is

almostnode is a **real competitor to WebContainers (StackBlitz)**. It runs Node.js natively in the browser — virtual filesystem, npm package installation, dev servers, the works.

## Core Principle

**Never write library-specific shim code. Fix the platform instead.**

When a package doesn't work, the fix goes into the generic shims (fs, path, crypto, etc.), not into a package-specific adapter. Every demo should use real npm packages installed via `PackageManager`, served via `/_npm/` bundling, and running through the standard runtime. No CDN shortcuts, no manual protocol reimplementations, no fake adapters.

## Architecture

- **Runtime** (`src/runtime.ts`) — JS execution engine with `require()`, ESM-to-CJS transforms, 43 built-in module shims
- **VirtualFS** (`src/virtual-fs.ts`) — In-memory filesystem, exposed as `require('fs')`
- **PackageManager** (`src/npm/`) — Real npm packages downloaded, extracted, ESM-to-CJS transformed via esbuild-wasm
- **Service Worker** — Network interception for HTTP servers (`/__virtual__/{port}/`)
- **Dev Servers** — `NextDevServer` (Pages + App Router), `ViteDevServer` (React + HMR)
- **just-bash** — Bash emulator with custom commands (`node`, `npm`, `convex`)
- **Code Transforms** (`src/frameworks/code-transforms.ts`) — CSS Modules (css-tree AST), ESM-to-CJS (acorn AST), React Refresh, npm import redirect

### Next.js Dev Server (split across files)

- `src/frameworks/next-dev-server.ts` — Orchestrator (~1360 lines)
- `src/frameworks/next-route-resolver.ts` — Route resolution (~600 lines)
- `src/frameworks/next-api-handler.ts` — API route handlers (~350 lines)
- `src/frameworks/next-shims.ts` — Shim string constants (~1040 lines)
- `src/frameworks/next-html-generator.ts` — HTML page generation (~560 lines)
- `src/frameworks/next-config-parser.ts` — next.config.js parsing (AST + regex fallback)

## Commands

```bash
npm run dev # Vite dev server (port 5173)
npm run test:run # Unit tests (vitest, ~2250 tests, ~10s)
npm run test:e2e # E2E tests (playwright, ~105 tests)
npm run build # Build for production
```

## Testing

- Unit tests: `tests/` directory, run with `npm run test:run`
- E2E tests: `e2e/` directory, run with `npx playwright test e2e/`
- Run a single E2E file: `npx playwright test e2e/vite-demo.spec.ts`
- Test harnesses live in `examples/` (HTML files with VFS setup)

## Key Technical Details

- **`/_npm/` endpoint**: Bundles npm packages from VFS as ESM for browser consumption via esbuild
- **`/_next/route-info`**: Server endpoint returning resolved route info (page, layouts, params) — used by client-side navigation
- **Virtual prefix**: `/__virtual__/{port}/` — all imports go through this for service worker interception
- **`isBrowser` flag**: In test env (jsdom), `isBrowser=false` — transforms run differently
- **ESM-to-CJS**: Happens both at install time (esbuild-wasm) and at runtime (in `loadModule()`)
- **Route groups**: `(groupName)` directories are transparent in URLs, resolved server-side

## Where to Find More Context

- **`README.md`** — Public API docs, usage examples, comparison with WebContainers, sandbox setup
- **`CHANGELOG.md`** — Version history and what changed
- **`examples/`** — Working demo HTML files (next-demo, vite-demo, express-demo, etc.) — read these to understand how the platform is used end-to-end
- **`e2e/`** — Playwright E2E tests that exercise each demo — read these to understand what each demo should do

When working on a specific demo or feature, read the corresponding example HTML and E2E test first.

## Release Process

Always bump version in `package.json` and update `CHANGELOG.md` before pushing. Follow [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format, Semantic Versioning.
108 changes: 106 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Built by the creators of [Macaly.com](https://macaly.com) — a tool that lets a

- **Virtual File System** - Full in-memory filesystem with Node.js-compatible API
- **Node.js API Shims** - 40+ shimmed modules (`fs`, `path`, `http`, `events`, and more)
- **npm Package Installation** - Install and run real npm packages in the browser
- **npm Package Installation** - Install and run real npm packages in the browser with automatic bin stub creation
- **Run Any CLI Tool** - npm packages with `bin` entries (vitest, eslint, tsc, etc.) work automatically
- **Dev Servers** - Built-in Vite and Next.js development servers
- **Hot Module Replacement** - React Refresh support for instant updates
- **TypeScript Support** - First-class TypeScript/TSX transformation via esbuild-wasm
Expand Down Expand Up @@ -127,6 +128,69 @@ container.execute(`
// Output: Hello world
```

### Running Shell Commands

```typescript
import { createContainer } from 'almostnode';

const container = createContainer();

// Write a package.json with scripts
container.vfs.writeFileSync('/package.json', JSON.stringify({
name: 'my-app',
scripts: {
build: 'echo Building...',
test: 'vitest run'
}
}));

// Run shell commands directly
const result = await container.run('npm run build');
console.log(result.stdout); // "Building..."

await container.run('npm test');
await container.run('echo hello && echo world');
await container.run('ls /');
```

Supported npm commands: `npm run <script>`, `npm start`, `npm test`, `npm install`, `npm ls`.
Pre/post lifecycle scripts (`prebuild`, `postbuild`, etc.) run automatically.

### Running CLI Tools

Any npm package with a `bin` field works automatically after install — no configuration needed.

```typescript
// Install a package that includes a CLI tool
await container.npm.install('vitest');

// Run it directly — bin stubs are created in /node_modules/.bin/
const result = await container.run('vitest run');
console.log(result.stdout); // Test results
```

This works because `npm install` reads each package's `bin` field and creates executable scripts in `/node_modules/.bin/`. The shell's PATH includes `/node_modules/.bin`, so tools like `vitest`, `eslint`, `tsc`, etc. resolve automatically.

### Streaming Output & Long-Running Commands

For commands that run continuously (like watch mode), use streaming callbacks and abort signals:

```typescript
const controller = new AbortController();

await container.run('vitest --watch', {
onStdout: (data) => console.log(data),
onStderr: (data) => console.error(data),
signal: controller.signal,
});

// Send input to the running process
container.sendInput('a'); // Press 'a' to re-run all tests

// Stop the command
controller.abort();
```

### With Next.js Dev Server

```typescript
Expand Down Expand Up @@ -395,6 +459,30 @@ Returns:
- `container.runtime` - Runtime instance
- `container.npm` - PackageManager instance
- `container.serverBridge` - ServerBridge instance
- `container.run(command, options?)` - Run a shell command (returns `Promise<RunResult>`)
- `container.sendInput(data)` - Send stdin data to the currently running process
- `container.execute(code)` - Execute JavaScript code
- `container.runFile(filename)` - Run a file from VirtualFS

#### `container.run(command, options?)`

```typescript
interface RunResult {
stdout: string;
stderr: string;
exitCode: number;
}

interface RunOptions {
onStdout?: (data: string) => void; // Stream stdout in real-time
onStderr?: (data: string) => void; // Stream stderr in real-time
signal?: AbortSignal; // Cancel the command
}
```

#### `container.sendInput(data)`

Sends 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).

### VirtualFS

Expand Down Expand Up @@ -853,6 +941,22 @@ triggerHMR('/app/page.tsx', iframe);

---

## Demos

Start the dev server with `npm run dev` and open any demo at `http://localhost:5173`:

| Demo | Path | Description |
|------|------|-------------|
| **Next.js** | `/examples/next-demo.html` | Pages & App Router, CSS modules, route groups, API routes, HMR |
| **Vite** | `/examples/vite-demo.html` | Vite dev server with React and HMR |
| **Vitest** | `/examples/vitest-demo.html` | Real vitest execution with xterm.js terminal and watch mode |
| **Express** | `/examples/express-demo.html` | Express.js HTTP server running in the browser |
| **Convex** | `/examples/demo-convex-app.html` | Real-time todo app with Convex cloud deployment |
| **Vercel AI SDK** | `/examples/demo-vercel-ai-sdk.html` | Streaming AI chatbot with Next.js and OpenAI |
| **Bash** | `/examples/bash-demo.html` | Interactive POSIX shell emulator |

---

## Development

### Setup
Expand All @@ -879,7 +983,7 @@ npm run test:e2e
npm run dev
```

Open `http://localhost:5173/examples/next-demo.html` to see the Next.js demo.
See the [Demos](#demos) section for all available examples.

---

Expand Down
8 changes: 4 additions & 4 deletions docs/AI_CHATBOT_TUTORIAL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Just Node + AI Chatbot Tutorial
# almostnode — AI Chatbot Tutorial

Build a streaming AI chatbot entirely in the browser using Just Node's virtual Node.js runtime, Next.js, and Vercel AI SDK.
Build a streaming AI chatbot entirely in the browser using almostnode's virtual Node.js runtime, Next.js, and Vercel AI SDK.

## Table of Contents

Expand Down Expand Up @@ -45,7 +45,7 @@ All code runs client-side - no backend server required.
npm run dev
```

2. Open `http://localhost:5173/examples/demo-ai-chatbot.html`
2. Open `http://localhost:5173/examples/demo-vercel-ai-sdk.html`

3. Enter your OpenAI API key and click "Connect"

Expand Down Expand Up @@ -460,4 +460,4 @@ for (const chunk of chunks) {
- [Vercel AI SDK Documentation](https://sdk.vercel.ai/docs)
- [OpenAI API Reference](https://platform.openai.com/docs/api-reference)
- [Next.js App Router](https://nextjs.org/docs/app)
- [Just Node API Documentation](./API.md)
- [almostnode API Documentation](../README.md#api-reference)
10 changes: 5 additions & 5 deletions docs/CONVEX_TUTORIAL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Just Node + Convex Tutorial
# almostnode — Convex Tutorial

Build and deploy real-time Convex applications entirely in the browser using Just Node's virtual Node.js runtime.
Build and deploy real-time Convex applications entirely in the browser using almostnode's virtual Node.js runtime.

## Table of Contents

Expand All @@ -15,7 +15,7 @@ Build and deploy real-time Convex applications entirely in the browser using Jus

## Introduction

**Just Node** provides a complete Node.js-compatible runtime that runs in your browser. You can:
**almostnode** provides a complete Node.js-compatible runtime that runs in your browser. You can:

- Create a virtual filesystem with project files
- Install npm packages (including Convex)
Expand Down Expand Up @@ -52,7 +52,7 @@ vfs.writeFileSync('/package.json', JSON.stringify({ name: 'my-app' }));
vfs.mkdirSync('/app', { recursive: true });
vfs.writeFileSync('/app/page.tsx', `
export default function Home() {
return <h1>Hello from Just Node!</h1>;
return <h1>Hello from almostnode!</h1>;
}
`);

Expand Down Expand Up @@ -577,5 +577,5 @@ Some CLI errors are expected (like `process.exit` calls). The deployment happens

- [Convex Documentation](https://docs.convex.dev)
- [Convex Dashboard](https://dashboard.convex.dev)
- [Just Node API Documentation](./API.md)
- [almostnode API Documentation](../README.md#api-reference)
- [CLI Integration Details](./CONVEX_CLI_INTEGRATION.md)
Loading