@blaxel/core 0.3.26-preview.309 → 0.3.26-preview.311

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.
Files changed (35) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/common/error-types.js +133 -0
  3. package/dist/cjs/common/settings.js +2 -2
  4. package/dist/cjs/index.js +1 -0
  5. package/dist/cjs/sandbox/index.js +1 -0
  6. package/dist/cjs/sandbox/preview.js +104 -1
  7. package/dist/cjs/types/common/error-types.d.ts +159 -0
  8. package/dist/cjs/types/index.d.ts +1 -0
  9. package/dist/cjs/types/sandbox/index.d.ts +1 -0
  10. package/dist/cjs/types/sandbox/preview.d.ts +12 -3
  11. package/dist/cjs/types/sandbox/types.d.ts +7 -0
  12. package/dist/cjs-browser/.tsbuildinfo +1 -1
  13. package/dist/cjs-browser/common/error-types.js +133 -0
  14. package/dist/cjs-browser/common/settings.js +2 -2
  15. package/dist/cjs-browser/index.js +1 -0
  16. package/dist/cjs-browser/sandbox/index.js +1 -0
  17. package/dist/cjs-browser/sandbox/preview.js +104 -1
  18. package/dist/cjs-browser/types/common/error-types.d.ts +159 -0
  19. package/dist/cjs-browser/types/index.d.ts +1 -0
  20. package/dist/cjs-browser/types/sandbox/index.d.ts +1 -0
  21. package/dist/cjs-browser/types/sandbox/preview.d.ts +12 -3
  22. package/dist/cjs-browser/types/sandbox/types.d.ts +7 -0
  23. package/dist/esm/.tsbuildinfo +1 -1
  24. package/dist/esm/common/error-types.js +126 -0
  25. package/dist/esm/common/settings.js +2 -2
  26. package/dist/esm/index.js +1 -0
  27. package/dist/esm/sandbox/index.js +1 -0
  28. package/dist/esm/sandbox/preview.js +104 -1
  29. package/dist/esm-browser/.tsbuildinfo +1 -1
  30. package/dist/esm-browser/common/error-types.js +126 -0
  31. package/dist/esm-browser/common/settings.js +2 -2
  32. package/dist/esm-browser/index.js +1 -0
  33. package/dist/esm-browser/sandbox/index.js +1 -0
  34. package/dist/esm-browser/sandbox/preview.js +104 -1
  35. package/package.json +1 -1
@@ -0,0 +1,126 @@
1
+ import { ResponseError } from "../sandbox/action.js";
2
+ import { SandboxCreationTimeoutError } from "../sandbox/sandbox.js";
3
+ function readProperty(value, key) {
4
+ try {
5
+ return typeof value === "object" && value !== null
6
+ ? value[key]
7
+ : undefined;
8
+ }
9
+ catch {
10
+ // Catch hostile getters and revoked proxies, including in custom transports.
11
+ return undefined;
12
+ }
13
+ }
14
+ const isCode = (value) => (typeof value === "string" && value.length > 0) ||
15
+ (typeof value === "number" && Number.isFinite(value));
16
+ const isStatus = (value) => typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599;
17
+ /**
18
+ * True only for a value that carries a Blaxel error code or HTTP status in a
19
+ * shape the SDK throws for API failures: a `ResponseError` (including
20
+ * `SandboxGatewayError`), a `SandboxCreationTimeoutError`, or a raw control-plane
21
+ * error body (`{ error, code }`, `{ code, message, status_code }` or
22
+ * `{ error: { code, message, status } }`). Plain `Error`s (including network
23
+ * errors with a `code` such as `ECONNRESET`), strings, raw HTML/text bodies, empty
24
+ * bodies and a bare `{ error: string }` are false: they carry neither. Matching
25
+ * `ResponseError` uses `instanceof`, so an error thrown by a second copy of
26
+ * `@blaxel/core` does not match. Does not wrap or mutate the value. Never throws.
27
+ * Like `is_blaxel_error` in the Python SDK, it is false for plain exceptions and
28
+ * strings.
29
+ */
30
+ export function isBlaxelError(err) {
31
+ try {
32
+ if (err instanceof Error) {
33
+ return err instanceof ResponseError || err instanceof SandboxCreationTimeoutError;
34
+ }
35
+ if (typeof err !== "object" || err === null || Array.isArray(err))
36
+ return false;
37
+ const error = readProperty(err, "error");
38
+ if (typeof error === "string")
39
+ return isStatus(readProperty(err, "code"));
40
+ if (typeof readProperty(err, "code") === "string" &&
41
+ typeof readProperty(err, "message") === "string" &&
42
+ isStatus(readProperty(err, "status_code")))
43
+ return true;
44
+ return (typeof error === "object" && error !== null && !Array.isArray(error) &&
45
+ typeof readProperty(error, "code") === "string" &&
46
+ typeof readProperty(error, "message") === "string" &&
47
+ isStatus(readProperty(error, "status")));
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ // Fixed-depth inspection: ResponseError carries `error`/`data`; gateway bodies
54
+ // nest their fields once more in `error`. No recursion, including on cyclic input.
55
+ function errorCandidates(err) {
56
+ const bodies = [err, readProperty(err, "error"), readProperty(err, "data")];
57
+ return bodies.flatMap((body) => [body, readProperty(body, "error")]);
58
+ }
59
+ function readHeader(err, name) {
60
+ try {
61
+ const headers = readProperty(readProperty(err, "response"), "headers");
62
+ const get = readProperty(headers, "get");
63
+ if (typeof get !== "function")
64
+ return undefined;
65
+ const value = Reflect.apply(get, headers, [name]);
66
+ return typeof value === "string" && value.length > 0 ? value : undefined;
67
+ }
68
+ catch {
69
+ return undefined;
70
+ }
71
+ }
72
+ /** Read a code from the existing error/body, then X-Blaxel-Error-Code. Never throws. */
73
+ export function getBlaxelErrorCode(err) {
74
+ for (const candidate of errorCandidates(err)) {
75
+ const code = readProperty(candidate, "code");
76
+ if (isCode(code))
77
+ return code;
78
+ }
79
+ return readHeader(err, "x-blaxel-error-code");
80
+ }
81
+ /**
82
+ * Read HTTP status from the existing error/response, then the body (`status`,
83
+ * `status_code`, or a numeric `code`). Raw text and plain Error have none.
84
+ * Never throws.
85
+ */
86
+ export function getBlaxelErrorStatus(err) {
87
+ const status = readProperty(err, "status");
88
+ if (isStatus(status))
89
+ return status;
90
+ const responseStatus = readProperty(readProperty(err, "response"), "status");
91
+ if (isStatus(responseStatus))
92
+ return responseStatus;
93
+ for (const candidate of errorCandidates(err)) {
94
+ for (const key of ["status", "status_code", "code"]) {
95
+ const value = readProperty(candidate, key);
96
+ if (isStatus(value))
97
+ return value;
98
+ }
99
+ }
100
+ return undefined;
101
+ }
102
+ /**
103
+ * Read the existing Error.message, body message/error text, or raw text/HTML.
104
+ * Returns undefined for an empty/unrecognized body. Never throws.
105
+ */
106
+ export function getBlaxelErrorMessage(err) {
107
+ for (const candidate of errorCandidates(err)) {
108
+ const message = typeof candidate === "string" ? candidate : readProperty(candidate, "message");
109
+ if (typeof message === "string" && message.length > 0)
110
+ return message;
111
+ }
112
+ return undefined;
113
+ }
114
+ /**
115
+ * Read X-Cf-Request-Id, X-Amz-Cf-Id, then CF-Ray from a retained response.
116
+ * Raw control-plane bodies and SandboxCreationTimeoutError do not retain
117
+ * response headers, so their request ID is unavailable. Never throws.
118
+ */
119
+ export function getBlaxelErrorRequestId(err) {
120
+ for (const name of ["x-cf-request-id", "x-amz-cf-id", "cf-ray"]) {
121
+ const value = readHeader(err, name);
122
+ if (value !== undefined)
123
+ return value;
124
+ }
125
+ return undefined;
126
+ }
@@ -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.26-preview.309";
28
- const BUILD_COMMIT = "314ba709f1a271c3a30cea714a3b97c8e8efb144";
27
+ const BUILD_VERSION = "0.3.26-preview.311";
28
+ const BUILD_COMMIT = "a62496fa4537136926770cdd0d6e48ceb8506491";
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
@@ -7,6 +7,7 @@ export * from "./common/env.js";
7
7
  export * from "./common/h2-runtime.js";
8
8
  export * from "./common/node.js";
9
9
  export * from "./common/errors.js";
10
+ export * from "./common/error-types.js";
10
11
  export * from "./common/internal.js";
11
12
  export * from "./common/logger.js";
12
13
  export * from "./common/pagination.js";
@@ -2,6 +2,7 @@ export {
2
2
  /* Export SDK functions */
3
3
  deleteFilesystemByPath, deleteNetworkProcessByPidMonitor, deleteProcessByIdentifier, deleteProcessByIdentifierKill, getFilesystemByPath, getNetworkProcessByPidPorts, putCodegenFastapplyByPath, getCodegenRerankingByPath, getProcess, getProcessByIdentifier, getProcessByIdentifierLogs, getProcessByIdentifierLogsStream, postNetworkProcessByPidMonitor, postProcess, putFilesystemByPath } from "./client/index.js";
4
4
  export { ResponseError, SandboxGatewayError, isGatewayError, isGatewayTimeout } from "./action.js";
5
+ export * from "../common/error-types.js";
5
6
  export * from "./filesystem/index.js";
6
7
  export * from "./codegen/index.js";
7
8
  export { SandboxDrive } from "./drive/index.js";
@@ -1,9 +1,46 @@
1
1
  import { createSandboxPreview, createSandboxPreviewToken, deleteSandboxPreview, deleteSandboxPreviewToken, getSandboxPreview, listSandboxPreviews, listSandboxPreviewTokens } from "../client/index.js";
2
+ function isShorthand(preview) {
3
+ return !("metadata" in preview || "spec" in preview);
4
+ }
5
+ function normalizePreview(preview) {
6
+ if (!isShorthand(preview))
7
+ return preview;
8
+ if (!Number.isInteger(preview.port) || preview.port < 1 || preview.port > 65535) {
9
+ throw new RangeError("Preview port must be an integer between 1 and 65535");
10
+ }
11
+ if (preview.name !== undefined && (typeof preview.name !== "string" || preview.name.length === 0)) {
12
+ throw new RangeError("Preview name must be a nonempty string");
13
+ }
14
+ if (preview.public !== undefined && typeof preview.public !== "boolean") {
15
+ throw new RangeError("Preview public must be a boolean");
16
+ }
17
+ return {
18
+ metadata: { name: preview.name ?? `preview-${preview.port}` },
19
+ spec: { port: preview.port, public: preview.public ?? false },
20
+ };
21
+ }
22
+ // Explicit HTTP status takes precedence over a legacy numeric service code.
23
+ function httpStatus(error) {
24
+ if (typeof error !== "object" || error === null)
25
+ return undefined;
26
+ const fields = error;
27
+ const response = fields.response;
28
+ const responseStatus = typeof response === "object" && response !== null && "status" in response
29
+ ? response.status : undefined;
30
+ for (const status of [fields.status, fields.statusCode, responseStatus, fields.code]) {
31
+ if (typeof status === "number")
32
+ return status;
33
+ }
34
+ return undefined;
35
+ }
2
36
  export class SandboxPreviewToken {
3
37
  previewToken;
4
38
  constructor(previewToken) {
5
39
  this.previewToken = previewToken;
6
40
  }
41
+ get name() {
42
+ return this.previewToken.metadata?.name ?? "";
43
+ }
7
44
  get value() {
8
45
  return this.previewToken.spec.token ?? "";
9
46
  }
@@ -43,6 +80,49 @@ export class SandboxPreviewTokens {
43
80
  });
44
81
  return new SandboxPreviewToken(data);
45
82
  }
83
+ /**
84
+ * Reuse the latest token within the requested expiry ceiling, or mint one.
85
+ * minValidity is in milliseconds. Concurrent callers may create separate tokens.
86
+ */
87
+ async createIfExpired(expiresAt, minValidity = 3_600_000) {
88
+ const now = Date.now();
89
+ if (!Number.isFinite(minValidity) || minValidity < 0) {
90
+ throw new RangeError("minValidity must be finite and non-negative");
91
+ }
92
+ const requestedExpiry = expiresAt ?? new Date(now + 86_400_000);
93
+ const ceiling = requestedExpiry instanceof Date ? requestedExpiry.getTime() : NaN;
94
+ if (!Number.isFinite(ceiling) || ceiling <= now || ceiling < now + minValidity) {
95
+ throw new RangeError("expiresAt must be a valid future date at least minValidity from now");
96
+ }
97
+ if (this.preview.spec?.public === true) {
98
+ throw new Error("Cannot create or reuse a token for a public preview");
99
+ }
100
+ const { data } = await listSandboxPreviewTokens({
101
+ path: {
102
+ sandboxName: this.resourceName,
103
+ previewName: this.previewName,
104
+ },
105
+ throwOnError: true,
106
+ });
107
+ if (!Array.isArray(data))
108
+ throw new Error("Failed to list preview tokens");
109
+ let selected;
110
+ let latestExpiry = -Infinity;
111
+ for (const token of data) {
112
+ const spec = token?.spec;
113
+ if (!spec || typeof spec.token !== "string" || spec.token.length === 0 || spec.expired === true ||
114
+ typeof spec.expiresAt !== "string")
115
+ continue;
116
+ const expiry = Date.parse(spec.expiresAt);
117
+ if (!Number.isFinite(expiry) || expiry <= now || expiry < now + minValidity || expiry > ceiling)
118
+ continue;
119
+ if (expiry > latestExpiry) {
120
+ selected = token;
121
+ latestExpiry = expiry;
122
+ }
123
+ }
124
+ return selected ? new SandboxPreviewToken(selected) : this.create(requestedExpiry);
125
+ }
46
126
  async list() {
47
127
  const { data } = await listSandboxPreviewTokens({
48
128
  path: {
@@ -75,6 +155,9 @@ export class SandboxPreview {
75
155
  get name() {
76
156
  return this.preview.metadata.name;
77
157
  }
158
+ get url() {
159
+ return this.preview.spec?.url ?? "";
160
+ }
78
161
  get metadata() {
79
162
  return this.preview.metadata;
80
163
  }
@@ -100,6 +183,7 @@ export class SandboxPreviews {
100
183
  return data.map((preview) => new SandboxPreview(preview));
101
184
  }
102
185
  async create(preview, force) {
186
+ const normalized = normalizePreview(preview);
103
187
  const query = {};
104
188
  if (force) {
105
189
  query['force'] = 'true';
@@ -109,12 +193,31 @@ export class SandboxPreviews {
109
193
  sandboxName: this.sandboxName,
110
194
  },
111
195
  query,
112
- body: preview,
196
+ body: normalized,
113
197
  throwOnError: true,
114
198
  });
115
199
  return new SandboxPreview(data);
116
200
  }
201
+ /** Existing previews are returned as-is; shorthand's private default applies only on creation. */
117
202
  async createIfNotExists(preview, force) {
203
+ if (isShorthand(preview)) {
204
+ const normalized = normalizePreview(preview);
205
+ try {
206
+ return await this.get(normalized.metadata.name);
207
+ }
208
+ catch (error) {
209
+ if (httpStatus(error) !== 404)
210
+ throw error;
211
+ }
212
+ try {
213
+ return await this.create(normalized, force);
214
+ }
215
+ catch (error) {
216
+ if (httpStatus(error) !== 409)
217
+ throw error;
218
+ return this.get(normalized.metadata.name);
219
+ }
220
+ }
118
221
  try {
119
222
  const previewInstance = await this.get(preview.metadata.name);
120
223
  return previewInstance;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blaxel/core",
3
- "version": "0.3.26-preview.309",
3
+ "version": "0.3.26-preview.311",
4
4
  "description": "Blaxel Core SDK for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "Blaxel, INC (https://blaxel.ai)",