@blaxel/core 0.2.89 → 0.2.90-preview.182
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/autoload.js +3 -0
- package/dist/cjs/common/controlPlaneFetch.js +57 -0
- package/dist/cjs/common/lazyInit.js +5 -4
- package/dist/cjs/common/settings.js +27 -2
- package/dist/cjs/types/common/controlPlaneFetch.d.ts +4 -0
- package/dist/cjs/types/common/settings.d.ts +18 -0
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/common/autoload.js +3 -0
- package/dist/cjs-browser/common/controlPlaneFetch.js +57 -0
- package/dist/cjs-browser/common/lazyInit.js +5 -4
- package/dist/cjs-browser/common/settings.js +27 -2
- package/dist/cjs-browser/types/common/controlPlaneFetch.d.ts +4 -0
- package/dist/cjs-browser/types/common/settings.d.ts +18 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/common/autoload.js +3 -0
- package/dist/esm/common/controlPlaneFetch.js +51 -0
- package/dist/esm/common/lazyInit.js +5 -4
- package/dist/esm/common/settings.js +27 -2
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/common/autoload.js +3 -0
- package/dist/esm-browser/common/controlPlaneFetch.js +51 -0
- package/dist/esm-browser/common/lazyInit.js +5 -4
- package/dist/esm-browser/common/settings.js +27 -2
- package/package.json +1 -1
|
@@ -2,10 +2,12 @@ import { client } from "../client/client.gen.js";
|
|
|
2
2
|
import { interceptors } from "../client/interceptors.js";
|
|
3
3
|
import { responseInterceptors } from "../client/responseInterceptor.js";
|
|
4
4
|
import { client as clientSandbox } from "../sandbox/client/client.gen.js";
|
|
5
|
+
import { controlPlaneFetch } from "./controlPlaneFetch.js";
|
|
5
6
|
import { settings } from "./settings.js";
|
|
6
7
|
export { ensureAutoloaded } from "./lazyInit.js";
|
|
7
8
|
client.setConfig({
|
|
8
9
|
baseUrl: settings.baseUrl,
|
|
10
|
+
fetch: controlPlaneFetch,
|
|
9
11
|
});
|
|
10
12
|
// Register request interceptors
|
|
11
13
|
for (const interceptor of interceptors) {
|
|
@@ -50,6 +52,7 @@ export function initialize(config) {
|
|
|
50
52
|
settings.setConfig(config);
|
|
51
53
|
client.setConfig({
|
|
52
54
|
baseUrl: settings.baseUrl,
|
|
55
|
+
fetch: controlPlaneFetch,
|
|
53
56
|
});
|
|
54
57
|
clientSandbox.setConfig({
|
|
55
58
|
baseUrl: settings.baseUrl,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createPoolBackedH2Fetch } from "./h2fetch.js";
|
|
2
|
+
import { h2Pool } from "./h2pool.js";
|
|
3
|
+
import { settings } from "./settings.js";
|
|
4
|
+
const h2FetchByHost = new Map();
|
|
5
|
+
// Node's global fetch dispatcher only negotiates HTTP/2 by default starting
|
|
6
|
+
// with undici 8 (Node 26+); undici 7 (Node 24) and undici 6 (Node 22) still
|
|
7
|
+
// ALPN to HTTP/1.1 for fetch, verified empirically against api.blaxel.ai. On
|
|
8
|
+
// undici >= 8 the pooled wrapper is redundant, so we prefer the native path.
|
|
9
|
+
export function undiciSupportsNativeH2(undiciVersion) {
|
|
10
|
+
const major = Number(undiciVersion?.split(".")[0] ?? 0);
|
|
11
|
+
return major >= 8;
|
|
12
|
+
}
|
|
13
|
+
// `process`/`process.versions` is absent on Cloudflare Workers and other
|
|
14
|
+
// non-Node runtimes, and `process.versions.undici` is undefined on Bun/Deno;
|
|
15
|
+
// all of those resolve to `false` and keep the wrapper as the fallback.
|
|
16
|
+
export const nativeFetchSupportsH2 = typeof process !== "undefined" && undiciSupportsNativeH2(process.versions?.undici);
|
|
17
|
+
export function shouldUseControlPlaneH2(url, h2Disabled, proxyConfigured = false, nativeH2 = false, forceWrapper = false) {
|
|
18
|
+
if (h2Disabled || proxyConfigured)
|
|
19
|
+
return false;
|
|
20
|
+
if (url.protocol !== "https:")
|
|
21
|
+
return false;
|
|
22
|
+
// Token refresh is sequential and unauthenticated; it cannot contribute to
|
|
23
|
+
// the create burst TLS storm, and device-mode refresh currently relies on the
|
|
24
|
+
// native fetch path.
|
|
25
|
+
if (url.pathname.endsWith("/oauth/token"))
|
|
26
|
+
return false;
|
|
27
|
+
if (!(url.hostname.endsWith("blaxel.ai") || url.hostname.endsWith("blaxel.dev"))) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
// Native fetch already negotiates HTTP/2: skip the redundant wrapper unless
|
|
31
|
+
// explicitly forced (e.g. to exercise the pooled path on a modern runtime).
|
|
32
|
+
if (nativeH2 && !forceWrapper)
|
|
33
|
+
return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
export function controlPlaneFetch(input) {
|
|
37
|
+
const url = new URL(input.url);
|
|
38
|
+
const proxyConfigured = Boolean(settings.config.proxy);
|
|
39
|
+
// Global disableH2 still wins; disableControlPlaneH2 opts out of just the
|
|
40
|
+
// control-plane wrapper while leaving data-plane H2 in place.
|
|
41
|
+
const h2Disabled = settings.disableH2 || settings.disableControlPlaneH2;
|
|
42
|
+
if (!shouldUseControlPlaneH2(url, h2Disabled, proxyConfigured, nativeFetchSupportsH2, settings.forceControlPlaneH2)) {
|
|
43
|
+
return globalThis.fetch(input);
|
|
44
|
+
}
|
|
45
|
+
let h2Fetch = h2FetchByHost.get(url.hostname);
|
|
46
|
+
if (!h2Fetch) {
|
|
47
|
+
h2Fetch = createPoolBackedH2Fetch(h2Pool, url.hostname);
|
|
48
|
+
h2FetchByHost.set(url.hostname, h2Fetch);
|
|
49
|
+
}
|
|
50
|
+
return h2Fetch(input);
|
|
51
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { client } from "../client/client.gen.js";
|
|
2
2
|
import { client as clientSandbox } from "../sandbox/client/client.gen.js";
|
|
3
|
+
import { controlPlaneFetch } from "./controlPlaneFetch.js";
|
|
3
4
|
import { initSentry } from "./sentry.js";
|
|
4
5
|
import { settings } from "./settings.js";
|
|
5
6
|
let autoloaded = false;
|
|
@@ -33,7 +34,7 @@ export function ensureAutoloaded() {
|
|
|
33
34
|
// Keep the clients' baseUrl in sync with the now-resolved env. Without
|
|
34
35
|
// this, the module-load `client.setConfig({ baseUrl })` would be stuck on
|
|
35
36
|
// the prod default for users who rely on `config.yaml` (no env vars).
|
|
36
|
-
client.setConfig({ baseUrl: settings.baseUrl });
|
|
37
|
+
client.setConfig({ baseUrl: settings.baseUrl, fetch: controlPlaneFetch });
|
|
37
38
|
clientSandbox.setConfig({ baseUrl: settings.baseUrl });
|
|
38
39
|
// Initialize Sentry for SDK error tracking.
|
|
39
40
|
initSentry();
|
|
@@ -44,9 +45,9 @@ export function ensureAutoloaded() {
|
|
|
44
45
|
if (isNode && !isBrowser && !settings.disableH2) {
|
|
45
46
|
try {
|
|
46
47
|
// Pre-warm edge H2 for the configured region so the first
|
|
47
|
-
// SandboxInstance.create() gets an instant session via the
|
|
48
|
-
//
|
|
49
|
-
//
|
|
48
|
+
// SandboxInstance.create() gets an instant data-plane session via the
|
|
49
|
+
// pool. Control-plane H2 is intentionally not warmed; its cold burst
|
|
50
|
+
// relies on pool deduplication instead.
|
|
50
51
|
const region = settings.region;
|
|
51
52
|
if (region) {
|
|
52
53
|
import("./h2pool.js").then(({ h2Pool }) => {
|
|
@@ -22,8 +22,8 @@ function missingCredentialsMessage() {
|
|
|
22
22
|
return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
|
|
23
23
|
}
|
|
24
24
|
// Build info - these placeholders are replaced at build time by build:replace-imports
|
|
25
|
-
const BUILD_VERSION = "0.2.
|
|
26
|
-
const BUILD_COMMIT = "
|
|
25
|
+
const BUILD_VERSION = "0.2.90-preview.182";
|
|
26
|
+
const BUILD_COMMIT = "78dad6b3e2d47dcf5d8464b9688b0829ddc3369c";
|
|
27
27
|
const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
|
|
28
28
|
const BLAXEL_API_VERSION = "2026-04-16";
|
|
29
29
|
// Cache for config.yaml tracking value
|
|
@@ -244,6 +244,31 @@ class Settings {
|
|
|
244
244
|
}
|
|
245
245
|
return isDenoRuntime();
|
|
246
246
|
}
|
|
247
|
+
// Control-plane-only escape hatch: disables the control-plane H2 wrapper
|
|
248
|
+
// without affecting data-plane (edge) H2. `disableH2` is the global override
|
|
249
|
+
// and is checked separately by callers, so it always wins.
|
|
250
|
+
get disableControlPlaneH2() {
|
|
251
|
+
if (typeof this.config.disableControlPlaneH2 === "boolean") {
|
|
252
|
+
return this.config.disableControlPlaneH2;
|
|
253
|
+
}
|
|
254
|
+
const value = env.BL_DISABLE_CONTROL_PLANE_H2;
|
|
255
|
+
if (value) {
|
|
256
|
+
return ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
// Forces the control-plane H2 wrapper on even when native fetch already
|
|
261
|
+
// supports H2 (undici >= 7). `disableH2`/`disableControlPlaneH2` still win.
|
|
262
|
+
get forceControlPlaneH2() {
|
|
263
|
+
if (typeof this.config.forceControlPlaneH2 === "boolean") {
|
|
264
|
+
return this.config.forceControlPlaneH2;
|
|
265
|
+
}
|
|
266
|
+
const value = env.BL_FORCE_CONTROL_PLANE_H2;
|
|
267
|
+
if (value) {
|
|
268
|
+
return ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
247
272
|
get maxConcurrentH2Requests() {
|
|
248
273
|
if (typeof this.config.maxConcurrentH2Requests === "number") {
|
|
249
274
|
return this.config.maxConcurrentH2Requests;
|