@ricsam/isolate-fetch 0.1.9 → 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.
@@ -50,6 +50,7 @@ var import_isolated_vm = __toESM(require("isolated-vm"));
50
50
  var import_isolate_core = require("@ricsam/isolate-core");
51
51
  var import_stream_state = require("./stream-state.cjs");
52
52
  var instanceStateMap = new WeakMap;
53
+ var passthruBodies = new WeakMap;
53
54
  var nextInstanceId = 1;
54
55
  function getInstanceStateMapForContext(context) {
55
56
  let map = instanceStateMap.get(context);
@@ -59,6 +60,14 @@ function getInstanceStateMapForContext(context) {
59
60
  }
60
61
  return map;
61
62
  }
63
+ function getPassthruBodiesForContext(context) {
64
+ let map = passthruBodies.get(context);
65
+ if (!map) {
66
+ map = new Map;
67
+ passthruBodies.set(context, map);
68
+ }
69
+ return map;
70
+ }
62
71
  var headersCode = `
63
72
  (function() {
64
73
  class Headers {
@@ -647,8 +656,12 @@ function setupResponse(context, stateMap) {
647
656
  // Mark as needing async Blob handling - will be read in constructor
648
657
  return { __isBlob: true, blob: body };
649
658
  }
650
- // Handle ReadableStream (both native and host-backed)
651
- if (body instanceof ReadableStream || body instanceof HostBackedReadableStream) {
659
+ // Handle HostBackedReadableStream specially - preserve streamId
660
+ if (body instanceof HostBackedReadableStream) {
661
+ return { __isHostStream: true, stream: body, streamId: body._getStreamId() };
662
+ }
663
+ // Handle native ReadableStream
664
+ if (body instanceof ReadableStream) {
652
665
  return { __isStream: true, stream: body };
653
666
  }
654
667
  // Try to convert to string
@@ -703,7 +716,27 @@ function setupResponse(context, stateMap) {
703
716
  return;
704
717
  }
705
718
 
706
- // Handle streaming body
719
+ // Handle HostBackedReadableStream - reuse existing streamId for pass-through
720
+ if (preparedBody && preparedBody.__isHostStream) {
721
+ // Reuse the existing streamId to preserve the pass-through body mapping
722
+ this.#streamId = preparedBody.streamId;
723
+ const status = init.status ?? 200;
724
+ const statusText = init.statusText ?? '';
725
+ const headers = new Headers(init.headers);
726
+ const headersArray = Array.from(headers.entries());
727
+
728
+ this.#instanceId = __Response_constructStreaming(
729
+ this.#streamId,
730
+ status,
731
+ statusText,
732
+ headersArray
733
+ );
734
+ this.#headers = headers;
735
+ // Don't pump - the body is already backed by this streamId
736
+ return;
737
+ }
738
+
739
+ // Handle native ReadableStream body
707
740
  if (preparedBody && preparedBody.__isStream) {
708
741
  this.#streamId = __Stream_create();
709
742
  const status = init.status ?? 200;
@@ -1384,7 +1417,8 @@ function setupRequest(context, stateMap) {
1384
1417
  `;
1385
1418
  context.evalSync(requestCode);
1386
1419
  }
1387
- function setupFetchFunction(context, stateMap, options) {
1420
+ var FETCH_STREAM_THRESHOLD = 64 * 1024;
1421
+ function setupFetchFunction(context, stateMap, streamRegistry, options) {
1388
1422
  const global = context.global;
1389
1423
  const fetchRef = new import_isolated_vm.default.Reference(async (url, method, headersJson, bodyJson, signalAborted) => {
1390
1424
  if (signalAborted) {
@@ -1400,6 +1434,69 @@ function setupFetchFunction(context, stateMap, options) {
1400
1434
  });
1401
1435
  const onFetch = options?.onFetch ?? fetch;
1402
1436
  const nativeResponse = await onFetch(nativeRequest);
1437
+ const contentLength = nativeResponse.headers.get("content-length");
1438
+ const knownSize = contentLength ? parseInt(contentLength, 10) : null;
1439
+ const isCallbackStream = nativeResponse.__isCallbackStream;
1440
+ const isNetworkResponse = nativeResponse.url && (nativeResponse.url.startsWith("http://") || nativeResponse.url.startsWith("https://"));
1441
+ const shouldStream = nativeResponse.body && (isCallbackStream || isNetworkResponse && (knownSize === null || knownSize > FETCH_STREAM_THRESHOLD));
1442
+ if (shouldStream && nativeResponse.body) {
1443
+ if (isCallbackStream) {
1444
+ const streamId2 = streamRegistry.create();
1445
+ const passthruMap = getPassthruBodiesForContext(context);
1446
+ passthruMap.set(streamId2, nativeResponse.body);
1447
+ const instanceId3 = nextInstanceId++;
1448
+ const state3 = {
1449
+ status: nativeResponse.status,
1450
+ statusText: nativeResponse.statusText,
1451
+ headers: Array.from(nativeResponse.headers.entries()),
1452
+ body: new Uint8Array(0),
1453
+ bodyUsed: false,
1454
+ type: "default",
1455
+ url: nativeResponse.url,
1456
+ redirected: nativeResponse.redirected,
1457
+ streamId: streamId2
1458
+ };
1459
+ stateMap.set(instanceId3, state3);
1460
+ return instanceId3;
1461
+ }
1462
+ const streamId = streamRegistry.create();
1463
+ const instanceId2 = nextInstanceId++;
1464
+ const state2 = {
1465
+ status: nativeResponse.status,
1466
+ statusText: nativeResponse.statusText,
1467
+ headers: Array.from(nativeResponse.headers.entries()),
1468
+ body: new Uint8Array(0),
1469
+ bodyUsed: false,
1470
+ type: "default",
1471
+ url: nativeResponse.url,
1472
+ redirected: nativeResponse.redirected,
1473
+ streamId
1474
+ };
1475
+ stateMap.set(instanceId2, state2);
1476
+ const reader = nativeResponse.body.getReader();
1477
+ (async () => {
1478
+ try {
1479
+ while (true) {
1480
+ const { done, value } = await reader.read();
1481
+ if (done) {
1482
+ streamRegistry.close(streamId);
1483
+ break;
1484
+ }
1485
+ if (value) {
1486
+ while (streamRegistry.isQueueFull(streamId)) {
1487
+ await new Promise((r) => setTimeout(r, 1));
1488
+ }
1489
+ streamRegistry.push(streamId, value);
1490
+ }
1491
+ }
1492
+ } catch (err) {
1493
+ streamRegistry.error(streamId, err);
1494
+ } finally {
1495
+ reader.releaseLock();
1496
+ }
1497
+ })();
1498
+ return instanceId2;
1499
+ }
1403
1500
  const responseBody = await nativeResponse.arrayBuffer();
1404
1501
  const responseBodyArray = Array.from(new Uint8Array(responseBody));
1405
1502
  const instanceId = nextInstanceId++;
@@ -1573,7 +1670,7 @@ async function setupFetch(context, options) {
1573
1670
  context.evalSync(hostBackedStreamCode);
1574
1671
  setupResponse(context, stateMap);
1575
1672
  setupRequest(context, stateMap);
1576
- setupFetchFunction(context, stateMap, options);
1673
+ setupFetchFunction(context, stateMap, streamRegistry, options);
1577
1674
  const serveState = {
1578
1675
  pendingUpgrade: null,
1579
1676
  activeConnections: new Map
@@ -1636,6 +1733,22 @@ async function setupFetch(context, options) {
1636
1733
  if (!responseState) {
1637
1734
  throw new Error("Response state not found");
1638
1735
  }
1736
+ if (responseState.streamId !== null) {
1737
+ const passthruMap = getPassthruBodiesForContext(context);
1738
+ const passthruBody = passthruMap.get(responseState.streamId);
1739
+ if (passthruBody) {
1740
+ passthruMap.delete(responseState.streamId);
1741
+ const responseHeaders2 = new Headers(responseState.headers);
1742
+ const status2 = responseState.status === 101 ? 200 : responseState.status;
1743
+ const response2 = new Response(passthruBody, {
1744
+ status: status2,
1745
+ statusText: responseState.statusText,
1746
+ headers: responseHeaders2
1747
+ });
1748
+ response2._originalStatus = responseState.status;
1749
+ return response2;
1750
+ }
1751
+ }
1639
1752
  if (responseState.streamId !== null) {
1640
1753
  const responseStreamId = responseState.streamId;
1641
1754
  let streamDone = false;
@@ -1817,4 +1930,4 @@ async function setupFetch(context, options) {
1817
1930
  };
1818
1931
  }
1819
1932
 
1820
- //# debugId=653D8B1934FCEAA064756E2164756E21
1933
+ //# debugId=D2829934F58098A764756E2164756E21