autotel 6.0.0 → 6.2.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 (412) hide show
  1. package/README.md +14 -1
  2. package/dist/analysis.cjs +1 -2
  3. package/dist/analysis.d.cts +1 -2
  4. package/dist/analysis.d.ts +1 -2
  5. package/dist/analysis.js +1 -2
  6. package/dist/attribute-redacting-processor-Pk7lAzWd.d.cts +1 -2
  7. package/dist/attribute-redacting-processor-Pk7lAzWd.d.ts +1 -2
  8. package/dist/attribute-redacting-processor.cjs +1 -2
  9. package/dist/attribute-redacting-processor.js +1 -2
  10. package/dist/attributes-B7wix2VG.js +1 -2
  11. package/dist/attributes-aDmJYral.cjs +1 -2
  12. package/dist/auto.cjs +2 -3
  13. package/dist/auto.js +2 -3
  14. package/dist/business-baggage.cjs +1 -2
  15. package/dist/business-baggage.d.cts +1 -2
  16. package/dist/business-baggage.d.ts +1 -2
  17. package/dist/business-baggage.js +1 -2
  18. package/dist/canonical-log-line-processor-B4WgG-VS.d.cts +1 -2
  19. package/dist/canonical-log-line-processor-CL_J7gb5.d.ts +1 -2
  20. package/dist/canonical-log-line-processor-DOjb6Kxy.cjs +1 -2
  21. package/dist/canonical-log-line-processor-mKABvGP0.js +1 -2
  22. package/dist/config.cjs +1 -2
  23. package/dist/config.d.cts +1 -2
  24. package/dist/config.d.ts +1 -2
  25. package/dist/config.js +1 -2
  26. package/dist/correlated-events-Bzh5y-UB.js +1 -2
  27. package/dist/correlated-events-kSwLo3mi.cjs +1 -2
  28. package/dist/correlation-id.cjs +1 -1
  29. package/dist/correlation-id.d.cts +1 -2
  30. package/dist/correlation-id.d.ts +1 -2
  31. package/dist/correlation-id.js +1 -1
  32. package/dist/db.cjs +1 -2
  33. package/dist/db.d.cts +1 -2
  34. package/dist/db.d.ts +1 -2
  35. package/dist/db.js +1 -2
  36. package/dist/decorators.cjs +2 -3
  37. package/dist/decorators.d.cts +1 -2
  38. package/dist/decorators.d.ts +1 -2
  39. package/dist/decorators.js +2 -3
  40. package/dist/define-event-B2z1SBe7.d.ts +1 -2
  41. package/dist/define-event-CGG048HE.d.cts +1 -2
  42. package/dist/diagnostics.cjs +1 -2
  43. package/dist/diagnostics.d.cts +1 -2
  44. package/dist/diagnostics.d.ts +1 -2
  45. package/dist/diagnostics.js +1 -2
  46. package/dist/drain-pipeline.cjs +11 -3
  47. package/dist/drain-pipeline.d.cts +6 -2
  48. package/dist/drain-pipeline.d.ts +6 -2
  49. package/dist/drain-pipeline.js +11 -3
  50. package/dist/enrichers.cjs +1 -2
  51. package/dist/enrichers.d.cts +1 -2
  52. package/dist/enrichers.d.ts +1 -2
  53. package/dist/enrichers.js +1 -2
  54. package/dist/event-subscriber-CWh8sTGh.d.cts +1 -2
  55. package/dist/event-subscriber-CWh8sTGh.d.ts +1 -2
  56. package/dist/event-testing-CA2mIx1E.d.ts +1 -2
  57. package/dist/event-testing-D0qWfqAy.d.cts +1 -2
  58. package/dist/event-testing.cjs +1 -2
  59. package/dist/event-testing.js +1 -2
  60. package/dist/event.cjs +1 -1
  61. package/dist/event.d.cts +1 -2
  62. package/dist/event.d.ts +1 -2
  63. package/dist/event.js +1 -1
  64. package/dist/exporters.d.cts +1 -2
  65. package/dist/exporters.d.ts +1 -2
  66. package/dist/filtering-span-processor-BpDagWNa.d.cts +1 -2
  67. package/dist/filtering-span-processor-BpDagWNa.d.ts +1 -2
  68. package/dist/filtering-span-processor.cjs +1 -2
  69. package/dist/filtering-span-processor.js +1 -2
  70. package/dist/{functional-DcLBuCPz.cjs → functional-D545J0Ba.cjs} +2 -3
  71. package/dist/{functional-BnDUxnSl.js → functional-DYAZiYxr.js} +2 -3
  72. package/dist/functional.cjs +1 -1
  73. package/dist/functional.d.cts +1 -2
  74. package/dist/functional.d.ts +1 -2
  75. package/dist/functional.js +1 -1
  76. package/dist/http.cjs +2 -3
  77. package/dist/http.d.cts +1 -2
  78. package/dist/http.d.ts +1 -2
  79. package/dist/http.js +2 -3
  80. package/dist/index-BPcxVucm.d.cts +1 -2
  81. package/dist/index-DolOVX6F.d.ts +1 -2
  82. package/dist/index.cjs +13 -6
  83. package/dist/index.d.cts +5 -3
  84. package/dist/index.d.ts +5 -3
  85. package/dist/index.js +13 -6
  86. package/dist/{init-D6n2diBy.d.ts → init-BHp-kqLJ.d.ts} +12 -4
  87. package/dist/{init-XzXzrarP.d.cts → init-BoIYyB6Q.d.cts} +12 -4
  88. package/dist/{init-C3RApHKJ.cjs → init-CzNFWUd8.cjs} +38 -5
  89. package/dist/{init-R-gSEXkJ.js → init-DDkh2brw.js} +38 -5
  90. package/dist/instrumentation.cjs +2 -3
  91. package/dist/instrumentation.d.cts +1 -2
  92. package/dist/instrumentation.d.ts +1 -2
  93. package/dist/instrumentation.js +2 -3
  94. package/dist/logger-CN4xpzMd.d.cts +1 -2
  95. package/dist/logger-CN4xpzMd.d.ts +1 -2
  96. package/dist/logger-D3Ej3DII.js +1 -2
  97. package/dist/logger-thMPLpOG.cjs +1 -2
  98. package/dist/messaging-adapters.cjs +1 -2
  99. package/dist/messaging-adapters.d.cts +1 -2
  100. package/dist/messaging-adapters.d.ts +1 -2
  101. package/dist/messaging-adapters.js +1 -2
  102. package/dist/messaging-testing.cjs +1 -2
  103. package/dist/messaging-testing.d.cts +1 -2
  104. package/dist/messaging-testing.d.ts +1 -2
  105. package/dist/messaging-testing.js +1 -2
  106. package/dist/messaging.cjs +2 -3
  107. package/dist/messaging.d.cts +1 -2
  108. package/dist/messaging.d.ts +1 -2
  109. package/dist/messaging.js +2 -3
  110. package/dist/metric-helpers.cjs +1 -2
  111. package/dist/metric-helpers.d.cts +1 -2
  112. package/dist/metric-helpers.d.ts +1 -2
  113. package/dist/metric-helpers.js +1 -2
  114. package/dist/metric-testing-CWm6UhaR.d.ts +1 -2
  115. package/dist/metric-testing-CpvP3ZIa.d.cts +1 -2
  116. package/dist/metric-testing.cjs +1 -2
  117. package/dist/metric-testing.js +1 -2
  118. package/dist/metric.cjs +1 -2
  119. package/dist/metric.d.cts +1 -2
  120. package/dist/metric.d.ts +1 -2
  121. package/dist/metric.js +1 -2
  122. package/dist/node-require-PFqDihze.cjs +1 -2
  123. package/dist/node-require-vROmTeJ8.js +1 -2
  124. package/dist/parse-error.cjs +1 -2
  125. package/dist/parse-error.d.cts +1 -2
  126. package/dist/parse-error.d.ts +1 -2
  127. package/dist/parse-error.js +1 -2
  128. package/dist/pretty-console-exporter-CMzlrRNg.cjs +1 -2
  129. package/dist/pretty-console-exporter-DqKl_q9z.js +1 -2
  130. package/dist/register.cjs +1 -2
  131. package/dist/register.js +1 -2
  132. package/dist/registry-DVSmWg6Y.js +1 -2
  133. package/dist/registry-DYgvb62e.cjs +1 -2
  134. package/dist/sampling-BR9fkSwx.d.cts +1 -2
  135. package/dist/sampling-CP3e7-Yr.d.ts +1 -2
  136. package/dist/sampling.cjs +1 -2
  137. package/dist/sampling.js +1 -2
  138. package/dist/security-schema.cjs +1 -2
  139. package/dist/security-schema.d.cts +1 -2
  140. package/dist/security-schema.d.ts +1 -2
  141. package/dist/security-schema.js +1 -2
  142. package/dist/semantic-conventions-FhSxv-bK.d.cts +1 -2
  143. package/dist/semantic-conventions-FhSxv-bK.d.ts +1 -2
  144. package/dist/semantic-conventions.cjs +1 -2
  145. package/dist/semantic-conventions.js +1 -2
  146. package/dist/semantic-helpers.cjs +2 -3
  147. package/dist/semantic-helpers.d.cts +1 -2
  148. package/dist/semantic-helpers.d.ts +1 -2
  149. package/dist/semantic-helpers.js +2 -3
  150. package/dist/slo.cjs +1 -2
  151. package/dist/slo.d.cts +1 -2
  152. package/dist/slo.d.ts +1 -2
  153. package/dist/slo.js +1 -2
  154. package/dist/span-name-normalizer-CN1-8C76.d.cts +1 -2
  155. package/dist/span-name-normalizer-CN1-8C76.d.ts +1 -2
  156. package/dist/span-name-normalizer.cjs +1 -2
  157. package/dist/span-name-normalizer.js +1 -2
  158. package/dist/stable-hash-CS9BxvVQ.cjs +1 -2
  159. package/dist/stable-hash-ChFBIhNt.js +1 -2
  160. package/dist/structured-error-9--cxBay.js +1 -2
  161. package/dist/structured-error-CHg7DoIQ.cjs +1 -2
  162. package/dist/tail-sampling-processor.cjs +1 -2
  163. package/dist/tail-sampling-processor.d.cts +1 -2
  164. package/dist/tail-sampling-processor.d.ts +1 -2
  165. package/dist/tail-sampling-processor.js +1 -2
  166. package/dist/test-span-collector.cjs +1 -2
  167. package/dist/test-span-collector.d.cts +1 -2
  168. package/dist/test-span-collector.d.ts +1 -2
  169. package/dist/test-span-collector.js +1 -2
  170. package/dist/testing.cjs +1 -2
  171. package/dist/testing.d.cts +1 -2
  172. package/dist/testing.d.ts +1 -2
  173. package/dist/testing.js +1 -2
  174. package/dist/trace-context-IuyoPyNq.d.cts +1 -2
  175. package/dist/trace-context-IuyoPyNq.d.ts +1 -2
  176. package/dist/trace-factory-validation-BjVAinz6.js +1 -2
  177. package/dist/trace-factory-validation-Dc8epn87.cjs +1 -2
  178. package/dist/trace-helpers.cjs +1 -2
  179. package/dist/trace-helpers.d.cts +1 -2
  180. package/dist/trace-helpers.d.ts +1 -2
  181. package/dist/trace-helpers.js +1 -2
  182. package/dist/tracer-provider.cjs +1 -2
  183. package/dist/tracer-provider.d.cts +1 -2
  184. package/dist/tracer-provider.d.ts +1 -2
  185. package/dist/tracer-provider.js +1 -2
  186. package/dist/validate.cjs +1 -2
  187. package/dist/validate.d.cts +1 -2
  188. package/dist/validate.d.ts +1 -2
  189. package/dist/validate.js +1 -2
  190. package/dist/validation-attributes.cjs +1 -2
  191. package/dist/validation-attributes.d.cts +1 -2
  192. package/dist/validation-attributes.d.ts +1 -2
  193. package/dist/validation-attributes.js +1 -2
  194. package/dist/webhook.cjs +2 -3
  195. package/dist/webhook.d.cts +1 -2
  196. package/dist/webhook.d.ts +1 -2
  197. package/dist/webhook.js +2 -3
  198. package/dist/workflow-distributed.cjs +2 -3
  199. package/dist/workflow-distributed.d.cts +1 -2
  200. package/dist/workflow-distributed.d.ts +1 -2
  201. package/dist/workflow-distributed.js +2 -3
  202. package/dist/workflow.cjs +2 -3
  203. package/dist/workflow.d.cts +1 -2
  204. package/dist/workflow.d.ts +1 -2
  205. package/dist/workflow.js +2 -3
  206. package/dist/yaml-config.cjs +1 -2
  207. package/dist/yaml-config.d.cts +4 -5
  208. package/dist/yaml-config.d.ts +4 -5
  209. package/dist/yaml-config.js +1 -2
  210. package/package.json +47 -32
  211. package/dist/analysis.cjs.map +0 -1
  212. package/dist/analysis.d.cts.map +0 -1
  213. package/dist/analysis.d.ts.map +0 -1
  214. package/dist/analysis.js.map +0 -1
  215. package/dist/attribute-redacting-processor-Pk7lAzWd.d.cts.map +0 -1
  216. package/dist/attribute-redacting-processor-Pk7lAzWd.d.ts.map +0 -1
  217. package/dist/attribute-redacting-processor.cjs.map +0 -1
  218. package/dist/attribute-redacting-processor.js.map +0 -1
  219. package/dist/attributes-B7wix2VG.js.map +0 -1
  220. package/dist/attributes-aDmJYral.cjs.map +0 -1
  221. package/dist/auto.cjs.map +0 -1
  222. package/dist/auto.js.map +0 -1
  223. package/dist/business-baggage.cjs.map +0 -1
  224. package/dist/business-baggage.d.cts.map +0 -1
  225. package/dist/business-baggage.d.ts.map +0 -1
  226. package/dist/business-baggage.js.map +0 -1
  227. package/dist/canonical-log-line-processor-B4WgG-VS.d.cts.map +0 -1
  228. package/dist/canonical-log-line-processor-CL_J7gb5.d.ts.map +0 -1
  229. package/dist/canonical-log-line-processor-DOjb6Kxy.cjs.map +0 -1
  230. package/dist/canonical-log-line-processor-mKABvGP0.js.map +0 -1
  231. package/dist/config.cjs.map +0 -1
  232. package/dist/config.d.cts.map +0 -1
  233. package/dist/config.d.ts.map +0 -1
  234. package/dist/config.js.map +0 -1
  235. package/dist/correlated-events-Bzh5y-UB.js.map +0 -1
  236. package/dist/correlated-events-kSwLo3mi.cjs.map +0 -1
  237. package/dist/correlation-id.d.cts.map +0 -1
  238. package/dist/correlation-id.d.ts.map +0 -1
  239. package/dist/db.cjs.map +0 -1
  240. package/dist/db.d.cts.map +0 -1
  241. package/dist/db.d.ts.map +0 -1
  242. package/dist/db.js.map +0 -1
  243. package/dist/decorators.cjs.map +0 -1
  244. package/dist/decorators.d.cts.map +0 -1
  245. package/dist/decorators.d.ts.map +0 -1
  246. package/dist/decorators.js.map +0 -1
  247. package/dist/define-event-B2z1SBe7.d.ts.map +0 -1
  248. package/dist/define-event-CGG048HE.d.cts.map +0 -1
  249. package/dist/diagnostics.cjs.map +0 -1
  250. package/dist/diagnostics.d.cts.map +0 -1
  251. package/dist/diagnostics.d.ts.map +0 -1
  252. package/dist/diagnostics.js.map +0 -1
  253. package/dist/drain-pipeline.cjs.map +0 -1
  254. package/dist/drain-pipeline.d.cts.map +0 -1
  255. package/dist/drain-pipeline.d.ts.map +0 -1
  256. package/dist/drain-pipeline.js.map +0 -1
  257. package/dist/enrichers.cjs.map +0 -1
  258. package/dist/enrichers.d.cts.map +0 -1
  259. package/dist/enrichers.d.ts.map +0 -1
  260. package/dist/enrichers.js.map +0 -1
  261. package/dist/event-subscriber-CWh8sTGh.d.cts.map +0 -1
  262. package/dist/event-subscriber-CWh8sTGh.d.ts.map +0 -1
  263. package/dist/event-testing-CA2mIx1E.d.ts.map +0 -1
  264. package/dist/event-testing-D0qWfqAy.d.cts.map +0 -1
  265. package/dist/event-testing.cjs.map +0 -1
  266. package/dist/event-testing.js.map +0 -1
  267. package/dist/event.d.cts.map +0 -1
  268. package/dist/event.d.ts.map +0 -1
  269. package/dist/exporters.d.cts.map +0 -1
  270. package/dist/exporters.d.ts.map +0 -1
  271. package/dist/filtering-span-processor-BpDagWNa.d.cts.map +0 -1
  272. package/dist/filtering-span-processor-BpDagWNa.d.ts.map +0 -1
  273. package/dist/filtering-span-processor.cjs.map +0 -1
  274. package/dist/filtering-span-processor.js.map +0 -1
  275. package/dist/functional-BnDUxnSl.js.map +0 -1
  276. package/dist/functional-DcLBuCPz.cjs.map +0 -1
  277. package/dist/functional.d.cts.map +0 -1
  278. package/dist/functional.d.ts.map +0 -1
  279. package/dist/http.cjs.map +0 -1
  280. package/dist/http.d.cts.map +0 -1
  281. package/dist/http.d.ts.map +0 -1
  282. package/dist/http.js.map +0 -1
  283. package/dist/index-BPcxVucm.d.cts.map +0 -1
  284. package/dist/index-DolOVX6F.d.ts.map +0 -1
  285. package/dist/index.cjs.map +0 -1
  286. package/dist/index.d.cts.map +0 -1
  287. package/dist/index.d.ts.map +0 -1
  288. package/dist/index.js.map +0 -1
  289. package/dist/init-C3RApHKJ.cjs.map +0 -1
  290. package/dist/init-D6n2diBy.d.ts.map +0 -1
  291. package/dist/init-R-gSEXkJ.js.map +0 -1
  292. package/dist/init-XzXzrarP.d.cts.map +0 -1
  293. package/dist/instrumentation.cjs.map +0 -1
  294. package/dist/instrumentation.d.cts.map +0 -1
  295. package/dist/instrumentation.d.ts.map +0 -1
  296. package/dist/instrumentation.js.map +0 -1
  297. package/dist/logger-CN4xpzMd.d.cts.map +0 -1
  298. package/dist/logger-CN4xpzMd.d.ts.map +0 -1
  299. package/dist/logger-D3Ej3DII.js.map +0 -1
  300. package/dist/logger-thMPLpOG.cjs.map +0 -1
  301. package/dist/messaging-adapters.cjs.map +0 -1
  302. package/dist/messaging-adapters.d.cts.map +0 -1
  303. package/dist/messaging-adapters.d.ts.map +0 -1
  304. package/dist/messaging-adapters.js.map +0 -1
  305. package/dist/messaging-testing.cjs.map +0 -1
  306. package/dist/messaging-testing.d.cts.map +0 -1
  307. package/dist/messaging-testing.d.ts.map +0 -1
  308. package/dist/messaging-testing.js.map +0 -1
  309. package/dist/messaging.cjs.map +0 -1
  310. package/dist/messaging.d.cts.map +0 -1
  311. package/dist/messaging.d.ts.map +0 -1
  312. package/dist/messaging.js.map +0 -1
  313. package/dist/metric-helpers.cjs.map +0 -1
  314. package/dist/metric-helpers.d.cts.map +0 -1
  315. package/dist/metric-helpers.d.ts.map +0 -1
  316. package/dist/metric-helpers.js.map +0 -1
  317. package/dist/metric-testing-CWm6UhaR.d.ts.map +0 -1
  318. package/dist/metric-testing-CpvP3ZIa.d.cts.map +0 -1
  319. package/dist/metric-testing.cjs.map +0 -1
  320. package/dist/metric-testing.js.map +0 -1
  321. package/dist/metric.cjs.map +0 -1
  322. package/dist/metric.d.cts.map +0 -1
  323. package/dist/metric.d.ts.map +0 -1
  324. package/dist/metric.js.map +0 -1
  325. package/dist/node-require-PFqDihze.cjs.map +0 -1
  326. package/dist/node-require-vROmTeJ8.js.map +0 -1
  327. package/dist/parse-error.cjs.map +0 -1
  328. package/dist/parse-error.d.cts.map +0 -1
  329. package/dist/parse-error.d.ts.map +0 -1
  330. package/dist/parse-error.js.map +0 -1
  331. package/dist/pretty-console-exporter-CMzlrRNg.cjs.map +0 -1
  332. package/dist/pretty-console-exporter-DqKl_q9z.js.map +0 -1
  333. package/dist/register.cjs.map +0 -1
  334. package/dist/register.js.map +0 -1
  335. package/dist/registry-DVSmWg6Y.js.map +0 -1
  336. package/dist/registry-DYgvb62e.cjs.map +0 -1
  337. package/dist/sampling-BR9fkSwx.d.cts.map +0 -1
  338. package/dist/sampling-CP3e7-Yr.d.ts.map +0 -1
  339. package/dist/sampling.cjs.map +0 -1
  340. package/dist/sampling.js.map +0 -1
  341. package/dist/security-schema.cjs.map +0 -1
  342. package/dist/security-schema.d.cts.map +0 -1
  343. package/dist/security-schema.d.ts.map +0 -1
  344. package/dist/security-schema.js.map +0 -1
  345. package/dist/semantic-conventions-FhSxv-bK.d.cts.map +0 -1
  346. package/dist/semantic-conventions-FhSxv-bK.d.ts.map +0 -1
  347. package/dist/semantic-conventions.cjs.map +0 -1
  348. package/dist/semantic-conventions.js.map +0 -1
  349. package/dist/semantic-helpers.cjs.map +0 -1
  350. package/dist/semantic-helpers.d.cts.map +0 -1
  351. package/dist/semantic-helpers.d.ts.map +0 -1
  352. package/dist/semantic-helpers.js.map +0 -1
  353. package/dist/slo.cjs.map +0 -1
  354. package/dist/slo.d.cts.map +0 -1
  355. package/dist/slo.d.ts.map +0 -1
  356. package/dist/slo.js.map +0 -1
  357. package/dist/span-name-normalizer-CN1-8C76.d.cts.map +0 -1
  358. package/dist/span-name-normalizer-CN1-8C76.d.ts.map +0 -1
  359. package/dist/span-name-normalizer.cjs.map +0 -1
  360. package/dist/span-name-normalizer.js.map +0 -1
  361. package/dist/stable-hash-CS9BxvVQ.cjs.map +0 -1
  362. package/dist/stable-hash-ChFBIhNt.js.map +0 -1
  363. package/dist/structured-error-9--cxBay.js.map +0 -1
  364. package/dist/structured-error-CHg7DoIQ.cjs.map +0 -1
  365. package/dist/tail-sampling-processor.cjs.map +0 -1
  366. package/dist/tail-sampling-processor.d.cts.map +0 -1
  367. package/dist/tail-sampling-processor.d.ts.map +0 -1
  368. package/dist/tail-sampling-processor.js.map +0 -1
  369. package/dist/test-span-collector.cjs.map +0 -1
  370. package/dist/test-span-collector.d.cts.map +0 -1
  371. package/dist/test-span-collector.d.ts.map +0 -1
  372. package/dist/test-span-collector.js.map +0 -1
  373. package/dist/testing.cjs.map +0 -1
  374. package/dist/testing.d.cts.map +0 -1
  375. package/dist/testing.d.ts.map +0 -1
  376. package/dist/testing.js.map +0 -1
  377. package/dist/trace-context-IuyoPyNq.d.cts.map +0 -1
  378. package/dist/trace-context-IuyoPyNq.d.ts.map +0 -1
  379. package/dist/trace-factory-validation-BjVAinz6.js.map +0 -1
  380. package/dist/trace-factory-validation-Dc8epn87.cjs.map +0 -1
  381. package/dist/trace-helpers.cjs.map +0 -1
  382. package/dist/trace-helpers.d.cts.map +0 -1
  383. package/dist/trace-helpers.d.ts.map +0 -1
  384. package/dist/trace-helpers.js.map +0 -1
  385. package/dist/tracer-provider.cjs.map +0 -1
  386. package/dist/tracer-provider.d.cts.map +0 -1
  387. package/dist/tracer-provider.d.ts.map +0 -1
  388. package/dist/tracer-provider.js.map +0 -1
  389. package/dist/validate.cjs.map +0 -1
  390. package/dist/validate.d.cts.map +0 -1
  391. package/dist/validate.d.ts.map +0 -1
  392. package/dist/validate.js.map +0 -1
  393. package/dist/validation-attributes.cjs.map +0 -1
  394. package/dist/validation-attributes.d.cts.map +0 -1
  395. package/dist/validation-attributes.d.ts.map +0 -1
  396. package/dist/validation-attributes.js.map +0 -1
  397. package/dist/webhook.cjs.map +0 -1
  398. package/dist/webhook.d.cts.map +0 -1
  399. package/dist/webhook.d.ts.map +0 -1
  400. package/dist/webhook.js.map +0 -1
  401. package/dist/workflow-distributed.cjs.map +0 -1
  402. package/dist/workflow-distributed.d.cts.map +0 -1
  403. package/dist/workflow-distributed.d.ts.map +0 -1
  404. package/dist/workflow-distributed.js.map +0 -1
  405. package/dist/workflow.cjs.map +0 -1
  406. package/dist/workflow.d.cts.map +0 -1
  407. package/dist/workflow.d.ts.map +0 -1
  408. package/dist/workflow.js.map +0 -1
  409. package/dist/yaml-config.cjs.map +0 -1
  410. package/dist/yaml-config.d.cts.map +0 -1
  411. package/dist/yaml-config.d.ts.map +0 -1
  412. package/dist/yaml-config.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"semantic-helpers.js","names":[],"sources":["../src/semantic-helpers.ts"],"sourcesContent":["/**\n * Semantic convention helpers for OpenTelemetry\n *\n * Pre-configured trace helpers that follow OpenTelemetry semantic conventions\n * for common operation types. Reduces boilerplate and ensures consistency.\n *\n * Based on: https://opentelemetry.io/docs/specs/semconv/\n */\n\nimport { withTracing } from './functional';\nimport { assertTraceFactory } from './trace-factory-validation';\nimport type { TraceContext } from './trace-context';\nimport { SpanKind, type Attributes } from '@opentelemetry/api';\n\ntype SemanticHandler<TArgs extends unknown[], TReturn> = (\n ...args: TArgs\n) => TReturn | Promise<TReturn>;\n\ntype SemanticFactory<TArgs extends unknown[], TReturn> = (\n ctx: TraceContext,\n) => SemanticHandler<TArgs, TReturn>;\n\nfunction setConfiguredAttributes(\n ctx: TraceContext,\n attributes?: Attributes,\n): void {\n if (!attributes) return;\n for (const [key, value] of Object.entries(attributes)) {\n if (value === undefined || value === null) continue;\n const attributeValue =\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value\n : JSON.stringify(value);\n ctx.setAttribute(key, attributeValue);\n }\n}\n\n/**\n * Shared tail of `traceDB`/`traceHTTP`/`traceMessaging`: wrap `fn` directly\n * when given, otherwise return the curried factory-acceptor form.\n */\nfunction wrapSemantic<TArgs extends unknown[], TReturn>(\n helperName: string,\n name: string,\n spanKind: SpanKind,\n configure: (ctx: TraceContext) => void,\n fn?: SemanticHandler<TArgs, TReturn>,\n):\n | SemanticHandler<TArgs, TReturn>\n | (<TFactoryArgs extends unknown[], TFactoryReturn>(\n factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n ) => SemanticHandler<TFactoryArgs, TFactoryReturn>) {\n if (fn) {\n assertTraceFactory(helperName, fn);\n return withTracing<TArgs, TReturn>({ name, spanKind })(\n (ctx: TraceContext) => {\n configure(ctx);\n return fn;\n },\n );\n }\n return <TFactoryArgs extends unknown[], TFactoryReturn>(\n factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n ) => {\n assertTraceFactory(helperName, factory);\n return withTracing<TFactoryArgs, TFactoryReturn>({ name, spanKind })(\n (ctx: TraceContext) => {\n configure(ctx);\n const handler = factory(ctx);\n assertTraceFactory(helperName, handler, 'result');\n return handler;\n },\n );\n };\n}\n\n/**\n * Configuration for database operations\n *\n * Follows DB semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/database/\n */\nexport interface DBConfig {\n /** Database system (e.g., 'postgresql', 'mongodb', 'redis') */\n system: string;\n /** Operation type (e.g., 'SELECT', 'INSERT', 'find', 'get') */\n operation?: string;\n /** Database name */\n database?: string;\n /** Collection/table name */\n collection?: string;\n /** Low-cardinality query summary used as the preferred span name */\n querySummary?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Configuration for HTTP client operations\n *\n * Follows HTTP semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/http/\n */\nexport interface HTTPConfig {\n /** HTTP method (e.g., 'GET', 'POST') */\n method?: string;\n /** Target URL or URL template */\n url?: string;\n /** Low-cardinality server route used in the span name */\n route?: string;\n /** Low-cardinality client URL template used in the span name */\n urlTemplate?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Configuration for messaging operations\n *\n * Follows Messaging semantic conventions:\n * https://opentelemetry.io/docs/specs/semconv/messaging/\n */\nexport interface MessagingConfig {\n /** Messaging system (e.g., 'kafka', 'rabbitmq', 'sqs') */\n system: string;\n /** Operation type */\n operation?: 'publish' | 'receive' | 'process';\n /** Destination name (queue/topic) */\n destination?: string;\n /** Additional attributes to add to the span */\n attributes?: Attributes;\n}\n\n/**\n * Trace database operations with DB semantic conventions\n *\n * Automatically adds standard attributes for database operations:\n * - db.system.name\n * - db.operation.name\n * - db.namespace\n * - db.collection.name (for NoSQL)\n *\n * **Use Cases:**\n * - SQL queries (PostgreSQL, MySQL, SQLite)\n * - NoSQL operations (MongoDB, DynamoDB, Redis)\n * - ORM queries (Prisma, TypeORM, Drizzle)\n *\n * @param config - Database operation configuration\n * @returns Traced function factory with DB attributes\n *\n * @example PostgreSQL query\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { pool } from './db'\n *\n * export const getUser = traceDB({\n * system: 'postgresql',\n * operation: 'SELECT',\n * database: 'app_db',\n * collection: 'users',\n * querySummary: 'SELECT users'\n * })(ctx => async (userId: string) => {\n * const query = 'SELECT * FROM users WHERE id = $1'\n * const result = await pool.query(query, [userId])\n * ctx.setAttribute('db.rows_affected', result.rowCount)\n *\n * return result.rows[0]\n * })\n * ```\n *\n * @example MongoDB with Mongoose\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { User } from './models/User'\n *\n * export const findUsers = traceDB({\n * system: 'mongodb',\n * operation: 'find',\n * database: 'app_db',\n * collection: 'users'\n * })(ctx => async (filter: object) => {\n * ctx.setAttribute('db.mongodb.filter', JSON.stringify(filter))\n *\n * const users = await User.find(filter).limit(100)\n * ctx.setAttribute('db.response.count', users.length)\n *\n * return users\n * })\n * ```\n *\n * @example Redis operations\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { redis } from './redis'\n *\n * export const cacheGet = traceDB({\n * system: 'redis',\n * operation: 'GET'\n * })(ctx => async (key: string) => {\n * ctx.setAttribute('db.redis.key', key)\n *\n * const value = await redis.get(key)\n * ctx.setAttribute('db.response.cache_hit', value !== null)\n *\n * return value\n * })\n * ```\n *\n * @example Prisma with detailed query info\n * ```typescript\n * import { traceDB } from 'autotel/semantic-helpers'\n * import { prisma } from './prisma'\n *\n * export const createPost = traceDB({\n * system: 'postgresql',\n * operation: 'INSERT',\n * database: 'app_db',\n * collection: 'posts'\n * })(ctx => async (data: { title: string; content: string; authorId: string }) => {\n * ctx.setAttribute('db.prisma.model', 'Post')\n * ctx.setAttribute('db.prisma.action', 'create')\n *\n * const post = await prisma.post.create({ data })\n *\n * ctx.setAttribute('db.response.id', post.id)\n * return post\n * })\n * ```\n *\n * @public\n */\nexport function traceDB(\n config: DBConfig,\n): <TArgs extends unknown[], TReturn>(\n factory: SemanticFactory<TArgs, TReturn>,\n) => SemanticHandler<TArgs, TReturn>;\nexport function traceDB<TArgs extends unknown[], TReturn>(\n config: DBConfig,\n fn: SemanticHandler<TArgs, TReturn>,\n): SemanticHandler<TArgs, TReturn>;\nexport function traceDB<TArgs extends unknown[], TReturn>(\n config: DBConfig,\n fn?: SemanticHandler<TArgs, TReturn>,\n):\n | SemanticHandler<TArgs, TReturn>\n | (<TFactoryArgs extends unknown[], TFactoryReturn>(\n factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n ) => SemanticHandler<TFactoryArgs, TFactoryReturn>) {\n if (!config || typeof config !== 'object') {\n throw new TypeError('traceDB: config must be an object');\n }\n if (typeof config.system !== 'string' || config.system.trim() === '') {\n throw new TypeError('traceDB: config.system must be a non-empty string');\n }\n const target = config.collection ?? config.database;\n const name =\n config.querySummary ?? [config.operation, target].filter(Boolean).join(' ');\n const spanName = name || config.system;\n const configure = (ctx: TraceContext) => {\n // Emit current stable names plus legacy aliases for dashboard compatibility.\n ctx.setAttribute('db.system.name', config.system);\n ctx.setAttribute('db.system', config.system);\n if (config.operation) {\n ctx.setAttribute('db.operation.name', config.operation);\n ctx.setAttribute('db.operation', config.operation);\n }\n if (config.database) {\n ctx.setAttribute('db.namespace', config.database);\n ctx.setAttribute('db.name', config.database);\n }\n if (config.collection) {\n ctx.setAttribute('db.collection.name', config.collection);\n }\n if (config.querySummary) {\n ctx.setAttribute('db.query.summary', config.querySummary);\n }\n setConfiguredAttributes(ctx, config.attributes);\n };\n return wrapSemantic('traceDB', spanName, SpanKind.CLIENT, configure, fn);\n}\n\n/**\n * Trace HTTP client operations with HTTP semantic conventions\n *\n * Automatically adds standard attributes for HTTP requests:\n * - http.request.method\n * - url.full\n *\n * **Use Cases:**\n * - External API calls\n * - Microservice communication\n * - Third-party integrations\n *\n * @param config - HTTP operation configuration\n * @returns Traced function factory with HTTP attributes\n *\n * @example Fetch API\n * ```typescript\n * import { traceHTTP } from 'autotel/semantic-helpers'\n *\n * export const fetchUser = traceHTTP({\n * method: 'GET',\n * urlTemplate: '/users/{id}'\n * })(ctx => async (userId: string) => {\n * const url = `https://api.example.com/users/${userId}`\n * ctx.setAttribute('url.full', url)\n *\n * const response = await fetch(url)\n * ctx.setAttribute('http.response.status_code', response.status)\n *\n * if (!response.ok) {\n * ctx.setAttribute('error', true)\n * throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n * }\n *\n * return response.json()\n * })\n * ```\n *\n * @example Axios with retry logic\n * ```typescript\n * import { traceHTTP } from 'autotel/semantic-helpers'\n * import axios from 'axios'\n *\n * export const sendWebhook = traceHTTP({\n * method: 'POST',\n * url: 'https://webhook.example.com/events'\n * })(ctx => async (payload: object) => {\n * let attempts = 0\n * const maxAttempts = 3\n *\n * while (attempts < maxAttempts) {\n * try {\n * attempts++\n * ctx.setAttribute('http.request.resend_count', attempts - 1)\n *\n * const response = await axios.post('https://webhook.example.com/events', payload)\n * ctx.setAttribute('http.response.status_code', response.status)\n * return response.data\n * } catch (error) {\n * if (attempts >= maxAttempts) throw error\n * await new Promise(resolve => setTimeout(resolve, 1000 * attempts))\n * }\n * }\n * })\n * ```\n *\n * @public\n */\nexport function traceHTTP(\n config: HTTPConfig,\n): <TArgs extends unknown[], TReturn>(\n factory: SemanticFactory<TArgs, TReturn>,\n) => SemanticHandler<TArgs, TReturn>;\nexport function traceHTTP<TArgs extends unknown[], TReturn>(\n config: HTTPConfig,\n fn: SemanticHandler<TArgs, TReturn>,\n): SemanticHandler<TArgs, TReturn>;\nexport function traceHTTP<TArgs extends unknown[], TReturn>(\n config: HTTPConfig,\n fn?: SemanticHandler<TArgs, TReturn>,\n):\n | SemanticHandler<TArgs, TReturn>\n | (<TFactoryArgs extends unknown[], TFactoryReturn>(\n factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n ) => SemanticHandler<TFactoryArgs, TFactoryReturn>) {\n if (!config || typeof config !== 'object') {\n throw new TypeError('traceHTTP: config must be an object');\n }\n const method = config.method?.toUpperCase() || 'HTTP';\n const target = config.route ?? config.urlTemplate;\n const spanName = target ? `${method} ${target}` : method;\n const configure = (ctx: TraceContext) => {\n if (config.method) ctx.setAttribute('http.request.method', method);\n if (config.url) ctx.setAttribute('url.full', config.url);\n if (config.route) ctx.setAttribute('http.route', config.route);\n if (config.urlTemplate) {\n ctx.setAttribute('url.template', config.urlTemplate);\n }\n setConfiguredAttributes(ctx, config.attributes);\n };\n return wrapSemantic('traceHTTP', spanName, SpanKind.CLIENT, configure, fn);\n}\n\n/**\n * Trace messaging operations with Messaging semantic conventions\n *\n * Automatically adds standard attributes for messaging:\n * - messaging.system\n * - messaging.operation\n * - messaging.destination.name\n *\n * **Use Cases:**\n * - Publishing messages to queues/topics\n * - Consuming messages from queues/topics\n * - Event-driven architectures\n *\n * @param config - Messaging operation configuration\n * @returns Traced function factory with Messaging attributes\n *\n * @example Publishing to Kafka\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { kafka } from './kafka'\n *\n * const producer = kafka.producer()\n *\n * export const publishEvent = traceMessaging({\n * system: 'kafka',\n * operation: 'publish',\n * destination: 'user-events'\n * })(ctx => async (event: { type: string; userId: string; data: object }) => {\n * ctx.setAttribute('messaging.message.type', event.type)\n * ctx.setAttribute('messaging.kafka.partition', 0)\n *\n * await producer.send({\n * topic: 'user-events',\n * messages: [\n * {\n * key: event.userId,\n * value: JSON.stringify(event.data)\n * }\n * ]\n * })\n *\n * ctx.setAttribute('messaging.message.id', event.userId)\n * })\n * ```\n *\n * @example Consuming from RabbitMQ\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { channel } from './rabbitmq'\n *\n * export const processOrder = traceMessaging({\n * system: 'rabbitmq',\n * operation: 'process',\n * destination: 'orders'\n * })(ctx => async (message: { orderId: string; items: object[] }) => {\n * ctx.setAttribute('messaging.message.id', message.orderId)\n * ctx.setAttribute('messaging.message.body.size', JSON.stringify(message).length)\n *\n * // Process order logic\n * const result = await processOrderInternal(message)\n *\n * ctx.setAttribute('messaging.operation.result', 'success')\n * return result\n * })\n * ```\n *\n * @example AWS SQS with batch processing\n * ```typescript\n * import { traceMessaging } from 'autotel/semantic-helpers'\n * import { SQS } from '@aws-sdk/client-sqs'\n *\n * const sqs = new SQS()\n *\n * export const sendBatch = traceMessaging({\n * system: 'aws_sqs',\n * operation: 'publish',\n * destination: 'notifications-queue'\n * })(ctx => async (messages: Array<{ id: string; body: object }>) => {\n * ctx.setAttribute('messaging.batch.message_count', messages.length)\n *\n * const result = await sqs.sendMessageBatch({\n * QueueUrl: process.env.QUEUE_URL,\n * Entries: messages.map(msg => ({\n * Id: msg.id,\n * MessageBody: JSON.stringify(msg.body)\n * }))\n * })\n *\n * ctx.setAttribute('messaging.operation.success_count', result.Successful?.length || 0)\n * ctx.setAttribute('messaging.operation.failed_count', result.Failed?.length || 0)\n *\n * return result\n * })\n * ```\n *\n * @public\n */\nexport function traceMessaging(\n config: MessagingConfig,\n): <TArgs extends unknown[], TReturn>(\n factory: SemanticFactory<TArgs, TReturn>,\n) => SemanticHandler<TArgs, TReturn>;\nexport function traceMessaging<TArgs extends unknown[], TReturn>(\n config: MessagingConfig,\n fn: SemanticHandler<TArgs, TReturn>,\n): SemanticHandler<TArgs, TReturn>;\nexport function traceMessaging<TArgs extends unknown[], TReturn>(\n config: MessagingConfig,\n fn?: SemanticHandler<TArgs, TReturn>,\n):\n | SemanticHandler<TArgs, TReturn>\n | (<TFactoryArgs extends unknown[], TFactoryReturn>(\n factory: SemanticFactory<TFactoryArgs, TFactoryReturn>,\n ) => SemanticHandler<TFactoryArgs, TFactoryReturn>) {\n if (!config || typeof config !== 'object') {\n throw new TypeError('traceMessaging: config must be an object');\n }\n if (typeof config.system !== 'string' || config.system.trim() === '') {\n throw new TypeError(\n 'traceMessaging: config.system must be a non-empty string',\n );\n }\n const operation = config.operation ?? 'messaging';\n const spanName = [operation, config.destination].filter(Boolean).join(' ');\n const operationType = operation === 'publish' ? 'send' : operation;\n const spanKind =\n operation === 'publish'\n ? SpanKind.PRODUCER\n : operation === 'process'\n ? SpanKind.CONSUMER\n : operation === 'receive'\n ? SpanKind.CLIENT\n : SpanKind.INTERNAL;\n const configure = (ctx: TraceContext) => {\n ctx.setAttribute('messaging.system', config.system);\n if (config.operation) {\n ctx.setAttribute('messaging.operation.name', config.operation);\n ctx.setAttribute('messaging.operation.type', operationType);\n ctx.setAttribute('messaging.operation', config.operation);\n }\n if (config.destination) {\n ctx.setAttribute('messaging.destination.name', config.destination);\n }\n setConfiguredAttributes(ctx, config.attributes);\n };\n return wrapSemantic('traceMessaging', spanName, spanKind, configure, fn);\n}\n"],"mappings":"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"}
package/dist/slo.cjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"slo.cjs","names":["getConfig","otelTrace"],"sources":["../src/slo.ts"],"sourcesContent":["/**\n * Service level objective tracking and burn-rate evaluation.\n *\n * The tracker keeps a bounded rolling window in memory, records low-cardinality\n * OpenTelemetry metrics, and returns snapshots that callers can feed into alert\n * evaluation. Production systems can persist the emitted metrics in their\n * telemetry backend while tests use the same calculations in process.\n */\n\nimport {\n trace as otelTrace,\n type Attributes,\n type Counter,\n type Histogram,\n} from '@opentelemetry/api';\nimport { getConfig } from './config';\n\nexport type SloOutcome = 'good' | 'bad';\n\nexport interface SloDefinition {\n /** Stable identifier used in metric attributes and alerts. */\n name: string;\n /** Required success ratio, expressed as a value between 0 and 1. */\n target: number;\n /** Rolling observation window in milliseconds. */\n windowMs: number;\n}\n\nexport interface SloTrackerOptions {\n /** Clock override for deterministic tests. */\n now?: () => number;\n /** Disable metric recording when calculations run outside an initialized SDK. */\n recordMetrics?: boolean;\n}\n\nexport interface SloSnapshot extends SloDefinition {\n observedAt: number;\n total: number;\n good: number;\n bad: number;\n /** Observed good-event ratio. Undefined until the tracker sees an event. */\n sli?: number;\n /** Fraction of events the objective permits to fail. */\n errorBudgetFraction: number;\n /** Fraction of the available error budget consumed in this window. */\n budgetConsumed: number;\n /** Remaining budget fraction. Negative values show overspend. */\n budgetRemaining: number;\n /** Observed failure ratio divided by the permitted failure ratio. */\n burnRate: number;\n meetsTarget: boolean;\n}\n\nexport interface SloTracker {\n readonly definition: Readonly<SloDefinition>;\n record(outcome: SloOutcome, attributes?: Attributes): SloSnapshot;\n snapshot(at?: number): SloSnapshot;\n forecast(options: SloForecastOptions): SloForecast;\n reset(): void;\n}\n\nexport interface SloForecastOptions {\n /** Recent observation period used to estimate the future failure rate. */\n baselineMs: number;\n /** Future period covered by the forecast. Must not exceed 4x the baseline. */\n lookaheadMs: number;\n /**\n * Expected eligible events during the lookahead. Defaults to the event rate\n * observed during the baseline.\n */\n expectedEventsInLookahead?: number;\n}\n\nexport interface SloForecast {\n name: string;\n target: number;\n observedAt: number;\n baselineMs: number;\n lookaheadMs: number;\n baselineTotal: number;\n baselineBad: number;\n baselineFailureRate: number;\n retainedTotal: number;\n retainedBad: number;\n projectedTotal: number;\n projectedBad: number;\n projectedSli?: number;\n /** Milliseconds until the retained and projected error budget is exhausted. */\n timeToExhaustionMs?: number;\n alerting: boolean;\n reason:\n 'no-baseline-traffic' | 'within-budget' | 'projected-budget-exhaustion';\n}\n\nexport interface BurnRateAlertOptions {\n shortWindow: SloSnapshot;\n longWindow: SloSnapshot;\n shortThreshold: number;\n longThreshold: number;\n}\n\nexport interface BurnRateAlertDecision {\n alerting: boolean;\n reason:\n | 'burn-rate-thresholds-exceeded'\n | 'short-window-below-threshold'\n | 'long-window-below-threshold'\n | 'no-traffic';\n shortBurnRate: number;\n longBurnRate: number;\n}\n\ninterface Observation {\n outcome: SloOutcome;\n timestamp: number;\n}\n\nfunction validateDefinition(definition: SloDefinition): void {\n if (!definition.name.trim()) {\n throw new Error('SLO name must not be empty');\n }\n if (\n !Number.isFinite(definition.target) ||\n definition.target <= 0 ||\n definition.target >= 1\n ) {\n throw new Error('SLO target must be greater than 0 and less than 1');\n }\n if (!Number.isFinite(definition.windowMs) || definition.windowMs <= 0) {\n throw new Error('SLO windowMs must be greater than 0');\n }\n}\n\nfunction validateThreshold(name: string, threshold: number): void {\n if (!Number.isFinite(threshold) || threshold <= 0) {\n throw new Error(`${name} must be greater than 0`);\n }\n}\n\nfunction validateForecastOptions(options: SloForecastOptions): void {\n if (!Number.isFinite(options.baselineMs) || options.baselineMs <= 0) {\n throw new Error('baselineMs must be greater than 0');\n }\n if (!Number.isFinite(options.lookaheadMs) || options.lookaheadMs <= 0) {\n throw new Error('lookaheadMs must be greater than 0');\n }\n if (options.lookaheadMs > options.baselineMs * 4) {\n throw new Error('lookaheadMs must not exceed 4 times baselineMs');\n }\n if (\n options.expectedEventsInLookahead !== undefined &&\n (!Number.isFinite(options.expectedEventsInLookahead) ||\n options.expectedEventsInLookahead < 0)\n ) {\n throw new Error('expectedEventsInLookahead must not be negative');\n }\n}\n\n/**\n * Track good and bad events for one rolling-window service level objective.\n */\nexport function createSloTracker(\n definition: SloDefinition,\n options: SloTrackerOptions = {},\n): SloTracker {\n validateDefinition(definition);\n\n const stableDefinition = Object.freeze({ ...definition });\n const now = options.now ?? Date.now;\n const observations: Observation[] = [];\n let firstLiveIndex = 0;\n\n let outcomeCounter: Counter | undefined;\n let burnRateHistogram: Histogram | undefined;\n\n if (options.recordMetrics !== false) {\n const meter = getConfig().meter;\n outcomeCounter = meter.createCounter('autotel.slo.outcomes', {\n description:\n 'Good and bad events recorded against a service level objective',\n unit: '1',\n });\n burnRateHistogram = meter.createHistogram('autotel.slo.burn_rate', {\n description: 'Error-budget burn rate for a service level objective',\n unit: '1',\n });\n }\n\n const prune = (at: number): void => {\n const cutoff = at - stableDefinition.windowMs;\n while (\n firstLiveIndex < observations.length &&\n observations[firstLiveIndex]!.timestamp < cutoff\n ) {\n firstLiveIndex += 1;\n }\n\n if (firstLiveIndex > 1024 && firstLiveIndex * 2 >= observations.length) {\n observations.splice(0, firstLiveIndex);\n firstLiveIndex = 0;\n }\n };\n\n const calculate = (at: number): SloSnapshot => {\n prune(at);\n\n let good = 0;\n for (let index = firstLiveIndex; index < observations.length; index += 1) {\n if (observations[index]!.outcome === 'good') good += 1;\n }\n\n const total = observations.length - firstLiveIndex;\n const bad = total - good;\n const sli = total === 0 ? undefined : good / total;\n const errorBudgetFraction = 1 - stableDefinition.target;\n const observedBadFraction = total === 0 ? 0 : bad / total;\n const burnRate = observedBadFraction / errorBudgetFraction;\n const budgetConsumed = burnRate;\n\n return {\n ...stableDefinition,\n observedAt: at,\n total,\n good,\n bad,\n sli,\n errorBudgetFraction,\n budgetConsumed,\n budgetRemaining: 1 - budgetConsumed,\n burnRate,\n meetsTarget: sli === undefined || sli >= stableDefinition.target,\n };\n };\n\n return {\n definition: stableDefinition,\n\n record(outcome: SloOutcome, attributes: Attributes = {}): SloSnapshot {\n if (outcome !== 'good' && outcome !== 'bad') {\n throw new Error('SLO outcome must be \"good\" or \"bad\"');\n }\n\n const timestamp = now();\n observations.push({ outcome, timestamp });\n const current = calculate(timestamp);\n const metricAttributes: Attributes = {\n ...attributes,\n 'slo.name': stableDefinition.name,\n 'slo.outcome': outcome,\n };\n\n outcomeCounter?.add(1, metricAttributes);\n burnRateHistogram?.record(current.burnRate, {\n 'slo.name': stableDefinition.name,\n });\n\n const activeSpan = otelTrace.getActiveSpan();\n activeSpan?.setAttributes({\n 'slo.name': stableDefinition.name,\n 'slo.target': stableDefinition.target,\n 'slo.outcome': outcome,\n 'slo.burn_rate': current.burnRate,\n });\n\n return current;\n },\n\n snapshot(at = now()): SloSnapshot {\n return calculate(at);\n },\n\n forecast(forecastOptions: SloForecastOptions): SloForecast {\n validateForecastOptions(forecastOptions);\n if (forecastOptions.baselineMs > stableDefinition.windowMs) {\n throw new Error('baselineMs must not exceed the SLO windowMs');\n }\n const observedAt = now();\n prune(observedAt);\n\n const baselineCutoff = observedAt - forecastOptions.baselineMs;\n const retainedCutoff =\n observedAt + forecastOptions.lookaheadMs - stableDefinition.windowMs;\n let baselineTotal = 0;\n let baselineBad = 0;\n let retainedTotal = 0;\n let retainedBad = 0;\n\n for (\n let index = firstLiveIndex;\n index < observations.length;\n index += 1\n ) {\n const observation = observations[index]!;\n if (observation.timestamp >= baselineCutoff) {\n baselineTotal += 1;\n if (observation.outcome === 'bad') baselineBad += 1;\n }\n if (observation.timestamp >= retainedCutoff) {\n retainedTotal += 1;\n if (observation.outcome === 'bad') retainedBad += 1;\n }\n }\n\n if (baselineTotal === 0) {\n return {\n name: stableDefinition.name,\n target: stableDefinition.target,\n observedAt,\n baselineMs: forecastOptions.baselineMs,\n lookaheadMs: forecastOptions.lookaheadMs,\n baselineTotal: 0,\n baselineBad: 0,\n baselineFailureRate: 0,\n retainedTotal,\n retainedBad,\n projectedTotal: retainedTotal,\n projectedBad: retainedBad,\n projectedSli:\n retainedTotal === 0\n ? undefined\n : (retainedTotal - retainedBad) / retainedTotal,\n alerting: false,\n reason: 'no-baseline-traffic',\n };\n }\n\n const baselineFailureRate = baselineBad / baselineTotal;\n const expectedEventsInLookahead =\n forecastOptions.expectedEventsInLookahead ??\n (baselineTotal * forecastOptions.lookaheadMs) /\n forecastOptions.baselineMs;\n const projectedFailures = baselineFailureRate * expectedEventsInLookahead;\n const projectedTotal = retainedTotal + expectedEventsInLookahead;\n const projectedBad = retainedBad + projectedFailures;\n const projectedSli =\n projectedTotal === 0\n ? undefined\n : (projectedTotal - projectedBad) / projectedTotal;\n const allowedFailureRate = 1 - stableDefinition.target;\n const remainingFailures =\n allowedFailureRate * retainedTotal - retainedBad;\n const netFailureRate = baselineFailureRate - allowedFailureRate;\n const eventRate = expectedEventsInLookahead / forecastOptions.lookaheadMs;\n const timeToExhaustionMs =\n remainingFailures <= 0\n ? 0\n : netFailureRate > 0 && eventRate > 0\n ? remainingFailures / (netFailureRate * eventRate)\n : undefined;\n const alerting =\n projectedSli !== undefined && projectedSli < stableDefinition.target;\n\n return {\n name: stableDefinition.name,\n target: stableDefinition.target,\n observedAt,\n baselineMs: forecastOptions.baselineMs,\n lookaheadMs: forecastOptions.lookaheadMs,\n baselineTotal,\n baselineBad,\n baselineFailureRate,\n retainedTotal,\n retainedBad,\n projectedTotal,\n projectedBad,\n projectedSli,\n timeToExhaustionMs,\n alerting,\n reason: alerting ? 'projected-budget-exhaustion' : 'within-budget',\n };\n },\n\n reset(): void {\n observations.length = 0;\n firstLiveIndex = 0;\n },\n };\n}\n\n/**\n * Evaluate a dual-window burn-rate alert.\n *\n * Both windows must exceed their threshold. The short window catches a sharp\n * regression while the long window rejects brief spikes.\n */\nexport function evaluateBurnRateAlert(\n options: BurnRateAlertOptions,\n): BurnRateAlertDecision {\n validateThreshold('shortThreshold', options.shortThreshold);\n validateThreshold('longThreshold', options.longThreshold);\n\n if (\n options.shortWindow.name !== options.longWindow.name ||\n options.shortWindow.target !== options.longWindow.target\n ) {\n throw new Error(\n 'Burn-rate windows must describe the same SLO name and target',\n );\n }\n\n if (options.shortWindow.total === 0 || options.longWindow.total === 0) {\n return {\n alerting: false,\n reason: 'no-traffic',\n shortBurnRate: options.shortWindow.burnRate,\n longBurnRate: options.longWindow.burnRate,\n };\n }\n\n if (options.shortWindow.burnRate < options.shortThreshold) {\n return {\n alerting: false,\n reason: 'short-window-below-threshold',\n shortBurnRate: options.shortWindow.burnRate,\n longBurnRate: options.longWindow.burnRate,\n };\n }\n\n if (options.longWindow.burnRate < options.longThreshold) {\n return {\n alerting: false,\n reason: 'long-window-below-threshold',\n shortBurnRate: options.shortWindow.burnRate,\n longBurnRate: options.longWindow.burnRate,\n };\n }\n\n return {\n alerting: true,\n reason: 'burn-rate-thresholds-exceeded',\n shortBurnRate: options.shortWindow.burnRate,\n longBurnRate: options.longWindow.burnRate,\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"slo.d.cts","names":[],"sources":["../src/slo.ts"],"mappings":";;KAiBY;UAEK;;EAEf;;EAEA;;EAEA;;UAGe;;EAEf;;EAEA;;UAGe,oBAAoB;EACnC;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;UAGe;WACN,YAAY,SAAS;EAC9B,OAAO,SAAS,YAAY,aAAa,aAAa;EACtD,SAAS,cAAc;EACvB,SAAS,SAAS,qBAAqB;EACvC;;UAGe;;EAEf;;EAEA;;;;;EAKA;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;;UAIe;EACf,aAAa;EACb,YAAY;EACZ;EACA;;UAGe;EACf;EACA;EAKA;EACA;;;;;iBAoDc,iBACd,YAAY,eACZ,UAAS,oBACR;;;;;;;iBA6Na,sBACd,SAAS,uBACR"}
package/dist/slo.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"slo.d.ts","names":[],"sources":["../src/slo.ts"],"mappings":";;KAiBY;UAEK;;EAEf;;EAEA;;EAEA;;UAGe;;EAEf;;EAEA;;UAGe,oBAAoB;EACnC;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;UAGe;WACN,YAAY,SAAS;EAC9B,OAAO,SAAS,YAAY,aAAa,aAAa;EACtD,SAAS,cAAc;EACvB,SAAS,SAAS,qBAAqB;EACvC;;UAGe;;EAEf;;EAEA;;;;;EAKA;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;;UAIe;EACf,aAAa;EACb,YAAY;EACZ;EACA;;UAGe;EACf;EACA;EAKA;EACA;;;;;iBAoDc,iBACd,YAAY,eACZ,UAAS,oBACR;;;;;;;iBA6Na,sBACd,SAAS,uBACR"}
package/dist/slo.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"slo.js","names":["otelTrace"],"sources":["../src/slo.ts"],"sourcesContent":["/**\n * Service level objective tracking and burn-rate evaluation.\n *\n * The tracker keeps a bounded rolling window in memory, records low-cardinality\n * OpenTelemetry metrics, and returns snapshots that callers can feed into alert\n * evaluation. Production systems can persist the emitted metrics in their\n * telemetry backend while tests use the same calculations in process.\n */\n\nimport {\n trace as otelTrace,\n type Attributes,\n type Counter,\n type Histogram,\n} from '@opentelemetry/api';\nimport { getConfig } from './config';\n\nexport type SloOutcome = 'good' | 'bad';\n\nexport interface SloDefinition {\n /** Stable identifier used in metric attributes and alerts. */\n name: string;\n /** Required success ratio, expressed as a value between 0 and 1. */\n target: number;\n /** Rolling observation window in milliseconds. */\n windowMs: number;\n}\n\nexport interface SloTrackerOptions {\n /** Clock override for deterministic tests. */\n now?: () => number;\n /** Disable metric recording when calculations run outside an initialized SDK. */\n recordMetrics?: boolean;\n}\n\nexport interface SloSnapshot extends SloDefinition {\n observedAt: number;\n total: number;\n good: number;\n bad: number;\n /** Observed good-event ratio. Undefined until the tracker sees an event. */\n sli?: number;\n /** Fraction of events the objective permits to fail. */\n errorBudgetFraction: number;\n /** Fraction of the available error budget consumed in this window. */\n budgetConsumed: number;\n /** Remaining budget fraction. Negative values show overspend. */\n budgetRemaining: number;\n /** Observed failure ratio divided by the permitted failure ratio. */\n burnRate: number;\n meetsTarget: boolean;\n}\n\nexport interface SloTracker {\n readonly definition: Readonly<SloDefinition>;\n record(outcome: SloOutcome, attributes?: Attributes): SloSnapshot;\n snapshot(at?: number): SloSnapshot;\n forecast(options: SloForecastOptions): SloForecast;\n reset(): void;\n}\n\nexport interface SloForecastOptions {\n /** Recent observation period used to estimate the future failure rate. */\n baselineMs: number;\n /** Future period covered by the forecast. Must not exceed 4x the baseline. */\n lookaheadMs: number;\n /**\n * Expected eligible events during the lookahead. Defaults to the event rate\n * observed during the baseline.\n */\n expectedEventsInLookahead?: number;\n}\n\nexport interface SloForecast {\n name: string;\n target: number;\n observedAt: number;\n baselineMs: number;\n lookaheadMs: number;\n baselineTotal: number;\n baselineBad: number;\n baselineFailureRate: number;\n retainedTotal: number;\n retainedBad: number;\n projectedTotal: number;\n projectedBad: number;\n projectedSli?: number;\n /** Milliseconds until the retained and projected error budget is exhausted. */\n timeToExhaustionMs?: number;\n alerting: boolean;\n reason:\n 'no-baseline-traffic' | 'within-budget' | 'projected-budget-exhaustion';\n}\n\nexport interface BurnRateAlertOptions {\n shortWindow: SloSnapshot;\n longWindow: SloSnapshot;\n shortThreshold: number;\n longThreshold: number;\n}\n\nexport interface BurnRateAlertDecision {\n alerting: boolean;\n reason:\n | 'burn-rate-thresholds-exceeded'\n | 'short-window-below-threshold'\n | 'long-window-below-threshold'\n | 'no-traffic';\n shortBurnRate: number;\n longBurnRate: number;\n}\n\ninterface Observation {\n outcome: SloOutcome;\n timestamp: number;\n}\n\nfunction validateDefinition(definition: SloDefinition): void {\n if (!definition.name.trim()) {\n throw new Error('SLO name must not be empty');\n }\n if (\n !Number.isFinite(definition.target) ||\n definition.target <= 0 ||\n definition.target >= 1\n ) {\n throw new Error('SLO target must be greater than 0 and less than 1');\n }\n if (!Number.isFinite(definition.windowMs) || definition.windowMs <= 0) {\n throw new Error('SLO windowMs must be greater than 0');\n }\n}\n\nfunction validateThreshold(name: string, threshold: number): void {\n if (!Number.isFinite(threshold) || threshold <= 0) {\n throw new Error(`${name} must be greater than 0`);\n }\n}\n\nfunction validateForecastOptions(options: SloForecastOptions): void {\n if (!Number.isFinite(options.baselineMs) || options.baselineMs <= 0) {\n throw new Error('baselineMs must be greater than 0');\n }\n if (!Number.isFinite(options.lookaheadMs) || options.lookaheadMs <= 0) {\n throw new Error('lookaheadMs must be greater than 0');\n }\n if (options.lookaheadMs > options.baselineMs * 4) {\n throw new Error('lookaheadMs must not exceed 4 times baselineMs');\n }\n if (\n options.expectedEventsInLookahead !== undefined &&\n (!Number.isFinite(options.expectedEventsInLookahead) ||\n options.expectedEventsInLookahead < 0)\n ) {\n throw new Error('expectedEventsInLookahead must not be negative');\n }\n}\n\n/**\n * Track good and bad events for one rolling-window service level objective.\n */\nexport function createSloTracker(\n definition: SloDefinition,\n options: SloTrackerOptions = {},\n): SloTracker {\n validateDefinition(definition);\n\n const stableDefinition = Object.freeze({ ...definition });\n const now = options.now ?? Date.now;\n const observations: Observation[] = [];\n let firstLiveIndex = 0;\n\n let outcomeCounter: Counter | undefined;\n let burnRateHistogram: Histogram | undefined;\n\n if (options.recordMetrics !== false) {\n const meter = getConfig().meter;\n outcomeCounter = meter.createCounter('autotel.slo.outcomes', {\n description:\n 'Good and bad events recorded against a service level objective',\n unit: '1',\n });\n burnRateHistogram = meter.createHistogram('autotel.slo.burn_rate', {\n description: 'Error-budget burn rate for a service level objective',\n unit: '1',\n });\n }\n\n const prune = (at: number): void => {\n const cutoff = at - stableDefinition.windowMs;\n while (\n firstLiveIndex < observations.length &&\n observations[firstLiveIndex]!.timestamp < cutoff\n ) {\n firstLiveIndex += 1;\n }\n\n if (firstLiveIndex > 1024 && firstLiveIndex * 2 >= observations.length) {\n observations.splice(0, firstLiveIndex);\n firstLiveIndex = 0;\n }\n };\n\n const calculate = (at: number): SloSnapshot => {\n prune(at);\n\n let good = 0;\n for (let index = firstLiveIndex; index < observations.length; index += 1) {\n if (observations[index]!.outcome === 'good') good += 1;\n }\n\n const total = observations.length - firstLiveIndex;\n const bad = total - good;\n const sli = total === 0 ? undefined : good / total;\n const errorBudgetFraction = 1 - stableDefinition.target;\n const observedBadFraction = total === 0 ? 0 : bad / total;\n const burnRate = observedBadFraction / errorBudgetFraction;\n const budgetConsumed = burnRate;\n\n return {\n ...stableDefinition,\n observedAt: at,\n total,\n good,\n bad,\n sli,\n errorBudgetFraction,\n budgetConsumed,\n budgetRemaining: 1 - budgetConsumed,\n burnRate,\n meetsTarget: sli === undefined || sli >= stableDefinition.target,\n };\n };\n\n return {\n definition: stableDefinition,\n\n record(outcome: SloOutcome, attributes: Attributes = {}): SloSnapshot {\n if (outcome !== 'good' && outcome !== 'bad') {\n throw new Error('SLO outcome must be \"good\" or \"bad\"');\n }\n\n const timestamp = now();\n observations.push({ outcome, timestamp });\n const current = calculate(timestamp);\n const metricAttributes: Attributes = {\n ...attributes,\n 'slo.name': stableDefinition.name,\n 'slo.outcome': outcome,\n };\n\n outcomeCounter?.add(1, metricAttributes);\n burnRateHistogram?.record(current.burnRate, {\n 'slo.name': stableDefinition.name,\n });\n\n const activeSpan = otelTrace.getActiveSpan();\n activeSpan?.setAttributes({\n 'slo.name': stableDefinition.name,\n 'slo.target': stableDefinition.target,\n 'slo.outcome': outcome,\n 'slo.burn_rate': current.burnRate,\n });\n\n return current;\n },\n\n snapshot(at = now()): SloSnapshot {\n return calculate(at);\n },\n\n forecast(forecastOptions: SloForecastOptions): SloForecast {\n validateForecastOptions(forecastOptions);\n if (forecastOptions.baselineMs > stableDefinition.windowMs) {\n throw new Error('baselineMs must not exceed the SLO windowMs');\n }\n const observedAt = now();\n prune(observedAt);\n\n const baselineCutoff = observedAt - forecastOptions.baselineMs;\n const retainedCutoff =\n observedAt + forecastOptions.lookaheadMs - stableDefinition.windowMs;\n let baselineTotal = 0;\n let baselineBad = 0;\n let retainedTotal = 0;\n let retainedBad = 0;\n\n for (\n let index = firstLiveIndex;\n index < observations.length;\n index += 1\n ) {\n const observation = observations[index]!;\n if (observation.timestamp >= baselineCutoff) {\n baselineTotal += 1;\n if (observation.outcome === 'bad') baselineBad += 1;\n }\n if (observation.timestamp >= retainedCutoff) {\n retainedTotal += 1;\n if (observation.outcome === 'bad') retainedBad += 1;\n }\n }\n\n if (baselineTotal === 0) {\n return {\n name: stableDefinition.name,\n target: stableDefinition.target,\n observedAt,\n baselineMs: forecastOptions.baselineMs,\n lookaheadMs: forecastOptions.lookaheadMs,\n baselineTotal: 0,\n baselineBad: 0,\n baselineFailureRate: 0,\n retainedTotal,\n retainedBad,\n projectedTotal: retainedTotal,\n projectedBad: retainedBad,\n projectedSli:\n retainedTotal === 0\n ? undefined\n : (retainedTotal - retainedBad) / retainedTotal,\n alerting: false,\n reason: 'no-baseline-traffic',\n };\n }\n\n const baselineFailureRate = baselineBad / baselineTotal;\n const expectedEventsInLookahead =\n forecastOptions.expectedEventsInLookahead ??\n (baselineTotal * forecastOptions.lookaheadMs) /\n forecastOptions.baselineMs;\n const projectedFailures = baselineFailureRate * expectedEventsInLookahead;\n const projectedTotal = retainedTotal + expectedEventsInLookahead;\n const projectedBad = retainedBad + projectedFailures;\n const projectedSli =\n projectedTotal === 0\n ? undefined\n : (projectedTotal - projectedBad) / projectedTotal;\n const allowedFailureRate = 1 - stableDefinition.target;\n const remainingFailures =\n allowedFailureRate * retainedTotal - retainedBad;\n const netFailureRate = baselineFailureRate - allowedFailureRate;\n const eventRate = expectedEventsInLookahead / forecastOptions.lookaheadMs;\n const timeToExhaustionMs =\n remainingFailures <= 0\n ? 0\n : netFailureRate > 0 && eventRate > 0\n ? remainingFailures / (netFailureRate * eventRate)\n : undefined;\n const alerting =\n projectedSli !== undefined && projectedSli < stableDefinition.target;\n\n return {\n name: stableDefinition.name,\n target: stableDefinition.target,\n observedAt,\n baselineMs: forecastOptions.baselineMs,\n lookaheadMs: forecastOptions.lookaheadMs,\n baselineTotal,\n baselineBad,\n baselineFailureRate,\n retainedTotal,\n retainedBad,\n projectedTotal,\n projectedBad,\n projectedSli,\n timeToExhaustionMs,\n alerting,\n reason: alerting ? 'projected-budget-exhaustion' : 'within-budget',\n };\n },\n\n reset(): void {\n observations.length = 0;\n firstLiveIndex = 0;\n },\n };\n}\n\n/**\n * Evaluate a dual-window burn-rate alert.\n *\n * Both windows must exceed their threshold. The short window catches a sharp\n * regression while the long window rejects brief spikes.\n */\nexport function evaluateBurnRateAlert(\n options: BurnRateAlertOptions,\n): BurnRateAlertDecision {\n validateThreshold('shortThreshold', options.shortThreshold);\n validateThreshold('longThreshold', options.longThreshold);\n\n if (\n options.shortWindow.name !== options.longWindow.name ||\n options.shortWindow.target !== options.longWindow.target\n ) {\n throw new Error(\n 'Burn-rate windows must describe the same SLO name and target',\n );\n }\n\n if (options.shortWindow.total === 0 || options.longWindow.total === 0) {\n return {\n alerting: false,\n reason: 'no-traffic',\n shortBurnRate: options.shortWindow.burnRate,\n longBurnRate: options.longWindow.burnRate,\n };\n }\n\n if (options.shortWindow.burnRate < options.shortThreshold) {\n return {\n alerting: false,\n reason: 'short-window-below-threshold',\n shortBurnRate: options.shortWindow.burnRate,\n longBurnRate: options.longWindow.burnRate,\n };\n }\n\n if (options.longWindow.burnRate < options.longThreshold) {\n return {\n alerting: false,\n reason: 'long-window-below-threshold',\n shortBurnRate: options.shortWindow.burnRate,\n longBurnRate: options.longWindow.burnRate,\n };\n }\n\n return {\n alerting: true,\n reason: 'burn-rate-thresholds-exceeded',\n shortBurnRate: options.shortWindow.burnRate,\n longBurnRate: options.longWindow.burnRate,\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"span-name-normalizer-CN1-8C76.d.cts","names":[],"sources":["../src/span-name-normalizer.ts"],"mappings":";;;;;;;;KAsCY,wBAAwB;;;;KAKxB;;;;KAKA,2BACV,uBAAuB;UAER;;;;EAIf,YAAY;;;;;cAMR;;;;;;;;;;;;;cA6BA,oBAAoB,OACxB,0BACA;;;;;;;;;;;;cAsEW,wCAAwC;mBAClC;mBACA;EAGf,YAAA,kBAAkB,eAClB,SAAS;;;;EASX,QAAQ,MAAM,MAAM,eAAe;;;;EAkBnC,MAAM,MAAM;EAIZ,cAAc;EAId,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"span-name-normalizer-CN1-8C76.d.ts","names":[],"sources":["../src/span-name-normalizer.ts"],"mappings":";;;;;;;;KAsCY,wBAAwB;;;;KAKxB;;;;KAKA,2BACV,uBAAuB;UAER;;;;EAIf,YAAY;;;;;cAMR;;;;;;;;;;;;;cA6BA,oBAAoB,OACxB,0BACA;;;;;;;;;;;;cAsEW,wCAAwC;mBAClC;mBACA;EAGf,YAAA,kBAAkB,eAClB,SAAS;;;;EASX,QAAQ,MAAM,MAAM,eAAe;;;;EAkBnC,MAAM,MAAM;EAIZ,cAAc;EAId,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"span-name-normalizer.cjs","names":[],"sources":["../src/span-name-normalizer.ts"],"sourcesContent":["/**\n * Span Name Normalizer\n *\n * Normalizes span names to reduce cardinality from dynamic path segments.\n * This is critical for observability backends that charge by unique span names\n * or have cardinality limits.\n *\n * @example Basic usage with custom function\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: (name) => {\n * return name.replace(/\\/[0-9]+/g, '/:id');\n * }\n * })\n * ```\n *\n * @example Using built-in preset\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: 'rest-api'\n * })\n * ```\n */\n\nimport type {\n SpanProcessor,\n ReadableSpan,\n} from '@opentelemetry/sdk-trace-base';\nimport type { Context } from '@opentelemetry/api';\nimport type { Span } from '@opentelemetry/sdk-trace-base';\n\n/**\n * Function to normalize a span name\n * @param name - The original span name\n * @returns The normalized span name\n */\nexport type SpanNameNormalizerFn = (name: string) => string;\n\n/**\n * Built-in normalizer preset names\n */\nexport type SpanNameNormalizerPreset = 'rest-api' | 'graphql' | 'minimal';\n\n/**\n * Normalizer config - either a function or a preset name\n */\nexport type SpanNameNormalizerConfig =\n SpanNameNormalizerFn | SpanNameNormalizerPreset;\n\nexport interface SpanNameNormalizingProcessorOptions {\n /**\n * Normalizer function or preset name\n */\n normalizer: SpanNameNormalizerConfig;\n}\n\n/**\n * Built-in normalizer patterns\n */\nconst NORMALIZER_PATTERNS = {\n // Numeric IDs: /users/123 → /users/:id\n numericId: /\\/\\d+(?=\\/|$)/g,\n\n // UUIDs: /users/550e8400-e29b-41d4-a716-446655440000 → /users/:uuid\n uuid: /\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?=\\/|$)/gi,\n\n // Short UUIDs (without dashes): /users/550e8400e29b41d4a716446655440000 → /users/:uuid\n shortUuid: /\\/[0-9a-f]{32}(?=\\/|$)/gi,\n\n // MongoDB ObjectIds: /docs/507f1f77bcf86cd799439011 → /docs/:objectId\n objectId: /\\/[0-9a-f]{24}(?=\\/|$)/gi,\n\n // Hashes (6+ hex chars): /assets/abc123def.js → /assets/:hash.js\n hash: /\\/[0-9a-f]{6,}(?=\\.[a-z]+$)/gi,\n\n // ISO dates: /logs/2024-01-15 → /logs/:date\n isoDate: /\\/\\d{4}-\\d{2}-\\d{2}(?=\\/|$)/g,\n\n // Timestamps: /events/1705334400 → /events/:timestamp\n timestamp: /\\/1[0-9]{9}(?=\\/|$)/g,\n\n // Email-like segments: /users/john@example.com → /users/:email\n email: /\\/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}(?=\\/|$)/g,\n} as const;\n\n/**\n * Built-in normalizer presets\n */\nconst NORMALIZER_PRESETS: Record<\n SpanNameNormalizerPreset,\n SpanNameNormalizerFn\n> = {\n /**\n * REST API preset - normalizes common REST path patterns\n * Handles: numeric IDs, UUIDs, ObjectIds, dates, timestamps, emails\n */\n 'rest-api': (name: string): string => {\n return name\n .replaceAll(NORMALIZER_PATTERNS.uuid, '/:uuid')\n .replaceAll(NORMALIZER_PATTERNS.shortUuid, '/:uuid')\n .replaceAll(NORMALIZER_PATTERNS.objectId, '/:objectId')\n .replaceAll(NORMALIZER_PATTERNS.isoDate, '/:date')\n .replaceAll(NORMALIZER_PATTERNS.timestamp, '/:timestamp')\n .replaceAll(NORMALIZER_PATTERNS.email, '/:email')\n .replaceAll(NORMALIZER_PATTERNS.numericId, '/:id');\n },\n\n /**\n * GraphQL preset - normalizes GraphQL operation names and paths\n * Keeps query/mutation names but normalizes embedded IDs\n */\n graphql: (name: string): string => {\n // For GraphQL, normalize both path-style and embedded IDs\n return name\n .replaceAll(NORMALIZER_PATTERNS.uuid, '/:uuid')\n .replaceAll(NORMALIZER_PATTERNS.numericId, '/:id');\n },\n\n /**\n * Minimal preset - only normalizes numeric IDs and UUIDs\n */\n minimal: (name: string): string => {\n return name\n .replaceAll(NORMALIZER_PATTERNS.uuid, '/:uuid')\n .replaceAll(NORMALIZER_PATTERNS.numericId, '/:id');\n },\n};\n\n/**\n * Resolve normalizer config to a function\n */\nfunction resolveNormalizer(\n config: SpanNameNormalizerConfig,\n): SpanNameNormalizerFn {\n if (typeof config === 'function') {\n return config;\n }\n\n const preset = NORMALIZER_PRESETS[config];\n if (!preset) {\n throw new Error(\n `Unknown span name normalizer preset: \"${config}\". ` +\n `Available presets: ${Object.keys(NORMALIZER_PRESETS).join(', ')}`,\n );\n }\n\n return preset;\n}\n\n/**\n * Span processor that normalizes span names to reduce cardinality.\n *\n * Normalization happens in onStart() when we have access to the mutable Span.\n * This allows us to call span.updateName() before the span is finalized.\n *\n * Common use cases:\n * - REST APIs: /users/123/posts/456 → /users/:id/posts/:id\n * - UUIDs: /items/550e8400-e29b-41d4-a716-446655440000 → /items/:uuid\n * - Dates: /logs/2024-01-15 → /logs/:date\n */\nexport class SpanNameNormalizingProcessor implements SpanProcessor {\n private readonly wrappedProcessor: SpanProcessor;\n private readonly normalizer: SpanNameNormalizerFn;\n\n constructor(\n wrappedProcessor: SpanProcessor,\n options: SpanNameNormalizingProcessorOptions,\n ) {\n this.wrappedProcessor = wrappedProcessor;\n this.normalizer = resolveNormalizer(options.normalizer);\n }\n\n /**\n * Normalize span name on start (when Span is mutable)\n */\n onStart(span: Span, parentContext: Context): void {\n try {\n const originalName = span.name;\n const normalizedName = this.normalizer(originalName);\n\n if (normalizedName !== originalName) {\n span.updateName(normalizedName);\n }\n } catch {\n // If normalizer throws, keep original name (fail-open)\n }\n\n this.wrappedProcessor.onStart(span, parentContext);\n }\n\n /**\n * Pass through onEnd unchanged\n */\n onEnd(span: ReadableSpan): void {\n this.wrappedProcessor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.wrappedProcessor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.wrappedProcessor.shutdown();\n }\n}\n\n/**\n * Export built-in patterns for advanced users who want to compose their own normalizers\n */\nexport { NORMALIZER_PATTERNS, NORMALIZER_PRESETS };\n"],"mappings":";;;;;;AA6DA,MAAM,sBAAsB;CAE1B,WAAW;CAGX,MAAM;CAGN,WAAW;CAGX,UAAU;CAGV,MAAM;CAGN,SAAS;CAGT,WAAW;CAGX,OAAO;AACT;;;;AAKA,MAAM,qBAGF;;;;;CAKF,aAAa,SAAyB;EACpC,OAAO,KACJ,WAAW,oBAAoB,MAAM,QAAQ,CAAC,CAC9C,WAAW,oBAAoB,WAAW,QAAQ,CAAC,CACnD,WAAW,oBAAoB,UAAU,YAAY,CAAC,CACtD,WAAW,oBAAoB,SAAS,QAAQ,CAAC,CACjD,WAAW,oBAAoB,WAAW,aAAa,CAAC,CACxD,WAAW,oBAAoB,OAAO,SAAS,CAAC,CAChD,WAAW,oBAAoB,WAAW,MAAM;CACrD;;;;;CAMA,UAAU,SAAyB;EAEjC,OAAO,KACJ,WAAW,oBAAoB,MAAM,QAAQ,CAAC,CAC9C,WAAW,oBAAoB,WAAW,MAAM;CACrD;;;;CAKA,UAAU,SAAyB;EACjC,OAAO,KACJ,WAAW,oBAAoB,MAAM,QAAQ,CAAC,CAC9C,WAAW,oBAAoB,WAAW,MAAM;CACrD;AACF;;;;AAKA,SAAS,kBACP,QACsB;CACtB,IAAI,OAAO,WAAW,YACpB,OAAO;CAGT,MAAM,SAAS,mBAAmB;CAClC,IAAI,CAAC,QACH,MAAM,IAAI,MACR,yCAAyC,OAAO,wBACxB,OAAO,KAAK,kBAAkB,CAAC,CAAC,KAAK,IAAI,GACnE;CAGF,OAAO;AACT;;;;;;;;;;;;AAaA,IAAa,+BAAb,MAAmE;CACjE,AAAiB;CACjB,AAAiB;CAEjB,YACE,kBACA,SACA;EACA,KAAK,mBAAmB;EACxB,KAAK,aAAa,kBAAkB,QAAQ,UAAU;CACxD;;;;CAKA,QAAQ,MAAY,eAA8B;EAChD,IAAI;GACF,MAAM,eAAe,KAAK;GAC1B,MAAM,iBAAiB,KAAK,WAAW,YAAY;GAEnD,IAAI,mBAAmB,cACrB,KAAK,WAAW,cAAc;EAElC,QAAQ,CAER;EAEA,KAAK,iBAAiB,QAAQ,MAAM,aAAa;CACnD;;;;CAKA,MAAM,MAA0B;EAC9B,KAAK,iBAAiB,MAAM,IAAI;CAClC;CAEA,aAA4B;EAC1B,OAAO,KAAK,iBAAiB,WAAW;CAC1C;CAEA,WAA0B;EACxB,OAAO,KAAK,iBAAiB,SAAS;CACxC;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"span-name-normalizer.js","names":[],"sources":["../src/span-name-normalizer.ts"],"sourcesContent":["/**\n * Span Name Normalizer\n *\n * Normalizes span names to reduce cardinality from dynamic path segments.\n * This is critical for observability backends that charge by unique span names\n * or have cardinality limits.\n *\n * @example Basic usage with custom function\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: (name) => {\n * return name.replace(/\\/[0-9]+/g, '/:id');\n * }\n * })\n * ```\n *\n * @example Using built-in preset\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: 'rest-api'\n * })\n * ```\n */\n\nimport type {\n SpanProcessor,\n ReadableSpan,\n} from '@opentelemetry/sdk-trace-base';\nimport type { Context } from '@opentelemetry/api';\nimport type { Span } from '@opentelemetry/sdk-trace-base';\n\n/**\n * Function to normalize a span name\n * @param name - The original span name\n * @returns The normalized span name\n */\nexport type SpanNameNormalizerFn = (name: string) => string;\n\n/**\n * Built-in normalizer preset names\n */\nexport type SpanNameNormalizerPreset = 'rest-api' | 'graphql' | 'minimal';\n\n/**\n * Normalizer config - either a function or a preset name\n */\nexport type SpanNameNormalizerConfig =\n SpanNameNormalizerFn | SpanNameNormalizerPreset;\n\nexport interface SpanNameNormalizingProcessorOptions {\n /**\n * Normalizer function or preset name\n */\n normalizer: SpanNameNormalizerConfig;\n}\n\n/**\n * Built-in normalizer patterns\n */\nconst NORMALIZER_PATTERNS = {\n // Numeric IDs: /users/123 → /users/:id\n numericId: /\\/\\d+(?=\\/|$)/g,\n\n // UUIDs: /users/550e8400-e29b-41d4-a716-446655440000 → /users/:uuid\n uuid: /\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?=\\/|$)/gi,\n\n // Short UUIDs (without dashes): /users/550e8400e29b41d4a716446655440000 → /users/:uuid\n shortUuid: /\\/[0-9a-f]{32}(?=\\/|$)/gi,\n\n // MongoDB ObjectIds: /docs/507f1f77bcf86cd799439011 → /docs/:objectId\n objectId: /\\/[0-9a-f]{24}(?=\\/|$)/gi,\n\n // Hashes (6+ hex chars): /assets/abc123def.js → /assets/:hash.js\n hash: /\\/[0-9a-f]{6,}(?=\\.[a-z]+$)/gi,\n\n // ISO dates: /logs/2024-01-15 → /logs/:date\n isoDate: /\\/\\d{4}-\\d{2}-\\d{2}(?=\\/|$)/g,\n\n // Timestamps: /events/1705334400 → /events/:timestamp\n timestamp: /\\/1[0-9]{9}(?=\\/|$)/g,\n\n // Email-like segments: /users/john@example.com → /users/:email\n email: /\\/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}(?=\\/|$)/g,\n} as const;\n\n/**\n * Built-in normalizer presets\n */\nconst NORMALIZER_PRESETS: Record<\n SpanNameNormalizerPreset,\n SpanNameNormalizerFn\n> = {\n /**\n * REST API preset - normalizes common REST path patterns\n * Handles: numeric IDs, UUIDs, ObjectIds, dates, timestamps, emails\n */\n 'rest-api': (name: string): string => {\n return name\n .replaceAll(NORMALIZER_PATTERNS.uuid, '/:uuid')\n .replaceAll(NORMALIZER_PATTERNS.shortUuid, '/:uuid')\n .replaceAll(NORMALIZER_PATTERNS.objectId, '/:objectId')\n .replaceAll(NORMALIZER_PATTERNS.isoDate, '/:date')\n .replaceAll(NORMALIZER_PATTERNS.timestamp, '/:timestamp')\n .replaceAll(NORMALIZER_PATTERNS.email, '/:email')\n .replaceAll(NORMALIZER_PATTERNS.numericId, '/:id');\n },\n\n /**\n * GraphQL preset - normalizes GraphQL operation names and paths\n * Keeps query/mutation names but normalizes embedded IDs\n */\n graphql: (name: string): string => {\n // For GraphQL, normalize both path-style and embedded IDs\n return name\n .replaceAll(NORMALIZER_PATTERNS.uuid, '/:uuid')\n .replaceAll(NORMALIZER_PATTERNS.numericId, '/:id');\n },\n\n /**\n * Minimal preset - only normalizes numeric IDs and UUIDs\n */\n minimal: (name: string): string => {\n return name\n .replaceAll(NORMALIZER_PATTERNS.uuid, '/:uuid')\n .replaceAll(NORMALIZER_PATTERNS.numericId, '/:id');\n },\n};\n\n/**\n * Resolve normalizer config to a function\n */\nfunction resolveNormalizer(\n config: SpanNameNormalizerConfig,\n): SpanNameNormalizerFn {\n if (typeof config === 'function') {\n return config;\n }\n\n const preset = NORMALIZER_PRESETS[config];\n if (!preset) {\n throw new Error(\n `Unknown span name normalizer preset: \"${config}\". ` +\n `Available presets: ${Object.keys(NORMALIZER_PRESETS).join(', ')}`,\n );\n }\n\n return preset;\n}\n\n/**\n * Span processor that normalizes span names to reduce cardinality.\n *\n * Normalization happens in onStart() when we have access to the mutable Span.\n * This allows us to call span.updateName() before the span is finalized.\n *\n * Common use cases:\n * - REST APIs: /users/123/posts/456 → /users/:id/posts/:id\n * - UUIDs: /items/550e8400-e29b-41d4-a716-446655440000 → /items/:uuid\n * - Dates: /logs/2024-01-15 → /logs/:date\n */\nexport class SpanNameNormalizingProcessor implements SpanProcessor {\n private readonly wrappedProcessor: SpanProcessor;\n private readonly normalizer: SpanNameNormalizerFn;\n\n constructor(\n wrappedProcessor: SpanProcessor,\n options: SpanNameNormalizingProcessorOptions,\n ) {\n this.wrappedProcessor = wrappedProcessor;\n this.normalizer = resolveNormalizer(options.normalizer);\n }\n\n /**\n * Normalize span name on start (when Span is mutable)\n */\n onStart(span: Span, parentContext: Context): void {\n try {\n const originalName = span.name;\n const normalizedName = this.normalizer(originalName);\n\n if (normalizedName !== originalName) {\n span.updateName(normalizedName);\n }\n } catch {\n // If normalizer throws, keep original name (fail-open)\n }\n\n this.wrappedProcessor.onStart(span, parentContext);\n }\n\n /**\n * Pass through onEnd unchanged\n */\n onEnd(span: ReadableSpan): void {\n this.wrappedProcessor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.wrappedProcessor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.wrappedProcessor.shutdown();\n }\n}\n\n/**\n * Export built-in patterns for advanced users who want to compose their own normalizers\n */\nexport { NORMALIZER_PATTERNS, NORMALIZER_PRESETS };\n"],"mappings":";;;;AA6DA,MAAM,sBAAsB;CAE1B,WAAW;CAGX,MAAM;CAGN,WAAW;CAGX,UAAU;CAGV,MAAM;CAGN,SAAS;CAGT,WAAW;CAGX,OAAO;AACT;;;;AAKA,MAAM,qBAGF;;;;;CAKF,aAAa,SAAyB;EACpC,OAAO,KACJ,WAAW,oBAAoB,MAAM,QAAQ,CAAC,CAC9C,WAAW,oBAAoB,WAAW,QAAQ,CAAC,CACnD,WAAW,oBAAoB,UAAU,YAAY,CAAC,CACtD,WAAW,oBAAoB,SAAS,QAAQ,CAAC,CACjD,WAAW,oBAAoB,WAAW,aAAa,CAAC,CACxD,WAAW,oBAAoB,OAAO,SAAS,CAAC,CAChD,WAAW,oBAAoB,WAAW,MAAM;CACrD;;;;;CAMA,UAAU,SAAyB;EAEjC,OAAO,KACJ,WAAW,oBAAoB,MAAM,QAAQ,CAAC,CAC9C,WAAW,oBAAoB,WAAW,MAAM;CACrD;;;;CAKA,UAAU,SAAyB;EACjC,OAAO,KACJ,WAAW,oBAAoB,MAAM,QAAQ,CAAC,CAC9C,WAAW,oBAAoB,WAAW,MAAM;CACrD;AACF;;;;AAKA,SAAS,kBACP,QACsB;CACtB,IAAI,OAAO,WAAW,YACpB,OAAO;CAGT,MAAM,SAAS,mBAAmB;CAClC,IAAI,CAAC,QACH,MAAM,IAAI,MACR,yCAAyC,OAAO,wBACxB,OAAO,KAAK,kBAAkB,CAAC,CAAC,KAAK,IAAI,GACnE;CAGF,OAAO;AACT;;;;;;;;;;;;AAaA,IAAa,+BAAb,MAAmE;CACjE,AAAiB;CACjB,AAAiB;CAEjB,YACE,kBACA,SACA;EACA,KAAK,mBAAmB;EACxB,KAAK,aAAa,kBAAkB,QAAQ,UAAU;CACxD;;;;CAKA,QAAQ,MAAY,eAA8B;EAChD,IAAI;GACF,MAAM,eAAe,KAAK;GAC1B,MAAM,iBAAiB,KAAK,WAAW,YAAY;GAEnD,IAAI,mBAAmB,cACrB,KAAK,WAAW,cAAc;EAElC,QAAQ,CAER;EAEA,KAAK,iBAAiB,QAAQ,MAAM,aAAa;CACnD;;;;CAKA,MAAM,MAA0B;EAC9B,KAAK,iBAAiB,MAAM,IAAI;CAClC;CAEA,aAA4B;EAC1B,OAAO,KAAK,iBAAiB,WAAW;CAC1C;CAEA,WAA0B;EACxB,OAAO,KAAK,iBAAiB,SAAS;CACxC;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stable-hash-CS9BxvVQ.cjs","names":["nodeCrypto"],"sources":["../src/stable-hash.ts"],"sourcesContent":["// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeCrypto from 'node:crypto';\n\n/**\n * Deterministic JSON stringify with sorted object keys, so two structurally\n * equal values always produce the same string regardless of key insertion\n * order. Shared by `defineEvent` and the validation layer for stable schema\n * hashes.\n */\nexport function stableStringify(value: unknown): string {\n if (value === null || value === undefined || typeof value !== 'object') {\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) {\n return '[' + value.map((v) => stableStringify(v)).join(',') + ']';\n }\n const obj = value as Record<string, unknown>;\n const body = Object.keys(obj)\n .toSorted()\n .map((k) => JSON.stringify(k) + ':' + stableStringify(obj[k]))\n .join(',');\n return '{' + body + '}';\n}\n\n/** Stable sha256 of any JSON-serializable value. */\nexport function hashJson(value: unknown): string {\n return nodeCrypto\n .createHash('sha256')\n .update(stableStringify(value))\n .digest('hex');\n}\n"],"mappings":";;;;;;;;;;;AASA,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,UAAU,QAAQ,UAAU,UAAa,OAAO,UAAU,UAC5D,OAAO,KAAK,UAAU,KAAK;CAE7B,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,MAAM,KAAK,MAAM,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI;CAEhE,MAAM,MAAM;CAKZ,OAAO,MAJM,OAAO,KAAK,GAAG,CAAC,CAC1B,SAAS,CAAC,CACV,KAAK,MAAM,KAAK,UAAU,CAAC,IAAI,MAAM,gBAAgB,IAAI,EAAE,CAAC,CAAC,CAC7D,KAAK,GACQ,IAAI;AACtB;;AAGA,SAAgB,SAAS,OAAwB;CAC/C,OAAOA,YACJ,WAAW,QAAQ,CAAC,CACpB,OAAO,gBAAgB,KAAK,CAAC,CAAC,CAC9B,OAAO,KAAK;AACjB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stable-hash-ChFBIhNt.js","names":[],"sources":["../src/stable-hash.ts"],"sourcesContent":["// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeCrypto from 'node:crypto';\n\n/**\n * Deterministic JSON stringify with sorted object keys, so two structurally\n * equal values always produce the same string regardless of key insertion\n * order. Shared by `defineEvent` and the validation layer for stable schema\n * hashes.\n */\nexport function stableStringify(value: unknown): string {\n if (value === null || value === undefined || typeof value !== 'object') {\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) {\n return '[' + value.map((v) => stableStringify(v)).join(',') + ']';\n }\n const obj = value as Record<string, unknown>;\n const body = Object.keys(obj)\n .toSorted()\n .map((k) => JSON.stringify(k) + ':' + stableStringify(obj[k]))\n .join(',');\n return '{' + body + '}';\n}\n\n/** Stable sha256 of any JSON-serializable value. */\nexport function hashJson(value: unknown): string {\n return nodeCrypto\n .createHash('sha256')\n .update(stableStringify(value))\n .digest('hex');\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,UAAU,QAAQ,UAAU,UAAa,OAAO,UAAU,UAC5D,OAAO,KAAK,UAAU,KAAK;CAE7B,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,MAAM,KAAK,MAAM,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI;CAEhE,MAAM,MAAM;CAKZ,OAAO,MAJM,OAAO,KAAK,GAAG,CAAC,CAC1B,SAAS,CAAC,CACV,KAAK,MAAM,KAAK,UAAU,CAAC,IAAI,MAAM,gBAAgB,IAAI,EAAE,CAAC,CAAC,CAC7D,KAAK,GACQ,IAAI;AACtB;;AAGA,SAAgB,SAAS,OAAwB;CAC/C,OAAO,WACJ,WAAW,QAAQ,CAAC,CACpB,OAAO,gBAAgB,KAAK,CAAC,CAAC,CAC9B,OAAO,KAAK;AACjB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"structured-error-9--cxBay.js","names":[],"sources":["../src/flatten-attributes.ts","../src/structured-error.ts"],"sourcesContent":["import type { AttributeValue } from './trace-context';\n\n/**\n * Convert an unknown value to an OTel-compatible AttributeValue.\n * Returns undefined when the value cannot be represented.\n */\nexport function toAttributeValue(value: unknown): AttributeValue | undefined {\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n if (Array.isArray(value)) {\n if (\n value.every((v) => typeof v === 'string') ||\n value.every((v) => typeof v === 'number') ||\n value.every((v) => typeof v === 'boolean')\n ) {\n return value as AttributeValue;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '<serialization-failed>';\n }\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (value instanceof Error) {\n return value.message;\n }\n return undefined;\n}\n\n/**\n * Recursively flatten a nested object into dot-notation OTel attributes.\n * Includes circular reference protection via WeakSet.\n */\nexport function flattenToAttributes(\n fields: Record<string, unknown>,\n prefix = '',\n): Record<string, AttributeValue> {\n const out: Record<string, AttributeValue> = {};\n const seen = new WeakSet<object>();\n\n function flatten(obj: Record<string, unknown>, currentPrefix: string): void {\n for (const [key, value] of Object.entries(obj)) {\n if (value == null) continue;\n const nextKey = currentPrefix ? `${currentPrefix}.${key}` : key;\n\n const attr = toAttributeValue(value);\n if (attr !== undefined) {\n out[nextKey] = attr;\n continue;\n }\n\n if (typeof value === 'object' && value.constructor === Object) {\n if (seen.has(value)) {\n out[nextKey] = '<circular-reference>';\n continue;\n }\n seen.add(value);\n flatten(value as Record<string, unknown>, nextKey);\n continue;\n }\n\n try {\n out[nextKey] = JSON.stringify(value);\n } catch {\n out[nextKey] = '<serialization-failed>';\n }\n }\n }\n\n flatten(fields, prefix);\n return out;\n}\n","import { SpanStatusCode } from '@opentelemetry/api';\nimport type { AttributeValue, TraceContext } from './trace-context';\nimport { flattenToAttributes } from './flatten-attributes';\n\nconst internalKey = Symbol.for('autotel.error.internal');\n\nexport interface StructuredErrorInput {\n message: string;\n why?: string;\n fix?: string;\n link?: string;\n code?: string | number;\n status?: number;\n cause?: unknown;\n details?: Record<string, unknown>;\n name?: string;\n /** Backend-only context. Omitted from toJSON() and never serialized to clients. */\n internal?: Record<string, unknown>;\n}\n\nexport interface StructuredError extends Error {\n why?: string;\n fix?: string;\n link?: string;\n code?: string | number;\n status?: number;\n details?: Record<string, unknown>;\n /** Backend-only context. Omitted from toJSON() and never serialized to clients. */\n readonly internal?: Record<string, unknown>;\n}\n\nexport function createStructuredError(\n input: StructuredErrorInput,\n): StructuredError {\n const error = new Error(input.message, {\n cause: input.cause,\n }) as StructuredError;\n\n error.name = input.name ?? 'StructuredError';\n if (input.why !== undefined) error.why = input.why;\n if (input.fix !== undefined) error.fix = input.fix;\n if (input.link !== undefined) error.link = input.link;\n if (input.code !== undefined) error.code = input.code;\n if (input.status !== undefined) error.status = input.status;\n if (input.details !== undefined) error.details = input.details;\n\n if (input.internal !== undefined) {\n Object.defineProperty(error, internalKey, {\n value: input.internal,\n enumerable: false,\n writable: false,\n configurable: true,\n });\n }\n\n Object.defineProperty(error, 'internal', {\n get() {\n return (\n this as StructuredError & { [internalKey]?: Record<string, unknown> }\n )[internalKey];\n },\n enumerable: false,\n configurable: true,\n });\n\n error.toString = () => {\n const lines = [`${error.name}: ${error.message}`];\n if (error.why) lines.push(` Why: ${error.why}`);\n if (error.fix) lines.push(` Fix: ${error.fix}`);\n if (error.link) lines.push(` Link: ${error.link}`);\n if (error.code !== undefined) lines.push(` Code: ${error.code}`);\n if (error.status !== undefined) lines.push(` Status: ${error.status}`);\n if (error.cause) {\n const cause = error.cause as Error;\n lines.push(` Caused by: ${cause.name}: ${cause.message}`);\n }\n return lines.join('\\n');\n };\n\n return error;\n}\n\nexport function structuredErrorToJSON(\n error: StructuredError,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {\n name: error.name,\n message: error.message,\n };\n\n if (error.status !== undefined) result.status = error.status;\n if (error.why || error.fix || error.link) {\n result.data = {\n ...(error.why && { why: error.why }),\n ...(error.fix && { fix: error.fix }),\n ...(error.link && { link: error.link }),\n };\n }\n if (error.code !== undefined) result.code = error.code;\n if (error.details) result.details = error.details;\n if (error.cause instanceof Error) {\n result.cause = { name: error.cause.name, message: error.cause.message };\n }\n\n return result;\n}\n\nexport function getStructuredErrorAttributes(\n error: Error,\n): Record<string, AttributeValue> {\n const structured = error as StructuredError;\n const attributes: Record<string, AttributeValue> = {\n 'error.type': error.name || 'Error',\n 'error.message': error.message,\n };\n\n if (error.stack) attributes['error.stack'] = error.stack;\n if (structured.why) attributes['error.why'] = structured.why;\n if (structured.fix) attributes['error.fix'] = structured.fix;\n if (structured.link) attributes['error.link'] = structured.link;\n if (structured.code !== undefined) {\n attributes['error.code'] =\n typeof structured.code === 'string'\n ? structured.code\n : String(structured.code);\n }\n if (structured.status !== undefined) {\n attributes['error.status'] = structured.status;\n }\n if (structured.details) {\n Object.assign(\n attributes,\n flattenToAttributes(structured.details, 'error.details'),\n );\n }\n\n return attributes;\n}\n\nexport function recordStructuredError(\n ctx: Pick<TraceContext, 'setAttributes' | 'setStatus'>,\n error: Error,\n): void {\n const maybeRecordException = (\n ctx as unknown as {\n recordException?: (e: Error) => void;\n }\n ).recordException;\n if (typeof maybeRecordException === 'function') {\n maybeRecordException(error);\n }\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n ctx.setAttributes(getStructuredErrorAttributes(error));\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"structured-error-CHg7DoIQ.cjs","names":["SpanStatusCode"],"sources":["../src/flatten-attributes.ts","../src/structured-error.ts"],"sourcesContent":["import type { AttributeValue } from './trace-context';\n\n/**\n * Convert an unknown value to an OTel-compatible AttributeValue.\n * Returns undefined when the value cannot be represented.\n */\nexport function toAttributeValue(value: unknown): AttributeValue | undefined {\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n if (Array.isArray(value)) {\n if (\n value.every((v) => typeof v === 'string') ||\n value.every((v) => typeof v === 'number') ||\n value.every((v) => typeof v === 'boolean')\n ) {\n return value as AttributeValue;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '<serialization-failed>';\n }\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (value instanceof Error) {\n return value.message;\n }\n return undefined;\n}\n\n/**\n * Recursively flatten a nested object into dot-notation OTel attributes.\n * Includes circular reference protection via WeakSet.\n */\nexport function flattenToAttributes(\n fields: Record<string, unknown>,\n prefix = '',\n): Record<string, AttributeValue> {\n const out: Record<string, AttributeValue> = {};\n const seen = new WeakSet<object>();\n\n function flatten(obj: Record<string, unknown>, currentPrefix: string): void {\n for (const [key, value] of Object.entries(obj)) {\n if (value == null) continue;\n const nextKey = currentPrefix ? `${currentPrefix}.${key}` : key;\n\n const attr = toAttributeValue(value);\n if (attr !== undefined) {\n out[nextKey] = attr;\n continue;\n }\n\n if (typeof value === 'object' && value.constructor === Object) {\n if (seen.has(value)) {\n out[nextKey] = '<circular-reference>';\n continue;\n }\n seen.add(value);\n flatten(value as Record<string, unknown>, nextKey);\n continue;\n }\n\n try {\n out[nextKey] = JSON.stringify(value);\n } catch {\n out[nextKey] = '<serialization-failed>';\n }\n }\n }\n\n flatten(fields, prefix);\n return out;\n}\n","import { SpanStatusCode } from '@opentelemetry/api';\nimport type { AttributeValue, TraceContext } from './trace-context';\nimport { flattenToAttributes } from './flatten-attributes';\n\nconst internalKey = Symbol.for('autotel.error.internal');\n\nexport interface StructuredErrorInput {\n message: string;\n why?: string;\n fix?: string;\n link?: string;\n code?: string | number;\n status?: number;\n cause?: unknown;\n details?: Record<string, unknown>;\n name?: string;\n /** Backend-only context. Omitted from toJSON() and never serialized to clients. */\n internal?: Record<string, unknown>;\n}\n\nexport interface StructuredError extends Error {\n why?: string;\n fix?: string;\n link?: string;\n code?: string | number;\n status?: number;\n details?: Record<string, unknown>;\n /** Backend-only context. Omitted from toJSON() and never serialized to clients. */\n readonly internal?: Record<string, unknown>;\n}\n\nexport function createStructuredError(\n input: StructuredErrorInput,\n): StructuredError {\n const error = new Error(input.message, {\n cause: input.cause,\n }) as StructuredError;\n\n error.name = input.name ?? 'StructuredError';\n if (input.why !== undefined) error.why = input.why;\n if (input.fix !== undefined) error.fix = input.fix;\n if (input.link !== undefined) error.link = input.link;\n if (input.code !== undefined) error.code = input.code;\n if (input.status !== undefined) error.status = input.status;\n if (input.details !== undefined) error.details = input.details;\n\n if (input.internal !== undefined) {\n Object.defineProperty(error, internalKey, {\n value: input.internal,\n enumerable: false,\n writable: false,\n configurable: true,\n });\n }\n\n Object.defineProperty(error, 'internal', {\n get() {\n return (\n this as StructuredError & { [internalKey]?: Record<string, unknown> }\n )[internalKey];\n },\n enumerable: false,\n configurable: true,\n });\n\n error.toString = () => {\n const lines = [`${error.name}: ${error.message}`];\n if (error.why) lines.push(` Why: ${error.why}`);\n if (error.fix) lines.push(` Fix: ${error.fix}`);\n if (error.link) lines.push(` Link: ${error.link}`);\n if (error.code !== undefined) lines.push(` Code: ${error.code}`);\n if (error.status !== undefined) lines.push(` Status: ${error.status}`);\n if (error.cause) {\n const cause = error.cause as Error;\n lines.push(` Caused by: ${cause.name}: ${cause.message}`);\n }\n return lines.join('\\n');\n };\n\n return error;\n}\n\nexport function structuredErrorToJSON(\n error: StructuredError,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {\n name: error.name,\n message: error.message,\n };\n\n if (error.status !== undefined) result.status = error.status;\n if (error.why || error.fix || error.link) {\n result.data = {\n ...(error.why && { why: error.why }),\n ...(error.fix && { fix: error.fix }),\n ...(error.link && { link: error.link }),\n };\n }\n if (error.code !== undefined) result.code = error.code;\n if (error.details) result.details = error.details;\n if (error.cause instanceof Error) {\n result.cause = { name: error.cause.name, message: error.cause.message };\n }\n\n return result;\n}\n\nexport function getStructuredErrorAttributes(\n error: Error,\n): Record<string, AttributeValue> {\n const structured = error as StructuredError;\n const attributes: Record<string, AttributeValue> = {\n 'error.type': error.name || 'Error',\n 'error.message': error.message,\n };\n\n if (error.stack) attributes['error.stack'] = error.stack;\n if (structured.why) attributes['error.why'] = structured.why;\n if (structured.fix) attributes['error.fix'] = structured.fix;\n if (structured.link) attributes['error.link'] = structured.link;\n if (structured.code !== undefined) {\n attributes['error.code'] =\n typeof structured.code === 'string'\n ? structured.code\n : String(structured.code);\n }\n if (structured.status !== undefined) {\n attributes['error.status'] = structured.status;\n }\n if (structured.details) {\n Object.assign(\n attributes,\n flattenToAttributes(structured.details, 'error.details'),\n );\n }\n\n return attributes;\n}\n\nexport function recordStructuredError(\n ctx: Pick<TraceContext, 'setAttributes' | 'setStatus'>,\n error: Error,\n): void {\n const maybeRecordException = (\n ctx as unknown as {\n recordException?: (e: Error) => void;\n }\n ).recordException;\n if (typeof maybeRecordException === 'function') {\n maybeRecordException(error);\n }\n ctx.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n ctx.setAttributes(getStructuredErrorAttributes(error));\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tail-sampling-processor.cjs","names":["AUTOTEL_SAMPLING_TAIL_EVALUATED","AUTOTEL_SAMPLING_TAIL_KEEP"],"sources":["../src/tail-sampling-processor.ts"],"sourcesContent":["/**\n * Tail Sampling Span Processor\n *\n * Filters spans based on the `autotel.sampling.tail.keep` attribute set during execution.\n * This enables adaptive sampling where we decide whether to keep a span AFTER\n * the operation completes, based on criteria like errors, duration, etc.\n *\n * How it works:\n * 1. Decorator creates span optimistically (head sampling returns true)\n * 2. Operation executes and completes\n * 3. Decorator calls shouldKeepTrace() and sets autotel.sampling.tail.keep attribute\n * 4. This processor checks the attribute and drops spans marked as false\n */\n\nimport type {\n SpanProcessor,\n ReadableSpan,\n} from '@opentelemetry/sdk-trace-base';\nimport type { Context } from '@opentelemetry/api';\nimport type { Span } from '@opentelemetry/sdk-trace-base';\nimport {\n AUTOTEL_SAMPLING_TAIL_KEEP,\n AUTOTEL_SAMPLING_TAIL_EVALUATED,\n} from './sampling';\n\nexport class TailSamplingSpanProcessor implements SpanProcessor {\n private wrappedProcessor: SpanProcessor;\n\n constructor(wrappedProcessor: SpanProcessor) {\n this.wrappedProcessor = wrappedProcessor;\n }\n\n onStart(span: Span, parentContext: Context): void {\n this.wrappedProcessor.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n const tailEvaluated = span.attributes[AUTOTEL_SAMPLING_TAIL_EVALUATED];\n const shouldKeep = span.attributes[AUTOTEL_SAMPLING_TAIL_KEEP];\n\n if (tailEvaluated === true && shouldKeep === false) {\n return;\n }\n\n this.wrappedProcessor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.wrappedProcessor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.wrappedProcessor.shutdown();\n }\n}\n"],"mappings":";;;;AAyBA,IAAa,4BAAb,MAAgE;CAC9D,AAAQ;CAER,YAAY,kBAAiC;EAC3C,KAAK,mBAAmB;CAC1B;CAEA,QAAQ,MAAY,eAA8B;EAChD,KAAK,iBAAiB,QAAQ,MAAM,aAAa;CACnD;CAEA,MAAM,MAA0B;EAC9B,MAAM,gBAAgB,KAAK,WAAWA;EACtC,MAAM,aAAa,KAAK,WAAWC;EAEnC,IAAI,kBAAkB,QAAQ,eAAe,OAC3C;EAGF,KAAK,iBAAiB,MAAM,IAAI;CAClC;CAEA,aAA4B;EAC1B,OAAO,KAAK,iBAAiB,WAAW;CAC1C;CAEA,WAA0B;EACxB,OAAO,KAAK,iBAAiB,SAAS;CACxC;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tail-sampling-processor.d.cts","names":[],"sources":["../src/tail-sampling-processor.ts"],"mappings":";;;cAyBa,qCAAqC;UACxC;EAEI,YAAA,kBAAkB;EAI9B,QAAQ,MAAM,MAAM,eAAe;EAInC,MAAM,MAAM;EAWZ,cAAc;EAId,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tail-sampling-processor.d.ts","names":[],"sources":["../src/tail-sampling-processor.ts"],"mappings":";;;cAyBa,qCAAqC;UACxC;EAEI,YAAA,kBAAkB;EAI9B,QAAQ,MAAM,MAAM,eAAe;EAInC,MAAM,MAAM;EAWZ,cAAc;EAId,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tail-sampling-processor.js","names":[],"sources":["../src/tail-sampling-processor.ts"],"sourcesContent":["/**\n * Tail Sampling Span Processor\n *\n * Filters spans based on the `autotel.sampling.tail.keep` attribute set during execution.\n * This enables adaptive sampling where we decide whether to keep a span AFTER\n * the operation completes, based on criteria like errors, duration, etc.\n *\n * How it works:\n * 1. Decorator creates span optimistically (head sampling returns true)\n * 2. Operation executes and completes\n * 3. Decorator calls shouldKeepTrace() and sets autotel.sampling.tail.keep attribute\n * 4. This processor checks the attribute and drops spans marked as false\n */\n\nimport type {\n SpanProcessor,\n ReadableSpan,\n} from '@opentelemetry/sdk-trace-base';\nimport type { Context } from '@opentelemetry/api';\nimport type { Span } from '@opentelemetry/sdk-trace-base';\nimport {\n AUTOTEL_SAMPLING_TAIL_KEEP,\n AUTOTEL_SAMPLING_TAIL_EVALUATED,\n} from './sampling';\n\nexport class TailSamplingSpanProcessor implements SpanProcessor {\n private wrappedProcessor: SpanProcessor;\n\n constructor(wrappedProcessor: SpanProcessor) {\n this.wrappedProcessor = wrappedProcessor;\n }\n\n onStart(span: Span, parentContext: Context): void {\n this.wrappedProcessor.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n const tailEvaluated = span.attributes[AUTOTEL_SAMPLING_TAIL_EVALUATED];\n const shouldKeep = span.attributes[AUTOTEL_SAMPLING_TAIL_KEEP];\n\n if (tailEvaluated === true && shouldKeep === false) {\n return;\n }\n\n this.wrappedProcessor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.wrappedProcessor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.wrappedProcessor.shutdown();\n }\n}\n"],"mappings":";;;AAyBA,IAAa,4BAAb,MAAgE;CAC9D,AAAQ;CAER,YAAY,kBAAiC;EAC3C,KAAK,mBAAmB;CAC1B;CAEA,QAAQ,MAAY,eAA8B;EAChD,KAAK,iBAAiB,QAAQ,MAAM,aAAa;CACnD;CAEA,MAAM,MAA0B;EAC9B,MAAM,gBAAgB,KAAK,WAAW;EACtC,MAAM,aAAa,KAAK,WAAW;EAEnC,IAAI,kBAAkB,QAAQ,eAAe,OAC3C;EAGF,KAAK,iBAAiB,MAAM,IAAI;CAClC;CAEA,aAA4B;EAC1B,OAAO,KAAK,iBAAiB,WAAW;CAC1C;CAEA,WAA0B;EACxB,OAAO,KAAK,iBAAiB,SAAS;CACxC;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-span-collector.cjs","names":["SpanStatusCode"],"sources":["../src/test-span-collector.ts"],"sourcesContent":["/**\n * TestSpanCollector — SpanExporter that groups finished spans by traceId\n * and drains per-trace for embedding in test metadata.\n *\n * Wire it to a tracer provider, register that provider with autotel, then\n * create spans through autotel's tracer so finished spans reach the collector.\n *\n * @example\n * ```typescript\n * import { TestSpanCollector } from 'autotel/test-span-collector';\n * import { SimpleSpanProcessor } from 'autotel/processors';\n * import { setAutotelTracerProvider, getAutotelTracer } from 'autotel';\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n *\n * const collector = new TestSpanCollector();\n *\n * // OTel SDK v2: pass processors at construction, then register the provider.\n * const provider = new NodeTracerProvider({\n * spanProcessors: [new SimpleSpanProcessor(collector)],\n * });\n * setAutotelTracerProvider(provider);\n *\n * const tracer = getAutotelTracer('test');\n * const root = tracer.startSpan('operation');\n * // ...work, child spans...\n * root.end();\n *\n * const spans = collector.drainTrace(\n * root.spanContext().traceId,\n * root.spanContext().spanId,\n * );\n * // spans contains the root span and its descendants\n * ```\n *\n * For high-level instrumentation written with `span()` / `track()`, prefer\n * `createTraceCollector()` from `autotel/testing`. For a plain low-level\n * exporter, use `InMemorySpanExporter` from `autotel/exporters`.\n */\n\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { SpanStatusCode } from '@opentelemetry/api';\n\n/** @see ExportResultCode from @opentelemetry/core */\nconst ExportResultCode = { SUCCESS: 0, FAILED: 1 } as const;\n\n/** Attribute value types that survive serialization */\ntype SerializableValue =\n string | number | boolean | string[] | number[] | boolean[];\n\n/**\n * Portable serialized span for embedding in test metadata.\n * `startTimeMs` is derived from OTel HrTime — epoch-based wall-clock ms in the current SDK.\n *\n * Defined as a `type` (not `interface`) so it is assignable to\n * `Record<string, unknown>` in TypeScript 6+ strict mode.\n */\nexport type SerializedSpan = {\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n durationMs: number;\n status: 'ok' | 'error' | 'unset';\n statusMessage?: string;\n attributes?: Record<string, SerializableValue>;\n};\n\nexport class TestSpanCollector implements SpanExporter {\n private traces = new Map<string, ReadableSpan[]>();\n\n export(\n spans: ReadableSpan[],\n callback: (result: { code: number }) => void,\n ): void {\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n let list = this.traces.get(traceId);\n if (!list) {\n list = [];\n this.traces.set(traceId, list);\n }\n list.push(span);\n }\n callback({ code: ExportResultCode.SUCCESS });\n }\n\n /**\n * Drain and serialize spans that are descendants of `rootSpanId` within `traceId`.\n * Filters to the subtree rooted at the test span to prevent cross-test mixing.\n * Removes the entire traceId entry from the collector.\n */\n drainTrace(traceId: string, rootSpanId: string): SerializedSpan[] {\n const spans = this.peekTrace(traceId, rootSpanId);\n this.traces.delete(traceId);\n return spans;\n }\n\n /**\n * Serialize the finished spans of `traceId` without removing them — safe to\n * call repeatedly while a trace is still growing (e.g. a scenario checker\n * polling until an async flow's completion boundary closes).\n *\n * With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.\n */\n peekTrace(traceId: string, rootSpanId?: string): SerializedSpan[] {\n const allSpans = this.traces.get(traceId);\n if (!allSpans?.length) return [];\n if (rootSpanId === undefined) return allSpans.map((s) => serializeSpan(s));\n\n // Build spanId → span index for efficient parent-chain walking\n const byId = new Map<string, ReadableSpan>();\n for (const s of allSpans) byId.set(s.spanContext().spanId, s);\n\n // Filter to spans that are the root or descendants of rootSpanId\n const included = allSpans.filter((s) => {\n let id: string | undefined = s.spanContext().spanId;\n while (id) {\n if (id === rootSpanId) return true;\n const parent = byId.get(id);\n const parentId = parent?.parentSpanContext?.spanId || undefined;\n if (parentId === id) break; // cycle guard\n id = parentId;\n }\n return false;\n });\n\n return included.map((s) => serializeSpan(s));\n }\n\n shutdown(): Promise<void> {\n this.traces.clear();\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\nfunction hrTimeToMs(hr: [number, number]): number {\n return hr[0] * 1000 + hr[1] / 1_000_000;\n}\n\nfunction isSerializable(v: unknown): v is SerializableValue {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')\n return true;\n if (Array.isArray(v) && v.length > 0) {\n const t = typeof v[0];\n return (\n (t === 'string' || t === 'number' || t === 'boolean') &&\n v.every((e) => typeof e === t)\n );\n }\n return false;\n}\n\nexport function serializeSpan(span: ReadableSpan): SerializedSpan {\n const attrs: Record<string, SerializableValue> = {};\n for (const [k, v] of Object.entries(span.attributes)) {\n if (isSerializable(v)) attrs[k] = v;\n }\n return {\n spanId: span.spanContext().spanId,\n parentSpanId: span.parentSpanContext?.spanId || undefined,\n name: span.name,\n startTimeMs: hrTimeToMs(span.startTime as [number, number]),\n durationMs: hrTimeToMs(span.duration as [number, number]),\n status:\n span.status.code === SpanStatusCode.ERROR\n ? 'error'\n : span.status.code === SpanStatusCode.OK\n ? 'ok'\n : 'unset',\n statusMessage: span.status.message || undefined,\n attributes: Object.keys(attrs).length > 0 ? attrs : undefined,\n };\n}\n"],"mappings":";;;;;AA2CA,MAAM,mBAAmB;CAAE,SAAS;CAAG,QAAQ;AAAE;AAwBjD,IAAa,oBAAb,MAAuD;CACrD,AAAQ,yBAAS,IAAI,IAA4B;CAEjD,OACE,OACA,UACM;EACN,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,YAAY,CAAC,CAAC;GACnC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO;GAClC,IAAI,CAAC,MAAM;IACT,OAAO,CAAC;IACR,KAAK,OAAO,IAAI,SAAS,IAAI;GAC/B;GACA,KAAK,KAAK,IAAI;EAChB;EACA,SAAS,EAAE,MAAM,iBAAiB,QAAQ,CAAC;CAC7C;;;;;;CAOA,WAAW,SAAiB,YAAsC;EAChE,MAAM,QAAQ,KAAK,UAAU,SAAS,UAAU;EAChD,KAAK,OAAO,OAAO,OAAO;EAC1B,OAAO;CACT;;;;;;;;CASA,UAAU,SAAiB,YAAuC;EAChE,MAAM,WAAW,KAAK,OAAO,IAAI,OAAO;EACxC,IAAI,CAAC,UAAU,QAAQ,OAAO,CAAC;EAC/B,IAAI,eAAe,QAAW,OAAO,SAAS,KAAK,MAAM,cAAc,CAAC,CAAC;EAGzE,MAAM,uBAAO,IAAI,IAA0B;EAC3C,KAAK,MAAM,KAAK,UAAU,KAAK,IAAI,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC;EAe5D,OAZiB,SAAS,QAAQ,MAAM;GACtC,IAAI,KAAyB,EAAE,YAAY,CAAC,CAAC;GAC7C,OAAO,IAAI;IACT,IAAI,OAAO,YAAY,OAAO;IAE9B,MAAM,WADS,KAAK,IAAI,EACF,CAAC,EAAE,mBAAmB,UAAU;IACtD,IAAI,aAAa,IAAI;IACrB,KAAK;GACP;GACA,OAAO;EACT,CAEc,CAAC,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC;CAC7C;CAEA,WAA0B;EACxB,KAAK,OAAO,MAAM;EAClB,OAAO,QAAQ,QAAQ;CACzB;CAEA,aAA4B;EAC1B,OAAO,QAAQ,QAAQ;CACzB;AACF;AAEA,SAAS,WAAW,IAA8B;CAChD,OAAO,GAAG,KAAK,MAAO,GAAG,KAAK;AAChC;AAEA,SAAS,eAAe,GAAoC;CAC1D,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WACjE,OAAO;CACT,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG;EACpC,MAAM,IAAI,OAAO,EAAE;EACnB,QACG,MAAM,YAAY,MAAM,YAAY,MAAM,cAC3C,EAAE,OAAO,MAAM,OAAO,MAAM,CAAC;CAEjC;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,MAAoC;CAChE,MAAM,QAA2C,CAAC;CAClD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,UAAU,GACjD,IAAI,eAAe,CAAC,GAAG,MAAM,KAAK;CAEpC,OAAO;EACL,QAAQ,KAAK,YAAY,CAAC,CAAC;EAC3B,cAAc,KAAK,mBAAmB,UAAU;EAChD,MAAM,KAAK;EACX,aAAa,WAAW,KAAK,SAA6B;EAC1D,YAAY,WAAW,KAAK,QAA4B;EACxD,QACE,KAAK,OAAO,SAASA,kCAAe,QAChC,UACA,KAAK,OAAO,SAASA,kCAAe,KAClC,OACA;EACR,eAAe,KAAK,OAAO,WAAW;EACtC,YAAY,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;CACtD;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-span-collector.d.cts","names":[],"sources":["../src/test-span-collector.ts"],"mappings":";;;KA8CK;;;;;;;;KAUO;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,eAAe;;cAGjB,6BAA6B;UAChC;EAER,OACE,OAAO,gBACP,WAAW;IAAU;;;;;;;EAmBvB,WAAW,iBAAiB,qBAAqB;;;;;;;;EAajD,UAAU,iBAAiB,sBAAsB;EAyBjD,YAAY;EAKZ,cAAc;;iBAsBA,cAAc,MAAM,eAAe"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-span-collector.d.ts","names":[],"sources":["../src/test-span-collector.ts"],"mappings":";;;KA8CK;;;;;;;;KAUO;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,eAAe;;cAGjB,6BAA6B;UAChC;EAER,OACE,OAAO,gBACP,WAAW;IAAU;;;;;;;EAmBvB,WAAW,iBAAiB,qBAAqB;;;;;;;;EAajD,UAAU,iBAAiB,sBAAsB;EAyBjD,YAAY;EAKZ,cAAc;;iBAsBA,cAAc,MAAM,eAAe"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-span-collector.js","names":[],"sources":["../src/test-span-collector.ts"],"sourcesContent":["/**\n * TestSpanCollector — SpanExporter that groups finished spans by traceId\n * and drains per-trace for embedding in test metadata.\n *\n * Wire it to a tracer provider, register that provider with autotel, then\n * create spans through autotel's tracer so finished spans reach the collector.\n *\n * @example\n * ```typescript\n * import { TestSpanCollector } from 'autotel/test-span-collector';\n * import { SimpleSpanProcessor } from 'autotel/processors';\n * import { setAutotelTracerProvider, getAutotelTracer } from 'autotel';\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n *\n * const collector = new TestSpanCollector();\n *\n * // OTel SDK v2: pass processors at construction, then register the provider.\n * const provider = new NodeTracerProvider({\n * spanProcessors: [new SimpleSpanProcessor(collector)],\n * });\n * setAutotelTracerProvider(provider);\n *\n * const tracer = getAutotelTracer('test');\n * const root = tracer.startSpan('operation');\n * // ...work, child spans...\n * root.end();\n *\n * const spans = collector.drainTrace(\n * root.spanContext().traceId,\n * root.spanContext().spanId,\n * );\n * // spans contains the root span and its descendants\n * ```\n *\n * For high-level instrumentation written with `span()` / `track()`, prefer\n * `createTraceCollector()` from `autotel/testing`. For a plain low-level\n * exporter, use `InMemorySpanExporter` from `autotel/exporters`.\n */\n\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { SpanStatusCode } from '@opentelemetry/api';\n\n/** @see ExportResultCode from @opentelemetry/core */\nconst ExportResultCode = { SUCCESS: 0, FAILED: 1 } as const;\n\n/** Attribute value types that survive serialization */\ntype SerializableValue =\n string | number | boolean | string[] | number[] | boolean[];\n\n/**\n * Portable serialized span for embedding in test metadata.\n * `startTimeMs` is derived from OTel HrTime — epoch-based wall-clock ms in the current SDK.\n *\n * Defined as a `type` (not `interface`) so it is assignable to\n * `Record<string, unknown>` in TypeScript 6+ strict mode.\n */\nexport type SerializedSpan = {\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n durationMs: number;\n status: 'ok' | 'error' | 'unset';\n statusMessage?: string;\n attributes?: Record<string, SerializableValue>;\n};\n\nexport class TestSpanCollector implements SpanExporter {\n private traces = new Map<string, ReadableSpan[]>();\n\n export(\n spans: ReadableSpan[],\n callback: (result: { code: number }) => void,\n ): void {\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n let list = this.traces.get(traceId);\n if (!list) {\n list = [];\n this.traces.set(traceId, list);\n }\n list.push(span);\n }\n callback({ code: ExportResultCode.SUCCESS });\n }\n\n /**\n * Drain and serialize spans that are descendants of `rootSpanId` within `traceId`.\n * Filters to the subtree rooted at the test span to prevent cross-test mixing.\n * Removes the entire traceId entry from the collector.\n */\n drainTrace(traceId: string, rootSpanId: string): SerializedSpan[] {\n const spans = this.peekTrace(traceId, rootSpanId);\n this.traces.delete(traceId);\n return spans;\n }\n\n /**\n * Serialize the finished spans of `traceId` without removing them — safe to\n * call repeatedly while a trace is still growing (e.g. a scenario checker\n * polling until an async flow's completion boundary closes).\n *\n * With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.\n */\n peekTrace(traceId: string, rootSpanId?: string): SerializedSpan[] {\n const allSpans = this.traces.get(traceId);\n if (!allSpans?.length) return [];\n if (rootSpanId === undefined) return allSpans.map((s) => serializeSpan(s));\n\n // Build spanId → span index for efficient parent-chain walking\n const byId = new Map<string, ReadableSpan>();\n for (const s of allSpans) byId.set(s.spanContext().spanId, s);\n\n // Filter to spans that are the root or descendants of rootSpanId\n const included = allSpans.filter((s) => {\n let id: string | undefined = s.spanContext().spanId;\n while (id) {\n if (id === rootSpanId) return true;\n const parent = byId.get(id);\n const parentId = parent?.parentSpanContext?.spanId || undefined;\n if (parentId === id) break; // cycle guard\n id = parentId;\n }\n return false;\n });\n\n return included.map((s) => serializeSpan(s));\n }\n\n shutdown(): Promise<void> {\n this.traces.clear();\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\nfunction hrTimeToMs(hr: [number, number]): number {\n return hr[0] * 1000 + hr[1] / 1_000_000;\n}\n\nfunction isSerializable(v: unknown): v is SerializableValue {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')\n return true;\n if (Array.isArray(v) && v.length > 0) {\n const t = typeof v[0];\n return (\n (t === 'string' || t === 'number' || t === 'boolean') &&\n v.every((e) => typeof e === t)\n );\n }\n return false;\n}\n\nexport function serializeSpan(span: ReadableSpan): SerializedSpan {\n const attrs: Record<string, SerializableValue> = {};\n for (const [k, v] of Object.entries(span.attributes)) {\n if (isSerializable(v)) attrs[k] = v;\n }\n return {\n spanId: span.spanContext().spanId,\n parentSpanId: span.parentSpanContext?.spanId || undefined,\n name: span.name,\n startTimeMs: hrTimeToMs(span.startTime as [number, number]),\n durationMs: hrTimeToMs(span.duration as [number, number]),\n status:\n span.status.code === SpanStatusCode.ERROR\n ? 'error'\n : span.status.code === SpanStatusCode.OK\n ? 'ok'\n : 'unset',\n statusMessage: span.status.message || undefined,\n attributes: Object.keys(attrs).length > 0 ? attrs : undefined,\n };\n}\n"],"mappings":";;;;AA2CA,MAAM,mBAAmB;CAAE,SAAS;CAAG,QAAQ;AAAE;AAwBjD,IAAa,oBAAb,MAAuD;CACrD,AAAQ,yBAAS,IAAI,IAA4B;CAEjD,OACE,OACA,UACM;EACN,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,YAAY,CAAC,CAAC;GACnC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO;GAClC,IAAI,CAAC,MAAM;IACT,OAAO,CAAC;IACR,KAAK,OAAO,IAAI,SAAS,IAAI;GAC/B;GACA,KAAK,KAAK,IAAI;EAChB;EACA,SAAS,EAAE,MAAM,iBAAiB,QAAQ,CAAC;CAC7C;;;;;;CAOA,WAAW,SAAiB,YAAsC;EAChE,MAAM,QAAQ,KAAK,UAAU,SAAS,UAAU;EAChD,KAAK,OAAO,OAAO,OAAO;EAC1B,OAAO;CACT;;;;;;;;CASA,UAAU,SAAiB,YAAuC;EAChE,MAAM,WAAW,KAAK,OAAO,IAAI,OAAO;EACxC,IAAI,CAAC,UAAU,QAAQ,OAAO,CAAC;EAC/B,IAAI,eAAe,QAAW,OAAO,SAAS,KAAK,MAAM,cAAc,CAAC,CAAC;EAGzE,MAAM,uBAAO,IAAI,IAA0B;EAC3C,KAAK,MAAM,KAAK,UAAU,KAAK,IAAI,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC;EAe5D,OAZiB,SAAS,QAAQ,MAAM;GACtC,IAAI,KAAyB,EAAE,YAAY,CAAC,CAAC;GAC7C,OAAO,IAAI;IACT,IAAI,OAAO,YAAY,OAAO;IAE9B,MAAM,WADS,KAAK,IAAI,EACF,CAAC,EAAE,mBAAmB,UAAU;IACtD,IAAI,aAAa,IAAI;IACrB,KAAK;GACP;GACA,OAAO;EACT,CAEc,CAAC,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC;CAC7C;CAEA,WAA0B;EACxB,KAAK,OAAO,MAAM;EAClB,OAAO,QAAQ,QAAQ;CACzB;CAEA,aAA4B;EAC1B,OAAO,QAAQ,QAAQ;CACzB;AACF;AAEA,SAAS,WAAW,IAA8B;CAChD,OAAO,GAAG,KAAK,MAAO,GAAG,KAAK;AAChC;AAEA,SAAS,eAAe,GAAoC;CAC1D,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WACjE,OAAO;CACT,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG;EACpC,MAAM,IAAI,OAAO,EAAE;EACnB,QACG,MAAM,YAAY,MAAM,YAAY,MAAM,cAC3C,EAAE,OAAO,MAAM,OAAO,MAAM,CAAC;CAEjC;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,MAAoC;CAChE,MAAM,QAA2C,CAAC;CAClD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,UAAU,GACjD,IAAI,eAAe,CAAC,GAAG,MAAM,KAAK;CAEpC,OAAO;EACL,QAAQ,KAAK,YAAY,CAAC,CAAC;EAC3B,cAAc,KAAK,mBAAmB,UAAU;EAChD,MAAM,KAAK;EACX,aAAa,WAAW,KAAK,SAA6B;EAC1D,YAAY,WAAW,KAAK,QAA4B;EACxD,QACE,KAAK,OAAO,SAAS,eAAe,QAChC,UACA,KAAK,OAAO,SAAS,eAAe,KAClC,OACA;EACR,eAAe,KAAK,OAAO,WAAW;EACtC,YAAY,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;CACtD;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.cjs","names":["nodeAsyncHooks","SpanStatusCode","otelTrace","context"],"sources":["../src/testing.ts"],"sourcesContent":["/**\n * Testing Utilities\n *\n * Helpers for testing instrumented code and verifying telemetry.\n * Perfect for integration tests and QA in production validation.\n *\n * @example Verify traces are created\n * ```typescript\n * import { assertTraceCreated, collectTraces } from '@your-org/otel-decorators/testing'\n *\n * describe('UserService', () => {\n * it('should create trace for user creation', async () => {\n * const collector = collectTraces()\n *\n * const service = new UserService()\n * await service.createUser({ email: 'test@example.com' })\n *\n * assertTraceCreated(collector, 'user.createUser')\n * })\n * })\n * ```\n */\n\nimport {\n SpanStatusCode,\n type SpanStatus,\n type Attributes,\n type AttributeValue,\n context,\n trace as otelTrace,\n type Span,\n type SpanContext,\n type TimeInput,\n type Exception,\n type Link,\n type SpanOptions,\n type Context as OtelContext,\n type Tracer,\n type SpanKind,\n} from '@opentelemetry/api';\nimport * as nodeAsyncHooks from 'node:async_hooks';\nimport { type Logger } from './logger';\nimport { configure } from './config';\n\n// Re-export events testing utilities\nexport {\n createEventCollector,\n assertEventTracked,\n assertOutcomeTracked,\n type EventCollector,\n type EventData,\n type EventsFunnelStep,\n type EventsOutcome,\n type EventsValue,\n} from './event-testing';\nexport {\n TestSpanCollector,\n serializeSpan,\n type SerializedSpan,\n} from './test-span-collector';\n\n/**\n * Note: OpenTelemetry exporters and processors have moved to dedicated modules\n * for better semantic clarity.\n *\n * For exporters (ConsoleSpanExporter, InMemorySpanExporter):\n * @see {@link autotel/exporters}\n *\n * For processors (SimpleSpanProcessor, BatchSpanProcessor):\n * @see {@link autotel/processors}\n *\n * This module focuses on high-level testing utilities with assertion helpers\n * and trace collectors.\n *\n * @example High-level testing (recommended)\n * ```typescript\n * import { createTraceCollector, assertTraceCreated } from 'autotel/testing'\n *\n * const collector = createTraceCollector()\n * await myService.doSomething()\n * assertTraceCreated(collector, 'myService.doSomething')\n * ```\n *\n * @example Low-level testing (when you need raw OTel spans)\n * ```typescript\n * import { InMemorySpanExporter } from 'autotel/exporters'\n * import { SimpleSpanProcessor } from 'autotel/processors'\n *\n * const exporter = new InMemorySpanExporter()\n * init({ service: 'test', spanProcessor: new SimpleSpanProcessor(exporter) })\n * ```\n */\n\n/**\n * Simplified span representation for testing\n */\nexport interface TestSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n kind?: SpanKind;\n name: string;\n status: SpanStatus;\n attributes: Attributes;\n events: Array<{ name: string; attributes?: Attributes }>;\n links: Link[];\n startTime: number;\n endTime: number;\n duration: number;\n}\n\nexport interface SpanMatch {\n name?: string;\n traceId?: string;\n spanId?: string;\n parentSpanId?: string;\n kind?: SpanKind;\n attributes?: Record<string, unknown>;\n}\n\n/**\n * In-memory trace collector for testing\n */\nexport interface TraceCollector {\n /** Get all collected spans */\n getSpans(): TestSpan[];\n /** Get spans matching a name */\n getSpansByName(name: string): TestSpan[];\n /** Get spans matching attributes */\n getSpansByAttributes(attributes: Record<string, unknown>): TestSpan[];\n /** Get spans belonging to a trace */\n getSpansByTraceId(traceId: string): TestSpan[];\n /** Get spans without a recorded parent */\n getRootSpans(): TestSpan[];\n /** Get all descendants of a span, ordered by completion */\n getDescendants(rootSpanId: string): TestSpan[];\n /** Find exactly one span or throw an actionable assertion error */\n expectSpan(criteria: string | SpanMatch): TestSpan;\n /** Clear all collected spans */\n clear(): void;\n /** Record a span (internal use) */\n recordSpan(span: TestSpan): void;\n}\n\n/**\n * Create an in-memory trace collector for testing\n *\n * IMPORTANT: This automatically configures the global tracer to record spans.\n * Call this in your test's beforeEach() to ensure proper setup.\n *\n * @example\n * ```typescript\n * import { createTraceCollector } from 'autotel/testing'\n *\n * describe('MyService', () => {\n * let collector: TraceCollector\n *\n * beforeEach(() => {\n * collector = createTraceCollector()\n * })\n *\n * it('should trace operations', async () => {\n * await myService.doSomething()\n *\n * const spans = collector.getSpansByName('myService.doSomething')\n * expect(spans).toHaveLength(1)\n * })\n * })\n * ```\n */\nexport function createTraceCollector(): TraceCollector {\n const spans: TestSpan[] = [];\n const activeMockSpanStorage = new nodeAsyncHooks.AsyncLocalStorage<Span>();\n let nextTraceId = 1;\n let nextSpanId = 1;\n\n const hexId = (value: number, length: number) =>\n value.toString(16).padStart(length, '0');\n\n // Create mock span that captures data and implements full Span interface\n const createMockSpan = (\n name: string,\n startTime: number,\n parentSpanContext?: SpanContext,\n options?: SpanOptions,\n ): Span => {\n const spanContextData: SpanContext = {\n traceId: parentSpanContext?.traceId ?? hexId(nextTraceId++, 32),\n spanId: hexId(nextSpanId++, 16),\n traceFlags: 1,\n isRemote: false,\n };\n const spanData: Partial<TestSpan> = {\n traceId: spanContextData.traceId,\n spanId: spanContextData.spanId,\n parentSpanId: parentSpanContext?.spanId,\n kind: options?.kind,\n name,\n startTime,\n attributes: {},\n events: [],\n links: [],\n status: { code: SpanStatusCode.OK },\n };\n\n const mockSpan: Span = {\n spanContext: () => spanContextData,\n\n setStatus(status: SpanStatus) {\n spanData.status = status;\n return this;\n },\n\n setAttributes(attributes: Attributes) {\n spanData.attributes = { ...spanData.attributes, ...attributes };\n return this;\n },\n\n setAttribute(key: string, value: AttributeValue) {\n spanData.attributes = spanData.attributes || {};\n spanData.attributes[key] = value;\n return this;\n },\n\n addEvent(\n name: string,\n attributesOrStartTime?: Attributes | TimeInput,\n startTime?: TimeInput,\n ) {\n const attributes =\n attributesOrStartTime &&\n !Array.isArray(attributesOrStartTime) &&\n typeof attributesOrStartTime === 'object'\n ? (attributesOrStartTime as Attributes)\n : undefined;\n spanData.events!.push({ name, attributes });\n void startTime;\n return this;\n },\n\n addLink(link: { context: SpanContext; attributes?: Attributes }) {\n spanData.links!.push(link);\n return this;\n },\n\n addLinks(\n links: Array<{ context: SpanContext; attributes?: Attributes }>,\n ) {\n spanData.links!.push(...links);\n return this;\n },\n\n updateName(newName: string) {\n spanData.name = newName;\n return this;\n },\n\n isRecording() {\n return true;\n },\n\n recordException(exception: Exception, time?: TimeInput) {\n void exception;\n void time;\n },\n\n end(endTimeArg?: TimeInput) {\n void endTimeArg;\n const endTime = performance.now();\n spans.push({\n traceId: spanData.traceId!,\n spanId: spanData.spanId!,\n parentSpanId: spanData.parentSpanId,\n kind: spanData.kind,\n name: spanData.name!,\n status: spanData.status!,\n attributes: spanData.attributes || {},\n events: spanData.events || [],\n links: spanData.links || [],\n startTime: spanData.startTime!,\n endTime,\n duration: endTime - spanData.startTime!,\n });\n },\n };\n\n return mockSpan;\n };\n\n // Create mock tracer\n const mockTracer: Tracer = {\n startSpan(name: string, options?: SpanOptions, ctx?: OtelContext): Span {\n const startTime = performance.now();\n const parent =\n (ctx ? otelTrace.getSpan(ctx)?.spanContext() : undefined) ??\n activeMockSpanStorage.getStore()?.spanContext();\n return createMockSpan(name, startTime, parent, options);\n },\n\n startActiveSpan<F extends (span: Span) => unknown>(\n name: string,\n optionsOrFn: SpanOptions | F,\n contextOrFn?: OtelContext | F,\n fn?: F,\n ): ReturnType<F> {\n const callback = (() => {\n if (typeof optionsOrFn === 'function') {\n return optionsOrFn;\n }\n if (typeof contextOrFn === 'function') {\n return contextOrFn;\n }\n if (fn) {\n return fn;\n }\n throw new Error('startActiveSpan requires a callback');\n })();\n\n const startTime = performance.now();\n const suppliedContext =\n typeof optionsOrFn === 'function'\n ? context.active()\n : typeof contextOrFn === 'function' || contextOrFn === undefined\n ? context.active()\n : contextOrFn;\n const parent =\n otelTrace.getSpan(suppliedContext)?.spanContext() ??\n activeMockSpanStorage.getStore()?.spanContext();\n const spanOptions =\n typeof optionsOrFn === 'function' ? undefined : optionsOrFn;\n const mockSpan = createMockSpan(name, startTime, parent, spanOptions);\n\n // Set span as active in context (makes otelTrace.getActiveSpan() work)\n const ctx = otelTrace.setSpan(suppliedContext, mockSpan);\n return activeMockSpanStorage.run(mockSpan, () => {\n return context.with(ctx, () => callback(mockSpan)) as ReturnType<F>;\n });\n },\n };\n\n // Auto-configure global tracer\n configure({ tracer: mockTracer });\n\n return {\n getSpans(): TestSpan[] {\n return [...spans];\n },\n\n getSpansByName(name: string): TestSpan[] {\n return spans.filter((span) => span.name === name);\n },\n\n getSpansByAttributes(attributes: Record<string, unknown>): TestSpan[] {\n return spans.filter((span) => {\n return Object.entries(attributes).every(\n ([key, value]) => span.attributes[key] === value,\n );\n });\n },\n\n getSpansByTraceId(traceId: string): TestSpan[] {\n return spans.filter((span) => span.traceId === traceId);\n },\n\n getRootSpans(): TestSpan[] {\n return spans.filter((span) => span.parentSpanId === undefined);\n },\n\n getDescendants(rootSpanId: string): TestSpan[] {\n const included = new Set([rootSpanId]);\n let changed = true;\n while (changed) {\n changed = false;\n for (const span of spans) {\n if (\n span.parentSpanId &&\n included.has(span.parentSpanId) &&\n !included.has(span.spanId)\n ) {\n included.add(span.spanId);\n changed = true;\n }\n }\n }\n included.delete(rootSpanId);\n return spans.filter((span) => included.has(span.spanId));\n },\n\n expectSpan(criteria: string | SpanMatch): TestSpan {\n const matches =\n typeof criteria === 'string'\n ? spans.filter((span) => span.name === criteria)\n : spans.filter((span) => {\n if (criteria.name !== undefined && span.name !== criteria.name) {\n return false;\n }\n if (\n criteria.traceId !== undefined &&\n span.traceId !== criteria.traceId\n ) {\n return false;\n }\n if (\n criteria.spanId !== undefined &&\n span.spanId !== criteria.spanId\n ) {\n return false;\n }\n if (\n criteria.parentSpanId !== undefined &&\n span.parentSpanId !== criteria.parentSpanId\n ) {\n return false;\n }\n if (criteria.kind !== undefined && span.kind !== criteria.kind) {\n return false;\n }\n return Object.entries(criteria.attributes ?? {}).every(\n ([key, value]) => span.attributes[key] === value,\n );\n });\n if (matches.length !== 1) {\n throw new Error(\n `Expected exactly one span matching ${JSON.stringify(criteria)}, found ${matches.length}`,\n );\n }\n return matches[0]!;\n },\n\n clear(): void {\n spans.length = 0;\n },\n\n recordSpan(span: TestSpan): void {\n spans.push(span);\n },\n };\n}\n\n/**\n * Assert that a trace was created for an operation\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param options - Optional assertion options\n * @throws Error if trace was not found or doesn't match expectations\n *\n * @example\n * ```typescript\n * assertTraceCreated(collector, 'user.createUser')\n * assertTraceCreated(collector, 'user.createUser', {\n * minCount: 1,\n * maxCount: 1,\n * status: SpanStatusCode.OK,\n * attributes: { 'user.email': 'test@example.com' }\n * })\n * ```\n */\nexport function assertTraceCreated(\n collector: TraceCollector,\n operationName: string,\n options?: {\n minCount?: number;\n maxCount?: number;\n status?: SpanStatusCode;\n attributes?: Record<string, unknown>;\n },\n): void {\n const spans = collector.getSpansByName(operationName);\n\n if (options?.minCount !== undefined && spans.length < options.minCount) {\n throw new Error(\n `Expected at least ${options.minCount} traces for ${operationName}, got ${spans.length}`,\n );\n }\n\n if (options?.maxCount !== undefined && spans.length > options.maxCount) {\n throw new Error(\n `Expected at most ${options.maxCount} traces for ${operationName}, got ${spans.length}`,\n );\n }\n\n if (spans.length === 0) {\n throw new Error(`No traces found for operation: ${operationName}`);\n }\n\n if (options?.status !== undefined) {\n const matchingSpans = spans.filter(\n (span) => span.status.code === options.status,\n );\n if (matchingSpans.length === 0) {\n throw new Error(\n `No traces with status ${options.status} found for ${operationName}`,\n );\n }\n }\n\n if (options?.attributes) {\n const matchingSpans = spans.filter((span) => {\n return Object.entries(options.attributes!).every(\n ([key, value]) => span.attributes[key] === value,\n );\n });\n if (matchingSpans.length === 0) {\n throw new Error(\n `No traces with attributes ${JSON.stringify(options.attributes)} found for ${operationName}`,\n );\n }\n }\n}\n\n/**\n * Assert that no errors were logged\n *\n * Use this in smoke tests to verify critical paths don't have errors.\n *\n * @param collector - Trace collector\n * @throws Error if any error traces are found\n *\n * @example\n * ```typescript\n * // Run critical user flows\n * await runSmokeTests()\n *\n * // Verify no errors occurred\n * assertNoErrors(collector)\n * ```\n */\nexport function assertNoErrors(collector: TraceCollector): void {\n const errorSpans = collector\n .getSpans()\n .filter((span) => span.status.code === SpanStatusCode.ERROR);\n\n if (errorSpans.length > 0) {\n const errorSummary = errorSpans\n .map((span) => `${span.name}: ${span.status.message}`)\n .join('\\n');\n throw new Error(`Found ${errorSpans.length} error spans:\\n${errorSummary}`);\n }\n}\n\n/**\n * Assert that a trace was created and succeeded\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n *\n * @example\n * ```typescript\n * assertTraceSucceeded(collector, 'user.createUser')\n * ```\n */\nexport function assertTraceSucceeded(\n collector: TraceCollector,\n operationName: string,\n): void {\n assertTraceCreated(collector, operationName, { status: SpanStatusCode.OK });\n}\n\n/**\n * Assert that a trace was created and failed\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param errorMessage - Optional expected error message\n *\n * @example\n * ```typescript\n * assertTraceFailed(collector, 'user.createUser', 'Invalid email')\n * ```\n */\nexport function assertTraceFailed(\n collector: TraceCollector,\n operationName: string,\n errorMessage?: string,\n): void {\n const spans = collector.getSpansByName(operationName);\n\n if (spans.length === 0) {\n throw new Error(`No traces found for operation: ${operationName}`);\n }\n\n const errorSpans = spans.filter(\n (span) => span.status.code === SpanStatusCode.ERROR,\n );\n\n if (errorSpans.length === 0) {\n throw new Error(`No error traces found for operation: ${operationName}`);\n }\n\n if (errorMessage) {\n const matchingSpans = errorSpans.filter(\n (span) => span.status.message === errorMessage,\n );\n if (matchingSpans.length === 0) {\n throw new Error(\n `No error traces with message \"${errorMessage}\" found for ${operationName}`,\n );\n }\n }\n}\n\n/**\n * In-memory log collector for testing\n */\nexport interface LogCollector {\n /** Get all collected logs */\n getLogs(): LogEntry[];\n /** Get logs by level */\n getLogsByLevel(level: 'info' | 'warn' | 'error' | 'debug'): LogEntry[];\n /** Get logs containing a message */\n getLogsByMessage(message: string): LogEntry[];\n /** Clear all collected logs */\n clear(): void;\n}\n\n/**\n * Log entry\n */\nexport interface LogEntry {\n level: 'info' | 'warn' | 'error' | 'debug';\n message: string;\n extra?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Create an in-memory log collector for testing\n *\n * @example\n * ```typescript\n * const logger = createMockLogger()\n *\n * // Use logger in your code\n * service.log = logger\n * await service.doSomething()\n *\n * // Assert logs were created\n * const logs = logger.getLogs()\n * expect(logs).toHaveLength(2)\n * expect(logs[0].message).toBe('Operation started')\n * ```\n */\nexport function createMockLogger(): Logger & LogCollector {\n const logs: LogEntry[] = [];\n\n // Pino-compatible signature: supports both:\n // - logger.info('message') - string only\n // - logger.info({ extra }, 'message') - object first with optional message\n const createLogMethod = (level: 'info' | 'warn' | 'debug') => {\n return (objOrMsg: Record<string, unknown> | string, msg?: string): void => {\n if (typeof objOrMsg === 'string') {\n // String-only call: logger.info('message')\n logs.push({\n level,\n message: objOrMsg,\n extra: undefined,\n });\n } else {\n // Pino style: logger.info({ extra }, 'message')\n logs.push({\n level,\n message: msg || '',\n extra: objOrMsg,\n });\n }\n };\n };\n\n return {\n info: createLogMethod('info'),\n warn: createLogMethod('warn'),\n debug: createLogMethod('debug'),\n\n error(objOrMsg: Record<string, unknown> | string, msg?: string): void {\n if (typeof objOrMsg === 'string') {\n // String-only call: logger.error('message')\n logs.push({\n level: 'error',\n message: objOrMsg,\n extra: undefined,\n error: undefined,\n });\n return;\n }\n\n // Pino style: logger.error({ err, ...extra }, 'message')\n // Extract err from extra if present (Pino convention)\n const { err, ...rest } = objOrMsg as Record<string, unknown> & {\n err?: unknown;\n };\n logs.push({\n level: 'error',\n message: msg || '',\n error: err instanceof Error ? err : undefined,\n extra:\n err !== undefined && !(err instanceof Error)\n ? { err, ...rest }\n : rest,\n });\n },\n\n getLogs(): LogEntry[] {\n return [...logs];\n },\n\n getLogsByLevel(level: 'info' | 'warn' | 'error' | 'debug'): LogEntry[] {\n return logs.filter((log) => log.level === level);\n },\n\n getLogsByMessage(message: string): LogEntry[] {\n return logs.filter((log) => log.message.includes(message));\n },\n\n clear(): void {\n logs.length = 0;\n },\n };\n}\n\n/**\n * Assert that no error logs were created\n *\n * @param logger - Log collector\n * @throws Error if any error logs are found\n *\n * @example\n * ```typescript\n * assertNoErrorsLogged(logger)\n * ```\n */\nexport function assertNoErrorsLogged(logger: LogCollector): void {\n const errorLogs = logger.getLogsByLevel('error');\n\n if (errorLogs.length > 0) {\n const errorSummary = errorLogs\n .map(\n (log) => `${log.message}${log.error ? ': ' + log.error.message : ''}`,\n )\n .join('\\n');\n throw new Error(`Found ${errorLogs.length} error logs:\\n${errorSummary}`);\n }\n}\n\n/**\n * Wait for a specific trace to be created\n *\n * Useful for async operations where you need to wait for telemetry.\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param timeoutMs - Timeout in milliseconds (default 5000)\n * @returns Promise that resolves when trace is found\n * @throws Error if timeout is reached\n *\n * @example\n * ```typescript\n * // Start async operation\n * const promise = service.doAsyncWork()\n *\n * // Wait for trace\n * await waitForTrace(collector, 'service.doAsyncWork', 1000)\n *\n * // Now you can assert on the trace\n * assertTraceSucceeded(collector, 'service.doAsyncWork')\n * ```\n */\nexport async function waitForTrace(\n collector: TraceCollector,\n operationName: string,\n timeoutMs: number = 5000,\n): Promise<void> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeoutMs) {\n const spans = collector.getSpansByName(operationName);\n if (spans.length > 0) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n\n throw new Error(\n `Timeout waiting for trace ${operationName} after ${timeoutMs}ms`,\n );\n}\n\n/**\n * Get trace duration in milliseconds\n *\n * @param collector - Trace collector\n * @param operationName - Operation name\n * @returns Duration in milliseconds, or undefined if trace not found\n *\n * @example\n * ```typescript\n * const duration = getTraceDuration(collector, 'user.createUser')\n * expect(duration).toBeLessThan(1000) // Should be < 1s\n * ```\n */\nexport function getTraceDuration(\n collector: TraceCollector,\n operationName: string,\n): number | undefined {\n const spans = collector.getSpansByName(operationName);\n if (spans.length === 0) {\n return undefined;\n }\n\n return spans[0]?.duration;\n}\n\n/**\n * Assert that an operation completed within a time threshold\n *\n * Perfect for performance testing and SLO validation.\n *\n * @param collector - Trace collector\n * @param operationName - Operation name\n * @param maxDurationMs - Maximum allowed duration in milliseconds\n * @throws Error if operation took too long\n *\n * @example\n * ```typescript\n * // Verify operation meets SLO\n * await service.createUser({ email: 'test@example.com' })\n * assertTraceDuration(collector, 'user.createUser', 500) // Must be < 500ms\n * ```\n */\nexport function assertTraceDuration(\n collector: TraceCollector,\n operationName: string,\n maxDurationMs: number,\n): void {\n const duration = getTraceDuration(collector, operationName);\n\n if (duration === undefined) {\n throw new Error(`No trace found for operation: ${operationName}`);\n }\n\n if (duration > maxDurationMs) {\n throw new Error(\n `Operation ${operationName} took ${duration.toFixed(2)}ms, exceeding ${maxDurationMs}ms threshold`,\n );\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.d.cts","names":[],"sources":["../src/testing.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAgGiB;EACf;EACA;EACA;EACA,OAAO;EACP;EACA,QAAQ;EACR,YAAY;EACZ,QAAQ;IAAQ;IAAc,aAAa;;EAC3C,OAAO;EACP;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA,OAAO;EACP,aAAa;;;;;UAME;;EAEf,YAAY;;EAEZ,eAAe,eAAe;;EAE9B,qBAAqB,YAAY,0BAA0B;;EAE3D,kBAAkB,kBAAkB;;EAEpC,gBAAgB;;EAEhB,eAAe,qBAAqB;;EAEpC,WAAW,mBAAmB,YAAY;;EAE1C;;EAEA,WAAW,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BH,wBAAwB;;;;;;;;;;;;;;;;;;;;iBAgSxB,mBACd,WAAW,gBACX,uBACA;EACE;EACA;EACA,SAAS;EACT,aAAa;;;;;;;;;;;;;;;;;;;iBA+DD,eAAe,WAAW;;;;;;;;;;;;iBAwB1B,qBACd,WAAW,gBACX;;;;;;;;;;;;;iBAiBc,kBACd,WAAW,gBACX,uBACA;;;;UA+Be;;EAEf,WAAW;;EAEX,eAAe,6CAA6C;;EAE5D,iBAAiB,kBAAkB;;EAEnC;;;;;UAMe;EACf;EACA;EACA,QAAQ;EACR,QAAQ;;;;;;;;;;;;;;;;;;;iBAoBM,oBAAoB,SAAS;;;;;;;;;;;;iBAwF7B,qBAAqB,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;iBAoCvB,aACpB,WAAW,gBACX,uBACA,qBACC;;;;;;;;;;;;;;iBA6Ba,iBACd,WAAW,gBACX;;;;;;;;;;;;;;;;;;iBA2Bc,oBACd,WAAW,gBACX,uBACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAgGiB;EACf;EACA;EACA;EACA,OAAO;EACP;EACA,QAAQ;EACR,YAAY;EACZ,QAAQ;IAAQ;IAAc,aAAa;;EAC3C,OAAO;EACP;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA,OAAO;EACP,aAAa;;;;;UAME;;EAEf,YAAY;;EAEZ,eAAe,eAAe;;EAE9B,qBAAqB,YAAY,0BAA0B;;EAE3D,kBAAkB,kBAAkB;;EAEpC,gBAAgB;;EAEhB,eAAe,qBAAqB;;EAEpC,WAAW,mBAAmB,YAAY;;EAE1C;;EAEA,WAAW,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BH,wBAAwB;;;;;;;;;;;;;;;;;;;;iBAgSxB,mBACd,WAAW,gBACX,uBACA;EACE;EACA;EACA,SAAS;EACT,aAAa;;;;;;;;;;;;;;;;;;;iBA+DD,eAAe,WAAW;;;;;;;;;;;;iBAwB1B,qBACd,WAAW,gBACX;;;;;;;;;;;;;iBAiBc,kBACd,WAAW,gBACX,uBACA;;;;UA+Be;;EAEf,WAAW;;EAEX,eAAe,6CAA6C;;EAE5D,iBAAiB,kBAAkB;;EAEnC;;;;;UAMe;EACf;EACA;EACA,QAAQ;EACR,QAAQ;;;;;;;;;;;;;;;;;;;iBAoBM,oBAAoB,SAAS;;;;;;;;;;;;iBAwF7B,qBAAqB,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;iBAoCvB,aACpB,WAAW,gBACX,uBACA,qBACC;;;;;;;;;;;;;;iBA6Ba,iBACd,WAAW,gBACX;;;;;;;;;;;;;;;;;;iBA2Bc,oBACd,WAAW,gBACX,uBACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.js","names":["otelTrace"],"sources":["../src/testing.ts"],"sourcesContent":["/**\n * Testing Utilities\n *\n * Helpers for testing instrumented code and verifying telemetry.\n * Perfect for integration tests and QA in production validation.\n *\n * @example Verify traces are created\n * ```typescript\n * import { assertTraceCreated, collectTraces } from '@your-org/otel-decorators/testing'\n *\n * describe('UserService', () => {\n * it('should create trace for user creation', async () => {\n * const collector = collectTraces()\n *\n * const service = new UserService()\n * await service.createUser({ email: 'test@example.com' })\n *\n * assertTraceCreated(collector, 'user.createUser')\n * })\n * })\n * ```\n */\n\nimport {\n SpanStatusCode,\n type SpanStatus,\n type Attributes,\n type AttributeValue,\n context,\n trace as otelTrace,\n type Span,\n type SpanContext,\n type TimeInput,\n type Exception,\n type Link,\n type SpanOptions,\n type Context as OtelContext,\n type Tracer,\n type SpanKind,\n} from '@opentelemetry/api';\nimport * as nodeAsyncHooks from 'node:async_hooks';\nimport { type Logger } from './logger';\nimport { configure } from './config';\n\n// Re-export events testing utilities\nexport {\n createEventCollector,\n assertEventTracked,\n assertOutcomeTracked,\n type EventCollector,\n type EventData,\n type EventsFunnelStep,\n type EventsOutcome,\n type EventsValue,\n} from './event-testing';\nexport {\n TestSpanCollector,\n serializeSpan,\n type SerializedSpan,\n} from './test-span-collector';\n\n/**\n * Note: OpenTelemetry exporters and processors have moved to dedicated modules\n * for better semantic clarity.\n *\n * For exporters (ConsoleSpanExporter, InMemorySpanExporter):\n * @see {@link autotel/exporters}\n *\n * For processors (SimpleSpanProcessor, BatchSpanProcessor):\n * @see {@link autotel/processors}\n *\n * This module focuses on high-level testing utilities with assertion helpers\n * and trace collectors.\n *\n * @example High-level testing (recommended)\n * ```typescript\n * import { createTraceCollector, assertTraceCreated } from 'autotel/testing'\n *\n * const collector = createTraceCollector()\n * await myService.doSomething()\n * assertTraceCreated(collector, 'myService.doSomething')\n * ```\n *\n * @example Low-level testing (when you need raw OTel spans)\n * ```typescript\n * import { InMemorySpanExporter } from 'autotel/exporters'\n * import { SimpleSpanProcessor } from 'autotel/processors'\n *\n * const exporter = new InMemorySpanExporter()\n * init({ service: 'test', spanProcessor: new SimpleSpanProcessor(exporter) })\n * ```\n */\n\n/**\n * Simplified span representation for testing\n */\nexport interface TestSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n kind?: SpanKind;\n name: string;\n status: SpanStatus;\n attributes: Attributes;\n events: Array<{ name: string; attributes?: Attributes }>;\n links: Link[];\n startTime: number;\n endTime: number;\n duration: number;\n}\n\nexport interface SpanMatch {\n name?: string;\n traceId?: string;\n spanId?: string;\n parentSpanId?: string;\n kind?: SpanKind;\n attributes?: Record<string, unknown>;\n}\n\n/**\n * In-memory trace collector for testing\n */\nexport interface TraceCollector {\n /** Get all collected spans */\n getSpans(): TestSpan[];\n /** Get spans matching a name */\n getSpansByName(name: string): TestSpan[];\n /** Get spans matching attributes */\n getSpansByAttributes(attributes: Record<string, unknown>): TestSpan[];\n /** Get spans belonging to a trace */\n getSpansByTraceId(traceId: string): TestSpan[];\n /** Get spans without a recorded parent */\n getRootSpans(): TestSpan[];\n /** Get all descendants of a span, ordered by completion */\n getDescendants(rootSpanId: string): TestSpan[];\n /** Find exactly one span or throw an actionable assertion error */\n expectSpan(criteria: string | SpanMatch): TestSpan;\n /** Clear all collected spans */\n clear(): void;\n /** Record a span (internal use) */\n recordSpan(span: TestSpan): void;\n}\n\n/**\n * Create an in-memory trace collector for testing\n *\n * IMPORTANT: This automatically configures the global tracer to record spans.\n * Call this in your test's beforeEach() to ensure proper setup.\n *\n * @example\n * ```typescript\n * import { createTraceCollector } from 'autotel/testing'\n *\n * describe('MyService', () => {\n * let collector: TraceCollector\n *\n * beforeEach(() => {\n * collector = createTraceCollector()\n * })\n *\n * it('should trace operations', async () => {\n * await myService.doSomething()\n *\n * const spans = collector.getSpansByName('myService.doSomething')\n * expect(spans).toHaveLength(1)\n * })\n * })\n * ```\n */\nexport function createTraceCollector(): TraceCollector {\n const spans: TestSpan[] = [];\n const activeMockSpanStorage = new nodeAsyncHooks.AsyncLocalStorage<Span>();\n let nextTraceId = 1;\n let nextSpanId = 1;\n\n const hexId = (value: number, length: number) =>\n value.toString(16).padStart(length, '0');\n\n // Create mock span that captures data and implements full Span interface\n const createMockSpan = (\n name: string,\n startTime: number,\n parentSpanContext?: SpanContext,\n options?: SpanOptions,\n ): Span => {\n const spanContextData: SpanContext = {\n traceId: parentSpanContext?.traceId ?? hexId(nextTraceId++, 32),\n spanId: hexId(nextSpanId++, 16),\n traceFlags: 1,\n isRemote: false,\n };\n const spanData: Partial<TestSpan> = {\n traceId: spanContextData.traceId,\n spanId: spanContextData.spanId,\n parentSpanId: parentSpanContext?.spanId,\n kind: options?.kind,\n name,\n startTime,\n attributes: {},\n events: [],\n links: [],\n status: { code: SpanStatusCode.OK },\n };\n\n const mockSpan: Span = {\n spanContext: () => spanContextData,\n\n setStatus(status: SpanStatus) {\n spanData.status = status;\n return this;\n },\n\n setAttributes(attributes: Attributes) {\n spanData.attributes = { ...spanData.attributes, ...attributes };\n return this;\n },\n\n setAttribute(key: string, value: AttributeValue) {\n spanData.attributes = spanData.attributes || {};\n spanData.attributes[key] = value;\n return this;\n },\n\n addEvent(\n name: string,\n attributesOrStartTime?: Attributes | TimeInput,\n startTime?: TimeInput,\n ) {\n const attributes =\n attributesOrStartTime &&\n !Array.isArray(attributesOrStartTime) &&\n typeof attributesOrStartTime === 'object'\n ? (attributesOrStartTime as Attributes)\n : undefined;\n spanData.events!.push({ name, attributes });\n void startTime;\n return this;\n },\n\n addLink(link: { context: SpanContext; attributes?: Attributes }) {\n spanData.links!.push(link);\n return this;\n },\n\n addLinks(\n links: Array<{ context: SpanContext; attributes?: Attributes }>,\n ) {\n spanData.links!.push(...links);\n return this;\n },\n\n updateName(newName: string) {\n spanData.name = newName;\n return this;\n },\n\n isRecording() {\n return true;\n },\n\n recordException(exception: Exception, time?: TimeInput) {\n void exception;\n void time;\n },\n\n end(endTimeArg?: TimeInput) {\n void endTimeArg;\n const endTime = performance.now();\n spans.push({\n traceId: spanData.traceId!,\n spanId: spanData.spanId!,\n parentSpanId: spanData.parentSpanId,\n kind: spanData.kind,\n name: spanData.name!,\n status: spanData.status!,\n attributes: spanData.attributes || {},\n events: spanData.events || [],\n links: spanData.links || [],\n startTime: spanData.startTime!,\n endTime,\n duration: endTime - spanData.startTime!,\n });\n },\n };\n\n return mockSpan;\n };\n\n // Create mock tracer\n const mockTracer: Tracer = {\n startSpan(name: string, options?: SpanOptions, ctx?: OtelContext): Span {\n const startTime = performance.now();\n const parent =\n (ctx ? otelTrace.getSpan(ctx)?.spanContext() : undefined) ??\n activeMockSpanStorage.getStore()?.spanContext();\n return createMockSpan(name, startTime, parent, options);\n },\n\n startActiveSpan<F extends (span: Span) => unknown>(\n name: string,\n optionsOrFn: SpanOptions | F,\n contextOrFn?: OtelContext | F,\n fn?: F,\n ): ReturnType<F> {\n const callback = (() => {\n if (typeof optionsOrFn === 'function') {\n return optionsOrFn;\n }\n if (typeof contextOrFn === 'function') {\n return contextOrFn;\n }\n if (fn) {\n return fn;\n }\n throw new Error('startActiveSpan requires a callback');\n })();\n\n const startTime = performance.now();\n const suppliedContext =\n typeof optionsOrFn === 'function'\n ? context.active()\n : typeof contextOrFn === 'function' || contextOrFn === undefined\n ? context.active()\n : contextOrFn;\n const parent =\n otelTrace.getSpan(suppliedContext)?.spanContext() ??\n activeMockSpanStorage.getStore()?.spanContext();\n const spanOptions =\n typeof optionsOrFn === 'function' ? undefined : optionsOrFn;\n const mockSpan = createMockSpan(name, startTime, parent, spanOptions);\n\n // Set span as active in context (makes otelTrace.getActiveSpan() work)\n const ctx = otelTrace.setSpan(suppliedContext, mockSpan);\n return activeMockSpanStorage.run(mockSpan, () => {\n return context.with(ctx, () => callback(mockSpan)) as ReturnType<F>;\n });\n },\n };\n\n // Auto-configure global tracer\n configure({ tracer: mockTracer });\n\n return {\n getSpans(): TestSpan[] {\n return [...spans];\n },\n\n getSpansByName(name: string): TestSpan[] {\n return spans.filter((span) => span.name === name);\n },\n\n getSpansByAttributes(attributes: Record<string, unknown>): TestSpan[] {\n return spans.filter((span) => {\n return Object.entries(attributes).every(\n ([key, value]) => span.attributes[key] === value,\n );\n });\n },\n\n getSpansByTraceId(traceId: string): TestSpan[] {\n return spans.filter((span) => span.traceId === traceId);\n },\n\n getRootSpans(): TestSpan[] {\n return spans.filter((span) => span.parentSpanId === undefined);\n },\n\n getDescendants(rootSpanId: string): TestSpan[] {\n const included = new Set([rootSpanId]);\n let changed = true;\n while (changed) {\n changed = false;\n for (const span of spans) {\n if (\n span.parentSpanId &&\n included.has(span.parentSpanId) &&\n !included.has(span.spanId)\n ) {\n included.add(span.spanId);\n changed = true;\n }\n }\n }\n included.delete(rootSpanId);\n return spans.filter((span) => included.has(span.spanId));\n },\n\n expectSpan(criteria: string | SpanMatch): TestSpan {\n const matches =\n typeof criteria === 'string'\n ? spans.filter((span) => span.name === criteria)\n : spans.filter((span) => {\n if (criteria.name !== undefined && span.name !== criteria.name) {\n return false;\n }\n if (\n criteria.traceId !== undefined &&\n span.traceId !== criteria.traceId\n ) {\n return false;\n }\n if (\n criteria.spanId !== undefined &&\n span.spanId !== criteria.spanId\n ) {\n return false;\n }\n if (\n criteria.parentSpanId !== undefined &&\n span.parentSpanId !== criteria.parentSpanId\n ) {\n return false;\n }\n if (criteria.kind !== undefined && span.kind !== criteria.kind) {\n return false;\n }\n return Object.entries(criteria.attributes ?? {}).every(\n ([key, value]) => span.attributes[key] === value,\n );\n });\n if (matches.length !== 1) {\n throw new Error(\n `Expected exactly one span matching ${JSON.stringify(criteria)}, found ${matches.length}`,\n );\n }\n return matches[0]!;\n },\n\n clear(): void {\n spans.length = 0;\n },\n\n recordSpan(span: TestSpan): void {\n spans.push(span);\n },\n };\n}\n\n/**\n * Assert that a trace was created for an operation\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param options - Optional assertion options\n * @throws Error if trace was not found or doesn't match expectations\n *\n * @example\n * ```typescript\n * assertTraceCreated(collector, 'user.createUser')\n * assertTraceCreated(collector, 'user.createUser', {\n * minCount: 1,\n * maxCount: 1,\n * status: SpanStatusCode.OK,\n * attributes: { 'user.email': 'test@example.com' }\n * })\n * ```\n */\nexport function assertTraceCreated(\n collector: TraceCollector,\n operationName: string,\n options?: {\n minCount?: number;\n maxCount?: number;\n status?: SpanStatusCode;\n attributes?: Record<string, unknown>;\n },\n): void {\n const spans = collector.getSpansByName(operationName);\n\n if (options?.minCount !== undefined && spans.length < options.minCount) {\n throw new Error(\n `Expected at least ${options.minCount} traces for ${operationName}, got ${spans.length}`,\n );\n }\n\n if (options?.maxCount !== undefined && spans.length > options.maxCount) {\n throw new Error(\n `Expected at most ${options.maxCount} traces for ${operationName}, got ${spans.length}`,\n );\n }\n\n if (spans.length === 0) {\n throw new Error(`No traces found for operation: ${operationName}`);\n }\n\n if (options?.status !== undefined) {\n const matchingSpans = spans.filter(\n (span) => span.status.code === options.status,\n );\n if (matchingSpans.length === 0) {\n throw new Error(\n `No traces with status ${options.status} found for ${operationName}`,\n );\n }\n }\n\n if (options?.attributes) {\n const matchingSpans = spans.filter((span) => {\n return Object.entries(options.attributes!).every(\n ([key, value]) => span.attributes[key] === value,\n );\n });\n if (matchingSpans.length === 0) {\n throw new Error(\n `No traces with attributes ${JSON.stringify(options.attributes)} found for ${operationName}`,\n );\n }\n }\n}\n\n/**\n * Assert that no errors were logged\n *\n * Use this in smoke tests to verify critical paths don't have errors.\n *\n * @param collector - Trace collector\n * @throws Error if any error traces are found\n *\n * @example\n * ```typescript\n * // Run critical user flows\n * await runSmokeTests()\n *\n * // Verify no errors occurred\n * assertNoErrors(collector)\n * ```\n */\nexport function assertNoErrors(collector: TraceCollector): void {\n const errorSpans = collector\n .getSpans()\n .filter((span) => span.status.code === SpanStatusCode.ERROR);\n\n if (errorSpans.length > 0) {\n const errorSummary = errorSpans\n .map((span) => `${span.name}: ${span.status.message}`)\n .join('\\n');\n throw new Error(`Found ${errorSpans.length} error spans:\\n${errorSummary}`);\n }\n}\n\n/**\n * Assert that a trace was created and succeeded\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n *\n * @example\n * ```typescript\n * assertTraceSucceeded(collector, 'user.createUser')\n * ```\n */\nexport function assertTraceSucceeded(\n collector: TraceCollector,\n operationName: string,\n): void {\n assertTraceCreated(collector, operationName, { status: SpanStatusCode.OK });\n}\n\n/**\n * Assert that a trace was created and failed\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param errorMessage - Optional expected error message\n *\n * @example\n * ```typescript\n * assertTraceFailed(collector, 'user.createUser', 'Invalid email')\n * ```\n */\nexport function assertTraceFailed(\n collector: TraceCollector,\n operationName: string,\n errorMessage?: string,\n): void {\n const spans = collector.getSpansByName(operationName);\n\n if (spans.length === 0) {\n throw new Error(`No traces found for operation: ${operationName}`);\n }\n\n const errorSpans = spans.filter(\n (span) => span.status.code === SpanStatusCode.ERROR,\n );\n\n if (errorSpans.length === 0) {\n throw new Error(`No error traces found for operation: ${operationName}`);\n }\n\n if (errorMessage) {\n const matchingSpans = errorSpans.filter(\n (span) => span.status.message === errorMessage,\n );\n if (matchingSpans.length === 0) {\n throw new Error(\n `No error traces with message \"${errorMessage}\" found for ${operationName}`,\n );\n }\n }\n}\n\n/**\n * In-memory log collector for testing\n */\nexport interface LogCollector {\n /** Get all collected logs */\n getLogs(): LogEntry[];\n /** Get logs by level */\n getLogsByLevel(level: 'info' | 'warn' | 'error' | 'debug'): LogEntry[];\n /** Get logs containing a message */\n getLogsByMessage(message: string): LogEntry[];\n /** Clear all collected logs */\n clear(): void;\n}\n\n/**\n * Log entry\n */\nexport interface LogEntry {\n level: 'info' | 'warn' | 'error' | 'debug';\n message: string;\n extra?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Create an in-memory log collector for testing\n *\n * @example\n * ```typescript\n * const logger = createMockLogger()\n *\n * // Use logger in your code\n * service.log = logger\n * await service.doSomething()\n *\n * // Assert logs were created\n * const logs = logger.getLogs()\n * expect(logs).toHaveLength(2)\n * expect(logs[0].message).toBe('Operation started')\n * ```\n */\nexport function createMockLogger(): Logger & LogCollector {\n const logs: LogEntry[] = [];\n\n // Pino-compatible signature: supports both:\n // - logger.info('message') - string only\n // - logger.info({ extra }, 'message') - object first with optional message\n const createLogMethod = (level: 'info' | 'warn' | 'debug') => {\n return (objOrMsg: Record<string, unknown> | string, msg?: string): void => {\n if (typeof objOrMsg === 'string') {\n // String-only call: logger.info('message')\n logs.push({\n level,\n message: objOrMsg,\n extra: undefined,\n });\n } else {\n // Pino style: logger.info({ extra }, 'message')\n logs.push({\n level,\n message: msg || '',\n extra: objOrMsg,\n });\n }\n };\n };\n\n return {\n info: createLogMethod('info'),\n warn: createLogMethod('warn'),\n debug: createLogMethod('debug'),\n\n error(objOrMsg: Record<string, unknown> | string, msg?: string): void {\n if (typeof objOrMsg === 'string') {\n // String-only call: logger.error('message')\n logs.push({\n level: 'error',\n message: objOrMsg,\n extra: undefined,\n error: undefined,\n });\n return;\n }\n\n // Pino style: logger.error({ err, ...extra }, 'message')\n // Extract err from extra if present (Pino convention)\n const { err, ...rest } = objOrMsg as Record<string, unknown> & {\n err?: unknown;\n };\n logs.push({\n level: 'error',\n message: msg || '',\n error: err instanceof Error ? err : undefined,\n extra:\n err !== undefined && !(err instanceof Error)\n ? { err, ...rest }\n : rest,\n });\n },\n\n getLogs(): LogEntry[] {\n return [...logs];\n },\n\n getLogsByLevel(level: 'info' | 'warn' | 'error' | 'debug'): LogEntry[] {\n return logs.filter((log) => log.level === level);\n },\n\n getLogsByMessage(message: string): LogEntry[] {\n return logs.filter((log) => log.message.includes(message));\n },\n\n clear(): void {\n logs.length = 0;\n },\n };\n}\n\n/**\n * Assert that no error logs were created\n *\n * @param logger - Log collector\n * @throws Error if any error logs are found\n *\n * @example\n * ```typescript\n * assertNoErrorsLogged(logger)\n * ```\n */\nexport function assertNoErrorsLogged(logger: LogCollector): void {\n const errorLogs = logger.getLogsByLevel('error');\n\n if (errorLogs.length > 0) {\n const errorSummary = errorLogs\n .map(\n (log) => `${log.message}${log.error ? ': ' + log.error.message : ''}`,\n )\n .join('\\n');\n throw new Error(`Found ${errorLogs.length} error logs:\\n${errorSummary}`);\n }\n}\n\n/**\n * Wait for a specific trace to be created\n *\n * Useful for async operations where you need to wait for telemetry.\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param timeoutMs - Timeout in milliseconds (default 5000)\n * @returns Promise that resolves when trace is found\n * @throws Error if timeout is reached\n *\n * @example\n * ```typescript\n * // Start async operation\n * const promise = service.doAsyncWork()\n *\n * // Wait for trace\n * await waitForTrace(collector, 'service.doAsyncWork', 1000)\n *\n * // Now you can assert on the trace\n * assertTraceSucceeded(collector, 'service.doAsyncWork')\n * ```\n */\nexport async function waitForTrace(\n collector: TraceCollector,\n operationName: string,\n timeoutMs: number = 5000,\n): Promise<void> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeoutMs) {\n const spans = collector.getSpansByName(operationName);\n if (spans.length > 0) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n\n throw new Error(\n `Timeout waiting for trace ${operationName} after ${timeoutMs}ms`,\n );\n}\n\n/**\n * Get trace duration in milliseconds\n *\n * @param collector - Trace collector\n * @param operationName - Operation name\n * @returns Duration in milliseconds, or undefined if trace not found\n *\n * @example\n * ```typescript\n * const duration = getTraceDuration(collector, 'user.createUser')\n * expect(duration).toBeLessThan(1000) // Should be < 1s\n * ```\n */\nexport function getTraceDuration(\n collector: TraceCollector,\n operationName: string,\n): number | undefined {\n const spans = collector.getSpansByName(operationName);\n if (spans.length === 0) {\n return undefined;\n }\n\n return spans[0]?.duration;\n}\n\n/**\n * Assert that an operation completed within a time threshold\n *\n * Perfect for performance testing and SLO validation.\n *\n * @param collector - Trace collector\n * @param operationName - Operation name\n * @param maxDurationMs - Maximum allowed duration in milliseconds\n * @throws Error if operation took too long\n *\n * @example\n * ```typescript\n * // Verify operation meets SLO\n * await service.createUser({ email: 'test@example.com' })\n * assertTraceDuration(collector, 'user.createUser', 500) // Must be < 500ms\n * ```\n */\nexport function assertTraceDuration(\n collector: TraceCollector,\n operationName: string,\n maxDurationMs: number,\n): void {\n const duration = getTraceDuration(collector, operationName);\n\n if (duration === undefined) {\n throw new Error(`No trace found for operation: ${operationName}`);\n }\n\n if (duration > maxDurationMs) {\n throw new Error(\n `Operation ${operationName} took ${duration.toFixed(2)}ms, exceeding ${maxDurationMs}ms threshold`,\n );\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"trace-context-IuyoPyNq.d.cts","names":[],"sources":["../src/trace-context.ts"],"mappings":";;;;;;UAiHiB;EACf;EACA;EACA;;;;;;KAOU,mBAAiB;;;;UAKZ;;EAEf,aAAa,aAAa,OAAO;;EAEjC,cAAc,OAAO;;EAErB,UAAU;IAAU,MAAM;IAAgB;;;EAE1C,QAAQ,MAAM;;EAEd,SAAS,OAAO;;EAEhB,WAAW;;EAEX;;;;;;;;;;EAUA,YAAY;;;;;;;EAOZ,MAAM,eAAe,0BAA0B,yBAC7C,aAAa,iBACb,OAAO,aAAa;;;;;;;UASP,eACf,iBAAiB;;;;;;EAOjB,WAAW;;;;;;;;;;;;;;;;;;;EAoBX,WAAW,aAAa;;;;;;;;;EAUxB,cAAc;;;;;EAMd,iBAAiB,YAAY;;;;;;;EAQ7B,kBAAkB,iBAAiB,2BAC9B,UAAU,UAAU,uBAAuB,QAAQ;;;;;;;EASxD,kBAAkB,iBAAiB,2BAC9B,UAAU,UACT,+BACA,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAgCX,aACV,iBAAiB,mDACf,mBAAmB,cAAc,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoOpC,oBAAoB,UAAU,yBAC5C;;;;;;EAQa,MAAA,KAAA,aAAa,OAAK,QAAQ;;;;;;;;;EAa1B,MAAA,KAAA,aAAa,IAAE,OAAS,QAAQ;;;;;;;;;;;EAepC,OAAA,GAAC,YACM,aAAa,KAAK,QAAQ,IAAE,WAC7B,QAAQ,YAAY,IAAI,QAAQ,KAAG,gBAC9B,IAAI,QAAQ,OAC3B,IAAI,QAAQ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"trace-context-IuyoPyNq.d.ts","names":[],"sources":["../src/trace-context.ts"],"mappings":";;;;;;UAiHiB;EACf;EACA;EACA;;;;;;KAOU,mBAAiB;;;;UAKZ;;EAEf,aAAa,aAAa,OAAO;;EAEjC,cAAc,OAAO;;EAErB,UAAU;IAAU,MAAM;IAAgB;;;EAE1C,QAAQ,MAAM;;EAEd,SAAS,OAAO;;EAEhB,WAAW;;EAEX;;;;;;;;;;EAUA,YAAY;;;;;;;EAOZ,MAAM,eAAe,0BAA0B,yBAC7C,aAAa,iBACb,OAAO,aAAa;;;;;;;UASP,eACf,iBAAiB;;;;;;EAOjB,WAAW;;;;;;;;;;;;;;;;;;;EAoBX,WAAW,aAAa;;;;;;;;;EAUxB,cAAc;;;;;EAMd,iBAAiB,YAAY;;;;;;;EAQ7B,kBAAkB,iBAAiB,2BAC9B,UAAU,UAAU,uBAAuB,QAAQ;;;;;;;EASxD,kBAAkB,iBAAiB,2BAC9B,UAAU,UACT,+BACA,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAgCX,aACV,iBAAiB,mDACf,mBAAmB,cAAc,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoOpC,oBAAoB,UAAU,yBAC5C;;;;;;EAQa,MAAA,KAAA,aAAa,OAAK,QAAQ;;;;;;;;;EAa1B,MAAA,KAAA,aAAa,IAAE,OAAS,QAAQ;;;;;;;;;;;EAepC,OAAA,GAAC,YACM,aAAa,KAAK,QAAQ,IAAE,WAC7B,QAAQ,YAAY,IAAI,QAAQ,KAAG,gBAC9B,IAAI,QAAQ,OAC3B,IAAI,QAAQ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"trace-factory-validation-BjVAinz6.js","names":[],"sources":["../src/trace-factory-validation.ts"],"sourcesContent":["/**\n * Validate the two stages of Autotel's public factory wrappers without\n * coupling helper modules to the large functional tracing implementation.\n */\nexport function assertTraceFactory(\n helperName: string,\n value: unknown,\n stage: 'factory' | 'result' = 'factory',\n): asserts value is (...args: never[]) => unknown {\n if (typeof value === 'function') return;\n\n throw new TypeError(\n stage === 'factory'\n ? `${helperName}: expected a factory (ctx) => (...args) => result`\n : `${helperName}: factory must return a function; expected (ctx) => (...args) => result`,\n );\n}\n"],"mappings":";;;;;AAIA,SAAgB,mBACd,YACA,OACA,QAA8B,WACkB;CAChD,IAAI,OAAO,UAAU,YAAY;CAEjC,MAAM,IAAI,UACR,UAAU,YACN,GAAG,WAAW,qDACd,GAAG,WAAW,wEACpB;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"trace-factory-validation-Dc8epn87.cjs","names":[],"sources":["../src/trace-factory-validation.ts"],"sourcesContent":["/**\n * Validate the two stages of Autotel's public factory wrappers without\n * coupling helper modules to the large functional tracing implementation.\n */\nexport function assertTraceFactory(\n helperName: string,\n value: unknown,\n stage: 'factory' | 'result' = 'factory',\n): asserts value is (...args: never[]) => unknown {\n if (typeof value === 'function') return;\n\n throw new TypeError(\n stage === 'factory'\n ? `${helperName}: expected a factory (ctx) => (...args) => result`\n : `${helperName}: factory must return a function; expected (ctx) => (...args) => result`,\n );\n}\n"],"mappings":";;;;;;AAIA,SAAgB,mBACd,YACA,OACA,QAA8B,WACkB;CAChD,IAAI,OAAO,UAAU,YAAY;CAEjC,MAAM,IAAI,UACR,UAAU,YACN,GAAG,WAAW,qDACd,GAAG,WAAW,wEACpB;AACF"}