@ricsam/quickjs-fetch 0.2.14 → 0.2.15
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/dist/cjs/globals/fetch.cjs +19 -22
- package/dist/cjs/globals/fetch.cjs.map +3 -3
- package/dist/cjs/globals/headers.cjs +5 -30
- package/dist/cjs/globals/headers.cjs.map +3 -3
- package/dist/cjs/globals/request.cjs +167 -65
- package/dist/cjs/globals/request.cjs.map +3 -3
- package/dist/cjs/globals/response.cjs +103 -53
- package/dist/cjs/globals/response.cjs.map +3 -3
- package/dist/cjs/handle.cjs +9 -6
- package/dist/cjs/handle.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/setup.cjs +3 -1
- package/dist/cjs/setup.cjs.map +3 -3
- package/dist/cjs/types.cjs +50 -1
- package/dist/cjs/types.cjs.map +4 -3
- package/dist/mjs/globals/fetch.mjs +20 -23
- package/dist/mjs/globals/fetch.mjs.map +3 -3
- package/dist/mjs/globals/headers.mjs +6 -31
- package/dist/mjs/globals/headers.mjs.map +3 -3
- package/dist/mjs/globals/request.mjs +175 -66
- package/dist/mjs/globals/request.mjs.map +3 -3
- package/dist/mjs/globals/response.mjs +108 -54
- package/dist/mjs/globals/response.mjs.map +3 -3
- package/dist/mjs/handle.mjs +9 -6
- package/dist/mjs/handle.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/setup.mjs +5 -3
- package/dist/mjs/setup.mjs.map +3 -3
- package/dist/mjs/types.mjs +42 -1
- package/dist/mjs/types.mjs.map +4 -3
- package/dist/types/globals/request.d.ts +9 -0
- package/dist/types/globals/response.d.ts +9 -0
- package/dist/types/types.d.ts +48 -0
- package/package.json +2 -2
package/dist/cjs/handle.cjs.map
CHANGED
|
@@ -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.cjs\";\nimport { createRequestStateFromNative } from \"./globals/request.cjs\";\nimport { responseStateToNative } from \"./globals/response.cjs\";\nimport { headersStateToNative } from \"./globals/headers.cjs\";\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"
|
|
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.cjs\";\nimport { isUnmarshalledUint8Array } from \"./types.cjs\";\nimport { createRequestStateFromNative } from \"./globals/request.cjs\";\nimport { responseStateToNative } from \"./globals/response.cjs\";\nimport { headersStateToNative } from \"./globals/headers.cjs\";\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 (isUnmarshalledUint8Array(chunk)) {\n // Handle unmarshalled Uint8Array (object with numeric keys)\n const keys = Object.keys(chunk).filter(k => !isNaN(parseInt(k, 10))).sort((a, b) => parseInt(a, 10) - parseInt(b, 10));\n bytes = new Uint8Array(keys.length);\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i]!;\n bytes[i] = chunk[key] ?? 0;\n }\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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACkF,IAAlF;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "2F54439D2001225C64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED
package/dist/cjs/setup.cjs
CHANGED
|
@@ -131,6 +131,8 @@ function setupFetch(context, options = {}) {
|
|
|
131
131
|
formDataIteratorResult.value.dispose();
|
|
132
132
|
}
|
|
133
133
|
import_request.addRequestFormDataMethod(context);
|
|
134
|
+
import_request.addRequestCloneMethod(context);
|
|
135
|
+
import_response.addResponseCloneMethod(context);
|
|
134
136
|
import_form_data.addFormDataFileMethods(context);
|
|
135
137
|
const ServerWebSocketClass = import_serve.createServerWebSocketClass(context, stateMap, dispatchWsCommand);
|
|
136
138
|
context.setProp(context.global, "__ServerWebSocket__", ServerWebSocketClass);
|
|
@@ -198,4 +200,4 @@ function setupFetch(context, options = {}) {
|
|
|
198
200
|
}
|
|
199
201
|
})
|
|
200
202
|
|
|
201
|
-
//# debugId=
|
|
203
|
+
//# debugId=A83500DEB966DAA164756E2164756E21
|
package/dist/cjs/setup.cjs.map
CHANGED
|
@@ -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.cjs\";\nimport { createHeadersClass } from \"./globals/headers.cjs\";\nimport { createRequestClass, addRequestFormDataMethod } from \"./globals/request.cjs\";\nimport { createResponseClass, addResponseStaticMethods } from \"./globals/response.cjs\";\nimport { setupAbortControllerAndSignal } from \"./globals/abort-controller.cjs\";\nimport { createFormDataClass, addFormDataFileMethods } from \"./globals/form-data.cjs\";\nimport { createFetchFunction } from \"./globals/fetch.cjs\";\nimport {\n createServeFunction,\n createServerClass,\n createServerWebSocketClass,\n} from \"./globals/serve.cjs\";\nimport { createFetchHandle } from \"./handle.cjs\";\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.cjs\").WebSocketCommand) => void\n >();\n const dispatchWsCommand = (cmd: import(\"./types.cjs\").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"
|
|
5
|
+
"import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { setupCore, createStateMap, createReadableStream } from \"@ricsam/quickjs-core\";\nimport type { SetupFetchOptions, FetchHandle, ServeState } from \"./types.cjs\";\nimport { createHeadersClass } from \"./globals/headers.cjs\";\nimport { createRequestClass, addRequestFormDataMethod, addRequestCloneMethod } from \"./globals/request.cjs\";\nimport { createResponseClass, addResponseStaticMethods, addResponseCloneMethod } from \"./globals/response.cjs\";\nimport { setupAbortControllerAndSignal } from \"./globals/abort-controller.cjs\";\nimport { createFormDataClass, addFormDataFileMethods } from \"./globals/form-data.cjs\";\nimport { createFetchFunction } from \"./globals/fetch.cjs\";\nimport {\n createServeFunction,\n createServerClass,\n createServerWebSocketClass,\n} from \"./globals/serve.cjs\";\nimport { createFetchHandle } from \"./handle.cjs\";\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.cjs\").WebSocketCommand) => void\n >();\n const dispatchWsCommand = (cmd: import(\"./types.cjs\").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 Request.clone() method that returns a proper Request instance\n // Must be after Request and Headers are on global\n addRequestCloneMethod(context);\n\n // Add Response.clone() method that returns a proper Response instance\n // Must be after Response and Headers are on global\n addResponseCloneMethod(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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACgE,IAAhE;AAEmC,IAAnC;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "A83500DEB966DAA164756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/types.cjs
CHANGED
|
@@ -17,10 +17,59 @@ var __toCommonJS = (from) => {
|
|
|
17
17
|
__moduleCache.set(from, entry);
|
|
18
18
|
return entry;
|
|
19
19
|
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
20
29
|
|
|
21
30
|
// packages/fetch/src/types.ts
|
|
22
31
|
var exports_types = {};
|
|
32
|
+
__export(exports_types, {
|
|
33
|
+
isUnmarshalledUint8Array: () => isUnmarshalledUint8Array,
|
|
34
|
+
isResponseState: () => isResponseState,
|
|
35
|
+
isRequestState: () => isRequestState,
|
|
36
|
+
isHeadersState: () => isHeadersState,
|
|
37
|
+
isAbortSignalState: () => isAbortSignalState
|
|
38
|
+
});
|
|
23
39
|
module.exports = __toCommonJS(exports_types);
|
|
40
|
+
function isHeadersState(obj) {
|
|
41
|
+
return obj !== null && typeof obj === "object" && "headers" in obj && obj.headers instanceof Map;
|
|
42
|
+
}
|
|
43
|
+
function isRequestState(obj) {
|
|
44
|
+
if (obj === null || typeof obj !== "object")
|
|
45
|
+
return false;
|
|
46
|
+
const state = obj;
|
|
47
|
+
return typeof state.method === "string" && typeof state.url === "string" && isHeadersState(state.headersState);
|
|
48
|
+
}
|
|
49
|
+
function isResponseState(obj) {
|
|
50
|
+
if (obj === null || typeof obj !== "object")
|
|
51
|
+
return false;
|
|
52
|
+
const state = obj;
|
|
53
|
+
return typeof state.status === "number" && "headersState" in state && isHeadersState(state.headersState) && typeof state.bodyUsed === "boolean";
|
|
54
|
+
}
|
|
55
|
+
function isAbortSignalState(obj) {
|
|
56
|
+
if (obj === null || typeof obj !== "object")
|
|
57
|
+
return false;
|
|
58
|
+
const state = obj;
|
|
59
|
+
return typeof state.aborted === "boolean" && "listeners" in state && state.listeners instanceof Set;
|
|
60
|
+
}
|
|
61
|
+
function isUnmarshalledUint8Array(obj) {
|
|
62
|
+
if (obj === null || typeof obj !== "object")
|
|
63
|
+
return false;
|
|
64
|
+
if (!("0" in obj))
|
|
65
|
+
return false;
|
|
66
|
+
const record = obj;
|
|
67
|
+
for (let i = 0;i < Math.min(3, Object.keys(record).length); i++) {
|
|
68
|
+
if (typeof record[String(i)] !== "number")
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
24
73
|
})
|
|
25
74
|
|
|
26
|
-
//# debugId=
|
|
75
|
+
//# debugId=03E8D870B0ED32C564756E2164756E21
|
package/dist/cjs/types.cjs.map
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": [],
|
|
3
|
+
"sources": ["../../src/types.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
+
"import type { QuickJSHandle } from \"quickjs-emscripten\";\nimport type { StateMap, CoreHandle } from \"@ricsam/quickjs-core\";\n\nexport type { StateMap, CoreHandle };\n\n/**\n * Options for setting up Fetch APIs\n */\nexport interface SetupFetchOptions {\n /**\n * Handler for outbound fetch() calls from QuickJS\n * If not provided, fetch() will throw an error\n */\n onFetch?: (request: Request) => Promise<Response>;\n\n /** Existing state map (creates new one if not provided) */\n stateMap?: StateMap;\n\n /** Existing core handle (sets up core if not provided) */\n coreHandle?: CoreHandle;\n}\n\n/**\n * Data associated with a WebSocket connection\n */\nexport interface WebSocketData {\n /** User-provided data from server.upgrade() */\n data: unknown;\n\n /** Connection ID assigned by the host */\n connectionId: string;\n}\n\n/**\n * Pending upgrade request info\n */\nexport interface UpgradeRequest {\n /** Whether an upgrade was requested */\n requested: true;\n\n /** Connection ID generated by server.upgrade() - used to look up user data from QuickJS registry */\n connectionId: string;\n}\n\n/**\n * Message to send to a WebSocket\n */\nexport interface WebSocketOutgoingMessage {\n type: \"message\";\n connectionId: string;\n data: string | ArrayBuffer;\n}\n\n/**\n * Close command for a WebSocket\n */\nexport interface WebSocketClose {\n type: \"close\";\n connectionId: string;\n code?: number;\n reason?: string;\n}\n\nexport type WebSocketCommand = WebSocketOutgoingMessage | WebSocketClose;\n\n/**\n * Internal state for serve() handler\n */\nexport interface ServeState {\n fetchHandler: QuickJSHandle | null;\n websocketHandlers: {\n open?: QuickJSHandle;\n message?: QuickJSHandle;\n close?: QuickJSHandle;\n error?: QuickJSHandle;\n };\n pendingUpgrade: UpgradeRequest | null;\n activeConnections: Map<string, ServerWebSocketState>;\n}\n\nexport interface ServerWebSocketState {\n // Note: 'data' is accessed via pure-JS getter that reads from __upgradeRegistry__\n // This avoids marshalling complex objects (like Zod schemas) that exceed depth limits\n readyState: number;\n connectionId: string;\n wsHandle: QuickJSHandle | null;\n}\n\n/**\n * Handle returned from setupFetch\n */\nexport interface FetchHandle {\n /** State map containing all internal states */\n readonly stateMap: StateMap;\n\n /**\n * Dispatch an HTTP request to the serve() handler\n *\n * @returns Response from the QuickJS handler\n * @throws If no serve() handler is registered\n */\n dispatchRequest(request: Request): Promise<Response>;\n\n /**\n * Check if the last request resulted in an upgrade request\n * Must be called immediately after dispatchRequest()\n */\n getUpgradeRequest(): UpgradeRequest | null;\n\n /**\n * Dispatch WebSocket open event\n *\n * @param connectionId The connectionId from getUpgradeRequest() - used to look up data from QuickJS registry\n */\n dispatchWebSocketOpen(connectionId: string): void;\n\n /**\n * Dispatch WebSocket message event\n */\n dispatchWebSocketMessage(\n connectionId: string,\n message: string | ArrayBuffer\n ): void;\n\n /**\n * Dispatch WebSocket close event\n */\n dispatchWebSocketClose(\n connectionId: string,\n code: number,\n reason: string\n ): void;\n\n /**\n * Dispatch WebSocket error event\n */\n dispatchWebSocketError(connectionId: string, error: Error): void;\n\n /**\n * Register a callback for outgoing WebSocket messages/commands\n * Called when QuickJS code calls ws.send() or ws.close()\n */\n onWebSocketCommand(callback: (command: WebSocketCommand) => void): () => void;\n\n /**\n * Check if a serve() handler is registered\n */\n hasServeHandler(): boolean;\n\n /**\n * Check if there are active WebSocket connections\n */\n hasActiveConnections(): boolean;\n\n /**\n * Dispose all handles and cleanup\n */\n dispose(): void;\n}\n\n// Internal state types for the classes\nexport interface HeadersState {\n headers: Map<string, string[]>;\n}\n\n/**\n * What we get when unmarshalling a Request instance from QuickJS.\n * Contains identity markers + enumerable debug properties.\n */\nexport interface UnmarshalledRequest {\n __instanceId__: number;\n __className__: \"Request\";\n __isDefineClassInstance__: true;\n // Debug properties (may be present from unmarshal.ts)\n method?: string;\n url?: string;\n}\n\n/**\n * What we get when unmarshalling a Response instance from QuickJS.\n * Contains identity markers + enumerable debug properties.\n */\nexport interface UnmarshalledResponse {\n __instanceId__: number;\n __className__: \"Response\";\n __isDefineClassInstance__: true;\n // Debug properties (may be present from unmarshal.ts)\n status?: number;\n ok?: boolean;\n url?: string;\n}\n\nexport interface RequestState {\n method: string;\n url: string;\n headersState: HeadersState;\n body: Uint8Array | null;\n bodyUsed: boolean;\n cache: string;\n credentials: string;\n destination: string;\n integrity: string;\n keepalive: boolean;\n mode: string;\n redirect: string;\n referrer: string;\n referrerPolicy: string;\n signal: AbortSignalState | null;\n /** Native ReadableStream for streaming request bodies (upload streaming) */\n nativeBodyStream?: ReadableStream<Uint8Array>;\n /** Instance ID of QuickJS stream created by body getter (for consumption methods) */\n _uploadStreamInstanceId?: number;\n}\n\nexport interface ResponseState {\n status: number;\n statusText: string;\n headersState: HeadersState;\n body: Uint8Array | null;\n bodyUsed: boolean;\n url: string;\n redirected: boolean;\n type: string;\n ok: boolean;\n /** Original body type - used to convert back to string for native Response */\n bodyType?: \"string\" | \"binary\" | \"stream\" | null;\n /** Instance ID of ReadableStream when bodyType is \"stream\" */\n streamInstanceId?: number;\n}\n\nexport interface AbortControllerState {\n signalState: AbortSignalState;\n}\n\nexport interface AbortSignalState {\n aborted: boolean;\n reason: unknown;\n listeners: Set<() => void>;\n}\n\nexport interface FormDataFileValue {\n __formDataFile__: true;\n data: Uint8Array;\n filename: string;\n type: string;\n}\n\nexport interface FormDataEntry {\n name: string;\n value: string | FormDataFileValue;\n}\n\nexport interface FormDataState {\n entries: FormDataEntry[];\n}\n\n/**\n * Host-side queue for upload stream bridging.\n * Native ReadableStream reader pushes chunks here.\n * QuickJS ReadableStream's synchronous pull() reads from here.\n * This avoids async callbacks with QuickJS handles.\n */\nexport interface UploadStreamQueue {\n /** Buffered chunks waiting to be read by QuickJS */\n chunks: Uint8Array[];\n /** Whether the native stream has closed */\n closed: boolean;\n /** Error from native stream if any */\n error?: Error;\n /** Whether the native reader should pause (backpressure) */\n paused: boolean;\n /** Callback to resume native reader when queue is drained */\n resumeCallback?: () => void;\n /** Callback to cancel native reader when QuickJS stream is cancelled */\n cancelCallback?: () => void;\n}\n\n// ============================================\n// Type Guards\n// ============================================\n\n/**\n * Type guard for HeadersState.\n * Validates that the object has the correct structure with a Map.\n */\nexport function isHeadersState(obj: unknown): obj is HeadersState {\n return (\n obj !== null &&\n typeof obj === \"object\" &&\n \"headers\" in obj &&\n (obj as HeadersState).headers instanceof Map\n );\n}\n\n/**\n * Type guard for RequestState.\n * Validates key properties including their types.\n */\nexport function isRequestState(obj: unknown): obj is RequestState {\n if (obj === null || typeof obj !== \"object\") return false;\n const state = obj as Partial<RequestState>;\n return (\n typeof state.method === \"string\" &&\n typeof state.url === \"string\" &&\n isHeadersState(state.headersState)\n );\n}\n\n/**\n * Type guard for ResponseState.\n * Validates key properties including their types.\n */\nexport function isResponseState(obj: unknown): obj is ResponseState {\n if (obj === null || typeof obj !== \"object\") return false;\n const state = obj as Partial<ResponseState>;\n return (\n typeof state.status === \"number\" &&\n \"headersState\" in state &&\n isHeadersState(state.headersState) &&\n typeof state.bodyUsed === \"boolean\"\n );\n}\n\n/**\n * Type guard for AbortSignalState.\n * Validates the aborted flag and listeners set.\n */\nexport function isAbortSignalState(obj: unknown): obj is AbortSignalState {\n if (obj === null || typeof obj !== \"object\") return false;\n const state = obj as Partial<AbortSignalState>;\n return (\n typeof state.aborted === \"boolean\" &&\n \"listeners\" in state &&\n state.listeners instanceof Set\n );\n}\n\n/**\n * Type guard for unmarshalled Uint8Array-like objects.\n * These are objects with numeric keys and number values, created by unmarshal.ts.\n */\nexport function isUnmarshalledUint8Array(obj: unknown): obj is Record<string, number> {\n if (obj === null || typeof obj !== \"object\") return false;\n if (!(\"0\" in obj)) return false;\n // Verify at least the first few numeric keys have number values\n // (checking all keys would be expensive for large arrays)\n const record = obj as Record<string, unknown>;\n for (let i = 0; i < Math.min(3, Object.keys(record).length); i++) {\n if (typeof record[String(i)] !== \"number\") return false;\n }\n return true;\n}\n"
|
|
5
6
|
],
|
|
6
|
-
"mappings": "",
|
|
7
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6RO,SAAS,cAAc,CAAC,KAAmC;AAAA,EAChE,OACE,QAAQ,QACR,OAAO,QAAQ,YACf,aAAa,OACZ,IAAqB,mBAAmB;AAAA;AAQtC,SAAS,cAAc,CAAC,KAAmC;AAAA,EAChE,IAAI,QAAQ,QAAQ,OAAO,QAAQ;AAAA,IAAU,OAAO;AAAA,EACpD,MAAM,QAAQ;AAAA,EACd,OACE,OAAO,MAAM,WAAW,YACxB,OAAO,MAAM,QAAQ,YACrB,eAAe,MAAM,YAAY;AAAA;AAQ9B,SAAS,eAAe,CAAC,KAAoC;AAAA,EAClE,IAAI,QAAQ,QAAQ,OAAO,QAAQ;AAAA,IAAU,OAAO;AAAA,EACpD,MAAM,QAAQ;AAAA,EACd,OACE,OAAO,MAAM,WAAW,YACxB,kBAAkB,SAClB,eAAe,MAAM,YAAY,KACjC,OAAO,MAAM,aAAa;AAAA;AAQvB,SAAS,kBAAkB,CAAC,KAAuC;AAAA,EACxE,IAAI,QAAQ,QAAQ,OAAO,QAAQ;AAAA,IAAU,OAAO;AAAA,EACpD,MAAM,QAAQ;AAAA,EACd,OACE,OAAO,MAAM,YAAY,aACzB,eAAe,SACf,MAAM,qBAAqB;AAAA;AAQxB,SAAS,wBAAwB,CAAC,KAA6C;AAAA,EACpF,IAAI,QAAQ,QAAQ,OAAO,QAAQ;AAAA,IAAU,OAAO;AAAA,EACpD,IAAI,EAAE,OAAO;AAAA,IAAM,OAAO;AAAA,EAG1B,MAAM,SAAS;AAAA,EACf,SAAS,IAAI,EAAG,IAAI,KAAK,IAAI,GAAG,OAAO,KAAK,MAAM,EAAE,MAAM,GAAG,KAAK;AAAA,IAChE,IAAI,OAAO,OAAO,OAAO,CAAC,OAAO;AAAA,MAAU,OAAO;AAAA,EACpD;AAAA,EACA,OAAO;AAAA;",
|
|
8
|
+
"debugId": "03E8D870B0ED32C564756E2164756E21",
|
|
8
9
|
"names": []
|
|
9
10
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/fetch/src/globals/fetch.ts
|
|
3
|
-
import { unmarshal, marshal } from "@ricsam/quickjs-core";
|
|
3
|
+
import { unmarshal, marshal, coerceHeaders, coerceBody } from "@ricsam/quickjs-core";
|
|
4
|
+
import { isRequestState } from "../types.mjs";
|
|
4
5
|
import { headersStateToNative } from "./headers.mjs";
|
|
5
6
|
import { createResponseStateFromNative } from "./response.mjs";
|
|
6
7
|
function createFetchFunction(context, onFetch) {
|
|
@@ -75,13 +76,17 @@ function createFetchFunction(context, onFetch) {
|
|
|
75
76
|
let body = null;
|
|
76
77
|
if (typeof input === "string") {
|
|
77
78
|
url = input;
|
|
79
|
+
} else if (isRequestState(input)) {
|
|
80
|
+
url = input.url;
|
|
81
|
+
method = input.method;
|
|
82
|
+
headers = headersStateToNative(input.headersState);
|
|
83
|
+
if (input.body) {
|
|
84
|
+
body = input.body.buffer.slice(input.body.byteOffset, input.body.byteOffset + input.body.byteLength);
|
|
85
|
+
}
|
|
78
86
|
} else if (input && typeof input === "object" && "url" in input) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
headers = headersStateToNative(reqState.headersState);
|
|
83
|
-
if (reqState.body) {
|
|
84
|
-
body = reqState.body;
|
|
87
|
+
url = String(input.url);
|
|
88
|
+
if ("method" in input && typeof input.method === "string") {
|
|
89
|
+
method = input.method;
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
if (init) {
|
|
@@ -89,8 +94,9 @@ function createFetchFunction(context, onFetch) {
|
|
|
89
94
|
method = init.method;
|
|
90
95
|
}
|
|
91
96
|
if (init.headers) {
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
const coerced = coerceHeaders.safeParse(init.headers);
|
|
98
|
+
if (coerced.success) {
|
|
99
|
+
headers = headersStateToNative(coerced.value);
|
|
94
100
|
} else {
|
|
95
101
|
headers = new Headers;
|
|
96
102
|
for (const [key, value] of Object.entries(init.headers)) {
|
|
@@ -99,22 +105,13 @@ function createFetchFunction(context, onFetch) {
|
|
|
99
105
|
}
|
|
100
106
|
}
|
|
101
107
|
if (init.body !== undefined) {
|
|
102
|
-
|
|
108
|
+
const coercedBody = coerceBody(init.body);
|
|
109
|
+
if (coercedBody) {
|
|
110
|
+
body = coercedBody.buffer.slice(coercedBody.byteOffset, coercedBody.byteOffset + coercedBody.byteLength);
|
|
111
|
+
} else if (typeof init.body === "string") {
|
|
103
112
|
body = init.body;
|
|
104
113
|
} else if (init.body instanceof ArrayBuffer) {
|
|
105
114
|
body = init.body;
|
|
106
|
-
} else if (init.body instanceof Uint8Array) {
|
|
107
|
-
body = init.body;
|
|
108
|
-
} else if (init.body && typeof init.body === "object" && "parts" in init.body) {
|
|
109
|
-
const parts = init.body.parts;
|
|
110
|
-
const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
|
|
111
|
-
const combined = new Uint8Array(totalLength);
|
|
112
|
-
let offset = 0;
|
|
113
|
-
for (const part of parts) {
|
|
114
|
-
combined.set(part, offset);
|
|
115
|
-
offset += part.length;
|
|
116
|
-
}
|
|
117
|
-
body = combined;
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
117
|
}
|
|
@@ -165,4 +162,4 @@ export {
|
|
|
165
162
|
createFetchFunction
|
|
166
163
|
};
|
|
167
164
|
|
|
168
|
-
//# debugId=
|
|
165
|
+
//# debugId=960321ED419AEC7A64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/globals/fetch.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { unmarshal, marshal } from \"@ricsam/quickjs-core\";\nimport type { RequestState, HeadersState } from \"../types.mjs\";\nimport { headersStateToNative } from \"./headers.mjs\";\nimport { createResponseStateFromNative } from \"./response.mjs\";\n\n/**\n * Create the fetch function for QuickJS\n */\nexport function createFetchFunction(\n context: QuickJSContext,\n onFetch?: (request: Request) => Promise<Response>\n): QuickJSHandle {\n return context.newFunction(\"fetch\", (...argHandles) => {\n // Create promise first\n const deferred = context.newPromise();\n\n const inputHandle = argHandles[0];\n const initHandle = argHandles[1];\n\n // Extract signal BEFORE unmarshalling (keep as handle)\n let signalHandle: QuickJSHandle | null = null;\n // Check if initHandle exists and is a valid QuickJS handle (not JS undefined)\n if (initHandle !== undefined) {\n const initType = context.typeof(initHandle);\n if (initType === \"object\") {\n const tempSignal = context.getProp(initHandle, \"signal\");\n const signalType = context.typeof(tempSignal);\n if (signalType !== \"undefined\" && signalType !== \"null\") {\n signalHandle = tempSignal;\n } else {\n tempSignal.dispose();\n }\n }\n }\n\n // Check if signal is pre-aborted\n let isPreAborted = false;\n let abortReason: { message?: string } | undefined;\n if (signalHandle) {\n const abortedHandle = context.getProp(signalHandle, \"aborted\");\n isPreAborted = Boolean(context.dump(abortedHandle));\n abortedHandle.dispose();\n\n if (isPreAborted) {\n const reasonHandle = context.getProp(signalHandle, \"reason\");\n abortReason = context.dump(reasonHandle) as { message?: string } | undefined;\n reasonHandle.dispose();\n signalHandle.dispose();\n signalHandle = null;\n }\n }\n\n // If pre-aborted, reject immediately in a microtask\n if (isPreAborted) {\n Promise.resolve().then(() => {\n // Create DOMException and reject\n const errorMessage = abortReason?.message || \"signal is aborted without reason\";\n // Escape quotes in the message for safety\n const safeMessage = errorMessage.replace(/\"/g, '\\\\\"');\n const errorResult = context.evalCode(\n `new DOMException(\"${safeMessage}\", \"AbortError\")`\n );\n if (\"value\" in errorResult) {\n deferred.reject(errorResult.value);\n errorResult.value.dispose();\n } else {\n // If evalCode failed, create a plain error\n const fallbackError = marshal(context, { name: \"AbortError\", message: errorMessage });\n deferred.reject(fallbackError);\n fallbackError.dispose();\n errorResult.error.dispose();\n }\n context.runtime.executePendingJobs();\n });\n return deferred.handle;\n }\n\n // Create native AbortController\n const nativeController = new AbortController();\n\n // Wire QuickJS signal to native controller\n if (signalHandle) {\n // Create callback that aborts native controller when QuickJS signal aborts\n const abortCallback = context.newFunction(\"__fetchAbortCallback__\", () => {\n nativeController.abort();\n return context.undefined;\n });\n\n // Call addEventListener on the QuickJS signal\n const addEventListenerHandle = context.getProp(signalHandle, \"addEventListener\");\n const typeHandle = context.newString(\"abort\");\n\n context.callFunction(addEventListenerHandle, signalHandle, typeHandle, abortCallback);\n\n typeHandle.dispose();\n addEventListenerHandle.dispose();\n abortCallback.dispose();\n signalHandle.dispose();\n }\n\n // Unmarshal arguments\n const input = inputHandle ? unmarshal(context, inputHandle) : undefined;\n const init = initHandle ? unmarshal(context, initHandle) as {\n method?: string;\n headers?: object;\n body?: unknown;\n } | undefined : undefined;\n\n // Build the request\n let url = \"\";\n let method = \"GET\";\n let headers = new Headers();\n let body: BodyInit | null = null;\n\n if (typeof input === \"string\") {\n url = input;\n } else if (input && typeof input === \"object\" && \"url\" in input) {\n //
|
|
5
|
+
"import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport { unmarshal, marshal, coerceHeaders, coerceBody } from \"@ricsam/quickjs-core\";\nimport type { RequestState, HeadersState } from \"../types.mjs\";\nimport { isRequestState } from \"../types.mjs\";\nimport { headersStateToNative } from \"./headers.mjs\";\nimport { createResponseStateFromNative } from \"./response.mjs\";\n\n/**\n * Create the fetch function for QuickJS\n */\nexport function createFetchFunction(\n context: QuickJSContext,\n onFetch?: (request: Request) => Promise<Response>\n): QuickJSHandle {\n return context.newFunction(\"fetch\", (...argHandles) => {\n // Create promise first\n const deferred = context.newPromise();\n\n const inputHandle = argHandles[0];\n const initHandle = argHandles[1];\n\n // Extract signal BEFORE unmarshalling (keep as handle)\n let signalHandle: QuickJSHandle | null = null;\n // Check if initHandle exists and is a valid QuickJS handle (not JS undefined)\n if (initHandle !== undefined) {\n const initType = context.typeof(initHandle);\n if (initType === \"object\") {\n const tempSignal = context.getProp(initHandle, \"signal\");\n const signalType = context.typeof(tempSignal);\n if (signalType !== \"undefined\" && signalType !== \"null\") {\n signalHandle = tempSignal;\n } else {\n tempSignal.dispose();\n }\n }\n }\n\n // Check if signal is pre-aborted\n let isPreAborted = false;\n let abortReason: { message?: string } | undefined;\n if (signalHandle) {\n const abortedHandle = context.getProp(signalHandle, \"aborted\");\n isPreAborted = Boolean(context.dump(abortedHandle));\n abortedHandle.dispose();\n\n if (isPreAborted) {\n const reasonHandle = context.getProp(signalHandle, \"reason\");\n abortReason = context.dump(reasonHandle) as { message?: string } | undefined;\n reasonHandle.dispose();\n signalHandle.dispose();\n signalHandle = null;\n }\n }\n\n // If pre-aborted, reject immediately in a microtask\n if (isPreAborted) {\n Promise.resolve().then(() => {\n // Create DOMException and reject\n const errorMessage = abortReason?.message || \"signal is aborted without reason\";\n // Escape quotes in the message for safety\n const safeMessage = errorMessage.replace(/\"/g, '\\\\\"');\n const errorResult = context.evalCode(\n `new DOMException(\"${safeMessage}\", \"AbortError\")`\n );\n if (\"value\" in errorResult) {\n deferred.reject(errorResult.value);\n errorResult.value.dispose();\n } else {\n // If evalCode failed, create a plain error\n const fallbackError = marshal(context, { name: \"AbortError\", message: errorMessage });\n deferred.reject(fallbackError);\n fallbackError.dispose();\n errorResult.error.dispose();\n }\n context.runtime.executePendingJobs();\n });\n return deferred.handle;\n }\n\n // Create native AbortController\n const nativeController = new AbortController();\n\n // Wire QuickJS signal to native controller\n if (signalHandle) {\n // Create callback that aborts native controller when QuickJS signal aborts\n const abortCallback = context.newFunction(\"__fetchAbortCallback__\", () => {\n nativeController.abort();\n return context.undefined;\n });\n\n // Call addEventListener on the QuickJS signal\n const addEventListenerHandle = context.getProp(signalHandle, \"addEventListener\");\n const typeHandle = context.newString(\"abort\");\n\n context.callFunction(addEventListenerHandle, signalHandle, typeHandle, abortCallback);\n\n typeHandle.dispose();\n addEventListenerHandle.dispose();\n abortCallback.dispose();\n signalHandle.dispose();\n }\n\n // Unmarshal arguments\n const input = inputHandle ? unmarshal(context, inputHandle) : undefined;\n const init = initHandle ? unmarshal(context, initHandle) as {\n method?: string;\n headers?: object;\n body?: unknown;\n } | undefined : undefined;\n\n // Build the request\n let url = \"\";\n let method = \"GET\";\n let headers = new Headers();\n let body: BodyInit | null = null;\n\n if (typeof input === \"string\") {\n url = input;\n } else if (isRequestState(input)) {\n // Full RequestState object - extract properties with type safety\n url = input.url;\n method = input.method;\n headers = headersStateToNative(input.headersState);\n if (input.body) {\n // Convert Uint8Array to ArrayBuffer for BodyInit compatibility\n body = input.body.buffer.slice(\n input.body.byteOffset,\n input.body.byteOffset + input.body.byteLength\n ) as ArrayBuffer;\n }\n } else if (input && typeof input === \"object\" && \"url\" in input) {\n // Partial object with url property - extract what we can\n url = String((input as Record<string, unknown>).url);\n if (\"method\" in input && typeof (input as Record<string, unknown>).method === \"string\") {\n method = (input as Record<string, unknown>).method as string;\n }\n }\n\n // Apply init overrides\n if (init) {\n if (init.method) {\n method = init.method;\n }\n if (init.headers) {\n // Use coercion system for type-safe headers handling\n const coerced = coerceHeaders.safeParse(init.headers);\n if (coerced.success) {\n headers = headersStateToNative(coerced.value);\n } else {\n // Fallback for plain objects\n headers = new Headers();\n for (const [key, value] of Object.entries(init.headers)) {\n headers.set(key, String(value));\n }\n }\n }\n if (init.body !== undefined) {\n // Use coerceBody for type-safe body handling\n const coercedBody = coerceBody(init.body);\n if (coercedBody) {\n // Convert Uint8Array to ArrayBuffer for BodyInit compatibility\n body = coercedBody.buffer.slice(\n coercedBody.byteOffset,\n coercedBody.byteOffset + coercedBody.byteLength\n ) as ArrayBuffer;\n } else if (typeof init.body === \"string\") {\n body = init.body;\n } else if (init.body instanceof ArrayBuffer) {\n body = init.body;\n }\n }\n }\n\n // Run the async fetch operation in a microtask to ensure proper promise settling\n Promise.resolve().then(async () => {\n try {\n if (!onFetch) {\n throw new Error(\n \"fetch is not configured - no onFetch handler provided to setupFetch\"\n );\n }\n\n // Create native Request with signal\n const nativeRequest = new Request(url, {\n method,\n headers,\n body: method !== \"GET\" && method !== \"HEAD\" ? body : undefined,\n signal: nativeController.signal,\n });\n\n // Call the host fetch handler\n const nativeResponse = await onFetch(nativeRequest);\n\n // Convert to ResponseState for QuickJS\n const responseState = await createResponseStateFromNative(nativeResponse);\n const resultHandle = marshal(context, responseState);\n deferred.resolve(resultHandle);\n resultHandle.dispose();\n } catch (error) {\n // Handle abort errors specially\n if (error instanceof DOMException && error.name === \"AbortError\") {\n const safeMessage = error.message.replace(/\"/g, '\\\\\"');\n const errorResult = context.evalCode(\n `new DOMException(\"${safeMessage}\", \"AbortError\")`\n );\n if (\"value\" in errorResult) {\n deferred.reject(errorResult.value);\n errorResult.value.dispose();\n } else {\n const fallbackError = marshal(context, { name: \"AbortError\", message: error.message });\n deferred.reject(fallbackError);\n fallbackError.dispose();\n errorResult.error.dispose();\n }\n } else {\n const errorHandle = marshal(context, {\n name: error instanceof Error ? error.name : \"Error\",\n message: error instanceof Error ? error.message : String(error),\n });\n deferred.reject(errorHandle);\n errorHandle.dispose();\n }\n }\n context.runtime.executePendingJobs();\n });\n\n return deferred.handle;\n });\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;AACA;AAEA;AACA;AAKO,SAAS,mBAAmB,CACjC,SACA,SACe;AAAA,EACf,OAAO,QAAQ,YAAY,SAAS,IAAI,eAAe;AAAA,IAErD,MAAM,WAAW,QAAQ,WAAW;AAAA,IAEpC,MAAM,cAAc,WAAW;AAAA,IAC/B,MAAM,aAAa,WAAW;AAAA,IAG9B,IAAI,eAAqC;AAAA,IAEzC,IAAI,eAAe,WAAW;AAAA,MAC5B,MAAM,WAAW,QAAQ,OAAO,UAAU;AAAA,MAC1C,IAAI,aAAa,UAAU;AAAA,QACzB,MAAM,aAAa,QAAQ,QAAQ,YAAY,QAAQ;AAAA,QACvD,MAAM,aAAa,QAAQ,OAAO,UAAU;AAAA,QAC5C,IAAI,eAAe,eAAe,eAAe,QAAQ;AAAA,UACvD,eAAe;AAAA,QACjB,EAAO;AAAA,UACL,WAAW,QAAQ;AAAA;AAAA,MAEvB;AAAA,IACF;AAAA,IAGA,IAAI,eAAe;AAAA,IACnB,IAAI;AAAA,IACJ,IAAI,cAAc;AAAA,MAChB,MAAM,gBAAgB,QAAQ,QAAQ,cAAc,SAAS;AAAA,MAC7D,eAAe,QAAQ,QAAQ,KAAK,aAAa,CAAC;AAAA,MAClD,cAAc,QAAQ;AAAA,MAEtB,IAAI,cAAc;AAAA,QAChB,MAAM,eAAe,QAAQ,QAAQ,cAAc,QAAQ;AAAA,QAC3D,cAAc,QAAQ,KAAK,YAAY;AAAA,QACvC,aAAa,QAAQ;AAAA,QACrB,aAAa,QAAQ;AAAA,QACrB,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,IAGA,IAAI,cAAc;AAAA,MAChB,QAAQ,QAAQ,EAAE,KAAK,MAAM;AAAA,QAE3B,MAAM,eAAe,aAAa,WAAW;AAAA,QAE7C,MAAM,cAAc,aAAa,QAAQ,MAAM,MAAK;AAAA,QACpD,MAAM,cAAc,QAAQ,SAC1B,qBAAqB,6BACvB;AAAA,QACA,IAAI,WAAW,aAAa;AAAA,UAC1B,SAAS,OAAO,YAAY,KAAK;AAAA,UACjC,YAAY,MAAM,QAAQ;AAAA,QAC5B,EAAO;AAAA,UAEL,MAAM,gBAAgB,QAAQ,SAAS,EAAE,MAAM,cAAc,SAAS,aAAa,CAAC;AAAA,UACpF,SAAS,OAAO,aAAa;AAAA,UAC7B,cAAc,QAAQ;AAAA,UACtB,YAAY,MAAM,QAAQ;AAAA;AAAA,QAE5B,QAAQ,QAAQ,mBAAmB;AAAA,OACpC;AAAA,MACD,OAAO,SAAS;AAAA,IAClB;AAAA,IAGA,MAAM,mBAAmB,IAAI;AAAA,IAG7B,IAAI,cAAc;AAAA,MAEhB,MAAM,gBAAgB,QAAQ,YAAY,0BAA0B,MAAM;AAAA,QACxE,iBAAiB,MAAM;AAAA,QACvB,OAAO,QAAQ;AAAA,OAChB;AAAA,MAGD,MAAM,yBAAyB,QAAQ,QAAQ,cAAc,kBAAkB;AAAA,MAC/E,MAAM,aAAa,QAAQ,UAAU,OAAO;AAAA,MAE5C,QAAQ,aAAa,wBAAwB,cAAc,YAAY,aAAa;AAAA,MAEpF,WAAW,QAAQ;AAAA,MACnB,uBAAuB,QAAQ;AAAA,MAC/B,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ;AAAA,IACvB;AAAA,IAGA,MAAM,QAAQ,cAAc,UAAU,SAAS,WAAW,IAAI;AAAA,IAC9D,MAAM,OAAO,aAAa,UAAU,SAAS,UAAU,IAIvC;AAAA,IAGhB,IAAI,MAAM;AAAA,IACV,IAAI,SAAS;AAAA,IACb,IAAI,UAAU,IAAI;AAAA,IAClB,IAAI,OAAwB;AAAA,IAE5B,IAAI,OAAO,UAAU,UAAU;AAAA,MAC7B,MAAM;AAAA,IACR,EAAO,SAAI,SAAS,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "960321ED419AEC7A64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,41 +1,16 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/fetch/src/globals/headers.ts
|
|
3
|
-
import { defineClass } from "@ricsam/quickjs-core";
|
|
3
|
+
import { defineClass, coerceHeaders } from "@ricsam/quickjs-core";
|
|
4
4
|
function createHeadersClass(context, stateMap) {
|
|
5
5
|
return defineClass(context, stateMap, {
|
|
6
6
|
name: "Headers",
|
|
7
7
|
construct: (args) => {
|
|
8
8
|
const init = args[0];
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
for (const pair of init) {
|
|
13
|
-
if (Array.isArray(pair) && pair.length >= 2) {
|
|
14
|
-
const key = String(pair[0]).toLowerCase();
|
|
15
|
-
const value = String(pair[1]);
|
|
16
|
-
const existing = headers.get(key) || [];
|
|
17
|
-
existing.push(value);
|
|
18
|
-
headers.set(key, existing);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
} else if (typeof init === "object" && init !== null) {
|
|
22
|
-
if ("headers" in init && init.headers instanceof Map) {
|
|
23
|
-
for (const [key, values] of init.headers) {
|
|
24
|
-
headers.set(key, [...values]);
|
|
25
|
-
}
|
|
26
|
-
} else {
|
|
27
|
-
for (const [key, value] of Object.entries(init)) {
|
|
28
|
-
const normalizedKey = key.toLowerCase();
|
|
29
|
-
if (Array.isArray(value)) {
|
|
30
|
-
headers.set(normalizedKey, value.map(String));
|
|
31
|
-
} else {
|
|
32
|
-
headers.set(normalizedKey, [String(value)]);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
9
|
+
const coerced = coerceHeaders.safeParse(init);
|
|
10
|
+
if (coerced.success) {
|
|
11
|
+
return coerced.value;
|
|
37
12
|
}
|
|
38
|
-
return { headers };
|
|
13
|
+
return { headers: new Map };
|
|
39
14
|
},
|
|
40
15
|
methods: {
|
|
41
16
|
append(name, value) {
|
|
@@ -162,4 +137,4 @@ export {
|
|
|
162
137
|
createHeadersClass
|
|
163
138
|
};
|
|
164
139
|
|
|
165
|
-
//# debugId=
|
|
140
|
+
//# debugId=18F0F0F61ED7D24064756E2164756E21
|