@blaxel/core 0.3.23 → 0.3.24-preview.303

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.
@@ -30,8 +30,8 @@ function missingCredentialsMessage() {
30
30
  return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
31
31
  }
32
32
  // Build info - these placeholders are replaced at build time by build:replace-imports
33
- const BUILD_VERSION = "0.3.23";
34
- const BUILD_COMMIT = "3f7319f88bf305c481c80932c29c7513c85b543d";
33
+ const BUILD_VERSION = "0.3.24-preview.303";
34
+ const BUILD_COMMIT = "d3dcd1599047a3cadaacac6805f29e210e57aaad";
35
35
  const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
36
36
  const BLAXEL_API_VERSION = "2026-09-22";
37
37
  // Bun < 1.3.11 never sends connection-level WINDOW_UPDATE: the pooled h2
@@ -33,7 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.SandboxInstance = void 0;
36
+ exports.SandboxInstance = exports.SandboxCreationTimeoutError = exports.MAX_CREATION_TIMEOUT_SECONDS = void 0;
37
+ exports.isCreationTimeoutError = isCreationTimeoutError;
37
38
  const index_js_1 = require("../client/index.js");
38
39
  const logger_js_1 = require("../common/logger.js");
39
40
  const transient_retry_js_1 = require("../common/transient-retry.js");
@@ -94,6 +95,63 @@ const ARCHIVE_ENTRY_MAX_WAIT_MS = 30_000;
94
95
  const CREATE_GATEWAY_TIMEOUT_MAX_WAIT_MS = 120_000;
95
96
  const CREATE_GATEWAY_TIMEOUT_BASE_POLL_MS = 1_000;
96
97
  const CREATE_GATEWAY_TIMEOUT_MAX_POLL_MS = 5_000;
98
+ // A creation deadline the caller sets per request through an undocumented
99
+ // header; it can only shorten the control plane's default. Capped below the
100
+ // edge's 60s origin-read timeout so the control plane's 408 always reaches the
101
+ // client instead of being masked by an edge 504.
102
+ const CREATION_TIMEOUT_HEADER = "X-Blaxel-Creation-Timeout";
103
+ exports.MAX_CREATION_TIMEOUT_SECONDS = 50;
104
+ /**
105
+ * Thrown by SandboxInstance.create / createIfNotExists when the sandbox was
106
+ * not ready within the creation deadline. The control plane has released the
107
+ * sandbox, so the name is free again and the creation can simply be started
108
+ * over. Catch it with `err instanceof SandboxCreationTimeoutError` or
109
+ * `isCreationTimeoutError(err)`.
110
+ */
111
+ class SandboxCreationTimeoutError extends Error {
112
+ sandboxName;
113
+ timeout;
114
+ code = "CREATION_TIMEOUT";
115
+ status = 408;
116
+ /** Raw error body returned by the control plane. */
117
+ data;
118
+ constructor(
119
+ /** Name of the sandbox that was being created, if one was requested. */
120
+ sandboxName,
121
+ /** The `timeout` option of the call, in seconds; undefined when the control plane's default applied. */
122
+ timeout, data) {
123
+ const detail = typeof data === "object" && data !== null && typeof data.message === "string"
124
+ ? data.message
125
+ : undefined;
126
+ const target = sandboxName ? `Sandbox ${sandboxName}` : "Sandbox";
127
+ const deadline = timeout !== undefined ? ` within ${timeout}s` : " within the creation deadline";
128
+ super(`${target} was not ready${deadline}; the creation was cancelled.${detail ? ` ${detail}` : ""}`);
129
+ this.sandboxName = sandboxName;
130
+ this.timeout = timeout;
131
+ this.name = "SandboxCreationTimeoutError";
132
+ this.data = data;
133
+ }
134
+ }
135
+ exports.SandboxCreationTimeoutError = SandboxCreationTimeoutError;
136
+ /** True when `err` is a sandbox creation timeout (408 CREATION_TIMEOUT). */
137
+ function isCreationTimeoutError(err) {
138
+ return err instanceof SandboxCreationTimeoutError;
139
+ }
140
+ const isCreationTimeoutResponse = (status, e) => {
141
+ if (status === 408)
142
+ return true;
143
+ if (typeof e !== "object" || e === null)
144
+ return false;
145
+ return e.code === "CREATION_TIMEOUT";
146
+ };
147
+ function validateCreateOptions({ timeout }) {
148
+ if (timeout === undefined)
149
+ return {};
150
+ if (!Number.isInteger(timeout) || timeout < 1 || timeout > exports.MAX_CREATION_TIMEOUT_SECONDS) {
151
+ throw new Error(`SandboxInstance.create: 'timeout' must be a whole number of seconds between 1 and ${exports.MAX_CREATION_TIMEOUT_SECONDS}, got ${timeout}.`);
152
+ }
153
+ return { timeout };
154
+ }
97
155
  const isSandboxNotFound = (e) => {
98
156
  if (typeof e !== "object" || e === null)
99
157
  return false;
@@ -288,7 +346,9 @@ class SandboxInstance {
288
346
  logger_js_1.logger.warn("⚠️ Warning: sandbox.wait() is deprecated. You don't need to wait for the sandbox to be deployed anymore.");
289
347
  return this;
290
348
  }
291
- static async create(sandbox, { safe = false, createIfNotExist = false } = {}) {
349
+ static async create(sandbox, options = {}) {
350
+ const { safe = false, createIfNotExist = false } = options;
351
+ const { timeout } = validateCreateOptions(options);
292
352
  // No client-side default name: when the caller omits a name we send the
293
353
  // creation without metadata.name so the server can assign one and unnamed
294
354
  // creations become eligible for warm sandbox pools (ENG-3931).
@@ -370,16 +430,21 @@ class SandboxInstance {
370
430
  if (edgeDomain && !settings_js_1.settings.disableH2) {
371
431
  Promise.resolve().then(() => __importStar(require("../common/h2pool.js"))).then(({ h2Pool }) => h2Pool.warm(edgeDomain)).catch(() => { });
372
432
  }
433
+ const headers = timeout !== undefined ? { [CREATION_TIMEOUT_HEADER]: String(timeout) } : undefined;
373
434
  const [createResult, h2Session] = await Promise.all([
374
435
  (0, index_js_1.createSandbox)({
375
436
  body: sandbox,
376
437
  query: createIfNotExist ? { createIfNotExist } : undefined,
438
+ headers,
377
439
  }),
378
440
  edgeDomain && !settings_js_1.settings.disableH2 ? Promise.resolve().then(() => __importStar(require("../common/h2pool.js"))).then(({ h2Pool }) => h2Pool.get(edgeDomain)).catch(() => null) : Promise.resolve(null),
379
441
  ]);
380
442
  let data = createResult.data;
381
443
  if (createResult.error !== undefined) {
382
444
  const name = sandbox.metadata.name;
445
+ if (isCreationTimeoutResponse(createResult.response.status, createResult.error)) {
446
+ throw new SandboxCreationTimeoutError(name, timeout, createResult.error);
447
+ }
383
448
  if (createResult.response.status === 504 && name) {
384
449
  // The edge gave up on the connection but the creation is still running
385
450
  // server-side; wait for the record instead of failing (ENG-3662).
@@ -614,7 +679,7 @@ class SandboxInstance {
614
679
  const instance = new SandboxInstance(data);
615
680
  return SandboxInstance.attachH2Session(instance);
616
681
  }
617
- static async createIfNotExists(sandbox) {
682
+ static async createIfNotExists(sandbox, options = {}) {
618
683
  const ATTEMPTS = 3;
619
684
  let lastStatus = "unknown";
620
685
  // The 'vanished' window (create 409s while get 404s) is driven by the same
@@ -626,7 +691,7 @@ class SandboxInstance {
626
691
  for (let i = 0; i < ATTEMPTS; ++i) {
627
692
  const finalAttempt = i === ATTEMPTS - 1;
628
693
  try {
629
- return await this.create(sandbox, { createIfNotExist: true });
694
+ return await this.create(sandbox, { ...options, createIfNotExist: true });
630
695
  }
631
696
  catch (e) {
632
697
  if (typeof e === "object" && e !== null && "code" in e && (e.code === 409 || e.code === 'SANDBOX_ALREADY_EXISTS')) {
@@ -46,6 +46,45 @@ export type SandboxArchiveOptions = {
46
46
  /** Milliseconds between two reads of the sandbox. Defaults to 2 seconds. */
47
47
  interval?: number;
48
48
  };
49
+ export declare const MAX_CREATION_TIMEOUT_SECONDS = 50;
50
+ /** Options of SandboxInstance.create / createIfNotExists. */
51
+ export type SandboxCreateOptions = {
52
+ /** Check the sandbox answers (fs.ls) and delete it if it does not. Defaults to false. */
53
+ safe?: boolean;
54
+ /** Return the existing sandbox instead of failing when the name is taken. */
55
+ createIfNotExist?: boolean;
56
+ /**
57
+ * Give up on the creation after this many seconds (whole number, 1 to
58
+ * MAX_CREATION_TIMEOUT_SECONDS). The control plane releases the sandbox
59
+ * and the call rejects with a SandboxCreationTimeoutError. Unset, the
60
+ * control plane's default deadline applies.
61
+ */
62
+ timeout?: number;
63
+ };
64
+ /**
65
+ * Thrown by SandboxInstance.create / createIfNotExists when the sandbox was
66
+ * not ready within the creation deadline. The control plane has released the
67
+ * sandbox, so the name is free again and the creation can simply be started
68
+ * over. Catch it with `err instanceof SandboxCreationTimeoutError` or
69
+ * `isCreationTimeoutError(err)`.
70
+ */
71
+ export declare class SandboxCreationTimeoutError extends Error {
72
+ /** Name of the sandbox that was being created, if one was requested. */
73
+ readonly sandboxName: string | undefined;
74
+ /** The `timeout` option of the call, in seconds; undefined when the control plane's default applied. */
75
+ readonly timeout: number | undefined;
76
+ readonly code = "CREATION_TIMEOUT";
77
+ readonly status = 408;
78
+ /** Raw error body returned by the control plane. */
79
+ readonly data: unknown;
80
+ constructor(
81
+ /** Name of the sandbox that was being created, if one was requested. */
82
+ sandboxName: string | undefined,
83
+ /** The `timeout` option of the call, in seconds; undefined when the control plane's default applied. */
84
+ timeout: number | undefined, data: unknown);
85
+ }
86
+ /** True when `err` is a sandbox creation timeout (408 CREATION_TIMEOUT). */
87
+ export declare function isCreationTimeoutError(err: unknown): err is SandboxCreationTimeoutError;
49
88
  export declare class SandboxInstance {
50
89
  private sandbox;
51
90
  fs: SandboxFileSystem;
@@ -142,10 +181,7 @@ export declare class SandboxInstance {
142
181
  maxWait?: number;
143
182
  interval?: number;
144
183
  }): Promise<this>;
145
- static create(sandbox?: SandboxModel | SandboxCreateConfiguration, { safe, createIfNotExist }?: {
146
- safe?: boolean;
147
- createIfNotExist?: boolean;
148
- }): Promise<SandboxInstance>;
184
+ static create(sandbox?: SandboxModel | SandboxCreateConfiguration, options?: SandboxCreateOptions): Promise<SandboxInstance>;
149
185
  /**
150
186
  * Archive a sandbox: keep its filesystem, stop the sandbox.
151
187
  *
@@ -224,7 +260,7 @@ export declare class SandboxInstance {
224
260
  static updateTtl(sandboxName: string, ttl: string | null): Promise<SandboxInstance>;
225
261
  static updateLifecycle(sandboxName: string, lifecycle: SandboxLifecycle | null): Promise<SandboxInstance>;
226
262
  static updateNetwork(sandboxName: string, network: SandboxUpdateNetwork): Promise<SandboxInstance>;
227
- static createIfNotExists(sandbox: SandboxModel | SandboxCreateConfiguration): Promise<SandboxInstance>;
263
+ static createIfNotExists(sandbox: SandboxModel | SandboxCreateConfiguration, options?: Omit<SandboxCreateOptions, "createIfNotExist">): Promise<SandboxInstance>;
228
264
  private static waitAfterCreateGatewayTimeout;
229
265
  private static waitWhileSandboxDying;
230
266
  static fromSession(session: SessionWithToken): Promise<SandboxInstance>;