brass-runtime 1.16.0 → 1.16.1

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 (210) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +283 -18
  3. package/dist/agent/cli/main.cjs +38 -38
  4. package/dist/agent/cli/main.js +6 -6
  5. package/dist/agent/cli/main.mjs +6 -6
  6. package/dist/agent/index.cjs +7 -7
  7. package/dist/agent/index.d.ts +1 -1
  8. package/dist/agent/index.js +6 -6
  9. package/dist/agent/index.mjs +6 -6
  10. package/dist/chunk-2HQTDLHF.mjs +683 -0
  11. package/dist/chunk-36I3M4UC.mjs +370 -0
  12. package/dist/{chunk-QY5FKYEQ.js → chunk-3AYM6WPJ.js} +570 -51
  13. package/dist/chunk-3LOYJFRR.cjs +300 -0
  14. package/dist/chunk-3Y2RIUMM.js +300 -0
  15. package/dist/{chunk-N6VHMOWB.cjs → chunk-4ROBZFL6.cjs} +128 -128
  16. package/dist/{chunk-NC5SDRYE.js → chunk-52OB2ROS.js} +4 -4
  17. package/dist/{chunk-JX3LZQJH.cjs → chunk-52PPNNI4.cjs} +82 -20
  18. package/dist/{chunk-5YOQOXEQ.cjs → chunk-5EC274J5.cjs} +676 -293
  19. package/dist/chunk-5QC7LRZ3.js +229 -0
  20. package/dist/{chunk-7TL2LHQJ.js → chunk-5VRJNBLZ.mjs} +524 -141
  21. package/dist/chunk-62AZW6UT.cjs +313 -0
  22. package/dist/chunk-6IXXWIUM.js +683 -0
  23. package/dist/chunk-74ZTY6CP.js +2871 -0
  24. package/dist/chunk-76YMRMH2.cjs +777 -0
  25. package/dist/chunk-7CMJS3QE.mjs +2871 -0
  26. package/dist/{chunk-2WC63LJK.mjs → chunk-7JIJOVCT.js} +20 -10
  27. package/dist/{chunk-FM4W4QPL.js → chunk-A2OM6NEH.mjs} +5 -4
  28. package/dist/chunk-AGR5B2BC.cjs +683 -0
  29. package/dist/chunk-AVNQLJ5V.js +777 -0
  30. package/dist/chunk-B33ICAKP.js +313 -0
  31. package/dist/{chunk-J3H54ZRV.mjs → chunk-B5JD23U7.mjs} +1 -1
  32. package/dist/chunk-BABBZK4Y.js +2024 -0
  33. package/dist/{chunk-U5KWK3PX.mjs → chunk-C3MDXTRZ.js} +11 -0
  34. package/dist/{chunk-F5EUMJL7.mjs → chunk-CIZFIMK5.js} +55 -5
  35. package/dist/{chunk-SPUEME2B.cjs → chunk-CZIVE6NT.cjs} +12 -1
  36. package/dist/{chunk-TDVMADDN.js → chunk-DNFJLJMW.mjs} +11 -0
  37. package/dist/chunk-DNFO2EIZ.mjs +777 -0
  38. package/dist/{chunk-XDZOO4L5.js → chunk-EJ6BPYVR.mjs} +79 -17
  39. package/dist/{chunk-JNFRRJYH.cjs → chunk-ENKODRU3.cjs} +242 -192
  40. package/dist/chunk-EOC4UHBS.mjs +229 -0
  41. package/dist/{chunk-7LVI2GIN.js → chunk-FH2X7BVP.js} +507 -72
  42. package/dist/{chunk-OOGJ73B6.js → chunk-FHQGHPMO.mjs} +20 -10
  43. package/dist/{chunk-WQ5QNU5R.cjs → chunk-GLE2WY7Z.cjs} +652 -217
  44. package/dist/{chunk-G6IQOE4P.mjs → chunk-GYM3LLGS.mjs} +507 -72
  45. package/dist/chunk-HLWLMW2F.mjs +2024 -0
  46. package/dist/{chunk-TVN5I4U6.cjs → chunk-JF5WGYJJ.cjs} +25 -24
  47. package/dist/{chunk-CY33PGEX.mjs → chunk-KH4SYAOS.mjs} +570 -51
  48. package/dist/chunk-KN32XNTH.mjs +313 -0
  49. package/dist/chunk-KQLYONSE.cjs +2871 -0
  50. package/dist/{chunk-7HUOJA4W.cjs → chunk-KZJQ723N.cjs} +90 -80
  51. package/dist/{chunk-CCKHV5BT.mjs → chunk-L2SYFEBS.js} +5 -4
  52. package/dist/{chunk-IJT6RRQ5.cjs → chunk-L6VB5N7Q.cjs} +20 -9
  53. package/dist/{chunk-ZGLD4TVZ.mjs → chunk-MBEJI5HF.mjs} +4 -4
  54. package/dist/{chunk-PRWCB3QL.mjs → chunk-MIIYDLGM.js} +524 -141
  55. package/dist/{chunk-H55LI6WY.js → chunk-MOO4L7F4.mjs} +15 -4
  56. package/dist/{chunk-7XOPAB5Q.js → chunk-MT3OWDPC.mjs} +55 -5
  57. package/dist/chunk-MVGUEJ5Z.cjs +370 -0
  58. package/dist/chunk-PD4EJTQC.cjs +229 -0
  59. package/dist/chunk-PWC3RBQE.mjs +300 -0
  60. package/dist/{chunk-MWXMNYJS.cjs → chunk-Q2I37RP3.cjs} +643 -124
  61. package/dist/{chunk-VFIUZG7J.mjs → chunk-RKGKFN2A.js} +79 -17
  62. package/dist/{chunk-NYL4D7SK.cjs → chunk-SA6HUJVI.cjs} +5 -5
  63. package/dist/{chunk-K2T3DV26.mjs → chunk-TRM4JUZQ.js} +15 -4
  64. package/dist/chunk-UB4B6OFY.js +370 -0
  65. package/dist/{chunk-G3XGCZDQ.js → chunk-UCUBNWM2.js} +1 -1
  66. package/dist/chunk-VN44DYYT.cjs +2024 -0
  67. package/dist/{client-CtFmoDvM.d.ts → client-CZHU674n.d.ts} +211 -36
  68. package/dist/core/index.cjs +135 -9
  69. package/dist/core/index.d.ts +238 -33
  70. package/dist/core/index.js +155 -29
  71. package/dist/core/index.mjs +155 -29
  72. package/dist/{effect-CGNl5Rqp.d.ts → effect-DIUHZ9IN.d.ts} +89 -1
  73. package/dist/effectRunner-CFLC32IK.cjs +8 -0
  74. package/dist/{effectRunner-A4CHJXJI.js → effectRunner-L4S7IPT3.js} +2 -2
  75. package/dist/{effectRunner-OPUF6QRN.mjs → effectRunner-NNGG75QA.mjs} +2 -2
  76. package/dist/http/index.cjs +324 -2986
  77. package/dist/http/index.d.ts +54 -68
  78. package/dist/http/index.js +238 -2900
  79. package/dist/http/index.mjs +238 -2900
  80. package/dist/http/testing.cjs +14 -12
  81. package/dist/http/testing.d.ts +5 -4
  82. package/dist/http/testing.js +10 -8
  83. package/dist/http/testing.mjs +10 -8
  84. package/dist/index.cjs +423 -255
  85. package/dist/index.d.ts +87 -69
  86. package/dist/index.js +301 -133
  87. package/dist/index.mjs +301 -133
  88. package/dist/observability/index.cjs +16 -531
  89. package/dist/observability/index.d.ts +81 -8
  90. package/dist/observability/index.js +23 -538
  91. package/dist/observability/index.mjs +23 -538
  92. package/dist/perf/cli.cjs +401 -0
  93. package/dist/perf/cli.d.ts +1 -0
  94. package/dist/perf/cli.js +401 -0
  95. package/dist/perf/cli.mjs +401 -0
  96. package/dist/perf/index.cjs +141 -0
  97. package/dist/perf/index.d.ts +483 -0
  98. package/dist/perf/index.js +141 -0
  99. package/dist/perf/index.mjs +141 -0
  100. package/dist/schedule-CK3Ml_7p.d.ts +259 -0
  101. package/dist/schema/index.cjs +6 -2
  102. package/dist/schema/index.d.ts +3 -1
  103. package/dist/schema/index.js +5 -1
  104. package/dist/schema/index.mjs +5 -1
  105. package/dist/{server-C8hDXA74.d.ts → server-GJPg8ZSG.d.ts} +4 -3
  106. package/dist/{stream-dvSs0QS5.d.ts → stream-B4oK9JFP.d.ts} +1 -1
  107. package/dist/{tracer-B5tRH9H7.d.ts → tracer-Hwt1cl7h.d.ts} +13 -54
  108. package/dist/{tracing-Dt9S_6V8.d.ts → tracing-DqbTKGcf.d.ts} +1 -1
  109. package/docs/ARCHITECTURE.md +292 -0
  110. package/docs/README.md +63 -0
  111. package/docs/adr/0001-ai-context-pack.md +32 -0
  112. package/docs/agent-apply-mode.md +104 -0
  113. package/docs/agent-approvals.md +110 -0
  114. package/docs/agent-batch.md +185 -0
  115. package/docs/agent-boundaries.md +112 -0
  116. package/docs/agent-chat-sessions.md +160 -0
  117. package/docs/agent-ci.md +17 -0
  118. package/docs/agent-cli.md +405 -0
  119. package/docs/agent-config.md +480 -0
  120. package/docs/agent-context-discovery.md +159 -0
  121. package/docs/agent-copilot-like-dx.md +126 -0
  122. package/docs/agent-declarative-optimized-planning.md +138 -0
  123. package/docs/agent-dx.md +224 -0
  124. package/docs/agent-env-files.md +126 -0
  125. package/docs/agent-follow-up-context.md +43 -0
  126. package/docs/agent-global-usage.md +180 -0
  127. package/docs/agent-init.md +109 -0
  128. package/docs/agent-install-and-configure.md +516 -0
  129. package/docs/agent-language-workspace-ux.md +99 -0
  130. package/docs/agent-llm-adapters.md +123 -0
  131. package/docs/agent-local-install.md +190 -0
  132. package/docs/agent-local-tests.md +51 -0
  133. package/docs/agent-observability.md +155 -0
  134. package/docs/agent-patch-quality-loop.md +162 -0
  135. package/docs/agent-presets.md +22 -0
  136. package/docs/agent-project-commands.md +237 -0
  137. package/docs/agent-project-intelligence.md +156 -0
  138. package/docs/agent-redaction.md +18 -0
  139. package/docs/agent-release-readiness.md +76 -0
  140. package/docs/agent-rollback-safety.md +162 -0
  141. package/docs/agent-rollback.md +23 -0
  142. package/docs/agent-run-artifacts.md +16 -0
  143. package/docs/agent-vscode-auto-discovery.md +137 -0
  144. package/docs/agent-vscode-batch-runner.md +100 -0
  145. package/docs/agent-vscode-chat-layout.md +90 -0
  146. package/docs/agent-vscode-clean-install.md +147 -0
  147. package/docs/agent-vscode-code-actions.md +70 -0
  148. package/docs/agent-vscode-diff-preview.md +45 -0
  149. package/docs/agent-vscode-inline-assist.md +56 -0
  150. package/docs/agent-vscode-install.md +186 -0
  151. package/docs/agent-vscode-model-setup.md +97 -0
  152. package/docs/agent-vscode-patch-preview.md +92 -0
  153. package/docs/agent-vscode-problems.md +79 -0
  154. package/docs/agent-vscode-project-dashboard.md +106 -0
  155. package/docs/agent-vscode-run-history.md +92 -0
  156. package/docs/agent-vscode-ux.md +73 -0
  157. package/docs/ai/INVARIANTS.md +84 -0
  158. package/docs/ai/PROJECT_MAP.md +338 -0
  159. package/docs/ai/PUBLIC_API.md +336 -0
  160. package/docs/ai/VALIDATION_MATRIX.md +67 -0
  161. package/docs/api-polish.md +37 -0
  162. package/docs/cancellation.md +162 -0
  163. package/docs/coverage.md +46 -0
  164. package/docs/getting-started.md +159 -0
  165. package/docs/guides/README.md +40 -0
  166. package/docs/guides/circuit-breaker.md +89 -0
  167. package/docs/guides/error-handling.md +91 -0
  168. package/docs/guides/getting-started.md +107 -0
  169. package/docs/guides/layers.md +189 -0
  170. package/docs/guides/metrics.md +101 -0
  171. package/docs/guides/resource-management.md +141 -0
  172. package/docs/guides/retry.md +215 -0
  173. package/docs/guides/semaphore.md +66 -0
  174. package/docs/guides/streams.md +117 -0
  175. package/docs/guides/supervisors.md +98 -0
  176. package/docs/guides/testing.md +162 -0
  177. package/docs/guides/tracing.md +71 -0
  178. package/docs/http-recipes.md +399 -0
  179. package/docs/http.md +749 -0
  180. package/docs/modules.md +285 -0
  181. package/docs/observability-collector-smoke.md +31 -0
  182. package/docs/observability-framework-examples.md +98 -0
  183. package/docs/observability.md +542 -0
  184. package/docs/otel-collector-smoke.yaml +27 -0
  185. package/docs/performance-profiler.md +199 -0
  186. package/docs/production-readiness.md +73 -0
  187. package/docs/recipes/README.md +12 -0
  188. package/docs/recipes/http-server.md +45 -0
  189. package/docs/recipes/layers.md +44 -0
  190. package/docs/recipes/performance.md +47 -0
  191. package/docs/recipes/runtime.md +41 -0
  192. package/docs/recipes/testing.md +41 -0
  193. package/docs/release.md +53 -0
  194. package/docs/wasm-bounded-queues.md +44 -0
  195. package/docs/wasm-engine-observability-benchmarks.md +85 -0
  196. package/docs/wasm-fiber-engine.md +117 -0
  197. package/docs/wasm-scheduler-state-machine.md +122 -0
  198. package/docs/wasm-stream-chunks.md +54 -0
  199. package/package.json +22 -2
  200. package/dist/chunk-45F7OKGT.cjs +0 -104
  201. package/dist/chunk-7V4KY4RL.mjs +0 -104
  202. package/dist/chunk-DJQ7OMMB.cjs +0 -144
  203. package/dist/chunk-GOV47PPB.mjs +0 -552
  204. package/dist/chunk-JF4XXPZ5.cjs +0 -552
  205. package/dist/chunk-KCPT2D6G.js +0 -552
  206. package/dist/chunk-NOYZIMUJ.mjs +0 -144
  207. package/dist/chunk-PNVFW245.js +0 -144
  208. package/dist/chunk-ROJC3NBJ.js +0 -104
  209. package/dist/effectRunner-3ZHAD3LE.cjs +0 -8
  210. package/dist/schedule-Fque9Abz.d.ts +0 -70
@@ -1,24 +1,37 @@
1
+ import {
2
+ registerHttpEffect
3
+ } from "./chunk-MOO4L7F4.mjs";
1
4
  import {
2
5
  streamFromReadableStream
3
- } from "./chunk-OOGJ73B6.js";
6
+ } from "./chunk-FHQGHPMO.mjs";
4
7
  import {
8
+ makeScheduleDriver,
5
9
  sleep
6
- } from "./chunk-ROJC3NBJ.js";
10
+ } from "./chunk-2HQTDLHF.mjs";
11
+ import {
12
+ getHttpRequestPolicy,
13
+ isExternalAbortError,
14
+ isHttpError,
15
+ isRetryableHttpError,
16
+ toHttpError,
17
+ withHttpRequestPolicy
18
+ } from "./chunk-PWC3RBQE.mjs";
7
19
  import {
8
20
  fromPromiseAbortable,
9
21
  resolveWasmModule
10
- } from "./chunk-7LVI2GIN.js";
22
+ } from "./chunk-GYM3LLGS.mjs";
11
23
  import {
24
+ Cause,
12
25
  asyncEffect,
13
26
  asyncFail,
14
27
  asyncFlatMap,
15
28
  asyncFold,
16
29
  asyncSucceed
17
- } from "./chunk-PNVFW245.js";
30
+ } from "./chunk-36I3M4UC.mjs";
18
31
  import {
19
32
  Schema,
20
33
  parseConfig
21
- } from "./chunk-TDVMADDN.js";
34
+ } from "./chunk-DNFJLJMW.mjs";
22
35
 
23
36
  // src/http/retry/wasmRetryPlanner.ts
24
37
  var WasmRetryPlannerBridge = class {
@@ -65,7 +78,7 @@ function makeWasmRetryPlanner() {
65
78
  // src/http/retry/retry.ts
66
79
  var defaultRetryableMethods = ["GET", "HEAD", "OPTIONS"];
67
80
  var defaultRetryOnStatus = (s) => s === 408 || s === 429 || s === 500 || s === 502 || s === 503 || s === 504;
68
- var defaultRetryOnError = (e) => e._tag === "FetchError" || e._tag === "Timeout" || e._tag === "PoolTimeout";
81
+ var defaultRetryOnError = (e) => isRetryableHttpError(e, { retryOnStatus: defaultRetryOnStatus });
69
82
  var clamp = (n, min, max) => Math.max(min, Math.min(max, n));
70
83
  var backoffDelayMs = (attempt, base, cap) => {
71
84
  const b = Math.max(0, base);
@@ -92,7 +105,7 @@ var normalizeRetryBudget = (ms) => {
92
105
  return Math.max(0, Math.floor(ms));
93
106
  };
94
107
  var resolveEffectivePolicy = (req, basePolicy) => {
95
- const override = req.retry;
108
+ const override = getHttpRequestPolicy(req).retry;
96
109
  if (override === false) return null;
97
110
  if (override === void 0) return basePolicy;
98
111
  return {
@@ -118,17 +131,16 @@ var withRetry = (p) => (next) => {
118
131
  const retryEngine = resolveRetryEngine(p);
119
132
  const wasmPlanner = retryEngine === "wasm" ? makeWasmRetryPlanner() : void 0;
120
133
  const isMethodRetryable = (req) => retryOnMethods.includes(req.method);
121
- const nextDelay = (ep, epMaxElapsedMs, retryId, attempt, startedAt, retryable, scheduleState, input) => {
134
+ const nextDelay = (ep, epMaxElapsedMs, retryId, attempt, startedAt, retryable, scheduleDriver, input) => {
122
135
  if (!retryable) return void 0;
123
136
  const remainingBudget = epMaxElapsedMs === void 0 ? Number.POSITIVE_INFINITY : epMaxElapsedMs - (performance.now() - startedAt);
124
137
  if (remainingBudget <= 0) return void 0;
125
- if (ep.schedule) {
126
- const [decision, nextScheduleState] = ep.schedule.step(scheduleState, input);
138
+ if (scheduleDriver) {
139
+ const decision = scheduleDriver.next(input);
127
140
  if (!decision.continue) return void 0;
128
141
  const rawDelay2 = input.retryAfterMs === void 0 ? decision.delayMs : Math.min(input.retryAfterMs, ep.maxDelayMs);
129
142
  return {
130
- delayMs: Math.max(0, Math.min(rawDelay2, remainingBudget)),
131
- scheduleState: nextScheduleState
143
+ delayMs: Math.max(0, Math.min(rawDelay2, remainingBudget))
132
144
  };
133
145
  }
134
146
  if (wasmPlanner && retryId !== void 0) {
@@ -137,10 +149,10 @@ var withRetry = (p) => (next) => {
137
149
  retryable,
138
150
  retryAfterMs: input.retryAfterMs
139
151
  });
140
- return delay === void 0 ? void 0 : { delayMs: delay, scheduleState };
152
+ return delay === void 0 ? void 0 : { delayMs: delay };
141
153
  }
142
154
  const rawDelay = input.retryAfterMs === void 0 ? backoffDelayMs(attempt, ep.baseDelayMs, ep.maxDelayMs) : Math.min(input.retryAfterMs, ep.maxDelayMs);
143
- return { delayMs: Math.max(0, Math.min(rawDelay, remainingBudget)), scheduleState };
155
+ return { delayMs: Math.max(0, Math.min(rawDelay, remainingBudget)) };
144
156
  };
145
157
  const sleepWithCleanup = (ms, onCancel) => {
146
158
  return asyncEffect((_env, cb) => {
@@ -152,13 +164,9 @@ var withRetry = (p) => (next) => {
152
164
  };
153
165
  });
154
166
  };
155
- const loop = (req, attempt, startedAt, retryId, ep, epMaxElapsedMs, epRetryOnStatus, epRetryOnError, originalPriority, safeDrop, scheduleState) => {
167
+ const loop = (req, attempt, startedAt, retryId, ep, epMaxElapsedMs, epRetryOnStatus, epRetryOnError, originalPriority, safeDrop, scheduleDriver) => {
156
168
  if (!isMethodRetryable(req)) return next(req);
157
- const effectiveReq = attempt > 0 ? (() => {
158
- const boostedReq = { ...req };
159
- boostedReq.priority = Math.max(0, originalPriority - 1);
160
- return boostedReq;
161
- })() : req;
169
+ const effectiveReq = attempt > 0 ? withHttpRequestPolicy(req, { priority: Math.max(0, originalPriority - 1) }) : req;
162
170
  const remainingBudget = () => epMaxElapsedMs === void 0 ? Number.POSITIVE_INFINITY : epMaxElapsedMs - (performance.now() - startedAt);
163
171
  return asyncFold(
164
172
  next(effectiveReq),
@@ -168,7 +176,7 @@ var withRetry = (p) => (next) => {
168
176
  return asyncFail(e);
169
177
  }
170
178
  const retryable = attempt < ep.maxRetries && epRetryOnError(e) && remainingBudget() > 0;
171
- const retryDecision = nextDelay(ep, epMaxElapsedMs, retryId, attempt, startedAt, retryable, scheduleState, {
179
+ const retryDecision = nextDelay(ep, epMaxElapsedMs, retryId, attempt, startedAt, retryable, scheduleDriver, {
172
180
  attempt,
173
181
  elapsedMs: performance.now() - startedAt,
174
182
  request: req,
@@ -189,12 +197,12 @@ var withRetry = (p) => (next) => {
189
197
  timestamp: Date.now()
190
198
  });
191
199
  }
192
- return asyncFlatMap(sleepWithCleanup(retryDecision.delayMs, () => safeDrop(retryId)), () => loop(req, attempt + 1, startedAt, retryId, ep, epMaxElapsedMs, epRetryOnStatus, epRetryOnError, originalPriority, safeDrop, retryDecision.scheduleState));
200
+ return asyncFlatMap(sleepWithCleanup(retryDecision.delayMs, () => safeDrop(retryId)), () => loop(req, attempt + 1, startedAt, retryId, ep, epMaxElapsedMs, epRetryOnStatus, epRetryOnError, originalPriority, safeDrop, scheduleDriver));
193
201
  },
194
202
  (w) => {
195
203
  const retryable = attempt < ep.maxRetries && epRetryOnStatus(w.status) && remainingBudget() > 0;
196
204
  const ra = ep.respectRetryAfter === false ? void 0 : retryAfterMs(w.headers);
197
- const retryDecision = nextDelay(ep, epMaxElapsedMs, retryId, attempt, startedAt, retryable, scheduleState, {
205
+ const retryDecision = nextDelay(ep, epMaxElapsedMs, retryId, attempt, startedAt, retryable, scheduleDriver, {
198
206
  attempt,
199
207
  elapsedMs: performance.now() - startedAt,
200
208
  request: req,
@@ -216,7 +224,7 @@ var withRetry = (p) => (next) => {
216
224
  timestamp: Date.now()
217
225
  });
218
226
  }
219
- return asyncFlatMap(sleepWithCleanup(retryDecision.delayMs, () => safeDrop(retryId)), () => loop(req, attempt + 1, startedAt, retryId, ep, epMaxElapsedMs, epRetryOnStatus, epRetryOnError, originalPriority, safeDrop, retryDecision.scheduleState));
227
+ return asyncFlatMap(sleepWithCleanup(retryDecision.delayMs, () => safeDrop(retryId)), () => loop(req, attempt + 1, startedAt, retryId, ep, epMaxElapsedMs, epRetryOnStatus, epRetryOnError, originalPriority, safeDrop, scheduleDriver));
220
228
  }
221
229
  );
222
230
  };
@@ -227,7 +235,7 @@ var withRetry = (p) => (next) => {
227
235
  const epRetryOnStatus = effectivePolicy.retryOnStatus ?? defaultRetryOnStatus;
228
236
  const epRetryOnError = effectivePolicy.retryOnError ?? defaultRetryOnError;
229
237
  const epMaxElapsedMs = normalizeRetryBudget(effectivePolicy.maxElapsedMs);
230
- const originalPriority = req.priority ?? 5;
238
+ const originalPriority = getHttpRequestPolicy(req).priority ?? 5;
231
239
  const startedAt = performance.now();
232
240
  const retryId = wasmPlanner?.start({
233
241
  nowMs: startedAt,
@@ -243,8 +251,12 @@ var withRetry = (p) => (next) => {
243
251
  wasmPlanner?.drop(id);
244
252
  }
245
253
  };
246
- const scheduleState = effectivePolicy.schedule?.initial();
247
- return loop(req, 0, startedAt, retryId, effectivePolicy, epMaxElapsedMs, epRetryOnStatus, epRetryOnError, originalPriority, safeDrop, scheduleState);
254
+ const scheduleDriver = effectivePolicy.schedule ? makeScheduleDriver(effectivePolicy.schedule, {
255
+ name: effectivePolicy.schedule.name ?? "http.retry",
256
+ startedAtMs: startedAt,
257
+ onDecision: effectivePolicy.onScheduleDecision
258
+ }) : void 0;
259
+ return loop(req, 0, startedAt, retryId, effectivePolicy, epMaxElapsedMs, epRetryOnStatus, epRetryOnError, originalPriority, safeDrop, scheduleDriver);
248
260
  };
249
261
  };
250
262
 
@@ -257,23 +269,23 @@ var WasmHttpPermitPoolBridge = class {
257
269
  constructor(Ctor, options) {
258
270
  this.pool = new Ctor(options.concurrency, options.maxQueue, toU64(options.queueTimeoutMs));
259
271
  }
260
- acquire(key, subjectId, nowMs = Date.now()) {
272
+ acquire(key, subjectId, nowMs2 = Date.now()) {
261
273
  const keyId = this.internKey(key);
262
- const decision = this.pool.acquire(subjectId, keyId, toU64(nowMs));
274
+ const decision = this.pool.acquire(subjectId, keyId, toU64(nowMs2));
263
275
  const permitId = this.pool.last_permit_id();
264
276
  if (decision === DECISION_RUN_NOW) return { kind: "run", keyId, permitId };
265
277
  if (decision === DECISION_QUEUED) return { kind: "queued", keyId, permitId };
266
278
  return { kind: "rejected", keyId, permitId };
267
279
  }
268
- release(keyId, nowMs = Date.now()) {
269
- const ptr = this.pool.release(keyId, toU64(nowMs));
280
+ release(keyId, nowMs2 = Date.now()) {
281
+ const ptr = this.pool.release(keyId, toU64(nowMs2));
270
282
  return this.readEvents(ptr, this.pool.permit_events_len());
271
283
  }
272
284
  cancel(permitId) {
273
285
  this.pool.cancel(permitId);
274
286
  }
275
- advanceTime(nowMs = Date.now()) {
276
- const ptr = this.pool.advance_time(toU64(nowMs));
287
+ advanceTime(nowMs2 = Date.now()) {
288
+ const ptr = this.pool.advance_time(toU64(nowMs2));
277
289
  return this.readEvents(ptr, this.pool.permit_events_len());
278
290
  }
279
291
  nextDeadlineMs() {
@@ -356,7 +368,7 @@ function resolveHttpPoolEngine(config) {
356
368
  return "ts";
357
369
  }
358
370
  function resolveHttpPoolKey(resolver, req, url) {
359
- const custom = req.poolKey?.trim();
371
+ const custom = getHttpRequestPolicy(req).poolKey?.trim();
360
372
  if (custom) return custom.slice(0, 160);
361
373
  const r = resolver ?? "origin";
362
374
  if (typeof r === "function") return r(req, url).trim().slice(0, 160) || "global";
@@ -1904,6 +1916,351 @@ var mergeHeaders = (extra) => (req) => Lens.over(Request.headers, (h) => ({ ...h
1904
1916
  var mergeHeadersUnder = (under) => (req) => Lens.over(Request.headers, (h) => ({ ...under, ...h }))(req);
1905
1917
  var setHeaderIfMissing = (k, v) => (req) => Lens.over(Request.headers, (h) => h[k] ? h : { ...h, [k]: v })(req);
1906
1918
 
1919
+ // src/http/transport.ts
1920
+ var isTaggedHttpError = (error) => {
1921
+ return isHttpError(error);
1922
+ };
1923
+ var isAbortError = (error) => isExternalAbortError(error);
1924
+ var normalizeHttpError = (error, options) => toHttpError(error, options);
1925
+ var headersOf = (response) => {
1926
+ const headers = {};
1927
+ response.headers.forEach((value, key) => {
1928
+ headers[key] = value;
1929
+ });
1930
+ return headers;
1931
+ };
1932
+ function normalizeHttpHeaders(headers) {
1933
+ if (!headers) return {};
1934
+ if (typeof Headers !== "undefined" && headers instanceof Headers) {
1935
+ const out = {};
1936
+ headers.forEach((value, key) => {
1937
+ out[key] = value;
1938
+ });
1939
+ return out;
1940
+ }
1941
+ if (Array.isArray(headers)) {
1942
+ return headers.reduce((acc, item) => {
1943
+ if (!Array.isArray(item) || item.length < 2) return acc;
1944
+ const [key, value] = item;
1945
+ if (key !== void 0 && value !== void 0 && value !== null) {
1946
+ acc[String(key)] = Array.isArray(value) ? value.map(String).join(", ") : String(value);
1947
+ }
1948
+ return acc;
1949
+ }, {});
1950
+ }
1951
+ if (typeof headers.toJSON === "function") {
1952
+ return normalizeHttpHeaders(headers.toJSON());
1953
+ }
1954
+ if (typeof headers === "object") {
1955
+ return Object.entries(headers).reduce((acc, [key, value]) => {
1956
+ if (value !== void 0 && value !== null) {
1957
+ acc[key] = Array.isArray(value) ? value.map(String).join(", ") : String(value);
1958
+ }
1959
+ return acc;
1960
+ }, {});
1961
+ }
1962
+ return {};
1963
+ }
1964
+ var abortErrorForSignal = (signal) => {
1965
+ const reason = signal.reason;
1966
+ if (isTaggedHttpError(reason) && reason._tag === "Timeout") return reason;
1967
+ return { _tag: "Abort" };
1968
+ };
1969
+ var linkAbortSignals = (...signals) => {
1970
+ const activeSignals = signals.filter((signal) => signal !== void 0);
1971
+ if (activeSignals.length === 0) {
1972
+ const controller2 = new AbortController();
1973
+ return { signal: controller2.signal, cleanup: () => void 0 };
1974
+ }
1975
+ const controller = new AbortController();
1976
+ const listeners = [];
1977
+ const abortFrom = (signal) => {
1978
+ try {
1979
+ controller.abort(signal.reason);
1980
+ } catch {
1981
+ controller.abort();
1982
+ }
1983
+ };
1984
+ for (const signal of activeSignals) {
1985
+ if (signal.aborted) {
1986
+ abortFrom(signal);
1987
+ break;
1988
+ }
1989
+ const abort = () => abortFrom(signal);
1990
+ signal.addEventListener("abort", abort, { once: true });
1991
+ listeners.push({ signal, abort });
1992
+ }
1993
+ return {
1994
+ signal: controller.signal,
1995
+ cleanup: () => {
1996
+ for (const listener of listeners) {
1997
+ listener.signal.removeEventListener("abort", listener.abort);
1998
+ }
1999
+ listeners.length = 0;
2000
+ }
2001
+ };
2002
+ };
2003
+ var nowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
2004
+ var hasMethod = (value, name) => typeof value === "object" && value !== null && typeof value[name] === "function";
2005
+ var defaultPromiseBody = (response, mode) => {
2006
+ if (mode === "json" && hasMethod(response, "json")) return response.json();
2007
+ if (mode === "text" && hasMethod(response, "text")) return response.text();
2008
+ if (typeof response === "object" && response !== null) {
2009
+ const record = response;
2010
+ if ("data" in record) return record.data;
2011
+ if ("bodyText" in record) return record.bodyText;
2012
+ if ("body" in record) return record.body;
2013
+ }
2014
+ return response;
2015
+ };
2016
+ var encodePromiseBody = (body, mode) => {
2017
+ if (mode === "text") return body === void 0 || body === null ? "" : String(body);
2018
+ const encoded = JSON.stringify(body);
2019
+ return encoded === void 0 ? "" : encoded;
2020
+ };
2021
+ var finiteNumber = (value) => {
2022
+ const n = typeof value === "number" ? value : Number(value);
2023
+ return Number.isFinite(n) ? n : void 0;
2024
+ };
2025
+ var isPlainRecord = (value) => Object.prototype.toString.call(value) === "[object Object]";
2026
+ var injectSignal = (config, signal) => isPlainRecord(config) ? { ...config, signal } : config;
2027
+ var inferPromiseResponseInfo = (response) => {
2028
+ if (typeof response !== "object" || response === null) {
2029
+ return { status: 200, statusText: "", headers: {} };
2030
+ }
2031
+ const record = response;
2032
+ return {
2033
+ status: finiteNumber(record.status) ?? finiteNumber(record.statusCode) ?? 200,
2034
+ statusText: typeof record.statusText === "string" ? record.statusText : typeof record.statusMessage === "string" ? record.statusMessage : "",
2035
+ headers: record.headers,
2036
+ ms: finiteNumber(record.ms)
2037
+ };
2038
+ };
2039
+ var toPromiseTransportResponse = (bodyText, info) => ({
2040
+ status: info.status ?? 200,
2041
+ statusText: info.statusText ?? "",
2042
+ headers: normalizeHttpHeaders(info.headers),
2043
+ bodyText,
2044
+ ...info.ms !== void 0 ? { ms: info.ms } : {},
2045
+ ...info.transportMeta !== void 0 ? { transportMeta: info.transportMeta } : {}
2046
+ });
2047
+ function makePromiseHttpTransport(config) {
2048
+ return (context) => asyncEffect((_env, cb) => {
2049
+ let done = false;
2050
+ const finish = (exit) => {
2051
+ if (done) return;
2052
+ done = true;
2053
+ context.signal.removeEventListener("abort", abort);
2054
+ cb(exit);
2055
+ };
2056
+ const fail = (error) => finish({ _tag: "Failure", cause: Cause.fail(error) });
2057
+ const abort = () => fail(abortErrorForSignal(context.signal));
2058
+ if (context.signal.aborted) {
2059
+ abort();
2060
+ return;
2061
+ }
2062
+ context.signal.addEventListener("abort", abort, { once: true });
2063
+ const run = async () => {
2064
+ const startedAt = nowMs();
2065
+ try {
2066
+ const raw = await config.request(context);
2067
+ const durationMs = Math.round(nowMs() - startedAt);
2068
+ const mapped = await config.response(raw, context, { startedAt, durationMs });
2069
+ finish({
2070
+ _tag: "Success",
2071
+ value: {
2072
+ ...mapped,
2073
+ ms: mapped.ms ?? durationMs
2074
+ }
2075
+ });
2076
+ } catch (error) {
2077
+ if (context.signal.aborted) {
2078
+ fail(abortErrorForSignal(context.signal));
2079
+ return;
2080
+ }
2081
+ fail(config.error?.(error, context) ?? normalizeHttpError(error, { signal: context.signal }));
2082
+ }
2083
+ };
2084
+ void run();
2085
+ return () => {
2086
+ if (done) return;
2087
+ done = true;
2088
+ context.signal.removeEventListener("abort", abort);
2089
+ };
2090
+ });
2091
+ }
2092
+ function makePromiseHttpTransportBodyBuilder(config) {
2093
+ const makeBodyTransport = (mode, body, responseMapper, currentConfig = config) => makePromiseHttpTransport({
2094
+ ...currentConfig,
2095
+ response: async (raw, context, timing) => {
2096
+ const selected = await (body ?? ((value) => defaultPromiseBody(value, mode)))(raw, context, timing);
2097
+ const inferred = inferPromiseResponseInfo(raw);
2098
+ const mapped = responseMapper ? await responseMapper(raw, context, timing) : {};
2099
+ return toPromiseTransportResponse(encodePromiseBody(selected, mode), {
2100
+ ...inferred,
2101
+ ...mapped,
2102
+ headers: mapped.headers ?? inferred.headers
2103
+ });
2104
+ }
2105
+ });
2106
+ const makeFluentResponseBuilder = (mode, body, currentConfig = config) => {
2107
+ return {
2108
+ response: (responseMapper) => makeBodyTransport(mode, body, responseMapper, currentConfig),
2109
+ error: (error) => makeFluentResponseBuilder(mode, body, { ...currentConfig, error })
2110
+ };
2111
+ };
2112
+ return {
2113
+ response: (response) => makePromiseHttpTransport({ ...config, response }),
2114
+ json: (body, response) => makeBodyTransport("json", body, response),
2115
+ text: (body, response) => makeBodyTransport("text", body, response),
2116
+ fromJson: (body) => makeFluentResponseBuilder("json", body),
2117
+ fromText: (body) => makeFluentResponseBuilder("text", body),
2118
+ error: (error) => makePromiseHttpTransportBodyBuilder({ ...config, error })
2119
+ };
2120
+ }
2121
+ function makePromiseHttpTransportRequestConfigBuilder(requestConfig) {
2122
+ return {
2123
+ send: (send) => makePromiseHttpTransportBodyBuilder({
2124
+ request: async (context) => {
2125
+ const config = await requestConfig({
2126
+ request: context.request,
2127
+ url: context.url
2128
+ });
2129
+ return send(injectSignal(config, context.signal));
2130
+ }
2131
+ })
2132
+ };
2133
+ }
2134
+ function promiseHttpTransport() {
2135
+ return {
2136
+ request: (request) => makePromiseHttpTransportBodyBuilder({ request }),
2137
+ requestConfig: (requestConfig) => makePromiseHttpTransportRequestConfigBuilder(requestConfig)
2138
+ };
2139
+ }
2140
+ var fetchUnavailableError = () => ({
2141
+ _tag: "FetchError",
2142
+ message: "global `fetch` is not available; provide MakeHttpConfig.transport/streamTransport or run in an environment with fetch support."
2143
+ });
2144
+ function makeFetchTransport() {
2145
+ return ({ request, url, signal }) => asyncEffect((_env, cb) => {
2146
+ if (typeof fetch === "undefined") {
2147
+ cb({ _tag: "Failure", cause: Cause.fail(fetchUnavailableError()) });
2148
+ return;
2149
+ }
2150
+ const localController = new AbortController();
2151
+ const linkedSignal = linkAbortSignals(signal, request.init?.signal, localController.signal);
2152
+ let done = false;
2153
+ const finish = (exit) => {
2154
+ if (done) return;
2155
+ done = true;
2156
+ linkedSignal.cleanup();
2157
+ cb(exit);
2158
+ };
2159
+ const run = async () => {
2160
+ try {
2161
+ if (linkedSignal.signal.aborted) throw abortErrorForSignal(linkedSignal.signal);
2162
+ const started = nowMs();
2163
+ const response = await fetch(url, {
2164
+ ...request.init ?? {},
2165
+ method: request.method,
2166
+ headers: Request.headers.get(request),
2167
+ body: request.body,
2168
+ signal: linkedSignal.signal
2169
+ });
2170
+ const bodyText = await response.text();
2171
+ const latencyMs = Math.round(nowMs() - started);
2172
+ finish({
2173
+ _tag: "Success",
2174
+ value: {
2175
+ status: response.status,
2176
+ statusText: response.statusText,
2177
+ headers: headersOf(response),
2178
+ bodyText,
2179
+ ms: latencyMs
2180
+ }
2181
+ });
2182
+ } catch (error) {
2183
+ finish({
2184
+ _tag: "Failure",
2185
+ cause: Cause.fail(normalizeHttpError(error, { signal: linkedSignal.signal }))
2186
+ });
2187
+ }
2188
+ };
2189
+ void run();
2190
+ return () => {
2191
+ if (done) return;
2192
+ try {
2193
+ localController.abort();
2194
+ } catch {
2195
+ }
2196
+ };
2197
+ });
2198
+ }
2199
+ function makeFetchStreamTransport() {
2200
+ return ({ request, url, signal }) => asyncEffect((_env, cb) => {
2201
+ if (typeof fetch === "undefined") {
2202
+ cb({ _tag: "Failure", cause: Cause.fail(fetchUnavailableError()) });
2203
+ return;
2204
+ }
2205
+ const localController = new AbortController();
2206
+ const linkedSignal = linkAbortSignals(signal, request.init?.signal, localController.signal);
2207
+ let done = false;
2208
+ let cleanupTransferredToBody = false;
2209
+ const cleanup = () => {
2210
+ if (!cleanupTransferredToBody) linkedSignal.cleanup();
2211
+ };
2212
+ const finish = (exit) => {
2213
+ if (done) return;
2214
+ done = true;
2215
+ cleanup();
2216
+ cb(exit);
2217
+ };
2218
+ const run = async () => {
2219
+ try {
2220
+ if (linkedSignal.signal.aborted) throw abortErrorForSignal(linkedSignal.signal);
2221
+ const started = nowMs();
2222
+ const response = await fetch(url, {
2223
+ ...request.init ?? {},
2224
+ method: request.method,
2225
+ headers: Request.headers.get(request),
2226
+ body: request.body,
2227
+ signal: linkedSignal.signal
2228
+ });
2229
+ const headers = headersOf(response);
2230
+ const latencyMs = Math.round(nowMs() - started);
2231
+ const body = streamFromReadableStream(response.body, normalizeHttpError, {
2232
+ signal: linkedSignal.signal,
2233
+ onRelease: linkedSignal.cleanup
2234
+ });
2235
+ cleanupTransferredToBody = response.body !== null;
2236
+ finish({
2237
+ _tag: "Success",
2238
+ value: {
2239
+ status: response.status,
2240
+ statusText: response.statusText,
2241
+ headers,
2242
+ body,
2243
+ ms: latencyMs
2244
+ }
2245
+ });
2246
+ } catch (error) {
2247
+ finish({
2248
+ _tag: "Failure",
2249
+ cause: Cause.fail(normalizeHttpError(error, { signal: linkedSignal.signal }))
2250
+ });
2251
+ }
2252
+ };
2253
+ void run();
2254
+ return () => {
2255
+ if (done) return;
2256
+ try {
2257
+ localController.abort();
2258
+ } catch {
2259
+ }
2260
+ };
2261
+ });
2262
+ }
2263
+
1907
2264
  // src/http/configValidation.ts
1908
2265
  var fn = Schema.custom((value) => typeof value === "function", "function");
1909
2266
  var object = Schema.custom(
@@ -2015,6 +2372,31 @@ var retryConfig = Schema.union([
2015
2372
  onRetry: fn.optional()
2016
2373
  }, { unknownKeys: "passthrough" })
2017
2374
  ]);
2375
+ var requestRetryOverrideConfig = Schema.union([
2376
+ Schema.literal(false),
2377
+ Schema.object({
2378
+ maxRetries: Schema.number({ min: 0, int: true }).optional(),
2379
+ baseDelayMs: Schema.number({ min: 0, int: true }).optional(),
2380
+ maxDelayMs: Schema.number({ min: 0, int: true }).optional(),
2381
+ schedule: object.optional(),
2382
+ retryOnStatus: fn.optional()
2383
+ }, { unknownKeys: "passthrough" })
2384
+ ]);
2385
+ var requestPolicyConfig = Schema.object({
2386
+ preset: Schema.string({ minLength: 1 }).optional(),
2387
+ lane: Schema.string({ minLength: 1 }).optional(),
2388
+ dedupKey: Schema.string({ minLength: 1 }).optional(),
2389
+ priority: Schema.number({ min: 0, max: 9, int: true }).optional(),
2390
+ retry: requestRetryOverrideConfig.optional(),
2391
+ poolKey: Schema.string({ minLength: 1 }).optional()
2392
+ }, { unknownKeys: "passthrough" });
2393
+ var policyPresetsConfig = Schema.record(Schema.union([requestPolicyConfig, fn]));
2394
+ var compressionConfig = Schema.union([
2395
+ Schema.literal(false),
2396
+ Schema.object({
2397
+ encodings: Schema.array(Schema.enum(["gzip", "br", "deflate"])).optional()
2398
+ }, { unknownKeys: "passthrough" })
2399
+ ]);
2018
2400
  var prewarmConfig = Schema.union([
2019
2401
  Schema.literal(false),
2020
2402
  Schema.object({
@@ -2041,6 +2423,8 @@ var wireConfig = Schema.object({
2041
2423
  baseUrl: Schema.string().optional(),
2042
2424
  headers: Schema.record(Schema.string()).optional(),
2043
2425
  timeoutMs: Schema.number({ min: 1, int: true }).optional(),
2426
+ transport: fn.optional(),
2427
+ streamTransport: fn.optional(),
2044
2428
  pool: poolConfig.optional(),
2045
2429
  adaptiveLimiter: adaptiveLimiterConfig.optional()
2046
2430
  }, { unknownKeys: "passthrough" });
@@ -2048,6 +2432,8 @@ var lifecycleConfig = Schema.object({
2048
2432
  baseUrl: Schema.string().optional(),
2049
2433
  headers: Schema.record(Schema.string()).optional(),
2050
2434
  timeoutMs: Schema.number({ min: 1, int: true }).optional(),
2435
+ transport: fn.optional(),
2436
+ streamTransport: fn.optional(),
2051
2437
  pool: poolConfig.optional(),
2052
2438
  adaptiveLimiter: adaptiveLimiterConfig.optional(),
2053
2439
  dedup: Schema.union([Schema.literal(false), object]).optional(),
@@ -2056,12 +2442,15 @@ var lifecycleConfig = Schema.object({
2056
2442
  priority: priorityConfig.optional(),
2057
2443
  retry: retryConfig.optional(),
2058
2444
  prewarm: prewarmConfig.optional(),
2059
- onEvent: fn.optional()
2445
+ onEvent: fn.optional(),
2446
+ policyPresets: policyPresetsConfig.optional()
2060
2447
  }, { unknownKeys: "passthrough" });
2061
2448
  var defaultClientConfig = Schema.object({
2062
2449
  baseUrl: Schema.string().optional(),
2063
2450
  headers: Schema.record(Schema.string()).optional(),
2064
2451
  timeoutMs: Schema.number({ min: 1, int: true }).optional(),
2452
+ transport: fn.optional(),
2453
+ streamTransport: fn.optional(),
2065
2454
  pool: poolConfig.optional(),
2066
2455
  adaptiveLimiter: adaptiveLimiterConfig.optional(),
2067
2456
  dedup: Schema.union([Schema.literal(false), object]).optional(),
@@ -2071,9 +2460,10 @@ var defaultClientConfig = Schema.object({
2071
2460
  retry: retryConfig.optional(),
2072
2461
  prewarm: prewarmConfig.optional(),
2073
2462
  onEvent: fn.optional(),
2074
- preset: Schema.enum(["minimal", "balanced", "default"]).optional(),
2075
- compression: Schema.union([Schema.literal(false), object]).optional(),
2076
- middleware: Schema.array(fn).optional()
2463
+ preset: Schema.enum(["minimal", "balanced", "default", "production"]).optional(),
2464
+ compression: compressionConfig.optional(),
2465
+ middleware: Schema.array(fn).optional(),
2466
+ policyPresets: policyPresetsConfig.optional()
2077
2467
  }, { unknownKeys: "passthrough" });
2078
2468
  function validateMakeHttpConfig(config) {
2079
2469
  parseConfig("MakeHttpConfig", wireConfig, config);
@@ -2110,17 +2500,6 @@ var withMiddleware = (mw) => (c) => decorate(mw(c), c.stats, {
2110
2500
  shutdown: c.shutdown
2111
2501
  });
2112
2502
  var decorateStream = (run, stats = emptyStats) => Object.assign(((req) => run(req)), { stats });
2113
- var isTaggedHttpError = (e) => {
2114
- if (typeof e !== "object" || e === null || !("_tag" in e)) return false;
2115
- const tag = e._tag;
2116
- return tag === "Abort" || tag === "BadUrl" || tag === "FetchError" || tag === "Timeout" || tag === "PoolRejected" || tag === "PoolTimeout" || tag === "PoolClosed" || tag === "BatchSplitError";
2117
- };
2118
- var isAbortError = (e) => typeof e === "object" && e !== null && "name" in e && e.name === "AbortError";
2119
- var normalizeHttpError = (e) => {
2120
- if (isTaggedHttpError(e)) return e;
2121
- if (isAbortError(e)) return { _tag: "Abort" };
2122
- return { _tag: "FetchError", message: String(e) };
2123
- };
2124
2503
  var normalizeHeadersInit = (h) => {
2125
2504
  if (!h) return void 0;
2126
2505
  if (typeof Headers !== "undefined" && h instanceof Headers) {
@@ -2212,11 +2591,6 @@ var resolveRequestUrl = (req, baseUrl) => {
2212
2591
  return { _tag: "BadUrl", message: `URL inv\xE1lida: ${req.url}` };
2213
2592
  }
2214
2593
  };
2215
- var headersOf = (res) => {
2216
- const headers = {};
2217
- res.headers.forEach((v, k) => headers[k] = v);
2218
- return headers;
2219
- };
2220
2594
  var fetchLabel = (req, url) => `http:${req.method}:${url.origin}`;
2221
2595
  var timeoutReason = (req, url, timeoutMs) => ({
2222
2596
  _tag: "Timeout",
@@ -2225,33 +2599,73 @@ var timeoutReason = (req, url, timeoutMs) => ({
2225
2599
  message: `HTTP ${req.method} ${url.origin} timed out after ${timeoutMs}ms`
2226
2600
  });
2227
2601
  var requestPriority = (req) => {
2228
- const fromReq = req.priority;
2229
- if (fromReq !== void 0) return fromReq;
2602
+ const fromPolicy = getHttpRequestPolicy(req).priority;
2603
+ if (fromPolicy !== void 0) return fromPolicy;
2230
2604
  return req.init?.priority;
2231
2605
  };
2232
- var linkAbortSignals = (runtimeSignal, requestSignal) => {
2233
- if (!requestSignal) return { signal: runtimeSignal, cleanup: () => void 0 };
2234
- const controller = new AbortController();
2235
- const abort = (source) => {
2236
- try {
2237
- controller.abort(source.reason);
2238
- } catch {
2239
- controller.abort();
2606
+ var exitError = (exit) => {
2607
+ if (exit._tag === "Success") return void 0;
2608
+ const failure = Cause.firstFailure(exit.cause);
2609
+ if (failure._tag === "Some") return failure.value;
2610
+ const defect = Cause.firstDefect(exit.cause);
2611
+ if (defect._tag === "Some") return defect.value;
2612
+ if (Cause.containsInterrupt(exit.cause)) return { _tag: "Abort" };
2613
+ return Cause.toError(exit.cause);
2614
+ };
2615
+ var runTransportEffect = (effect, env, signal) => new Promise((resolve, reject) => {
2616
+ let done = false;
2617
+ let cancel;
2618
+ const finish = (exit) => {
2619
+ if (done) return;
2620
+ done = true;
2621
+ signal.removeEventListener("abort", abort);
2622
+ cancel = void 0;
2623
+ if (exit._tag === "Success") {
2624
+ resolve(exit.value);
2625
+ return;
2240
2626
  }
2627
+ reject(exitError(exit));
2241
2628
  };
2242
- const abortFromRuntime = () => abort(runtimeSignal);
2243
- const abortFromRequest = () => abort(requestSignal);
2244
- if (runtimeSignal.aborted) abortFromRuntime();
2245
- else runtimeSignal.addEventListener("abort", abortFromRuntime, { once: true });
2246
- if (requestSignal.aborted) abortFromRequest();
2247
- else requestSignal.addEventListener("abort", abortFromRequest, { once: true });
2248
- return {
2249
- signal: controller.signal,
2250
- cleanup: () => {
2251
- runtimeSignal.removeEventListener("abort", abortFromRuntime);
2252
- requestSignal.removeEventListener("abort", abortFromRequest);
2253
- }
2629
+ const abort = () => {
2630
+ if (done) return;
2631
+ done = true;
2632
+ signal.removeEventListener("abort", abort);
2633
+ const currentCancel = cancel;
2634
+ cancel = void 0;
2635
+ currentCancel?.();
2636
+ reject(abortErrorForSignal(signal));
2254
2637
  };
2638
+ if (signal.aborted) {
2639
+ abort();
2640
+ return;
2641
+ }
2642
+ signal.addEventListener("abort", abort, { once: true });
2643
+ try {
2644
+ cancel = registerHttpEffect(effect, env, finish);
2645
+ } catch (error) {
2646
+ if (done) return;
2647
+ done = true;
2648
+ signal.removeEventListener("abort", abort);
2649
+ reject(error);
2650
+ }
2651
+ });
2652
+ var releaseSuccess = (lease, adaptiveLimiter, response) => {
2653
+ if (!lease) return void 0;
2654
+ if (adaptiveLimiter) {
2655
+ lease.release(response.ms, { status: response.status });
2656
+ } else {
2657
+ lease.release();
2658
+ }
2659
+ return void 0;
2660
+ };
2661
+ var releaseFailure = (lease, adaptiveLimiter) => {
2662
+ if (!lease) return void 0;
2663
+ if (adaptiveLimiter) {
2664
+ lease.release(0, { error: true });
2665
+ } else {
2666
+ lease.release();
2667
+ }
2668
+ return void 0;
2255
2669
  };
2256
2670
  function makeHttpStream(cfg = {}) {
2257
2671
  validateMakeHttpConfig(cfg);
@@ -2261,17 +2675,18 @@ function makeHttpStream(cfg = {}) {
2261
2675
  const adaptiveLimiter = makeAdaptiveLimiter(cfg);
2262
2676
  const pool = adaptiveLimiter ? void 0 : makePool(cfg);
2263
2677
  const metrics = makeHttpStats(pool, adaptiveLimiter);
2678
+ const transport = cfg.streamTransport ?? makeFetchStreamTransport();
2264
2679
  const run = (req0) => {
2265
2680
  const req = normalize(req0);
2266
2681
  const url = resolveRequestUrl(req, baseUrl);
2267
2682
  if (!(url instanceof URL)) return asyncFail(url);
2268
2683
  const timeoutMs = resolvePositiveTimeout(req.timeoutMs ?? cfg.timeoutMs);
2269
2684
  return fromPromiseAbortable(
2270
- async (signal) => {
2685
+ async (signal, env) => {
2271
2686
  let lease;
2272
2687
  const linkedSignal = linkAbortSignals(signal, req.init?.signal);
2273
- let cleanupTransferredToBody = false;
2274
2688
  try {
2689
+ if (linkedSignal.signal.aborted) throw abortErrorForSignal(linkedSignal.signal);
2275
2690
  if (adaptiveLimiter) {
2276
2691
  const key = resolveHttpPoolKey(adaptiveLimiter.keyResolver, req, url);
2277
2692
  lease = await adaptiveLimiter.acquire(key, linkedSignal.signal, { priority: requestPriority(req) });
@@ -2279,44 +2694,18 @@ function makeHttpStream(cfg = {}) {
2279
2694
  const key = resolveHttpPoolKey(pool.keyResolver, req, url);
2280
2695
  lease = await pool.acquire(key, linkedSignal.signal);
2281
2696
  }
2282
- const started = performance.now();
2283
- const res = await fetch(url, {
2284
- ...req.init ?? {},
2285
- method: req.method,
2286
- headers: Request.headers.get(req),
2287
- body: req.body,
2288
- signal: linkedSignal.signal
2289
- });
2290
- const headers = headersOf(res);
2291
- const latencyMs = Math.round(performance.now() - started);
2292
- const body = streamFromReadableStream(res.body, normalizeHttpError, {
2293
- signal: linkedSignal.signal,
2294
- onRelease: linkedSignal.cleanup
2295
- });
2296
- cleanupTransferredToBody = res.body !== null;
2297
- if (adaptiveLimiter && lease) {
2298
- lease.release(latencyMs, { status: res.status });
2299
- } else {
2300
- lease?.release();
2301
- }
2697
+ const response = await runTransportEffect(
2698
+ transport({ request: req, url, signal: linkedSignal.signal }),
2699
+ env,
2700
+ linkedSignal.signal
2701
+ );
2702
+ lease = releaseSuccess(lease, adaptiveLimiter, response);
2302
2703
  lease = void 0;
2303
- return {
2304
- status: res.status,
2305
- statusText: res.statusText,
2306
- headers,
2307
- body,
2308
- ms: latencyMs
2309
- };
2704
+ return response;
2310
2705
  } finally {
2311
- if (!cleanupTransferredToBody) {
2312
- linkedSignal.cleanup();
2313
- }
2706
+ linkedSignal.cleanup();
2314
2707
  if (lease) {
2315
- if (adaptiveLimiter) {
2316
- lease.release(0, { error: true });
2317
- } else {
2318
- lease.release();
2319
- }
2708
+ releaseFailure(lease, adaptiveLimiter);
2320
2709
  }
2321
2710
  }
2322
2711
  },
@@ -2340,16 +2729,18 @@ function makeHttp(cfg = {}) {
2340
2729
  const adaptiveLimiter = makeAdaptiveLimiter(cfg);
2341
2730
  const pool = adaptiveLimiter ? void 0 : makePool(cfg);
2342
2731
  const metrics = makeHttpStats(pool, adaptiveLimiter);
2732
+ const transport = cfg.transport ?? makeFetchTransport();
2343
2733
  const run = (req0) => {
2344
2734
  const req = normalize(req0);
2345
2735
  const url = resolveRequestUrl(req, baseUrl);
2346
2736
  if (!(url instanceof URL)) return asyncFail(url);
2347
2737
  const timeoutMs = resolvePositiveTimeout(req.timeoutMs ?? cfg.timeoutMs);
2348
2738
  return fromPromiseAbortable(
2349
- async (signal) => {
2739
+ async (signal, env) => {
2350
2740
  let lease;
2351
2741
  const linkedSignal = linkAbortSignals(signal, req.init?.signal);
2352
2742
  try {
2743
+ if (linkedSignal.signal.aborted) throw abortErrorForSignal(linkedSignal.signal);
2353
2744
  if (adaptiveLimiter) {
2354
2745
  const key = resolveHttpPoolKey(adaptiveLimiter.keyResolver, req, url);
2355
2746
  lease = await adaptiveLimiter.acquire(key, linkedSignal.signal, { priority: requestPriority(req) });
@@ -2357,36 +2748,17 @@ function makeHttp(cfg = {}) {
2357
2748
  const key = resolveHttpPoolKey(pool.keyResolver, req, url);
2358
2749
  lease = await pool.acquire(key, linkedSignal.signal);
2359
2750
  }
2360
- const started = performance.now();
2361
- const res = await fetch(url, {
2362
- ...req.init ?? {},
2363
- method: req.method,
2364
- headers: Request.headers.get(req),
2365
- body: req.body,
2366
- signal: linkedSignal.signal
2367
- });
2368
- const bodyText = await res.text();
2369
- const headers = headersOf(res);
2370
- const latencyMs = Math.round(performance.now() - started);
2371
- if (adaptiveLimiter && lease) {
2372
- lease.release(latencyMs, { status: res.status });
2373
- lease = void 0;
2374
- }
2375
- return {
2376
- status: res.status,
2377
- statusText: res.statusText,
2378
- headers,
2379
- bodyText,
2380
- ms: latencyMs
2381
- };
2751
+ const response = await runTransportEffect(
2752
+ transport({ request: req, url, signal: linkedSignal.signal }),
2753
+ env,
2754
+ linkedSignal.signal
2755
+ );
2756
+ lease = releaseSuccess(lease, adaptiveLimiter, response);
2757
+ return response;
2382
2758
  } finally {
2383
2759
  linkedSignal.cleanup();
2384
2760
  if (lease) {
2385
- if (adaptiveLimiter) {
2386
- lease.release(0, { error: true });
2387
- } else {
2388
- lease.release();
2389
- }
2761
+ releaseFailure(lease, adaptiveLimiter);
2390
2762
  }
2391
2763
  }
2392
2764
  },
@@ -2482,6 +2854,17 @@ export {
2482
2854
  AdaptiveLimiter,
2483
2855
  validateLifecycleClientConfig,
2484
2856
  validateDefaultHttpClientConfig,
2857
+ isTaggedHttpError,
2858
+ isAbortError,
2859
+ normalizeHttpError,
2860
+ headersOf,
2861
+ normalizeHttpHeaders,
2862
+ abortErrorForSignal,
2863
+ linkAbortSignals,
2864
+ makePromiseHttpTransport,
2865
+ promiseHttpTransport,
2866
+ makeFetchTransport,
2867
+ makeFetchStreamTransport,
2485
2868
  decorate,
2486
2869
  withMiddleware,
2487
2870
  normalizeHeadersInit,