@blaxel/core 0.2.80-preview.139 → 0.2.80
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 +35 -33
- package/dist/cjs/common/h2pool.js +22 -2
- package/dist/cjs/common/settings.js +2 -2
- package/dist/cjs/types/common/h2fetch.d.ts +5 -2
- package/dist/cjs/types/common/h2pool.d.ts +7 -2
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/common/settings.js +2 -2
- package/dist/cjs-browser/types/common/h2fetch.d.ts +5 -2
- package/dist/cjs-browser/types/common/h2pool.d.ts +7 -2
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/common/h2fetch.js +35 -33
- package/dist/esm/common/h2pool.js +21 -2
- package/dist/esm/common/settings.js +2 -2
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/common/settings.js +2 -2
- package/package.json +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
const H2_REQUEST_TIMEOUT_MS = 10_000;
|
|
2
1
|
/**
|
|
3
2
|
* Creates a fetch()-compatible function that sends requests over an existing
|
|
4
|
-
* HTTP/2 session. Falls back to
|
|
5
|
-
* destroyed
|
|
3
|
+
* HTTP/2 session. Falls back to globalThis.fetch() only when the session is
|
|
4
|
+
* closed/destroyed at call time (pre-flight, nothing sent on the wire).
|
|
5
|
+
*
|
|
6
|
+
* Any failure after session.request() succeeds propagates to the caller:
|
|
7
|
+
* this transport never retries. Retry and timeout policy are caller concerns.
|
|
6
8
|
*/
|
|
7
9
|
export function createH2Fetch(session) {
|
|
8
10
|
return (input) => {
|
|
@@ -73,7 +75,8 @@ export function h2RequestDirect(session, url, init) {
|
|
|
73
75
|
}
|
|
74
76
|
else {
|
|
75
77
|
// FormData, ReadableStream, Blob, etc. can't be serialized to Buffer
|
|
76
|
-
// for manual H2 framing — fall back to regular fetch
|
|
78
|
+
// for manual H2 framing — fall back to regular fetch (pre-flight,
|
|
79
|
+
// nothing has been sent on the wire yet).
|
|
77
80
|
return globalThis.fetch(url, init);
|
|
78
81
|
}
|
|
79
82
|
if (!h2Headers["content-length"]) {
|
|
@@ -111,42 +114,49 @@ async function _h2Request(session, input) {
|
|
|
111
114
|
}
|
|
112
115
|
function _h2Send(session, h2Headers, body, signal, fallbackUrl, fallbackInit) {
|
|
113
116
|
return new Promise((resolve, reject) => {
|
|
117
|
+
let settled = false;
|
|
118
|
+
let responded = false;
|
|
119
|
+
let streamController = null;
|
|
120
|
+
let streamClosed = false;
|
|
114
121
|
let req;
|
|
115
122
|
try {
|
|
116
123
|
req = session.request(h2Headers);
|
|
117
124
|
}
|
|
118
125
|
catch {
|
|
119
|
-
|
|
126
|
+
// Pre-flight fallback: session.request() threw synchronously, so no
|
|
127
|
+
// H2 frames were sent. Safe to retry over globalThis.fetch.
|
|
128
|
+
globalThis.fetch(fallbackUrl, fallbackInit).then(resolve, reject);
|
|
129
|
+
return;
|
|
120
130
|
}
|
|
121
|
-
const
|
|
122
|
-
if (settled)
|
|
123
|
-
return;
|
|
124
|
-
settled = true;
|
|
131
|
+
const abort = () => {
|
|
125
132
|
req.close();
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
const abortError = new DOMException("The operation was aborted.", "AbortError");
|
|
134
|
+
if (!responded) {
|
|
135
|
+
if (!settled) {
|
|
136
|
+
settled = true;
|
|
137
|
+
reject(abortError);
|
|
138
|
+
}
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!streamClosed) {
|
|
142
|
+
streamClosed = true;
|
|
143
|
+
streamController?.error(abortError);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
128
146
|
if (signal) {
|
|
129
147
|
if (signal.aborted) {
|
|
130
|
-
clearTimeout(timer);
|
|
131
148
|
req.close();
|
|
149
|
+
settled = true;
|
|
132
150
|
reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
133
151
|
return;
|
|
134
152
|
}
|
|
135
|
-
signal.addEventListener("abort",
|
|
136
|
-
clearTimeout(timer);
|
|
137
|
-
req.close();
|
|
138
|
-
if (!settled) {
|
|
139
|
-
settled = true;
|
|
140
|
-
reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
141
|
-
}
|
|
142
|
-
}, { once: true });
|
|
153
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
143
154
|
}
|
|
144
|
-
let settled = false;
|
|
145
155
|
req.on("response", (headers) => {
|
|
146
|
-
clearTimeout(timer);
|
|
147
156
|
if (settled)
|
|
148
157
|
return;
|
|
149
158
|
settled = true;
|
|
159
|
+
responded = true;
|
|
150
160
|
const status = headers[":status"] ?? 200;
|
|
151
161
|
const resHeaders = new Headers();
|
|
152
162
|
for (const [k, v] of Object.entries(headers)) {
|
|
@@ -156,9 +166,9 @@ function _h2Send(session, h2Headers, body, signal, fallbackUrl, fallbackInit) {
|
|
|
156
166
|
continue;
|
|
157
167
|
resHeaders.set(k, Array.isArray(v) ? v.join(", ") : String(v));
|
|
158
168
|
}
|
|
159
|
-
let streamClosed = false;
|
|
160
169
|
const readable = new ReadableStream({
|
|
161
170
|
start(controller) {
|
|
171
|
+
streamController = controller;
|
|
162
172
|
req.on("data", (chunk) => {
|
|
163
173
|
if (!streamClosed)
|
|
164
174
|
controller.enqueue(new Uint8Array(chunk));
|
|
@@ -175,23 +185,15 @@ function _h2Send(session, h2Headers, body, signal, fallbackUrl, fallbackInit) {
|
|
|
175
185
|
controller.error(err);
|
|
176
186
|
}
|
|
177
187
|
});
|
|
178
|
-
signal?.addEventListener("abort", () => {
|
|
179
|
-
req.close();
|
|
180
|
-
if (!streamClosed) {
|
|
181
|
-
streamClosed = true;
|
|
182
|
-
controller.error(new DOMException("The operation was aborted.", "AbortError"));
|
|
183
|
-
}
|
|
184
|
-
}, { once: true });
|
|
185
188
|
},
|
|
186
189
|
});
|
|
187
190
|
resolve(new Response(readable, { status, headers: resHeaders }));
|
|
188
191
|
});
|
|
189
|
-
req.on("error", () => {
|
|
190
|
-
clearTimeout(timer);
|
|
192
|
+
req.on("error", (err) => {
|
|
191
193
|
if (settled)
|
|
192
194
|
return;
|
|
193
195
|
settled = true;
|
|
194
|
-
|
|
196
|
+
reject(err);
|
|
195
197
|
});
|
|
196
198
|
if (body) {
|
|
197
199
|
req.end(body);
|
|
@@ -6,20 +6,39 @@
|
|
|
6
6
|
* an in-flight warming, or establishes a fresh one.
|
|
7
7
|
* - Closed / destroyed sessions are automatically evicted.
|
|
8
8
|
*/
|
|
9
|
-
class H2Pool {
|
|
9
|
+
export class H2Pool {
|
|
10
10
|
sessions = new Map();
|
|
11
11
|
inflight = new Map();
|
|
12
12
|
_establish = null;
|
|
13
13
|
/**
|
|
14
14
|
* Lazily resolve the establish function so the http2 / tls / dns modules
|
|
15
15
|
* are only imported in Node.js environments.
|
|
16
|
+
*
|
|
17
|
+
* Wires up self-healing eviction: the session is removed from the cache
|
|
18
|
+
* as soon as it emits `goaway`, `error`, or `close`, so `tryGet()` never
|
|
19
|
+
* returns a dead session. This replaces the old behavior of papering
|
|
20
|
+
* over session failures at the fetch layer.
|
|
16
21
|
*/
|
|
17
22
|
async establish(domain) {
|
|
18
23
|
if (!this._establish) {
|
|
19
24
|
const { establishH2 } = await import("./h2warm.js");
|
|
20
25
|
this._establish = establishH2;
|
|
21
26
|
}
|
|
22
|
-
|
|
27
|
+
const session = await this._establish(domain);
|
|
28
|
+
this.attachEvictionListeners(domain, session);
|
|
29
|
+
return session;
|
|
30
|
+
}
|
|
31
|
+
attachEvictionListeners(domain, session) {
|
|
32
|
+
const evict = () => {
|
|
33
|
+
// Only evict if this specific session is still the cached one.
|
|
34
|
+
// A newer session may have taken its place after reconnect.
|
|
35
|
+
if (this.sessions.get(domain) === session) {
|
|
36
|
+
this.sessions.delete(domain);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
session.on("goaway", evict);
|
|
40
|
+
session.on("error", evict);
|
|
41
|
+
session.on("close", evict);
|
|
23
42
|
}
|
|
24
43
|
/**
|
|
25
44
|
* Fire-and-forget background warming. Safe to call multiple times for
|
|
@@ -5,8 +5,8 @@ import { authentication } from "../authentication/index.js";
|
|
|
5
5
|
import { env } from "../common/env.js";
|
|
6
6
|
import { fs, os, path } from "../common/node.js";
|
|
7
7
|
// Build info - these placeholders are replaced at build time by build:replace-imports
|
|
8
|
-
const BUILD_VERSION = "0.2.80
|
|
9
|
-
const BUILD_COMMIT = "
|
|
8
|
+
const BUILD_VERSION = "0.2.80";
|
|
9
|
+
const BUILD_COMMIT = "800934e57ec03de060b909ccb1f5599343cae9e5";
|
|
10
10
|
const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
|
|
11
11
|
// Cache for config.yaml tracking value
|
|
12
12
|
let configTrackingValue = null;
|