almostnode 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +731 -0
- package/dist/__sw__.js +394 -0
- package/dist/ai-chatbot-demo-entry.d.ts +6 -0
- package/dist/ai-chatbot-demo-entry.d.ts.map +1 -0
- package/dist/ai-chatbot-demo.d.ts +42 -0
- package/dist/ai-chatbot-demo.d.ts.map +1 -0
- package/dist/assets/runtime-worker-D9x_Ddwz.js +60543 -0
- package/dist/assets/runtime-worker-D9x_Ddwz.js.map +1 -0
- package/dist/convex-app-demo-entry.d.ts +6 -0
- package/dist/convex-app-demo-entry.d.ts.map +1 -0
- package/dist/convex-app-demo.d.ts +68 -0
- package/dist/convex-app-demo.d.ts.map +1 -0
- package/dist/cors-proxy.d.ts +46 -0
- package/dist/cors-proxy.d.ts.map +1 -0
- package/dist/create-runtime.d.ts +42 -0
- package/dist/create-runtime.d.ts.map +1 -0
- package/dist/demo.d.ts +6 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/dev-server.d.ts +97 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/frameworks/next-dev-server.d.ts +202 -0
- package/dist/frameworks/next-dev-server.d.ts.map +1 -0
- package/dist/frameworks/vite-dev-server.d.ts +85 -0
- package/dist/frameworks/vite-dev-server.d.ts.map +1 -0
- package/dist/index.cjs +14965 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +14867 -0
- package/dist/index.mjs.map +1 -0
- package/dist/next-demo.d.ts +49 -0
- package/dist/next-demo.d.ts.map +1 -0
- package/dist/npm/index.d.ts +71 -0
- package/dist/npm/index.d.ts.map +1 -0
- package/dist/npm/registry.d.ts +66 -0
- package/dist/npm/registry.d.ts.map +1 -0
- package/dist/npm/resolver.d.ts +52 -0
- package/dist/npm/resolver.d.ts.map +1 -0
- package/dist/npm/tarball.d.ts +29 -0
- package/dist/npm/tarball.d.ts.map +1 -0
- package/dist/runtime-interface.d.ts +90 -0
- package/dist/runtime-interface.d.ts.map +1 -0
- package/dist/runtime.d.ts +103 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/sandbox-helpers.d.ts +43 -0
- package/dist/sandbox-helpers.d.ts.map +1 -0
- package/dist/sandbox-runtime.d.ts +65 -0
- package/dist/sandbox-runtime.d.ts.map +1 -0
- package/dist/server-bridge.d.ts +89 -0
- package/dist/server-bridge.d.ts.map +1 -0
- package/dist/shims/assert.d.ts +51 -0
- package/dist/shims/assert.d.ts.map +1 -0
- package/dist/shims/async_hooks.d.ts +37 -0
- package/dist/shims/async_hooks.d.ts.map +1 -0
- package/dist/shims/buffer.d.ts +20 -0
- package/dist/shims/buffer.d.ts.map +1 -0
- package/dist/shims/child_process-browser.d.ts +92 -0
- package/dist/shims/child_process-browser.d.ts.map +1 -0
- package/dist/shims/child_process.d.ts +93 -0
- package/dist/shims/child_process.d.ts.map +1 -0
- package/dist/shims/chokidar.d.ts +55 -0
- package/dist/shims/chokidar.d.ts.map +1 -0
- package/dist/shims/cluster.d.ts +52 -0
- package/dist/shims/cluster.d.ts.map +1 -0
- package/dist/shims/crypto.d.ts +122 -0
- package/dist/shims/crypto.d.ts.map +1 -0
- package/dist/shims/dgram.d.ts +34 -0
- package/dist/shims/dgram.d.ts.map +1 -0
- package/dist/shims/diagnostics_channel.d.ts +80 -0
- package/dist/shims/diagnostics_channel.d.ts.map +1 -0
- package/dist/shims/dns.d.ts +87 -0
- package/dist/shims/dns.d.ts.map +1 -0
- package/dist/shims/domain.d.ts +25 -0
- package/dist/shims/domain.d.ts.map +1 -0
- package/dist/shims/esbuild.d.ts +105 -0
- package/dist/shims/esbuild.d.ts.map +1 -0
- package/dist/shims/events.d.ts +37 -0
- package/dist/shims/events.d.ts.map +1 -0
- package/dist/shims/fs.d.ts +115 -0
- package/dist/shims/fs.d.ts.map +1 -0
- package/dist/shims/fsevents.d.ts +67 -0
- package/dist/shims/fsevents.d.ts.map +1 -0
- package/dist/shims/http.d.ts +217 -0
- package/dist/shims/http.d.ts.map +1 -0
- package/dist/shims/http2.d.ts +81 -0
- package/dist/shims/http2.d.ts.map +1 -0
- package/dist/shims/https.d.ts +36 -0
- package/dist/shims/https.d.ts.map +1 -0
- package/dist/shims/inspector.d.ts +25 -0
- package/dist/shims/inspector.d.ts.map +1 -0
- package/dist/shims/module.d.ts +22 -0
- package/dist/shims/module.d.ts.map +1 -0
- package/dist/shims/net.d.ts +100 -0
- package/dist/shims/net.d.ts.map +1 -0
- package/dist/shims/os.d.ts +159 -0
- package/dist/shims/os.d.ts.map +1 -0
- package/dist/shims/path.d.ts +72 -0
- package/dist/shims/path.d.ts.map +1 -0
- package/dist/shims/perf_hooks.d.ts +50 -0
- package/dist/shims/perf_hooks.d.ts.map +1 -0
- package/dist/shims/process.d.ts +93 -0
- package/dist/shims/process.d.ts.map +1 -0
- package/dist/shims/querystring.d.ts +23 -0
- package/dist/shims/querystring.d.ts.map +1 -0
- package/dist/shims/readdirp.d.ts +52 -0
- package/dist/shims/readdirp.d.ts.map +1 -0
- package/dist/shims/readline.d.ts +62 -0
- package/dist/shims/readline.d.ts.map +1 -0
- package/dist/shims/rollup.d.ts +34 -0
- package/dist/shims/rollup.d.ts.map +1 -0
- package/dist/shims/sentry.d.ts +163 -0
- package/dist/shims/sentry.d.ts.map +1 -0
- package/dist/shims/stream.d.ts +181 -0
- package/dist/shims/stream.d.ts.map +1 -0
- package/dist/shims/tls.d.ts +53 -0
- package/dist/shims/tls.d.ts.map +1 -0
- package/dist/shims/tty.d.ts +30 -0
- package/dist/shims/tty.d.ts.map +1 -0
- package/dist/shims/url.d.ts +64 -0
- package/dist/shims/url.d.ts.map +1 -0
- package/dist/shims/util.d.ts +106 -0
- package/dist/shims/util.d.ts.map +1 -0
- package/dist/shims/v8.d.ts +73 -0
- package/dist/shims/v8.d.ts.map +1 -0
- package/dist/shims/vfs-adapter.d.ts +126 -0
- package/dist/shims/vfs-adapter.d.ts.map +1 -0
- package/dist/shims/vm.d.ts +45 -0
- package/dist/shims/vm.d.ts.map +1 -0
- package/dist/shims/worker_threads.d.ts +66 -0
- package/dist/shims/worker_threads.d.ts.map +1 -0
- package/dist/shims/ws.d.ts +66 -0
- package/dist/shims/ws.d.ts.map +1 -0
- package/dist/shims/zlib.d.ts +161 -0
- package/dist/shims/zlib.d.ts.map +1 -0
- package/dist/transform.d.ts +24 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/virtual-fs.d.ts +226 -0
- package/dist/virtual-fs.d.ts.map +1 -0
- package/dist/vite-demo.d.ts +35 -0
- package/dist/vite-demo.d.ts.map +1 -0
- package/dist/vite-sw.js +132 -0
- package/dist/worker/runtime-worker.d.ts +8 -0
- package/dist/worker/runtime-worker.d.ts.map +1 -0
- package/dist/worker-runtime.d.ts +50 -0
- package/dist/worker-runtime.d.ts.map +1 -0
- package/package.json +85 -0
- package/src/ai-chatbot-demo-entry.ts +244 -0
- package/src/ai-chatbot-demo.ts +509 -0
- package/src/convex-app-demo-entry.ts +1107 -0
- package/src/convex-app-demo.ts +1316 -0
- package/src/cors-proxy.ts +81 -0
- package/src/create-runtime.ts +147 -0
- package/src/demo.ts +304 -0
- package/src/dev-server.ts +274 -0
- package/src/frameworks/next-dev-server.ts +2224 -0
- package/src/frameworks/vite-dev-server.ts +702 -0
- package/src/index.ts +101 -0
- package/src/next-demo.ts +1784 -0
- package/src/npm/index.ts +347 -0
- package/src/npm/registry.ts +152 -0
- package/src/npm/resolver.ts +385 -0
- package/src/npm/tarball.ts +209 -0
- package/src/runtime-interface.ts +103 -0
- package/src/runtime.ts +1046 -0
- package/src/sandbox-helpers.ts +173 -0
- package/src/sandbox-runtime.ts +252 -0
- package/src/server-bridge.ts +426 -0
- package/src/shims/assert.ts +664 -0
- package/src/shims/async_hooks.ts +86 -0
- package/src/shims/buffer.ts +75 -0
- package/src/shims/child_process-browser.ts +217 -0
- package/src/shims/child_process.ts +463 -0
- package/src/shims/chokidar.ts +313 -0
- package/src/shims/cluster.ts +67 -0
- package/src/shims/crypto.ts +830 -0
- package/src/shims/dgram.ts +47 -0
- package/src/shims/diagnostics_channel.ts +196 -0
- package/src/shims/dns.ts +172 -0
- package/src/shims/domain.ts +58 -0
- package/src/shims/esbuild.ts +805 -0
- package/src/shims/events.ts +195 -0
- package/src/shims/fs.ts +803 -0
- package/src/shims/fsevents.ts +63 -0
- package/src/shims/http.ts +904 -0
- package/src/shims/http2.ts +96 -0
- package/src/shims/https.ts +86 -0
- package/src/shims/inspector.ts +30 -0
- package/src/shims/module.ts +82 -0
- package/src/shims/net.ts +359 -0
- package/src/shims/os.ts +195 -0
- package/src/shims/path.ts +199 -0
- package/src/shims/perf_hooks.ts +92 -0
- package/src/shims/process.ts +346 -0
- package/src/shims/querystring.ts +97 -0
- package/src/shims/readdirp.ts +228 -0
- package/src/shims/readline.ts +110 -0
- package/src/shims/rollup.ts +80 -0
- package/src/shims/sentry.ts +133 -0
- package/src/shims/stream.ts +1126 -0
- package/src/shims/tls.ts +95 -0
- package/src/shims/tty.ts +64 -0
- package/src/shims/url.ts +171 -0
- package/src/shims/util.ts +312 -0
- package/src/shims/v8.ts +113 -0
- package/src/shims/vfs-adapter.ts +402 -0
- package/src/shims/vm.ts +83 -0
- package/src/shims/worker_threads.ts +111 -0
- package/src/shims/ws.ts +382 -0
- package/src/shims/zlib.ts +289 -0
- package/src/transform.ts +313 -0
- package/src/types/external.d.ts +67 -0
- package/src/virtual-fs.ts +903 -0
- package/src/vite-demo.ts +577 -0
- package/src/worker/runtime-worker.ts +128 -0
- package/src/worker-runtime.ts +145 -0
package/src/vite-demo.ts
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite Demo - Running Vite in the browser using our Node.js shims
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { VirtualFS } from './virtual-fs';
|
|
6
|
+
import { Runtime } from './runtime';
|
|
7
|
+
import { PackageManager } from './npm';
|
|
8
|
+
import { ViteDevServer } from './frameworks/vite-dev-server';
|
|
9
|
+
import { getServerBridge } from './server-bridge';
|
|
10
|
+
import { Buffer } from './shims/stream';
|
|
11
|
+
|
|
12
|
+
// Create a simple Vite project in the virtual filesystem
|
|
13
|
+
export function createViteProject(vfs: VirtualFS): void {
|
|
14
|
+
// Create package.json
|
|
15
|
+
vfs.writeFileSync(
|
|
16
|
+
'/package.json',
|
|
17
|
+
JSON.stringify(
|
|
18
|
+
{
|
|
19
|
+
name: 'react-vite-browser-demo',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
type: 'module',
|
|
22
|
+
scripts: {
|
|
23
|
+
dev: 'vite',
|
|
24
|
+
build: 'vite build',
|
|
25
|
+
preview: 'vite preview',
|
|
26
|
+
},
|
|
27
|
+
dependencies: {
|
|
28
|
+
react: '^18.2.0',
|
|
29
|
+
'react-dom': '^18.2.0',
|
|
30
|
+
},
|
|
31
|
+
devDependencies: {
|
|
32
|
+
vite: '^5.0.0',
|
|
33
|
+
'@vitejs/plugin-react': '^4.2.0',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
null,
|
|
37
|
+
2
|
|
38
|
+
)
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Create vite.config.js with React plugin
|
|
42
|
+
vfs.writeFileSync(
|
|
43
|
+
'/vite.config.js',
|
|
44
|
+
`
|
|
45
|
+
import { defineConfig } from 'vite';
|
|
46
|
+
import react from '@vitejs/plugin-react';
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
root: '/',
|
|
50
|
+
plugins: [react()],
|
|
51
|
+
server: {
|
|
52
|
+
port: 3000,
|
|
53
|
+
strictPort: true,
|
|
54
|
+
},
|
|
55
|
+
build: {
|
|
56
|
+
outDir: 'dist',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
`
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Create index.html for React app
|
|
63
|
+
// Note: Use relative paths (./src/) so they work with /__virtual__/port/ URLs
|
|
64
|
+
// Import map resolves bare imports (react, react-dom) to CDN URLs
|
|
65
|
+
// IMPORTANT: Use ?dev to get development builds with DevTools/React Refresh support
|
|
66
|
+
vfs.writeFileSync(
|
|
67
|
+
'/index.html',
|
|
68
|
+
`<!DOCTYPE html>
|
|
69
|
+
<html lang="en">
|
|
70
|
+
<head>
|
|
71
|
+
<meta charset="UTF-8">
|
|
72
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
73
|
+
<title>React + Vite Browser Demo</title>
|
|
74
|
+
<script type="importmap">
|
|
75
|
+
{
|
|
76
|
+
"imports": {
|
|
77
|
+
"react": "https://esm.sh/react@18.2.0?dev",
|
|
78
|
+
"react/": "https://esm.sh/react@18.2.0&dev/",
|
|
79
|
+
"react-dom": "https://esm.sh/react-dom@18.2.0?dev",
|
|
80
|
+
"react-dom/": "https://esm.sh/react-dom@18.2.0&dev/"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
</head>
|
|
85
|
+
<body>
|
|
86
|
+
<div id="root"></div>
|
|
87
|
+
<script type="module" src="./src/main.jsx"></script>
|
|
88
|
+
</body>
|
|
89
|
+
</html>
|
|
90
|
+
`
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Create src directory and main.jsx
|
|
94
|
+
vfs.mkdirSync('/src', { recursive: true });
|
|
95
|
+
vfs.writeFileSync(
|
|
96
|
+
'/src/main.jsx',
|
|
97
|
+
`
|
|
98
|
+
import React from 'react';
|
|
99
|
+
import ReactDOM from 'react-dom/client';
|
|
100
|
+
import App from './App.jsx';
|
|
101
|
+
import './style.css';
|
|
102
|
+
|
|
103
|
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
104
|
+
<React.StrictMode>
|
|
105
|
+
<App />
|
|
106
|
+
</React.StrictMode>
|
|
107
|
+
);
|
|
108
|
+
`
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Create App.jsx
|
|
112
|
+
vfs.writeFileSync(
|
|
113
|
+
'/src/App.jsx',
|
|
114
|
+
`
|
|
115
|
+
import React, { useState } from 'react';
|
|
116
|
+
import Counter from './Counter.jsx';
|
|
117
|
+
|
|
118
|
+
function App() {
|
|
119
|
+
const [theme, setTheme] = useState('light');
|
|
120
|
+
|
|
121
|
+
const toggleTheme = () => {
|
|
122
|
+
setTheme(theme === 'light' ? 'dark' : 'light');
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div className={\`app \${theme}\`}>
|
|
127
|
+
<header>
|
|
128
|
+
<h1>⚡ React + Vite in Browser</h1>
|
|
129
|
+
<p>Running with shimmed Node.js APIs</p>
|
|
130
|
+
</header>
|
|
131
|
+
|
|
132
|
+
<main>
|
|
133
|
+
<Counter />
|
|
134
|
+
|
|
135
|
+
<div className="theme-toggle">
|
|
136
|
+
<button onClick={toggleTheme}>
|
|
137
|
+
{theme === 'light' ? '🌙 Dark Mode' : '☀️ Light Mode'}
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div className="info-card">
|
|
142
|
+
<h3>How it works</h3>
|
|
143
|
+
<ul>
|
|
144
|
+
<li>VirtualFS stores all files in memory</li>
|
|
145
|
+
<li>Node.js APIs are shimmed for the browser</li>
|
|
146
|
+
<li>Edit files on the left to see HMR updates</li>
|
|
147
|
+
</ul>
|
|
148
|
+
</div>
|
|
149
|
+
</main>
|
|
150
|
+
|
|
151
|
+
<footer>
|
|
152
|
+
Made with 💜 WebContainers
|
|
153
|
+
</footer>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export default App;
|
|
159
|
+
`
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Create Counter.jsx
|
|
163
|
+
vfs.writeFileSync(
|
|
164
|
+
'/src/Counter.jsx',
|
|
165
|
+
`
|
|
166
|
+
import React, { useState } from 'react';
|
|
167
|
+
|
|
168
|
+
function Counter() {
|
|
169
|
+
const [count, setCount] = useState(0);
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div className="counter-card">
|
|
173
|
+
<h2>Interactive Counter</h2>
|
|
174
|
+
<div className="counter-display">{count}</div>
|
|
175
|
+
<div className="counter-buttons">
|
|
176
|
+
<button onClick={() => setCount(c => c - 1)}>➖</button>
|
|
177
|
+
<button onClick={() => setCount(0)}>Reset</button>
|
|
178
|
+
<button onClick={() => setCount(c => c + 1)}>➕</button>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export default Counter;
|
|
185
|
+
`
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// Create style.css for React app
|
|
189
|
+
vfs.writeFileSync(
|
|
190
|
+
'/src/style.css',
|
|
191
|
+
`
|
|
192
|
+
* {
|
|
193
|
+
box-sizing: border-box;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
:root {
|
|
197
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
198
|
+
line-height: 1.5;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
body {
|
|
202
|
+
margin: 0;
|
|
203
|
+
min-height: 100vh;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.app {
|
|
207
|
+
min-height: 100vh;
|
|
208
|
+
display: flex;
|
|
209
|
+
flex-direction: column;
|
|
210
|
+
transition: all 0.3s ease;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.app.light {
|
|
214
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
215
|
+
color: white;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.app.dark {
|
|
219
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
220
|
+
color: #eee;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
header {
|
|
224
|
+
text-align: center;
|
|
225
|
+
padding: 2rem;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
header h1 {
|
|
229
|
+
font-size: 2.5rem;
|
|
230
|
+
margin: 0 0 0.5rem 0;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
header p {
|
|
234
|
+
opacity: 0.8;
|
|
235
|
+
margin: 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
main {
|
|
239
|
+
flex: 1;
|
|
240
|
+
display: flex;
|
|
241
|
+
flex-direction: column;
|
|
242
|
+
align-items: center;
|
|
243
|
+
gap: 1.5rem;
|
|
244
|
+
padding: 1rem;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.counter-card {
|
|
248
|
+
background: rgba(255, 255, 255, 0.15);
|
|
249
|
+
backdrop-filter: blur(10px);
|
|
250
|
+
border-radius: 16px;
|
|
251
|
+
padding: 2rem;
|
|
252
|
+
text-align: center;
|
|
253
|
+
min-width: 280px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.counter-card h2 {
|
|
257
|
+
margin: 0 0 1rem 0;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.counter-display {
|
|
261
|
+
font-size: 4rem;
|
|
262
|
+
font-weight: bold;
|
|
263
|
+
margin: 1rem 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.counter-buttons {
|
|
267
|
+
display: flex;
|
|
268
|
+
gap: 0.5rem;
|
|
269
|
+
justify-content: center;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
button {
|
|
273
|
+
padding: 0.75rem 1.5rem;
|
|
274
|
+
font-size: 1rem;
|
|
275
|
+
font-weight: 500;
|
|
276
|
+
border: none;
|
|
277
|
+
border-radius: 8px;
|
|
278
|
+
background: rgba(255, 255, 255, 0.2);
|
|
279
|
+
color: inherit;
|
|
280
|
+
cursor: pointer;
|
|
281
|
+
transition: all 0.2s ease;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
button:hover {
|
|
285
|
+
background: rgba(255, 255, 255, 0.3);
|
|
286
|
+
transform: translateY(-2px);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
button:active {
|
|
290
|
+
transform: translateY(0);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.theme-toggle button {
|
|
294
|
+
background: rgba(0, 0, 0, 0.2);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.info-card {
|
|
298
|
+
background: rgba(255, 255, 255, 0.1);
|
|
299
|
+
border-radius: 12px;
|
|
300
|
+
padding: 1.5rem;
|
|
301
|
+
max-width: 400px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.info-card h3 {
|
|
305
|
+
margin: 0 0 1rem 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.info-card ul {
|
|
309
|
+
margin: 0;
|
|
310
|
+
padding-left: 1.5rem;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.info-card li {
|
|
314
|
+
margin: 0.5rem 0;
|
|
315
|
+
opacity: 0.9;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
footer {
|
|
319
|
+
text-align: center;
|
|
320
|
+
padding: 1.5rem;
|
|
321
|
+
opacity: 0.7;
|
|
322
|
+
}
|
|
323
|
+
`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Initialize the demo
|
|
328
|
+
export async function initViteDemo(
|
|
329
|
+
outputElement: HTMLElement,
|
|
330
|
+
iframeElement: HTMLIFrameElement | null
|
|
331
|
+
): Promise<{ vfs: VirtualFS; runtime: Runtime; npm: PackageManager }> {
|
|
332
|
+
const log = (message: string) => {
|
|
333
|
+
const line = document.createElement('div');
|
|
334
|
+
line.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
|
|
335
|
+
outputElement.appendChild(line);
|
|
336
|
+
outputElement.scrollTop = outputElement.scrollHeight;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
log('Creating virtual file system...');
|
|
340
|
+
const vfs = new VirtualFS();
|
|
341
|
+
|
|
342
|
+
log('Creating Vite project structure...');
|
|
343
|
+
createViteProject(vfs);
|
|
344
|
+
|
|
345
|
+
log('Initializing runtime...');
|
|
346
|
+
const runtime = new Runtime(vfs, {
|
|
347
|
+
cwd: '/',
|
|
348
|
+
env: {
|
|
349
|
+
NODE_ENV: 'development',
|
|
350
|
+
},
|
|
351
|
+
onConsole: (method, args) => {
|
|
352
|
+
const prefix = method === 'error' ? '[ERROR]' : method === 'warn' ? '[WARN]' : '';
|
|
353
|
+
log(`${prefix} ${args.map((a) => String(a)).join(' ')}`);
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Initialize package manager
|
|
358
|
+
const npm = new PackageManager(vfs, { cwd: '/' });
|
|
359
|
+
|
|
360
|
+
// Set up file watcher demo
|
|
361
|
+
log('Setting up file watcher...');
|
|
362
|
+
vfs.watch('/src', { recursive: true }, (eventType, filename) => {
|
|
363
|
+
log(`File ${eventType}: ${filename}`);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
log('Vite demo initialized!');
|
|
367
|
+
log('');
|
|
368
|
+
log('Virtual FS contents:');
|
|
369
|
+
listFiles(vfs, '/', log, ' ');
|
|
370
|
+
|
|
371
|
+
return { vfs, runtime, npm };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Install Vite into the virtual file system
|
|
376
|
+
*/
|
|
377
|
+
export async function installVite(
|
|
378
|
+
npm: PackageManager,
|
|
379
|
+
log: (message: string) => void
|
|
380
|
+
): Promise<void> {
|
|
381
|
+
log('');
|
|
382
|
+
log('=== Installing Vite ===');
|
|
383
|
+
log('This will download Vite and its dependencies from npm...');
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
const result = await npm.install('vite@5.0.12', {
|
|
387
|
+
onProgress: (msg) => log(` ${msg}`),
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
log(`Installed ${result.added.length} packages`);
|
|
391
|
+
log('Vite installation complete!');
|
|
392
|
+
} catch (error) {
|
|
393
|
+
log(`ERROR installing Vite: ${error}`);
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Try to run Vite's createServer
|
|
400
|
+
*/
|
|
401
|
+
export async function runVite(
|
|
402
|
+
runtime: Runtime,
|
|
403
|
+
log: (message: string) => void
|
|
404
|
+
): Promise<unknown> {
|
|
405
|
+
log('');
|
|
406
|
+
log('=== Starting Vite ===');
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
// ESM packages are pre-transformed to CJS during npm install
|
|
410
|
+
// Try to require vite
|
|
411
|
+
log('Loading Vite module...');
|
|
412
|
+
|
|
413
|
+
const code = `
|
|
414
|
+
const vite = require('vite');
|
|
415
|
+
console.log('Vite loaded:', typeof vite);
|
|
416
|
+
console.log('Vite exports:', Object.keys(vite));
|
|
417
|
+
|
|
418
|
+
// Try to call createServer
|
|
419
|
+
async function start() {
|
|
420
|
+
try {
|
|
421
|
+
console.log('Creating Vite dev server...');
|
|
422
|
+
const server = await vite.createServer({
|
|
423
|
+
root: '/',
|
|
424
|
+
configFile: false,
|
|
425
|
+
server: {
|
|
426
|
+
port: 3000,
|
|
427
|
+
strictPort: true,
|
|
428
|
+
hmr: false, // Disable HMR for initial test
|
|
429
|
+
},
|
|
430
|
+
logLevel: 'info',
|
|
431
|
+
});
|
|
432
|
+
console.log('Vite server created!');
|
|
433
|
+
return server;
|
|
434
|
+
} catch (err) {
|
|
435
|
+
console.error('Failed to create Vite server:', err.message);
|
|
436
|
+
console.error(err.stack);
|
|
437
|
+
throw err;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
module.exports = { start };
|
|
442
|
+
`;
|
|
443
|
+
|
|
444
|
+
const { exports } = runtime.execute(code, '/run-vite.js');
|
|
445
|
+
const { start } = exports as { start: () => Promise<unknown> };
|
|
446
|
+
|
|
447
|
+
log('Executing Vite createServer...');
|
|
448
|
+
const server = await start();
|
|
449
|
+
log('Vite server started successfully!');
|
|
450
|
+
|
|
451
|
+
return server;
|
|
452
|
+
} catch (error) {
|
|
453
|
+
log(`ERROR running Vite: ${error}`);
|
|
454
|
+
if (error instanceof Error) {
|
|
455
|
+
log(`Stack: ${error.stack}`);
|
|
456
|
+
}
|
|
457
|
+
throw error;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function listFiles(
|
|
462
|
+
vfs: VirtualFS,
|
|
463
|
+
path: string,
|
|
464
|
+
log: (msg: string) => void,
|
|
465
|
+
indent: string
|
|
466
|
+
): void {
|
|
467
|
+
try {
|
|
468
|
+
const entries = vfs.readdirSync(path);
|
|
469
|
+
for (const entry of entries) {
|
|
470
|
+
const fullPath = path === '/' ? '/' + entry : path + '/' + entry;
|
|
471
|
+
const stats = vfs.statSync(fullPath);
|
|
472
|
+
if (stats.isDirectory()) {
|
|
473
|
+
log(`${indent}📁 ${entry}/`);
|
|
474
|
+
listFiles(vfs, fullPath, log, indent + ' ');
|
|
475
|
+
} else {
|
|
476
|
+
log(`${indent}📄 ${entry}`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
} catch (e) {
|
|
480
|
+
log(`${indent}Error: ${e}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Start the dev server using Service Worker approach
|
|
486
|
+
* This is the recommended way to run the preview - no npm install required!
|
|
487
|
+
*/
|
|
488
|
+
export async function startDevServer(
|
|
489
|
+
vfs: VirtualFS,
|
|
490
|
+
options: {
|
|
491
|
+
port?: number;
|
|
492
|
+
log?: (message: string) => void;
|
|
493
|
+
} = {}
|
|
494
|
+
): Promise<{
|
|
495
|
+
server: ViteDevServer;
|
|
496
|
+
url: string;
|
|
497
|
+
stop: () => void;
|
|
498
|
+
}> {
|
|
499
|
+
const port = options.port || 3000;
|
|
500
|
+
const log = options.log || console.log;
|
|
501
|
+
|
|
502
|
+
log('Starting dev server...');
|
|
503
|
+
|
|
504
|
+
// Create ViteDevServer
|
|
505
|
+
const server = new ViteDevServer(vfs, { port, root: '/' });
|
|
506
|
+
|
|
507
|
+
// Get the server bridge
|
|
508
|
+
const bridge = getServerBridge();
|
|
509
|
+
|
|
510
|
+
// Initialize Service Worker
|
|
511
|
+
try {
|
|
512
|
+
log('Initializing Service Worker...');
|
|
513
|
+
await bridge.initServiceWorker();
|
|
514
|
+
log('Service Worker ready');
|
|
515
|
+
} catch (error) {
|
|
516
|
+
log(`Warning: Service Worker failed to initialize: ${error}`);
|
|
517
|
+
log('Falling back to direct request handling...');
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Register the server with the bridge
|
|
521
|
+
// The bridge will route /__virtual__/{port}/* requests to this server
|
|
522
|
+
bridge.on('server-ready', (p: unknown, u: unknown) => {
|
|
523
|
+
log(`Server ready at ${u}`);
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// Wire up the ViteDevServer to handle requests through the bridge
|
|
527
|
+
// We need to make the server compatible with the bridge's http.Server interface
|
|
528
|
+
const httpServer = createHttpServerWrapper(server);
|
|
529
|
+
bridge.registerServer(httpServer, port);
|
|
530
|
+
|
|
531
|
+
// Start watching for file changes
|
|
532
|
+
server.start();
|
|
533
|
+
log('File watcher started');
|
|
534
|
+
|
|
535
|
+
// Set up HMR event forwarding
|
|
536
|
+
server.on('hmr-update', (update: unknown) => {
|
|
537
|
+
log(`HMR update: ${JSON.stringify(update)}`);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
const url = bridge.getServerUrl(port);
|
|
541
|
+
log(`Dev server running at: ${url}/`);
|
|
542
|
+
|
|
543
|
+
return {
|
|
544
|
+
server,
|
|
545
|
+
url: url + '/',
|
|
546
|
+
stop: () => {
|
|
547
|
+
server.stop();
|
|
548
|
+
bridge.unregisterServer(port);
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Create an http.Server-compatible wrapper around ViteDevServer
|
|
555
|
+
*/
|
|
556
|
+
function createHttpServerWrapper(devServer: ViteDevServer) {
|
|
557
|
+
return {
|
|
558
|
+
listening: true,
|
|
559
|
+
address: () => ({ port: devServer.getPort(), address: '0.0.0.0', family: 'IPv4' }),
|
|
560
|
+
async handleRequest(
|
|
561
|
+
method: string,
|
|
562
|
+
url: string,
|
|
563
|
+
headers: Record<string, string>,
|
|
564
|
+
body?: string | Buffer
|
|
565
|
+
) {
|
|
566
|
+
const bodyBuffer = body
|
|
567
|
+
? typeof body === 'string'
|
|
568
|
+
? Buffer.from(body)
|
|
569
|
+
: body
|
|
570
|
+
: undefined;
|
|
571
|
+
return devServer.handleRequest(method, url, headers, bodyBuffer);
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Export for use in the demo page
|
|
577
|
+
export { VirtualFS, Runtime, PackageManager, ViteDevServer };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Worker - Runs the Just Node runtime in a Web Worker
|
|
3
|
+
*
|
|
4
|
+
* This worker receives code execution requests from the main thread
|
|
5
|
+
* and runs them in isolation, preventing UI blocking.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { expose } from 'comlink';
|
|
9
|
+
import { VirtualFS } from '../virtual-fs';
|
|
10
|
+
import { Runtime } from '../runtime';
|
|
11
|
+
import type { VFSSnapshot, IRuntimeOptions, IExecuteResult } from '../runtime-interface';
|
|
12
|
+
|
|
13
|
+
let runtime: Runtime | null = null;
|
|
14
|
+
let vfs: VirtualFS | null = null;
|
|
15
|
+
let consoleCallback: ((method: string, args: unknown[]) => void) | null = null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Worker API exposed via Comlink
|
|
19
|
+
*/
|
|
20
|
+
const workerApi = {
|
|
21
|
+
/**
|
|
22
|
+
* Initialize the worker with a VFS snapshot and runtime options
|
|
23
|
+
*/
|
|
24
|
+
init(vfsSnapshot: VFSSnapshot, options: IRuntimeOptions): void {
|
|
25
|
+
console.log('[Worker] Initializing with', vfsSnapshot.files.length, 'files');
|
|
26
|
+
|
|
27
|
+
// Restore VFS from snapshot
|
|
28
|
+
vfs = VirtualFS.fromSnapshot(vfsSnapshot);
|
|
29
|
+
|
|
30
|
+
// Create runtime with console forwarding
|
|
31
|
+
const runtimeOptions: IRuntimeOptions = {
|
|
32
|
+
...options,
|
|
33
|
+
onConsole: (method, args) => {
|
|
34
|
+
// Forward console output to main thread
|
|
35
|
+
if (consoleCallback) {
|
|
36
|
+
consoleCallback(method, args);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
runtime = new Runtime(vfs, runtimeOptions);
|
|
42
|
+
console.log('[Worker] Runtime initialized');
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Set the console callback for forwarding output to main thread
|
|
47
|
+
*/
|
|
48
|
+
setConsoleCallback(callback: ((method: string, args: unknown[]) => void) | null): void {
|
|
49
|
+
consoleCallback = callback;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Sync a file change from the main thread
|
|
54
|
+
*/
|
|
55
|
+
syncFile(path: string, content: string | null): void {
|
|
56
|
+
if (!vfs) {
|
|
57
|
+
console.warn('[Worker] VFS not initialized, cannot sync file:', path);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (content === null) {
|
|
62
|
+
// File was deleted
|
|
63
|
+
try {
|
|
64
|
+
vfs.unlinkSync(path);
|
|
65
|
+
console.log('[Worker] Deleted file:', path);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
// File might not exist, that's ok
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
// File was created or modified
|
|
71
|
+
vfs.writeFileSync(path, content);
|
|
72
|
+
console.log('[Worker] Synced file:', path);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Clear module cache for this file to pick up changes
|
|
76
|
+
if (runtime) {
|
|
77
|
+
runtime.clearCache();
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Execute code in the worker
|
|
83
|
+
*/
|
|
84
|
+
async execute(code: string, filename?: string): Promise<IExecuteResult> {
|
|
85
|
+
if (!runtime) {
|
|
86
|
+
throw new Error('Worker runtime not initialized. Call init() first.');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('[Worker] Executing code in file:', filename);
|
|
90
|
+
return runtime.execute(code, filename);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Run a file from the VFS
|
|
95
|
+
*/
|
|
96
|
+
async runFile(filename: string): Promise<IExecuteResult> {
|
|
97
|
+
if (!runtime) {
|
|
98
|
+
throw new Error('Worker runtime not initialized. Call init() first.');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log('[Worker] Running file:', filename);
|
|
102
|
+
return runtime.runFile(filename);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Clear the module cache
|
|
107
|
+
*/
|
|
108
|
+
clearCache(): void {
|
|
109
|
+
if (runtime) {
|
|
110
|
+
runtime.clearCache();
|
|
111
|
+
console.log('[Worker] Cache cleared');
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get current VFS state (for debugging)
|
|
117
|
+
*/
|
|
118
|
+
getVFSSnapshot(): VFSSnapshot | null {
|
|
119
|
+
if (!vfs) return null;
|
|
120
|
+
return vfs.toSnapshot();
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Expose the API via Comlink
|
|
125
|
+
expose(workerApi);
|
|
126
|
+
|
|
127
|
+
// Log that worker is ready
|
|
128
|
+
console.log('[Worker] Runtime worker loaded and ready');
|