@cloudflare/sandbox 0.8.0 → 0.8.2
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 +77 -13
- package/dist/index.js.map +1 -1
- package/dist/sandbox-DDDSuWA6.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -737,6 +737,9 @@ var BaseTransport = class {
|
|
|
737
737
|
setRetryTimeoutMs(ms) {
|
|
738
738
|
this.retryTimeoutMs = ms;
|
|
739
739
|
}
|
|
740
|
+
getRetryTimeoutMs() {
|
|
741
|
+
return this.retryTimeoutMs;
|
|
742
|
+
}
|
|
740
743
|
/**
|
|
741
744
|
* Fetch with automatic retry for 503 (container starting)
|
|
742
745
|
*
|
|
@@ -841,6 +844,8 @@ var HttpTransport = class extends BaseTransport {
|
|
|
841
844
|
const DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
|
|
842
845
|
const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
843
846
|
const DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
|
|
847
|
+
const DEFAULT_IDLE_DISCONNECT_MS = 1e3;
|
|
848
|
+
const MIN_TIME_FOR_CONNECT_RETRY_MS = 15e3;
|
|
844
849
|
/**
|
|
845
850
|
* WebSocket transport implementation
|
|
846
851
|
*
|
|
@@ -852,6 +857,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
852
857
|
state = "disconnected";
|
|
853
858
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
854
859
|
connectPromise = null;
|
|
860
|
+
idleDisconnectTimer = null;
|
|
855
861
|
boundHandleMessage;
|
|
856
862
|
boundHandleClose;
|
|
857
863
|
constructor(config) {
|
|
@@ -876,6 +882,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
876
882
|
* callers share the same connection attempt.
|
|
877
883
|
*/
|
|
878
884
|
async connect() {
|
|
885
|
+
this.clearIdleDisconnectTimer();
|
|
879
886
|
if (this.isConnected()) return;
|
|
880
887
|
if (this.connectPromise) return this.connectPromise;
|
|
881
888
|
this.connectPromise = this.doConnect();
|
|
@@ -943,6 +950,26 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
943
950
|
if (this.config.stub) await this.connectViaFetch();
|
|
944
951
|
else await this.connectViaWebSocket();
|
|
945
952
|
}
|
|
953
|
+
async fetchUpgradeWithRetry(attemptUpgrade) {
|
|
954
|
+
const retryTimeoutMs = this.getRetryTimeoutMs();
|
|
955
|
+
const startTime = Date.now();
|
|
956
|
+
let attempt = 0;
|
|
957
|
+
while (true) {
|
|
958
|
+
const response = await attemptUpgrade();
|
|
959
|
+
if (response.status !== 503) return response;
|
|
960
|
+
const remaining = retryTimeoutMs - (Date.now() - startTime);
|
|
961
|
+
if (remaining <= MIN_TIME_FOR_CONNECT_RETRY_MS) return response;
|
|
962
|
+
const delay = Math.min(3e3 * 2 ** attempt, 3e4);
|
|
963
|
+
this.logger.info("WebSocket container not ready, retrying", {
|
|
964
|
+
status: response.status,
|
|
965
|
+
attempt: attempt + 1,
|
|
966
|
+
delayMs: delay,
|
|
967
|
+
remainingSec: Math.floor(remaining / 1e3)
|
|
968
|
+
});
|
|
969
|
+
await this.sleep(delay);
|
|
970
|
+
attempt++;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
946
973
|
/**
|
|
947
974
|
* Connect using fetch-based WebSocket (Cloudflare Workers style)
|
|
948
975
|
* This is required when running inside a Durable Object.
|
|
@@ -952,20 +979,10 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
952
979
|
*/
|
|
953
980
|
async connectViaFetch() {
|
|
954
981
|
const timeoutMs = this.config.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
955
|
-
const controller = new AbortController();
|
|
956
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
957
982
|
try {
|
|
958
983
|
const wsPath = new URL(this.config.wsUrl).pathname;
|
|
959
984
|
const httpUrl = `http://localhost:${this.config.port || 3e3}${wsPath}`;
|
|
960
|
-
const
|
|
961
|
-
headers: {
|
|
962
|
-
Upgrade: "websocket",
|
|
963
|
-
Connection: "Upgrade"
|
|
964
|
-
},
|
|
965
|
-
signal: controller.signal
|
|
966
|
-
});
|
|
967
|
-
const response = await this.config.stub.fetch(request);
|
|
968
|
-
clearTimeout(timeout);
|
|
985
|
+
const response = await this.fetchUpgradeWithRetry(() => this.fetchUpgradeAttempt(httpUrl, timeoutMs));
|
|
969
986
|
if (response.status !== 101) throw new Error(`WebSocket upgrade failed: ${response.status} ${response.statusText}`);
|
|
970
987
|
const ws = response.webSocket;
|
|
971
988
|
if (!ws) throw new Error("No WebSocket in upgrade response");
|
|
@@ -976,12 +993,27 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
976
993
|
this.ws.addEventListener("message", this.boundHandleMessage);
|
|
977
994
|
this.logger.debug("WebSocket connected via fetch", { url: this.config.wsUrl });
|
|
978
995
|
} catch (error) {
|
|
979
|
-
clearTimeout(timeout);
|
|
980
996
|
this.state = "error";
|
|
981
997
|
this.logger.error("WebSocket fetch connection failed", error instanceof Error ? error : new Error(String(error)));
|
|
982
998
|
throw error;
|
|
983
999
|
}
|
|
984
1000
|
}
|
|
1001
|
+
async fetchUpgradeAttempt(httpUrl, timeoutMs) {
|
|
1002
|
+
const controller = new AbortController();
|
|
1003
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
1004
|
+
try {
|
|
1005
|
+
const request = new Request(httpUrl, {
|
|
1006
|
+
headers: {
|
|
1007
|
+
Upgrade: "websocket",
|
|
1008
|
+
Connection: "Upgrade"
|
|
1009
|
+
},
|
|
1010
|
+
signal: controller.signal
|
|
1011
|
+
});
|
|
1012
|
+
return await this.config.stub.fetch(request);
|
|
1013
|
+
} finally {
|
|
1014
|
+
clearTimeout(timeout);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
985
1017
|
/**
|
|
986
1018
|
* Connect using standard WebSocket API (browser/Node style)
|
|
987
1019
|
*/
|
|
@@ -1026,6 +1058,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1026
1058
|
*/
|
|
1027
1059
|
async request(method, path$1, body, headers) {
|
|
1028
1060
|
await this.connect();
|
|
1061
|
+
this.clearIdleDisconnectTimer();
|
|
1029
1062
|
const id = generateRequestId();
|
|
1030
1063
|
const request = {
|
|
1031
1064
|
type: "request",
|
|
@@ -1039,12 +1072,14 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1039
1072
|
const timeoutMs = this.config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
1040
1073
|
const timeoutId = setTimeout(() => {
|
|
1041
1074
|
this.pendingRequests.delete(id);
|
|
1075
|
+
this.scheduleIdleDisconnect();
|
|
1042
1076
|
reject(/* @__PURE__ */ new Error(`Request timeout after ${timeoutMs}ms: ${method} ${path$1}`));
|
|
1043
1077
|
}, timeoutMs);
|
|
1044
1078
|
this.pendingRequests.set(id, {
|
|
1045
1079
|
resolve: (response) => {
|
|
1046
1080
|
clearTimeout(timeoutId);
|
|
1047
1081
|
this.pendingRequests.delete(id);
|
|
1082
|
+
this.scheduleIdleDisconnect();
|
|
1048
1083
|
resolve({
|
|
1049
1084
|
status: response.status,
|
|
1050
1085
|
body: response.body
|
|
@@ -1053,6 +1088,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1053
1088
|
reject: (error) => {
|
|
1054
1089
|
clearTimeout(timeoutId);
|
|
1055
1090
|
this.pendingRequests.delete(id);
|
|
1091
|
+
this.scheduleIdleDisconnect();
|
|
1056
1092
|
reject(error);
|
|
1057
1093
|
},
|
|
1058
1094
|
isStreaming: false,
|
|
@@ -1063,6 +1099,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1063
1099
|
} catch (error) {
|
|
1064
1100
|
clearTimeout(timeoutId);
|
|
1065
1101
|
this.pendingRequests.delete(id);
|
|
1102
|
+
this.scheduleIdleDisconnect();
|
|
1066
1103
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1067
1104
|
}
|
|
1068
1105
|
});
|
|
@@ -1083,6 +1120,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1083
1120
|
*/
|
|
1084
1121
|
async requestStream(method, path$1, body, headers) {
|
|
1085
1122
|
await this.connect();
|
|
1123
|
+
this.clearIdleDisconnectTimer();
|
|
1086
1124
|
const id = generateRequestId();
|
|
1087
1125
|
const request = {
|
|
1088
1126
|
type: "request",
|
|
@@ -1099,6 +1137,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1099
1137
|
const createIdleTimeout = () => {
|
|
1100
1138
|
return setTimeout(() => {
|
|
1101
1139
|
this.pendingRequests.delete(id);
|
|
1140
|
+
this.scheduleIdleDisconnect();
|
|
1102
1141
|
const error = /* @__PURE__ */ new Error(`Stream idle timeout after ${idleTimeoutMs}ms: ${method} ${path$1}`);
|
|
1103
1142
|
if (firstMessageReceived) try {
|
|
1104
1143
|
streamController?.error(error);
|
|
@@ -1126,6 +1165,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1126
1165
|
});
|
|
1127
1166
|
}
|
|
1128
1167
|
this.pendingRequests.delete(id);
|
|
1168
|
+
this.scheduleIdleDisconnect();
|
|
1129
1169
|
}
|
|
1130
1170
|
});
|
|
1131
1171
|
this.pendingRequests.set(id, {
|
|
@@ -1133,6 +1173,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1133
1173
|
const pending = this.pendingRequests.get(id);
|
|
1134
1174
|
if (pending?.timeoutId) clearTimeout(pending.timeoutId);
|
|
1135
1175
|
this.pendingRequests.delete(id);
|
|
1176
|
+
this.scheduleIdleDisconnect();
|
|
1136
1177
|
if (!firstMessageReceived) {
|
|
1137
1178
|
firstMessageReceived = true;
|
|
1138
1179
|
if (response.status >= 400) rejectStream(/* @__PURE__ */ new Error(`Stream error: ${response.status} - ${JSON.stringify(response.body)}`));
|
|
@@ -1149,6 +1190,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1149
1190
|
const pending = this.pendingRequests.get(id);
|
|
1150
1191
|
if (pending?.timeoutId) clearTimeout(pending.timeoutId);
|
|
1151
1192
|
this.pendingRequests.delete(id);
|
|
1193
|
+
this.scheduleIdleDisconnect();
|
|
1152
1194
|
if (firstMessageReceived) try {
|
|
1153
1195
|
streamController?.error(error);
|
|
1154
1196
|
} catch {}
|
|
@@ -1173,6 +1215,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1173
1215
|
});
|
|
1174
1216
|
if (pending.timeoutId) clearTimeout(pending.timeoutId);
|
|
1175
1217
|
this.pendingRequests.delete(id);
|
|
1218
|
+
this.scheduleIdleDisconnect();
|
|
1176
1219
|
}
|
|
1177
1220
|
pending.bufferedChunks = void 0;
|
|
1178
1221
|
}
|
|
@@ -1186,6 +1229,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1186
1229
|
} catch (error) {
|
|
1187
1230
|
clearTimeout(timeoutId);
|
|
1188
1231
|
this.pendingRequests.delete(id);
|
|
1232
|
+
this.scheduleIdleDisconnect();
|
|
1189
1233
|
rejectStream(error instanceof Error ? error : new Error(String(error)));
|
|
1190
1234
|
}
|
|
1191
1235
|
});
|
|
@@ -1272,6 +1316,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1272
1316
|
});
|
|
1273
1317
|
if (pending.timeoutId) clearTimeout(pending.timeoutId);
|
|
1274
1318
|
this.pendingRequests.delete(chunk.id);
|
|
1319
|
+
this.scheduleIdleDisconnect();
|
|
1275
1320
|
}
|
|
1276
1321
|
}
|
|
1277
1322
|
/**
|
|
@@ -1283,6 +1328,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1283
1328
|
const idleTimeoutMs = this.config.streamIdleTimeoutMs ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS;
|
|
1284
1329
|
pending.timeoutId = setTimeout(() => {
|
|
1285
1330
|
this.pendingRequests.delete(id);
|
|
1331
|
+
this.scheduleIdleDisconnect();
|
|
1286
1332
|
if (pending.streamController) try {
|
|
1287
1333
|
pending.streamController.error(/* @__PURE__ */ new Error(`Stream idle timeout after ${idleTimeoutMs}ms`));
|
|
1288
1334
|
} catch {}
|
|
@@ -1325,6 +1371,7 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1325
1371
|
* Cleanup resources
|
|
1326
1372
|
*/
|
|
1327
1373
|
cleanup() {
|
|
1374
|
+
this.clearIdleDisconnectTimer();
|
|
1328
1375
|
if (this.ws) {
|
|
1329
1376
|
this.ws.removeEventListener("close", this.boundHandleClose);
|
|
1330
1377
|
this.ws.removeEventListener("message", this.boundHandleMessage);
|
|
@@ -1336,6 +1383,23 @@ var WebSocketTransport = class extends BaseTransport {
|
|
|
1336
1383
|
for (const pending of this.pendingRequests.values()) if (pending.timeoutId) clearTimeout(pending.timeoutId);
|
|
1337
1384
|
this.pendingRequests.clear();
|
|
1338
1385
|
}
|
|
1386
|
+
scheduleIdleDisconnect() {
|
|
1387
|
+
if (!this.isConnected() || this.pendingRequests.size > 0) return;
|
|
1388
|
+
this.clearIdleDisconnectTimer();
|
|
1389
|
+
this.idleDisconnectTimer = setTimeout(() => {
|
|
1390
|
+
this.idleDisconnectTimer = null;
|
|
1391
|
+
if (this.pendingRequests.size === 0 && this.isConnected()) {
|
|
1392
|
+
this.logger.debug("Disconnecting idle WebSocket transport");
|
|
1393
|
+
this.cleanup();
|
|
1394
|
+
}
|
|
1395
|
+
}, DEFAULT_IDLE_DISCONNECT_MS);
|
|
1396
|
+
}
|
|
1397
|
+
clearIdleDisconnectTimer() {
|
|
1398
|
+
if (this.idleDisconnectTimer) {
|
|
1399
|
+
clearTimeout(this.idleDisconnectTimer);
|
|
1400
|
+
this.idleDisconnectTimer = null;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1339
1403
|
};
|
|
1340
1404
|
|
|
1341
1405
|
//#endregion
|
|
@@ -3562,7 +3626,7 @@ function buildS3fsSource(bucket, prefix) {
|
|
|
3562
3626
|
* This file is auto-updated by .github/changeset-version.ts during releases
|
|
3563
3627
|
* DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
|
|
3564
3628
|
*/
|
|
3565
|
-
const SDK_VERSION = "0.8.
|
|
3629
|
+
const SDK_VERSION = "0.8.2";
|
|
3566
3630
|
|
|
3567
3631
|
//#endregion
|
|
3568
3632
|
//#region src/sandbox.ts
|