@ricsam/isolate-fetch 0.1.8 → 0.1.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.
@@ -1,4 +1,3 @@
1
- // @bun
2
1
  // packages/fetch/src/index.ts
3
2
  import ivm from "isolated-vm";
4
3
  import { setupCore, clearAllInstanceState } from "@ricsam/isolate-core";
@@ -7,6 +6,7 @@ import {
7
6
  startNativeStreamReader
8
7
  } from "./stream-state.mjs";
9
8
  var instanceStateMap = new WeakMap;
9
+ var passthruBodies = new WeakMap;
10
10
  var nextInstanceId = 1;
11
11
  function getInstanceStateMapForContext(context) {
12
12
  let map = instanceStateMap.get(context);
@@ -16,6 +16,14 @@ function getInstanceStateMapForContext(context) {
16
16
  }
17
17
  return map;
18
18
  }
19
+ function getPassthruBodiesForContext(context) {
20
+ let map = passthruBodies.get(context);
21
+ if (!map) {
22
+ map = new Map;
23
+ passthruBodies.set(context, map);
24
+ }
25
+ return map;
26
+ }
19
27
  var headersCode = `
20
28
  (function() {
21
29
  class Headers {
@@ -604,8 +612,12 @@ function setupResponse(context, stateMap) {
604
612
  // Mark as needing async Blob handling - will be read in constructor
605
613
  return { __isBlob: true, blob: body };
606
614
  }
607
- // Handle ReadableStream (both native and host-backed)
608
- if (body instanceof ReadableStream || body instanceof HostBackedReadableStream) {
615
+ // Handle HostBackedReadableStream specially - preserve streamId
616
+ if (body instanceof HostBackedReadableStream) {
617
+ return { __isHostStream: true, stream: body, streamId: body._getStreamId() };
618
+ }
619
+ // Handle native ReadableStream
620
+ if (body instanceof ReadableStream) {
609
621
  return { __isStream: true, stream: body };
610
622
  }
611
623
  // Try to convert to string
@@ -660,7 +672,27 @@ function setupResponse(context, stateMap) {
660
672
  return;
661
673
  }
662
674
 
663
- // Handle streaming body
675
+ // Handle HostBackedReadableStream - reuse existing streamId for pass-through
676
+ if (preparedBody && preparedBody.__isHostStream) {
677
+ // Reuse the existing streamId to preserve the pass-through body mapping
678
+ this.#streamId = preparedBody.streamId;
679
+ const status = init.status ?? 200;
680
+ const statusText = init.statusText ?? '';
681
+ const headers = new Headers(init.headers);
682
+ const headersArray = Array.from(headers.entries());
683
+
684
+ this.#instanceId = __Response_constructStreaming(
685
+ this.#streamId,
686
+ status,
687
+ statusText,
688
+ headersArray
689
+ );
690
+ this.#headers = headers;
691
+ // Don't pump - the body is already backed by this streamId
692
+ return;
693
+ }
694
+
695
+ // Handle native ReadableStream body
664
696
  if (preparedBody && preparedBody.__isStream) {
665
697
  this.#streamId = __Stream_create();
666
698
  const status = init.status ?? 200;
@@ -1341,7 +1373,8 @@ function setupRequest(context, stateMap) {
1341
1373
  `;
1342
1374
  context.evalSync(requestCode);
1343
1375
  }
1344
- function setupFetchFunction(context, stateMap, options) {
1376
+ var FETCH_STREAM_THRESHOLD = 64 * 1024;
1377
+ function setupFetchFunction(context, stateMap, streamRegistry, options) {
1345
1378
  const global = context.global;
1346
1379
  const fetchRef = new ivm.Reference(async (url, method, headersJson, bodyJson, signalAborted) => {
1347
1380
  if (signalAborted) {
@@ -1357,6 +1390,69 @@ function setupFetchFunction(context, stateMap, options) {
1357
1390
  });
1358
1391
  const onFetch = options?.onFetch ?? fetch;
1359
1392
  const nativeResponse = await onFetch(nativeRequest);
1393
+ const contentLength = nativeResponse.headers.get("content-length");
1394
+ const knownSize = contentLength ? parseInt(contentLength, 10) : null;
1395
+ const isCallbackStream = nativeResponse.__isCallbackStream;
1396
+ const isNetworkResponse = nativeResponse.url && (nativeResponse.url.startsWith("http://") || nativeResponse.url.startsWith("https://"));
1397
+ const shouldStream = nativeResponse.body && (isCallbackStream || isNetworkResponse && (knownSize === null || knownSize > FETCH_STREAM_THRESHOLD));
1398
+ if (shouldStream && nativeResponse.body) {
1399
+ if (isCallbackStream) {
1400
+ const streamId2 = streamRegistry.create();
1401
+ const passthruMap = getPassthruBodiesForContext(context);
1402
+ passthruMap.set(streamId2, nativeResponse.body);
1403
+ const instanceId3 = nextInstanceId++;
1404
+ const state3 = {
1405
+ status: nativeResponse.status,
1406
+ statusText: nativeResponse.statusText,
1407
+ headers: Array.from(nativeResponse.headers.entries()),
1408
+ body: new Uint8Array(0),
1409
+ bodyUsed: false,
1410
+ type: "default",
1411
+ url: nativeResponse.url,
1412
+ redirected: nativeResponse.redirected,
1413
+ streamId: streamId2
1414
+ };
1415
+ stateMap.set(instanceId3, state3);
1416
+ return instanceId3;
1417
+ }
1418
+ const streamId = streamRegistry.create();
1419
+ const instanceId2 = nextInstanceId++;
1420
+ const state2 = {
1421
+ status: nativeResponse.status,
1422
+ statusText: nativeResponse.statusText,
1423
+ headers: Array.from(nativeResponse.headers.entries()),
1424
+ body: new Uint8Array(0),
1425
+ bodyUsed: false,
1426
+ type: "default",
1427
+ url: nativeResponse.url,
1428
+ redirected: nativeResponse.redirected,
1429
+ streamId
1430
+ };
1431
+ stateMap.set(instanceId2, state2);
1432
+ const reader = nativeResponse.body.getReader();
1433
+ (async () => {
1434
+ try {
1435
+ while (true) {
1436
+ const { done, value } = await reader.read();
1437
+ if (done) {
1438
+ streamRegistry.close(streamId);
1439
+ break;
1440
+ }
1441
+ if (value) {
1442
+ while (streamRegistry.isQueueFull(streamId)) {
1443
+ await new Promise((r) => setTimeout(r, 1));
1444
+ }
1445
+ streamRegistry.push(streamId, value);
1446
+ }
1447
+ }
1448
+ } catch (err) {
1449
+ streamRegistry.error(streamId, err);
1450
+ } finally {
1451
+ reader.releaseLock();
1452
+ }
1453
+ })();
1454
+ return instanceId2;
1455
+ }
1360
1456
  const responseBody = await nativeResponse.arrayBuffer();
1361
1457
  const responseBodyArray = Array.from(new Uint8Array(responseBody));
1362
1458
  const instanceId = nextInstanceId++;
@@ -1530,7 +1626,7 @@ async function setupFetch(context, options) {
1530
1626
  context.evalSync(hostBackedStreamCode);
1531
1627
  setupResponse(context, stateMap);
1532
1628
  setupRequest(context, stateMap);
1533
- setupFetchFunction(context, stateMap, options);
1629
+ setupFetchFunction(context, stateMap, streamRegistry, options);
1534
1630
  const serveState = {
1535
1631
  pendingUpgrade: null,
1536
1632
  activeConnections: new Map
@@ -1593,6 +1689,22 @@ async function setupFetch(context, options) {
1593
1689
  if (!responseState) {
1594
1690
  throw new Error("Response state not found");
1595
1691
  }
1692
+ if (responseState.streamId !== null) {
1693
+ const passthruMap = getPassthruBodiesForContext(context);
1694
+ const passthruBody = passthruMap.get(responseState.streamId);
1695
+ if (passthruBody) {
1696
+ passthruMap.delete(responseState.streamId);
1697
+ const responseHeaders2 = new Headers(responseState.headers);
1698
+ const status2 = responseState.status === 101 ? 200 : responseState.status;
1699
+ const response2 = new Response(passthruBody, {
1700
+ status: status2,
1701
+ statusText: responseState.statusText,
1702
+ headers: responseHeaders2
1703
+ });
1704
+ response2._originalStatus = responseState.status;
1705
+ return response2;
1706
+ }
1707
+ }
1596
1708
  if (responseState.streamId !== null) {
1597
1709
  const responseStreamId = responseState.streamId;
1598
1710
  let streamDone = false;
@@ -1778,4 +1890,4 @@ export {
1778
1890
  clearAllInstanceState
1779
1891
  };
1780
1892
 
1781
- //# debugId=AECB616396B74DA864756E2164756E21
1893
+ //# debugId=DA9402429BBB147864756E2164756E21