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
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Bridge
|
|
3
|
+
* Connects Service Worker requests to virtual HTTP servers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Server,
|
|
8
|
+
ResponseData,
|
|
9
|
+
setServerListenCallback,
|
|
10
|
+
setServerCloseCallback,
|
|
11
|
+
getServer,
|
|
12
|
+
} from './shims/http';
|
|
13
|
+
import { EventEmitter } from './shims/events';
|
|
14
|
+
import { Buffer } from './shims/stream';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Interface for virtual servers that can be registered with the bridge
|
|
18
|
+
*/
|
|
19
|
+
export interface IVirtualServer {
|
|
20
|
+
listening: boolean;
|
|
21
|
+
address(): { port: number; address: string; family: string } | null;
|
|
22
|
+
handleRequest(
|
|
23
|
+
method: string,
|
|
24
|
+
url: string,
|
|
25
|
+
headers: Record<string, string>,
|
|
26
|
+
body?: Buffer | string
|
|
27
|
+
): Promise<ResponseData>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface VirtualServer {
|
|
31
|
+
server: Server | IVirtualServer;
|
|
32
|
+
port: number;
|
|
33
|
+
hostname: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface BridgeOptions {
|
|
37
|
+
baseUrl?: string;
|
|
38
|
+
onServerReady?: (port: number, url: string) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Server Bridge manages virtual HTTP servers and routes requests
|
|
43
|
+
*/
|
|
44
|
+
export class ServerBridge extends EventEmitter {
|
|
45
|
+
private servers: Map<number, VirtualServer> = new Map();
|
|
46
|
+
private baseUrl: string;
|
|
47
|
+
private options: BridgeOptions;
|
|
48
|
+
private messageChannel: MessageChannel | null = null;
|
|
49
|
+
private serviceWorkerReady: boolean = false;
|
|
50
|
+
|
|
51
|
+
constructor(options: BridgeOptions = {}) {
|
|
52
|
+
super();
|
|
53
|
+
this.options = options;
|
|
54
|
+
|
|
55
|
+
// Handle browser vs Node.js environment
|
|
56
|
+
if (typeof location !== 'undefined') {
|
|
57
|
+
this.baseUrl = options.baseUrl || `${location.protocol}//${location.host}`;
|
|
58
|
+
} else {
|
|
59
|
+
this.baseUrl = options.baseUrl || 'http://localhost';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Set up auto-registration from http module
|
|
63
|
+
setServerListenCallback((port, server) => {
|
|
64
|
+
this.registerServer(server, port);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
setServerCloseCallback((port) => {
|
|
68
|
+
this.unregisterServer(port);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Register a server on a port
|
|
74
|
+
*/
|
|
75
|
+
registerServer(server: Server | IVirtualServer, port: number, hostname: string = '0.0.0.0'): void {
|
|
76
|
+
this.servers.set(port, { server, port, hostname });
|
|
77
|
+
|
|
78
|
+
// Emit server-ready event
|
|
79
|
+
const url = this.getServerUrl(port);
|
|
80
|
+
this.emit('server-ready', port, url);
|
|
81
|
+
|
|
82
|
+
if (this.options.onServerReady) {
|
|
83
|
+
this.options.onServerReady(port, url);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Notify service worker if connected
|
|
87
|
+
this.notifyServiceWorker('server-registered', { port, hostname });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Unregister a server
|
|
92
|
+
*/
|
|
93
|
+
unregisterServer(port: number): void {
|
|
94
|
+
this.servers.delete(port);
|
|
95
|
+
this.notifyServiceWorker('server-unregistered', { port });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get server URL for a port
|
|
100
|
+
*/
|
|
101
|
+
getServerUrl(port: number): string {
|
|
102
|
+
return `${this.baseUrl}/__virtual__/${port}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get all registered server ports
|
|
107
|
+
*/
|
|
108
|
+
getServerPorts(): number[] {
|
|
109
|
+
return [...this.servers.keys()];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Handle an incoming request from Service Worker
|
|
114
|
+
*/
|
|
115
|
+
async handleRequest(
|
|
116
|
+
port: number,
|
|
117
|
+
method: string,
|
|
118
|
+
url: string,
|
|
119
|
+
headers: Record<string, string>,
|
|
120
|
+
body?: ArrayBuffer
|
|
121
|
+
): Promise<ResponseData> {
|
|
122
|
+
const virtualServer = this.servers.get(port);
|
|
123
|
+
|
|
124
|
+
if (!virtualServer) {
|
|
125
|
+
return {
|
|
126
|
+
statusCode: 503,
|
|
127
|
+
statusMessage: 'Service Unavailable',
|
|
128
|
+
headers: { 'Content-Type': 'text/plain' },
|
|
129
|
+
body: Buffer.from(`No server listening on port ${port}`),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const bodyBuffer = body ? Buffer.from(new Uint8Array(body)) : undefined;
|
|
135
|
+
return await virtualServer.server.handleRequest(method, url, headers, bodyBuffer);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
const message = error instanceof Error ? error.message : 'Internal Server Error';
|
|
138
|
+
return {
|
|
139
|
+
statusCode: 500,
|
|
140
|
+
statusMessage: 'Internal Server Error',
|
|
141
|
+
headers: { 'Content-Type': 'text/plain' },
|
|
142
|
+
body: Buffer.from(message),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Initialize Service Worker communication
|
|
149
|
+
*/
|
|
150
|
+
async initServiceWorker(): Promise<void> {
|
|
151
|
+
if (!('serviceWorker' in navigator)) {
|
|
152
|
+
throw new Error('Service Workers not supported');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Register service worker
|
|
156
|
+
const registration = await navigator.serviceWorker.register('/__sw__.js', {
|
|
157
|
+
scope: '/',
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Wait for service worker to be active
|
|
161
|
+
const sw = registration.active || registration.waiting || registration.installing;
|
|
162
|
+
|
|
163
|
+
if (!sw) {
|
|
164
|
+
throw new Error('Service Worker registration failed');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
await new Promise<void>((resolve) => {
|
|
168
|
+
if (sw.state === 'activated') {
|
|
169
|
+
resolve();
|
|
170
|
+
} else {
|
|
171
|
+
sw.addEventListener('statechange', () => {
|
|
172
|
+
if (sw.state === 'activated') {
|
|
173
|
+
resolve();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Set up message channel for communication
|
|
180
|
+
this.messageChannel = new MessageChannel();
|
|
181
|
+
this.messageChannel.port1.onmessage = this.handleServiceWorkerMessage.bind(this);
|
|
182
|
+
|
|
183
|
+
// Send port to service worker
|
|
184
|
+
sw.postMessage({ type: 'init', port: this.messageChannel.port2 }, [
|
|
185
|
+
this.messageChannel.port2,
|
|
186
|
+
]);
|
|
187
|
+
|
|
188
|
+
this.serviceWorkerReady = true;
|
|
189
|
+
this.emit('sw-ready');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Handle messages from Service Worker
|
|
194
|
+
*/
|
|
195
|
+
private async handleServiceWorkerMessage(event: MessageEvent): Promise<void> {
|
|
196
|
+
const { type, id, data } = event.data;
|
|
197
|
+
|
|
198
|
+
console.log('[ServerBridge] SW message:', type, id, data?.url);
|
|
199
|
+
|
|
200
|
+
if (type === 'request') {
|
|
201
|
+
const { port, method, url, headers, body, streaming } = data;
|
|
202
|
+
|
|
203
|
+
console.log('[ServerBridge] Handling request:', port, method, url, 'streaming:', streaming);
|
|
204
|
+
if (streaming) {
|
|
205
|
+
console.log('[ServerBridge] 🔴 Will use streaming handler');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
if (streaming) {
|
|
210
|
+
// Handle streaming request
|
|
211
|
+
await this.handleStreamingRequest(id, port, method, url, headers, body);
|
|
212
|
+
} else {
|
|
213
|
+
// Handle regular request
|
|
214
|
+
const response = await this.handleRequest(port, method, url, headers, body);
|
|
215
|
+
console.log('[ServerBridge] Response:', response.statusCode, 'body length:', response.body?.length);
|
|
216
|
+
|
|
217
|
+
// Convert body to base64 string to avoid structured cloning issues with Uint8Array
|
|
218
|
+
let bodyBase64 = '';
|
|
219
|
+
if (response.body && response.body.length > 0) {
|
|
220
|
+
// Convert Uint8Array to base64 string
|
|
221
|
+
const bytes = response.body instanceof Uint8Array ? response.body : new Uint8Array(0);
|
|
222
|
+
let binary = '';
|
|
223
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
224
|
+
binary += String.fromCharCode(bytes[i]);
|
|
225
|
+
}
|
|
226
|
+
bodyBase64 = btoa(binary);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
console.log('[ServerBridge] Sending response to SW, body base64 length:', bodyBase64.length);
|
|
230
|
+
|
|
231
|
+
this.messageChannel?.port1.postMessage({
|
|
232
|
+
type: 'response',
|
|
233
|
+
id,
|
|
234
|
+
data: {
|
|
235
|
+
statusCode: response.statusCode,
|
|
236
|
+
statusMessage: response.statusMessage,
|
|
237
|
+
headers: response.headers,
|
|
238
|
+
bodyBase64: bodyBase64,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
} catch (error) {
|
|
243
|
+
this.messageChannel?.port1.postMessage({
|
|
244
|
+
type: 'response',
|
|
245
|
+
id,
|
|
246
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Handle a streaming request - sends chunks as they arrive
|
|
254
|
+
*/
|
|
255
|
+
private async handleStreamingRequest(
|
|
256
|
+
id: number,
|
|
257
|
+
port: number,
|
|
258
|
+
method: string,
|
|
259
|
+
url: string,
|
|
260
|
+
headers: Record<string, string>,
|
|
261
|
+
body?: ArrayBuffer
|
|
262
|
+
): Promise<void> {
|
|
263
|
+
const virtualServer = this.servers.get(port);
|
|
264
|
+
|
|
265
|
+
if (!virtualServer) {
|
|
266
|
+
this.messageChannel?.port1.postMessage({
|
|
267
|
+
type: 'stream-start',
|
|
268
|
+
id,
|
|
269
|
+
data: { statusCode: 503, statusMessage: 'Service Unavailable', headers: {} },
|
|
270
|
+
});
|
|
271
|
+
this.messageChannel?.port1.postMessage({ type: 'stream-end', id });
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Check if the server supports streaming (has handleStreamingRequest method)
|
|
276
|
+
const server = virtualServer.server as any;
|
|
277
|
+
if (typeof server.handleStreamingRequest === 'function') {
|
|
278
|
+
console.log('[ServerBridge] 🟢 Server has streaming support, calling handleStreamingRequest');
|
|
279
|
+
// Use streaming handler
|
|
280
|
+
const bodyBuffer = body ? Buffer.from(new Uint8Array(body)) : undefined;
|
|
281
|
+
|
|
282
|
+
await server.handleStreamingRequest(
|
|
283
|
+
method,
|
|
284
|
+
url,
|
|
285
|
+
headers,
|
|
286
|
+
bodyBuffer,
|
|
287
|
+
// onStart - called with headers
|
|
288
|
+
(statusCode: number, statusMessage: string, respHeaders: Record<string, string>) => {
|
|
289
|
+
console.log('[ServerBridge] 🟢 onStart called, sending stream-start');
|
|
290
|
+
this.messageChannel?.port1.postMessage({
|
|
291
|
+
type: 'stream-start',
|
|
292
|
+
id,
|
|
293
|
+
data: { statusCode, statusMessage, headers: respHeaders },
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
// onChunk - called for each chunk
|
|
297
|
+
(chunk: string | Uint8Array) => {
|
|
298
|
+
const bytes = typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk;
|
|
299
|
+
let binary = '';
|
|
300
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
301
|
+
binary += String.fromCharCode(bytes[i]);
|
|
302
|
+
}
|
|
303
|
+
const chunkBase64 = btoa(binary);
|
|
304
|
+
console.log('[ServerBridge] 🟡 onChunk called, sending stream-chunk, size:', chunkBase64.length);
|
|
305
|
+
this.messageChannel?.port1.postMessage({
|
|
306
|
+
type: 'stream-chunk',
|
|
307
|
+
id,
|
|
308
|
+
data: { chunkBase64 },
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
// onEnd - called when response is complete
|
|
312
|
+
() => {
|
|
313
|
+
console.log('[ServerBridge] 🟢 onEnd called, sending stream-end');
|
|
314
|
+
this.messageChannel?.port1.postMessage({ type: 'stream-end', id });
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
} else {
|
|
318
|
+
// Fall back to regular request handling
|
|
319
|
+
const bodyBuffer = body ? Buffer.from(new Uint8Array(body)) : undefined;
|
|
320
|
+
const response = await virtualServer.server.handleRequest(method, url, headers, bodyBuffer);
|
|
321
|
+
|
|
322
|
+
// Send as a single stream
|
|
323
|
+
this.messageChannel?.port1.postMessage({
|
|
324
|
+
type: 'stream-start',
|
|
325
|
+
id,
|
|
326
|
+
data: {
|
|
327
|
+
statusCode: response.statusCode,
|
|
328
|
+
statusMessage: response.statusMessage,
|
|
329
|
+
headers: response.headers,
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (response.body && response.body.length > 0) {
|
|
334
|
+
const bytes = response.body instanceof Uint8Array ? response.body : new Uint8Array(0);
|
|
335
|
+
let binary = '';
|
|
336
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
337
|
+
binary += String.fromCharCode(bytes[i]);
|
|
338
|
+
}
|
|
339
|
+
this.messageChannel?.port1.postMessage({
|
|
340
|
+
type: 'stream-chunk',
|
|
341
|
+
id,
|
|
342
|
+
data: { chunkBase64: btoa(binary) },
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
this.messageChannel?.port1.postMessage({ type: 'stream-end', id });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Send message to Service Worker
|
|
352
|
+
*/
|
|
353
|
+
private notifyServiceWorker(type: string, data: unknown): void {
|
|
354
|
+
if (this.serviceWorkerReady && this.messageChannel) {
|
|
355
|
+
this.messageChannel.port1.postMessage({ type, data });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Create a mock request handler for testing without Service Worker
|
|
361
|
+
*/
|
|
362
|
+
createFetchHandler(): (request: Request) => Promise<Response> {
|
|
363
|
+
return async (request: Request): Promise<Response> => {
|
|
364
|
+
const url = new URL(request.url);
|
|
365
|
+
|
|
366
|
+
// Check if this is a virtual server request
|
|
367
|
+
const match = url.pathname.match(/^\/__virtual__\/(\d+)(\/.*)?$/);
|
|
368
|
+
if (!match) {
|
|
369
|
+
throw new Error('Not a virtual server request');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const port = parseInt(match[1], 10);
|
|
373
|
+
const path = match[2] || '/';
|
|
374
|
+
|
|
375
|
+
// Build headers object
|
|
376
|
+
const headers: Record<string, string> = {};
|
|
377
|
+
request.headers.forEach((value, key) => {
|
|
378
|
+
headers[key] = value;
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Get body if present
|
|
382
|
+
let body: ArrayBuffer | undefined;
|
|
383
|
+
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
384
|
+
body = await request.arrayBuffer();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Handle request
|
|
388
|
+
const response = await this.handleRequest(
|
|
389
|
+
port,
|
|
390
|
+
request.method,
|
|
391
|
+
path + url.search,
|
|
392
|
+
headers,
|
|
393
|
+
body
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
// Convert to fetch Response
|
|
397
|
+
return new Response(response.body, {
|
|
398
|
+
status: response.statusCode,
|
|
399
|
+
statusText: response.statusMessage,
|
|
400
|
+
headers: response.headers,
|
|
401
|
+
});
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Global bridge instance
|
|
407
|
+
let globalBridge: ServerBridge | null = null;
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Get or create the global server bridge
|
|
411
|
+
*/
|
|
412
|
+
export function getServerBridge(options?: BridgeOptions): ServerBridge {
|
|
413
|
+
if (!globalBridge) {
|
|
414
|
+
globalBridge = new ServerBridge(options);
|
|
415
|
+
}
|
|
416
|
+
return globalBridge;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Reset the global bridge (for testing)
|
|
421
|
+
*/
|
|
422
|
+
export function resetServerBridge(): void {
|
|
423
|
+
globalBridge = null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export default ServerBridge;
|