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.
Files changed (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +731 -0
  3. package/dist/__sw__.js +394 -0
  4. package/dist/ai-chatbot-demo-entry.d.ts +6 -0
  5. package/dist/ai-chatbot-demo-entry.d.ts.map +1 -0
  6. package/dist/ai-chatbot-demo.d.ts +42 -0
  7. package/dist/ai-chatbot-demo.d.ts.map +1 -0
  8. package/dist/assets/runtime-worker-D9x_Ddwz.js +60543 -0
  9. package/dist/assets/runtime-worker-D9x_Ddwz.js.map +1 -0
  10. package/dist/convex-app-demo-entry.d.ts +6 -0
  11. package/dist/convex-app-demo-entry.d.ts.map +1 -0
  12. package/dist/convex-app-demo.d.ts +68 -0
  13. package/dist/convex-app-demo.d.ts.map +1 -0
  14. package/dist/cors-proxy.d.ts +46 -0
  15. package/dist/cors-proxy.d.ts.map +1 -0
  16. package/dist/create-runtime.d.ts +42 -0
  17. package/dist/create-runtime.d.ts.map +1 -0
  18. package/dist/demo.d.ts +6 -0
  19. package/dist/demo.d.ts.map +1 -0
  20. package/dist/dev-server.d.ts +97 -0
  21. package/dist/dev-server.d.ts.map +1 -0
  22. package/dist/frameworks/next-dev-server.d.ts +202 -0
  23. package/dist/frameworks/next-dev-server.d.ts.map +1 -0
  24. package/dist/frameworks/vite-dev-server.d.ts +85 -0
  25. package/dist/frameworks/vite-dev-server.d.ts.map +1 -0
  26. package/dist/index.cjs +14965 -0
  27. package/dist/index.cjs.map +1 -0
  28. package/dist/index.d.ts +71 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.mjs +14867 -0
  31. package/dist/index.mjs.map +1 -0
  32. package/dist/next-demo.d.ts +49 -0
  33. package/dist/next-demo.d.ts.map +1 -0
  34. package/dist/npm/index.d.ts +71 -0
  35. package/dist/npm/index.d.ts.map +1 -0
  36. package/dist/npm/registry.d.ts +66 -0
  37. package/dist/npm/registry.d.ts.map +1 -0
  38. package/dist/npm/resolver.d.ts +52 -0
  39. package/dist/npm/resolver.d.ts.map +1 -0
  40. package/dist/npm/tarball.d.ts +29 -0
  41. package/dist/npm/tarball.d.ts.map +1 -0
  42. package/dist/runtime-interface.d.ts +90 -0
  43. package/dist/runtime-interface.d.ts.map +1 -0
  44. package/dist/runtime.d.ts +103 -0
  45. package/dist/runtime.d.ts.map +1 -0
  46. package/dist/sandbox-helpers.d.ts +43 -0
  47. package/dist/sandbox-helpers.d.ts.map +1 -0
  48. package/dist/sandbox-runtime.d.ts +65 -0
  49. package/dist/sandbox-runtime.d.ts.map +1 -0
  50. package/dist/server-bridge.d.ts +89 -0
  51. package/dist/server-bridge.d.ts.map +1 -0
  52. package/dist/shims/assert.d.ts +51 -0
  53. package/dist/shims/assert.d.ts.map +1 -0
  54. package/dist/shims/async_hooks.d.ts +37 -0
  55. package/dist/shims/async_hooks.d.ts.map +1 -0
  56. package/dist/shims/buffer.d.ts +20 -0
  57. package/dist/shims/buffer.d.ts.map +1 -0
  58. package/dist/shims/child_process-browser.d.ts +92 -0
  59. package/dist/shims/child_process-browser.d.ts.map +1 -0
  60. package/dist/shims/child_process.d.ts +93 -0
  61. package/dist/shims/child_process.d.ts.map +1 -0
  62. package/dist/shims/chokidar.d.ts +55 -0
  63. package/dist/shims/chokidar.d.ts.map +1 -0
  64. package/dist/shims/cluster.d.ts +52 -0
  65. package/dist/shims/cluster.d.ts.map +1 -0
  66. package/dist/shims/crypto.d.ts +122 -0
  67. package/dist/shims/crypto.d.ts.map +1 -0
  68. package/dist/shims/dgram.d.ts +34 -0
  69. package/dist/shims/dgram.d.ts.map +1 -0
  70. package/dist/shims/diagnostics_channel.d.ts +80 -0
  71. package/dist/shims/diagnostics_channel.d.ts.map +1 -0
  72. package/dist/shims/dns.d.ts +87 -0
  73. package/dist/shims/dns.d.ts.map +1 -0
  74. package/dist/shims/domain.d.ts +25 -0
  75. package/dist/shims/domain.d.ts.map +1 -0
  76. package/dist/shims/esbuild.d.ts +105 -0
  77. package/dist/shims/esbuild.d.ts.map +1 -0
  78. package/dist/shims/events.d.ts +37 -0
  79. package/dist/shims/events.d.ts.map +1 -0
  80. package/dist/shims/fs.d.ts +115 -0
  81. package/dist/shims/fs.d.ts.map +1 -0
  82. package/dist/shims/fsevents.d.ts +67 -0
  83. package/dist/shims/fsevents.d.ts.map +1 -0
  84. package/dist/shims/http.d.ts +217 -0
  85. package/dist/shims/http.d.ts.map +1 -0
  86. package/dist/shims/http2.d.ts +81 -0
  87. package/dist/shims/http2.d.ts.map +1 -0
  88. package/dist/shims/https.d.ts +36 -0
  89. package/dist/shims/https.d.ts.map +1 -0
  90. package/dist/shims/inspector.d.ts +25 -0
  91. package/dist/shims/inspector.d.ts.map +1 -0
  92. package/dist/shims/module.d.ts +22 -0
  93. package/dist/shims/module.d.ts.map +1 -0
  94. package/dist/shims/net.d.ts +100 -0
  95. package/dist/shims/net.d.ts.map +1 -0
  96. package/dist/shims/os.d.ts +159 -0
  97. package/dist/shims/os.d.ts.map +1 -0
  98. package/dist/shims/path.d.ts +72 -0
  99. package/dist/shims/path.d.ts.map +1 -0
  100. package/dist/shims/perf_hooks.d.ts +50 -0
  101. package/dist/shims/perf_hooks.d.ts.map +1 -0
  102. package/dist/shims/process.d.ts +93 -0
  103. package/dist/shims/process.d.ts.map +1 -0
  104. package/dist/shims/querystring.d.ts +23 -0
  105. package/dist/shims/querystring.d.ts.map +1 -0
  106. package/dist/shims/readdirp.d.ts +52 -0
  107. package/dist/shims/readdirp.d.ts.map +1 -0
  108. package/dist/shims/readline.d.ts +62 -0
  109. package/dist/shims/readline.d.ts.map +1 -0
  110. package/dist/shims/rollup.d.ts +34 -0
  111. package/dist/shims/rollup.d.ts.map +1 -0
  112. package/dist/shims/sentry.d.ts +163 -0
  113. package/dist/shims/sentry.d.ts.map +1 -0
  114. package/dist/shims/stream.d.ts +181 -0
  115. package/dist/shims/stream.d.ts.map +1 -0
  116. package/dist/shims/tls.d.ts +53 -0
  117. package/dist/shims/tls.d.ts.map +1 -0
  118. package/dist/shims/tty.d.ts +30 -0
  119. package/dist/shims/tty.d.ts.map +1 -0
  120. package/dist/shims/url.d.ts +64 -0
  121. package/dist/shims/url.d.ts.map +1 -0
  122. package/dist/shims/util.d.ts +106 -0
  123. package/dist/shims/util.d.ts.map +1 -0
  124. package/dist/shims/v8.d.ts +73 -0
  125. package/dist/shims/v8.d.ts.map +1 -0
  126. package/dist/shims/vfs-adapter.d.ts +126 -0
  127. package/dist/shims/vfs-adapter.d.ts.map +1 -0
  128. package/dist/shims/vm.d.ts +45 -0
  129. package/dist/shims/vm.d.ts.map +1 -0
  130. package/dist/shims/worker_threads.d.ts +66 -0
  131. package/dist/shims/worker_threads.d.ts.map +1 -0
  132. package/dist/shims/ws.d.ts +66 -0
  133. package/dist/shims/ws.d.ts.map +1 -0
  134. package/dist/shims/zlib.d.ts +161 -0
  135. package/dist/shims/zlib.d.ts.map +1 -0
  136. package/dist/transform.d.ts +24 -0
  137. package/dist/transform.d.ts.map +1 -0
  138. package/dist/virtual-fs.d.ts +226 -0
  139. package/dist/virtual-fs.d.ts.map +1 -0
  140. package/dist/vite-demo.d.ts +35 -0
  141. package/dist/vite-demo.d.ts.map +1 -0
  142. package/dist/vite-sw.js +132 -0
  143. package/dist/worker/runtime-worker.d.ts +8 -0
  144. package/dist/worker/runtime-worker.d.ts.map +1 -0
  145. package/dist/worker-runtime.d.ts +50 -0
  146. package/dist/worker-runtime.d.ts.map +1 -0
  147. package/package.json +85 -0
  148. package/src/ai-chatbot-demo-entry.ts +244 -0
  149. package/src/ai-chatbot-demo.ts +509 -0
  150. package/src/convex-app-demo-entry.ts +1107 -0
  151. package/src/convex-app-demo.ts +1316 -0
  152. package/src/cors-proxy.ts +81 -0
  153. package/src/create-runtime.ts +147 -0
  154. package/src/demo.ts +304 -0
  155. package/src/dev-server.ts +274 -0
  156. package/src/frameworks/next-dev-server.ts +2224 -0
  157. package/src/frameworks/vite-dev-server.ts +702 -0
  158. package/src/index.ts +101 -0
  159. package/src/next-demo.ts +1784 -0
  160. package/src/npm/index.ts +347 -0
  161. package/src/npm/registry.ts +152 -0
  162. package/src/npm/resolver.ts +385 -0
  163. package/src/npm/tarball.ts +209 -0
  164. package/src/runtime-interface.ts +103 -0
  165. package/src/runtime.ts +1046 -0
  166. package/src/sandbox-helpers.ts +173 -0
  167. package/src/sandbox-runtime.ts +252 -0
  168. package/src/server-bridge.ts +426 -0
  169. package/src/shims/assert.ts +664 -0
  170. package/src/shims/async_hooks.ts +86 -0
  171. package/src/shims/buffer.ts +75 -0
  172. package/src/shims/child_process-browser.ts +217 -0
  173. package/src/shims/child_process.ts +463 -0
  174. package/src/shims/chokidar.ts +313 -0
  175. package/src/shims/cluster.ts +67 -0
  176. package/src/shims/crypto.ts +830 -0
  177. package/src/shims/dgram.ts +47 -0
  178. package/src/shims/diagnostics_channel.ts +196 -0
  179. package/src/shims/dns.ts +172 -0
  180. package/src/shims/domain.ts +58 -0
  181. package/src/shims/esbuild.ts +805 -0
  182. package/src/shims/events.ts +195 -0
  183. package/src/shims/fs.ts +803 -0
  184. package/src/shims/fsevents.ts +63 -0
  185. package/src/shims/http.ts +904 -0
  186. package/src/shims/http2.ts +96 -0
  187. package/src/shims/https.ts +86 -0
  188. package/src/shims/inspector.ts +30 -0
  189. package/src/shims/module.ts +82 -0
  190. package/src/shims/net.ts +359 -0
  191. package/src/shims/os.ts +195 -0
  192. package/src/shims/path.ts +199 -0
  193. package/src/shims/perf_hooks.ts +92 -0
  194. package/src/shims/process.ts +346 -0
  195. package/src/shims/querystring.ts +97 -0
  196. package/src/shims/readdirp.ts +228 -0
  197. package/src/shims/readline.ts +110 -0
  198. package/src/shims/rollup.ts +80 -0
  199. package/src/shims/sentry.ts +133 -0
  200. package/src/shims/stream.ts +1126 -0
  201. package/src/shims/tls.ts +95 -0
  202. package/src/shims/tty.ts +64 -0
  203. package/src/shims/url.ts +171 -0
  204. package/src/shims/util.ts +312 -0
  205. package/src/shims/v8.ts +113 -0
  206. package/src/shims/vfs-adapter.ts +402 -0
  207. package/src/shims/vm.ts +83 -0
  208. package/src/shims/worker_threads.ts +111 -0
  209. package/src/shims/ws.ts +382 -0
  210. package/src/shims/zlib.ts +289 -0
  211. package/src/transform.ts +313 -0
  212. package/src/types/external.d.ts +67 -0
  213. package/src/virtual-fs.ts +903 -0
  214. package/src/vite-demo.ts +577 -0
  215. package/src/worker/runtime-worker.ts +128 -0
  216. 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;