Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ 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.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
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -941,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 @@ -967,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)
76 changes: 75 additions & 1 deletion examples/demo-convex-app.html
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,71 @@
margin-bottom: 12px;
}
@keyframes spin { to { transform: rotate(360deg); } }
.setup-overlay {
position: absolute;
inset: 0;
background: rgba(0,0,0,0.85);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.setup-overlay.hidden { display: none; }
.setup-dialog {
background: var(--surface);
border: 1px solid var(--border);
padding: 32px;
max-width: 420px;
width: 90%;
}
.setup-dialog h2 {
font-family: var(--mono);
font-size: 0.9rem;
color: var(--text-bright);
margin-bottom: 8px;
}
.setup-dialog p {
font-size: 0.8rem;
color: var(--text-dim);
line-height: 1.6;
margin-bottom: 16px;
}
.setup-dialog .privacy-note {
font-family: var(--mono);
font-size: 0.65rem;
color: var(--accent-dim);
background: var(--accent-bg);
border: 1px solid var(--accent-border);
padding: 8px 12px;
margin-bottom: 16px;
line-height: 1.5;
}
.setup-dialog input {
width: 100%;
padding: 10px 12px;
font-family: var(--mono);
font-size: 0.78rem;
border: 1px solid var(--border);
background: var(--black);
color: var(--text);
margin-bottom: 12px;
}
.setup-dialog input::placeholder { color: var(--text-dim); }
.setup-dialog button {
width: 100%;
padding: 10px;
font-family: var(--mono);
font-size: 0.78rem;
font-weight: 600;
background: var(--accent);
color: var(--black);
border: none;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.setup-dialog button:hover { background: #00ffaa; }
.setup-dialog button:disabled { opacity: 0.4; cursor: not-allowed; }
@media (max-width: 900px) {
.layout { grid-template-columns: 1fr; }
.file-tree-panel { display: none; }
Expand All @@ -160,7 +225,16 @@
<span class="tag">next.js · convex · realtime</span>
</div>

<div class="layout">
<div class="layout" style="position:relative;">
<div class="setup-overlay" id="setupOverlay">
<div class="setup-dialog">
<h2>Convex Deploy Key Required</h2>
<p>This demo deploys functions to Convex cloud and syncs data in real time. Enter your deploy key to get started.</p>
<div class="privacy-note">Your key stays in your browser. It is used directly to communicate with the Convex API — it is never sent to our servers.</div>
<input type="password" id="setupKeyInput" placeholder="dev:deployment-name|token" />
<button id="setupKeyBtn" disabled>Deploy</button>
</div>
</div>
<!-- File Tree -->
<div class="panel file-tree-panel">
<div class="panel-header">
Expand Down
84 changes: 79 additions & 5 deletions examples/demo-ai-chatbot.html → examples/demo-vercel-ai-sdk.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AI Chatbot Demo — almostnode agent runtime</title>
<title>Vercel AI SDK Demo — almostnode agent runtime</title>
<link rel="stylesheet" href="./shared-styles.css" />
<style>
.layout {
Expand Down Expand Up @@ -62,6 +62,71 @@
font-size: 0.6rem;
color: var(--accent-dim);
}
.setup-overlay {
position: absolute;
inset: 0;
background: rgba(0,0,0,0.85);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.setup-overlay.hidden { display: none; }
.setup-dialog {
background: var(--surface);
border: 1px solid var(--border);
padding: 32px;
max-width: 420px;
width: 90%;
}
.setup-dialog h2 {
font-family: var(--mono);
font-size: 0.9rem;
color: var(--text-bright);
margin-bottom: 8px;
}
.setup-dialog p {
font-size: 0.8rem;
color: var(--text-dim);
line-height: 1.6;
margin-bottom: 16px;
}
.setup-dialog .privacy-note {
font-family: var(--mono);
font-size: 0.65rem;
color: var(--accent-dim);
background: var(--accent-bg);
border: 1px solid var(--accent-border);
padding: 8px 12px;
margin-bottom: 16px;
line-height: 1.5;
}
.setup-dialog input {
width: 100%;
padding: 10px 12px;
font-family: var(--mono);
font-size: 0.78rem;
border: 1px solid var(--border);
background: var(--black);
color: var(--text);
margin-bottom: 12px;
}
.setup-dialog input::placeholder { color: var(--text-dim); }
.setup-dialog button {
width: 100%;
padding: 10px;
font-family: var(--mono);
font-size: 0.78rem;
font-weight: 600;
background: var(--accent);
color: var(--black);
border: none;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.setup-dialog button:hover { background: #00ffaa; }
.setup-dialog button:disabled { opacity: 0.4; cursor: not-allowed; }
.loading-content {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -89,13 +154,13 @@
<span class="sep">/</span>
<a href="../index.html" class="logo">almostnode</a>
<span class="sep">/</span>
<span class="title">AI Chatbot</span>
<span class="tag">next.js · openai · streaming</span>
<span class="title">Vercel AI SDK</span>
<span class="tag">next.js · ai sdk · openai · streaming</span>
</div>

<div class="layout">
<!-- Preview -->
<div class="panel" style="overflow:hidden;">
<div class="panel" style="overflow:hidden;position:relative;">
<div class="panel-header">
<span class="panel-label">Preview</span>
<div style="display:flex;gap:6px;">
Expand All @@ -109,6 +174,15 @@
<p>Starting dev server...</p>
</div>
</div>
<div class="setup-overlay" id="setupOverlay">
<div class="setup-dialog">
<h2>OpenAI API Key Required</h2>
<p>This demo uses the Vercel AI SDK to stream responses from OpenAI. Enter your API key to get started.</p>
<div class="privacy-note">Your key stays in your browser. It is never sent to our servers — API calls go directly from your browser to OpenAI via a CORS proxy.</div>
<input type="password" id="setupKeyInput" placeholder="sk-..." />
<button id="setupKeyBtn" disabled>Connect</button>
</div>
</div>
</div>

<!-- Bottom -->
Expand All @@ -134,6 +208,6 @@
</div>
</div>

<script type="module" src="/src/ai-chatbot-demo-entry.ts"></script>
<script type="module" src="/src/vercel-ai-sdk-demo-entry.ts"></script>
</body>
</html>
12 changes: 12 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1362,6 +1362,18 @@ <h3>Vitest Testing</h3>
<span class="demo-tag">vitest · npm run test</span>
</a>

<a class="demo-card" href="./examples/demo-convex-app.html">
<h3>Convex Todo App</h3>
<p>Real-time todo list with Next.js and Convex — deploy and sync from the browser.</p>
<span class="demo-tag">next.js · convex</span>
</a>

<a class="demo-card" href="./examples/demo-vercel-ai-sdk.html">
<h3>Vercel AI SDK</h3>
<p>Streaming AI chatbot with Next.js and OpenAI — real-time token streaming.</p>
<span class="demo-tag">next.js · ai sdk · openai</span>
</a>

</div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "almostnode",
"version": "0.2.12",
"version": "0.2.13",
"description": "Node.js in your browser. Just like that.",
"type": "module",
"license": "MIT",
Expand Down
24 changes: 24 additions & 0 deletions src/config/cdn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Centralized CDN version pins and URLs.
* Change versions here to update them across the entire platform.
*/

// ── Version pins ──
export const REACT_VERSION = '18.2.0';
export const ESBUILD_WASM_VERSION = '0.20.0';
export const ROLLUP_BROWSER_VERSION = '4.9.0';
export const REACT_REFRESH_VERSION = '0.14.0';

// ── React CDN URLs ──
export const REACT_CDN = `https://esm.sh/react@${REACT_VERSION}`;
export const REACT_DOM_CDN = `https://esm.sh/react-dom@${REACT_VERSION}`;
export const REACT_REFRESH_CDN = `https://esm.sh/react-refresh@${REACT_REFRESH_VERSION}/runtime`;

// ── Build tool CDN URLs ──
export const ESBUILD_WASM_ESM_CDN = `https://esm.sh/esbuild-wasm@${ESBUILD_WASM_VERSION}`;
export const ESBUILD_WASM_BINARY_CDN = `https://unpkg.com/esbuild-wasm@${ESBUILD_WASM_VERSION}/esbuild.wasm`;
export const ESBUILD_WASM_BROWSER_CDN = `https://unpkg.com/esbuild-wasm@${ESBUILD_WASM_VERSION}/esm/browser.min.js`;
export const ROLLUP_BROWSER_CDN = `https://esm.sh/@rollup/browser@${ROLLUP_BROWSER_VERSION}`;

// ── Styling CDN URLs ──
export const TAILWIND_CDN_URL = 'https://cdn.tailwindcss.com';
Loading