@blaxel/core 0.3.12-preview.254 → 0.3.12-preview.255
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/h2fetch.js +7 -0
- package/dist/cjs/common/h2pool.js +19 -21
- package/dist/cjs/common/h2stats.js +115 -0
- package/dist/cjs/common/settings.js +2 -2
- package/dist/cjs/types/common/h2pool.d.ts +1 -0
- package/dist/cjs/types/common/h2stats.d.ts +17 -0
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/common/h2stats.js +115 -0
- package/dist/cjs-browser/common/settings.js +2 -2
- package/dist/cjs-browser/types/common/h2pool.d.ts +1 -0
- package/dist/cjs-browser/types/common/h2stats.d.ts +17 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/common/h2fetch.js +7 -0
- package/dist/esm/common/h2pool.js +19 -21
- package/dist/esm/common/h2stats.js +109 -0
- package/dist/esm/common/settings.js +2 -2
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/common/h2stats.js +109 -0
- package/dist/esm-browser/common/settings.js +2 -2
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ exports.h2RequestDirect = h2RequestDirect;
|
|
|
12
12
|
const http2_1 = __importDefault(require("http2"));
|
|
13
13
|
const settings_js_1 = require("./settings.js");
|
|
14
14
|
const h2ref_js_1 = require("./h2ref.js");
|
|
15
|
+
const h2stats_js_1 = require("./h2stats.js");
|
|
15
16
|
// Statuses the Response constructor refuses to pair with a body. Handing it one
|
|
16
17
|
// throws, so a 204 (e.g. from a DELETE) would blow up instead of resolving.
|
|
17
18
|
const NULL_BODY_STATUSES = new Set([101, 103, 204, 205, 304]);
|
|
@@ -169,6 +170,7 @@ async function withUploadSlot(domain, fn) {
|
|
|
169
170
|
function createH2Fetch(session) {
|
|
170
171
|
return (input) => {
|
|
171
172
|
if (session.closed || session.destroyed) {
|
|
173
|
+
(0, h2stats_js_1.recordH2Fallback)(new URL(input.url).hostname, "session-unusable");
|
|
172
174
|
return globalThis.fetch(input);
|
|
173
175
|
}
|
|
174
176
|
return _h2Request(session, input);
|
|
@@ -234,6 +236,7 @@ async function h2GatewayAttempt(pool, domain, send, fallback) {
|
|
|
234
236
|
let h2RequestCreated = false;
|
|
235
237
|
try {
|
|
236
238
|
return await send(session, {
|
|
239
|
+
domain,
|
|
237
240
|
onH2RequestCreated: () => {
|
|
238
241
|
h2RequestCreated = true;
|
|
239
242
|
},
|
|
@@ -252,6 +255,7 @@ async function h2GatewayAttempt(pool, domain, send, fallback) {
|
|
|
252
255
|
// No usable session: free the slot before falling back over a different
|
|
253
256
|
// connection (no stream opens on the shared session).
|
|
254
257
|
rel();
|
|
258
|
+
(0, h2stats_js_1.recordH2Fallback)(domain, "no-session");
|
|
255
259
|
return await fallback();
|
|
256
260
|
}
|
|
257
261
|
catch (err) {
|
|
@@ -291,6 +295,7 @@ function h2RequestDirectInternal(session, url, init, options) {
|
|
|
291
295
|
// Pre-flight fallback (session unusable): no stream opens on the shared
|
|
292
296
|
// session, so free any held slot before going over globalThis.fetch.
|
|
293
297
|
options?.releaseSlot?.();
|
|
298
|
+
(0, h2stats_js_1.recordH2Fallback)(options?.domain ?? new URL(url).hostname, "session-unusable");
|
|
294
299
|
return globalThis.fetch(url, init);
|
|
295
300
|
}
|
|
296
301
|
const parsed = new URL(url);
|
|
@@ -333,6 +338,7 @@ function h2RequestDirectInternal(session, url, init, options) {
|
|
|
333
338
|
// nothing has been sent on the wire yet). No stream opens on the shared
|
|
334
339
|
// session, so free any held slot before falling back.
|
|
335
340
|
options?.releaseSlot?.();
|
|
341
|
+
(0, h2stats_js_1.recordH2Fallback)(options?.domain ?? parsed.hostname, "unsupported-body");
|
|
336
342
|
return globalThis.fetch(url, init);
|
|
337
343
|
}
|
|
338
344
|
if (!h2Headers["content-length"]) {
|
|
@@ -412,6 +418,7 @@ function _h2Send(session, h2Headers, body, signal, fallbackUrl, fallbackInit, op
|
|
|
412
418
|
// H2 frames were sent. No stream opened on the shared session, so free
|
|
413
419
|
// the slot before retrying over globalThis.fetch.
|
|
414
420
|
releaseSlot();
|
|
421
|
+
(0, h2stats_js_1.recordH2Fallback)(options?.domain ?? new URL(fallbackUrl).hostname, session.closed || session.destroyed ? "session-unusable" : "request-rejected");
|
|
415
422
|
globalThis.fetch(fallbackUrl, fallbackInit).then(resolve, reject);
|
|
416
423
|
return;
|
|
417
424
|
}
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.h2Pool = exports.H2Pool = void 0;
|
|
37
|
+
const h2stats_js_1 = require("./h2stats.js");
|
|
37
38
|
const DEFAULT_MAX_IDLE_MS = 5_000;
|
|
38
39
|
const DEFAULT_PING_TIMEOUT_MS = 500;
|
|
39
40
|
/**
|
|
@@ -75,6 +76,22 @@ class H2Pool {
|
|
|
75
76
|
this.attachEvictionListeners(domain, session);
|
|
76
77
|
return session;
|
|
77
78
|
}
|
|
79
|
+
startEstablish(domain) {
|
|
80
|
+
const pending = this.establish(domain)
|
|
81
|
+
.then((session) => {
|
|
82
|
+
this.cache(domain, session);
|
|
83
|
+
return session;
|
|
84
|
+
})
|
|
85
|
+
.catch((error) => {
|
|
86
|
+
(0, h2stats_js_1.recordH2EstablishFailure)(domain, error);
|
|
87
|
+
return null;
|
|
88
|
+
})
|
|
89
|
+
.finally(() => {
|
|
90
|
+
this.inflight.delete(domain);
|
|
91
|
+
});
|
|
92
|
+
this.inflight.set(domain, pending);
|
|
93
|
+
return pending;
|
|
94
|
+
}
|
|
78
95
|
attachEvictionListeners(domain, session) {
|
|
79
96
|
const evict = () => {
|
|
80
97
|
// Only evict if this specific session is still the cached one.
|
|
@@ -193,16 +210,7 @@ class H2Pool {
|
|
|
193
210
|
return;
|
|
194
211
|
if (this.inflight.has(domain))
|
|
195
212
|
return;
|
|
196
|
-
|
|
197
|
-
.then((session) => {
|
|
198
|
-
this.cache(domain, session);
|
|
199
|
-
return session;
|
|
200
|
-
})
|
|
201
|
-
.catch(() => null)
|
|
202
|
-
.finally(() => {
|
|
203
|
-
this.inflight.delete(domain);
|
|
204
|
-
});
|
|
205
|
-
this.inflight.set(domain, p);
|
|
213
|
+
void this.startEstablish(domain);
|
|
206
214
|
}
|
|
207
215
|
/**
|
|
208
216
|
* Synchronous cache check. Returns a live cached session or `null`.
|
|
@@ -250,17 +258,7 @@ class H2Pool {
|
|
|
250
258
|
const freshCached = this.tryGet(domain);
|
|
251
259
|
if (freshCached)
|
|
252
260
|
return freshCached;
|
|
253
|
-
|
|
254
|
-
.then((session) => {
|
|
255
|
-
this.cache(domain, session);
|
|
256
|
-
return session;
|
|
257
|
-
})
|
|
258
|
-
.catch(() => null)
|
|
259
|
-
.finally(() => {
|
|
260
|
-
this.inflight.delete(domain);
|
|
261
|
-
});
|
|
262
|
-
this.inflight.set(domain, p);
|
|
263
|
-
return p;
|
|
261
|
+
return this.startEstablish(domain);
|
|
264
262
|
}
|
|
265
263
|
/** Close all sessions (for cleanup). */
|
|
266
264
|
closeAll() {
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.snapshotH2TransportStats = snapshotH2TransportStats;
|
|
4
|
+
exports.resetH2TransportStats = resetH2TransportStats;
|
|
5
|
+
exports.recordH2EstablishFailure = recordH2EstablishFailure;
|
|
6
|
+
exports.recordH2Fallback = recordH2Fallback;
|
|
7
|
+
const logger_js_1 = require("./logger.js");
|
|
8
|
+
const emptyReasons = () => ({
|
|
9
|
+
"no-session": 0,
|
|
10
|
+
"request-rejected": 0,
|
|
11
|
+
"session-unusable": 0,
|
|
12
|
+
"unsupported-body": 0,
|
|
13
|
+
});
|
|
14
|
+
const MAX_TRACKED_DOMAINS = 100;
|
|
15
|
+
const H2_DEBUG_STATS_SYMBOL = Symbol.for("blaxel.h2stats");
|
|
16
|
+
const h2DebugStatsEnabled = typeof process !== "undefined" && process.env?.BL_H2_DEBUG_STATS === "1";
|
|
17
|
+
function publishDebugSnapshot(snapshot) {
|
|
18
|
+
if (!h2DebugStatsEnabled)
|
|
19
|
+
return;
|
|
20
|
+
try {
|
|
21
|
+
globalThis[H2_DEBUG_STATS_SYMBOL] = snapshot;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Debug diagnostics must never change transport behavior.
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const emptyDomainStats = () => ({
|
|
28
|
+
establishFailures: 0,
|
|
29
|
+
fetchFallbacks: 0,
|
|
30
|
+
fallbacksByReason: emptyReasons(),
|
|
31
|
+
});
|
|
32
|
+
class H2TransportStatsStore {
|
|
33
|
+
totals = emptyDomainStats();
|
|
34
|
+
byDomain = new Map();
|
|
35
|
+
snapshot() {
|
|
36
|
+
return {
|
|
37
|
+
...this.clone(this.totals),
|
|
38
|
+
byDomain: Object.fromEntries([...this.byDomain].map(([domain, stats]) => [domain, this.clone(stats)])),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
reset() {
|
|
42
|
+
this.totals = emptyDomainStats();
|
|
43
|
+
this.byDomain.clear();
|
|
44
|
+
publishDebugSnapshot(this.snapshot());
|
|
45
|
+
}
|
|
46
|
+
/** @internal */
|
|
47
|
+
recordEstablishFailure(domain, error) {
|
|
48
|
+
this.totals.establishFailures++;
|
|
49
|
+
this.forDomain(domain).establishFailures++;
|
|
50
|
+
try {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
logger_js_1.logger.debug(`H2 session establishment failed for ${domain}: ${message}`);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Diagnostics must never change transport behavior.
|
|
56
|
+
}
|
|
57
|
+
publishDebugSnapshot(this.snapshot());
|
|
58
|
+
}
|
|
59
|
+
/** @internal */
|
|
60
|
+
recordFallback(domain, reason) {
|
|
61
|
+
this.totals.fetchFallbacks++;
|
|
62
|
+
this.totals.fallbacksByReason[reason]++;
|
|
63
|
+
const stats = this.forDomain(domain);
|
|
64
|
+
stats.fetchFallbacks++;
|
|
65
|
+
stats.fallbacksByReason[reason]++;
|
|
66
|
+
try {
|
|
67
|
+
logger_js_1.logger.debug(`H2 transport falling back to fetch for ${domain}: ${reason}`);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Diagnostics must never change transport behavior.
|
|
71
|
+
}
|
|
72
|
+
publishDebugSnapshot(this.snapshot());
|
|
73
|
+
}
|
|
74
|
+
forDomain(domain) {
|
|
75
|
+
let stats = this.byDomain.get(domain);
|
|
76
|
+
if (stats) {
|
|
77
|
+
this.byDomain.delete(domain);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (this.byDomain.size >= MAX_TRACKED_DOMAINS) {
|
|
81
|
+
const oldest = this.byDomain.keys().next().value;
|
|
82
|
+
if (oldest !== undefined)
|
|
83
|
+
this.byDomain.delete(oldest);
|
|
84
|
+
}
|
|
85
|
+
stats = emptyDomainStats();
|
|
86
|
+
}
|
|
87
|
+
this.byDomain.set(domain, stats);
|
|
88
|
+
return stats;
|
|
89
|
+
}
|
|
90
|
+
clone(stats) {
|
|
91
|
+
return {
|
|
92
|
+
establishFailures: stats.establishFailures,
|
|
93
|
+
fetchFallbacks: stats.fetchFallbacks,
|
|
94
|
+
fallbacksByReason: { ...stats.fallbacksByReason },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const store = new H2TransportStatsStore();
|
|
99
|
+
publishDebugSnapshot(store.snapshot());
|
|
100
|
+
/** @internal */
|
|
101
|
+
function snapshotH2TransportStats() {
|
|
102
|
+
return store.snapshot();
|
|
103
|
+
}
|
|
104
|
+
/** @internal */
|
|
105
|
+
function resetH2TransportStats() {
|
|
106
|
+
store.reset();
|
|
107
|
+
}
|
|
108
|
+
/** @internal */
|
|
109
|
+
function recordH2EstablishFailure(domain, error) {
|
|
110
|
+
store.recordEstablishFailure(domain, error);
|
|
111
|
+
}
|
|
112
|
+
/** @internal */
|
|
113
|
+
function recordH2Fallback(domain, reason) {
|
|
114
|
+
store.recordFallback(domain, reason);
|
|
115
|
+
}
|
|
@@ -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.12-preview.
|
|
34
|
-
const BUILD_COMMIT = "
|
|
33
|
+
const BUILD_VERSION = "0.3.12-preview.255";
|
|
34
|
+
const BUILD_COMMIT = "0a7ecff86c697285b849e61a8cc5219e85b5ba89";
|
|
35
35
|
const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
|
|
36
36
|
const BLAXEL_API_VERSION = "2026-04-28";
|
|
37
37
|
// Bun < 1.3.11 never sends connection-level WINDOW_UPDATE: the pooled h2
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type H2FallbackReason = "no-session" | "request-rejected" | "session-unusable" | "unsupported-body";
|
|
2
|
+
export type H2TransportDomainStats = {
|
|
3
|
+
establishFailures: number;
|
|
4
|
+
fetchFallbacks: number;
|
|
5
|
+
fallbacksByReason: Record<H2FallbackReason, number>;
|
|
6
|
+
};
|
|
7
|
+
export type H2TransportStatsSnapshot = H2TransportDomainStats & {
|
|
8
|
+
byDomain: Record<string, H2TransportDomainStats>;
|
|
9
|
+
};
|
|
10
|
+
/** @internal */
|
|
11
|
+
export declare function snapshotH2TransportStats(): H2TransportStatsSnapshot;
|
|
12
|
+
/** @internal */
|
|
13
|
+
export declare function resetH2TransportStats(): void;
|
|
14
|
+
/** @internal */
|
|
15
|
+
export declare function recordH2EstablishFailure(domain: string, error: unknown): void;
|
|
16
|
+
/** @internal */
|
|
17
|
+
export declare function recordH2Fallback(domain: string, reason: H2FallbackReason): void;
|