@cloudflare/sandbox 0.8.5 → 0.8.6

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/index.js CHANGED
@@ -773,6 +773,44 @@ var BaseTransport = class {
773
773
  }
774
774
  }
775
775
  /**
776
+ * Build a URL targeting the container's HTTP server.
777
+ */
778
+ buildContainerUrl(path$1) {
779
+ if (this.config.stub) return `http://localhost:${this.config.port || 3e3}${path$1}`;
780
+ return `${this.config.baseUrl ?? `http://localhost:${this.config.port || 3e3}`}${path$1}`;
781
+ }
782
+ /**
783
+ * Single HTTP request to the container — no WebSocket, no 503 retry.
784
+ */
785
+ httpFetch(path$1, options) {
786
+ const url = this.buildContainerUrl(path$1);
787
+ if (this.config.stub) return this.config.stub.containerFetch(url, options || {}, this.config.port);
788
+ return globalThis.fetch(url, options);
789
+ }
790
+ /**
791
+ * Streaming HTTP request to the container — no WebSocket, no 503 retry.
792
+ */
793
+ async httpFetchStream(path$1, body, method = "POST", headers) {
794
+ const url = this.buildContainerUrl(path$1);
795
+ const init = {
796
+ method,
797
+ headers: body && method === "POST" ? {
798
+ ...headers,
799
+ "Content-Type": "application/json"
800
+ } : headers,
801
+ body: body && method === "POST" ? JSON.stringify(body) : void 0
802
+ };
803
+ let response;
804
+ if (this.config.stub) response = await this.config.stub.containerFetch(url, init, this.config.port);
805
+ else response = await globalThis.fetch(url, init);
806
+ if (!response.ok) {
807
+ const errorBody = await response.text();
808
+ throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`);
809
+ }
810
+ if (!response.body) throw new Error("No response body for streaming");
811
+ return response.body;
812
+ }
813
+ /**
776
814
  * Sleep utility for retry delays
777
815
  */
778
816
  sleep(ms) {
@@ -787,13 +825,12 @@ var BaseTransport = class {
787
825
  *
788
826
  * Uses standard fetch API for communication with the container.
789
827
  * HTTP is stateless, so connect/disconnect are no-ops.
828
+ *
829
+ * All HTTP request logic lives in {@link BaseTransport.httpFetch} and
830
+ * {@link BaseTransport.httpFetchStream}; this subclass simply wires
831
+ * the abstract `doFetch` / `fetchStream` hooks to those shared helpers.
790
832
  */
791
833
  var HttpTransport = class extends BaseTransport {
792
- baseUrl;
793
- constructor(config) {
794
- super(config);
795
- this.baseUrl = config.baseUrl ?? "http://localhost:3000";
796
- }
797
834
  getMode() {
798
835
  return "http";
799
836
  }
@@ -803,36 +840,10 @@ var HttpTransport = class extends BaseTransport {
803
840
  return true;
804
841
  }
805
842
  async doFetch(path$1, options) {
806
- const url = this.buildUrl(path$1);
807
- if (this.config.stub) return this.config.stub.containerFetch(url, options || {}, this.config.port);
808
- return globalThis.fetch(url, options);
843
+ return this.httpFetch(path$1, options);
809
844
  }
810
845
  async fetchStream(path$1, body, method = "POST", headers) {
811
- const url = this.buildUrl(path$1);
812
- const options = this.buildStreamOptions(body, method, headers);
813
- let response;
814
- if (this.config.stub) response = await this.config.stub.containerFetch(url, options, this.config.port);
815
- else response = await globalThis.fetch(url, options);
816
- if (!response.ok) {
817
- const errorBody = await response.text();
818
- throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`);
819
- }
820
- if (!response.body) throw new Error("No response body for streaming");
821
- return response.body;
822
- }
823
- buildUrl(path$1) {
824
- if (this.config.stub) return `http://localhost:${this.config.port}${path$1}`;
825
- return `${this.baseUrl}${path$1}`;
826
- }
827
- buildStreamOptions(body, method, headers) {
828
- return {
829
- method,
830
- headers: body && method === "POST" ? {
831
- ...headers,
832
- "Content-Type": "application/json"
833
- } : headers,
834
- body: body && method === "POST" ? JSON.stringify(body) : void 0
835
- };
846
+ return this.httpFetchStream(path$1, body, method, headers);
836
847
  }
837
848
  };
838
849
 
@@ -899,10 +910,30 @@ var WebSocketTransport = class extends BaseTransport {
899
910
  this.cleanup();
900
911
  }
901
912
  /**
902
- * Transport-specific fetch implementation
913
+ * Whether a WebSocket connection is currently being established.
914
+ *
915
+ * When true, awaiting `connectPromise` from a nested call would deadlock:
916
+ * the outer `connectViaFetch → stub.fetch → containerFetch →
917
+ * startAndWaitForPorts → blockConcurrencyWhile(onStart)` chain may call
918
+ * back into the SDK (e.g. `exec()`), which would await the same
919
+ * `connectPromise` that cannot resolve until `onStart` returns.
920
+ *
921
+ * Callers use this to fall back to a direct HTTP request, which is safe
922
+ * because `startAndWaitForPorts()` calls `setHealthy()` before invoking
923
+ * `onStart()`, so `containerFetch()` routes directly to the container.
924
+ */
925
+ isWebSocketConnecting() {
926
+ return this.state === "connecting";
927
+ }
928
+ /**
929
+ * Transport-specific fetch implementation.
903
930
  * Converts WebSocket response to standard Response object.
931
+ *
932
+ * Falls back to HTTP while a WebSocket connection is being established
933
+ * to avoid the re-entrant deadlock described in `isWebSocketConnecting()`.
904
934
  */
905
935
  async doFetch(path$1, options) {
936
+ if (this.isWebSocketConnecting()) return this.httpFetch(path$1, options);
906
937
  await this.connect();
907
938
  const method = options?.method || "GET";
908
939
  const body = this.parseBody(options?.body);
@@ -914,7 +945,9 @@ var WebSocketTransport = class extends BaseTransport {
914
945
  });
915
946
  }
916
947
  /**
917
- * Streaming fetch implementation
948
+ * Streaming fetch implementation.
949
+ *
950
+ * Delegates to `requestStream()`, which applies the re-entrancy guard.
918
951
  */
919
952
  async fetchStream(path$1, body, method = "POST", headers) {
920
953
  return this.requestStream(method, path$1, body, headers);
@@ -1054,7 +1087,12 @@ var WebSocketTransport = class extends BaseTransport {
1054
1087
  });
1055
1088
  }
1056
1089
  /**
1057
- * Send a request and wait for response
1090
+ * Send a request and wait for response.
1091
+ *
1092
+ * Only reachable from `doFetch()`, which already applies the re-entrancy
1093
+ * guard via `isWebSocketConnecting()`. The `connect()` call here handles
1094
+ * the case where the WebSocket was closed between `doFetch` and `request`
1095
+ * (idle disconnect).
1058
1096
  */
1059
1097
  async request(method, path$1, body, headers) {
1060
1098
  await this.connect();
@@ -1105,7 +1143,7 @@ var WebSocketTransport = class extends BaseTransport {
1105
1143
  });
1106
1144
  }
1107
1145
  /**
1108
- * Send a streaming request and return a ReadableStream
1146
+ * Send a streaming request and return a ReadableStream.
1109
1147
  *
1110
1148
  * The stream will receive data chunks as they arrive over the WebSocket.
1111
1149
  * Format matches SSE for compatibility with existing streaming code.
@@ -1117,8 +1155,12 @@ var WebSocketTransport = class extends BaseTransport {
1117
1155
  * Uses an inactivity timeout instead of a total-duration timeout so that
1118
1156
  * long-running streams (e.g. execStream from an agent) stay alive as long
1119
1157
  * as data is flowing. The timer resets on every chunk or response message.
1158
+ *
1159
+ * Falls back to HTTP while a WebSocket connection is being established
1160
+ * to avoid the re-entrant deadlock described in `isWebSocketConnecting()`.
1120
1161
  */
1121
1162
  async requestStream(method, path$1, body, headers) {
1163
+ if (this.isWebSocketConnecting()) return this.httpFetchStream(path$1, body, method, headers);
1122
1164
  await this.connect();
1123
1165
  this.clearIdleDisconnectTimer();
1124
1166
  const id = generateRequestId();
@@ -3645,7 +3687,7 @@ function buildS3fsSource(bucket, prefix) {
3645
3687
  * This file is auto-updated by .github/changeset-version.ts during releases
3646
3688
  * DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
3647
3689
  */
3648
- const SDK_VERSION = "0.8.5";
3690
+ const SDK_VERSION = "0.8.6";
3649
3691
 
3650
3692
  //#endregion
3651
3693
  //#region src/sandbox.ts