brass-runtime 1.16.0 → 1.17.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.
Files changed (219) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +287 -23
  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-7XOPAB5Q.js → chunk-4P2HHGAX.mjs} +83 -5
  16. package/dist/{chunk-N6VHMOWB.cjs → chunk-4ROBZFL6.cjs} +128 -128
  17. package/dist/{chunk-NC5SDRYE.js → chunk-52OB2ROS.js} +4 -4
  18. package/dist/{chunk-JX3LZQJH.cjs → chunk-52PPNNI4.cjs} +82 -20
  19. package/dist/{chunk-5YOQOXEQ.cjs → chunk-5EC274J5.cjs} +676 -293
  20. package/dist/chunk-5QC7LRZ3.js +229 -0
  21. package/dist/{chunk-7TL2LHQJ.js → chunk-5VRJNBLZ.mjs} +524 -141
  22. package/dist/chunk-62AZW6UT.cjs +313 -0
  23. package/dist/chunk-6IXXWIUM.js +683 -0
  24. package/dist/chunk-6RY2FFN4.mjs +2024 -0
  25. package/dist/chunk-74ZTY6CP.js +2871 -0
  26. package/dist/chunk-7CMJS3QE.mjs +2871 -0
  27. package/dist/{chunk-2WC63LJK.mjs → chunk-7JIJOVCT.js} +20 -10
  28. package/dist/chunk-7X3K5RMS.js +2024 -0
  29. package/dist/chunk-7ZPEZ57L.cjs +2024 -0
  30. package/dist/{chunk-FM4W4QPL.js → chunk-A2OM6NEH.mjs} +5 -4
  31. package/dist/chunk-AGR5B2BC.cjs +683 -0
  32. package/dist/chunk-B33ICAKP.js +313 -0
  33. package/dist/{chunk-J3H54ZRV.mjs → chunk-B5JD23U7.mjs} +1 -1
  34. package/dist/{chunk-F5EUMJL7.mjs → chunk-BKK77SBA.js} +83 -5
  35. package/dist/{chunk-U5KWK3PX.mjs → chunk-C3MDXTRZ.js} +11 -0
  36. package/dist/{chunk-SPUEME2B.cjs → chunk-CZIVE6NT.cjs} +12 -1
  37. package/dist/{chunk-TDVMADDN.js → chunk-DNFJLJMW.mjs} +11 -0
  38. package/dist/{chunk-XDZOO4L5.js → chunk-EJ6BPYVR.mjs} +79 -17
  39. package/dist/chunk-EOC4UHBS.mjs +229 -0
  40. package/dist/chunk-F6XWZQY4.cjs +777 -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-TVN5I4U6.cjs → chunk-JF5WGYJJ.cjs} +25 -24
  46. package/dist/{chunk-CY33PGEX.mjs → chunk-KH4SYAOS.mjs} +570 -51
  47. package/dist/chunk-KN32XNTH.mjs +313 -0
  48. package/dist/chunk-KQLYONSE.cjs +2871 -0
  49. package/dist/{chunk-7HUOJA4W.cjs → chunk-KZJQ723N.cjs} +90 -80
  50. package/dist/{chunk-CCKHV5BT.mjs → chunk-L2SYFEBS.js} +5 -4
  51. package/dist/{chunk-IJT6RRQ5.cjs → chunk-L6VB5N7Q.cjs} +20 -9
  52. package/dist/{chunk-ZGLD4TVZ.mjs → chunk-MBEJI5HF.mjs} +4 -4
  53. package/dist/{chunk-PRWCB3QL.mjs → chunk-MIIYDLGM.js} +524 -141
  54. package/dist/{chunk-H55LI6WY.js → chunk-MOO4L7F4.mjs} +15 -4
  55. package/dist/chunk-MVGUEJ5Z.cjs +370 -0
  56. package/dist/chunk-PD4EJTQC.cjs +229 -0
  57. package/dist/chunk-PWC3RBQE.mjs +300 -0
  58. package/dist/{chunk-MWXMNYJS.cjs → chunk-Q2I37RP3.cjs} +643 -124
  59. package/dist/{chunk-VFIUZG7J.mjs → chunk-RKGKFN2A.js} +79 -17
  60. package/dist/{chunk-NYL4D7SK.cjs → chunk-SA6HUJVI.cjs} +5 -5
  61. package/dist/chunk-SK7UZRNI.mjs +777 -0
  62. package/dist/{chunk-K2T3DV26.mjs → chunk-TRM4JUZQ.js} +15 -4
  63. package/dist/chunk-UB4B6OFY.js +370 -0
  64. package/dist/{chunk-G3XGCZDQ.js → chunk-UCUBNWM2.js} +1 -1
  65. package/dist/chunk-VWIPB6I5.js +777 -0
  66. package/dist/{chunk-JNFRRJYH.cjs → chunk-WBGRHGBP.cjs} +270 -192
  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 +18 -531
  89. package/dist/observability/index.d.ts +81 -8
  90. package/dist/observability/index.js +25 -538
  91. package/dist/observability/index.mjs +25 -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-D6JZ15_e.d.ts} +16 -4
  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 +65 -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 +339 -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/framework-integrations.md +38 -0
  165. package/docs/frameworks/angular.md +153 -0
  166. package/docs/frameworks/express.md +125 -0
  167. package/docs/frameworks/fastify.md +124 -0
  168. package/docs/frameworks/nestjs.md +282 -0
  169. package/docs/frameworks/nextjs.md +147 -0
  170. package/docs/frameworks/react.md +139 -0
  171. package/docs/frameworks/vanilla.md +224 -0
  172. package/docs/getting-started.md +159 -0
  173. package/docs/guides/README.md +40 -0
  174. package/docs/guides/circuit-breaker.md +89 -0
  175. package/docs/guides/error-handling.md +91 -0
  176. package/docs/guides/getting-started.md +107 -0
  177. package/docs/guides/layers.md +189 -0
  178. package/docs/guides/metrics.md +101 -0
  179. package/docs/guides/resource-management.md +141 -0
  180. package/docs/guides/retry.md +215 -0
  181. package/docs/guides/semaphore.md +66 -0
  182. package/docs/guides/streams.md +117 -0
  183. package/docs/guides/supervisors.md +98 -0
  184. package/docs/guides/testing.md +162 -0
  185. package/docs/guides/tracing.md +71 -0
  186. package/docs/http-recipes.md +399 -0
  187. package/docs/http.md +749 -0
  188. package/docs/modules.md +285 -0
  189. package/docs/nestjs.md +6 -0
  190. package/docs/observability-collector-smoke.md +31 -0
  191. package/docs/observability-framework-examples.md +110 -0
  192. package/docs/observability.md +649 -0
  193. package/docs/otel-collector-smoke.yaml +27 -0
  194. package/docs/performance-profiler.md +199 -0
  195. package/docs/production-readiness.md +73 -0
  196. package/docs/recipes/README.md +12 -0
  197. package/docs/recipes/http-server.md +45 -0
  198. package/docs/recipes/layers.md +44 -0
  199. package/docs/recipes/performance.md +47 -0
  200. package/docs/recipes/runtime.md +41 -0
  201. package/docs/recipes/testing.md +41 -0
  202. package/docs/release.md +53 -0
  203. package/docs/wasm-bounded-queues.md +44 -0
  204. package/docs/wasm-engine-observability-benchmarks.md +85 -0
  205. package/docs/wasm-fiber-engine.md +117 -0
  206. package/docs/wasm-scheduler-state-machine.md +122 -0
  207. package/docs/wasm-stream-chunks.md +54 -0
  208. package/package.json +22 -2
  209. package/dist/chunk-45F7OKGT.cjs +0 -104
  210. package/dist/chunk-7V4KY4RL.mjs +0 -104
  211. package/dist/chunk-DJQ7OMMB.cjs +0 -144
  212. package/dist/chunk-GOV47PPB.mjs +0 -552
  213. package/dist/chunk-JF4XXPZ5.cjs +0 -552
  214. package/dist/chunk-KCPT2D6G.js +0 -552
  215. package/dist/chunk-NOYZIMUJ.mjs +0 -144
  216. package/dist/chunk-PNVFW245.js +0 -144
  217. package/dist/chunk-ROJC3NBJ.js +0 -104
  218. package/dist/effectRunner-3ZHAD3LE.cjs +0 -8
  219. package/dist/schedule-Fque9Abz.d.ts +0 -70
@@ -1,3 +1,19 @@
1
+ import {
2
+ HTTP_OBSERVABILITY_CONTRACT,
3
+ makeExpressRequestObservabilityContext,
4
+ makeFastifyRequestObservabilityContext,
5
+ makeFetchRequestObservabilityContext,
6
+ makeHttpObservabilityMiddleware,
7
+ makeNodeRequestObservabilityContext,
8
+ makeNoopObservability,
9
+ makeObservabilityFromEnv,
10
+ makeObservabilityPreset,
11
+ observabilityOptionsForPreset,
12
+ observabilityOptionsFromEnv,
13
+ withFetchRequestObservability,
14
+ withHttpObservability,
15
+ withNodeRequestObservability
16
+ } from "../chunk-VWIPB6I5.js";
1
17
  import {
2
18
  OTLP_JSON_CONTENT_TYPE,
3
19
  PROMETHEUS_CONTENT_TYPE,
@@ -25,6 +41,7 @@ import {
25
41
  makeOtlpHttpLogExporter,
26
42
  makeOtlpHttpMetricsExporter,
27
43
  makeOtlpHttpSpanExporter,
44
+ makeOtlpOptions,
28
45
  makePrometheusMetricsExporter,
29
46
  makeRequestObservabilityContext,
30
47
  makeRuntimeHealth,
@@ -60,546 +77,15 @@ import {
60
77
  withLogContext,
61
78
  withSpan,
62
79
  withTimeout
63
- } from "../chunk-7XOPAB5Q.js";
64
- import "../chunk-XDZOO4L5.js";
65
- import {
66
- getCurrentFiber
67
- } from "../chunk-7LVI2GIN.js";
68
- import {
69
- asyncFail,
70
- asyncFlatMap,
71
- asyncFold,
72
- asyncSucceed,
73
- asyncSync
74
- } from "../chunk-PNVFW245.js";
75
- import "../chunk-TDVMADDN.js";
80
+ } from "../chunk-BKK77SBA.js";
81
+ import "../chunk-RKGKFN2A.js";
82
+ import "../chunk-3Y2RIUMM.js";
83
+ import "../chunk-FH2X7BVP.js";
84
+ import "../chunk-UB4B6OFY.js";
85
+ import "../chunk-C3MDXTRZ.js";
76
86
  import "../chunk-3RG5ZIWI.js";
77
-
78
- // src/observability/http.ts
79
- var DEFAULT_DURATION_BUCKETS = [1, 5, 10, 25, 50, 100, 250, 500, 1e3, 2500, 5e3, 1e4];
80
- function withHttpObservability(options) {
81
- const resolved = resolveHttpObservabilityOptions(options);
82
- return (next) => {
83
- return (req) => {
84
- const run = asyncFlatMap(
85
- beginHttpObservation(req, resolved),
86
- (state) => asyncFlatMap(
87
- logHttpRequest(req, resolved),
88
- () => asyncFlatMap(
89
- prepareHttpRequest(req, resolved),
90
- (wireReq) => asyncFold(
91
- next(wireReq),
92
- (error) => {
93
- const finish = state.finishWithError(error);
94
- const adaptiveLimiterAttrs = observeAdaptiveLimiter(wireReq, next, resolved);
95
- return asyncFlatMap(
96
- spanEvent("http.client.error", {
97
- ...finish.spanAttributes,
98
- ...adaptiveLimiterAttrs,
99
- "error.type": error._tag
100
- }),
101
- () => asyncFlatMap(logHttpError(req, error, finish, resolved), () => asyncFail(error))
102
- );
103
- },
104
- (res) => {
105
- const finish = state.finishWithResponse(res);
106
- const adaptiveLimiterAttrs = observeAdaptiveLimiter(wireReq, next, resolved);
107
- return asyncFlatMap(
108
- spanEvent("http.client.response", {
109
- ...finish.spanAttributes,
110
- ...adaptiveLimiterAttrs
111
- }),
112
- () => asyncFlatMap(logHttpResponse(req, res, finish, resolved), () => asyncSucceed(res))
113
- );
114
- }
115
- )
116
- )
117
- )
118
- );
119
- if (resolved.spans === false) return run;
120
- return withSpan(spanName(req, resolved.spans), run, spanStartAttributes(req, resolved));
121
- };
122
- };
123
- }
124
- var makeHttpObservabilityMiddleware = withHttpObservability;
125
- function beginHttpObservation(req, options) {
126
- return asyncSync(() => {
127
- const startedAt = options.clock();
128
- let finished = false;
129
- const inFlight = options.metrics?.gauge("brass_http_client_in_flight", requestBaseLabels(req, options));
130
- inFlight?.increment();
131
- const finish = (outcome, status, extra = {}) => {
132
- const durationMs = Math.max(0, options.clock() - startedAt);
133
- const labels = requestFinishLabels(req, outcome, status, options);
134
- if (!finished) {
135
- finished = true;
136
- if (inFlight && inFlight.value() > 0) inFlight.decrement();
137
- options.metrics?.counter("brass_http_client_requests_total", labels).increment();
138
- options.metrics?.histogram("brass_http_client_duration_ms", [...options.durationBuckets ?? DEFAULT_DURATION_BUCKETS], labels).observe(durationMs, currentTraceExemplar(durationMs, startedAt + durationMs));
139
- }
140
- return {
141
- durationMs,
142
- outcome,
143
- labels,
144
- spanAttributes: {
145
- "http.duration_ms": durationMs,
146
- "http.outcome": outcome,
147
- ...status ? { "http.status_code": Number(status) } : {},
148
- ...status ? { "http.response.status_code": Number(status) } : {},
149
- ...extra
150
- }
151
- };
152
- };
153
- const fiber = getCurrentFiber();
154
- fiber?.addFinalizer?.(() => {
155
- finish("abort", void 0, { "http.cancelled": true });
156
- });
157
- return {
158
- finishWithResponse: (res) => finish(httpStatusOutcome(res.status), String(res.status)),
159
- finishWithError: (error) => finish(httpErrorOutcome(error), void 0, { "error.type": error._tag })
160
- };
161
- });
162
- }
163
- function prepareHttpRequest(req, options) {
164
- if (!options.injectTraceHeaders) return asyncSucceed(req);
165
- return asyncSync(() => injectCurrentTraceContext(req));
166
- }
167
- function logHttpRequest(req, options) {
168
- const level = options.logs === false ? false : options.logs.requestLevel ?? false;
169
- if (!level) return asyncSucceed(void 0);
170
- return logEffect(level, "http.client.request", requestLogFields(req, options));
171
- }
172
- function logHttpResponse(req, res, finish, options) {
173
- const configured = options.logs === false ? false : options.logs.responseLevel ?? false;
174
- const level = configured || (finish.outcome === "error" ? options.logs !== false ? options.logs.errorLevel ?? "warn" : false : false);
175
- if (!level) return asyncSucceed(void 0);
176
- return logEffect(level, "http.client.response", {
177
- ...requestLogFields(req, options),
178
- status: res.status,
179
- statusText: res.statusText,
180
- outcome: finish.outcome,
181
- durationMs: finish.durationMs
182
- });
183
- }
184
- function logHttpError(req, error, finish, options) {
185
- const level = options.logs === false ? false : options.logs.errorLevel ?? "error";
186
- if (!level) return asyncSucceed(void 0);
187
- return logEffect(level, "http.client.error", {
188
- ...requestLogFields(req, options),
189
- outcome: finish.outcome,
190
- durationMs: finish.durationMs,
191
- errorTag: error._tag,
192
- message: httpErrorMessage(error)
193
- });
194
- }
195
- function resolveHttpObservabilityOptions(options) {
196
- const maybeObservability = options && "eventBus" in options && "prometheus" in options ? options : void 0;
197
- const raw = maybeObservability ? { metrics: maybeObservability.metrics } : options;
198
- return {
199
- metrics: raw?.metrics === false ? void 0 : raw?.metrics,
200
- logs: raw?.logs ?? {},
201
- spans: raw?.spans ?? {},
202
- adaptiveLimiter: resolveAdaptiveLimiterObservabilityOptions(raw?.adaptiveLimiter),
203
- injectTraceHeaders: raw?.injectTraceHeaders ?? true,
204
- includeHostLabel: raw?.includeHostLabel ?? true,
205
- route: raw?.route,
206
- clock: raw?.clock ?? Date.now,
207
- durationBuckets: raw?.durationBuckets
208
- };
209
- }
210
- function resolveAdaptiveLimiterObservabilityOptions(options) {
211
- if (options === false) return { enabled: false, includeKeyLabel: false };
212
- if (options === true || options === void 0) return { enabled: true, includeKeyLabel: false };
213
- return {
214
- enabled: options.enabled ?? true,
215
- includeKeyLabel: options.includeKeyLabel ?? false
216
- };
217
- }
218
- function observeAdaptiveLimiter(req, next, options) {
219
- if (!options.adaptiveLimiter.enabled) return {};
220
- const limiter = adaptiveLimiterOf(next);
221
- if (!limiter || !options.metrics && options.spans === false) return {};
222
- const stats = limiter.stats();
223
- const labels = adaptiveLimiterLabels(req, stats, options);
224
- setGauge(options.metrics, "brass_http_adaptive_limiter_limit", stats.limit, labels);
225
- setGauge(options.metrics, "brass_http_adaptive_limiter_in_flight", stats.inFlight, labels);
226
- setGauge(options.metrics, "brass_http_adaptive_limiter_queue_depth", stats.queueDepth, labels);
227
- setGauge(options.metrics, "brass_http_adaptive_limiter_state_count", stats.stateCount, labels);
228
- setGauge(options.metrics, "brass_http_adaptive_limiter_utilization", stats.utilization, labels);
229
- setGauge(options.metrics, "brass_http_adaptive_limiter_error_rate", stats.errorRate, labels);
230
- setGauge(options.metrics, "brass_http_adaptive_limiter_requests_per_second", stats.requestsPerSecond, labels);
231
- setGauge(options.metrics, "brass_http_adaptive_limiter_completions_per_second", stats.completionsPerSecond, labels);
232
- setGauge(options.metrics, "brass_http_adaptive_limiter_rejection_rate", stats.rejectionRate, labels);
233
- return compactSpanAttributes({
234
- "http.adaptive_limiter.limit": stats.limit,
235
- "http.adaptive_limiter.in_flight": stats.inFlight,
236
- "http.adaptive_limiter.queue_depth": stats.queueDepth,
237
- "http.adaptive_limiter.state_count": stats.stateCount,
238
- "http.adaptive_limiter.utilization": stats.utilization,
239
- "http.adaptive_limiter.error_rate": stats.errorRate,
240
- "http.adaptive_limiter.requests_per_second": stats.requestsPerSecond,
241
- "http.adaptive_limiter.completions_per_second": stats.completionsPerSecond,
242
- "http.adaptive_limiter.rejection_rate": stats.rejectionRate
243
- });
244
- }
245
- function adaptiveLimiterOf(next) {
246
- const limiter = next.adaptiveLimiter;
247
- return limiter && typeof limiter.stats === "function" ? limiter : void 0;
248
- }
249
- function adaptiveLimiterLabels(req, stats, options) {
250
- const key = options.adaptiveLimiter.includeKeyLabel ? inferAdaptiveLimiterKey(req, stats) : void 0;
251
- return compactLabels({
252
- ...requestBaseLabels(req, options),
253
- key
254
- });
255
- }
256
- function inferAdaptiveLimiterKey(req, stats) {
257
- if (req.poolKey) return req.poolKey;
258
- const host = requestHost(req);
259
- if (host) return host;
260
- return stats.keys?.length === 1 ? stats.keys[0] : void 0;
261
- }
262
- function setGauge(metrics, name, value, labels) {
263
- if (!metrics || value === void 0 || !Number.isFinite(value)) return;
264
- metrics.gauge(name, labels).set(value);
265
- }
266
- function compactSpanAttributes(attrs) {
267
- const out = {};
268
- for (const [key, value] of Object.entries(attrs)) {
269
- if (value !== void 0 && Number.isFinite(value)) out[key] = value;
270
- }
271
- return out;
272
- }
273
- function spanName(req, options) {
274
- if (options && options.name) {
275
- return typeof options.name === "function" ? options.name(req) : options.name;
276
- }
277
- return `HTTP ${req.method}`;
278
- }
279
- function spanStartAttributes(req, options) {
280
- const spanOptions = options.spans === false ? {} : options.spans.attributes;
281
- const custom = typeof spanOptions === "function" ? spanOptions(req) : spanOptions ?? {};
282
- const host = requestHost(req);
283
- const route = requestRoute(req, options);
284
- return {
285
- "span.kind": "client",
286
- "http.method": req.method,
287
- "http.request.method": req.method,
288
- "http.url": sanitizeUrl(req.url),
289
- "url.full": sanitizeUrl(req.url),
290
- ...urlScheme(req.url) ? { "url.scheme": urlScheme(req.url) } : {},
291
- ...urlPath(req.url) ? { "url.path": urlPath(req.url) } : {},
292
- ...host ? { "server.address": host } : {},
293
- ...route ? { "http.route": route } : {},
294
- ...custom
295
- };
296
- }
297
- function requestBaseLabels(req, options) {
298
- return compactLabels({
299
- method: req.method,
300
- ...options.includeHostLabel ? { host: requestHost(req) } : {},
301
- ...requestRoute(req, options) ? { route: requestRoute(req, options) } : {}
302
- });
303
- }
304
- function requestFinishLabels(req, outcome, status, options) {
305
- return {
306
- ...requestBaseLabels(req, options),
307
- outcome,
308
- status: status ?? "none"
309
- };
310
- }
311
- function requestLogFields(req, options) {
312
- const host = requestHost(req);
313
- const route = requestRoute(req, options);
314
- return {
315
- method: req.method,
316
- url: sanitizeUrl(req.url),
317
- ...host ? { host } : {},
318
- ...route ? { route } : {}
319
- };
320
- }
321
- function requestRoute(req, options) {
322
- if (typeof options.route === "function") return options.route(req);
323
- return options.route;
324
- }
325
- function httpStatusOutcome(status) {
326
- return status >= 400 ? "error" : "success";
327
- }
328
- function httpErrorOutcome(error) {
329
- switch (error._tag) {
330
- case "Abort":
331
- return "abort";
332
- case "BadUrl":
333
- return "bad_url";
334
- case "FetchError":
335
- return "fetch_error";
336
- case "Timeout":
337
- return "timeout";
338
- case "PoolRejected":
339
- return "pool_rejected";
340
- case "PoolTimeout":
341
- return "pool_timeout";
342
- case "PoolClosed":
343
- return "pool_closed";
344
- case "BatchSplitError":
345
- return "batch_split_error";
346
- }
347
- }
348
- function httpErrorMessage(error) {
349
- return "message" in error ? error.message : void 0;
350
- }
351
- function injectCurrentTraceContext(req) {
352
- const fiber = getCurrentFiber();
353
- const trace = fiber?.fiberContext?.trace;
354
- if (!trace?.traceId || !trace?.spanId) return req;
355
- return {
356
- ...req,
357
- headers: injectTraceContext(req.headers, trace)
358
- };
359
- }
360
- function currentTraceExemplar(value, timestamp) {
361
- const fiber = getCurrentFiber();
362
- return exemplarFromTraceContext(fiber?.fiberContext?.trace, value, timestamp);
363
- }
364
- function requestHost(req) {
365
- try {
366
- return new URL(req.url).host;
367
- } catch {
368
- return void 0;
369
- }
370
- }
371
- function urlScheme(url) {
372
- try {
373
- return new URL(url).protocol.replace(/:$/, "");
374
- } catch {
375
- return void 0;
376
- }
377
- }
378
- function urlPath(url) {
379
- try {
380
- return new URL(url).pathname;
381
- } catch {
382
- return void 0;
383
- }
384
- }
385
- function sanitizeUrl(url) {
386
- try {
387
- const parsed = new URL(url);
388
- parsed.search = "";
389
- parsed.hash = "";
390
- return parsed.toString();
391
- } catch {
392
- const [withoutHash] = url.split("#", 1);
393
- const [withoutQuery] = withoutHash.split("?", 1);
394
- return withoutQuery;
395
- }
396
- }
397
- function compactLabels(labels) {
398
- const out = {};
399
- for (const [key, value] of Object.entries(labels)) {
400
- if (value !== void 0 && value !== "") out[key] = value;
401
- }
402
- return out;
403
- }
404
-
405
- // src/observability/adapters.ts
406
- function makeFetchRequestObservabilityContext(observability, request, input = {}) {
407
- return makeRequestObservabilityContext(observability, {
408
- headers: request.headers,
409
- method: request.method,
410
- target: sanitizeHttpTarget(request.url),
411
- route: input.route ?? normalizeHttpRoute(urlPathname(request.url)),
412
- ...input
413
- });
414
- }
415
- function makeNodeRequestObservabilityContext(observability, request, input = {}) {
416
- return makeRequestObservabilityContext(observability, {
417
- headers: request.headers,
418
- method: request.method,
419
- target: sanitizeHttpTarget(request.url),
420
- route: input.route ?? normalizeHttpRoute(request.url),
421
- ...input
422
- });
423
- }
424
- function makeExpressRequestObservabilityContext(observability, request, input = {}) {
425
- return makeRequestObservabilityContext(observability, {
426
- headers: request.headers,
427
- method: request.method,
428
- target: sanitizeHttpTarget(request.originalUrl ?? request.url),
429
- route: input.route ?? request.route?.path ?? normalizeHttpRoute(request.path ?? request.url),
430
- ...input
431
- });
432
- }
433
- function makeFastifyRequestObservabilityContext(observability, request, input = {}) {
434
- return makeRequestObservabilityContext(observability, {
435
- headers: request.headers,
436
- method: request.method,
437
- target: sanitizeHttpTarget(request.url),
438
- route: input.route ?? request.routeOptions?.url ?? request.routerPath ?? normalizeHttpRoute(request.url),
439
- ...input
440
- });
441
- }
442
- function withFetchRequestObservability(observability, handler, input = {}) {
443
- return async (request) => handler(makeFetchRequestObservabilityContext(observability, request, input), request);
444
- }
445
- function withNodeRequestObservability(observability, handler, input = {}) {
446
- return async (request) => handler(makeNodeRequestObservabilityContext(observability, request, input), request);
447
- }
448
- function urlPathname(url) {
449
- if (!url) return void 0;
450
- try {
451
- return new URL(url, "http://local").pathname;
452
- } catch {
453
- return url;
454
- }
455
- }
456
-
457
- // src/observability/config.ts
458
- function makeNoopObservability() {
459
- return makeObservability({
460
- metrics: false,
461
- logs: false,
462
- traces: false,
463
- autoStart: false
464
- });
465
- }
466
- function makeObservabilityPreset(preset, overrides = {}) {
467
- if (preset === "disabled") return makeNoopObservability();
468
- return makeObservability(mergeObservabilityOptions(observabilityOptionsForPreset(preset), overrides));
469
- }
470
- function makeObservabilityFromEnv(env = readProcessEnv(), overrides = {}) {
471
- const preset = env.BRASS_OBSERVABILITY === "disabled" ? "disabled" : parsePreset(env.BRASS_OBSERVABILITY_PRESET ?? env.NODE_ENV);
472
- if (preset === "disabled") return makeNoopObservability();
473
- return makeObservability(mergeObservabilityOptions(observabilityOptionsFromEnv(env, preset), overrides));
474
- }
475
- function observabilityOptionsForPreset(preset) {
476
- switch (preset) {
477
- case "production":
478
- return {
479
- logs: { minLevel: "info" },
480
- sampling: { ratio: 1, respectRemoteSampled: true, forceSampleOnError: true },
481
- redaction: {},
482
- cardinality: { maxValuesPerLabel: 100 },
483
- otlp: {
484
- timeoutMs: 1e4,
485
- retry: { attempts: 3, initialDelayMs: 100, maxDelayMs: 2e3 },
486
- pipeline: {
487
- maxQueueSize: 1e4,
488
- batchSize: 512,
489
- dropPolicy: "drop-oldest",
490
- shutdownTimeoutMs: 1e4
491
- }
492
- },
493
- flushIntervalMs: 1e4,
494
- autoStart: true
495
- };
496
- case "test":
497
- return {
498
- logs: false,
499
- sampling: 1,
500
- redaction: {},
501
- otlp: {
502
- timeoutMs: 1e3,
503
- retry: { attempts: 1 },
504
- pipeline: { maxQueueSize: 1e3, batchSize: 256, shutdownTimeoutMs: 1e3 }
505
- },
506
- autoStart: false
507
- };
508
- case "development":
509
- return {
510
- logs: { minLevel: "debug" },
511
- sampling: 1,
512
- redaction: {},
513
- cardinality: { maxValuesPerLabel: 100 },
514
- autoStart: false
515
- };
516
- }
517
- }
518
- function observabilityOptionsFromEnv(env = readProcessEnv(), preset = parsePreset(env.BRASS_OBSERVABILITY_PRESET ?? env.NODE_ENV)) {
519
- const base = observabilityOptionsForPreset(preset);
520
- const serviceName = env.OTEL_SERVICE_NAME ?? env.BRASS_SERVICE_NAME;
521
- const serviceVersion = env.OTEL_SERVICE_VERSION ?? env.BRASS_SERVICE_VERSION;
522
- const endpoint = trimTrailingSlash(env.OTEL_EXPORTER_OTLP_ENDPOINT);
523
- const metricsUrl = env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT ?? (endpoint ? `${endpoint}/v1/metrics` : void 0);
524
- const tracesUrl = env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ?? (endpoint ? `${endpoint}/v1/traces` : void 0);
525
- const logsUrl = env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ?? (endpoint ? `${endpoint}/v1/logs` : void 0);
526
- const headers = parseHeaders(env.OTEL_EXPORTER_OTLP_HEADERS);
527
- const flushIntervalMs = parsePositiveInt(env.BRASS_OBSERVABILITY_FLUSH_INTERVAL_MS);
528
- const timeoutMs = parsePositiveInt(env.BRASS_OBSERVABILITY_EXPORT_TIMEOUT_MS);
529
- const sampleRatio = parseRatio(env.BRASS_TRACE_SAMPLE_RATIO ?? env.OTEL_TRACES_SAMPLER_ARG);
530
- const minLevel = env.BRASS_OBSERVABILITY_LOG_LEVEL;
531
- const otlp = {
532
- ...base.otlp ?? {},
533
- ...metricsUrl ? { metricsUrl } : {},
534
- ...tracesUrl ? { tracesUrl } : {},
535
- ...logsUrl ? { logsUrl } : {},
536
- ...headers ? { headers } : {},
537
- ...timeoutMs ? { timeoutMs } : {}
538
- };
539
- return mergeObservabilityOptions(base, {
540
- ...serviceName ? { serviceName } : {},
541
- ...serviceVersion ? { serviceVersion } : {},
542
- ...Object.keys(otlp).length > 0 ? { otlp } : {},
543
- ...flushIntervalMs ? { flushIntervalMs, autoStart: true } : {},
544
- ...sampleRatio !== void 0 ? { sampling: { ratio: sampleRatio, respectRemoteSampled: true, forceSampleOnError: true } } : {},
545
- ...minLevel === "debug" || minLevel === "info" || minLevel === "warn" || minLevel === "error" ? { logs: { minLevel } } : {}
546
- });
547
- }
548
- function mergeObservabilityOptions(base, overrides) {
549
- return {
550
- ...base,
551
- ...overrides,
552
- metrics: overrides.metrics ?? base.metrics,
553
- logs: overrides.logs ?? base.logs,
554
- otlp: {
555
- ...base.otlp ?? {},
556
- ...overrides.otlp ?? {},
557
- retry: overrides.otlp?.retry ?? base.otlp?.retry,
558
- pipeline: {
559
- ...base.otlp?.pipeline ?? {},
560
- ...overrides.otlp?.pipeline ?? {}
561
- }
562
- }
563
- };
564
- }
565
- function parsePreset(value) {
566
- if (value === "production" || value === "prod") return "production";
567
- if (value === "test") return "test";
568
- return "development";
569
- }
570
- function parseHeaders(value) {
571
- if (!value) return void 0;
572
- const out = {};
573
- for (const part of value.split(",")) {
574
- const index = part.indexOf("=");
575
- if (index <= 0) continue;
576
- const key = part.slice(0, index).trim();
577
- const headerValue = part.slice(index + 1).trim();
578
- if (key) out[key] = headerValue;
579
- }
580
- return Object.keys(out).length > 0 ? out : void 0;
581
- }
582
- function parsePositiveInt(value) {
583
- if (!value) return void 0;
584
- const parsed = Number.parseInt(value, 10);
585
- return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
586
- }
587
- function parseRatio(value) {
588
- if (!value) return void 0;
589
- const parsed = Number.parseFloat(value);
590
- if (!Number.isFinite(parsed)) return void 0;
591
- return Math.max(0, Math.min(1, parsed));
592
- }
593
- function trimTrailingSlash(value) {
594
- if (!value) return void 0;
595
- let out = value;
596
- while (out.endsWith("/")) out = out.slice(0, -1);
597
- return out || void 0;
598
- }
599
- function readProcessEnv() {
600
- return typeof process !== "undefined" ? process.env : {};
601
- }
602
87
  export {
88
+ HTTP_OBSERVABILITY_CONTRACT,
603
89
  OTLP_JSON_CONTENT_TYPE,
604
90
  PROMETHEUS_CONTENT_TYPE,
605
91
  alwaysOffSampler,
@@ -634,6 +120,7 @@ export {
634
120
  makeOtlpHttpLogExporter,
635
121
  makeOtlpHttpMetricsExporter,
636
122
  makeOtlpHttpSpanExporter,
123
+ makeOtlpOptions,
637
124
  makePrometheusMetricsExporter,
638
125
  makeRequestObservabilityContext,
639
126
  makeRuntimeHealth,