veryfront 0.1.281 → 0.1.282

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/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.281",
3
+ "version": "0.1.282",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "workspace": [
@@ -5,6 +5,7 @@ export interface LazySandboxOptions extends SandboxOptions {
5
5
  pollIntervalMs?: number;
6
6
  heartbeatIntervalMs?: number;
7
7
  heartbeatGraceMs?: number;
8
+ controlRequestTimeoutMs?: number;
8
9
  execStartTimeoutMs?: number;
9
10
  execStartMaxAttempts?: number;
10
11
  execStartRetryDelayMs?: number;
@@ -22,6 +23,7 @@ export declare class LazySandbox {
22
23
  private readonly pollIntervalMs;
23
24
  private readonly heartbeatIntervalMs;
24
25
  private readonly heartbeatGraceMs;
26
+ private readonly controlRequestTimeoutMs;
25
27
  private readonly execStartTimeoutMs;
26
28
  private readonly execStartMaxAttempts;
27
29
  private readonly execStartRetryDelayMs;
@@ -56,6 +58,7 @@ export declare class LazySandbox {
56
58
  get isActive(): boolean;
57
59
  private bootstrapSession;
58
60
  private resolveReadyEndpoint;
61
+ private waitForReadySession;
59
62
  private touchSession;
60
63
  private startHeartbeatLoop;
61
64
  private stopHeartbeatLoop;
@@ -68,6 +71,7 @@ export declare class LazySandbox {
68
71
  private updateTrackedCommandJob;
69
72
  private startExec;
70
73
  private fetchExecStart;
74
+ private fetchControl;
71
75
  private waitForExecStartRetry;
72
76
  private reprovisionAfterExecStartFailure;
73
77
  private resolveRuntimeEndpoint;
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-sandbox.d.ts","sourceRoot":"","sources":["../../../src/src/sandbox/lazy-sandbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lazy-sandbox.d.ts","sourceRoot":"","sources":["../../../src/src/sandbox/lazy-sandbox.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
2
  import { REQUEST_ERROR } from "../errors/index.js";
3
- import { resolveSandboxApiUrl, resolveSandboxAuthToken, waitForSandboxReady, } from "./sandbox.js";
3
+ import { resolveSandboxApiUrl, resolveSandboxAuthToken, } from "./sandbox.js";
4
4
  const DEFAULT_STARTUP_TIMEOUT_MS = 180_000;
5
5
  const DEFAULT_POLL_INTERVAL_MS = 2_000;
6
6
  const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;
7
7
  const DEFAULT_HEARTBEAT_GRACE_MS = 5_000;
8
+ const DEFAULT_CONTROL_REQUEST_TIMEOUT_MS = 15_000;
8
9
  const DEFAULT_EXEC_START_TIMEOUT_MS = 30_000;
9
10
  const DEFAULT_EXEC_START_MAX_ATTEMPTS = 3;
10
11
  const DEFAULT_EXEC_START_RETRY_DELAY_MS = 1_000;
@@ -23,6 +24,7 @@ export class LazySandbox {
23
24
  pollIntervalMs;
24
25
  heartbeatIntervalMs;
25
26
  heartbeatGraceMs;
27
+ controlRequestTimeoutMs;
26
28
  execStartTimeoutMs;
27
29
  execStartMaxAttempts;
28
30
  execStartRetryDelayMs;
@@ -44,6 +46,8 @@ export class LazySandbox {
44
46
  this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
45
47
  this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
46
48
  this.heartbeatGraceMs = options.heartbeatGraceMs ?? DEFAULT_HEARTBEAT_GRACE_MS;
49
+ this.controlRequestTimeoutMs = options.controlRequestTimeoutMs ??
50
+ DEFAULT_CONTROL_REQUEST_TIMEOUT_MS;
47
51
  this.execStartTimeoutMs = options.execStartTimeoutMs ?? DEFAULT_EXEC_START_TIMEOUT_MS;
48
52
  this.execStartMaxAttempts = options.execStartMaxAttempts ?? DEFAULT_EXEC_START_MAX_ATTEMPTS;
49
53
  this.execStartRetryDelayMs = options.execStartRetryDelayMs ??
@@ -126,7 +130,7 @@ export class LazySandbox {
126
130
  }
127
131
  async readFile(path) {
128
132
  await this.touchSession();
129
- const res = await fetch(`${this.requireEndpoint()}/file?path=${encodeURIComponent(path)}`, {
133
+ const res = await this.fetchControl(`${this.requireEndpoint()}/file?path=${encodeURIComponent(path)}`, {
130
134
  headers: this.authHeaders(),
131
135
  });
132
136
  if (!res.ok) {
@@ -136,7 +140,7 @@ export class LazySandbox {
136
140
  }
137
141
  async writeFiles(files) {
138
142
  await this.touchSession();
139
- const res = await fetch(`${this.requireEndpoint()}/files`, {
143
+ const res = await this.fetchControl(`${this.requireEndpoint()}/files`, {
140
144
  method: "POST",
141
145
  headers: this.jsonHeaders(),
142
146
  body: JSON.stringify({ files }),
@@ -150,7 +154,7 @@ export class LazySandbox {
150
154
  async startCommandJob(command, options) {
151
155
  await this.touchSession();
152
156
  const endpoint = this.resolveRuntimeEndpoint();
153
- const res = await fetch(`${endpoint}/exec/jobs`, {
157
+ const res = await this.fetchControl(`${endpoint}/exec/jobs`, {
154
158
  method: "POST",
155
159
  headers: this.jsonHeaders(),
156
160
  body: JSON.stringify({ command, ...this.resolveExecOptions(options) }),
@@ -166,7 +170,7 @@ export class LazySandbox {
166
170
  }
167
171
  async getCommandJob(jobId) {
168
172
  const endpoint = await this.resolveCommandJobEndpoint(jobId);
169
- const res = await fetch(`${endpoint}/exec/jobs/${jobId}`, {
173
+ const res = await this.fetchControl(`${endpoint}/exec/jobs/${jobId}`, {
170
174
  headers: this.authHeaders(),
171
175
  });
172
176
  if (!res.ok) {
@@ -180,7 +184,7 @@ export class LazySandbox {
180
184
  }
181
185
  async getCommandJobOutput(jobId) {
182
186
  const endpoint = await this.resolveCommandJobEndpoint(jobId);
183
- const res = await fetch(`${endpoint}/exec/jobs/${jobId}/output`, {
187
+ const res = await this.fetchControl(`${endpoint}/exec/jobs/${jobId}/output`, {
184
188
  headers: this.authHeaders(),
185
189
  });
186
190
  if (!res.ok) {
@@ -201,7 +205,7 @@ export class LazySandbox {
201
205
  }
202
206
  async listCommandJobs() {
203
207
  await this.ensure();
204
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs`, {
208
+ const res = await this.fetchControl(`${this.requireEndpoint()}/exec/jobs`, {
205
209
  headers: this.authHeaders(),
206
210
  });
207
211
  if (!res.ok) {
@@ -215,7 +219,7 @@ export class LazySandbox {
215
219
  }
216
220
  async cancelCommandJob(jobId) {
217
221
  const endpoint = await this.resolveCommandJobEndpoint(jobId);
218
- const res = await fetch(`${endpoint}/exec/jobs/${jobId}/cancel`, {
222
+ const res = await this.fetchControl(`${endpoint}/exec/jobs/${jobId}/cancel`, {
219
223
  method: "POST",
220
224
  headers: this.authHeaders(),
221
225
  });
@@ -241,7 +245,7 @@ export class LazySandbox {
241
245
  return;
242
246
  }
243
247
  const promise = (async () => {
244
- const res = await fetch(`${this.apiUrl}/sandbox-sessions/${currentSessionId}/heartbeat`, {
248
+ const res = await this.fetchControl(`${this.apiUrl}/sandbox-sessions/${currentSessionId}/heartbeat`, {
245
249
  method: "POST",
246
250
  headers: this.authHeaders(),
247
251
  });
@@ -309,7 +313,7 @@ export class LazySandbox {
309
313
  }
310
314
  async bootstrapSession() {
311
315
  const projectId = this.resolveProjectId();
312
- const res = await fetch(`${this.apiUrl}/sandbox-sessions`, {
316
+ const res = await this.fetchControl(`${this.apiUrl}/sandbox-sessions`, {
313
317
  method: "POST",
314
318
  headers: this.jsonHeaders(),
315
319
  body: JSON.stringify(projectId ? { project_id: projectId } : {}),
@@ -341,23 +345,29 @@ export class LazySandbox {
341
345
  if (session.status === "running") {
342
346
  return session.endpoint;
343
347
  }
344
- await waitForSandboxReady({
345
- apiUrl: this.apiUrl,
346
- id: session.id,
347
- authToken: this.authToken,
348
- maxWaitMs: this.startupTimeoutMs,
349
- pollIntervalMs: this.pollIntervalMs,
350
- });
351
- const res = await fetch(`${this.apiUrl}/sandbox-sessions/${session.id}`, {
352
- headers: this.authHeaders(),
353
- });
354
- if (!res.ok) {
355
- throw REQUEST_ERROR.create({
356
- detail: `Failed to get sandbox: ${res.status} ${await res.text()}`,
348
+ return (await this.waitForReadySession(session.id)).endpoint;
349
+ }
350
+ async waitForReadySession(sessionId) {
351
+ const start = Date.now();
352
+ while (Date.now() - start < this.startupTimeoutMs) {
353
+ await new Promise((resolve) => dntShim.setTimeout(resolve, this.pollIntervalMs));
354
+ const res = await this.fetchControl(`${this.apiUrl}/sandbox-sessions/${sessionId}`, {
355
+ headers: this.authHeaders(),
357
356
  });
357
+ if (!res.ok) {
358
+ continue;
359
+ }
360
+ const session = await res.json();
361
+ if (session.status === "running") {
362
+ return session;
363
+ }
364
+ if (session.status === "error" || session.status === "deleting") {
365
+ throw REQUEST_ERROR.create({
366
+ detail: `Sandbox failed to start: status=${session.status}`,
367
+ });
368
+ }
358
369
  }
359
- const nextSession = await res.json();
360
- return nextSession.endpoint;
370
+ throw REQUEST_ERROR.create({ detail: "Sandbox did not become ready within timeout" });
361
371
  }
362
372
  async touchSession() {
363
373
  const projectId = this.resolveProjectId();
@@ -387,7 +397,7 @@ export class LazySandbox {
387
397
  this.heartbeatTimer = null;
388
398
  }
389
399
  async deleteSession(sessionId) {
390
- await fetch(`${this.apiUrl}/sandbox-sessions/${sessionId}`, {
400
+ await this.fetchControl(`${this.apiUrl}/sandbox-sessions/${sessionId}`, {
391
401
  method: "DELETE",
392
402
  headers: this.authHeaders(),
393
403
  });
@@ -466,14 +476,10 @@ export class LazySandbox {
466
476
  throw new Error("Sandbox exec failed before a request was made");
467
477
  }
468
478
  async fetchExecStart(url, init) {
469
- const controller = new AbortController();
470
- const timeout = dntShim.setTimeout(() => controller.abort(), this.execStartTimeoutMs);
471
- try {
472
- return await fetch(url, { ...init, signal: controller.signal });
473
- }
474
- finally {
475
- clearTimeout(timeout);
476
- }
479
+ return fetchWithTimeout(url, this.execStartTimeoutMs, init);
480
+ }
481
+ async fetchControl(url, init = {}) {
482
+ return fetchWithTimeout(url, this.controlRequestTimeoutMs, init);
477
483
  }
478
484
  waitForExecStartRetry() {
479
485
  return new Promise((resolve) => dntShim.setTimeout(resolve, this.execStartRetryDelayMs));
@@ -523,6 +529,19 @@ function shouldReprovisionAfterExecStartFailure(error) {
523
529
  return typeof cause.code === "string" &&
524
530
  REPROVISIONABLE_EXEC_START_ERROR_CODES.has(cause.code);
525
531
  }
532
+ async function fetchWithTimeout(url, timeoutMs, init = {}) {
533
+ if (timeoutMs <= 0) {
534
+ return await fetch(url, init);
535
+ }
536
+ const controller = new AbortController();
537
+ const timeout = dntShim.setTimeout(() => controller.abort(), timeoutMs);
538
+ try {
539
+ return await fetch(url, { ...init, signal: controller.signal });
540
+ }
541
+ finally {
542
+ clearTimeout(timeout);
543
+ }
544
+ }
526
545
  function mapCommandJob(json) {
527
546
  return {
528
547
  id: json.id,
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.281";
1
+ export declare const VERSION = "0.1.282";
2
2
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.281";
3
+ export const VERSION = "0.1.282";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.281",
3
+ "version": "0.1.282",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.281",
3
+ "version": "0.1.282",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "workspace": [
@@ -10,7 +10,6 @@ import {
10
10
  resolveSandboxApiUrl,
11
11
  resolveSandboxAuthToken,
12
12
  type SandboxOptions,
13
- waitForSandboxReady,
14
13
  } from "./sandbox.js";
15
14
 
16
15
  export interface LazySandboxOptions extends SandboxOptions {
@@ -19,6 +18,7 @@ export interface LazySandboxOptions extends SandboxOptions {
19
18
  pollIntervalMs?: number;
20
19
  heartbeatIntervalMs?: number;
21
20
  heartbeatGraceMs?: number;
21
+ controlRequestTimeoutMs?: number;
22
22
  execStartTimeoutMs?: number;
23
23
  execStartMaxAttempts?: number;
24
24
  execStartRetryDelayMs?: number;
@@ -35,6 +35,7 @@ const DEFAULT_STARTUP_TIMEOUT_MS = 180_000;
35
35
  const DEFAULT_POLL_INTERVAL_MS = 2_000;
36
36
  const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;
37
37
  const DEFAULT_HEARTBEAT_GRACE_MS = 5_000;
38
+ const DEFAULT_CONTROL_REQUEST_TIMEOUT_MS = 15_000;
38
39
  const DEFAULT_EXEC_START_TIMEOUT_MS = 30_000;
39
40
  const DEFAULT_EXEC_START_MAX_ATTEMPTS = 3;
40
41
  const DEFAULT_EXEC_START_RETRY_DELAY_MS = 1_000;
@@ -54,6 +55,7 @@ export class LazySandbox {
54
55
  private readonly pollIntervalMs: number;
55
56
  private readonly heartbeatIntervalMs: number;
56
57
  private readonly heartbeatGraceMs: number;
58
+ private readonly controlRequestTimeoutMs: number;
57
59
  private readonly execStartTimeoutMs: number;
58
60
  private readonly execStartMaxAttempts: number;
59
61
  private readonly execStartRetryDelayMs: number;
@@ -82,6 +84,8 @@ export class LazySandbox {
82
84
  this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
83
85
  this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
84
86
  this.heartbeatGraceMs = options.heartbeatGraceMs ?? DEFAULT_HEARTBEAT_GRACE_MS;
87
+ this.controlRequestTimeoutMs = options.controlRequestTimeoutMs ??
88
+ DEFAULT_CONTROL_REQUEST_TIMEOUT_MS;
85
89
  this.execStartTimeoutMs = options.execStartTimeoutMs ?? DEFAULT_EXEC_START_TIMEOUT_MS;
86
90
  this.execStartMaxAttempts = options.execStartMaxAttempts ?? DEFAULT_EXEC_START_MAX_ATTEMPTS;
87
91
  this.execStartRetryDelayMs = options.execStartRetryDelayMs ??
@@ -175,9 +179,12 @@ export class LazySandbox {
175
179
  async readFile(path: string): Promise<string> {
176
180
  await this.touchSession();
177
181
 
178
- const res = await fetch(`${this.requireEndpoint()}/file?path=${encodeURIComponent(path)}`, {
179
- headers: this.authHeaders(),
180
- });
182
+ const res = await this.fetchControl(
183
+ `${this.requireEndpoint()}/file?path=${encodeURIComponent(path)}`,
184
+ {
185
+ headers: this.authHeaders(),
186
+ },
187
+ );
181
188
 
182
189
  if (!res.ok) {
183
190
  throw REQUEST_ERROR.create({ detail: `Read file failed: ${res.status} ${await res.text()}` });
@@ -189,7 +196,7 @@ export class LazySandbox {
189
196
  async writeFiles(files: Array<{ path: string; content: string }>): Promise<void> {
190
197
  await this.touchSession();
191
198
 
192
- const res = await fetch(`${this.requireEndpoint()}/files`, {
199
+ const res = await this.fetchControl(`${this.requireEndpoint()}/files`, {
193
200
  method: "POST",
194
201
  headers: this.jsonHeaders(),
195
202
  body: JSON.stringify({ files }),
@@ -206,7 +213,7 @@ export class LazySandbox {
206
213
  await this.touchSession();
207
214
  const endpoint = this.resolveRuntimeEndpoint();
208
215
 
209
- const res = await fetch(`${endpoint}/exec/jobs`, {
216
+ const res = await this.fetchControl(`${endpoint}/exec/jobs`, {
210
217
  method: "POST",
211
218
  headers: this.jsonHeaders(),
212
219
  body: JSON.stringify({ command, ...this.resolveExecOptions(options) }),
@@ -226,7 +233,7 @@ export class LazySandbox {
226
233
  async getCommandJob(jobId: string): Promise<CommandJob> {
227
234
  const endpoint = await this.resolveCommandJobEndpoint(jobId);
228
235
 
229
- const res = await fetch(`${endpoint}/exec/jobs/${jobId}`, {
236
+ const res = await this.fetchControl(`${endpoint}/exec/jobs/${jobId}`, {
230
237
  headers: this.authHeaders(),
231
238
  });
232
239
 
@@ -244,7 +251,7 @@ export class LazySandbox {
244
251
  async getCommandJobOutput(jobId: string): Promise<CommandJobOutput> {
245
252
  const endpoint = await this.resolveCommandJobEndpoint(jobId);
246
253
 
247
- const res = await fetch(`${endpoint}/exec/jobs/${jobId}/output`, {
254
+ const res = await this.fetchControl(`${endpoint}/exec/jobs/${jobId}/output`, {
248
255
  headers: this.authHeaders(),
249
256
  });
250
257
 
@@ -269,7 +276,7 @@ export class LazySandbox {
269
276
  async listCommandJobs(): Promise<CommandJob[]> {
270
277
  await this.ensure();
271
278
 
272
- const res = await fetch(`${this.requireEndpoint()}/exec/jobs`, {
279
+ const res = await this.fetchControl(`${this.requireEndpoint()}/exec/jobs`, {
273
280
  headers: this.authHeaders(),
274
281
  });
275
282
 
@@ -287,7 +294,7 @@ export class LazySandbox {
287
294
  async cancelCommandJob(jobId: string): Promise<CommandJob> {
288
295
  const endpoint = await this.resolveCommandJobEndpoint(jobId);
289
296
 
290
- const res = await fetch(`${endpoint}/exec/jobs/${jobId}/cancel`, {
297
+ const res = await this.fetchControl(`${endpoint}/exec/jobs/${jobId}/cancel`, {
291
298
  method: "POST",
292
299
  headers: this.authHeaders(),
293
300
  });
@@ -320,10 +327,13 @@ export class LazySandbox {
320
327
  }
321
328
 
322
329
  const promise = (async () => {
323
- const res = await fetch(`${this.apiUrl}/sandbox-sessions/${currentSessionId}/heartbeat`, {
324
- method: "POST",
325
- headers: this.authHeaders(),
326
- });
330
+ const res = await this.fetchControl(
331
+ `${this.apiUrl}/sandbox-sessions/${currentSessionId}/heartbeat`,
332
+ {
333
+ method: "POST",
334
+ headers: this.authHeaders(),
335
+ },
336
+ );
327
337
 
328
338
  if (!res.ok) {
329
339
  if (this.sessionId === currentSessionId) {
@@ -400,7 +410,7 @@ export class LazySandbox {
400
410
 
401
411
  private async bootstrapSession(): Promise<void> {
402
412
  const projectId = this.resolveProjectId();
403
- const res = await fetch(`${this.apiUrl}/sandbox-sessions`, {
413
+ const res = await this.fetchControl(`${this.apiUrl}/sandbox-sessions`, {
404
414
  method: "POST",
405
415
  headers: this.jsonHeaders(),
406
416
  body: JSON.stringify(projectId ? { project_id: projectId } : {}),
@@ -436,26 +446,35 @@ export class LazySandbox {
436
446
  return session.endpoint;
437
447
  }
438
448
 
439
- await waitForSandboxReady({
440
- apiUrl: this.apiUrl,
441
- id: session.id,
442
- authToken: this.authToken,
443
- maxWaitMs: this.startupTimeoutMs,
444
- pollIntervalMs: this.pollIntervalMs,
445
- });
449
+ return (await this.waitForReadySession(session.id)).endpoint;
450
+ }
446
451
 
447
- const res = await fetch(`${this.apiUrl}/sandbox-sessions/${session.id}`, {
448
- headers: this.authHeaders(),
449
- });
452
+ private async waitForReadySession(sessionId: string): Promise<SandboxSessionRecord> {
453
+ const start = Date.now();
450
454
 
451
- if (!res.ok) {
452
- throw REQUEST_ERROR.create({
453
- detail: `Failed to get sandbox: ${res.status} ${await res.text()}`,
455
+ while (Date.now() - start < this.startupTimeoutMs) {
456
+ await new Promise((resolve) => dntShim.setTimeout(resolve, this.pollIntervalMs));
457
+
458
+ const res = await this.fetchControl(`${this.apiUrl}/sandbox-sessions/${sessionId}`, {
459
+ headers: this.authHeaders(),
454
460
  });
461
+
462
+ if (!res.ok) {
463
+ continue;
464
+ }
465
+
466
+ const session = await res.json() as SandboxSessionRecord;
467
+ if (session.status === "running") {
468
+ return session;
469
+ }
470
+ if (session.status === "error" || session.status === "deleting") {
471
+ throw REQUEST_ERROR.create({
472
+ detail: `Sandbox failed to start: status=${session.status}`,
473
+ });
474
+ }
455
475
  }
456
476
 
457
- const nextSession = await res.json();
458
- return nextSession.endpoint;
477
+ throw REQUEST_ERROR.create({ detail: "Sandbox did not become ready within timeout" });
459
478
  }
460
479
 
461
480
  private async touchSession(): Promise<void> {
@@ -489,7 +508,7 @@ export class LazySandbox {
489
508
  }
490
509
 
491
510
  private async deleteSession(sessionId: string): Promise<void> {
492
- await fetch(`${this.apiUrl}/sandbox-sessions/${sessionId}`, {
511
+ await this.fetchControl(`${this.apiUrl}/sandbox-sessions/${sessionId}`, {
493
512
  method: "DELETE",
494
513
  headers: this.authHeaders(),
495
514
  });
@@ -584,14 +603,11 @@ export class LazySandbox {
584
603
  }
585
604
 
586
605
  private async fetchExecStart(url: string, init: RequestInit): Promise<Response> {
587
- const controller = new AbortController();
588
- const timeout = dntShim.setTimeout(() => controller.abort(), this.execStartTimeoutMs);
606
+ return fetchWithTimeout(url, this.execStartTimeoutMs, init);
607
+ }
589
608
 
590
- try {
591
- return await fetch(url, { ...init, signal: controller.signal });
592
- } finally {
593
- clearTimeout(timeout);
594
- }
609
+ private async fetchControl(url: string, init: RequestInit = {}): Promise<Response> {
610
+ return fetchWithTimeout(url, this.controlRequestTimeoutMs, init);
595
611
  }
596
612
 
597
613
  private waitForExecStartRetry(): Promise<void> {
@@ -654,6 +670,25 @@ function shouldReprovisionAfterExecStartFailure(error: unknown): boolean {
654
670
  REPROVISIONABLE_EXEC_START_ERROR_CODES.has(cause.code);
655
671
  }
656
672
 
673
+ async function fetchWithTimeout(
674
+ url: string,
675
+ timeoutMs: number,
676
+ init: RequestInit = {},
677
+ ): Promise<Response> {
678
+ if (timeoutMs <= 0) {
679
+ return await fetch(url, init);
680
+ }
681
+
682
+ const controller = new AbortController();
683
+ const timeout = dntShim.setTimeout(() => controller.abort(), timeoutMs);
684
+
685
+ try {
686
+ return await fetch(url, { ...init, signal: controller.signal });
687
+ } finally {
688
+ clearTimeout(timeout);
689
+ }
690
+ }
691
+
657
692
  function mapCommandJob(json: Record<string, unknown>): CommandJob {
658
693
  return {
659
694
  id: json.id as string,
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.281";
3
+ export const VERSION = "0.1.282";