brass-runtime 1.13.8 → 1.14.0
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/agent/cli/main.cjs +43 -43
- package/dist/agent/cli/main.js +2 -2
- package/dist/agent/cli/main.mjs +2 -2
- package/dist/agent/index.cjs +3 -3
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +2 -2
- package/dist/agent/index.mjs +2 -2
- package/dist/chunk-4N2JEK4H.mjs +3897 -0
- package/dist/chunk-BKBFSOGT.cjs +3897 -0
- package/dist/{chunk-XNOTJSMZ.mjs → chunk-BMRF4FN6.js} +268 -8
- package/dist/chunk-JT7D6M5H.js +3897 -0
- package/dist/{chunk-3R7ZYRK2.mjs → chunk-MQF7HZ7Y.mjs} +1 -1
- package/dist/chunk-SKVY72E5.cjs +667 -0
- package/dist/{chunk-ATHSSDUF.js → chunk-UWMMYKVK.mjs} +268 -8
- package/dist/{chunk-INZBKOHY.js → chunk-WJESVBWN.js} +1 -1
- package/dist/{chunk-XDINDYNA.cjs → chunk-XTMZTVIT.cjs} +134 -134
- package/dist/{effect-ISvXPLgc.d.ts → effect-DM56H743.d.ts} +191 -21
- package/dist/http/index.cjs +808 -140
- package/dist/http/index.d.ts +181 -8
- package/dist/http/index.js +793 -125
- package/dist/http/index.mjs +793 -125
- package/dist/index.cjs +1785 -137
- package/dist/index.d.ts +979 -36
- package/dist/index.js +1675 -27
- package/dist/index.mjs +1675 -27
- package/dist/stream-Oqe6WeLE.d.ts +173 -0
- package/package.json +1 -1
- package/wasm/pkg/brass_runtime_wasm_engine.d.ts +95 -16
- package/wasm/pkg/brass_runtime_wasm_engine.js +715 -15
- package/wasm/pkg/brass_runtime_wasm_engine_bg.wasm +0 -0
- package/wasm/pkg/brass_runtime_wasm_engine_bg.wasm.d.ts +78 -7
- package/dist/chunk-2P4PD6D7.cjs +0 -2557
- package/dist/chunk-7F2R7A2V.mjs +0 -2557
- package/dist/chunk-L6KKKM66.js +0 -2557
- package/dist/chunk-ZTDK2DLG.cjs +0 -407
- package/dist/stream-BvukHxCv.d.ts +0 -66
package/dist/http/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
|
|
2
2
|
|
|
3
|
-
var _chunkZTDK2DLGcjs = require('../chunk-ZTDK2DLG.cjs');
|
|
4
3
|
|
|
5
4
|
|
|
5
|
+
var _chunkSKVY72E5cjs = require('../chunk-SKVY72E5.cjs');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
@@ -10,7 +10,10 @@ var _chunkZTDK2DLGcjs = require('../chunk-ZTDK2DLG.cjs');
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
var _chunkBKBFSOGTcjs = require('../chunk-BKBFSOGT.cjs');
|
|
14
17
|
|
|
15
18
|
// src/http/optics/lens.ts
|
|
16
19
|
var Lens = {
|
|
@@ -39,36 +42,389 @@ var mergeHeaders = (extra) => (req) => Lens.over(Request.headers, (h) => ({ ...h
|
|
|
39
42
|
var mergeHeadersUnder = (under) => (req) => Lens.over(Request.headers, (h) => ({ ...under, ...h }))(req);
|
|
40
43
|
var setHeaderIfMissing = (k, v) => (req) => Lens.over(Request.headers, (h) => h[k] ? h : { ...h, [k]: v })(req);
|
|
41
44
|
|
|
42
|
-
// src/http/
|
|
43
|
-
var
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// src/http/wasmPermitPool.ts
|
|
46
|
+
var DECISION_RUN_NOW = 0;
|
|
47
|
+
var DECISION_QUEUED = 1;
|
|
48
|
+
var WasmHttpPermitPoolBridge = (_class = class {
|
|
49
|
+
|
|
50
|
+
__init() {this.keyCache = /* @__PURE__ */ new Map()}
|
|
51
|
+
constructor(Ctor, options) {;_class.prototype.__init.call(this);
|
|
52
|
+
this.pool = new Ctor(options.concurrency, options.maxQueue, toU64(options.queueTimeoutMs));
|
|
48
53
|
}
|
|
49
|
-
|
|
54
|
+
acquire(key, subjectId, nowMs = Date.now()) {
|
|
55
|
+
const keyId = this.internKey(key);
|
|
56
|
+
const decision = this.pool.acquire(subjectId, keyId, toU64(nowMs));
|
|
57
|
+
const permitId = this.pool.last_permit_id();
|
|
58
|
+
if (decision === DECISION_RUN_NOW) return { kind: "run", keyId, permitId };
|
|
59
|
+
if (decision === DECISION_QUEUED) return { kind: "queued", keyId, permitId };
|
|
60
|
+
return { kind: "rejected", keyId, permitId };
|
|
61
|
+
}
|
|
62
|
+
release(keyId, nowMs = Date.now()) {
|
|
63
|
+
const ptr = this.pool.release(keyId, toU64(nowMs));
|
|
64
|
+
return this.readEvents(ptr, this.pool.permit_events_len());
|
|
65
|
+
}
|
|
66
|
+
cancel(permitId) {
|
|
67
|
+
this.pool.cancel(permitId);
|
|
68
|
+
}
|
|
69
|
+
advanceTime(nowMs = Date.now()) {
|
|
70
|
+
const ptr = this.pool.advance_time(toU64(nowMs));
|
|
71
|
+
return this.readEvents(ptr, this.pool.permit_events_len());
|
|
72
|
+
}
|
|
73
|
+
nextDeadlineMs() {
|
|
74
|
+
return this.pool.next_deadline_ms();
|
|
75
|
+
}
|
|
76
|
+
stats() {
|
|
77
|
+
return {
|
|
78
|
+
running: this.pool.metric_u64(0),
|
|
79
|
+
queued: this.pool.metric_u64(1),
|
|
80
|
+
acquired: this.pool.metric_u64(2),
|
|
81
|
+
released: this.pool.metric_u64(3),
|
|
82
|
+
rejected: this.pool.metric_u64(4),
|
|
83
|
+
queueTimeouts: this.pool.metric_u64(5),
|
|
84
|
+
keys: this.pool.metric_u64(6)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
internKey(key) {
|
|
88
|
+
const normalized = key.trim().slice(0, 160) || "global";
|
|
89
|
+
let id = this.keyCache.get(normalized);
|
|
90
|
+
if (id === void 0) {
|
|
91
|
+
id = this.pool.intern_key(normalized);
|
|
92
|
+
this.keyCache.set(normalized, id);
|
|
93
|
+
}
|
|
94
|
+
return id;
|
|
95
|
+
}
|
|
96
|
+
readEvents(ptr, len) {
|
|
97
|
+
if (ptr === 0 || len <= 1) return [];
|
|
98
|
+
const words = new Uint32Array(this.pool.memory().buffer, ptr, len);
|
|
99
|
+
const count = words[0] >>> 0;
|
|
100
|
+
const out = [];
|
|
101
|
+
for (let i = 0; i < count; i++) {
|
|
102
|
+
const base = 1 + i * 3;
|
|
103
|
+
if (base + 2 >= words.length) break;
|
|
104
|
+
out.push({
|
|
105
|
+
subjectId: words[base] >>> 0,
|
|
106
|
+
permitId: words[base + 1] >>> 0,
|
|
107
|
+
keyId: words[base + 2] >>> 0
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
}, _class);
|
|
113
|
+
function makeWasmHttpPermitPool(options) {
|
|
114
|
+
const mod = _chunkBKBFSOGTcjs.resolveWasmModule.call(void 0, );
|
|
115
|
+
const Ctor = _optionalChain([mod, 'optionalAccess', _ => _.BrassWasmHttpPermitPool]);
|
|
116
|
+
if (!Ctor) throw new Error("brass-runtime wasm HTTP permit pool is not available. Run npm run build:wasm first.");
|
|
117
|
+
return new WasmHttpPermitPoolBridge(Ctor, options);
|
|
118
|
+
}
|
|
119
|
+
function toU64(value) {
|
|
120
|
+
return BigInt(Math.max(0, Math.floor(value)));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/http/pool.ts
|
|
124
|
+
var DEFAULT_CONCURRENCY = 64;
|
|
125
|
+
var DEFAULT_MAX_QUEUE = 256;
|
|
126
|
+
var clampInt = (n, fallback, min) => {
|
|
127
|
+
if (n === void 0 || !Number.isFinite(n)) return fallback;
|
|
128
|
+
return Math.max(min, Math.floor(n));
|
|
50
129
|
};
|
|
51
|
-
var
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
130
|
+
var queueTimeoutError = (key, timeoutMs) => ({
|
|
131
|
+
_tag: "PoolTimeout",
|
|
132
|
+
key,
|
|
133
|
+
timeoutMs,
|
|
134
|
+
message: `HTTP pool '${key}' did not grant a slot within ${timeoutMs}ms`
|
|
135
|
+
});
|
|
136
|
+
var poolRejectedError = (key, maxQueue) => ({
|
|
137
|
+
_tag: "PoolRejected",
|
|
138
|
+
key,
|
|
139
|
+
limit: maxQueue,
|
|
140
|
+
message: `HTTP pool '${key}' queue is full`
|
|
141
|
+
});
|
|
142
|
+
var abortError = () => ({ _tag: "Abort" });
|
|
143
|
+
function resolveHttpPoolEngine(config) {
|
|
144
|
+
if (config.engine !== void 0) {
|
|
145
|
+
if (config.engine === "ts" || config.engine === "wasm") return config.engine;
|
|
146
|
+
throw new Error(`brass-runtime HTTP pool engine must be 'ts' or 'wasm'; received '${String(config.engine)}'`);
|
|
147
|
+
}
|
|
148
|
+
if (config.wasm === true) return "wasm";
|
|
149
|
+
if (config.wasm === false) return "ts";
|
|
150
|
+
return "ts";
|
|
151
|
+
}
|
|
152
|
+
function resolveHttpPoolKey(resolver, req, url) {
|
|
153
|
+
const custom = _optionalChain([req, 'access', _2 => _2.poolKey, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4()]);
|
|
154
|
+
if (custom) return custom.slice(0, 160);
|
|
155
|
+
const r = _nullishCoalesce(resolver, () => ( "origin"));
|
|
156
|
+
if (typeof r === "function") return r(req, url).trim().slice(0, 160) || "global";
|
|
157
|
+
if (r === "global") return "global";
|
|
158
|
+
if (r === "host") return url.host;
|
|
159
|
+
return url.origin;
|
|
160
|
+
}
|
|
161
|
+
var HttpConcurrencyPool = (_class2 = class {
|
|
162
|
+
__init2() {this.states = /* @__PURE__ */ new Map()}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
__init3() {this.wasmWaiters = /* @__PURE__ */ new Map()}
|
|
169
|
+
|
|
170
|
+
__init4() {this.nextSubjectId = 1}
|
|
171
|
+
constructor(config = {}) {;_class2.prototype.__init2.call(this);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);
|
|
172
|
+
this.concurrency = clampInt(config.concurrency, DEFAULT_CONCURRENCY, 1);
|
|
173
|
+
this.maxQueue = clampInt(config.maxQueue, DEFAULT_MAX_QUEUE, 0);
|
|
174
|
+
this.queueTimeoutMs = config.queueTimeoutMs !== void 0 && Number.isFinite(config.queueTimeoutMs) ? Math.max(0, Math.floor(config.queueTimeoutMs)) : void 0;
|
|
175
|
+
this.keyResolver = config.key;
|
|
176
|
+
const engine = resolveHttpPoolEngine(config);
|
|
177
|
+
this.wasm = engine === "wasm" ? makeWasmHttpPermitPool({
|
|
178
|
+
concurrency: this.concurrency,
|
|
179
|
+
maxQueue: this.maxQueue,
|
|
180
|
+
queueTimeoutMs: _nullishCoalesce(this.queueTimeoutMs, () => ( 0))
|
|
181
|
+
}) : void 0;
|
|
182
|
+
}
|
|
183
|
+
acquire(key, signal) {
|
|
184
|
+
return this.wasm ? this.acquireWasm(key, signal) : this.acquireJs(key, signal);
|
|
185
|
+
}
|
|
186
|
+
stats() {
|
|
187
|
+
const keys = Array.from(this.states.values()).map((state) => ({
|
|
188
|
+
key: state.key,
|
|
189
|
+
running: state.running,
|
|
190
|
+
queued: state.queue.length,
|
|
191
|
+
concurrency: this.concurrency,
|
|
192
|
+
maxQueue: this.maxQueue,
|
|
193
|
+
acquired: state.acquired,
|
|
194
|
+
released: state.released,
|
|
195
|
+
rejected: state.rejected,
|
|
196
|
+
queueTimeouts: state.queueTimeouts,
|
|
197
|
+
abortedWhileQueued: state.abortedWhileQueued
|
|
198
|
+
})).sort((a, b) => b.running + b.queued - (a.running + a.queued) || a.key.localeCompare(b.key));
|
|
199
|
+
return keys.reduce((acc, key) => ({
|
|
200
|
+
running: acc.running + key.running,
|
|
201
|
+
queued: acc.queued + key.queued,
|
|
202
|
+
acquired: acc.acquired + key.acquired,
|
|
203
|
+
released: acc.released + key.released,
|
|
204
|
+
rejected: acc.rejected + key.rejected,
|
|
205
|
+
queueTimeouts: acc.queueTimeouts + key.queueTimeouts,
|
|
206
|
+
abortedWhileQueued: acc.abortedWhileQueued + key.abortedWhileQueued,
|
|
207
|
+
wasm: _optionalChain([this, 'access', _5 => _5.wasm, 'optionalAccess', _6 => _6.stats, 'call', _7 => _7()]),
|
|
208
|
+
keys: acc.keys.concat(key)
|
|
209
|
+
}), {
|
|
210
|
+
running: 0,
|
|
211
|
+
queued: 0,
|
|
212
|
+
acquired: 0,
|
|
213
|
+
released: 0,
|
|
214
|
+
rejected: 0,
|
|
215
|
+
queueTimeouts: 0,
|
|
216
|
+
abortedWhileQueued: 0,
|
|
217
|
+
...this.wasm ? { wasm: this.wasm.stats() } : {},
|
|
218
|
+
keys: []
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
acquireJs(key, signal) {
|
|
222
|
+
const state = this.getState(key);
|
|
223
|
+
if (signal.aborted) return Promise.reject(abortError());
|
|
224
|
+
if (state.running < this.concurrency) {
|
|
225
|
+
state.running++;
|
|
226
|
+
state.acquired++;
|
|
227
|
+
return Promise.resolve(this.makeLease(state));
|
|
228
|
+
}
|
|
229
|
+
if (state.queue.length >= this.maxQueue) {
|
|
230
|
+
state.rejected++;
|
|
231
|
+
return Promise.reject(poolRejectedError(key, this.maxQueue));
|
|
232
|
+
}
|
|
233
|
+
return new Promise((resolve, reject) => {
|
|
234
|
+
const waiter = { signal, resolve, reject };
|
|
235
|
+
const removeWaiter = () => this.removeWaiter(state, waiter);
|
|
236
|
+
const cleanup = () => this.cleanupWaiter(waiter);
|
|
237
|
+
waiter.abort = () => {
|
|
238
|
+
cleanup();
|
|
239
|
+
removeWaiter();
|
|
240
|
+
state.abortedWhileQueued++;
|
|
241
|
+
reject(abortError());
|
|
242
|
+
};
|
|
243
|
+
signal.addEventListener("abort", waiter.abort, { once: true });
|
|
244
|
+
if (this.queueTimeoutMs !== void 0 && this.queueTimeoutMs > 0) {
|
|
245
|
+
waiter.timer = setTimeout(() => {
|
|
246
|
+
cleanup();
|
|
247
|
+
removeWaiter();
|
|
248
|
+
state.queueTimeouts++;
|
|
249
|
+
reject(queueTimeoutError(key, this.queueTimeoutMs));
|
|
250
|
+
}, this.queueTimeoutMs);
|
|
251
|
+
}
|
|
252
|
+
state.queue.push(waiter);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
acquireWasm(key, signal) {
|
|
256
|
+
const wasm = this.wasm;
|
|
257
|
+
const state = this.getState(key);
|
|
258
|
+
if (signal.aborted) return Promise.reject(abortError());
|
|
259
|
+
const subjectId = this.allocateSubjectId();
|
|
260
|
+
const decision = wasm.acquire(key, subjectId);
|
|
261
|
+
if (decision.kind === "run") {
|
|
262
|
+
state.running++;
|
|
263
|
+
state.acquired++;
|
|
264
|
+
return Promise.resolve(this.makeLease(state, decision.keyId));
|
|
265
|
+
}
|
|
266
|
+
if (decision.kind === "rejected") {
|
|
267
|
+
state.rejected++;
|
|
268
|
+
return Promise.reject(poolRejectedError(key, this.maxQueue));
|
|
269
|
+
}
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
const waiter = { signal, resolve, reject };
|
|
272
|
+
const removeWaiter = () => this.removeWaiter(state, waiter);
|
|
273
|
+
const cleanup = () => this.cleanupWaiter(waiter);
|
|
274
|
+
waiter.abort = () => {
|
|
275
|
+
cleanup();
|
|
276
|
+
removeWaiter();
|
|
277
|
+
wasm.cancel(decision.permitId);
|
|
278
|
+
this.wasmWaiters.delete(decision.permitId);
|
|
279
|
+
state.abortedWhileQueued++;
|
|
280
|
+
reject(abortError());
|
|
281
|
+
};
|
|
282
|
+
signal.addEventListener("abort", waiter.abort, { once: true });
|
|
283
|
+
state.queue.push(waiter);
|
|
284
|
+
this.wasmWaiters.set(decision.permitId, { waiter, state, keyId: decision.keyId });
|
|
285
|
+
this.scheduleWasmTimeoutPump();
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
getState(key) {
|
|
289
|
+
const k = key.trim().slice(0, 160) || "global";
|
|
290
|
+
const existing = this.states.get(k);
|
|
291
|
+
if (existing) return existing;
|
|
292
|
+
const created = {
|
|
293
|
+
key: k,
|
|
294
|
+
running: 0,
|
|
295
|
+
queue: [],
|
|
296
|
+
acquired: 0,
|
|
297
|
+
released: 0,
|
|
298
|
+
rejected: 0,
|
|
299
|
+
queueTimeouts: 0,
|
|
300
|
+
abortedWhileQueued: 0
|
|
301
|
+
};
|
|
302
|
+
this.states.set(k, created);
|
|
303
|
+
return created;
|
|
304
|
+
}
|
|
305
|
+
makeLease(state, wasmKeyId) {
|
|
306
|
+
let released = false;
|
|
307
|
+
return {
|
|
308
|
+
key: state.key,
|
|
309
|
+
release: () => {
|
|
310
|
+
if (released) return;
|
|
311
|
+
released = true;
|
|
312
|
+
if (state.running > 0) state.running--;
|
|
313
|
+
state.released++;
|
|
314
|
+
if (this.wasm && wasmKeyId !== void 0) {
|
|
315
|
+
this.handleWasmGrants(this.wasm.release(wasmKeyId));
|
|
316
|
+
this.scheduleWasmTimeoutPump();
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.drain(state);
|
|
320
|
+
}
|
|
58
321
|
};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
);
|
|
322
|
+
}
|
|
323
|
+
drain(state) {
|
|
324
|
+
while (state.running < this.concurrency && state.queue.length > 0) {
|
|
325
|
+
const waiter = state.queue.shift();
|
|
326
|
+
this.cleanupWaiter(waiter);
|
|
327
|
+
if (waiter.signal.aborted) {
|
|
328
|
+
state.abortedWhileQueued++;
|
|
329
|
+
waiter.reject(abortError());
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
state.running++;
|
|
333
|
+
state.acquired++;
|
|
334
|
+
waiter.resolve(this.makeLease(state));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
handleWasmGrants(events) {
|
|
338
|
+
for (const event of events) {
|
|
339
|
+
const pending = this.wasmWaiters.get(event.permitId);
|
|
340
|
+
if (!pending) continue;
|
|
341
|
+
this.wasmWaiters.delete(event.permitId);
|
|
342
|
+
this.cleanupWaiter(pending.waiter);
|
|
343
|
+
this.removeWaiter(pending.state, pending.waiter);
|
|
344
|
+
if (pending.waiter.signal.aborted) {
|
|
345
|
+
pending.state.abortedWhileQueued++;
|
|
346
|
+
pending.waiter.reject(abortError());
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
pending.state.running++;
|
|
350
|
+
pending.state.acquired++;
|
|
351
|
+
pending.waiter.resolve(this.makeLease(pending.state, event.keyId));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
handleWasmTimeouts(events) {
|
|
355
|
+
for (const event of events) {
|
|
356
|
+
const pending = this.wasmWaiters.get(event.permitId);
|
|
357
|
+
if (!pending) continue;
|
|
358
|
+
this.wasmWaiters.delete(event.permitId);
|
|
359
|
+
this.cleanupWaiter(pending.waiter);
|
|
360
|
+
this.removeWaiter(pending.state, pending.waiter);
|
|
361
|
+
pending.state.queueTimeouts++;
|
|
362
|
+
pending.waiter.reject(queueTimeoutError(pending.state.key, _nullishCoalesce(this.queueTimeoutMs, () => ( 0))));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
scheduleWasmTimeoutPump() {
|
|
366
|
+
if (!this.wasm) return;
|
|
367
|
+
if (this.wasmTimer !== void 0) clearTimeout(this.wasmTimer);
|
|
368
|
+
this.wasmTimer = void 0;
|
|
369
|
+
const next = this.wasm.nextDeadlineMs();
|
|
370
|
+
if (!Number.isFinite(next) || next < 0) return;
|
|
371
|
+
const delay = Math.max(0, Math.min(2 ** 31 - 1, Math.floor(next - Date.now())));
|
|
372
|
+
this.wasmTimer = setTimeout(() => {
|
|
373
|
+
this.wasmTimer = void 0;
|
|
374
|
+
if (!this.wasm) return;
|
|
375
|
+
this.handleWasmTimeouts(this.wasm.advanceTime());
|
|
376
|
+
this.scheduleWasmTimeoutPump();
|
|
377
|
+
}, delay);
|
|
378
|
+
if (typeof this.wasmTimer.unref === "function") this.wasmTimer.unref();
|
|
379
|
+
}
|
|
380
|
+
cleanupWaiter(waiter) {
|
|
381
|
+
if (waiter.timer !== void 0) {
|
|
382
|
+
clearTimeout(waiter.timer);
|
|
383
|
+
waiter.timer = void 0;
|
|
384
|
+
}
|
|
385
|
+
if (waiter.abort) {
|
|
386
|
+
waiter.signal.removeEventListener("abort", waiter.abort);
|
|
387
|
+
waiter.abort = void 0;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
removeWaiter(state, waiter) {
|
|
391
|
+
const idx = state.queue.indexOf(waiter);
|
|
392
|
+
if (idx >= 0) state.queue.splice(idx, 1);
|
|
393
|
+
}
|
|
394
|
+
allocateSubjectId() {
|
|
395
|
+
const id = this.nextSubjectId >>> 0;
|
|
396
|
+
this.nextSubjectId = this.nextSubjectId + 1 >>> 0;
|
|
397
|
+
if (this.nextSubjectId === 0) this.nextSubjectId = 1;
|
|
398
|
+
return id === 0 ? this.allocateSubjectId() : id;
|
|
399
|
+
}
|
|
400
|
+
}, _class2);
|
|
63
401
|
|
|
64
402
|
// src/http/client.ts
|
|
65
|
-
var
|
|
66
|
-
|
|
67
|
-
|
|
403
|
+
var emptyStats = () => ({
|
|
404
|
+
inFlight: 0,
|
|
405
|
+
started: 0,
|
|
406
|
+
succeeded: 0,
|
|
407
|
+
failed: 0,
|
|
408
|
+
aborted: 0,
|
|
409
|
+
timedOut: 0,
|
|
410
|
+
poolRejected: 0,
|
|
411
|
+
poolTimeouts: 0
|
|
412
|
+
});
|
|
413
|
+
var decorate = (run, stats = emptyStats) => Object.assign(((req) => run(req)), {
|
|
414
|
+
with: (mw) => decorate(mw(run), stats),
|
|
415
|
+
stats
|
|
68
416
|
});
|
|
69
|
-
var
|
|
70
|
-
|
|
71
|
-
|
|
417
|
+
var withMiddleware = (mw) => (c) => decorate(mw(c), c.stats);
|
|
418
|
+
var decorateStream = (run, stats = emptyStats) => Object.assign(((req) => run(req)), { stats });
|
|
419
|
+
var isTaggedHttpError = (e) => {
|
|
420
|
+
if (typeof e !== "object" || e === null || !("_tag" in e)) return false;
|
|
421
|
+
const tag = e._tag;
|
|
422
|
+
return tag === "Abort" || tag === "BadUrl" || tag === "FetchError" || tag === "Timeout" || tag === "PoolRejected" || tag === "PoolTimeout";
|
|
423
|
+
};
|
|
424
|
+
var isAbortError = (e) => typeof e === "object" && e !== null && "name" in e && e.name === "AbortError";
|
|
425
|
+
var normalizeHttpError = (e) => {
|
|
426
|
+
if (isTaggedHttpError(e)) return e;
|
|
427
|
+
if (isAbortError(e)) return { _tag: "Abort" };
|
|
72
428
|
return { _tag: "FetchError", message: String(e) };
|
|
73
429
|
};
|
|
74
430
|
var normalizeHeadersInit = (h) => {
|
|
@@ -84,87 +440,202 @@ var normalizeHeadersInit = (h) => {
|
|
|
84
440
|
};
|
|
85
441
|
var normalizeRequest = (defaultHeaders) => (req0) => {
|
|
86
442
|
let req = Object.keys(defaultHeaders).length ? mergeHeadersUnder(defaultHeaders)(req0) : req0;
|
|
87
|
-
const initHeaders = normalizeHeadersInit(_optionalChain([req0, 'access',
|
|
443
|
+
const initHeaders = normalizeHeadersInit(_optionalChain([req0, 'access', _8 => _8.init, 'optionalAccess', _9 => _9.headers]));
|
|
88
444
|
if (initHeaders && Object.keys(initHeaders).length) {
|
|
89
445
|
req = mergeHeadersUnder(initHeaders)(req);
|
|
90
446
|
}
|
|
91
447
|
return req;
|
|
92
448
|
};
|
|
449
|
+
var resolvePositiveTimeout = (value) => {
|
|
450
|
+
if (value === void 0 || !Number.isFinite(value)) return void 0;
|
|
451
|
+
const n = Math.floor(value);
|
|
452
|
+
return n > 0 ? n : void 0;
|
|
453
|
+
};
|
|
454
|
+
var makeHttpStats = (pool) => {
|
|
455
|
+
const stats = {
|
|
456
|
+
inFlight: 0,
|
|
457
|
+
started: 0,
|
|
458
|
+
succeeded: 0,
|
|
459
|
+
failed: 0,
|
|
460
|
+
aborted: 0,
|
|
461
|
+
timedOut: 0,
|
|
462
|
+
poolRejected: 0,
|
|
463
|
+
poolTimeouts: 0
|
|
464
|
+
};
|
|
465
|
+
const onStart = () => {
|
|
466
|
+
stats.inFlight++;
|
|
467
|
+
stats.started++;
|
|
468
|
+
};
|
|
469
|
+
const onFinish = (finish) => {
|
|
470
|
+
if (stats.inFlight > 0) stats.inFlight--;
|
|
471
|
+
stats.lastDurationMs = finish.durationMs;
|
|
472
|
+
if (finish.outcome === "success") {
|
|
473
|
+
stats.succeeded++;
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (finish.outcome === "interrupt") {
|
|
477
|
+
stats.aborted++;
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (finish.outcome === "timeout") {
|
|
481
|
+
stats.timedOut++;
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const err = normalizeHttpError(finish.error);
|
|
485
|
+
switch (err._tag) {
|
|
486
|
+
case "Abort":
|
|
487
|
+
stats.aborted++;
|
|
488
|
+
return;
|
|
489
|
+
case "Timeout":
|
|
490
|
+
stats.timedOut++;
|
|
491
|
+
return;
|
|
492
|
+
case "PoolRejected":
|
|
493
|
+
stats.poolRejected++;
|
|
494
|
+
stats.failed++;
|
|
495
|
+
return;
|
|
496
|
+
case "PoolTimeout":
|
|
497
|
+
stats.poolTimeouts++;
|
|
498
|
+
stats.failed++;
|
|
499
|
+
return;
|
|
500
|
+
default:
|
|
501
|
+
stats.failed++;
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
const snapshot = () => ({
|
|
506
|
+
...stats,
|
|
507
|
+
...pool ? { pool: pool.stats() } : {}
|
|
508
|
+
});
|
|
509
|
+
return { onStart, onFinish, snapshot };
|
|
510
|
+
};
|
|
511
|
+
var makePool = (cfg) => cfg.pool === void 0 || cfg.pool === false ? void 0 : new HttpConcurrencyPool(cfg.pool);
|
|
512
|
+
var resolveRequestUrl = (req, baseUrl) => {
|
|
513
|
+
try {
|
|
514
|
+
return new URL(req.url, baseUrl);
|
|
515
|
+
} catch (e2) {
|
|
516
|
+
return { _tag: "BadUrl", message: `URL inv\xE1lida: ${req.url}` };
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
var headersOf = (res) => {
|
|
520
|
+
const headers = {};
|
|
521
|
+
res.headers.forEach((v, k) => headers[k] = v);
|
|
522
|
+
return headers;
|
|
523
|
+
};
|
|
524
|
+
var fetchLabel = (req, url) => `http:${req.method}:${url.origin}`;
|
|
525
|
+
var timeoutReason = (req, url, timeoutMs) => ({
|
|
526
|
+
_tag: "Timeout",
|
|
527
|
+
timeoutMs,
|
|
528
|
+
phase: "request",
|
|
529
|
+
message: `HTTP ${req.method} ${url.origin} timed out after ${timeoutMs}ms`
|
|
530
|
+
});
|
|
93
531
|
function makeHttpStream(cfg = {}) {
|
|
94
532
|
const baseUrl = _nullishCoalesce(cfg.baseUrl, () => ( ""));
|
|
95
533
|
const defaultHeaders = _nullishCoalesce(cfg.headers, () => ( {}));
|
|
96
534
|
const normalize = normalizeRequest(defaultHeaders);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
535
|
+
const pool = makePool(cfg);
|
|
536
|
+
const metrics = makeHttpStats(pool);
|
|
537
|
+
const run = (req0) => {
|
|
538
|
+
const req = normalize(req0);
|
|
539
|
+
const url = resolveRequestUrl(req, baseUrl);
|
|
540
|
+
if (!(url instanceof URL)) return _chunkBKBFSOGTcjs.asyncFail.call(void 0, url);
|
|
541
|
+
const timeoutMs = resolvePositiveTimeout(_nullishCoalesce(req.timeoutMs, () => ( cfg.timeoutMs)));
|
|
542
|
+
return _chunkBKBFSOGTcjs.fromPromiseAbortable.call(void 0,
|
|
543
|
+
async (signal) => {
|
|
544
|
+
let lease;
|
|
545
|
+
try {
|
|
546
|
+
if (pool) {
|
|
547
|
+
const key = resolveHttpPoolKey(pool.keyResolver, req, url);
|
|
548
|
+
lease = await pool.acquire(key, signal);
|
|
549
|
+
}
|
|
550
|
+
const started = performance.now();
|
|
551
|
+
const res = await fetch(url, {
|
|
552
|
+
..._nullishCoalesce(req.init, () => ( {})),
|
|
553
|
+
method: req.method,
|
|
554
|
+
headers: Request.headers.get(req),
|
|
555
|
+
body: req.body,
|
|
556
|
+
signal
|
|
557
|
+
});
|
|
558
|
+
const headers = headersOf(res);
|
|
559
|
+
const body = _chunkSKVY72E5cjs.streamFromReadableStream.call(void 0, res.body, normalizeHttpError);
|
|
560
|
+
_optionalChain([lease, 'optionalAccess', _10 => _10.release, 'call', _11 => _11()]);
|
|
561
|
+
lease = void 0;
|
|
562
|
+
return {
|
|
563
|
+
status: res.status,
|
|
564
|
+
statusText: res.statusText,
|
|
565
|
+
headers,
|
|
566
|
+
body,
|
|
567
|
+
ms: Math.round(performance.now() - started)
|
|
568
|
+
};
|
|
569
|
+
} finally {
|
|
570
|
+
_optionalChain([lease, 'optionalAccess', _12 => _12.release, 'call', _13 => _13()]);
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
normalizeHttpError,
|
|
574
|
+
{
|
|
575
|
+
label: fetchLabel(req, url),
|
|
576
|
+
timeoutMs,
|
|
577
|
+
timeoutReason: timeoutMs ? () => timeoutReason(req, url, timeoutMs) : void 0,
|
|
578
|
+
onStart: metrics.onStart,
|
|
579
|
+
onFinish: metrics.onFinish
|
|
105
580
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
method: req.method,
|
|
110
|
-
headers: Request.headers.get(req),
|
|
111
|
-
// 👈 optics: headers ya normalizados
|
|
112
|
-
body: req.body,
|
|
113
|
-
signal
|
|
114
|
-
});
|
|
115
|
-
const headers = {};
|
|
116
|
-
res.headers.forEach((v, k) => headers[k] = v);
|
|
117
|
-
const body = _chunkZTDK2DLGcjs.streamFromReadableStream.call(void 0, res.body, normalizeHttpError2);
|
|
118
|
-
return {
|
|
119
|
-
status: res.status,
|
|
120
|
-
statusText: res.statusText,
|
|
121
|
-
headers,
|
|
122
|
-
body,
|
|
123
|
-
ms: Math.round(performance.now() - started)
|
|
124
|
-
};
|
|
125
|
-
},
|
|
126
|
-
normalizeHttpError2
|
|
127
|
-
);
|
|
581
|
+
);
|
|
582
|
+
};
|
|
583
|
+
return decorateStream(run, metrics.snapshot);
|
|
128
584
|
}
|
|
129
585
|
function makeHttp(cfg = {}) {
|
|
130
586
|
const baseUrl = _nullishCoalesce(cfg.baseUrl, () => ( ""));
|
|
131
587
|
const defaultHeaders = _nullishCoalesce(cfg.headers, () => ( {}));
|
|
132
588
|
const normalize = normalizeRequest(defaultHeaders);
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
589
|
+
const pool = makePool(cfg);
|
|
590
|
+
const metrics = makeHttpStats(pool);
|
|
591
|
+
const run = (req0) => {
|
|
592
|
+
const req = normalize(req0);
|
|
593
|
+
const url = resolveRequestUrl(req, baseUrl);
|
|
594
|
+
if (!(url instanceof URL)) return _chunkBKBFSOGTcjs.asyncFail.call(void 0, url);
|
|
595
|
+
const timeoutMs = resolvePositiveTimeout(_nullishCoalesce(req.timeoutMs, () => ( cfg.timeoutMs)));
|
|
596
|
+
return _chunkBKBFSOGTcjs.fromPromiseAbortable.call(void 0,
|
|
597
|
+
async (signal) => {
|
|
598
|
+
let lease;
|
|
599
|
+
try {
|
|
600
|
+
if (pool) {
|
|
601
|
+
const key = resolveHttpPoolKey(pool.keyResolver, req, url);
|
|
602
|
+
lease = await pool.acquire(key, signal);
|
|
603
|
+
}
|
|
604
|
+
const started = performance.now();
|
|
605
|
+
const res = await fetch(url, {
|
|
606
|
+
..._nullishCoalesce(req.init, () => ( {})),
|
|
607
|
+
method: req.method,
|
|
608
|
+
headers: Request.headers.get(req),
|
|
609
|
+
body: req.body,
|
|
610
|
+
signal
|
|
611
|
+
});
|
|
612
|
+
const bodyText = await res.text();
|
|
613
|
+
const headers = headersOf(res);
|
|
614
|
+
return {
|
|
615
|
+
status: res.status,
|
|
616
|
+
statusText: res.statusText,
|
|
617
|
+
headers,
|
|
618
|
+
bodyText,
|
|
619
|
+
ms: Math.round(performance.now() - started)
|
|
620
|
+
};
|
|
621
|
+
} finally {
|
|
622
|
+
_optionalChain([lease, 'optionalAccess', _14 => _14.release, 'call', _15 => _15()]);
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
normalizeHttpError,
|
|
626
|
+
{
|
|
627
|
+
label: fetchLabel(req, url),
|
|
628
|
+
timeoutMs,
|
|
629
|
+
timeoutReason: timeoutMs ? () => timeoutReason(req, url, timeoutMs) : void 0,
|
|
630
|
+
onStart: metrics.onStart,
|
|
631
|
+
onFinish: metrics.onFinish
|
|
141
632
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
method: req.method,
|
|
146
|
-
headers: Request.headers.get(req),
|
|
147
|
-
// 👈 optics
|
|
148
|
-
body: req.body,
|
|
149
|
-
signal
|
|
150
|
-
});
|
|
151
|
-
const bodyText = await res.text();
|
|
152
|
-
const headers = {};
|
|
153
|
-
res.headers.forEach((v, k) => headers[k] = v);
|
|
154
|
-
return {
|
|
155
|
-
status: res.status,
|
|
156
|
-
statusText: res.statusText,
|
|
157
|
-
headers,
|
|
158
|
-
bodyText,
|
|
159
|
-
ms: Math.round(performance.now() - started)
|
|
160
|
-
};
|
|
161
|
-
},
|
|
162
|
-
normalizeHttpError2
|
|
163
|
-
);
|
|
164
|
-
return decorate(run);
|
|
633
|
+
);
|
|
634
|
+
};
|
|
635
|
+
return decorate(run, metrics.snapshot);
|
|
165
636
|
}
|
|
166
637
|
var clamp = (n, min, max) => Math.max(min, Math.min(max, n));
|
|
167
|
-
var defaultRetryOnError = (e) => e._tag === "FetchError";
|
|
638
|
+
var defaultRetryOnError = (e) => e._tag === "FetchError" || e._tag === "Timeout" || e._tag === "PoolTimeout";
|
|
168
639
|
var defaultRetryOnStatus = (s) => s === 408 || s === 429 || s === 500 || s === 502 || s === 503 || s === 504;
|
|
169
640
|
var backoffDelayMs = (attempt, base, cap) => {
|
|
170
641
|
const exp = base * Math.pow(2, attempt);
|
|
@@ -174,7 +645,7 @@ var backoffDelayMs = (attempt, base, cap) => {
|
|
|
174
645
|
var retryAfterMs = (headers) => {
|
|
175
646
|
const key = Object.keys(headers).find((k) => k.toLowerCase() === "retry-after");
|
|
176
647
|
if (!key) return void 0;
|
|
177
|
-
const v = _optionalChain([headers, 'access',
|
|
648
|
+
const v = _optionalChain([headers, 'access', _16 => _16[key], 'optionalAccess', _17 => _17.trim, 'call', _18 => _18()]);
|
|
178
649
|
if (!v) return void 0;
|
|
179
650
|
const secs = Number(v);
|
|
180
651
|
if (Number.isFinite(secs)) return Math.max(0, Math.floor(secs * 1e3));
|
|
@@ -182,31 +653,85 @@ var retryAfterMs = (headers) => {
|
|
|
182
653
|
if (Number.isFinite(t)) return Math.max(0, t - Date.now());
|
|
183
654
|
return void 0;
|
|
184
655
|
};
|
|
185
|
-
var withRetryStream = (p) => (next) =>
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
656
|
+
var withRetryStream = (p) => (next) => {
|
|
657
|
+
const retryOnStatus = _nullishCoalesce(p.retryOnStatus, () => ( defaultRetryOnStatus));
|
|
658
|
+
const retryOnError = _nullishCoalesce(p.retryOnError, () => ( defaultRetryOnError));
|
|
659
|
+
const maxElapsedMs = p.maxElapsedMs !== void 0 && Number.isFinite(p.maxElapsedMs) ? Math.max(0, Math.floor(p.maxElapsedMs)) : void 0;
|
|
660
|
+
const run = (req) => {
|
|
661
|
+
const startedAt = performance.now();
|
|
662
|
+
const remainingBudget = () => maxElapsedMs === void 0 ? Number.POSITIVE_INFINITY : maxElapsedMs - (performance.now() - startedAt);
|
|
663
|
+
const delayWithinBudget = (delayMs) => Math.max(0, Math.min(delayMs, remainingBudget()));
|
|
664
|
+
const loop = (attempt) => _chunkBKBFSOGTcjs.asyncFold.call(void 0,
|
|
665
|
+
next(req),
|
|
666
|
+
(e) => {
|
|
667
|
+
if (e._tag === "Abort" || e._tag === "BadUrl" || e._tag === "PoolRejected") return _chunkBKBFSOGTcjs.asyncFail.call(void 0, e);
|
|
668
|
+
const canRetry = attempt < p.maxRetries && retryOnError(e) && remainingBudget() > 0;
|
|
669
|
+
if (!canRetry) return _chunkBKBFSOGTcjs.asyncFail.call(void 0, e);
|
|
670
|
+
const d = delayWithinBudget(backoffDelayMs(attempt, p.baseDelayMs, p.maxDelayMs));
|
|
671
|
+
if (d <= 0 && maxElapsedMs !== void 0) return _chunkBKBFSOGTcjs.asyncFail.call(void 0, e);
|
|
672
|
+
return _chunkBKBFSOGTcjs.asyncFlatMap.call(void 0, _chunkSKVY72E5cjs.sleep.call(void 0, d), () => loop(attempt + 1));
|
|
673
|
+
},
|
|
674
|
+
(w) => {
|
|
675
|
+
const canRetry = attempt < p.maxRetries && retryOnStatus(w.status) && remainingBudget() > 0;
|
|
676
|
+
if (!canRetry) return _chunkBKBFSOGTcjs.asyncSucceed.call(void 0, w);
|
|
677
|
+
const ra = p.respectRetryAfter === false ? void 0 : retryAfterMs(w.headers);
|
|
678
|
+
const rawDelay = ra === void 0 ? backoffDelayMs(attempt, p.baseDelayMs, p.maxDelayMs) : Math.min(ra, p.maxDelayMs);
|
|
679
|
+
const d = delayWithinBudget(rawDelay);
|
|
680
|
+
if (d <= 0 && maxElapsedMs !== void 0) return _chunkBKBFSOGTcjs.asyncSucceed.call(void 0, w);
|
|
681
|
+
return _chunkBKBFSOGTcjs.asyncFlatMap.call(void 0, _chunkSKVY72E5cjs.sleep.call(void 0, d), () => loop(attempt + 1));
|
|
682
|
+
}
|
|
683
|
+
);
|
|
684
|
+
return loop(0);
|
|
685
|
+
};
|
|
686
|
+
return decorateStream(run, next.stats);
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
// src/http/retry/wasmRetryPlanner.ts
|
|
690
|
+
var WasmRetryPlannerBridge = class {
|
|
691
|
+
|
|
692
|
+
constructor(Ctor) {
|
|
693
|
+
this.planner = new Ctor();
|
|
694
|
+
}
|
|
695
|
+
start(options) {
|
|
696
|
+
return this.planner.start(
|
|
697
|
+
options.nowMs,
|
|
698
|
+
options.maxRetries,
|
|
699
|
+
options.baseDelayMs,
|
|
700
|
+
options.maxDelayMs,
|
|
701
|
+
_nullishCoalesce(options.maxElapsedMs, () => ( -1)),
|
|
702
|
+
BigInt(this.seed())
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
nextDelayMs(retryId, options) {
|
|
706
|
+
const delay = this.planner.next_delay_ms(retryId, options.nowMs, options.retryable, _nullishCoalesce(options.retryAfterMs, () => ( -1)));
|
|
707
|
+
return delay < 0 ? void 0 : delay;
|
|
708
|
+
}
|
|
709
|
+
drop(retryId) {
|
|
710
|
+
this.planner.drop_state(retryId);
|
|
711
|
+
}
|
|
712
|
+
stats() {
|
|
713
|
+
return {
|
|
714
|
+
live: this.planner.metric_u64(0),
|
|
715
|
+
planned: this.planner.metric_u64(1),
|
|
716
|
+
exhausted: this.planner.metric_u64(2),
|
|
717
|
+
dropped: this.planner.metric_u64(3)
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
seed() {
|
|
721
|
+
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
function makeWasmRetryPlanner() {
|
|
725
|
+
const mod = _chunkBKBFSOGTcjs.resolveWasmModule.call(void 0, );
|
|
726
|
+
const Ctor = _optionalChain([mod, 'optionalAccess', _19 => _19.BrassWasmRetryPlanner]);
|
|
727
|
+
if (!Ctor) throw new Error("brass-runtime wasm retry planner is not available. Run npm run build:wasm first.");
|
|
728
|
+
return new WasmRetryPlannerBridge(Ctor);
|
|
729
|
+
}
|
|
205
730
|
|
|
206
731
|
// src/http/retry/retry.ts
|
|
207
732
|
var defaultRetryableMethods = ["GET", "HEAD", "OPTIONS"];
|
|
208
733
|
var defaultRetryOnStatus2 = (s) => s === 408 || s === 429 || s === 500 || s === 502 || s === 503 || s === 504;
|
|
209
|
-
var defaultRetryOnError2 = (e) => e._tag === "FetchError";
|
|
734
|
+
var defaultRetryOnError2 = (e) => e._tag === "FetchError" || e._tag === "Timeout" || e._tag === "PoolTimeout";
|
|
210
735
|
var clamp2 = (n, min, max) => Math.max(min, Math.min(max, n));
|
|
211
736
|
var backoffDelayMs2 = (attempt, base, cap) => {
|
|
212
737
|
const b = Math.max(0, base);
|
|
@@ -220,7 +745,7 @@ var headerCI = (h, name) => {
|
|
|
220
745
|
return k ? h[k] : void 0;
|
|
221
746
|
};
|
|
222
747
|
var retryAfterMs2 = (headers) => {
|
|
223
|
-
const v = _optionalChain([headerCI, 'call',
|
|
748
|
+
const v = _optionalChain([headerCI, 'call', _20 => _20(headers, "retry-after"), 'optionalAccess', _21 => _21.trim, 'call', _22 => _22()]);
|
|
224
749
|
if (!v) return void 0;
|
|
225
750
|
const secs = Number(v);
|
|
226
751
|
if (Number.isFinite(secs)) return Math.max(0, Math.floor(secs * 1e3));
|
|
@@ -228,50 +753,106 @@ var retryAfterMs2 = (headers) => {
|
|
|
228
753
|
if (Number.isFinite(t)) return Math.max(0, t - Date.now());
|
|
229
754
|
return void 0;
|
|
230
755
|
};
|
|
756
|
+
var normalizeBudget = (ms) => {
|
|
757
|
+
if (ms === void 0 || !Number.isFinite(ms)) return void 0;
|
|
758
|
+
return Math.max(0, Math.floor(ms));
|
|
759
|
+
};
|
|
760
|
+
var resolveRetryEngine = (p) => {
|
|
761
|
+
if (p.engine !== void 0) {
|
|
762
|
+
if (p.engine === "ts" || p.engine === "wasm") return p.engine;
|
|
763
|
+
throw new Error(`brass-runtime retry engine must be 'ts' or 'wasm'; received '${String(p.engine)}'`);
|
|
764
|
+
}
|
|
765
|
+
if (p.wasm === true) return "wasm";
|
|
766
|
+
if (p.wasm === false) return "ts";
|
|
767
|
+
return "ts";
|
|
768
|
+
};
|
|
231
769
|
var withRetry = (p) => (next) => {
|
|
232
770
|
const retryOnMethods = _nullishCoalesce(p.retryOnMethods, () => ( defaultRetryableMethods));
|
|
233
771
|
const retryOnStatus = _nullishCoalesce(p.retryOnStatus, () => ( defaultRetryOnStatus2));
|
|
234
772
|
const retryOnError = _nullishCoalesce(p.retryOnError, () => ( defaultRetryOnError2));
|
|
773
|
+
const maxElapsedMs = normalizeBudget(p.maxElapsedMs);
|
|
774
|
+
const retryEngine = resolveRetryEngine(p);
|
|
775
|
+
const wasmPlanner = retryEngine === "wasm" ? makeWasmRetryPlanner() : void 0;
|
|
235
776
|
const isMethodRetryable = (req) => retryOnMethods.includes(req.method);
|
|
236
|
-
const
|
|
777
|
+
const nextDelay = (retryId, attempt, startedAt, retryable, retryAfter) => {
|
|
778
|
+
if (!retryable) return void 0;
|
|
779
|
+
if (wasmPlanner && retryId !== void 0) {
|
|
780
|
+
return wasmPlanner.nextDelayMs(retryId, {
|
|
781
|
+
nowMs: performance.now(),
|
|
782
|
+
retryable,
|
|
783
|
+
retryAfterMs: retryAfter
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
const remainingBudget = maxElapsedMs === void 0 ? Number.POSITIVE_INFINITY : maxElapsedMs - (performance.now() - startedAt);
|
|
787
|
+
if (remainingBudget <= 0) return void 0;
|
|
788
|
+
const rawDelay = retryAfter === void 0 ? backoffDelayMs2(attempt, p.baseDelayMs, p.maxDelayMs) : Math.min(retryAfter, p.maxDelayMs);
|
|
789
|
+
return Math.max(0, Math.min(rawDelay, remainingBudget));
|
|
790
|
+
};
|
|
791
|
+
const dropPlanner = (retryId) => {
|
|
792
|
+
if (retryId !== void 0) _optionalChain([wasmPlanner, 'optionalAccess', _23 => _23.drop, 'call', _24 => _24(retryId)]);
|
|
793
|
+
};
|
|
794
|
+
const loop = (req, attempt, startedAt, retryId) => {
|
|
237
795
|
if (!isMethodRetryable(req)) return next(req);
|
|
238
|
-
|
|
796
|
+
const remainingBudget = () => maxElapsedMs === void 0 ? Number.POSITIVE_INFINITY : maxElapsedMs - (performance.now() - startedAt);
|
|
797
|
+
return _chunkBKBFSOGTcjs.asyncFold.call(void 0,
|
|
239
798
|
next(req),
|
|
240
799
|
(e) => {
|
|
241
|
-
if (e._tag === "Abort" || e._tag === "BadUrl"
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
800
|
+
if (e._tag === "Abort" || e._tag === "BadUrl" || e._tag === "PoolRejected") {
|
|
801
|
+
dropPlanner(retryId);
|
|
802
|
+
return _chunkBKBFSOGTcjs.asyncFail.call(void 0, e);
|
|
803
|
+
}
|
|
804
|
+
const retryable = attempt < p.maxRetries && retryOnError(e) && remainingBudget() > 0;
|
|
805
|
+
const d = nextDelay(retryId, attempt, startedAt, retryable);
|
|
806
|
+
if (d === void 0 || d <= 0 && maxElapsedMs !== void 0) {
|
|
807
|
+
dropPlanner(retryId);
|
|
808
|
+
return _chunkBKBFSOGTcjs.asyncFail.call(void 0, e);
|
|
809
|
+
}
|
|
810
|
+
return _chunkBKBFSOGTcjs.asyncFlatMap.call(void 0, _chunkSKVY72E5cjs.sleep.call(void 0, d), () => loop(req, attempt + 1, startedAt, retryId));
|
|
246
811
|
},
|
|
247
812
|
(w) => {
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
813
|
+
const retryable = attempt < p.maxRetries && retryOnStatus(w.status) && remainingBudget() > 0;
|
|
814
|
+
const ra = p.respectRetryAfter === false ? void 0 : retryAfterMs2(w.headers);
|
|
815
|
+
const d = nextDelay(retryId, attempt, startedAt, retryable, ra);
|
|
816
|
+
if (d === void 0 || d <= 0 && maxElapsedMs !== void 0) {
|
|
817
|
+
dropPlanner(retryId);
|
|
818
|
+
return _chunkBKBFSOGTcjs.asyncSucceed.call(void 0, w);
|
|
819
|
+
}
|
|
820
|
+
return _chunkBKBFSOGTcjs.asyncFlatMap.call(void 0, _chunkSKVY72E5cjs.sleep.call(void 0, d), () => loop(req, attempt + 1, startedAt, retryId));
|
|
253
821
|
}
|
|
254
822
|
);
|
|
255
823
|
};
|
|
256
|
-
return (req) =>
|
|
824
|
+
return (req) => {
|
|
825
|
+
if (!isMethodRetryable(req)) return next(req);
|
|
826
|
+
const startedAt = performance.now();
|
|
827
|
+
const retryId = _optionalChain([wasmPlanner, 'optionalAccess', _25 => _25.start, 'call', _26 => _26({
|
|
828
|
+
nowMs: startedAt,
|
|
829
|
+
maxRetries: p.maxRetries,
|
|
830
|
+
baseDelayMs: p.baseDelayMs,
|
|
831
|
+
maxDelayMs: p.maxDelayMs,
|
|
832
|
+
maxElapsedMs
|
|
833
|
+
})]);
|
|
834
|
+
return loop(req, 0, startedAt, retryId);
|
|
835
|
+
};
|
|
257
836
|
};
|
|
258
837
|
|
|
259
838
|
// src/http/httpClient.ts
|
|
260
839
|
var resolveFinalUrl = (baseUrl, url) => {
|
|
261
840
|
try {
|
|
262
841
|
return new URL(url, _nullishCoalesce(baseUrl, () => ( ""))).toString();
|
|
263
|
-
} catch (
|
|
842
|
+
} catch (e3) {
|
|
264
843
|
return (_nullishCoalesce(baseUrl, () => ( ""))) + url;
|
|
265
844
|
}
|
|
266
845
|
};
|
|
267
846
|
var createHttpCore = (cfg = {}) => {
|
|
268
847
|
const wire = makeHttp(cfg);
|
|
269
|
-
const withPromise = (eff) =>
|
|
848
|
+
const withPromise = (eff) => _chunkBKBFSOGTcjs.withAsyncPromise.call(void 0, (e, env) => _chunkBKBFSOGTcjs.toPromise.call(void 0, e, env))(eff);
|
|
270
849
|
const requestRaw = (req) => wire(req);
|
|
271
850
|
const splitInit = (init) => {
|
|
272
|
-
const { headers, ...rest } = _nullishCoalesce(init, () => ( {}));
|
|
851
|
+
const { headers, timeoutMs, poolKey, ...rest } = _nullishCoalesce(init, () => ( {}));
|
|
273
852
|
return {
|
|
274
853
|
headers: normalizeHeadersInit(headers),
|
|
854
|
+
timeoutMs: typeof timeoutMs === "number" ? timeoutMs : void 0,
|
|
855
|
+
poolKey: typeof poolKey === "string" ? poolKey : void 0,
|
|
275
856
|
init: rest
|
|
276
857
|
};
|
|
277
858
|
};
|
|
@@ -282,6 +863,8 @@ var createHttpCore = (cfg = {}) => {
|
|
|
282
863
|
method,
|
|
283
864
|
url,
|
|
284
865
|
...body && body.length > 0 ? { body } : {},
|
|
866
|
+
...s.timeoutMs !== void 0 ? { timeoutMs: s.timeoutMs } : {},
|
|
867
|
+
...s.poolKey !== void 0 ? { poolKey: s.poolKey } : {},
|
|
285
868
|
init: s.init
|
|
286
869
|
};
|
|
287
870
|
return applyInitHeaders(s.headers)(req);
|
|
@@ -312,12 +895,12 @@ function httpClient(cfg = {}) {
|
|
|
312
895
|
const post = (url, body, init) => request(core.buildReq("POST", url, init, body));
|
|
313
896
|
const getText = (url, init) => {
|
|
314
897
|
const req = core.buildReq("GET", url, init);
|
|
315
|
-
return core.withPromise(
|
|
898
|
+
return core.withPromise(_chunkBKBFSOGTcjs.mapTryAsync.call(void 0, requestRaw(req), (w) => core.toResponse(w, w.bodyText)));
|
|
316
899
|
};
|
|
317
900
|
const getJson = (url, init) => {
|
|
318
901
|
const base = core.buildReq("GET", url, init);
|
|
319
902
|
const req = setHeaderIfMissing("accept", "application/json")(base);
|
|
320
|
-
return core.withPromise(
|
|
903
|
+
return core.withPromise(_chunkBKBFSOGTcjs.mapTryAsync.call(void 0, requestRaw(req), (w) => core.toResponse(w, JSON.parse(w.bodyText))));
|
|
321
904
|
};
|
|
322
905
|
const postJson = (url, bodyObj, init) => {
|
|
323
906
|
const base = core.buildReq("POST", url, init, JSON.stringify(_nullishCoalesce(bodyObj, () => ( {}))));
|
|
@@ -325,7 +908,7 @@ function httpClient(cfg = {}) {
|
|
|
325
908
|
setHeaderIfMissing("accept", "application/json")(base)
|
|
326
909
|
);
|
|
327
910
|
return core.withPromise(
|
|
328
|
-
|
|
911
|
+
_chunkBKBFSOGTcjs.mapTryAsync.call(void 0, requestRaw(req), (w) => core.toResponse(w, JSON.parse(w.bodyText)))
|
|
329
912
|
);
|
|
330
913
|
};
|
|
331
914
|
return {
|
|
@@ -337,7 +920,8 @@ function httpClient(cfg = {}) {
|
|
|
337
920
|
postJson,
|
|
338
921
|
with: (mw) => make(wire.with(mw)),
|
|
339
922
|
withRetry: (p) => make(wire.with(withRetry(p))),
|
|
340
|
-
wire
|
|
923
|
+
wire,
|
|
924
|
+
stats: () => wire.stats()
|
|
341
925
|
};
|
|
342
926
|
};
|
|
343
927
|
return make(core.wire);
|
|
@@ -353,7 +937,7 @@ function httpClientWithMeta(cfg = {}) {
|
|
|
353
937
|
const request = (req) => {
|
|
354
938
|
const startedAt = Date.now();
|
|
355
939
|
return core.withPromise(
|
|
356
|
-
|
|
940
|
+
_chunkBKBFSOGTcjs.mapTryAsync.call(void 0, core.requestRaw(req), (w) => ({
|
|
357
941
|
wire: w,
|
|
358
942
|
meta: mkMeta(req, w, startedAt)
|
|
359
943
|
}))
|
|
@@ -374,7 +958,7 @@ function httpClientWithMeta(cfg = {}) {
|
|
|
374
958
|
);
|
|
375
959
|
const startedAt = Date.now();
|
|
376
960
|
return core.withPromise(
|
|
377
|
-
|
|
961
|
+
_chunkBKBFSOGTcjs.mapTryAsync.call(void 0, core.requestRaw(req), (w) => ({
|
|
378
962
|
wire: w,
|
|
379
963
|
response: core.toResponse(w, JSON.parse(w.bodyText)),
|
|
380
964
|
meta: mkMeta(req, w, startedAt)
|
|
@@ -385,7 +969,7 @@ function httpClientWithMeta(cfg = {}) {
|
|
|
385
969
|
const req = core.buildReq("GET", url, init);
|
|
386
970
|
const startedAt = Date.now();
|
|
387
971
|
return core.withPromise(
|
|
388
|
-
|
|
972
|
+
_chunkBKBFSOGTcjs.mapTryAsync.call(void 0, core.requestRaw(req), (w) => ({
|
|
389
973
|
wire: w,
|
|
390
974
|
response: core.toResponse(w, w.bodyText),
|
|
391
975
|
meta: mkMeta(req, w, startedAt)
|
|
@@ -397,7 +981,7 @@ function httpClientWithMeta(cfg = {}) {
|
|
|
397
981
|
const req = setHeaderIfMissing("accept", "application/json")(base);
|
|
398
982
|
const startedAt = Date.now();
|
|
399
983
|
return core.withPromise(
|
|
400
|
-
|
|
984
|
+
_chunkBKBFSOGTcjs.mapTryAsync.call(void 0, core.requestRaw(req), (w) => ({
|
|
401
985
|
wire: w,
|
|
402
986
|
response: core.toResponse(w, JSON.parse(w.bodyText)),
|
|
403
987
|
meta: mkMeta(req, w, startedAt)
|
|
@@ -422,7 +1006,7 @@ function httpClientWithMeta(cfg = {}) {
|
|
|
422
1006
|
function httpClientStream(cfg = {}) {
|
|
423
1007
|
const wire = makeHttpStream(cfg);
|
|
424
1008
|
const make = (w) => {
|
|
425
|
-
const withPromise = (eff) =>
|
|
1009
|
+
const withPromise = (eff) => _chunkBKBFSOGTcjs.withAsyncPromise.call(void 0, (e, env) => _chunkBKBFSOGTcjs.toPromise.call(void 0, e, env))(eff);
|
|
426
1010
|
const request = (req) => withPromise(w(req));
|
|
427
1011
|
const getStream = (url, init) => {
|
|
428
1012
|
const base = { method: "GET", url, init };
|
|
@@ -435,12 +1019,96 @@ function httpClientStream(cfg = {}) {
|
|
|
435
1019
|
get: getStream,
|
|
436
1020
|
with: (mw) => make(mw(w)),
|
|
437
1021
|
withRetry: (p) => make(withRetryStream(p)(w)),
|
|
438
|
-
wire: w
|
|
1022
|
+
wire: w,
|
|
1023
|
+
stats: () => w.stats()
|
|
439
1024
|
};
|
|
440
1025
|
};
|
|
441
1026
|
return make(wire);
|
|
442
1027
|
}
|
|
443
1028
|
|
|
1029
|
+
// src/http/circuitBreaker.ts
|
|
1030
|
+
function withCircuitBreaker(config = {}) {
|
|
1031
|
+
if (config.perOrigin) {
|
|
1032
|
+
const breakers = /* @__PURE__ */ new Map();
|
|
1033
|
+
const getBreaker = (url) => {
|
|
1034
|
+
try {
|
|
1035
|
+
const origin = new URL(url).origin;
|
|
1036
|
+
if (!breakers.has(origin)) {
|
|
1037
|
+
breakers.set(origin, _chunkSKVY72E5cjs.makeCircuitBreaker.call(void 0, config));
|
|
1038
|
+
}
|
|
1039
|
+
return breakers.get(origin);
|
|
1040
|
+
} catch (e4) {
|
|
1041
|
+
if (!breakers.has("__global__")) {
|
|
1042
|
+
breakers.set("__global__", _chunkSKVY72E5cjs.makeCircuitBreaker.call(void 0, config));
|
|
1043
|
+
}
|
|
1044
|
+
return breakers.get("__global__");
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
return (next) => (req) => {
|
|
1048
|
+
const breaker2 = getBreaker(req.url);
|
|
1049
|
+
return breaker2.protect(next(req));
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
const breaker = _chunkSKVY72E5cjs.makeCircuitBreaker.call(void 0, {
|
|
1053
|
+
...config,
|
|
1054
|
+
isFailure: _nullishCoalesce(config.isFailure, () => ( ((e) => {
|
|
1055
|
+
const err = e;
|
|
1056
|
+
return err._tag !== "BadUrl" && err._tag !== "Abort";
|
|
1057
|
+
})))
|
|
1058
|
+
});
|
|
1059
|
+
return (next) => (req) => {
|
|
1060
|
+
return breaker.protect(next(req));
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// src/http/tracing.ts
|
|
1065
|
+
function withTracing(tracer) {
|
|
1066
|
+
return (next) => (req) => {
|
|
1067
|
+
return tracer.span(
|
|
1068
|
+
`HTTP ${req.method} ${req.url}`,
|
|
1069
|
+
next(req),
|
|
1070
|
+
{
|
|
1071
|
+
"http.method": req.method,
|
|
1072
|
+
"http.url": req.url,
|
|
1073
|
+
..._optionalChain([req, 'access', _27 => _27.headers, 'optionalAccess', _28 => _28["content-type"]]) ? { "http.content_type": req.headers["content-type"] } : {}
|
|
1074
|
+
}
|
|
1075
|
+
);
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// src/http/validation.ts
|
|
1080
|
+
function validatedJson(client, validator) {
|
|
1081
|
+
return (req) => _chunkBKBFSOGTcjs.asyncFold.call(void 0,
|
|
1082
|
+
client(req),
|
|
1083
|
+
(error) => _chunkBKBFSOGTcjs.asyncFail.call(void 0, error),
|
|
1084
|
+
(response) => {
|
|
1085
|
+
try {
|
|
1086
|
+
const parsed = JSON.parse(response.bodyText);
|
|
1087
|
+
const result = validator(parsed);
|
|
1088
|
+
if (result.success) {
|
|
1089
|
+
return _chunkBKBFSOGTcjs.asyncSucceed.call(void 0, result.data);
|
|
1090
|
+
}
|
|
1091
|
+
return _chunkBKBFSOGTcjs.asyncFail.call(void 0, {
|
|
1092
|
+
_tag: "ValidationError",
|
|
1093
|
+
message: result.error,
|
|
1094
|
+
body: response.bodyText
|
|
1095
|
+
});
|
|
1096
|
+
} catch (e) {
|
|
1097
|
+
return _chunkBKBFSOGTcjs.asyncFail.call(void 0, {
|
|
1098
|
+
_tag: "ValidationError",
|
|
1099
|
+
message: `JSON parse error: ${String(e)}`,
|
|
1100
|
+
body: response.bodyText
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
|
|
444
1112
|
|
|
445
1113
|
|
|
446
1114
|
|
|
@@ -450,4 +1118,4 @@ function httpClientStream(cfg = {}) {
|
|
|
450
1118
|
|
|
451
1119
|
|
|
452
1120
|
|
|
453
|
-
exports.decorate = decorate; exports.httpClient = httpClient; exports.httpClientStream = httpClientStream; exports.httpClientWithMeta = httpClientWithMeta; exports.makeHttp = makeHttp; exports.makeHttpStream = makeHttpStream; exports.normalizeHeadersInit = normalizeHeadersInit; exports.withMiddleware = withMiddleware; exports.withRetryStream = withRetryStream;
|
|
1121
|
+
exports.HttpConcurrencyPool = HttpConcurrencyPool; exports.decorate = decorate; exports.httpClient = httpClient; exports.httpClientStream = httpClientStream; exports.httpClientWithMeta = httpClientWithMeta; exports.makeHttp = makeHttp; exports.makeHttpStream = makeHttpStream; exports.normalizeHeadersInit = normalizeHeadersInit; exports.resolveHttpPoolKey = resolveHttpPoolKey; exports.validatedJson = validatedJson; exports.withCircuitBreaker = withCircuitBreaker; exports.withMiddleware = withMiddleware; exports.withRetryStream = withRetryStream; exports.withTracing = withTracing;
|