@ricsam/quickjs-fetch 0.2.9 → 0.2.11

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.
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/handle.ts"],
4
4
  "sourcesContent": [
5
- "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { marshal, getInstanceState, setInstanceState, getInstanceStateById } from \"@ricsam/quickjs-core\";\nimport type {\n StateMap,\n FetchHandle,\n ServeState,\n UpgradeRequest,\n WebSocketCommand,\n ServerWebSocketState,\n RequestState,\n ResponseState,\n} from \"./types.mjs\";\nimport { createRequestStateFromNative } from \"./globals/request.mjs\";\nimport { responseStateToNative } from \"./globals/response.mjs\";\nimport { headersStateToNative } from \"./globals/headers.mjs\";\n\n/**\n * Internal state of a QuickJS ReadableStream\n * Used to directly access stream queue for bridging to native streams\n */\ninterface ReadableStreamInternalState {\n queue: unknown[];\n closeRequested: boolean;\n closed: boolean;\n errored: boolean;\n errorValue: unknown;\n}\n\n/**\n * Create a native ReadableStream that reads from a QuickJS stream's internal state\n * This avoids needing a QuickJS handle by directly accessing the stream's queue\n */\nfunction createNativeStreamFromState(\n context: QuickJSContext,\n streamInstanceId: number\n): ReadableStream<Uint8Array> {\n let done = false;\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n while (!done) {\n // Pump QuickJS event loop to process async writes\n context.runtime.executePendingJobs();\n\n const state = getInstanceStateById<ReadableStreamInternalState>(streamInstanceId);\n\n if (!state) {\n controller.close();\n done = true;\n return;\n }\n\n // Check for error\n if (state.errored) {\n controller.error(state.errorValue);\n done = true;\n return;\n }\n\n // Check for data in queue\n if (state.queue && state.queue.length > 0) {\n const chunk = state.queue.shift();\n\n // Convert chunk to Uint8Array\n let bytes: Uint8Array;\n if (chunk instanceof Uint8Array) {\n bytes = chunk;\n } else if (typeof chunk === \"string\") {\n bytes = new TextEncoder().encode(chunk);\n } else if (chunk && typeof chunk === \"object\" && \"0\" in chunk) {\n // Handle unmarshalled Uint8Array (object with numeric keys)\n const obj = chunk as Record<string, number>;\n const keys = Object.keys(obj).filter(k => !isNaN(parseInt(k))).sort((a, b) => parseInt(a) - parseInt(b));\n const values = keys.map(k => obj[k]) as number[];\n bytes = new Uint8Array(values);\n } else {\n // Skip unknown chunk types\n continue;\n }\n\n controller.enqueue(bytes);\n return;\n }\n\n // Check if closed\n if (state.closeRequested || state.closed) {\n controller.close();\n done = true;\n return;\n }\n\n // Yield to event loop, then poll again\n await new Promise(r => setTimeout(r, 0));\n }\n },\n\n cancel() {\n done = true;\n }\n });\n}\n\n/**\n * Create a proper Request instance in QuickJS with the given state\n */\nfunction createRequestInstance(\n context: QuickJSContext,\n stateMap: StateMap,\n requestState: RequestState\n): QuickJSHandle {\n // Create the Request instance using evalCode (classes must be called with 'new')\n // Pass the URL as the constructor argument - minimal construction\n const urlHandle = context.newString(requestState.url);\n context.setProp(context.global, \"__requestUrl__\", urlHandle);\n urlHandle.dispose();\n\n const requestResult = context.evalCode(`new Request(__requestUrl__)`);\n\n // Clean up temporary global\n context.setProp(context.global, \"__requestUrl__\", context.undefined);\n\n if (requestResult.error) {\n const error = context.dump(requestResult.error);\n requestResult.error.dispose();\n throw new Error(`Failed to create Request: ${JSON.stringify(error)}`);\n }\n\n const requestHandle = requestResult.value;\n\n // Overwrite the internal state with our full RequestState\n setInstanceState(context, requestHandle, requestState);\n\n return requestHandle;\n}\n\n/**\n * Create the FetchHandle implementation\n */\nexport function createFetchHandle(\n context: QuickJSContext,\n stateMap: StateMap,\n serveState: ServeState\n): FetchHandle {\n const wsCommandCallbacks = new Set<(cmd: WebSocketCommand) => void>();\n\n return {\n stateMap,\n\n async dispatchRequest(request: Request): Promise<Response> {\n if (!serveState.fetchHandler) {\n throw new Error(\"No serve() handler registered\");\n }\n\n // Clear any previous upgrade request\n serveState.pendingUpgrade = null;\n\n // Convert native Request to RequestState\n const requestState = createRequestStateFromNative(request);\n\n // Create proper QuickJS Request instance with internal state\n const requestHandle = createRequestInstance(context, stateMap, requestState);\n\n // Create Server instance using evalCode (classes must be called with 'new')\n const serverResult = context.evalCode(`new __Server__()`);\n if (serverResult.error) {\n requestHandle.dispose();\n const error = context.dump(serverResult.error);\n serverResult.error.dispose();\n throw new Error(`Failed to create Server: ${error}`);\n }\n const serverHandle = serverResult.value;\n\n try {\n // Call the fetch handler\n const result = context.callFunction(\n serveState.fetchHandler,\n context.undefined,\n requestHandle,\n serverHandle\n );\n\n if (result.error) {\n const error = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Fetch handler error: ${JSON.stringify(error)}`);\n }\n\n let responseHandle = result.value;\n\n // Check if result is a Promise\n const typeofResult = context.typeof(responseHandle);\n if (typeofResult === \"object\") {\n // Check for .then method (Promise-like)\n const thenHandle = context.getProp(responseHandle, \"then\");\n const hasThen = context.typeof(thenHandle) === \"function\";\n thenHandle.dispose();\n\n if (hasThen) {\n // Execute pending jobs BEFORE resolvePromise to allow\n // JavaScript-native async functions to schedule their resolution.\n // This is critical for async functions that don't use host-side awaits.\n context.runtime.executePendingJobs();\n\n // For pure JS async functions (without host-side awaits), we need to\n // wrap resolvePromise in a race with repeated executePendingJobs calls.\n // The sync variant of QuickJS needs this to process microtasks.\n const resolved = await Promise.race([\n context.resolvePromise(responseHandle),\n (async () => {\n // Keep executing pending jobs until the promise resolves\n // This handles pure JS async functions that don't trigger host callbacks\n for (let i = 0; i < 1000; i++) {\n await new Promise((r) => setTimeout(r, 0));\n context.runtime.executePendingJobs();\n }\n // If we get here, something is very wrong\n throw new Error(\"Promise resolution timeout\");\n })(),\n ]);\n responseHandle.dispose();\n context.runtime.executePendingJobs();\n\n if (resolved.error) {\n const error = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Fetch handler promise rejected: ${JSON.stringify(error)}`);\n }\n responseHandle = resolved.value;\n }\n }\n\n // Get the ResponseState directly from the instance\n const responseState = getInstanceState<ResponseState>(context, responseHandle);\n responseHandle.dispose();\n\n if (!responseState) {\n throw new Error(\"Failed to get Response state\");\n }\n\n // Check if streaming response\n if (responseState.bodyType === \"stream\" && responseState.streamInstanceId !== undefined) {\n const nativeStream = createNativeStreamFromState(\n context,\n responseState.streamInstanceId\n );\n\n return new Response(nativeStream, {\n status: responseState.status,\n statusText: responseState.statusText,\n headers: headersStateToNative(responseState.headersState),\n });\n }\n\n // Convert to native Response (non-streaming)\n return responseStateToNative(responseState);\n } finally {\n requestHandle.dispose();\n serverHandle.dispose();\n }\n },\n\n getUpgradeRequest(): UpgradeRequest | null {\n return serveState.pendingUpgrade;\n },\n\n /**\n * Dispatch WebSocket open event to the QuickJS handler.\n *\n * **IMPORTANT:** An `open` handler MUST be defined in the serve() websocket\n * options for the connection to be tracked. Without an open handler, the\n * connection is not stored and subsequent message/close/error events will\n * be silently ignored.\n *\n * @param connectionId - Unique identifier for this WebSocket connection (host-assigned)\n * @param data - Optional user data from server.upgrade() to associate with the connection\n */\n dispatchWebSocketOpen(connectionId: string, data?: unknown): void {\n if (!serveState.websocketHandlers.open) {\n return;\n }\n\n // Create ServerWebSocket instance using evalCode (classes must be called with 'new')\n // Pass arguments through temporary globals\n const connectionIdHandle = context.newString(connectionId);\n const dataHandle = marshal(context, data);\n context.setProp(context.global, \"__wsConnectionId__\", connectionIdHandle);\n context.setProp(context.global, \"__wsData__\", dataHandle);\n connectionIdHandle.dispose();\n dataHandle.dispose();\n\n const wsResult = context.evalCode(`new __ServerWebSocket__(__wsConnectionId__, __wsData__)`);\n\n // Clean up temporary globals\n context.setProp(context.global, \"__wsConnectionId__\", context.undefined);\n context.setProp(context.global, \"__wsData__\", context.undefined);\n\n if (wsResult.error) {\n wsResult.error.dispose();\n return;\n }\n\n const wsHandle = wsResult.value;\n\n // Store the connection\n serveState.activeConnections.set(connectionId, {\n data,\n readyState: 1,\n connectionId,\n wsHandle,\n });\n\n // Call the open handler\n const result = context.callFunction(\n serveState.websocketHandlers.open,\n context.undefined,\n wsHandle\n );\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket message event to the QuickJS handler.\n *\n * Requires the connection to have been tracked via dispatchWebSocketOpen().\n * If no message handler is defined, or the connection is not tracked, this is a no-op.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param message - The message content (string or binary data)\n */\n dispatchWebSocketMessage(\n connectionId: string,\n message: string | ArrayBuffer\n ): void {\n if (!serveState.websocketHandlers.message) {\n return;\n }\n\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n return;\n }\n\n const messageHandle =\n typeof message === \"string\"\n ? context.newString(message)\n : context.newArrayBuffer(message);\n\n const result = context.callFunction(\n serveState.websocketHandlers.message,\n context.undefined,\n connection.wsHandle,\n messageHandle\n );\n\n messageHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket close event to the QuickJS handler and clean up the connection.\n *\n * Updates the connection ready state to CLOSED (3), calls the close handler if defined,\n * disposes the WebSocket handle, and removes the connection from tracking.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param code - The WebSocket close code (e.g., 1000 for normal closure)\n * @param reason - The close reason string\n */\n dispatchWebSocketClose(\n connectionId: string,\n code: number,\n reason: string\n ): void {\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n serveState.activeConnections.delete(connectionId);\n return;\n }\n\n if (!serveState.websocketHandlers.close) {\n // No close handler, but still need to cleanup the connection\n connection.wsHandle.dispose();\n serveState.activeConnections.delete(connectionId);\n return;\n }\n\n // Update ready state\n connection.readyState = 3; // CLOSED\n\n const codeHandle = context.newNumber(code);\n const reasonHandle = context.newString(reason);\n\n const result = context.callFunction(\n serveState.websocketHandlers.close,\n context.undefined,\n connection.wsHandle,\n codeHandle,\n reasonHandle\n );\n\n codeHandle.dispose();\n reasonHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n // Cleanup\n connection.wsHandle.dispose();\n serveState.activeConnections.delete(connectionId);\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket error event to the QuickJS handler.\n *\n * Requires the connection to have been tracked via dispatchWebSocketOpen().\n * If no error handler is defined, or the connection is not tracked, this is a no-op.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param error - The error that occurred\n */\n dispatchWebSocketError(connectionId: string, error: Error): void {\n if (!serveState.websocketHandlers.error) {\n return;\n }\n\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n return;\n }\n\n const errorHandle = marshal(context, {\n name: error.name,\n message: error.message,\n });\n\n const result = context.callFunction(\n serveState.websocketHandlers.error,\n context.undefined,\n connection.wsHandle,\n errorHandle\n );\n\n errorHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Register a callback for outgoing WebSocket commands from QuickJS.\n *\n * Called when QuickJS code calls ws.send() or ws.close() on a ServerWebSocket.\n * The callback receives command objects that should be forwarded to the actual WebSocket.\n *\n * @param callback - Function to handle outgoing WebSocket commands\n * @returns Unsubscribe function to remove the callback\n */\n onWebSocketCommand(\n callback: (command: WebSocketCommand) => void\n ): () => void {\n wsCommandCallbacks.add(callback);\n return () => wsCommandCallbacks.delete(callback);\n },\n\n hasServeHandler(): boolean {\n return serveState.fetchHandler !== null;\n },\n\n dispose(): void {\n // Dispose WebSocket handler handles\n if (serveState.websocketHandlers.open) {\n serveState.websocketHandlers.open.dispose();\n serveState.websocketHandlers.open = undefined;\n }\n if (serveState.websocketHandlers.message) {\n serveState.websocketHandlers.message.dispose();\n serveState.websocketHandlers.message = undefined;\n }\n if (serveState.websocketHandlers.close) {\n serveState.websocketHandlers.close.dispose();\n serveState.websocketHandlers.close = undefined;\n }\n if (serveState.websocketHandlers.error) {\n serveState.websocketHandlers.error.dispose();\n serveState.websocketHandlers.error = undefined;\n }\n\n // Dispose fetch handler handle\n if (serveState.fetchHandler) {\n serveState.fetchHandler.dispose();\n serveState.fetchHandler = null;\n }\n\n // Dispose active WebSocket connection handles\n for (const connection of serveState.activeConnections.values()) {\n if (connection.wsHandle) {\n connection.wsHandle.dispose();\n }\n }\n serveState.activeConnections.clear();\n\n // Note: __Server__ and __ServerWebSocket__ are on global and will be cleaned up by context.dispose()\n },\n };\n}\n"
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { marshal, getInstanceState, setInstanceState, getInstanceStateById } from \"@ricsam/quickjs-core\";\nimport type {\n StateMap,\n FetchHandle,\n ServeState,\n UpgradeRequest,\n WebSocketCommand,\n ServerWebSocketState,\n RequestState,\n ResponseState,\n} from \"./types.mjs\";\nimport { createRequestStateFromNative } from \"./globals/request.mjs\";\nimport { responseStateToNative } from \"./globals/response.mjs\";\nimport { headersStateToNative } from \"./globals/headers.mjs\";\n\n/**\n * Internal state of a QuickJS ReadableStream\n * Used to directly access stream queue for bridging to native streams\n */\ninterface ReadableStreamInternalState {\n queue: unknown[];\n closeRequested: boolean;\n closed: boolean;\n errored: boolean;\n errorValue: unknown;\n}\n\n/**\n * Create a native ReadableStream that reads from a QuickJS stream's internal state\n * This avoids needing a QuickJS handle by directly accessing the stream's queue\n */\nfunction createNativeStreamFromState(\n context: QuickJSContext,\n streamInstanceId: number\n): ReadableStream<Uint8Array> {\n let done = false;\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n while (!done) {\n // Pump QuickJS event loop to process async writes\n context.runtime.executePendingJobs();\n\n const state = getInstanceStateById<ReadableStreamInternalState>(streamInstanceId);\n\n if (!state) {\n controller.close();\n done = true;\n return;\n }\n\n // Check for error\n if (state.errored) {\n controller.error(state.errorValue);\n done = true;\n return;\n }\n\n // Check for data in queue\n if (state.queue && state.queue.length > 0) {\n const chunk = state.queue.shift();\n\n // Convert chunk to Uint8Array\n let bytes: Uint8Array;\n if (chunk instanceof Uint8Array) {\n bytes = chunk;\n } else if (typeof chunk === \"string\") {\n bytes = new TextEncoder().encode(chunk);\n } else if (chunk && typeof chunk === \"object\" && \"0\" in chunk) {\n // Handle unmarshalled Uint8Array (object with numeric keys)\n const obj = chunk as Record<string, number>;\n const keys = Object.keys(obj).filter(k => !isNaN(parseInt(k))).sort((a, b) => parseInt(a) - parseInt(b));\n const values = keys.map(k => obj[k]) as number[];\n bytes = new Uint8Array(values);\n } else {\n // Skip unknown chunk types\n continue;\n }\n\n controller.enqueue(bytes);\n return;\n }\n\n // Check if closed\n if (state.closeRequested || state.closed) {\n controller.close();\n done = true;\n return;\n }\n\n // Yield to event loop, then poll again\n await new Promise(r => setTimeout(r, 0));\n }\n },\n\n cancel() {\n done = true;\n }\n });\n}\n\n/**\n * Create a proper Request instance in QuickJS with the given state\n */\nfunction createRequestInstance(\n context: QuickJSContext,\n stateMap: StateMap,\n requestState: RequestState\n): QuickJSHandle {\n // Create the Request instance using evalCode (classes must be called with 'new')\n // Pass the URL as the constructor argument - minimal construction\n const urlHandle = context.newString(requestState.url);\n context.setProp(context.global, \"__requestUrl__\", urlHandle);\n urlHandle.dispose();\n\n const requestResult = context.evalCode(`new Request(__requestUrl__)`);\n\n // Clean up temporary global\n context.setProp(context.global, \"__requestUrl__\", context.undefined);\n\n if (requestResult.error) {\n const error = context.dump(requestResult.error);\n requestResult.error.dispose();\n throw new Error(`Failed to create Request: ${JSON.stringify(error)}`);\n }\n\n const requestHandle = requestResult.value;\n\n // Overwrite the internal state with our full RequestState\n setInstanceState(context, requestHandle, requestState);\n\n return requestHandle;\n}\n\n/**\n * Create the FetchHandle implementation\n */\nexport function createFetchHandle(\n context: QuickJSContext,\n stateMap: StateMap,\n serveState: ServeState\n): FetchHandle {\n const wsCommandCallbacks = new Set<(cmd: WebSocketCommand) => void>();\n\n return {\n stateMap,\n\n async dispatchRequest(request: Request): Promise<Response> {\n if (!serveState.fetchHandler) {\n throw new Error(\"No serve() handler registered\");\n }\n\n // Clean up previous pending upgrade entry if it was never consumed\n // This handles the case where host called getUpgradeRequest() but never called dispatchWebSocketOpen()\n // Only delete from registry if the connection was NOT opened (not in activeConnections)\n if (serveState.pendingUpgrade?.connectionId) {\n const connId = serveState.pendingUpgrade.connectionId;\n // Only clean up if this upgrade was never consumed (connection never opened)\n if (!serveState.activeConnections.has(connId)) {\n const deleteResult = context.evalCode(`__upgradeRegistry__.delete(\"${connId}\")`);\n if (deleteResult.error) {\n deleteResult.error.dispose();\n } else {\n deleteResult.value.dispose();\n }\n }\n }\n serveState.pendingUpgrade = null;\n\n // Convert native Request to RequestState\n const requestState = createRequestStateFromNative(request);\n\n // Create proper QuickJS Request instance with internal state\n const requestHandle = createRequestInstance(context, stateMap, requestState);\n\n // Create Server instance using evalCode (classes must be called with 'new')\n const serverResult = context.evalCode(`new __Server__()`);\n if (serverResult.error) {\n requestHandle.dispose();\n const error = context.dump(serverResult.error);\n serverResult.error.dispose();\n throw new Error(`Failed to create Server: ${error}`);\n }\n const serverHandle = serverResult.value;\n\n try {\n // Call the fetch handler\n const result = context.callFunction(\n serveState.fetchHandler,\n context.undefined,\n requestHandle,\n serverHandle\n );\n\n if (result.error) {\n const error = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Fetch handler error: ${JSON.stringify(error)}`);\n }\n\n let responseHandle = result.value;\n\n // Check if result is a Promise\n const typeofResult = context.typeof(responseHandle);\n if (typeofResult === \"object\") {\n // Check for .then method (Promise-like)\n const thenHandle = context.getProp(responseHandle, \"then\");\n const hasThen = context.typeof(thenHandle) === \"function\";\n thenHandle.dispose();\n\n if (hasThen) {\n // Execute pending jobs BEFORE resolvePromise to allow\n // JavaScript-native async functions to schedule their resolution.\n // This is critical for async functions that don't use host-side awaits.\n context.runtime.executePendingJobs();\n\n // For pure JS async functions (without host-side awaits), we need to\n // wrap resolvePromise in a race with repeated executePendingJobs calls.\n // The sync variant of QuickJS needs this to process microtasks.\n const resolved = await Promise.race([\n context.resolvePromise(responseHandle),\n (async () => {\n // Keep executing pending jobs until the promise resolves\n // This handles pure JS async functions that don't trigger host callbacks\n for (let i = 0; i < 1000; i++) {\n await new Promise((r) => setTimeout(r, 0));\n context.runtime.executePendingJobs();\n }\n // If we get here, something is very wrong\n throw new Error(\"Promise resolution timeout\");\n })(),\n ]);\n responseHandle.dispose();\n context.runtime.executePendingJobs();\n\n if (resolved.error) {\n const error = context.dump(resolved.error);\n resolved.error.dispose();\n throw new Error(`Fetch handler promise rejected: ${JSON.stringify(error)}`);\n }\n responseHandle = resolved.value;\n }\n }\n\n // Get the ResponseState directly from the instance\n const responseState = getInstanceState<ResponseState>(context, responseHandle);\n responseHandle.dispose();\n\n if (!responseState) {\n throw new Error(\"Failed to get Response state\");\n }\n\n // Check if streaming response\n if (responseState.bodyType === \"stream\" && responseState.streamInstanceId !== undefined) {\n const nativeStream = createNativeStreamFromState(\n context,\n responseState.streamInstanceId\n );\n\n return new Response(nativeStream, {\n status: responseState.status,\n statusText: responseState.statusText,\n headers: headersStateToNative(responseState.headersState),\n });\n }\n\n // Convert to native Response (non-streaming)\n return responseStateToNative(responseState);\n } finally {\n requestHandle.dispose();\n serverHandle.dispose();\n }\n },\n\n getUpgradeRequest(): UpgradeRequest | null {\n return serveState.pendingUpgrade;\n },\n\n /**\n * Dispatch WebSocket open event to the QuickJS handler.\n *\n * **IMPORTANT:** An `open` handler MUST be defined in the serve() websocket\n * options for the connection to be tracked. Without an open handler, the\n * connection is not stored and subsequent message/close/error events will\n * be silently ignored.\n *\n * The user data is looked up from the QuickJS-side __upgradeRegistry__ using\n * the connectionId. This avoids marshalling complex objects across the boundary.\n *\n * @param connectionId - The connectionId from getUpgradeRequest()\n */\n dispatchWebSocketOpen(connectionId: string): void {\n // Note: Data stays in __upgradeRegistry__ and is accessed via pure-JS getter\n // This avoids marshalling complex objects (like Zod schemas) that exceed depth limits\n // Registry cleanup happens in dispatchWebSocketClose()\n\n if (!serveState.websocketHandlers.open) {\n return;\n }\n\n // Create ServerWebSocket instance using evalCode (classes must be called with 'new')\n // Only pass connectionId to constructor to avoid unmarshalling complex data objects\n const connectionIdHandle = context.newString(connectionId);\n context.setProp(context.global, \"__wsConnectionId__\", connectionIdHandle);\n connectionIdHandle.dispose();\n\n const wsResult = context.evalCode(`new __ServerWebSocket__(__wsConnectionId__)`);\n\n // Clean up temporary global\n context.setProp(context.global, \"__wsConnectionId__\", context.undefined);\n\n if (wsResult.error) {\n wsResult.error.dispose();\n return;\n }\n\n const wsHandle = wsResult.value;\n\n // Set __connectionId__ on the instance for the pure-JS data getter to use\n const connIdForGetter = context.newString(connectionId);\n context.setProp(wsHandle, \"__connectionId__\", connIdForGetter);\n connIdForGetter.dispose();\n\n // Store the connection\n serveState.activeConnections.set(connectionId, {\n readyState: 1,\n connectionId,\n wsHandle,\n });\n\n // Call the open handler\n const result = context.callFunction(\n serveState.websocketHandlers.open,\n context.undefined,\n wsHandle\n );\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket message event to the QuickJS handler.\n *\n * Requires the connection to have been tracked via dispatchWebSocketOpen().\n * If no message handler is defined, or the connection is not tracked, this is a no-op.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param message - The message content (string or binary data)\n */\n dispatchWebSocketMessage(\n connectionId: string,\n message: string | ArrayBuffer\n ): void {\n if (!serveState.websocketHandlers.message) {\n return;\n }\n\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n return;\n }\n\n const messageHandle =\n typeof message === \"string\"\n ? context.newString(message)\n : context.newArrayBuffer(message);\n\n const result = context.callFunction(\n serveState.websocketHandlers.message,\n context.undefined,\n connection.wsHandle,\n messageHandle\n );\n\n messageHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket close event to the QuickJS handler and clean up the connection.\n *\n * Updates the connection ready state to CLOSED (3), calls the close handler if defined,\n * disposes the WebSocket handle, and removes the connection from tracking.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param code - The WebSocket close code (e.g., 1000 for normal closure)\n * @param reason - The close reason string\n */\n dispatchWebSocketClose(\n connectionId: string,\n code: number,\n reason: string\n ): void {\n // Helper to clean up registry entry\n const cleanupRegistry = () => {\n const deleteResult = context.evalCode(`__upgradeRegistry__.delete(\"${connectionId}\")`);\n if (deleteResult.error) {\n deleteResult.error.dispose();\n } else {\n deleteResult.value.dispose();\n }\n };\n\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n serveState.activeConnections.delete(connectionId);\n cleanupRegistry();\n return;\n }\n\n if (!serveState.websocketHandlers.close) {\n // No close handler, but still need to cleanup the connection\n connection.wsHandle.dispose();\n serveState.activeConnections.delete(connectionId);\n cleanupRegistry();\n return;\n }\n\n // Update ready state\n connection.readyState = 3; // CLOSED\n\n const codeHandle = context.newNumber(code);\n const reasonHandle = context.newString(reason);\n\n const result = context.callFunction(\n serveState.websocketHandlers.close,\n context.undefined,\n connection.wsHandle,\n codeHandle,\n reasonHandle\n );\n\n codeHandle.dispose();\n reasonHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n // Cleanup\n connection.wsHandle.dispose();\n serveState.activeConnections.delete(connectionId);\n cleanupRegistry();\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Dispatch WebSocket error event to the QuickJS handler.\n *\n * Requires the connection to have been tracked via dispatchWebSocketOpen().\n * If no error handler is defined, or the connection is not tracked, this is a no-op.\n *\n * @param connectionId - The connection ID from dispatchWebSocketOpen()\n * @param error - The error that occurred\n */\n dispatchWebSocketError(connectionId: string, error: Error): void {\n if (!serveState.websocketHandlers.error) {\n return;\n }\n\n const connection = serveState.activeConnections.get(connectionId);\n if (!connection || !connection.wsHandle) {\n return;\n }\n\n const errorHandle = marshal(context, {\n name: error.name,\n message: error.message,\n });\n\n const result = context.callFunction(\n serveState.websocketHandlers.error,\n context.undefined,\n connection.wsHandle,\n errorHandle\n );\n\n errorHandle.dispose();\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n\n context.runtime.executePendingJobs();\n },\n\n /**\n * Register a callback for outgoing WebSocket commands from QuickJS.\n *\n * Called when QuickJS code calls ws.send() or ws.close() on a ServerWebSocket.\n * The callback receives command objects that should be forwarded to the actual WebSocket.\n *\n * @param callback - Function to handle outgoing WebSocket commands\n * @returns Unsubscribe function to remove the callback\n */\n onWebSocketCommand(\n callback: (command: WebSocketCommand) => void\n ): () => void {\n wsCommandCallbacks.add(callback);\n return () => wsCommandCallbacks.delete(callback);\n },\n\n hasServeHandler(): boolean {\n return serveState.fetchHandler !== null;\n },\n\n hasActiveConnections(): boolean {\n return serveState.activeConnections.size > 0;\n },\n\n dispose(): void {\n // Clear upgrade registry to prevent memory leaks\n const clearResult = context.evalCode(`__upgradeRegistry__.clear()`);\n if (clearResult.error) {\n clearResult.error.dispose();\n } else {\n clearResult.value.dispose();\n }\n\n // Dispose WebSocket handler handles\n if (serveState.websocketHandlers.open) {\n serveState.websocketHandlers.open.dispose();\n serveState.websocketHandlers.open = undefined;\n }\n if (serveState.websocketHandlers.message) {\n serveState.websocketHandlers.message.dispose();\n serveState.websocketHandlers.message = undefined;\n }\n if (serveState.websocketHandlers.close) {\n serveState.websocketHandlers.close.dispose();\n serveState.websocketHandlers.close = undefined;\n }\n if (serveState.websocketHandlers.error) {\n serveState.websocketHandlers.error.dispose();\n serveState.websocketHandlers.error = undefined;\n }\n\n // Dispose fetch handler handle\n if (serveState.fetchHandler) {\n serveState.fetchHandler.dispose();\n serveState.fetchHandler = null;\n }\n\n // Dispose active WebSocket connection handles\n for (const connection of serveState.activeConnections.values()) {\n if (connection.wsHandle) {\n connection.wsHandle.dispose();\n }\n }\n serveState.activeConnections.clear();\n\n // Note: __Server__ and __ServerWebSocket__ are on global and will be cleaned up by context.dispose()\n },\n };\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "802249127874F14F64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "023C0E73AA057C8764756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/quickjs-fetch",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "type": "module"
5
5
  }
@@ -51,6 +51,18 @@ function setupFetch(context, options = {}) {
51
51
  result.value.dispose();
52
52
  return { instanceId, globalKey };
53
53
  },
54
+ getStreamByKey: (globalKey) => {
55
+ const result = context.evalCode(`globalThis["${globalKey}"]`);
56
+ if (result.error) {
57
+ result.error.dispose();
58
+ return null;
59
+ }
60
+ if (context.typeof(result.value) === "undefined") {
61
+ result.value.dispose();
62
+ return null;
63
+ }
64
+ return result.value;
65
+ },
54
66
  pumpEventLoop: () => {
55
67
  context.runtime.executePendingJobs();
56
68
  }
@@ -71,36 +83,6 @@ function setupFetch(context, options = {}) {
71
83
  const RequestClass = createRequestClass(context, stateMap, streamHelpers);
72
84
  context.setProp(context.global, "Request", RequestClass);
73
85
  RequestClass.dispose();
74
- const bodyGetterWrapperResult = context.evalCode(`
75
- (function() {
76
- const originalBodyDescriptor = Object.getOwnPropertyDescriptor(Request.prototype, 'body');
77
- if (originalBodyDescriptor && originalBodyDescriptor.get) {
78
- const originalGetter = originalBodyDescriptor.get;
79
- Object.defineProperty(Request.prototype, 'body', {
80
- get: function() {
81
- const result = originalGetter.call(this);
82
- // Check if result is an upload stream marker
83
- if (typeof result === 'string' && result.startsWith('__UPLOAD_STREAM_KEY__:')) {
84
- const globalKey = result.slice('__UPLOAD_STREAM_KEY__:'.length);
85
- const stream = globalThis[globalKey];
86
- // Don't delete the global - keep it around for subsequent calls
87
- // The wrapper will cache this in _cachedBodyStream for future calls
88
- return stream || null;
89
- }
90
- return result;
91
- },
92
- configurable: true,
93
- enumerable: true
94
- });
95
- }
96
- })();
97
- `);
98
- if (bodyGetterWrapperResult.error) {
99
- console.error("Failed to wrap body getter:", context.dump(bodyGetterWrapperResult.error));
100
- bodyGetterWrapperResult.error.dispose();
101
- } else {
102
- bodyGetterWrapperResult.value.dispose();
103
- }
104
86
  const ResponseClass = createResponseClass(context, stateMap, streamFactory);
105
87
  context.setProp(context.global, "Response", ResponseClass);
106
88
  ResponseClass.dispose();
@@ -124,9 +106,53 @@ function setupFetch(context, options = {}) {
124
106
  const ServerWebSocketClass = createServerWebSocketClass(context, stateMap, dispatchWsCommand);
125
107
  context.setProp(context.global, "__ServerWebSocket__", ServerWebSocketClass);
126
108
  ServerWebSocketClass.dispose();
109
+ const dataGetterResult = context.evalCode(`
110
+ Object.defineProperty(__ServerWebSocket__.prototype, 'data', {
111
+ get: function() {
112
+ return __upgradeRegistry__.get(this.__connectionId__);
113
+ },
114
+ enumerable: true,
115
+ configurable: true
116
+ });
117
+ `);
118
+ if (dataGetterResult.error) {
119
+ dataGetterResult.error.dispose();
120
+ } else {
121
+ dataGetterResult.value.dispose();
122
+ }
127
123
  const ServerClass = createServerClass(context, stateMap, serveState);
128
124
  context.setProp(context.global, "__Server__", ServerClass);
129
125
  ServerClass.dispose();
126
+ const registryResult = context.evalCode(`
127
+ globalThis.__upgradeRegistry__ = new Map();
128
+ globalThis.__upgradeIdCounter__ = 0;
129
+ `);
130
+ if (registryResult.error) {
131
+ registryResult.error.dispose();
132
+ } else {
133
+ registryResult.value.dispose();
134
+ }
135
+ const setPendingUpgradeFn = context.newFunction("__setPendingUpgrade__", (connIdHandle) => {
136
+ const connectionId = context.getString(connIdHandle);
137
+ serveState.pendingUpgrade = { requested: true, connectionId };
138
+ return context.undefined;
139
+ });
140
+ context.setProp(context.global, "__setPendingUpgrade__", setPendingUpgradeFn);
141
+ setPendingUpgradeFn.dispose();
142
+ const upgradeMethodResult = context.evalCode(`
143
+ __Server__.prototype.upgrade = function(request, options) {
144
+ const data = options && typeof options === 'object' ? options.data : undefined;
145
+ const connectionId = String(++globalThis.__upgradeIdCounter__);
146
+ globalThis.__upgradeRegistry__.set(connectionId, data);
147
+ __setPendingUpgrade__(connectionId);
148
+ return true;
149
+ };
150
+ `);
151
+ if (upgradeMethodResult.error) {
152
+ upgradeMethodResult.error.dispose();
153
+ } else {
154
+ upgradeMethodResult.value.dispose();
155
+ }
130
156
  const fetchFn = createFetchFunction(context, options.onFetch);
131
157
  context.setProp(context.global, "fetch", fetchFn);
132
158
  fetchFn.dispose();
@@ -145,4 +171,4 @@ export {
145
171
  setupFetch
146
172
  };
147
173
 
148
- //# debugId=540F333C1CD9EF6B64756E2164756E21
174
+ //# debugId=AF095A66B34E6FFE64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/setup.ts"],
4
4
  "sourcesContent": [
5
- "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { setupCore, createStateMap, createReadableStream } from \"@ricsam/quickjs-core\";\nimport type { SetupFetchOptions, FetchHandle, ServeState } from \"./types.mjs\";\nimport { createHeadersClass } from \"./globals/headers.mjs\";\nimport { createRequestClass, addRequestFormDataMethod } from \"./globals/request.mjs\";\nimport { createResponseClass, addResponseStaticMethods } from \"./globals/response.mjs\";\nimport { setupAbortControllerAndSignal } from \"./globals/abort-controller.mjs\";\nimport { createFormDataClass, addFormDataFileMethods } from \"./globals/form-data.mjs\";\nimport { createFetchFunction } from \"./globals/fetch.mjs\";\nimport {\n createServeFunction,\n createServerClass,\n createServerWebSocketClass,\n} from \"./globals/serve.mjs\";\nimport { createFetchHandle } from \"./handle.mjs\";\n\n/**\n * Setup Fetch API in a QuickJS context\n *\n * Injects the following globals:\n * - fetch\n * - Request\n * - Response\n * - Headers\n * - AbortController\n * - AbortSignal\n * - serve\n * - FormData\n *\n * Also sets up Core APIs (Streams, Blob, File) if not already present.\n *\n * **Private globals (internal use):**\n * - `__Server__` - Server class for serve() handler, instantiated via evalCode\n * - `__ServerWebSocket__` - WebSocket class for connection handling\n * - `__scheduleTimeout__` - Host function to schedule AbortSignal.timeout()\n * - `__checkTimeout__` - Host function to check if timeout elapsed\n *\n * These private globals follow the `__Name__` convention and are required for\n * JavaScript code in QuickJS to create class instances via evalCode.\n * See PATTERNS.md section 5 for details.\n *\n * @example\n * const handle = setupFetch(context, {\n * onFetch: async (request) => {\n * // Proxy to real fetch\n * return fetch(request);\n * }\n * });\n *\n * context.evalCode(`\n * serve({\n * fetch(request, server) {\n * return new Response(\"Hello!\");\n * }\n * });\n * `);\n *\n * const response = await handle.dispatchRequest(\n * new Request(\"http://localhost/\")\n * );\n */\nexport function setupFetch(\n context: QuickJSContext,\n options: SetupFetchOptions = {}\n): FetchHandle {\n // Setup core if not already done\n const coreHandle =\n options.coreHandle ??\n setupCore(context, {\n stateMap: options.stateMap,\n });\n\n const stateMap = options.stateMap ?? coreHandle.stateMap;\n\n // Create serve state\n const serveState: ServeState = {\n fetchHandler: null,\n websocketHandlers: {},\n pendingUpgrade: null,\n activeConnections: new Map(),\n };\n\n // WebSocket command dispatcher\n const wsCommandCallbacks = new Set<\n (cmd: import(\"./types.mjs\").WebSocketCommand) => void\n >();\n const dispatchWsCommand = (cmd: import(\"./types.mjs\").WebSocketCommand) => {\n for (const cb of wsCommandCallbacks) {\n cb(cmd);\n }\n };\n\n // Create stream factory for Request/Response body\n const streamFactory = (source: UnderlyingSource) =>\n createReadableStream(context, stateMap, source);\n\n // Create stream helpers for upload streaming\n const streamHelpers = {\n createStream: streamFactory,\n createEmptyStream: (): { instanceId: number; globalKey: string } => {\n // Create a pure QuickJS ReadableStream with no source (no host callbacks)\n // Store it on a unique global key so it can be retrieved from JS without\n // returning a handle through the callback (which causes lifetime issues)\n const globalKey = `__uploadStream_${Date.now()}_${Math.random().toString(36).slice(2)}__`;\n const result = context.evalCode(`\n (function() {\n const stream = new ReadableStream();\n globalThis[\"${globalKey}\"] = stream;\n return stream.__instanceId__;\n })()\n `);\n if (result.error) {\n const error = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Failed to create empty ReadableStream: ${JSON.stringify(error)}`);\n }\n const instanceId = context.getNumber(result.value);\n result.value.dispose();\n\n return { instanceId, globalKey };\n },\n pumpEventLoop: () => {\n context.runtime.executePendingJobs();\n },\n };\n\n // Create Headers class\n const HeadersClass = createHeadersClass(context, stateMap);\n context.setProp(context.global, \"Headers\", HeadersClass);\n HeadersClass.dispose();\n\n // Add Symbol.iterator support for Headers (for...of, Array.from, spread)\n const iteratorResult = context.evalCode(`\n Headers.prototype[Symbol.iterator] = function() {\n return this.entries()[Symbol.iterator]();\n };\n `);\n if (iteratorResult.error) {\n iteratorResult.error.dispose();\n } else {\n iteratorResult.value.dispose();\n }\n\n // Create Request class\n const RequestClass = createRequestClass(context, stateMap, streamHelpers);\n context.setProp(context.global, \"Request\", RequestClass);\n RequestClass.dispose();\n\n // Wrap the body getter to resolve upload stream markers\n // This works around the issue of returning QuickJSHandle from __hostCall__\n const bodyGetterWrapperResult = context.evalCode(`\n (function() {\n const originalBodyDescriptor = Object.getOwnPropertyDescriptor(Request.prototype, 'body');\n if (originalBodyDescriptor && originalBodyDescriptor.get) {\n const originalGetter = originalBodyDescriptor.get;\n Object.defineProperty(Request.prototype, 'body', {\n get: function() {\n const result = originalGetter.call(this);\n // Check if result is an upload stream marker\n if (typeof result === 'string' && result.startsWith('__UPLOAD_STREAM_KEY__:')) {\n const globalKey = result.slice('__UPLOAD_STREAM_KEY__:'.length);\n const stream = globalThis[globalKey];\n // Don't delete the global - keep it around for subsequent calls\n // The wrapper will cache this in _cachedBodyStream for future calls\n return stream || null;\n }\n return result;\n },\n configurable: true,\n enumerable: true\n });\n }\n })();\n `);\n if (bodyGetterWrapperResult.error) {\n console.error(\"Failed to wrap body getter:\", context.dump(bodyGetterWrapperResult.error));\n bodyGetterWrapperResult.error.dispose();\n } else {\n bodyGetterWrapperResult.value.dispose();\n }\n\n // Create Response class\n const ResponseClass = createResponseClass(context, stateMap, streamFactory);\n context.setProp(context.global, \"Response\", ResponseClass);\n ResponseClass.dispose();\n\n // Add Response static methods (must be after Response and Headers are on global)\n addResponseStaticMethods(context);\n\n // Create AbortSignal and AbortController classes (pure QuickJS implementation)\n setupAbortControllerAndSignal(context);\n\n // Create FormData class\n const FormDataClass = createFormDataClass(context, stateMap);\n context.setProp(context.global, \"FormData\", FormDataClass);\n FormDataClass.dispose();\n\n // Add Symbol.iterator support for FormData (for...of, Array.from, spread)\n const formDataIteratorResult = context.evalCode(`\n FormData.prototype[Symbol.iterator] = function() {\n return this.entries()[Symbol.iterator]();\n };\n `);\n if (formDataIteratorResult.error) {\n formDataIteratorResult.error.dispose();\n } else {\n formDataIteratorResult.value.dispose();\n }\n\n // Add Request.formData() method that returns a proper FormData instance\n // Must be after both Request and FormData are on global\n addRequestFormDataMethod(context);\n\n // Add FormData.get()/getAll() overrides to reconstruct File instances\n // Must be after both FormData and File are on global\n addFormDataFileMethods(context);\n\n // Create ServerWebSocket class (internal, for WebSocket handling)\n const ServerWebSocketClass = createServerWebSocketClass(\n context,\n stateMap,\n dispatchWsCommand\n );\n // Set on global with internal name so we can use evalCode to instantiate\n context.setProp(context.global, \"__ServerWebSocket__\", ServerWebSocketClass);\n ServerWebSocketClass.dispose();\n // Note: ServerWebSocketClass handle is now owned by global\n\n // Create Server class (internal, passed to fetch handler)\n const ServerClass = createServerClass(context, stateMap, serveState);\n // Set on global with internal name so we can use evalCode to instantiate\n context.setProp(context.global, \"__Server__\", ServerClass);\n ServerClass.dispose();\n // Note: ServerClass handle is now owned by global\n\n // Create fetch function\n const fetchFn = createFetchFunction(context, options.onFetch);\n context.setProp(context.global, \"fetch\", fetchFn);\n fetchFn.dispose();\n\n // Create serve function\n const serveFn = createServeFunction(context, stateMap, serveState);\n context.setProp(context.global, \"serve\", serveFn);\n serveFn.dispose();\n\n // Create and return the handle\n const fetchHandle = createFetchHandle(\n context,\n stateMap,\n serveState\n );\n\n // Wire up WebSocket command callbacks\n const originalOnWebSocketCommand = fetchHandle.onWebSocketCommand;\n fetchHandle.onWebSocketCommand = (callback) => {\n wsCommandCallbacks.add(callback);\n return () => wsCommandCallbacks.delete(callback);\n };\n\n return fetchHandle;\n}\n"
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { setupCore, createStateMap, createReadableStream } from \"@ricsam/quickjs-core\";\nimport type { SetupFetchOptions, FetchHandle, ServeState } from \"./types.mjs\";\nimport { createHeadersClass } from \"./globals/headers.mjs\";\nimport { createRequestClass, addRequestFormDataMethod } from \"./globals/request.mjs\";\nimport { createResponseClass, addResponseStaticMethods } from \"./globals/response.mjs\";\nimport { setupAbortControllerAndSignal } from \"./globals/abort-controller.mjs\";\nimport { createFormDataClass, addFormDataFileMethods } from \"./globals/form-data.mjs\";\nimport { createFetchFunction } from \"./globals/fetch.mjs\";\nimport {\n createServeFunction,\n createServerClass,\n createServerWebSocketClass,\n} from \"./globals/serve.mjs\";\nimport { createFetchHandle } from \"./handle.mjs\";\n\n/**\n * Setup Fetch API in a QuickJS context\n *\n * Injects the following globals:\n * - fetch\n * - Request\n * - Response\n * - Headers\n * - AbortController\n * - AbortSignal\n * - serve\n * - FormData\n *\n * Also sets up Core APIs (Streams, Blob, File) if not already present.\n *\n * **Private globals (internal use):**\n * - `__Server__` - Server class for serve() handler, instantiated via evalCode\n * - `__ServerWebSocket__` - WebSocket class for connection handling\n * - `__scheduleTimeout__` - Host function to schedule AbortSignal.timeout()\n * - `__checkTimeout__` - Host function to check if timeout elapsed\n *\n * These private globals follow the `__Name__` convention and are required for\n * JavaScript code in QuickJS to create class instances via evalCode.\n * See PATTERNS.md section 5 for details.\n *\n * @example\n * const handle = setupFetch(context, {\n * onFetch: async (request) => {\n * // Proxy to real fetch\n * return fetch(request);\n * }\n * });\n *\n * context.evalCode(`\n * serve({\n * fetch(request, server) {\n * return new Response(\"Hello!\");\n * }\n * });\n * `);\n *\n * const response = await handle.dispatchRequest(\n * new Request(\"http://localhost/\")\n * );\n */\nexport function setupFetch(\n context: QuickJSContext,\n options: SetupFetchOptions = {}\n): FetchHandle {\n // Setup core if not already done\n const coreHandle =\n options.coreHandle ??\n setupCore(context, {\n stateMap: options.stateMap,\n });\n\n const stateMap = options.stateMap ?? coreHandle.stateMap;\n\n // Create serve state\n const serveState: ServeState = {\n fetchHandler: null,\n websocketHandlers: {},\n pendingUpgrade: null,\n activeConnections: new Map(),\n };\n\n // WebSocket command dispatcher\n const wsCommandCallbacks = new Set<\n (cmd: import(\"./types.mjs\").WebSocketCommand) => void\n >();\n const dispatchWsCommand = (cmd: import(\"./types.mjs\").WebSocketCommand) => {\n for (const cb of wsCommandCallbacks) {\n cb(cmd);\n }\n };\n\n // Create stream factory for Request/Response body\n const streamFactory = (source: UnderlyingSource) =>\n createReadableStream(context, stateMap, source);\n\n // Create stream helpers for upload streaming\n const streamHelpers = {\n createStream: streamFactory,\n /**\n * Create an empty ReadableStream for upload streaming.\n *\n * IMPORTANT: We store the stream on a global variable and return the key.\n * This is necessary because __hostCall__ manages and disposes returned handles\n * in its scope. If we cached the handle and returned it multiple times,\n * subsequent calls would return a dead handle.\n *\n * The getter must call getStreamByKey() each time to get a fresh handle.\n */\n createEmptyStream: (): { instanceId: number; globalKey: string } => {\n const globalKey = `__uploadStream_${Date.now()}_${Math.random().toString(36).slice(2)}__`;\n const result = context.evalCode(`\n (function() {\n const stream = new ReadableStream();\n globalThis[\"${globalKey}\"] = stream;\n return stream.__instanceId__;\n })()\n `);\n if (result.error) {\n const error = context.dump(result.error);\n result.error.dispose();\n throw new Error(`Failed to create empty ReadableStream: ${JSON.stringify(error)}`);\n }\n const instanceId = context.getNumber(result.value);\n result.value.dispose();\n\n return { instanceId, globalKey };\n },\n /**\n * Get a fresh handle to the stream stored at globalKey.\n * This must be called each time the body getter is accessed to avoid\n * returning disposed handles.\n */\n getStreamByKey: (globalKey: string): import(\"quickjs-emscripten\").QuickJSHandle | null => {\n const result = context.evalCode(`globalThis[\"${globalKey}\"]`);\n if (result.error) {\n result.error.dispose();\n return null;\n }\n if (context.typeof(result.value) === \"undefined\") {\n result.value.dispose();\n return null;\n }\n return result.value;\n },\n pumpEventLoop: () => {\n context.runtime.executePendingJobs();\n },\n };\n\n // Create Headers class\n const HeadersClass = createHeadersClass(context, stateMap);\n context.setProp(context.global, \"Headers\", HeadersClass);\n HeadersClass.dispose();\n\n // Add Symbol.iterator support for Headers (for...of, Array.from, spread)\n const iteratorResult = context.evalCode(`\n Headers.prototype[Symbol.iterator] = function() {\n return this.entries()[Symbol.iterator]();\n };\n `);\n if (iteratorResult.error) {\n iteratorResult.error.dispose();\n } else {\n iteratorResult.value.dispose();\n }\n\n // Create Request class\n const RequestClass = createRequestClass(context, stateMap, streamHelpers);\n context.setProp(context.global, \"Request\", RequestClass);\n RequestClass.dispose();\n\n // Note: No body getter wrapper needed - handles are returned directly\n // through __hostCall__ because quickjs-emscripten properly duplicates\n // handles via QTS_DupValuePointer before returning to QuickJS.\n\n // Create Response class\n const ResponseClass = createResponseClass(context, stateMap, streamFactory);\n context.setProp(context.global, \"Response\", ResponseClass);\n ResponseClass.dispose();\n\n // Add Response static methods (must be after Response and Headers are on global)\n addResponseStaticMethods(context);\n\n // Create AbortSignal and AbortController classes (pure QuickJS implementation)\n setupAbortControllerAndSignal(context);\n\n // Create FormData class\n const FormDataClass = createFormDataClass(context, stateMap);\n context.setProp(context.global, \"FormData\", FormDataClass);\n FormDataClass.dispose();\n\n // Add Symbol.iterator support for FormData (for...of, Array.from, spread)\n const formDataIteratorResult = context.evalCode(`\n FormData.prototype[Symbol.iterator] = function() {\n return this.entries()[Symbol.iterator]();\n };\n `);\n if (formDataIteratorResult.error) {\n formDataIteratorResult.error.dispose();\n } else {\n formDataIteratorResult.value.dispose();\n }\n\n // Add Request.formData() method that returns a proper FormData instance\n // Must be after both Request and FormData are on global\n addRequestFormDataMethod(context);\n\n // Add FormData.get()/getAll() overrides to reconstruct File instances\n // Must be after both FormData and File are on global\n addFormDataFileMethods(context);\n\n // Create ServerWebSocket class (internal, for WebSocket handling)\n const ServerWebSocketClass = createServerWebSocketClass(\n context,\n stateMap,\n dispatchWsCommand\n );\n // Set on global with internal name so we can use evalCode to instantiate\n context.setProp(context.global, \"__ServerWebSocket__\", ServerWebSocketClass);\n ServerWebSocketClass.dispose();\n // Note: ServerWebSocketClass handle is now owned by global\n\n // Add pure-JS data getter to ServerWebSocket prototype\n // This getter reads from __upgradeRegistry__ using __connectionId__,\n // keeping complex data (like Zod schemas) entirely within QuickJS\n // to avoid marshalling depth limit issues\n const dataGetterResult = context.evalCode(`\n Object.defineProperty(__ServerWebSocket__.prototype, 'data', {\n get: function() {\n return __upgradeRegistry__.get(this.__connectionId__);\n },\n enumerable: true,\n configurable: true\n });\n `);\n if (dataGetterResult.error) {\n dataGetterResult.error.dispose();\n } else {\n dataGetterResult.value.dispose();\n }\n\n // Create Server class (internal, passed to fetch handler)\n const ServerClass = createServerClass(context, stateMap, serveState);\n // Set on global with internal name so we can use evalCode to instantiate\n context.setProp(context.global, \"__Server__\", ServerClass);\n ServerClass.dispose();\n // Note: ServerClass handle is now owned by global\n\n // Setup upgrade registry and helper function for WebSocket upgrades\n // The registry keeps data within QuickJS to avoid marshalling complex objects\n const registryResult = context.evalCode(`\n globalThis.__upgradeRegistry__ = new Map();\n globalThis.__upgradeIdCounter__ = 0;\n `);\n if (registryResult.error) {\n registryResult.error.dispose();\n } else {\n registryResult.value.dispose();\n }\n\n // Host function to set pendingUpgrade - only receives connectionId string\n const setPendingUpgradeFn = context.newFunction(\n \"__setPendingUpgrade__\",\n (connIdHandle) => {\n const connectionId = context.getString(connIdHandle);\n serveState.pendingUpgrade = { requested: true, connectionId };\n return context.undefined;\n }\n );\n context.setProp(context.global, \"__setPendingUpgrade__\", setPendingUpgradeFn);\n setPendingUpgradeFn.dispose();\n\n // Add upgrade method to Server prototype as pure JavaScript\n // This keeps the data within QuickJS and only passes connectionId to host\n const upgradeMethodResult = context.evalCode(`\n __Server__.prototype.upgrade = function(request, options) {\n const data = options && typeof options === 'object' ? options.data : undefined;\n const connectionId = String(++globalThis.__upgradeIdCounter__);\n globalThis.__upgradeRegistry__.set(connectionId, data);\n __setPendingUpgrade__(connectionId);\n return true;\n };\n `);\n if (upgradeMethodResult.error) {\n upgradeMethodResult.error.dispose();\n } else {\n upgradeMethodResult.value.dispose();\n }\n\n // Create fetch function\n const fetchFn = createFetchFunction(context, options.onFetch);\n context.setProp(context.global, \"fetch\", fetchFn);\n fetchFn.dispose();\n\n // Create serve function\n const serveFn = createServeFunction(context, stateMap, serveState);\n context.setProp(context.global, \"serve\", serveFn);\n serveFn.dispose();\n\n // Create and return the handle\n const fetchHandle = createFetchHandle(\n context,\n stateMap,\n serveState\n );\n\n // Wire up WebSocket command callbacks\n const originalOnWebSocketCommand = fetchHandle.onWebSocketCommand;\n fetchHandle.onWebSocketCommand = (callback) => {\n wsCommandCallbacks.add(callback);\n return () => wsCommandCallbacks.delete(callback);\n };\n\n return fetchHandle;\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "540F333C1CD9EF6B64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "AF095A66B34E6FFE64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -15,6 +15,8 @@ interface StreamHelpers {
15
15
  instanceId: number;
16
16
  globalKey: string;
17
17
  };
18
+ /** Gets a fresh handle to the stream stored at globalKey */
19
+ getStreamByKey: (globalKey: string) => QuickJSHandle | null;
18
20
  pumpEventLoop: () => void;
19
21
  }
20
22
  /**
@@ -8,7 +8,7 @@ export declare function createServerWebSocketClass(context: QuickJSContext, stat
8
8
  /**
9
9
  * Create the Server class for QuickJS
10
10
  */
11
- export declare function createServerClass(context: QuickJSContext, stateMap: StateMap, serveState: ServeState): QuickJSHandle;
11
+ export declare function createServerClass(context: QuickJSContext, stateMap: StateMap, _serveState: ServeState): QuickJSHandle;
12
12
  /**
13
13
  * Create the serve function for QuickJS
14
14
  * Uses context.newFunction instead of defineFunction to properly manage handle lifetimes
@@ -30,8 +30,8 @@ export interface WebSocketData {
30
30
  export interface UpgradeRequest {
31
31
  /** Whether an upgrade was requested */
32
32
  requested: true;
33
- /** User data passed to server.upgrade() */
34
- data: unknown;
33
+ /** Connection ID generated by server.upgrade() - used to look up user data from QuickJS registry */
34
+ connectionId: string;
35
35
  }
36
36
  /**
37
37
  * Message to send to a WebSocket
@@ -66,7 +66,6 @@ export interface ServeState {
66
66
  activeConnections: Map<string, ServerWebSocketState>;
67
67
  }
68
68
  export interface ServerWebSocketState {
69
- data: unknown;
70
69
  readyState: number;
71
70
  connectionId: string;
72
71
  wsHandle: QuickJSHandle | null;
@@ -92,10 +91,9 @@ export interface FetchHandle {
92
91
  /**
93
92
  * Dispatch WebSocket open event
94
93
  *
95
- * @param connectionId Unique ID for this connection (host-assigned)
96
- * @param data The data object from getUpgradeRequest()
94
+ * @param connectionId The connectionId from getUpgradeRequest() - used to look up data from QuickJS registry
97
95
  */
98
- dispatchWebSocketOpen(connectionId: string, data?: unknown): void;
96
+ dispatchWebSocketOpen(connectionId: string): void;
99
97
  /**
100
98
  * Dispatch WebSocket message event
101
99
  */
@@ -117,6 +115,10 @@ export interface FetchHandle {
117
115
  * Check if a serve() handler is registered
118
116
  */
119
117
  hasServeHandler(): boolean;
118
+ /**
119
+ * Check if there are active WebSocket connections
120
+ */
121
+ hasActiveConnections(): boolean;
120
122
  /**
121
123
  * Dispose all handles and cleanup
122
124
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/quickjs-fetch",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "main": "./dist/cjs/index.cjs",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "exports": {
@@ -16,7 +16,7 @@
16
16
  "typecheck": "tsc --noEmit"
17
17
  },
18
18
  "dependencies": {
19
- "@ricsam/quickjs-core": "^0.2.8",
19
+ "@ricsam/quickjs-core": "^0.2.10",
20
20
  "quickjs-emscripten": "^0.31.0"
21
21
  },
22
22
  "peerDependencies": {