@blaxel/core 0.3.23 → 0.3.24
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/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/common/settings.js +2 -2
- package/dist/cjs/sandbox/sandbox.js +69 -4
- package/dist/cjs/types/sandbox/sandbox.d.ts +41 -5
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/common/settings.js +2 -2
- package/dist/cjs-browser/sandbox/sandbox.js +69 -4
- package/dist/cjs-browser/types/sandbox/sandbox.d.ts +41 -5
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/common/settings.js +2 -2
- package/dist/esm/sandbox/sandbox.js +66 -3
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/common/settings.js +2 -2
- package/dist/esm-browser/sandbox/sandbox.js +66 -3
- package/package.json +1 -1
|
@@ -24,8 +24,8 @@ function missingCredentialsMessage() {
|
|
|
24
24
|
return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
|
|
25
25
|
}
|
|
26
26
|
// Build info - these placeholders are replaced at build time by build:replace-imports
|
|
27
|
-
const BUILD_VERSION = "0.3.
|
|
28
|
-
const BUILD_COMMIT = "
|
|
27
|
+
const BUILD_VERSION = "0.3.24";
|
|
28
|
+
const BUILD_COMMIT = "d3dcd1599047a3cadaacac6805f29e210e57aaad";
|
|
29
29
|
const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
|
|
30
30
|
const BLAXEL_API_VERSION = "2026-09-22";
|
|
31
31
|
// Bun < 1.3.11 never sends connection-level WINDOW_UPDATE: the pooled h2
|
|
@@ -58,6 +58,62 @@ const ARCHIVE_ENTRY_MAX_WAIT_MS = 30_000;
|
|
|
58
58
|
const CREATE_GATEWAY_TIMEOUT_MAX_WAIT_MS = 120_000;
|
|
59
59
|
const CREATE_GATEWAY_TIMEOUT_BASE_POLL_MS = 1_000;
|
|
60
60
|
const CREATE_GATEWAY_TIMEOUT_MAX_POLL_MS = 5_000;
|
|
61
|
+
// A creation deadline the caller sets per request through an undocumented
|
|
62
|
+
// header; it can only shorten the control plane's default. Capped below the
|
|
63
|
+
// edge's 60s origin-read timeout so the control plane's 408 always reaches the
|
|
64
|
+
// client instead of being masked by an edge 504.
|
|
65
|
+
const CREATION_TIMEOUT_HEADER = "X-Blaxel-Creation-Timeout";
|
|
66
|
+
export const MAX_CREATION_TIMEOUT_SECONDS = 50;
|
|
67
|
+
/**
|
|
68
|
+
* Thrown by SandboxInstance.create / createIfNotExists when the sandbox was
|
|
69
|
+
* not ready within the creation deadline. The control plane has released the
|
|
70
|
+
* sandbox, so the name is free again and the creation can simply be started
|
|
71
|
+
* over. Catch it with `err instanceof SandboxCreationTimeoutError` or
|
|
72
|
+
* `isCreationTimeoutError(err)`.
|
|
73
|
+
*/
|
|
74
|
+
export class SandboxCreationTimeoutError extends Error {
|
|
75
|
+
sandboxName;
|
|
76
|
+
timeout;
|
|
77
|
+
code = "CREATION_TIMEOUT";
|
|
78
|
+
status = 408;
|
|
79
|
+
/** Raw error body returned by the control plane. */
|
|
80
|
+
data;
|
|
81
|
+
constructor(
|
|
82
|
+
/** Name of the sandbox that was being created, if one was requested. */
|
|
83
|
+
sandboxName,
|
|
84
|
+
/** The `timeout` option of the call, in seconds; undefined when the control plane's default applied. */
|
|
85
|
+
timeout, data) {
|
|
86
|
+
const detail = typeof data === "object" && data !== null && typeof data.message === "string"
|
|
87
|
+
? data.message
|
|
88
|
+
: undefined;
|
|
89
|
+
const target = sandboxName ? `Sandbox ${sandboxName}` : "Sandbox";
|
|
90
|
+
const deadline = timeout !== undefined ? ` within ${timeout}s` : " within the creation deadline";
|
|
91
|
+
super(`${target} was not ready${deadline}; the creation was cancelled.${detail ? ` ${detail}` : ""}`);
|
|
92
|
+
this.sandboxName = sandboxName;
|
|
93
|
+
this.timeout = timeout;
|
|
94
|
+
this.name = "SandboxCreationTimeoutError";
|
|
95
|
+
this.data = data;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** True when `err` is a sandbox creation timeout (408 CREATION_TIMEOUT). */
|
|
99
|
+
export function isCreationTimeoutError(err) {
|
|
100
|
+
return err instanceof SandboxCreationTimeoutError;
|
|
101
|
+
}
|
|
102
|
+
const isCreationTimeoutResponse = (status, e) => {
|
|
103
|
+
if (status === 408)
|
|
104
|
+
return true;
|
|
105
|
+
if (typeof e !== "object" || e === null)
|
|
106
|
+
return false;
|
|
107
|
+
return e.code === "CREATION_TIMEOUT";
|
|
108
|
+
};
|
|
109
|
+
function validateCreateOptions({ timeout }) {
|
|
110
|
+
if (timeout === undefined)
|
|
111
|
+
return {};
|
|
112
|
+
if (!Number.isInteger(timeout) || timeout < 1 || timeout > MAX_CREATION_TIMEOUT_SECONDS) {
|
|
113
|
+
throw new Error(`SandboxInstance.create: 'timeout' must be a whole number of seconds between 1 and ${MAX_CREATION_TIMEOUT_SECONDS}, got ${timeout}.`);
|
|
114
|
+
}
|
|
115
|
+
return { timeout };
|
|
116
|
+
}
|
|
61
117
|
const isSandboxNotFound = (e) => {
|
|
62
118
|
if (typeof e !== "object" || e === null)
|
|
63
119
|
return false;
|
|
@@ -252,7 +308,9 @@ export class SandboxInstance {
|
|
|
252
308
|
logger.warn("⚠️ Warning: sandbox.wait() is deprecated. You don't need to wait for the sandbox to be deployed anymore.");
|
|
253
309
|
return this;
|
|
254
310
|
}
|
|
255
|
-
static async create(sandbox,
|
|
311
|
+
static async create(sandbox, options = {}) {
|
|
312
|
+
const { safe = false, createIfNotExist = false } = options;
|
|
313
|
+
const { timeout } = validateCreateOptions(options);
|
|
256
314
|
// No client-side default name: when the caller omits a name we send the
|
|
257
315
|
// creation without metadata.name so the server can assign one and unnamed
|
|
258
316
|
// creations become eligible for warm sandbox pools (ENG-3931).
|
|
@@ -334,16 +392,21 @@ export class SandboxInstance {
|
|
|
334
392
|
if (edgeDomain && !settings.disableH2) {
|
|
335
393
|
import("../common/h2pool.js").then(({ h2Pool }) => h2Pool.warm(edgeDomain)).catch(() => { });
|
|
336
394
|
}
|
|
395
|
+
const headers = timeout !== undefined ? { [CREATION_TIMEOUT_HEADER]: String(timeout) } : undefined;
|
|
337
396
|
const [createResult, h2Session] = await Promise.all([
|
|
338
397
|
createSandbox({
|
|
339
398
|
body: sandbox,
|
|
340
399
|
query: createIfNotExist ? { createIfNotExist } : undefined,
|
|
400
|
+
headers,
|
|
341
401
|
}),
|
|
342
402
|
edgeDomain && !settings.disableH2 ? import("../common/h2pool.js").then(({ h2Pool }) => h2Pool.get(edgeDomain)).catch(() => null) : Promise.resolve(null),
|
|
343
403
|
]);
|
|
344
404
|
let data = createResult.data;
|
|
345
405
|
if (createResult.error !== undefined) {
|
|
346
406
|
const name = sandbox.metadata.name;
|
|
407
|
+
if (isCreationTimeoutResponse(createResult.response.status, createResult.error)) {
|
|
408
|
+
throw new SandboxCreationTimeoutError(name, timeout, createResult.error);
|
|
409
|
+
}
|
|
347
410
|
if (createResult.response.status === 504 && name) {
|
|
348
411
|
// The edge gave up on the connection but the creation is still running
|
|
349
412
|
// server-side; wait for the record instead of failing (ENG-3662).
|
|
@@ -578,7 +641,7 @@ export class SandboxInstance {
|
|
|
578
641
|
const instance = new SandboxInstance(data);
|
|
579
642
|
return SandboxInstance.attachH2Session(instance);
|
|
580
643
|
}
|
|
581
|
-
static async createIfNotExists(sandbox) {
|
|
644
|
+
static async createIfNotExists(sandbox, options = {}) {
|
|
582
645
|
const ATTEMPTS = 3;
|
|
583
646
|
let lastStatus = "unknown";
|
|
584
647
|
// The 'vanished' window (create 409s while get 404s) is driven by the same
|
|
@@ -590,7 +653,7 @@ export class SandboxInstance {
|
|
|
590
653
|
for (let i = 0; i < ATTEMPTS; ++i) {
|
|
591
654
|
const finalAttempt = i === ATTEMPTS - 1;
|
|
592
655
|
try {
|
|
593
|
-
return await this.create(sandbox, { createIfNotExist: true });
|
|
656
|
+
return await this.create(sandbox, { ...options, createIfNotExist: true });
|
|
594
657
|
}
|
|
595
658
|
catch (e) {
|
|
596
659
|
if (typeof e === "object" && e !== null && "code" in e && (e.code === 409 || e.code === 'SANDBOX_ALREADY_EXISTS')) {
|