@ricsam/isolate-fetch 0.1.12 → 0.1.13
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/index.cjs +173 -99
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/stream-state.cjs +14 -1
- package/dist/cjs/stream-state.cjs.map +3 -3
- package/dist/mjs/index.mjs +173 -99
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/stream-state.mjs +14 -1
- package/dist/mjs/stream-state.mjs.map +3 -3
- package/dist/types/stream-state.d.ts +4 -0
- package/package.json +4 -3
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/stream-state.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import ivm from \"isolated-vm\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface StreamState {\n /** Buffered chunks waiting to be read */\n queue: Uint8Array[];\n\n /** Total bytes in queue (for backpressure) */\n queueSize: number;\n\n /** Stream has been closed (no more data) */\n closed: boolean;\n\n /** Stream encountered an error */\n errored: boolean;\n\n /** The error value if errored */\n errorValue: unknown;\n\n /** A pull is waiting for data */\n pullWaiting: boolean;\n\n /** Resolve function for waiting pull */\n pullResolve: ((chunk: Uint8Array | null) => void) | null;\n\n /** Reject function for waiting pull */\n pullReject: ((error: unknown) => void) | null;\n}\n\nexport interface StreamStateRegistry {\n /** Create a new stream and return its ID */\n create(): number;\n\n /** Get stream state by ID */\n get(streamId: number): StreamState | undefined;\n\n /** Push a chunk to the stream's queue */\n push(streamId: number, chunk: Uint8Array): boolean;\n\n /** Pull a chunk from the stream (returns Promise that resolves when data available) */\n pull(\n streamId: number\n ): Promise<{ value: Uint8Array; done: false } | { done: true }>;\n\n /** Close the stream (no more data) */\n close(streamId: number): void;\n\n /** Error the stream */\n error(streamId: number, errorValue: unknown): void;\n\n /** Check if stream queue is above high-water mark */\n isQueueFull(streamId: number): boolean;\n\n /** Delete stream state (cleanup) */\n delete(streamId: number): void;\n\n /** Clear all streams (context cleanup) */\n clear(): void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Maximum bytes to buffer before backpressure kicks in */\nexport const HIGH_WATER_MARK = 64 * 1024; // 64KB\n\n/** Maximum number of chunks in queue */\nexport const MAX_QUEUE_CHUNKS = 16;\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nexport function createStreamStateRegistry(): StreamStateRegistry {\n const streams = new Map<number, StreamState>();\n let nextStreamId = 1;\n\n return {\n create(): number {\n const streamId = nextStreamId++;\n streams.set(streamId, {\n queue: [],\n queueSize: 0,\n closed: false,\n errored: false,\n errorValue: undefined,\n pullWaiting: false,\n pullResolve: null,\n pullReject: null,\n });\n return streamId;\n },\n\n get(streamId: number): StreamState | undefined {\n return streams.get(streamId);\n },\n\n push(streamId: number, chunk: Uint8Array): boolean {\n const state = streams.get(streamId);\n if (!state) return false;\n if (state.closed || state.errored) return false;\n\n // If a pull is waiting, deliver directly\n if (state.pullWaiting && state.pullResolve) {\n state.pullWaiting = false;\n const resolve = state.pullResolve;\n state.pullResolve = null;\n state.pullReject = null;\n resolve(chunk);\n return true;\n }\n\n // Otherwise queue the chunk\n state.queue.push(chunk);\n state.queueSize += chunk.length;\n return true;\n },\n\n async pull(\n streamId: number\n ): Promise<{ value: Uint8Array; done: false } | { done: true }> {\n const state = streams.get(streamId);\n if (!state) {\n return { done: true };\n }\n\n // If queue has data, return it first (even if stream is errored)\n if (state.queue.length > 0) {\n const chunk = state.queue.shift()!;\n state.queueSize -= chunk.length;\n return { value: chunk, done: false };\n }\n\n // If errored (and queue is empty), throw\n if (state.errored) {\n throw state.errorValue;\n }\n\n // If closed and queue empty, we're done\n if (state.closed) {\n return { done: true };\n }\n\n // Wait for data\n return new Promise((resolve, reject) => {\n state.pullWaiting = true;\n state.pullResolve = (chunk) => {\n if (chunk === null) {\n resolve({ done: true });\n } else {\n resolve({ value: chunk, done: false });\n }\n };\n state.pullReject = reject;\n });\n },\n\n close(streamId: number): void {\n const state = streams.get(streamId);\n if (!state) return;\n\n state.closed = true;\n\n // If a pull is waiting, resolve with done\n if (state.pullWaiting && state.pullResolve) {\n state.pullWaiting = false;\n const resolve = state.pullResolve;\n state.pullResolve = null;\n state.pullReject = null;\n resolve(null);\n }\n },\n\n error(streamId: number, errorValue: unknown): void {\n const state = streams.get(streamId);\n if (!state) return;\n\n state.errored = true;\n state.errorValue = errorValue;\n\n // If a pull is waiting, reject it\n if (state.pullWaiting && state.pullReject) {\n state.pullWaiting = false;\n const reject = state.pullReject;\n state.pullResolve = null;\n state.pullReject = null;\n reject(errorValue);\n }\n },\n\n isQueueFull(streamId: number): boolean {\n const state = streams.get(streamId);\n if (!state) return true;\n return (\n state.queueSize >= HIGH_WATER_MARK ||\n state.queue.length >= MAX_QUEUE_CHUNKS\n );\n },\n\n delete(streamId: number): void {\n const state = streams.get(streamId);\n if (state && state.pullWaiting && state.pullReject) {\n state.pullReject(new Error(\"Stream deleted\"));\n }\n streams.delete(streamId);\n },\n\n clear(): void {\n for (const [streamId] of streams) {\n this.delete(streamId);\n }\n },\n };\n}\n\n// ============================================================================\n// Context-Scoped Registry\n// ============================================================================\n\nconst contextRegistries = new WeakMap<ivm.Context, StreamStateRegistry>();\n\nexport function getStreamRegistryForContext(\n context: ivm.Context\n): StreamStateRegistry {\n let registry = contextRegistries.get(context);\n if (!registry) {\n registry = createStreamStateRegistry();\n contextRegistries.set(context, registry);\n }\n return registry;\n}\n\nexport function clearStreamRegistryForContext(context: ivm.Context): void {\n const registry = contextRegistries.get(context);\n if (registry) {\n registry.clear();\n contextRegistries.delete(context);\n }\n}\n\n// ============================================================================\n// Native Stream Reader\n// ============================================================================\n\n/**\n * Start reading from a native ReadableStream and push to host queue.\n * Respects backpressure by pausing when queue is full.\n *\n * @param nativeStream The native ReadableStream to read from\n * @param streamId The stream ID in the registry\n * @param registry The stream state registry\n * @returns Async cleanup function to cancel the reader\n */\nexport function startNativeStreamReader(\n nativeStream: ReadableStream<Uint8Array>,\n streamId: number,\n registry: StreamStateRegistry\n): () => Promise<void> {\n let cancelled = false;\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n let readLoopPromise: Promise<void> | null = null;\n\n const CHUNK_SIZE = 64 * 1024; // 64KB max chunk size\n\n async function readLoop() {\n try {\n reader = nativeStream.getReader();\n\n while (!cancelled) {\n // Respect backpressure - wait if queue is full\n while (registry.isQueueFull(streamId) && !cancelled) {\n await new Promise((resolve) => setTimeout(resolve, 1));\n }\n if (cancelled) break;\n\n const { done, value } = await reader.read();\n\n if (done) {\n registry.close(streamId);\n break;\n }\n\n if (value) {\n // Split large chunks to maintain granularity\n if (value.length > CHUNK_SIZE) {\n for (let offset = 0; offset < value.length; offset += CHUNK_SIZE) {\n const chunk = value.slice(\n offset,\n Math.min(offset + CHUNK_SIZE, value.length)\n );\n registry.push(streamId, chunk);\n }\n } else {\n registry.push(streamId, value);\n }\n }\n }\n } catch (error) {\n registry.error(streamId, error);\n } finally {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // Ignore release errors\n }\n }\n }\n }\n\n // Start the read loop and save the promise\n readLoopPromise = readLoop();\n\n // Return async cleanup function\n return async () => {\n cancelled = true;\n if (reader) {\n try {\n await reader.cancel();\n } catch {\n // Ignore cancel errors\n }\n }\n // Wait for read loop to finish\n if (readLoopPromise) {\n try {\n await readLoopPromise;\n } catch {\n // Ignore read loop errors during cleanup\n }\n }\n };\n}\n"
|
|
5
|
+
"import ivm from \"isolated-vm\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface StreamState {\n /** Buffered chunks waiting to be read */\n queue: Uint8Array[];\n\n /** Total bytes in queue (for backpressure) */\n queueSize: number;\n\n /** Stream has been closed (no more data) */\n closed: boolean;\n\n /** Stream encountered an error */\n errored: boolean;\n\n /** The error value if errored */\n errorValue: unknown;\n\n /** A pull is waiting for data */\n pullWaiting: boolean;\n\n /** Resolve function for waiting pull */\n pullResolve: ((chunk: Uint8Array | null) => void) | null;\n\n /** Reject function for waiting pull */\n pullReject: ((error: unknown) => void) | null;\n}\n\nexport interface StreamStateRegistry {\n /** Create a new stream and return its ID */\n create(): number;\n\n /** Get stream state by ID */\n get(streamId: number): StreamState | undefined;\n\n /** Push a chunk to the stream's queue */\n push(streamId: number, chunk: Uint8Array): boolean;\n\n /** Pull a chunk from the stream (returns Promise that resolves when data available) */\n pull(\n streamId: number\n ): Promise<{ value: Uint8Array; done: false } | { done: true }>;\n\n /** Close the stream (no more data) */\n close(streamId: number): void;\n\n /** Error the stream */\n error(streamId: number, errorValue: unknown): void;\n\n /** Check if stream queue is above high-water mark */\n isQueueFull(streamId: number): boolean;\n\n /** Delete stream state (cleanup) */\n delete(streamId: number): void;\n\n /** Clear all streams (context cleanup) */\n clear(): void;\n\n /** Cancel a stream and call its cleanup function */\n cancel(streamId: number): void;\n\n /** Register a cleanup function for a stream */\n setCleanup(streamId: number, cleanup: () => Promise<void>): void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Maximum bytes to buffer before backpressure kicks in */\nexport const HIGH_WATER_MARK = 64 * 1024; // 64KB\n\n/** Maximum number of chunks in queue */\nexport const MAX_QUEUE_CHUNKS = 16;\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nexport function createStreamStateRegistry(): StreamStateRegistry {\n const streams = new Map<number, StreamState>();\n const cleanups = new Map<number, () => Promise<void>>();\n let nextStreamId = 1;\n\n return {\n create(): number {\n const streamId = nextStreamId++;\n streams.set(streamId, {\n queue: [],\n queueSize: 0,\n closed: false,\n errored: false,\n errorValue: undefined,\n pullWaiting: false,\n pullResolve: null,\n pullReject: null,\n });\n return streamId;\n },\n\n get(streamId: number): StreamState | undefined {\n return streams.get(streamId);\n },\n\n push(streamId: number, chunk: Uint8Array): boolean {\n const state = streams.get(streamId);\n if (!state) return false;\n if (state.closed || state.errored) return false;\n\n // If a pull is waiting, deliver directly\n if (state.pullWaiting && state.pullResolve) {\n state.pullWaiting = false;\n const resolve = state.pullResolve;\n state.pullResolve = null;\n state.pullReject = null;\n resolve(chunk);\n return true;\n }\n\n // Otherwise queue the chunk\n state.queue.push(chunk);\n state.queueSize += chunk.length;\n return true;\n },\n\n async pull(\n streamId: number\n ): Promise<{ value: Uint8Array; done: false } | { done: true }> {\n const state = streams.get(streamId);\n if (!state) {\n return { done: true };\n }\n\n // If queue has data, return it first (even if stream is errored)\n if (state.queue.length > 0) {\n const chunk = state.queue.shift()!;\n state.queueSize -= chunk.length;\n return { value: chunk, done: false };\n }\n\n // If errored (and queue is empty), throw\n if (state.errored) {\n throw state.errorValue;\n }\n\n // If closed and queue empty, we're done\n if (state.closed) {\n return { done: true };\n }\n\n // Wait for data\n return new Promise((resolve, reject) => {\n state.pullWaiting = true;\n state.pullResolve = (chunk) => {\n if (chunk === null) {\n resolve({ done: true });\n } else {\n resolve({ value: chunk, done: false });\n }\n };\n state.pullReject = reject;\n });\n },\n\n close(streamId: number): void {\n const state = streams.get(streamId);\n if (!state) return;\n\n state.closed = true;\n\n // If a pull is waiting, resolve with done\n if (state.pullWaiting && state.pullResolve) {\n state.pullWaiting = false;\n const resolve = state.pullResolve;\n state.pullResolve = null;\n state.pullReject = null;\n resolve(null);\n }\n },\n\n error(streamId: number, errorValue: unknown): void {\n const state = streams.get(streamId);\n if (!state) return;\n\n state.errored = true;\n state.errorValue = errorValue;\n\n // If a pull is waiting, reject it\n if (state.pullWaiting && state.pullReject) {\n state.pullWaiting = false;\n const reject = state.pullReject;\n state.pullResolve = null;\n state.pullReject = null;\n reject(errorValue);\n }\n },\n\n isQueueFull(streamId: number): boolean {\n const state = streams.get(streamId);\n if (!state) return true;\n return (\n state.queueSize >= HIGH_WATER_MARK ||\n state.queue.length >= MAX_QUEUE_CHUNKS\n );\n },\n\n delete(streamId: number): void {\n const state = streams.get(streamId);\n if (state && state.pullWaiting && state.pullReject) {\n state.pullReject(new Error(\"Stream deleted\"));\n }\n streams.delete(streamId);\n cleanups.delete(streamId);\n },\n\n clear(): void {\n for (const [streamId] of streams) {\n this.delete(streamId);\n }\n },\n\n cancel(streamId: number): void {\n this.close(streamId);\n const cleanup = cleanups.get(streamId);\n if (cleanup) {\n cleanup().catch(() => {});\n cleanups.delete(streamId);\n }\n },\n\n setCleanup(streamId: number, cleanup: () => Promise<void>): void {\n cleanups.set(streamId, cleanup);\n },\n };\n}\n\n// ============================================================================\n// Context-Scoped Registry\n// ============================================================================\n\nconst contextRegistries = new WeakMap<ivm.Context, StreamStateRegistry>();\n\nexport function getStreamRegistryForContext(\n context: ivm.Context\n): StreamStateRegistry {\n let registry = contextRegistries.get(context);\n if (!registry) {\n registry = createStreamStateRegistry();\n contextRegistries.set(context, registry);\n }\n return registry;\n}\n\nexport function clearStreamRegistryForContext(context: ivm.Context): void {\n const registry = contextRegistries.get(context);\n if (registry) {\n registry.clear();\n contextRegistries.delete(context);\n }\n}\n\n// ============================================================================\n// Native Stream Reader\n// ============================================================================\n\n/**\n * Start reading from a native ReadableStream and push to host queue.\n * Respects backpressure by pausing when queue is full.\n *\n * @param nativeStream The native ReadableStream to read from\n * @param streamId The stream ID in the registry\n * @param registry The stream state registry\n * @returns Async cleanup function to cancel the reader\n */\nexport function startNativeStreamReader(\n nativeStream: ReadableStream<Uint8Array>,\n streamId: number,\n registry: StreamStateRegistry\n): () => Promise<void> {\n let cancelled = false;\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n let readLoopPromise: Promise<void> | null = null;\n\n const CHUNK_SIZE = 64 * 1024; // 64KB max chunk size\n\n async function readLoop() {\n try {\n reader = nativeStream.getReader();\n\n while (!cancelled) {\n // Respect backpressure - wait if queue is full\n while (registry.isQueueFull(streamId) && !cancelled) {\n await new Promise((resolve) => setTimeout(resolve, 1));\n }\n if (cancelled) break;\n\n const { done, value } = await reader.read();\n\n if (done) {\n registry.close(streamId);\n break;\n }\n\n if (value) {\n // Split large chunks to maintain granularity\n if (value.length > CHUNK_SIZE) {\n for (let offset = 0; offset < value.length; offset += CHUNK_SIZE) {\n const chunk = value.slice(\n offset,\n Math.min(offset + CHUNK_SIZE, value.length)\n );\n registry.push(streamId, chunk);\n }\n } else {\n registry.push(streamId, value);\n }\n }\n }\n } catch (error) {\n registry.error(streamId, error);\n } finally {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // Ignore release errors\n }\n }\n }\n }\n\n // Start the read loop and save the promise\n readLoopPromise = readLoop();\n\n // Return async cleanup function\n return async () => {\n cancelled = true;\n if (reader) {\n try {\n await reader.cancel();\n } catch {\n // Ignore cancel errors\n }\n }\n // Wait for read loop to finish\n if (readLoopPromise) {\n try {\n await readLoopPromise;\n } catch {\n // Ignore read loop errors during cleanup\n }\n }\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "56A6539DC27E874264756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/index.mjs
CHANGED
|
@@ -366,6 +366,9 @@ function setupStreamCallbacks(context, streamRegistry) {
|
|
|
366
366
|
global.setSync("__Stream_isQueueFull", new ivm.Callback((streamId) => {
|
|
367
367
|
return streamRegistry.isQueueFull(streamId);
|
|
368
368
|
}));
|
|
369
|
+
global.setSync("__Stream_cancel", new ivm.Callback((streamId) => {
|
|
370
|
+
streamRegistry.cancel(streamId);
|
|
371
|
+
}));
|
|
369
372
|
const pullRef = new ivm.Reference(async (streamId) => {
|
|
370
373
|
const result = await streamRegistry.pull(streamId);
|
|
371
374
|
if (result.done) {
|
|
@@ -416,7 +419,7 @@ var hostBackedStreamCode = `
|
|
|
416
419
|
async pull(controller) {
|
|
417
420
|
if (closed) return;
|
|
418
421
|
|
|
419
|
-
const resultJson = __Stream_pull_ref.
|
|
422
|
+
const resultJson = await __Stream_pull_ref.apply(undefined, [streamId], { result: { promise: true, copy: true } });
|
|
420
423
|
const result = JSON.parse(resultJson);
|
|
421
424
|
|
|
422
425
|
if (result.done) {
|
|
@@ -428,7 +431,7 @@ var hostBackedStreamCode = `
|
|
|
428
431
|
},
|
|
429
432
|
cancel(reason) {
|
|
430
433
|
closed = true;
|
|
431
|
-
|
|
434
|
+
__Stream_cancel(streamId);
|
|
432
435
|
}
|
|
433
436
|
});
|
|
434
437
|
|
|
@@ -455,7 +458,7 @@ var hostBackedStreamCode = `
|
|
|
455
458
|
globalThis.HostBackedReadableStream = HostBackedReadableStream;
|
|
456
459
|
})();
|
|
457
460
|
`;
|
|
458
|
-
function setupResponse(context, stateMap) {
|
|
461
|
+
function setupResponse(context, stateMap, streamRegistry) {
|
|
459
462
|
const global = context.global;
|
|
460
463
|
global.setSync("__Response_construct", new ivm.Callback((bodyBytes, status, statusText, headers) => {
|
|
461
464
|
const instanceId = nextInstanceId++;
|
|
@@ -535,6 +538,10 @@ function setupResponse(context, stateMap) {
|
|
|
535
538
|
const state = stateMap.get(instanceId);
|
|
536
539
|
return state?.type ?? "default";
|
|
537
540
|
}));
|
|
541
|
+
global.setSync("__Response_get_nullBody", new ivm.Callback((instanceId) => {
|
|
542
|
+
const state = stateMap.get(instanceId);
|
|
543
|
+
return state?.nullBody ?? false;
|
|
544
|
+
}));
|
|
538
545
|
global.setSync("__Response_setType", new ivm.Callback((instanceId, type) => {
|
|
539
546
|
const state = stateMap.get(instanceId);
|
|
540
547
|
if (state) {
|
|
@@ -568,6 +575,38 @@ function setupResponse(context, stateMap) {
|
|
|
568
575
|
if (!state) {
|
|
569
576
|
throw new Error("[TypeError]Cannot clone invalid Response");
|
|
570
577
|
}
|
|
578
|
+
if (state.streamId !== null) {
|
|
579
|
+
const streamId1 = streamRegistry.create();
|
|
580
|
+
const streamId2 = streamRegistry.create();
|
|
581
|
+
const origStreamId = state.streamId;
|
|
582
|
+
(async () => {
|
|
583
|
+
try {
|
|
584
|
+
while (true) {
|
|
585
|
+
const result = await streamRegistry.pull(origStreamId);
|
|
586
|
+
if (result.done) {
|
|
587
|
+
streamRegistry.close(streamId1);
|
|
588
|
+
streamRegistry.close(streamId2);
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
streamRegistry.push(streamId1, new Uint8Array(result.value));
|
|
592
|
+
streamRegistry.push(streamId2, new Uint8Array(result.value));
|
|
593
|
+
}
|
|
594
|
+
} catch (err) {
|
|
595
|
+
streamRegistry.error(streamId1, err);
|
|
596
|
+
streamRegistry.error(streamId2, err);
|
|
597
|
+
}
|
|
598
|
+
})();
|
|
599
|
+
state.streamId = streamId1;
|
|
600
|
+
const newId2 = nextInstanceId++;
|
|
601
|
+
const newState2 = {
|
|
602
|
+
...state,
|
|
603
|
+
streamId: streamId2,
|
|
604
|
+
body: state.body ? new Uint8Array(state.body) : null,
|
|
605
|
+
bodyUsed: false
|
|
606
|
+
};
|
|
607
|
+
stateMap.set(newId2, newState2);
|
|
608
|
+
return newId2;
|
|
609
|
+
}
|
|
571
610
|
const newId = nextInstanceId++;
|
|
572
611
|
const newState = {
|
|
573
612
|
...state,
|
|
@@ -794,6 +833,11 @@ function setupResponse(context, stateMap) {
|
|
|
794
833
|
}
|
|
795
834
|
|
|
796
835
|
get body() {
|
|
836
|
+
// Null-body responses (204, 304, HEAD) must return null
|
|
837
|
+
if (__Response_get_nullBody(this.#instanceId)) {
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
|
|
797
841
|
// Return cached body if available (WHATWG spec requires same object on repeated access)
|
|
798
842
|
if (this.#cachedBody !== null) {
|
|
799
843
|
return this.#cachedBody;
|
|
@@ -825,6 +869,26 @@ function setupResponse(context, stateMap) {
|
|
|
825
869
|
} catch (err) {
|
|
826
870
|
throw __decodeError(err);
|
|
827
871
|
}
|
|
872
|
+
if (__Response_get_nullBody(this.#instanceId)) {
|
|
873
|
+
return "";
|
|
874
|
+
}
|
|
875
|
+
if (this.#streamId !== null) {
|
|
876
|
+
const reader = this.body.getReader();
|
|
877
|
+
const chunks = [];
|
|
878
|
+
while (true) {
|
|
879
|
+
const { done, value } = await reader.read();
|
|
880
|
+
if (done) break;
|
|
881
|
+
if (value) chunks.push(value);
|
|
882
|
+
}
|
|
883
|
+
const totalLength = chunks.reduce((acc, c) => acc + c.length, 0);
|
|
884
|
+
const result = new Uint8Array(totalLength);
|
|
885
|
+
let offset = 0;
|
|
886
|
+
for (const chunk of chunks) {
|
|
887
|
+
result.set(chunk, offset);
|
|
888
|
+
offset += chunk.length;
|
|
889
|
+
}
|
|
890
|
+
return new TextDecoder().decode(result);
|
|
891
|
+
}
|
|
828
892
|
return __Response_text(this.#instanceId);
|
|
829
893
|
}
|
|
830
894
|
|
|
@@ -1075,30 +1139,10 @@ function setupRequest(context, stateMap) {
|
|
|
1075
1139
|
return Array.from(new TextEncoder().encode(body.toString()));
|
|
1076
1140
|
}
|
|
1077
1141
|
if (body instanceof FormData) {
|
|
1078
|
-
//
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
hasFiles = true;
|
|
1083
|
-
break;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
if (hasFiles) {
|
|
1088
|
-
// Serialize as multipart/form-data
|
|
1089
|
-
const { body: bytes, contentType } = __serializeFormData(body);
|
|
1090
|
-
globalThis.__pendingFormDataContentType = contentType;
|
|
1091
|
-
return Array.from(bytes);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// URL-encoded for string-only FormData
|
|
1095
|
-
const parts = [];
|
|
1096
|
-
body.forEach((value, key) => {
|
|
1097
|
-
if (typeof value === 'string') {
|
|
1098
|
-
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
|
|
1099
|
-
}
|
|
1100
|
-
});
|
|
1101
|
-
return Array.from(new TextEncoder().encode(parts.join('&')));
|
|
1142
|
+
// Always serialize as multipart/form-data per spec
|
|
1143
|
+
const { body: bytes, contentType } = __serializeFormData(body);
|
|
1144
|
+
globalThis.__pendingFormDataContentType = contentType;
|
|
1145
|
+
return Array.from(bytes);
|
|
1102
1146
|
}
|
|
1103
1147
|
// Try to convert to string
|
|
1104
1148
|
return Array.from(new TextEncoder().encode(String(body)));
|
|
@@ -1196,7 +1240,7 @@ function setupRequest(context, stateMap) {
|
|
|
1196
1240
|
if (globalThis.__pendingFormDataContentType) {
|
|
1197
1241
|
headers.set('content-type', globalThis.__pendingFormDataContentType);
|
|
1198
1242
|
delete globalThis.__pendingFormDataContentType;
|
|
1199
|
-
} else if (body instanceof
|
|
1243
|
+
} else if (body instanceof URLSearchParams && !headers.has('content-type')) {
|
|
1200
1244
|
headers.set('content-type', 'application/x-www-form-urlencoded');
|
|
1201
1245
|
}
|
|
1202
1246
|
|
|
@@ -1386,32 +1430,56 @@ function setupRequest(context, stateMap) {
|
|
|
1386
1430
|
var FETCH_STREAM_THRESHOLD = 64 * 1024;
|
|
1387
1431
|
function setupFetchFunction(context, stateMap, streamRegistry, options) {
|
|
1388
1432
|
const global = context.global;
|
|
1389
|
-
const
|
|
1433
|
+
const fetchAbortControllers = new Map;
|
|
1434
|
+
global.setSync("__fetch_abort", new ivm.Callback((fetchId) => {
|
|
1435
|
+
const controller = fetchAbortControllers.get(fetchId);
|
|
1436
|
+
if (controller) {
|
|
1437
|
+
setImmediate(() => controller.abort());
|
|
1438
|
+
}
|
|
1439
|
+
}));
|
|
1440
|
+
const fetchRef = new ivm.Reference(async (url, method, headersJson, bodyJson, signalAborted, fetchId) => {
|
|
1390
1441
|
if (signalAborted) {
|
|
1391
1442
|
throw new Error("[AbortError]The operation was aborted.");
|
|
1392
1443
|
}
|
|
1444
|
+
const hostController = new AbortController;
|
|
1445
|
+
fetchAbortControllers.set(fetchId, hostController);
|
|
1393
1446
|
const headers = JSON.parse(headersJson);
|
|
1394
1447
|
const bodyBytes = bodyJson ? JSON.parse(bodyJson) : null;
|
|
1395
1448
|
const body = bodyBytes ? new Uint8Array(bodyBytes) : null;
|
|
1396
1449
|
const nativeRequest = new Request(url, {
|
|
1397
1450
|
method,
|
|
1398
1451
|
headers,
|
|
1399
|
-
body
|
|
1452
|
+
body,
|
|
1453
|
+
signal: hostController.signal
|
|
1400
1454
|
});
|
|
1401
1455
|
const onFetch = options?.onFetch ?? fetch;
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1456
|
+
try {
|
|
1457
|
+
let cleanupAbort;
|
|
1458
|
+
const abortPromise = new Promise((_, reject) => {
|
|
1459
|
+
if (hostController.signal.aborted) {
|
|
1460
|
+
reject(Object.assign(new Error("The operation was aborted."), { name: "AbortError" }));
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
const onAbort = () => {
|
|
1464
|
+
reject(Object.assign(new Error("The operation was aborted."), { name: "AbortError" }));
|
|
1465
|
+
};
|
|
1466
|
+
hostController.signal.addEventListener("abort", onAbort, { once: true });
|
|
1467
|
+
cleanupAbort = () => hostController.signal.removeEventListener("abort", onAbort);
|
|
1468
|
+
});
|
|
1469
|
+
abortPromise.catch(() => {});
|
|
1470
|
+
const nativeResponse = await Promise.race([onFetch(nativeRequest), abortPromise]);
|
|
1471
|
+
cleanupAbort?.();
|
|
1472
|
+
const status = nativeResponse.status;
|
|
1473
|
+
const isNullBody = status === 204 || status === 304 || method.toUpperCase() === "HEAD";
|
|
1474
|
+
const isCallbackStream = nativeResponse.__isCallbackStream;
|
|
1475
|
+
const isNetworkResponse = nativeResponse.url && (nativeResponse.url.startsWith("http://") || nativeResponse.url.startsWith("https://"));
|
|
1476
|
+
const shouldStream = !isNullBody && nativeResponse.body && (isCallbackStream || isNetworkResponse);
|
|
1477
|
+
if (shouldStream && nativeResponse.body) {
|
|
1478
|
+
const streamId = streamRegistry.create();
|
|
1479
|
+
const streamCleanupFn = startNativeStreamReader(nativeResponse.body, streamId, streamRegistry);
|
|
1480
|
+
streamRegistry.setCleanup(streamId, streamCleanupFn);
|
|
1481
|
+
const instanceId2 = nextInstanceId++;
|
|
1482
|
+
const state2 = {
|
|
1415
1483
|
status: nativeResponse.status,
|
|
1416
1484
|
statusText: nativeResponse.statusText,
|
|
1417
1485
|
headers: Array.from(nativeResponse.headers.entries()),
|
|
@@ -1420,65 +1488,37 @@ function setupFetchFunction(context, stateMap, streamRegistry, options) {
|
|
|
1420
1488
|
type: "default",
|
|
1421
1489
|
url: nativeResponse.url,
|
|
1422
1490
|
redirected: nativeResponse.redirected,
|
|
1423
|
-
streamId
|
|
1491
|
+
streamId,
|
|
1492
|
+
nullBody: isNullBody
|
|
1424
1493
|
};
|
|
1425
|
-
stateMap.set(
|
|
1426
|
-
return
|
|
1494
|
+
stateMap.set(instanceId2, state2);
|
|
1495
|
+
return instanceId2;
|
|
1427
1496
|
}
|
|
1428
|
-
const
|
|
1429
|
-
const
|
|
1430
|
-
const
|
|
1497
|
+
const responseBody = await nativeResponse.arrayBuffer();
|
|
1498
|
+
const responseBodyArray = Array.from(new Uint8Array(responseBody));
|
|
1499
|
+
const instanceId = nextInstanceId++;
|
|
1500
|
+
const state = {
|
|
1431
1501
|
status: nativeResponse.status,
|
|
1432
1502
|
statusText: nativeResponse.statusText,
|
|
1433
1503
|
headers: Array.from(nativeResponse.headers.entries()),
|
|
1434
|
-
body: new Uint8Array(
|
|
1504
|
+
body: new Uint8Array(responseBodyArray),
|
|
1435
1505
|
bodyUsed: false,
|
|
1436
1506
|
type: "default",
|
|
1437
1507
|
url: nativeResponse.url,
|
|
1438
1508
|
redirected: nativeResponse.redirected,
|
|
1439
|
-
streamId
|
|
1509
|
+
streamId: null,
|
|
1510
|
+
nullBody: isNullBody
|
|
1440
1511
|
};
|
|
1441
|
-
stateMap.set(
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
}
|
|
1451
|
-
if (value) {
|
|
1452
|
-
while (streamRegistry.isQueueFull(streamId)) {
|
|
1453
|
-
await new Promise((r) => setTimeout(r, 1));
|
|
1454
|
-
}
|
|
1455
|
-
streamRegistry.push(streamId, value);
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
} catch (err) {
|
|
1459
|
-
streamRegistry.error(streamId, err);
|
|
1460
|
-
} finally {
|
|
1461
|
-
reader.releaseLock();
|
|
1462
|
-
}
|
|
1463
|
-
})();
|
|
1464
|
-
return instanceId2;
|
|
1512
|
+
stateMap.set(instanceId, state);
|
|
1513
|
+
return instanceId;
|
|
1514
|
+
} catch (err) {
|
|
1515
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1516
|
+
throw new Error("[AbortError]The operation was aborted.");
|
|
1517
|
+
}
|
|
1518
|
+
throw err;
|
|
1519
|
+
} finally {
|
|
1520
|
+
fetchAbortControllers.delete(fetchId);
|
|
1465
1521
|
}
|
|
1466
|
-
const responseBody = await nativeResponse.arrayBuffer();
|
|
1467
|
-
const responseBodyArray = Array.from(new Uint8Array(responseBody));
|
|
1468
|
-
const instanceId = nextInstanceId++;
|
|
1469
|
-
const state = {
|
|
1470
|
-
status: nativeResponse.status,
|
|
1471
|
-
statusText: nativeResponse.statusText,
|
|
1472
|
-
headers: Array.from(nativeResponse.headers.entries()),
|
|
1473
|
-
body: new Uint8Array(responseBodyArray),
|
|
1474
|
-
bodyUsed: false,
|
|
1475
|
-
type: "default",
|
|
1476
|
-
url: nativeResponse.url,
|
|
1477
|
-
redirected: nativeResponse.redirected,
|
|
1478
|
-
streamId: null
|
|
1479
|
-
};
|
|
1480
|
-
stateMap.set(instanceId, state);
|
|
1481
|
-
return instanceId;
|
|
1482
1522
|
});
|
|
1483
1523
|
global.setSync("__fetch_ref", fetchRef);
|
|
1484
1524
|
const fetchCode = `
|
|
@@ -1496,7 +1536,28 @@ function setupFetchFunction(context, stateMap, streamRegistry, options) {
|
|
|
1496
1536
|
return err;
|
|
1497
1537
|
}
|
|
1498
1538
|
|
|
1499
|
-
|
|
1539
|
+
let __nextFetchId = 1;
|
|
1540
|
+
|
|
1541
|
+
globalThis.fetch = async function(input, init = {}) {
|
|
1542
|
+
// Handle Blob and ReadableStream bodies before creating Request
|
|
1543
|
+
if (init.body instanceof Blob && !(init.body instanceof File)) {
|
|
1544
|
+
const buf = await init.body.arrayBuffer();
|
|
1545
|
+
init = Object.assign({}, init, { body: new Uint8Array(buf) });
|
|
1546
|
+
} else if (init.body instanceof ReadableStream) {
|
|
1547
|
+
const reader = init.body.getReader();
|
|
1548
|
+
const chunks = [];
|
|
1549
|
+
while (true) {
|
|
1550
|
+
const { done, value } = await reader.read();
|
|
1551
|
+
if (done) break;
|
|
1552
|
+
chunks.push(value);
|
|
1553
|
+
}
|
|
1554
|
+
const total = chunks.reduce((s, c) => s + c.length, 0);
|
|
1555
|
+
const buf = new Uint8Array(total);
|
|
1556
|
+
let off = 0;
|
|
1557
|
+
for (const c of chunks) { buf.set(c, off); off += c.length; }
|
|
1558
|
+
init = Object.assign({}, init, { body: buf });
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1500
1561
|
// Create Request from input
|
|
1501
1562
|
const request = input instanceof Request ? input : new Request(input, init);
|
|
1502
1563
|
|
|
@@ -1504,20 +1565,34 @@ function setupFetchFunction(context, stateMap, streamRegistry, options) {
|
|
|
1504
1565
|
const signal = init.signal ?? request.signal;
|
|
1505
1566
|
const signalAborted = signal?.aborted ?? false;
|
|
1506
1567
|
|
|
1568
|
+
// Assign a fetch ID for abort tracking
|
|
1569
|
+
const fetchId = __nextFetchId++;
|
|
1570
|
+
|
|
1571
|
+
// Register abort listener if signal exists
|
|
1572
|
+
if (signal && !signalAborted) {
|
|
1573
|
+
signal.addEventListener('abort', () => { __fetch_abort(fetchId); });
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1507
1576
|
// Serialize headers and body to JSON for transfer
|
|
1508
1577
|
const headersJson = JSON.stringify(Array.from(request.headers.entries()));
|
|
1509
1578
|
const bodyBytes = request._getBodyBytes();
|
|
1510
1579
|
const bodyJson = bodyBytes ? JSON.stringify(bodyBytes) : null;
|
|
1511
1580
|
|
|
1581
|
+
// Short-circuit: if signal is already aborted, throw without calling host
|
|
1582
|
+
if (signalAborted) {
|
|
1583
|
+
throw new DOMException('The operation was aborted.', 'AbortError');
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1512
1586
|
// Call host - returns just the response instance ID
|
|
1513
1587
|
try {
|
|
1514
|
-
const instanceId = __fetch_ref.
|
|
1588
|
+
const instanceId = await __fetch_ref.apply(undefined, [
|
|
1515
1589
|
request.url,
|
|
1516
1590
|
request.method,
|
|
1517
1591
|
headersJson,
|
|
1518
1592
|
bodyJson,
|
|
1519
|
-
signalAborted
|
|
1520
|
-
|
|
1593
|
+
signalAborted,
|
|
1594
|
+
fetchId
|
|
1595
|
+
], { result: { promise: true, copy: true } });
|
|
1521
1596
|
|
|
1522
1597
|
// Construct Response from the instance ID
|
|
1523
1598
|
return Response._fromInstanceId(instanceId);
|
|
@@ -1634,7 +1709,7 @@ async function setupFetch(context, options) {
|
|
|
1634
1709
|
context.evalSync(multipartCode);
|
|
1635
1710
|
setupStreamCallbacks(context, streamRegistry);
|
|
1636
1711
|
context.evalSync(hostBackedStreamCode);
|
|
1637
|
-
setupResponse(context, stateMap);
|
|
1712
|
+
setupResponse(context, stateMap, streamRegistry);
|
|
1638
1713
|
setupRequest(context, stateMap);
|
|
1639
1714
|
setupFetchFunction(context, stateMap, streamRegistry, options);
|
|
1640
1715
|
const serveState = {
|
|
@@ -1757,8 +1832,7 @@ async function setupFetch(context, options) {
|
|
|
1757
1832
|
},
|
|
1758
1833
|
cancel() {
|
|
1759
1834
|
streamDone = true;
|
|
1760
|
-
streamRegistry.
|
|
1761
|
-
streamRegistry.delete(responseStreamId);
|
|
1835
|
+
streamRegistry.cancel(responseStreamId);
|
|
1762
1836
|
}
|
|
1763
1837
|
});
|
|
1764
1838
|
const responseHeaders2 = new Headers(responseState.headers);
|
|
@@ -1911,4 +1985,4 @@ export {
|
|
|
1911
1985
|
clearAllInstanceState
|
|
1912
1986
|
};
|
|
1913
1987
|
|
|
1914
|
-
//# debugId=
|
|
1988
|
+
//# debugId=D6C7C0BA2C0C13D164756E2164756E21
|