@ricsam/quickjs-fetch 0.2.9 → 0.2.10
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/README.md +4 -3
- package/dist/cjs/globals/request.cjs +7 -4
- package/dist/cjs/globals/request.cjs.map +3 -3
- package/dist/cjs/globals/serve.cjs +4 -20
- package/dist/cjs/globals/serve.cjs.map +3 -3
- package/dist/cjs/handle.cjs +34 -8
- package/dist/cjs/handle.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/setup.cjs +57 -31
- package/dist/cjs/setup.cjs.map +3 -3
- package/dist/mjs/globals/request.mjs +7 -4
- package/dist/mjs/globals/request.mjs.map +3 -3
- package/dist/mjs/globals/serve.mjs +4 -20
- package/dist/mjs/globals/serve.mjs.map +3 -3
- package/dist/mjs/handle.mjs +34 -8
- package/dist/mjs/handle.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/setup.mjs +57 -31
- package/dist/mjs/setup.mjs.map +3 -3
- package/dist/types/globals/request.d.ts +2 -0
- package/dist/types/globals/serve.d.ts +1 -1
- package/dist/types/types.d.ts +4 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -94,9 +94,10 @@ const response = await handle.dispatchRequest(
|
|
|
94
94
|
// Check for WebSocket upgrade
|
|
95
95
|
const upgrade = handle.getUpgradeRequest();
|
|
96
96
|
if (upgrade?.requested) {
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
// connectionId is generated by QuickJS in server.upgrade()
|
|
98
|
+
// User data stays within QuickJS (no marshalling of complex objects)
|
|
99
|
+
const { connectionId } = upgrade;
|
|
100
|
+
handle.dispatchWebSocketOpen(connectionId);
|
|
100
101
|
|
|
101
102
|
// Listen for outgoing messages
|
|
102
103
|
handle.onWebSocketCommand((command) => {
|
|
@@ -169,19 +169,22 @@ function createRequestClass(context, stateMap, streamHelpers) {
|
|
|
169
169
|
if (!createStream) {
|
|
170
170
|
return this.body;
|
|
171
171
|
}
|
|
172
|
+
if (this._uploadStreamGlobalKey) {
|
|
173
|
+
return streamHelpers?.getStreamByKey(this._uploadStreamGlobalKey) || null;
|
|
174
|
+
}
|
|
172
175
|
if (this._cachedBodyStream !== undefined) {
|
|
173
176
|
return this._cachedBodyStream;
|
|
174
177
|
}
|
|
175
178
|
if (this.nativeBodyStream) {
|
|
176
|
-
if (!streamHelpers?.createEmptyStream) {
|
|
179
|
+
if (!streamHelpers?.createEmptyStream || !streamHelpers?.getStreamByKey) {
|
|
177
180
|
return null;
|
|
178
181
|
}
|
|
179
182
|
const { instanceId: streamInstanceId, globalKey } = streamHelpers.createEmptyStream();
|
|
180
183
|
const cleanup = import_upload_stream_queue.startNativeStreamReader(this.nativeBodyStream, streamInstanceId, streamHelpers.pumpEventLoop);
|
|
181
184
|
this._uploadStreamCleanup = cleanup;
|
|
182
|
-
this.
|
|
185
|
+
this._uploadStreamGlobalKey = globalKey;
|
|
183
186
|
this.nativeBodyStream = undefined;
|
|
184
|
-
return
|
|
187
|
+
return streamHelpers.getStreamByKey(globalKey);
|
|
185
188
|
}
|
|
186
189
|
if (!this.body) {
|
|
187
190
|
this._cachedBodyStream = null;
|
|
@@ -433,4 +436,4 @@ function createRequestStateFromNative(request) {
|
|
|
433
436
|
}
|
|
434
437
|
})
|
|
435
438
|
|
|
436
|
-
//# debugId=
|
|
439
|
+
//# debugId=B2E1A14BC8FBB5F164756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/globals/request.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type { StateMap } from \"@ricsam/quickjs-core\";\nimport { defineClass, getInstanceStateById } from \"@ricsam/quickjs-core\";\nimport type { RequestState, HeadersState, AbortSignalState, FormDataState } from \"../types.cjs\";\nimport { createHeadersStateFromNative, createHeadersLike } from \"./headers.cjs\";\nimport { parseMultipartFormData, parseUrlEncodedFormData } from \"./form-data.cjs\";\nimport { startNativeStreamReader } from \"../upload-stream-queue.cjs\";\n\n/**\n * Type for the stream factory function\n */\ntype StreamFactory = (source: UnderlyingSource) => QuickJSHandle;\n\n/**\n * Stream helpers passed to the body getter for upload streaming\n */\ninterface StreamHelpers {\n createStream: StreamFactory;\n /** Creates an empty ReadableStream, stores on global, returns instanceId and globalKey */\n createEmptyStream: () => { instanceId: number; globalKey: string };\n pumpEventLoop: () => void;\n}\n\n/**\n * Helper to consume a native ReadableStream into a Uint8Array.\n * Used by text(), json(), arrayBuffer(), blob() methods.\n */\nasync function consumeNativeStream(\n stream: ReadableStream<Uint8Array>\n): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n // Concatenate chunks into single Uint8Array\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result;\n}\n\n/**\n * Create the Request class for QuickJS\n */\nexport function createRequestClass(\n context: QuickJSContext,\n stateMap: StateMap,\n streamHelpers?: StreamHelpers\n): QuickJSHandle {\n const createStream = streamHelpers?.createStream;\n return defineClass<RequestState>(context, stateMap, {\n name: \"Request\",\n construct: (args) => {\n const input = args[0];\n const init = args[1] as {\n method?: string;\n headers?: object;\n body?: unknown;\n cache?: string;\n credentials?: string;\n integrity?: string;\n keepalive?: boolean;\n mode?: string;\n redirect?: string;\n referrer?: string;\n referrerPolicy?: string;\n signal?: AbortSignalState;\n } | undefined;\n\n let url = \"\";\n let method = \"GET\";\n let headersState: HeadersState = { headers: new Map() };\n let body: Uint8Array | null = null;\n let signal: AbortSignalState | null = null;\n\n // Handle input\n if (typeof input === \"string\") {\n url = input;\n } else if (input && typeof input === \"object\") {\n // Could be URL or Request-like\n if (\"url\" in input) {\n url = String((input as { url: string }).url);\n }\n if (\"method\" in input) {\n method = String((input as { method: string }).method);\n }\n if (\"headersState\" in input) {\n const inputHeaders = (input as RequestState).headersState;\n headersState = {\n headers: new Map(inputHeaders.headers),\n };\n }\n if (\"body\" in input && (input as RequestState).body) {\n body = (input as RequestState).body;\n }\n if (\"signal\" in input) {\n signal = (input as RequestState).signal;\n }\n }\n\n // Apply init options\n if (init) {\n if (init.method) {\n method = init.method.toUpperCase();\n }\n if (init.headers) {\n if (init.headers && typeof init.headers === \"object\") {\n if (\"headers\" in init.headers && init.headers.headers instanceof Map) {\n headersState = {\n headers: new Map((init.headers as HeadersState).headers),\n };\n } else if (\n \"__isDefineClassInstance__\" in init.headers &&\n (init.headers as { __isDefineClassInstance__?: boolean }).__isDefineClassInstance__ === true &&\n \"__instanceId__\" in init.headers\n ) {\n // Unmarshalled Headers instance from defineClass\n const instanceId = (init.headers as { __instanceId__: number }).__instanceId__;\n const state = getInstanceStateById<HeadersState>(instanceId);\n if (state && state.headers instanceof Map) {\n headersState = {\n headers: new Map(state.headers),\n };\n }\n } else {\n headersState = { headers: new Map() };\n for (const [key, value] of Object.entries(init.headers)) {\n headersState.headers.set(key.toLowerCase(), [String(value)]);\n }\n }\n }\n }\n if (init.body !== undefined && init.body !== null) {\n if (typeof init.body === \"string\") {\n body = new TextEncoder().encode(init.body);\n } else if (init.body instanceof ArrayBuffer) {\n body = new Uint8Array(init.body);\n } else if (init.body instanceof Uint8Array) {\n body = init.body;\n }\n }\n if (init.signal) {\n signal = init.signal;\n }\n }\n\n return {\n method,\n url,\n headersState,\n body,\n bodyUsed: false,\n cache: init?.cache || \"default\",\n credentials: init?.credentials || \"same-origin\",\n destination: \"\",\n integrity: init?.integrity || \"\",\n keepalive: init?.keepalive || false,\n mode: init?.mode || \"cors\",\n redirect: init?.redirect || \"follow\",\n referrer: init?.referrer || \"about:client\",\n referrerPolicy: init?.referrerPolicy || \"\",\n signal,\n };\n },\n properties: {\n method: {\n get(this: RequestState) {\n return this.method;\n },\n },\n url: {\n get(this: RequestState) {\n return this.url;\n },\n },\n headers: {\n get(this: RequestState) {\n return createHeadersLike(this.headersState);\n },\n },\n body: {\n get(this: RequestState & { _cachedBodyStream?: object | string | null; _uploadStreamCleanup?: () => void }) {\n if (!createStream) {\n // Fallback: return raw body if no stream factory\n return this.body;\n }\n\n // Return cached stream if already created\n if (this._cachedBodyStream !== undefined) {\n return this._cachedBodyStream;\n }\n\n // Streaming native body - create a QuickJS ReadableStream that bridges to the native stream\n // Uses the \"Inverted Direct State Access\" pattern: host pushes to QuickJS stream's queue\n if (this.nativeBodyStream) {\n if (!streamHelpers?.createEmptyStream) {\n // No helpers available - fall back to null (legacy behavior)\n return null;\n }\n\n // Create a QuickJS ReadableStream with no source callbacks via evalCode\n // The stream is stored on a global key - we return a marker string that\n // the JavaScript wrapper will use to retrieve the actual stream.\n // This avoids returning a QuickJSHandle through __hostCall__ which causes lifetime issues.\n const { instanceId: streamInstanceId, globalKey } = streamHelpers.createEmptyStream();\n\n // Start background reader that pushes to the QuickJS stream\n // The onChunkPushed callback pumps the event loop to process pending reads\n const cleanup = startNativeStreamReader(\n this.nativeBodyStream,\n streamInstanceId,\n streamHelpers.pumpEventLoop\n );\n\n // Store cleanup function for potential cancellation\n this._uploadStreamCleanup = cleanup;\n // Cache the global key so subsequent calls return the same stream\n this._cachedBodyStream = `__UPLOAD_STREAM_KEY__:${globalKey}`;\n\n // Clear nativeBodyStream to prevent double-consumption\n // This ensures text()/json()/arrayBuffer() don't try to read from it\n this.nativeBodyStream = undefined;\n\n return this._cachedBodyStream;\n }\n\n // Fallback: buffered body (backwards compatibility)\n if (!this.body) {\n this._cachedBodyStream = null;\n return null;\n }\n const bodyData = this.body;\n let offset = 0;\n const chunkSize = 65536; // 64KB chunks\n const stream = createStream({\n pull(controller) {\n if (offset >= bodyData.length) {\n controller.close();\n return;\n }\n const chunk = bodyData.slice(offset, Math.min(offset + chunkSize, bodyData.length));\n offset += chunk.length;\n controller.enqueue(chunk);\n },\n });\n this._cachedBodyStream = stream;\n return stream;\n },\n },\n bodyUsed: {\n get(this: RequestState) {\n return this.bodyUsed;\n },\n },\n cache: {\n get(this: RequestState) {\n return this.cache;\n },\n },\n credentials: {\n get(this: RequestState) {\n return this.credentials;\n },\n },\n destination: {\n get(this: RequestState) {\n return this.destination;\n },\n },\n integrity: {\n get(this: RequestState) {\n return this.integrity;\n },\n },\n keepalive: {\n get(this: RequestState) {\n return this.keepalive;\n },\n },\n mode: {\n get(this: RequestState) {\n return this.mode;\n },\n },\n redirect: {\n get(this: RequestState) {\n return this.redirect;\n },\n },\n referrer: {\n get(this: RequestState) {\n return this.referrer;\n },\n },\n referrerPolicy: {\n get(this: RequestState) {\n return this.referrerPolicy;\n },\n },\n signal: {\n get(this: RequestState) {\n return this.signal;\n },\n },\n },\n methods: {\n async arrayBuffer(this: RequestState): Promise<ArrayBuffer> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n\n // Consume native stream if present\n if (this.nativeBodyStream) {\n const buffer = await consumeNativeStream(this.nativeBodyStream);\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) as ArrayBuffer;\n }\n\n // Fallback: buffered body\n if (!this.body) {\n return new ArrayBuffer(0);\n }\n return this.body.buffer.slice(\n this.body.byteOffset,\n this.body.byteOffset + this.body.byteLength\n ) as ArrayBuffer;\n },\n async blob(this: RequestState): Promise<object> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n const contentType = this.headersState.headers.get(\"content-type\")?.[0] || \"\";\n\n // Consume native stream if present\n if (this.nativeBodyStream) {\n const buffer = await consumeNativeStream(this.nativeBodyStream);\n return {\n parts: [buffer],\n type: contentType,\n size: buffer.length,\n };\n }\n\n // Fallback: buffered body\n return {\n parts: this.body ? [this.body] : [],\n type: contentType,\n size: this.body?.length || 0,\n };\n },\n clone(this: RequestState): RequestState {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n // Note: Cannot clone streaming body - would need to tee the stream\n if (this.nativeBodyStream) {\n throw new TypeError(\"Cannot clone Request with streaming body\");\n }\n return {\n ...this,\n headersState: {\n headers: new Map(this.headersState.headers),\n },\n body: this.body ? new Uint8Array(this.body) : null,\n bodyUsed: false,\n };\n },\n async json(this: RequestState): Promise<unknown> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n\n // Consume native stream if present\n if (this.nativeBodyStream) {\n const buffer = await consumeNativeStream(this.nativeBodyStream);\n const text = new TextDecoder().decode(buffer);\n return JSON.parse(text);\n }\n\n // Fallback: buffered body\n if (!this.body) {\n return JSON.parse(\"\");\n }\n const text = new TextDecoder().decode(this.body);\n return JSON.parse(text);\n },\n async text(this: RequestState): Promise<string> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n\n // Consume native stream if present\n if (this.nativeBodyStream) {\n const buffer = await consumeNativeStream(this.nativeBodyStream);\n return new TextDecoder().decode(buffer);\n }\n\n // Fallback: buffered body\n if (!this.body) {\n return \"\";\n }\n return new TextDecoder().decode(this.body);\n },\n /**\n * Private method that returns raw FormData entries.\n * Used by the formData() method added via evalCode (see addRequestFormDataMethod).\n *\n * Note: File data is converted to plain number arrays to avoid memory issues\n * when marshalling Uint8Array between host and QuickJS contexts.\n */\n async __getFormDataEntries__(this: RequestState): Promise<{\n entries: Array<{\n name: string;\n value: string | { __formDataFile__: true; data: number[]; filename: string; type: string };\n }>;\n }> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n\n // Get body data from native stream or buffered body\n let bodyData: Uint8Array | null = null;\n if (this.nativeBodyStream) {\n bodyData = await consumeNativeStream(this.nativeBodyStream);\n } else {\n bodyData = this.body;\n }\n\n if (!bodyData) {\n return { entries: [] };\n }\n\n const contentType = this.headersState.headers.get(\"content-type\")?.[0] || \"\";\n\n let formDataState: FormDataState;\n if (contentType.includes(\"multipart/form-data\")) {\n formDataState = parseMultipartFormData(bodyData, contentType);\n } else if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n formDataState = parseUrlEncodedFormData(bodyData);\n } else {\n throw new TypeError(\"Could not parse content as FormData\");\n }\n\n // Convert Uint8Array data to plain number arrays for safe marshalling\n // Include marker so FormData.get() can reconstruct File instances\n type SerializedFileValue = { __formDataFile__: true; data: number[]; filename: string; type: string };\n type SerializedEntry = { name: string; value: string | SerializedFileValue };\n return {\n entries: formDataState.entries.map((entry): SerializedEntry => {\n if (typeof entry.value === \"string\") {\n return { name: entry.name, value: entry.value };\n }\n // Convert Uint8Array to number array and include marker\n return {\n name: entry.name,\n value: {\n __formDataFile__: true,\n data: Array.from(entry.value.data),\n filename: entry.value.filename,\n type: entry.value.type,\n },\n };\n }),\n };\n },\n },\n });\n}\n\n/**\n * Add the formData() method to Request.prototype via evalCode.\n *\n * This must be called AFTER both Request and FormData classes are on global.\n * The method creates a proper FormData instance with all entries.\n *\n * @see PATTERNS.md section 2 (Class Methods That Return Instances)\n */\nexport function addRequestFormDataMethod(context: QuickJSContext): void {\n const result = context.evalCode(`\n Request.prototype.formData = async function() {\n // Get raw entries from private method\n // Note: File data comes as plain number arrays (converted in host side)\n const rawData = await this.__getFormDataEntries__();\n\n // Create a proper FormData instance\n const formData = new FormData();\n\n // Populate with entries\n // FormData.append handles both string values and file-like objects\n // with number arrays (converted to Uint8Array on the host side)\n for (const entry of rawData.entries) {\n formData.append(entry.name, entry.value);\n }\n\n return formData;\n };\n `);\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n}\n\n/**\n * Create a RequestState from a native Request object.\n *\n * This is now synchronous - we preserve the native ReadableStream instead of\n * buffering it. The stream is consumed lazily when QuickJS code accesses\n * request.body or calls request.text()/json()/arrayBuffer().\n *\n * This enables streaming large uploads (1GB+) without buffering.\n */\nexport function createRequestStateFromNative(\n request: Request\n): RequestState {\n return {\n method: request.method,\n url: request.url,\n headersState: createHeadersStateFromNative(request.headers),\n body: null, // Not buffered upfront - use nativeBodyStream instead\n bodyUsed: false,\n cache: request.cache,\n credentials: request.credentials,\n destination: request.destination,\n integrity: request.integrity,\n keepalive: request.keepalive,\n mode: request.mode,\n redirect: request.redirect,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n signal: null, // Signal handling is complex, simplified here\n nativeBodyStream: request.body ?? undefined,\n };\n}\n"
|
|
5
|
+
"import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type { StateMap } from \"@ricsam/quickjs-core\";\nimport { defineClass, getInstanceStateById } from \"@ricsam/quickjs-core\";\nimport type { RequestState, HeadersState, AbortSignalState, FormDataState } from \"../types.cjs\";\nimport { createHeadersStateFromNative, createHeadersLike } from \"./headers.cjs\";\nimport { parseMultipartFormData, parseUrlEncodedFormData } from \"./form-data.cjs\";\nimport { startNativeStreamReader } from \"../upload-stream-queue.cjs\";\n\n/**\n * Type for the stream factory function\n */\ntype StreamFactory = (source: UnderlyingSource) => QuickJSHandle;\n\n/**\n * Stream helpers passed to the body getter for upload streaming\n */\ninterface StreamHelpers {\n createStream: StreamFactory;\n /** Creates an empty ReadableStream, stores on global, returns instanceId and globalKey */\n createEmptyStream: () => { instanceId: number; globalKey: string };\n /** Gets a fresh handle to the stream stored at globalKey */\n getStreamByKey: (globalKey: string) => QuickJSHandle | null;\n pumpEventLoop: () => void;\n}\n\n/**\n * Helper to consume a native ReadableStream into a Uint8Array.\n * Used by text(), json(), arrayBuffer(), blob() methods.\n */\nasync function consumeNativeStream(\n stream: ReadableStream<Uint8Array>\n): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n // Concatenate chunks into single Uint8Array\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result;\n}\n\n/**\n * Create the Request class for QuickJS\n */\nexport function createRequestClass(\n context: QuickJSContext,\n stateMap: StateMap,\n streamHelpers?: StreamHelpers\n): QuickJSHandle {\n const createStream = streamHelpers?.createStream;\n return defineClass<RequestState>(context, stateMap, {\n name: \"Request\",\n construct: (args) => {\n const input = args[0];\n const init = args[1] as {\n method?: string;\n headers?: object;\n body?: unknown;\n cache?: string;\n credentials?: string;\n integrity?: string;\n keepalive?: boolean;\n mode?: string;\n redirect?: string;\n referrer?: string;\n referrerPolicy?: string;\n signal?: AbortSignalState;\n } | undefined;\n\n let url = \"\";\n let method = \"GET\";\n let headersState: HeadersState = { headers: new Map() };\n let body: Uint8Array | null = null;\n let signal: AbortSignalState | null = null;\n\n // Handle input\n if (typeof input === \"string\") {\n url = input;\n } else if (input && typeof input === \"object\") {\n // Could be URL or Request-like\n if (\"url\" in input) {\n url = String((input as { url: string }).url);\n }\n if (\"method\" in input) {\n method = String((input as { method: string }).method);\n }\n if (\"headersState\" in input) {\n const inputHeaders = (input as RequestState).headersState;\n headersState = {\n headers: new Map(inputHeaders.headers),\n };\n }\n if (\"body\" in input && (input as RequestState).body) {\n body = (input as RequestState).body;\n }\n if (\"signal\" in input) {\n signal = (input as RequestState).signal;\n }\n }\n\n // Apply init options\n if (init) {\n if (init.method) {\n method = init.method.toUpperCase();\n }\n if (init.headers) {\n if (init.headers && typeof init.headers === \"object\") {\n if (\"headers\" in init.headers && init.headers.headers instanceof Map) {\n headersState = {\n headers: new Map((init.headers as HeadersState).headers),\n };\n } else if (\n \"__isDefineClassInstance__\" in init.headers &&\n (init.headers as { __isDefineClassInstance__?: boolean }).__isDefineClassInstance__ === true &&\n \"__instanceId__\" in init.headers\n ) {\n // Unmarshalled Headers instance from defineClass\n const instanceId = (init.headers as { __instanceId__: number }).__instanceId__;\n const state = getInstanceStateById<HeadersState>(instanceId);\n if (state && state.headers instanceof Map) {\n headersState = {\n headers: new Map(state.headers),\n };\n }\n } else {\n headersState = { headers: new Map() };\n for (const [key, value] of Object.entries(init.headers)) {\n headersState.headers.set(key.toLowerCase(), [String(value)]);\n }\n }\n }\n }\n if (init.body !== undefined && init.body !== null) {\n if (typeof init.body === \"string\") {\n body = new TextEncoder().encode(init.body);\n } else if (init.body instanceof ArrayBuffer) {\n body = new Uint8Array(init.body);\n } else if (init.body instanceof Uint8Array) {\n body = init.body;\n }\n }\n if (init.signal) {\n signal = init.signal;\n }\n }\n\n return {\n method,\n url,\n headersState,\n body,\n bodyUsed: false,\n cache: init?.cache || \"default\",\n credentials: init?.credentials || \"same-origin\",\n destination: \"\",\n integrity: init?.integrity || \"\",\n keepalive: init?.keepalive || false,\n mode: init?.mode || \"cors\",\n redirect: init?.redirect || \"follow\",\n referrer: init?.referrer || \"about:client\",\n referrerPolicy: init?.referrerPolicy || \"\",\n signal,\n };\n },\n properties: {\n method: {\n get(this: RequestState) {\n return this.method;\n },\n },\n url: {\n get(this: RequestState) {\n return this.url;\n },\n },\n headers: {\n get(this: RequestState) {\n return createHeadersLike(this.headersState);\n },\n },\n body: {\n get(this: RequestState & { _uploadStreamGlobalKey?: string; _uploadStreamCleanup?: () => void; _cachedBodyStream?: object | null }) {\n if (!createStream) {\n // Fallback: return raw body if no stream factory\n return this.body;\n }\n\n // If we have an upload stream, get a fresh handle each time\n // (Can't cache handles because __hostCall__ disposes them after each call)\n if (this._uploadStreamGlobalKey) {\n return streamHelpers?.getStreamByKey(this._uploadStreamGlobalKey) || null;\n }\n\n // Return cached buffered body stream if already created\n if (this._cachedBodyStream !== undefined) {\n return this._cachedBodyStream;\n }\n\n // Streaming native body - create a QuickJS ReadableStream that bridges to the native stream\n // Uses the \"Inverted Direct State Access\" pattern: host pushes to QuickJS stream's queue\n if (this.nativeBodyStream) {\n if (!streamHelpers?.createEmptyStream || !streamHelpers?.getStreamByKey) {\n // No helpers available - fall back to null (legacy behavior)\n return null;\n }\n\n // Create a QuickJS ReadableStream with no source callbacks.\n // The stream is stored on a global because __hostCall__ disposes returned\n // handles after each call. We cache the globalKey and get a fresh handle\n // on each access via getStreamByKey().\n const { instanceId: streamInstanceId, globalKey } = streamHelpers.createEmptyStream();\n\n // Start background reader that pushes to the QuickJS stream\n // The onChunkPushed callback pumps the event loop to process pending reads\n const cleanup = startNativeStreamReader(\n this.nativeBodyStream,\n streamInstanceId,\n streamHelpers.pumpEventLoop\n );\n\n // Store cleanup function for potential cancellation\n this._uploadStreamCleanup = cleanup;\n // Cache the global key so subsequent calls can get the same stream\n this._uploadStreamGlobalKey = globalKey;\n\n // Clear nativeBodyStream to prevent double-consumption\n // This ensures text()/json()/arrayBuffer() don't try to read from it\n this.nativeBodyStream = undefined;\n\n // Get fresh handle to return\n return streamHelpers.getStreamByKey(globalKey);\n }\n\n // Fallback: buffered body (backwards compatibility)\n if (!this.body) {\n this._cachedBodyStream = null;\n return null;\n }\n const bodyData = this.body;\n let offset = 0;\n const chunkSize = 65536; // 64KB chunks\n const stream = createStream({\n pull(controller) {\n if (offset >= bodyData.length) {\n controller.close();\n return;\n }\n const chunk = bodyData.slice(offset, Math.min(offset + chunkSize, bodyData.length));\n offset += chunk.length;\n controller.enqueue(chunk);\n },\n });\n this._cachedBodyStream = stream;\n return stream;\n },\n },\n bodyUsed: {\n get(this: RequestState) {\n return this.bodyUsed;\n },\n },\n cache: {\n get(this: RequestState) {\n return this.cache;\n },\n },\n credentials: {\n get(this: RequestState) {\n return this.credentials;\n },\n },\n destination: {\n get(this: RequestState) {\n return this.destination;\n },\n },\n integrity: {\n get(this: RequestState) {\n return this.integrity;\n },\n },\n keepalive: {\n get(this: RequestState) {\n return this.keepalive;\n },\n },\n mode: {\n get(this: RequestState) {\n return this.mode;\n },\n },\n redirect: {\n get(this: RequestState) {\n return this.redirect;\n },\n },\n referrer: {\n get(this: RequestState) {\n return this.referrer;\n },\n },\n referrerPolicy: {\n get(this: RequestState) {\n return this.referrerPolicy;\n },\n },\n signal: {\n get(this: RequestState) {\n return this.signal;\n },\n },\n },\n methods: {\n async arrayBuffer(this: RequestState): Promise<ArrayBuffer> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n\n // Consume native stream if present\n if (this.nativeBodyStream) {\n const buffer = await consumeNativeStream(this.nativeBodyStream);\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) as ArrayBuffer;\n }\n\n // Fallback: buffered body\n if (!this.body) {\n return new ArrayBuffer(0);\n }\n return this.body.buffer.slice(\n this.body.byteOffset,\n this.body.byteOffset + this.body.byteLength\n ) as ArrayBuffer;\n },\n async blob(this: RequestState): Promise<object> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n const contentType = this.headersState.headers.get(\"content-type\")?.[0] || \"\";\n\n // Consume native stream if present\n if (this.nativeBodyStream) {\n const buffer = await consumeNativeStream(this.nativeBodyStream);\n return {\n parts: [buffer],\n type: contentType,\n size: buffer.length,\n };\n }\n\n // Fallback: buffered body\n return {\n parts: this.body ? [this.body] : [],\n type: contentType,\n size: this.body?.length || 0,\n };\n },\n clone(this: RequestState): RequestState {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n // Note: Cannot clone streaming body - would need to tee the stream\n if (this.nativeBodyStream) {\n throw new TypeError(\"Cannot clone Request with streaming body\");\n }\n return {\n ...this,\n headersState: {\n headers: new Map(this.headersState.headers),\n },\n body: this.body ? new Uint8Array(this.body) : null,\n bodyUsed: false,\n };\n },\n async json(this: RequestState): Promise<unknown> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n\n // Consume native stream if present\n if (this.nativeBodyStream) {\n const buffer = await consumeNativeStream(this.nativeBodyStream);\n const text = new TextDecoder().decode(buffer);\n return JSON.parse(text);\n }\n\n // Fallback: buffered body\n if (!this.body) {\n return JSON.parse(\"\");\n }\n const text = new TextDecoder().decode(this.body);\n return JSON.parse(text);\n },\n async text(this: RequestState): Promise<string> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n\n // Consume native stream if present\n if (this.nativeBodyStream) {\n const buffer = await consumeNativeStream(this.nativeBodyStream);\n return new TextDecoder().decode(buffer);\n }\n\n // Fallback: buffered body\n if (!this.body) {\n return \"\";\n }\n return new TextDecoder().decode(this.body);\n },\n /**\n * Private method that returns raw FormData entries.\n * Used by the formData() method added via evalCode (see addRequestFormDataMethod).\n *\n * Note: File data is converted to plain number arrays to avoid memory issues\n * when marshalling Uint8Array between host and QuickJS contexts.\n */\n async __getFormDataEntries__(this: RequestState): Promise<{\n entries: Array<{\n name: string;\n value: string | { __formDataFile__: true; data: number[]; filename: string; type: string };\n }>;\n }> {\n if (this.bodyUsed) {\n throw new TypeError(\"Body has already been consumed\");\n }\n this.bodyUsed = true;\n\n // Get body data from native stream or buffered body\n let bodyData: Uint8Array | null = null;\n if (this.nativeBodyStream) {\n bodyData = await consumeNativeStream(this.nativeBodyStream);\n } else {\n bodyData = this.body;\n }\n\n if (!bodyData) {\n return { entries: [] };\n }\n\n const contentType = this.headersState.headers.get(\"content-type\")?.[0] || \"\";\n\n let formDataState: FormDataState;\n if (contentType.includes(\"multipart/form-data\")) {\n formDataState = parseMultipartFormData(bodyData, contentType);\n } else if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n formDataState = parseUrlEncodedFormData(bodyData);\n } else {\n throw new TypeError(\"Could not parse content as FormData\");\n }\n\n // Convert Uint8Array data to plain number arrays for safe marshalling\n // Include marker so FormData.get() can reconstruct File instances\n type SerializedFileValue = { __formDataFile__: true; data: number[]; filename: string; type: string };\n type SerializedEntry = { name: string; value: string | SerializedFileValue };\n return {\n entries: formDataState.entries.map((entry): SerializedEntry => {\n if (typeof entry.value === \"string\") {\n return { name: entry.name, value: entry.value };\n }\n // Convert Uint8Array to number array and include marker\n return {\n name: entry.name,\n value: {\n __formDataFile__: true,\n data: Array.from(entry.value.data),\n filename: entry.value.filename,\n type: entry.value.type,\n },\n };\n }),\n };\n },\n },\n });\n}\n\n/**\n * Add the formData() method to Request.prototype via evalCode.\n *\n * This must be called AFTER both Request and FormData classes are on global.\n * The method creates a proper FormData instance with all entries.\n *\n * @see PATTERNS.md section 2 (Class Methods That Return Instances)\n */\nexport function addRequestFormDataMethod(context: QuickJSContext): void {\n const result = context.evalCode(`\n Request.prototype.formData = async function() {\n // Get raw entries from private method\n // Note: File data comes as plain number arrays (converted in host side)\n const rawData = await this.__getFormDataEntries__();\n\n // Create a proper FormData instance\n const formData = new FormData();\n\n // Populate with entries\n // FormData.append handles both string values and file-like objects\n // with number arrays (converted to Uint8Array on the host side)\n for (const entry of rawData.entries) {\n formData.append(entry.name, entry.value);\n }\n\n return formData;\n };\n `);\n\n if (result.error) {\n result.error.dispose();\n } else {\n result.value.dispose();\n }\n}\n\n/**\n * Create a RequestState from a native Request object.\n *\n * This is now synchronous - we preserve the native ReadableStream instead of\n * buffering it. The stream is consumed lazily when QuickJS code accesses\n * request.body or calls request.text()/json()/arrayBuffer().\n *\n * This enables streaming large uploads (1GB+) without buffering.\n */\nexport function createRequestStateFromNative(\n request: Request\n): RequestState {\n return {\n method: request.method,\n url: request.url,\n headersState: createHeadersStateFromNative(request.headers),\n body: null, // Not buffered upfront - use nativeBodyStream instead\n bodyUsed: false,\n cache: request.cache,\n credentials: request.credentials,\n destination: request.destination,\n integrity: request.integrity,\n keepalive: request.keepalive,\n mode: request.mode,\n redirect: request.redirect,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n signal: null, // Signal handling is complex, simplified here\n nativeBodyStream: request.body ?? undefined,\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEkD,IAAlD;AAEgE,IAAhE;AACgE,IAAhE;AACwC,IAAxC;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "B2E1A14BC8FBB5F164756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -41,20 +41,13 @@ function createServerWebSocketClass(context, stateMap, onWebSocketCommand) {
|
|
|
41
41
|
name: "ServerWebSocket",
|
|
42
42
|
construct: (args) => {
|
|
43
43
|
const connectionId = String(args[0]);
|
|
44
|
-
const data = args[1];
|
|
45
44
|
return {
|
|
46
|
-
data,
|
|
47
45
|
readyState: 1,
|
|
48
46
|
connectionId,
|
|
49
47
|
wsHandle: null
|
|
50
48
|
};
|
|
51
49
|
},
|
|
52
50
|
properties: {
|
|
53
|
-
data: {
|
|
54
|
-
get() {
|
|
55
|
-
return this.data;
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
51
|
readyState: {
|
|
59
52
|
get() {
|
|
60
53
|
return this.readyState;
|
|
@@ -97,22 +90,13 @@ function createServerWebSocketClass(context, stateMap, onWebSocketCommand) {
|
|
|
97
90
|
}
|
|
98
91
|
});
|
|
99
92
|
}
|
|
100
|
-
function createServerClass(context, stateMap,
|
|
93
|
+
function createServerClass(context, stateMap, _serveState) {
|
|
101
94
|
return import_quickjs_core.defineClass(context, stateMap, {
|
|
102
95
|
name: "Server",
|
|
103
96
|
construct: () => {
|
|
104
|
-
return {
|
|
97
|
+
return {};
|
|
105
98
|
},
|
|
106
|
-
methods: {
|
|
107
|
-
upgrade(_request, options) {
|
|
108
|
-
const data = options && typeof options === "object" && "data" in options ? options.data : undefined;
|
|
109
|
-
this.serveState.pendingUpgrade = {
|
|
110
|
-
requested: true,
|
|
111
|
-
data
|
|
112
|
-
};
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
99
|
+
methods: {}
|
|
116
100
|
});
|
|
117
101
|
}
|
|
118
102
|
function createServeFunction(context, stateMap, serveState) {
|
|
@@ -179,4 +163,4 @@ function createServeFunction(context, stateMap, serveState) {
|
|
|
179
163
|
}
|
|
180
164
|
})
|
|
181
165
|
|
|
182
|
-
//# debugId=
|
|
166
|
+
//# debugId=D110C1B8E74ED4FB64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/globals/serve.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type { StateMap } from \"@ricsam/quickjs-core\";\nimport { defineClass } from \"@ricsam/quickjs-core\";\nimport type { ServeState, ServerWebSocketState, WebSocketCommand } from \"../types.cjs\";\n\
|
|
5
|
+
"import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type { StateMap } from \"@ricsam/quickjs-core\";\nimport { defineClass } from \"@ricsam/quickjs-core\";\nimport type { ServeState, ServerWebSocketState, WebSocketCommand } from \"../types.cjs\";\n\n// Server state is empty - upgrade method is pure JavaScript and stores data in a global registry\ntype ServerState = Record<string, never>;\n\n/**\n * Create the ServerWebSocket class for QuickJS\n */\nexport function createServerWebSocketClass(\n context: QuickJSContext,\n stateMap: StateMap,\n onWebSocketCommand: (cmd: WebSocketCommand) => void\n): QuickJSHandle {\n return defineClass<ServerWebSocketState>(context, stateMap, {\n name: \"ServerWebSocket\",\n construct: (args) => {\n const connectionId = String(args[0]);\n // Note: 'data' is accessed via a pure-JS getter that reads from __upgradeRegistry__\n // This avoids unmarshalling complex objects (like Zod schemas) that would exceed depth limit\n return {\n readyState: 1, // OPEN\n connectionId,\n wsHandle: null,\n };\n },\n properties: {\n // Note: 'data' is NOT defined here - it's a pure-JS getter added in setup.ts\n // that reads from __upgradeRegistry__ to avoid marshalling complex objects\n readyState: {\n get(this: ServerWebSocketState) {\n return this.readyState;\n },\n },\n },\n methods: {\n send(this: ServerWebSocketState, message: unknown) {\n if (this.readyState !== 1) {\n throw new Error(\"WebSocket is not open\");\n }\n\n let data: string | ArrayBuffer;\n if (typeof message === \"string\") {\n data = message;\n } else if (message instanceof ArrayBuffer) {\n data = message;\n } else if (message instanceof Uint8Array) {\n data = message.buffer.slice(\n message.byteOffset,\n message.byteOffset + message.byteLength\n ) as ArrayBuffer;\n } else {\n data = String(message);\n }\n\n onWebSocketCommand({\n type: \"message\",\n connectionId: this.connectionId,\n data,\n });\n },\n close(this: ServerWebSocketState, code?: unknown, reason?: unknown) {\n if (this.readyState === 3) {\n return; // Already closed\n }\n\n this.readyState = 2; // CLOSING\n\n onWebSocketCommand({\n type: \"close\",\n connectionId: this.connectionId,\n code: typeof code === \"number\" ? code : undefined,\n reason: typeof reason === \"string\" ? reason : undefined,\n });\n },\n },\n });\n}\n\n/**\n * Create the Server class for QuickJS\n */\nexport function createServerClass(\n context: QuickJSContext,\n stateMap: StateMap,\n _serveState: ServeState\n): QuickJSHandle {\n // Note: The upgrade method is added as pure JavaScript in setup.ts\n // to avoid marshalling data across the QuickJS/host boundary.\n // This keeps complex objects (like Zod schemas) within QuickJS.\n return defineClass<ServerState>(context, stateMap, {\n name: \"Server\",\n construct: () => {\n return {};\n },\n methods: {},\n });\n}\n\n/**\n * Create the serve function for QuickJS\n * Uses context.newFunction instead of defineFunction to properly manage handle lifetimes\n */\nexport function createServeFunction(\n context: QuickJSContext,\n stateMap: StateMap,\n serveState: ServeState\n): QuickJSHandle {\n return context.newFunction(\"serve\", (optionsHandle) => {\n if (context.typeof(optionsHandle) !== \"object\") {\n throw context.newError(\"serve requires an options object\");\n }\n\n // Dispose previous handlers if any\n if (serveState.fetchHandler) {\n serveState.fetchHandler.dispose();\n serveState.fetchHandler = null;\n }\n\n // Get and store fetch handler - don't dispose, keep alive for later calls\n const fetchHandle = context.getProp(optionsHandle, \"fetch\");\n if (context.typeof(fetchHandle) === \"function\") {\n serveState.fetchHandler = fetchHandle;\n } else {\n fetchHandle.dispose();\n }\n\n // Get and store websocket handlers\n const websocketHandle = context.getProp(optionsHandle, \"websocket\");\n if (context.typeof(websocketHandle) === \"object\") {\n // Dispose previous handlers\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 const openHandle = context.getProp(websocketHandle, \"open\");\n if (context.typeof(openHandle) === \"function\") {\n serveState.websocketHandlers.open = openHandle;\n } else {\n openHandle.dispose();\n }\n\n const messageHandle = context.getProp(websocketHandle, \"message\");\n if (context.typeof(messageHandle) === \"function\") {\n serveState.websocketHandlers.message = messageHandle;\n } else {\n messageHandle.dispose();\n }\n\n const closeHandle = context.getProp(websocketHandle, \"close\");\n if (context.typeof(closeHandle) === \"function\") {\n serveState.websocketHandlers.close = closeHandle;\n } else {\n closeHandle.dispose();\n }\n\n const errorHandle = context.getProp(websocketHandle, \"error\");\n if (context.typeof(errorHandle) === \"function\") {\n serveState.websocketHandlers.error = errorHandle;\n } else {\n errorHandle.dispose();\n }\n }\n websocketHandle.dispose();\n\n return context.undefined;\n });\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE4B,IAA5B;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "D110C1B8E74ED4FB64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/handle.cjs
CHANGED
|
@@ -108,6 +108,17 @@ function createFetchHandle(context, stateMap, serveState) {
|
|
|
108
108
|
if (!serveState.fetchHandler) {
|
|
109
109
|
throw new Error("No serve() handler registered");
|
|
110
110
|
}
|
|
111
|
+
if (serveState.pendingUpgrade?.connectionId) {
|
|
112
|
+
const connId = serveState.pendingUpgrade.connectionId;
|
|
113
|
+
if (!serveState.activeConnections.has(connId)) {
|
|
114
|
+
const deleteResult = context.evalCode(`__upgradeRegistry__.delete("${connId}")`);
|
|
115
|
+
if (deleteResult.error) {
|
|
116
|
+
deleteResult.error.dispose();
|
|
117
|
+
} else {
|
|
118
|
+
deleteResult.value.dispose();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
111
122
|
serveState.pendingUpgrade = null;
|
|
112
123
|
const requestState = import_request.createRequestStateFromNative(request);
|
|
113
124
|
const requestHandle = createRequestInstance(context, stateMap, requestState);
|
|
@@ -176,26 +187,24 @@ function createFetchHandle(context, stateMap, serveState) {
|
|
|
176
187
|
getUpgradeRequest() {
|
|
177
188
|
return serveState.pendingUpgrade;
|
|
178
189
|
},
|
|
179
|
-
dispatchWebSocketOpen(connectionId
|
|
190
|
+
dispatchWebSocketOpen(connectionId) {
|
|
180
191
|
if (!serveState.websocketHandlers.open) {
|
|
181
192
|
return;
|
|
182
193
|
}
|
|
183
194
|
const connectionIdHandle = context.newString(connectionId);
|
|
184
|
-
const dataHandle = import_quickjs_core.marshal(context, data);
|
|
185
195
|
context.setProp(context.global, "__wsConnectionId__", connectionIdHandle);
|
|
186
|
-
context.setProp(context.global, "__wsData__", dataHandle);
|
|
187
196
|
connectionIdHandle.dispose();
|
|
188
|
-
|
|
189
|
-
const wsResult = context.evalCode(`new __ServerWebSocket__(__wsConnectionId__, __wsData__)`);
|
|
197
|
+
const wsResult = context.evalCode(`new __ServerWebSocket__(__wsConnectionId__)`);
|
|
190
198
|
context.setProp(context.global, "__wsConnectionId__", context.undefined);
|
|
191
|
-
context.setProp(context.global, "__wsData__", context.undefined);
|
|
192
199
|
if (wsResult.error) {
|
|
193
200
|
wsResult.error.dispose();
|
|
194
201
|
return;
|
|
195
202
|
}
|
|
196
203
|
const wsHandle = wsResult.value;
|
|
204
|
+
const connIdForGetter = context.newString(connectionId);
|
|
205
|
+
context.setProp(wsHandle, "__connectionId__", connIdForGetter);
|
|
206
|
+
connIdForGetter.dispose();
|
|
197
207
|
serveState.activeConnections.set(connectionId, {
|
|
198
|
-
data,
|
|
199
208
|
readyState: 1,
|
|
200
209
|
connectionId,
|
|
201
210
|
wsHandle
|
|
@@ -227,14 +236,24 @@ function createFetchHandle(context, stateMap, serveState) {
|
|
|
227
236
|
context.runtime.executePendingJobs();
|
|
228
237
|
},
|
|
229
238
|
dispatchWebSocketClose(connectionId, code, reason) {
|
|
239
|
+
const cleanupRegistry = () => {
|
|
240
|
+
const deleteResult = context.evalCode(`__upgradeRegistry__.delete("${connectionId}")`);
|
|
241
|
+
if (deleteResult.error) {
|
|
242
|
+
deleteResult.error.dispose();
|
|
243
|
+
} else {
|
|
244
|
+
deleteResult.value.dispose();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
230
247
|
const connection = serveState.activeConnections.get(connectionId);
|
|
231
248
|
if (!connection || !connection.wsHandle) {
|
|
232
249
|
serveState.activeConnections.delete(connectionId);
|
|
250
|
+
cleanupRegistry();
|
|
233
251
|
return;
|
|
234
252
|
}
|
|
235
253
|
if (!serveState.websocketHandlers.close) {
|
|
236
254
|
connection.wsHandle.dispose();
|
|
237
255
|
serveState.activeConnections.delete(connectionId);
|
|
256
|
+
cleanupRegistry();
|
|
238
257
|
return;
|
|
239
258
|
}
|
|
240
259
|
connection.readyState = 3;
|
|
@@ -250,6 +269,7 @@ function createFetchHandle(context, stateMap, serveState) {
|
|
|
250
269
|
}
|
|
251
270
|
connection.wsHandle.dispose();
|
|
252
271
|
serveState.activeConnections.delete(connectionId);
|
|
272
|
+
cleanupRegistry();
|
|
253
273
|
context.runtime.executePendingJobs();
|
|
254
274
|
},
|
|
255
275
|
dispatchWebSocketError(connectionId, error) {
|
|
@@ -281,6 +301,12 @@ function createFetchHandle(context, stateMap, serveState) {
|
|
|
281
301
|
return serveState.fetchHandler !== null;
|
|
282
302
|
},
|
|
283
303
|
dispose() {
|
|
304
|
+
const clearResult = context.evalCode(`__upgradeRegistry__.clear()`);
|
|
305
|
+
if (clearResult.error) {
|
|
306
|
+
clearResult.error.dispose();
|
|
307
|
+
} else {
|
|
308
|
+
clearResult.value.dispose();
|
|
309
|
+
}
|
|
284
310
|
if (serveState.websocketHandlers.open) {
|
|
285
311
|
serveState.websocketHandlers.open.dispose();
|
|
286
312
|
serveState.websocketHandlers.open = undefined;
|
|
@@ -312,4 +338,4 @@ function createFetchHandle(context, stateMap, serveState) {
|
|
|
312
338
|
}
|
|
313
339
|
})
|
|
314
340
|
|
|
315
|
-
//# debugId=
|
|
341
|
+
//# debugId=CDC4947C4614CBB964756E2164756E21
|
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 // 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.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 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;AAW6C,IAA7C;AACsC,IAAtC;AACqC,IAArC;AAkBA,SAAS,2BAA2B,CAClC,SACA,kBAC4B;AAAA,EAC5B,IAAI,OAAO;AAAA,EAEX,OAAO,IAAI,eAA2B;AAAA,SAC9B,KAAI,CAAC,YAAY;AAAA,MACrB,OAAO,CAAC,MAAM;AAAA,QAEZ,QAAQ,QAAQ,mBAAmB;AAAA,QAEnC,MAAM,QAAQ,yCAAkD,gBAAgB;AAAA,QAEhF,IAAI,CAAC,OAAO;AAAA,UACV,WAAW,MAAM;AAAA,UACjB,OAAO;AAAA,UACP;AAAA,QACF;AAAA,QAGA,IAAI,MAAM,SAAS;AAAA,UACjB,WAAW,MAAM,MAAM,UAAU;AAAA,UACjC,OAAO;AAAA,UACP;AAAA,QACF;AAAA,QAGA,IAAI,MAAM,SAAS,MAAM,MAAM,SAAS,GAAG;AAAA,UACzC,MAAM,QAAQ,MAAM,MAAM,MAAM;AAAA,UAGhC,IAAI;AAAA,UACJ,IAAI,iBAAiB,YAAY;AAAA,YAC/B,QAAQ;AAAA,UACV,EAAO,SAAI,OAAO,UAAU,UAAU;AAAA,YACpC,QAAQ,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,UACxC,EAAO,SAAI,SAAS,OAAO,UAAU,YAAY,OAAO,OAAO;AAAA,YAE7D,MAAM,MAAM;AAAA,YACZ,MAAM,OAAO,OAAO,KAAK,GAAG,EAAE,OAAO,OAAK,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,SAAS,CAAC,IAAI,SAAS,CAAC,CAAC;AAAA,YACvG,MAAM,SAAS,KAAK,IAAI,OAAK,IAAI,EAAE;AAAA,YACnC,QAAQ,IAAI,WAAW,MAAM;AAAA,UAC/B,EAAO;AAAA,YAEL;AAAA;AAAA,UAGF,WAAW,QAAQ,KAAK;AAAA,UACxB;AAAA,QACF;AAAA,QAGA,IAAI,MAAM,kBAAkB,MAAM,QAAQ;AAAA,UACxC,WAAW,MAAM;AAAA,UACjB,OAAO;AAAA,UACP;AAAA,QACF;AAAA,QAGA,MAAM,IAAI,QAAQ,OAAK,WAAW,GAAG,CAAC,CAAC;AAAA,MACzC;AAAA;AAAA,IAGF,MAAM,GAAG;AAAA,MACP,OAAO;AAAA;AAAA,EAEX,CAAC;AAAA;AAMH,SAAS,qBAAqB,CAC5B,SACA,UACA,cACe;AAAA,EAGf,MAAM,YAAY,QAAQ,UAAU,aAAa,GAAG;AAAA,EACpD,QAAQ,QAAQ,QAAQ,QAAQ,kBAAkB,SAAS;AAAA,EAC3D,UAAU,QAAQ;AAAA,EAElB,MAAM,gBAAgB,QAAQ,SAAS,6BAA6B;AAAA,EAGpE,QAAQ,QAAQ,QAAQ,QAAQ,kBAAkB,QAAQ,SAAS;AAAA,EAEnE,IAAI,cAAc,OAAO;AAAA,IACvB,MAAM,QAAQ,QAAQ,KAAK,cAAc,KAAK;AAAA,IAC9C,cAAc,MAAM,QAAQ;AAAA,IAC5B,MAAM,IAAI,MAAM,6BAA6B,KAAK,UAAU,KAAK,GAAG;AAAA,EACtE;AAAA,EAEA,MAAM,gBAAgB,cAAc;AAAA,EAGpC,qCAAiB,SAAS,eAAe,YAAY;AAAA,EAErD,OAAO;AAAA;AAMF,SAAS,iBAAiB,CAC/B,SACA,UACA,YACa;AAAA,EACb,MAAM,qBAAqB,IAAI;AAAA,EAE/B,OAAO;AAAA,IACL;AAAA,SAEM,gBAAe,CAAC,SAAqC;AAAA,MACzD,IAAI,CAAC,WAAW,cAAc;AAAA,QAC5B,MAAM,IAAI,MAAM,+BAA+B;AAAA,MACjD;AAAA,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "CDC4947C4614CBB964756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED