autotel 2.1.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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1946 -0
  3. package/dist/chunk-2LNRY4QK.js +273 -0
  4. package/dist/chunk-2LNRY4QK.js.map +1 -0
  5. package/dist/chunk-3HENGDW2.js +587 -0
  6. package/dist/chunk-3HENGDW2.js.map +1 -0
  7. package/dist/chunk-4OAT42CA.cjs +73 -0
  8. package/dist/chunk-4OAT42CA.cjs.map +1 -0
  9. package/dist/chunk-5GWX5LFW.js +70 -0
  10. package/dist/chunk-5GWX5LFW.js.map +1 -0
  11. package/dist/chunk-5R2M36QB.js +195 -0
  12. package/dist/chunk-5R2M36QB.js.map +1 -0
  13. package/dist/chunk-5ZN622AO.js +73 -0
  14. package/dist/chunk-5ZN622AO.js.map +1 -0
  15. package/dist/chunk-77MSMAUQ.cjs +498 -0
  16. package/dist/chunk-77MSMAUQ.cjs.map +1 -0
  17. package/dist/chunk-ABPEQ6RK.cjs +596 -0
  18. package/dist/chunk-ABPEQ6RK.cjs.map +1 -0
  19. package/dist/chunk-BWYGJKRB.js +95 -0
  20. package/dist/chunk-BWYGJKRB.js.map +1 -0
  21. package/dist/chunk-BZHG5IZ4.js +73 -0
  22. package/dist/chunk-BZHG5IZ4.js.map +1 -0
  23. package/dist/chunk-G7VZBCD6.cjs +35 -0
  24. package/dist/chunk-G7VZBCD6.cjs.map +1 -0
  25. package/dist/chunk-GVLK7YUU.cjs +30 -0
  26. package/dist/chunk-GVLK7YUU.cjs.map +1 -0
  27. package/dist/chunk-HCCXC7XG.js +205 -0
  28. package/dist/chunk-HCCXC7XG.js.map +1 -0
  29. package/dist/chunk-HE6T6FIX.cjs +203 -0
  30. package/dist/chunk-HE6T6FIX.cjs.map +1 -0
  31. package/dist/chunk-KIXWPOCO.cjs +100 -0
  32. package/dist/chunk-KIXWPOCO.cjs.map +1 -0
  33. package/dist/chunk-KVGNW3FC.js +87 -0
  34. package/dist/chunk-KVGNW3FC.js.map +1 -0
  35. package/dist/chunk-LITNXTTT.js +3 -0
  36. package/dist/chunk-LITNXTTT.js.map +1 -0
  37. package/dist/chunk-M4ANN7RL.js +114 -0
  38. package/dist/chunk-M4ANN7RL.js.map +1 -0
  39. package/dist/chunk-NC52UBR2.cjs +32 -0
  40. package/dist/chunk-NC52UBR2.cjs.map +1 -0
  41. package/dist/chunk-NHCNRQD3.cjs +212 -0
  42. package/dist/chunk-NHCNRQD3.cjs.map +1 -0
  43. package/dist/chunk-NZ72VDNY.cjs +4 -0
  44. package/dist/chunk-NZ72VDNY.cjs.map +1 -0
  45. package/dist/chunk-P6JUDYNO.js +57 -0
  46. package/dist/chunk-P6JUDYNO.js.map +1 -0
  47. package/dist/chunk-RJYY7BWX.js +1349 -0
  48. package/dist/chunk-RJYY7BWX.js.map +1 -0
  49. package/dist/chunk-TRI4V5BF.cjs +126 -0
  50. package/dist/chunk-TRI4V5BF.cjs.map +1 -0
  51. package/dist/chunk-UL33I6IS.js +139 -0
  52. package/dist/chunk-UL33I6IS.js.map +1 -0
  53. package/dist/chunk-URRW6M2C.cjs +61 -0
  54. package/dist/chunk-URRW6M2C.cjs.map +1 -0
  55. package/dist/chunk-UY3UYPBZ.cjs +77 -0
  56. package/dist/chunk-UY3UYPBZ.cjs.map +1 -0
  57. package/dist/chunk-W3253FGB.cjs +277 -0
  58. package/dist/chunk-W3253FGB.cjs.map +1 -0
  59. package/dist/chunk-W7LHZVQF.js +26 -0
  60. package/dist/chunk-W7LHZVQF.js.map +1 -0
  61. package/dist/chunk-WBWNM6LB.cjs +1360 -0
  62. package/dist/chunk-WBWNM6LB.cjs.map +1 -0
  63. package/dist/chunk-WFJ7L2RV.js +494 -0
  64. package/dist/chunk-WFJ7L2RV.js.map +1 -0
  65. package/dist/chunk-X4RMFFMR.js +28 -0
  66. package/dist/chunk-X4RMFFMR.js.map +1 -0
  67. package/dist/chunk-Y4Y2S7BM.cjs +92 -0
  68. package/dist/chunk-Y4Y2S7BM.cjs.map +1 -0
  69. package/dist/chunk-YLPNXZFI.cjs +143 -0
  70. package/dist/chunk-YLPNXZFI.cjs.map +1 -0
  71. package/dist/chunk-YTXEZ4SD.cjs +77 -0
  72. package/dist/chunk-YTXEZ4SD.cjs.map +1 -0
  73. package/dist/chunk-Z6ZWNWWR.js +30 -0
  74. package/dist/chunk-Z6ZWNWWR.js.map +1 -0
  75. package/dist/config.cjs +26 -0
  76. package/dist/config.cjs.map +1 -0
  77. package/dist/config.d.cts +75 -0
  78. package/dist/config.d.ts +75 -0
  79. package/dist/config.js +5 -0
  80. package/dist/config.js.map +1 -0
  81. package/dist/db.cjs +233 -0
  82. package/dist/db.cjs.map +1 -0
  83. package/dist/db.d.cts +123 -0
  84. package/dist/db.d.ts +123 -0
  85. package/dist/db.js +228 -0
  86. package/dist/db.js.map +1 -0
  87. package/dist/decorators.cjs +67 -0
  88. package/dist/decorators.cjs.map +1 -0
  89. package/dist/decorators.d.cts +91 -0
  90. package/dist/decorators.d.ts +91 -0
  91. package/dist/decorators.js +65 -0
  92. package/dist/decorators.js.map +1 -0
  93. package/dist/event-subscriber.cjs +6 -0
  94. package/dist/event-subscriber.cjs.map +1 -0
  95. package/dist/event-subscriber.d.cts +116 -0
  96. package/dist/event-subscriber.d.ts +116 -0
  97. package/dist/event-subscriber.js +3 -0
  98. package/dist/event-subscriber.js.map +1 -0
  99. package/dist/event-testing.cjs +21 -0
  100. package/dist/event-testing.cjs.map +1 -0
  101. package/dist/event-testing.d.cts +110 -0
  102. package/dist/event-testing.d.ts +110 -0
  103. package/dist/event-testing.js +4 -0
  104. package/dist/event-testing.js.map +1 -0
  105. package/dist/event.cjs +30 -0
  106. package/dist/event.cjs.map +1 -0
  107. package/dist/event.d.cts +282 -0
  108. package/dist/event.d.ts +282 -0
  109. package/dist/event.js +13 -0
  110. package/dist/event.js.map +1 -0
  111. package/dist/exporters.cjs +17 -0
  112. package/dist/exporters.cjs.map +1 -0
  113. package/dist/exporters.d.cts +1 -0
  114. package/dist/exporters.d.ts +1 -0
  115. package/dist/exporters.js +4 -0
  116. package/dist/exporters.js.map +1 -0
  117. package/dist/functional.cjs +46 -0
  118. package/dist/functional.cjs.map +1 -0
  119. package/dist/functional.d.cts +478 -0
  120. package/dist/functional.d.ts +478 -0
  121. package/dist/functional.js +13 -0
  122. package/dist/functional.js.map +1 -0
  123. package/dist/http.cjs +189 -0
  124. package/dist/http.cjs.map +1 -0
  125. package/dist/http.d.cts +169 -0
  126. package/dist/http.d.ts +169 -0
  127. package/dist/http.js +184 -0
  128. package/dist/http.js.map +1 -0
  129. package/dist/index.cjs +333 -0
  130. package/dist/index.cjs.map +1 -0
  131. package/dist/index.d.cts +758 -0
  132. package/dist/index.d.ts +758 -0
  133. package/dist/index.js +143 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/instrumentation.cjs +182 -0
  136. package/dist/instrumentation.cjs.map +1 -0
  137. package/dist/instrumentation.d.cts +49 -0
  138. package/dist/instrumentation.d.ts +49 -0
  139. package/dist/instrumentation.js +179 -0
  140. package/dist/instrumentation.js.map +1 -0
  141. package/dist/logger.cjs +19 -0
  142. package/dist/logger.cjs.map +1 -0
  143. package/dist/logger.d.cts +146 -0
  144. package/dist/logger.d.ts +146 -0
  145. package/dist/logger.js +6 -0
  146. package/dist/logger.js.map +1 -0
  147. package/dist/metric-helpers.cjs +31 -0
  148. package/dist/metric-helpers.cjs.map +1 -0
  149. package/dist/metric-helpers.d.cts +13 -0
  150. package/dist/metric-helpers.d.ts +13 -0
  151. package/dist/metric-helpers.js +6 -0
  152. package/dist/metric-helpers.js.map +1 -0
  153. package/dist/metric-testing.cjs +21 -0
  154. package/dist/metric-testing.cjs.map +1 -0
  155. package/dist/metric-testing.d.cts +110 -0
  156. package/dist/metric-testing.d.ts +110 -0
  157. package/dist/metric-testing.js +4 -0
  158. package/dist/metric-testing.js.map +1 -0
  159. package/dist/metric.cjs +26 -0
  160. package/dist/metric.cjs.map +1 -0
  161. package/dist/metric.d.cts +240 -0
  162. package/dist/metric.d.ts +240 -0
  163. package/dist/metric.js +9 -0
  164. package/dist/metric.js.map +1 -0
  165. package/dist/processors.cjs +17 -0
  166. package/dist/processors.cjs.map +1 -0
  167. package/dist/processors.d.cts +1 -0
  168. package/dist/processors.d.ts +1 -0
  169. package/dist/processors.js +4 -0
  170. package/dist/processors.js.map +1 -0
  171. package/dist/sampling.cjs +40 -0
  172. package/dist/sampling.cjs.map +1 -0
  173. package/dist/sampling.d.cts +260 -0
  174. package/dist/sampling.d.ts +260 -0
  175. package/dist/sampling.js +7 -0
  176. package/dist/sampling.js.map +1 -0
  177. package/dist/semantic-helpers.cjs +35 -0
  178. package/dist/semantic-helpers.cjs.map +1 -0
  179. package/dist/semantic-helpers.d.cts +442 -0
  180. package/dist/semantic-helpers.d.ts +442 -0
  181. package/dist/semantic-helpers.js +14 -0
  182. package/dist/semantic-helpers.js.map +1 -0
  183. package/dist/tail-sampling-processor.cjs +13 -0
  184. package/dist/tail-sampling-processor.cjs.map +1 -0
  185. package/dist/tail-sampling-processor.d.cts +27 -0
  186. package/dist/tail-sampling-processor.d.ts +27 -0
  187. package/dist/tail-sampling-processor.js +4 -0
  188. package/dist/tail-sampling-processor.js.map +1 -0
  189. package/dist/testing.cjs +286 -0
  190. package/dist/testing.cjs.map +1 -0
  191. package/dist/testing.d.cts +291 -0
  192. package/dist/testing.d.ts +291 -0
  193. package/dist/testing.js +263 -0
  194. package/dist/testing.js.map +1 -0
  195. package/dist/trace-context-DRZdUvVY.d.cts +181 -0
  196. package/dist/trace-context-DRZdUvVY.d.ts +181 -0
  197. package/dist/trace-helpers.cjs +54 -0
  198. package/dist/trace-helpers.cjs.map +1 -0
  199. package/dist/trace-helpers.d.cts +524 -0
  200. package/dist/trace-helpers.d.ts +524 -0
  201. package/dist/trace-helpers.js +5 -0
  202. package/dist/trace-helpers.js.map +1 -0
  203. package/dist/tracer-provider.cjs +21 -0
  204. package/dist/tracer-provider.cjs.map +1 -0
  205. package/dist/tracer-provider.d.cts +169 -0
  206. package/dist/tracer-provider.d.ts +169 -0
  207. package/dist/tracer-provider.js +4 -0
  208. package/dist/tracer-provider.js.map +1 -0
  209. package/package.json +280 -0
  210. package/src/baggage-span-processor.test.ts +202 -0
  211. package/src/baggage-span-processor.ts +98 -0
  212. package/src/circuit-breaker.test.ts +341 -0
  213. package/src/circuit-breaker.ts +184 -0
  214. package/src/config.test.ts +94 -0
  215. package/src/config.ts +169 -0
  216. package/src/db.test.ts +252 -0
  217. package/src/db.ts +447 -0
  218. package/src/decorators.test.ts +203 -0
  219. package/src/decorators.ts +188 -0
  220. package/src/env-config.test.ts +246 -0
  221. package/src/env-config.ts +158 -0
  222. package/src/event-queue.test.ts +222 -0
  223. package/src/event-queue.ts +203 -0
  224. package/src/event-subscriber.ts +136 -0
  225. package/src/event-testing.ts +197 -0
  226. package/src/event.test.ts +718 -0
  227. package/src/event.ts +556 -0
  228. package/src/exporters.ts +96 -0
  229. package/src/functional.test.ts +1059 -0
  230. package/src/functional.ts +2295 -0
  231. package/src/http.test.ts +487 -0
  232. package/src/http.ts +424 -0
  233. package/src/index.ts +158 -0
  234. package/src/init.customization.test.ts +210 -0
  235. package/src/init.integrations.test.ts +366 -0
  236. package/src/init.openllmetry.test.ts +282 -0
  237. package/src/init.protocol.test.ts +215 -0
  238. package/src/init.ts +1426 -0
  239. package/src/instrumentation.test.ts +108 -0
  240. package/src/instrumentation.ts +308 -0
  241. package/src/logger.test.ts +117 -0
  242. package/src/logger.ts +246 -0
  243. package/src/metric-helpers.ts +47 -0
  244. package/src/metric-testing.ts +197 -0
  245. package/src/metric.ts +434 -0
  246. package/src/metrics.test.ts +205 -0
  247. package/src/operation-context.ts +93 -0
  248. package/src/processors.ts +106 -0
  249. package/src/rate-limiter.test.ts +199 -0
  250. package/src/rate-limiter.ts +98 -0
  251. package/src/sampling.test.ts +513 -0
  252. package/src/sampling.ts +428 -0
  253. package/src/semantic-helpers.test.ts +311 -0
  254. package/src/semantic-helpers.ts +584 -0
  255. package/src/shutdown.test.ts +311 -0
  256. package/src/shutdown.ts +222 -0
  257. package/src/stub.integration.test.ts +361 -0
  258. package/src/tail-sampling-processor.test.ts +226 -0
  259. package/src/tail-sampling-processor.ts +51 -0
  260. package/src/testing.ts +670 -0
  261. package/src/trace-context.ts +470 -0
  262. package/src/trace-helpers.new.test.ts +278 -0
  263. package/src/trace-helpers.test.ts +242 -0
  264. package/src/trace-helpers.ts +690 -0
  265. package/src/tracer-provider.test.ts +183 -0
  266. package/src/tracer-provider.ts +266 -0
  267. package/src/track.test.ts +153 -0
  268. package/src/track.ts +120 -0
  269. package/src/validation.test.ts +306 -0
  270. package/src/validation.ts +239 -0
  271. package/src/variable-name-inference.test.ts +178 -0
  272. package/src/variable-name-inference.ts +242 -0
@@ -0,0 +1,73 @@
1
+ // src/metric-testing.ts
2
+ function createMetricsCollector() {
3
+ const events = [];
4
+ const funnelSteps = [];
5
+ const outcomes = [];
6
+ const values = [];
7
+ return {
8
+ getEvents() {
9
+ return [...events];
10
+ },
11
+ getFunnelSteps() {
12
+ return [...funnelSteps];
13
+ },
14
+ getOutcomes() {
15
+ return [...outcomes];
16
+ },
17
+ getValues() {
18
+ return [...values];
19
+ },
20
+ clear() {
21
+ events.length = 0;
22
+ funnelSteps.length = 0;
23
+ outcomes.length = 0;
24
+ values.length = 0;
25
+ },
26
+ recordEvent(event) {
27
+ events.push(event);
28
+ },
29
+ recordFunnelStep(step) {
30
+ funnelSteps.push(step);
31
+ },
32
+ recordOutcome(outcome) {
33
+ outcomes.push(outcome);
34
+ },
35
+ recordValue(value) {
36
+ values.push(value);
37
+ }
38
+ };
39
+ }
40
+ function assertEventTracked(options) {
41
+ const events = options.collector.getEvents();
42
+ const matching = events.filter((e) => e.event === options.eventName);
43
+ if (matching.length === 0) {
44
+ throw new Error(`No events found with name: ${options.eventName}`);
45
+ }
46
+ if (options.attributes) {
47
+ const matchingWithAttrs = matching.filter(
48
+ (e) => Object.entries(options.attributes).every(
49
+ ([key, value]) => e.attributes && e.attributes[key] === value
50
+ )
51
+ );
52
+ if (matchingWithAttrs.length === 0) {
53
+ throw new Error(
54
+ `Event ${options.eventName} found but attributes don't match: ${JSON.stringify(options.attributes)}`
55
+ );
56
+ }
57
+ }
58
+ }
59
+ function assertOutcomeTracked(options) {
60
+ const outcomes = options.collector.getOutcomes();
61
+ const matching = outcomes.filter(
62
+ (o) => o.operation === options.operation && o.status === options.status
63
+ );
64
+ if (matching.length === 0) {
65
+ throw new Error(
66
+ `No outcomes found with operation: ${options.operation} and status: ${options.status}`
67
+ );
68
+ }
69
+ }
70
+
71
+ export { assertEventTracked, assertOutcomeTracked, createMetricsCollector };
72
+ //# sourceMappingURL=chunk-5ZN622AO.js.map
73
+ //# sourceMappingURL=chunk-5ZN622AO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/metric-testing.ts"],"names":[],"mappings":"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","file":"chunk-5ZN622AO.js","sourcesContent":["/**\n * Testing utilities for Metrics\n *\n * Provides in-memory collection of metrics for testing purposes.\n */\n\nimport type {\n EventAttributes,\n FunnelStatus,\n OutcomeStatus,\n} from './event-subscriber';\n\nexport interface MetricsEvent {\n event: string;\n attributes?: EventAttributes;\n service: string;\n timestamp: number;\n}\n\nexport interface MetricsFunnelStep {\n funnel: string;\n status: FunnelStatus;\n attributes?: EventAttributes;\n service: string;\n timestamp: number;\n}\n\nexport interface MetricsOutcome {\n operation: string;\n status: OutcomeStatus;\n attributes?: EventAttributes;\n service: string;\n timestamp: number;\n}\n\nexport interface MetricsValue {\n metric: string;\n value: number;\n attributes?: EventAttributes;\n service: string;\n timestamp: number;\n}\n\n/**\n * In-memory metrics collector for testing\n */\nexport interface MetricsCollector {\n /** Get all collected events */\n getEvents(): MetricsEvent[];\n /** Get all collected funnel steps */\n getFunnelSteps(): MetricsFunnelStep[];\n /** Get all collected outcomes */\n getOutcomes(): MetricsOutcome[];\n /** Get all collected values */\n getValues(): MetricsValue[];\n /** Clear all collected metrics */\n clear(): void;\n /** Record an event (internal use) */\n recordEvent(event: MetricsEvent): void;\n /** Record a funnel step (internal use) */\n recordFunnelStep(step: MetricsFunnelStep): void;\n /** Record an outcome (internal use) */\n recordOutcome(outcome: MetricsOutcome): void;\n /** Record a value (internal use) */\n recordValue(value: MetricsValue): void;\n}\n\n/**\n * Create an in-memory metrics collector for testing\n *\n * @example\n * ```typescript\n * const collector = createMetricsCollector()\n *\n * const metrics = new Metric('test-service', { collector })\n * metrics.trackEvent('order.completed', { orderId: '123' })\n *\n * const event =collector.getEvents()\n * expect(events).toHaveLength(1)\n * expect(events[0].event).toBe('order.completed')\n * ```\n */\nexport function createMetricsCollector(): MetricsCollector {\n const events: MetricsEvent[] = [];\n const funnelSteps: MetricsFunnelStep[] = [];\n const outcomes: MetricsOutcome[] = [];\n const values: MetricsValue[] = [];\n\n return {\n getEvents(): MetricsEvent[] {\n return [...events];\n },\n\n getFunnelSteps(): MetricsFunnelStep[] {\n return [...funnelSteps];\n },\n\n getOutcomes(): MetricsOutcome[] {\n return [...outcomes];\n },\n\n getValues(): MetricsValue[] {\n return [...values];\n },\n\n clear(): void {\n events.length = 0;\n funnelSteps.length = 0;\n outcomes.length = 0;\n values.length = 0;\n },\n\n recordEvent(event: MetricsEvent): void {\n events.push(event);\n },\n\n recordFunnelStep(step: MetricsFunnelStep): void {\n funnelSteps.push(step);\n },\n\n recordOutcome(outcome: MetricsOutcome): void {\n outcomes.push(outcome);\n },\n\n recordValue(value: MetricsValue): void {\n values.push(value);\n },\n };\n}\n\n/**\n * Assert that a metric event was tracked\n *\n * @example\n * ```typescript\n * assertEventTracked({\n * collector,\n * eventName: 'order.completed',\n * attributes: { orderId: '123' }\n * })\n * ```\n */\nexport function assertEventTracked(options: {\n collector: MetricsCollector;\n eventName: string;\n attributes?: Record<string, unknown>;\n}): void {\n const events = options.collector.getEvents();\n const matching = events.filter((e) => e.event === options.eventName);\n\n if (matching.length === 0) {\n throw new Error(`No events found with name: ${options.eventName}`);\n }\n\n if (options.attributes) {\n const matchingWithAttrs = matching.filter((e) =>\n Object.entries(options.attributes!).every(\n ([key, value]) => e.attributes && e.attributes[key] === value,\n ),\n );\n\n if (matchingWithAttrs.length === 0) {\n throw new Error(\n `Event ${options.eventName} found but attributes don't match: ${JSON.stringify(options.attributes)}`,\n );\n }\n }\n}\n\n/**\n * Assert that an outcome was tracked\n *\n * @example\n * ```typescript\n * assertOutcomeTracked({\n * collector,\n * operation: 'payment.process',\n * status: 'success'\n * })\n * ```\n */\nexport function assertOutcomeTracked(options: {\n collector: MetricsCollector;\n operation: string;\n status: 'success' | 'failure' | 'partial';\n}): void {\n const outcomes = options.collector.getOutcomes();\n const matching = outcomes.filter(\n (o) => o.operation === options.operation && o.status === options.status,\n );\n\n if (matching.length === 0) {\n throw new Error(\n `No outcomes found with operation: ${options.operation} and status: ${options.status}`,\n );\n }\n}\n"]}
@@ -0,0 +1,498 @@
1
+ 'use strict';
2
+
3
+ var chunkYLPNXZFI_cjs = require('./chunk-YLPNXZFI.cjs');
4
+ var chunkABPEQ6RK_cjs = require('./chunk-ABPEQ6RK.cjs');
5
+ var api = require('@opentelemetry/api');
6
+
7
+ // src/circuit-breaker.ts
8
+ var DEFAULT_CONFIG = {
9
+ failureThreshold: 5,
10
+ resetTimeout: 3e4,
11
+ // 30 seconds
12
+ windowSize: 6e4
13
+ // 1 minute
14
+ };
15
+ var CircuitState = {
16
+ CLOSED: "CLOSED",
17
+ // Normal operation
18
+ OPEN: "OPEN",
19
+ // Fast-fail mode
20
+ HALF_OPEN: "HALF_OPEN"
21
+ // Testing recovery
22
+ };
23
+ var CircuitBreaker = class {
24
+ state = CircuitState.CLOSED;
25
+ failures = [];
26
+ lastFailureTime = 0;
27
+ config;
28
+ name;
29
+ constructor(name, config) {
30
+ this.name = name;
31
+ this.config = { ...DEFAULT_CONFIG, ...config };
32
+ }
33
+ /**
34
+ * Execute a function with circuit breaker protection
35
+ * Throws CircuitOpenError if circuit is open
36
+ */
37
+ async execute(fn) {
38
+ if (this.state === CircuitState.OPEN) {
39
+ const now = Date.now();
40
+ if (now - this.lastFailureTime >= this.config.resetTimeout) {
41
+ this.state = CircuitState.HALF_OPEN;
42
+ } else {
43
+ throw new CircuitOpenError(
44
+ `Circuit breaker is OPEN for ${this.name}. Will retry in ${Math.ceil((this.config.resetTimeout - (now - this.lastFailureTime)) / 1e3)}s`
45
+ );
46
+ }
47
+ }
48
+ try {
49
+ const result = await fn();
50
+ if (this.state === CircuitState.HALF_OPEN) {
51
+ this.reset();
52
+ }
53
+ return result;
54
+ } catch (error) {
55
+ this.recordFailure(error);
56
+ throw error;
57
+ }
58
+ }
59
+ /**
60
+ * Record a failure and potentially open the circuit
61
+ */
62
+ recordFailure(error) {
63
+ const now = Date.now();
64
+ this.failures = this.failures.filter(
65
+ (f) => now - f.timestamp < this.config.windowSize
66
+ );
67
+ this.failures.push({
68
+ timestamp: now,
69
+ error: error instanceof Error ? error.message : String(error)
70
+ });
71
+ this.lastFailureTime = now;
72
+ if (this.failures.length >= this.config.failureThreshold) {
73
+ if (this.state === CircuitState.HALF_OPEN) {
74
+ this.state = CircuitState.OPEN;
75
+ } else if (this.state === CircuitState.CLOSED) {
76
+ this.state = CircuitState.OPEN;
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Reset the circuit breaker (on success)
82
+ */
83
+ reset() {
84
+ this.state = CircuitState.CLOSED;
85
+ this.failures = [];
86
+ this.lastFailureTime = 0;
87
+ }
88
+ /**
89
+ * Get current state (for monitoring)
90
+ */
91
+ getState() {
92
+ return this.state;
93
+ }
94
+ /**
95
+ * Get failure count in current window
96
+ */
97
+ getFailureCount() {
98
+ const now = Date.now();
99
+ this.failures = this.failures.filter(
100
+ (f) => now - f.timestamp < this.config.windowSize
101
+ );
102
+ return this.failures.length;
103
+ }
104
+ /**
105
+ * Get recent failures (for debugging)
106
+ */
107
+ getRecentFailures() {
108
+ const now = Date.now();
109
+ return this.failures.filter(
110
+ (f) => now - f.timestamp < this.config.windowSize
111
+ );
112
+ }
113
+ /**
114
+ * Manually reset the circuit breaker (for testing or manual intervention)
115
+ */
116
+ forceReset() {
117
+ this.reset();
118
+ }
119
+ /**
120
+ * Manually open the circuit (for testing or manual intervention)
121
+ */
122
+ forceOpen() {
123
+ this.state = CircuitState.OPEN;
124
+ this.lastFailureTime = Date.now();
125
+ }
126
+ };
127
+ var CircuitOpenError = class extends Error {
128
+ constructor(message) {
129
+ super(message);
130
+ this.name = "CircuitOpenError";
131
+ }
132
+ };
133
+
134
+ // src/event.ts
135
+ var Event = class {
136
+ serviceName;
137
+ logger;
138
+ collector;
139
+ subscribers;
140
+ hasSubscribers;
141
+ // Cached for performance
142
+ circuitBreakers;
143
+ // One per subscriber
144
+ /**
145
+ * Create a new Event instance
146
+ *
147
+ * **Note**: Most users should use `init()` + `track()` instead of creating Event instances directly.
148
+ *
149
+ * **Subscriber Resolution**:
150
+ * - If `subscribers` provided in options → uses those (instance override)
151
+ * - If `subscribers` not provided → falls back to subscribers from `init()` (global config)
152
+ * - If neither → no subscribers (events logged only)
153
+ *
154
+ * @param serviceName - Service name for identifying events
155
+ * @param options - Optional configuration (logger, collector, subscribers)
156
+ *
157
+ * @example Recommended: Use track() with init()
158
+ * ```typescript
159
+ * import { init, track } from 'autotel';
160
+ * import { PostHogSubscriber } from 'autotel-subscribers/posthog';
161
+ *
162
+ * init({
163
+ * service: 'checkout',
164
+ * subscribers: [new PostHogSubscriber({ apiKey: 'phc_...' })]
165
+ * });
166
+ *
167
+ * track('purchase.completed', { amount: 99.99 });
168
+ * ```
169
+ *
170
+ * @example Inherit subscribers from init()
171
+ * ```typescript
172
+ * // Uses subscribers configured in init()
173
+ * const event = new Event('checkout');
174
+ * event.trackEvent('purchase.completed', { amount: 99.99 });
175
+ * ```
176
+ *
177
+ * @example Override subscribers for this instance
178
+ * ```typescript
179
+ * import { Event } from 'autotel/event';
180
+ * import { PostHogSubscriber } from 'autotel-subscribers/posthog';
181
+ *
182
+ * // Override: use different subscribers for this instance only
183
+ * const event = new Event('checkout', {
184
+ * subscribers: [new PostHogSubscriber({ apiKey: 'phc_different_project' })]
185
+ * });
186
+ * ```
187
+ */
188
+ constructor(serviceName, options = {}) {
189
+ this.serviceName = serviceName;
190
+ this.logger = options.logger;
191
+ this.collector = options.collector;
192
+ this.subscribers = options.subscribers === void 0 ? chunkABPEQ6RK_cjs.getConfig()?.subscribers || [] : options.subscribers;
193
+ this.hasSubscribers = this.subscribers.length > 0;
194
+ this.circuitBreakers = /* @__PURE__ */ new Map();
195
+ for (const subscriber of this.subscribers) {
196
+ const subscriberName = subscriber.name || "Unknown";
197
+ this.circuitBreakers.set(
198
+ subscriber,
199
+ new CircuitBreaker(subscriberName, {
200
+ failureThreshold: 5,
201
+ resetTimeout: 3e4,
202
+ // 30s
203
+ windowSize: 6e4
204
+ // 1min
205
+ })
206
+ );
207
+ }
208
+ }
209
+ /**
210
+ * Automatically enrich attributes with all available telemetry context
211
+ *
212
+ * Auto-captures:
213
+ * - Resource attributes: service.version, deployment.environment
214
+ * - Trace context: traceId, spanId, correlationId
215
+ * - Operation context: operation.name
216
+ */
217
+ enrichWithTelemetryContext(attributes = {}) {
218
+ const enriched = {
219
+ service: this.serviceName,
220
+ ...attributes
221
+ };
222
+ const config = chunkABPEQ6RK_cjs.getConfig();
223
+ if (config) {
224
+ if (config.version) {
225
+ enriched["service.version"] = config.version;
226
+ }
227
+ if (config.environment) {
228
+ enriched["deployment.environment"] = config.environment;
229
+ }
230
+ }
231
+ const span = api.trace.getActiveSpan();
232
+ const spanContext = span?.spanContext();
233
+ if (spanContext) {
234
+ enriched.traceId = spanContext.traceId;
235
+ enriched.spanId = spanContext.spanId;
236
+ enriched.correlationId = spanContext.traceId.slice(0, 16);
237
+ }
238
+ const operationContext = chunkYLPNXZFI_cjs.getOperationContext();
239
+ if (operationContext) {
240
+ enriched["operation.name"] = operationContext.name;
241
+ }
242
+ return enriched;
243
+ }
244
+ /**
245
+ * Track a business event
246
+ *
247
+ * Use this for tracking user actions, business events, product usage:
248
+ * - "user.signup"
249
+ * - "order.completed"
250
+ * - "feature.used"
251
+ *
252
+ * Events are sent to configured subscribers (PostHog, Mixpanel, etc.).
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * // Track user signup
257
+ * events.trackEvent('user.signup', {
258
+ * userId: '123',
259
+ * plan: 'pro'
260
+ * })
261
+ *
262
+ * // Track order
263
+ * events.trackEvent('order.completed', {
264
+ * orderId: 'ord_123',
265
+ * amount: 99.99
266
+ * })
267
+ * ```
268
+ */
269
+ trackEvent(eventName, attributes) {
270
+ const validationConfig = chunkABPEQ6RK_cjs.getValidationConfig();
271
+ const validated = chunkYLPNXZFI_cjs.validateEvent(
272
+ eventName,
273
+ attributes,
274
+ validationConfig || void 0
275
+ );
276
+ const enrichedAttributes = this.enrichWithTelemetryContext(
277
+ validated.attributes
278
+ );
279
+ this.logger?.info("Event tracked", {
280
+ event: validated.eventName,
281
+ attributes: enrichedAttributes
282
+ });
283
+ this.collector?.recordEvent({
284
+ event: validated.eventName,
285
+ attributes: enrichedAttributes,
286
+ service: this.serviceName,
287
+ timestamp: Date.now()
288
+ });
289
+ if (this.hasSubscribers) {
290
+ void this.notifySubscribers(
291
+ (subscriber) => subscriber.trackEvent(validated.eventName, enrichedAttributes)
292
+ );
293
+ }
294
+ }
295
+ /**
296
+ * Notify all subscribers concurrently without blocking
297
+ * Uses circuit breakers to protect against failing subscribers
298
+ * Uses Promise.allSettled to prevent subscriber errors from affecting other subscribers
299
+ */
300
+ async notifySubscribers(fn) {
301
+ const promises = this.subscribers.map(async (subscriber) => {
302
+ const circuitBreaker = this.circuitBreakers.get(subscriber);
303
+ if (!circuitBreaker) return;
304
+ try {
305
+ await circuitBreaker.execute(() => fn(subscriber));
306
+ } catch (error) {
307
+ if (error instanceof CircuitOpenError) {
308
+ chunkABPEQ6RK_cjs.getLogger().warn(`[Events] ${error.message}`, {
309
+ subscriberName: subscriber.name || "Unknown"
310
+ });
311
+ return;
312
+ }
313
+ chunkABPEQ6RK_cjs.getLogger().error(
314
+ `[Events] Subscriber ${subscriber.name || "Unknown"} failed`,
315
+ error instanceof Error ? error : void 0,
316
+ { subscriberName: subscriber.name || "Unknown" }
317
+ );
318
+ }
319
+ });
320
+ await Promise.allSettled(promises);
321
+ }
322
+ /**
323
+ * Track conversion funnel steps
324
+ *
325
+ * Monitor where users drop off in multi-step processes.
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * // Track signup funnel
330
+ * events.trackFunnelStep('signup', 'started', { userId: '123' })
331
+ * events.trackFunnelStep('signup', 'email_verified', { userId: '123' })
332
+ * events.trackFunnelStep('signup', 'completed', { userId: '123' })
333
+ *
334
+ * // Track checkout flow
335
+ * events.trackFunnelStep('checkout', 'started', { cartValue: 99.99 })
336
+ * events.trackFunnelStep('checkout', 'payment_info', { cartValue: 99.99 })
337
+ * events.trackFunnelStep('checkout', 'completed', { cartValue: 99.99 })
338
+ * ```
339
+ */
340
+ trackFunnelStep(funnelName, status, attributes) {
341
+ const enrichedAttributes = this.enrichWithTelemetryContext(attributes);
342
+ this.logger?.info("Funnel step tracked", {
343
+ funnel: funnelName,
344
+ status,
345
+ attributes: enrichedAttributes
346
+ });
347
+ this.collector?.recordFunnelStep({
348
+ funnel: funnelName,
349
+ status,
350
+ attributes: enrichedAttributes,
351
+ service: this.serviceName,
352
+ timestamp: Date.now()
353
+ });
354
+ if (this.hasSubscribers) {
355
+ void this.notifySubscribers(
356
+ (subscriber) => subscriber.trackFunnelStep(funnelName, status, enrichedAttributes)
357
+ );
358
+ }
359
+ }
360
+ /**
361
+ * Track outcomes (success/failure/partial)
362
+ *
363
+ * Monitor success rates of critical operations.
364
+ *
365
+ * @example
366
+ * ```typescript
367
+ * // Track email delivery
368
+ * events.trackOutcome('email.delivery', 'success', {
369
+ * recipientType: 'user',
370
+ * emailType: 'welcome'
371
+ * })
372
+ *
373
+ * events.trackOutcome('email.delivery', 'failure', {
374
+ * recipientType: 'user',
375
+ * errorCode: 'invalid_email'
376
+ * })
377
+ *
378
+ * // Track payment processing
379
+ * events.trackOutcome('payment.process', 'success', { amount: 99.99 })
380
+ * events.trackOutcome('payment.process', 'failure', { error: 'insufficient_funds' })
381
+ * ```
382
+ */
383
+ trackOutcome(operationName, status, attributes) {
384
+ const enrichedAttributes = this.enrichWithTelemetryContext(attributes);
385
+ this.logger?.info("Outcome tracked", {
386
+ operation: operationName,
387
+ status,
388
+ attributes: enrichedAttributes
389
+ });
390
+ this.collector?.recordOutcome({
391
+ operation: operationName,
392
+ status,
393
+ attributes: enrichedAttributes,
394
+ service: this.serviceName,
395
+ timestamp: Date.now()
396
+ });
397
+ if (this.hasSubscribers) {
398
+ void this.notifySubscribers(
399
+ (subscriber) => subscriber.trackOutcome(operationName, status, enrichedAttributes)
400
+ );
401
+ }
402
+ }
403
+ /**
404
+ * Track value metrics
405
+ *
406
+ * Record numerical values like revenue, transaction amounts,
407
+ * item counts, processing times, engagement scores, etc.
408
+ *
409
+ * @example
410
+ * ```typescript
411
+ * // Track revenue
412
+ * events.trackValue('order.revenue', 149.99, {
413
+ * currency: 'USD',
414
+ * productCategory: 'electronics'
415
+ * })
416
+ *
417
+ * // Track items per cart
418
+ * events.trackValue('cart.item_count', 5, {
419
+ * userId: '123'
420
+ * })
421
+ *
422
+ * // Track processing time
423
+ * events.trackValue('api.response_time', 250, {
424
+ * unit: 'ms',
425
+ * endpoint: '/api/checkout'
426
+ * })
427
+ * ```
428
+ */
429
+ trackValue(metricName, value, attributes) {
430
+ const enrichedAttributes = this.enrichWithTelemetryContext({
431
+ metric: metricName,
432
+ ...attributes
433
+ });
434
+ this.logger?.debug("Value tracked", {
435
+ metric: metricName,
436
+ value,
437
+ attributes: enrichedAttributes
438
+ });
439
+ this.collector?.recordValue({
440
+ metric: metricName,
441
+ value,
442
+ attributes: enrichedAttributes,
443
+ service: this.serviceName,
444
+ timestamp: Date.now()
445
+ });
446
+ if (this.hasSubscribers) {
447
+ void this.notifySubscribers(
448
+ (subscriber) => subscriber.trackValue(metricName, value, enrichedAttributes)
449
+ );
450
+ }
451
+ }
452
+ /**
453
+ * Flush all subscribers and wait for pending events
454
+ *
455
+ * Call this before shutdown to ensure all events are delivered.
456
+ *
457
+ * @example
458
+ * ```typescript
459
+ * const event =new Event('app', { subscribers: [...] });
460
+ *
461
+ * // Before shutdown
462
+ * await events.flush();
463
+ * ```
464
+ */
465
+ async flush() {
466
+ if (!this.hasSubscribers) return;
467
+ const shutdownPromises = this.subscribers.map(async (subscriber) => {
468
+ if (subscriber.shutdown) {
469
+ try {
470
+ await subscriber.shutdown();
471
+ } catch (error) {
472
+ chunkABPEQ6RK_cjs.getLogger().error(
473
+ `[Events] Failed to shutdown subscriber ${subscriber.name || "Unknown"}`,
474
+ error instanceof Error ? error : void 0,
475
+ { subscriberName: subscriber.name || "Unknown" }
476
+ );
477
+ }
478
+ }
479
+ });
480
+ await Promise.allSettled(shutdownPromises);
481
+ }
482
+ };
483
+ var eventsInstances = /* @__PURE__ */ new Map();
484
+ function getEvents(serviceName, logger) {
485
+ if (!eventsInstances.has(serviceName)) {
486
+ eventsInstances.set(serviceName, new Event(serviceName, { logger }));
487
+ }
488
+ return eventsInstances.get(serviceName);
489
+ }
490
+ function resetEvents() {
491
+ eventsInstances.clear();
492
+ }
493
+
494
+ exports.Event = Event;
495
+ exports.getEvents = getEvents;
496
+ exports.resetEvents = resetEvents;
497
+ //# sourceMappingURL=chunk-77MSMAUQ.cjs.map
498
+ //# sourceMappingURL=chunk-77MSMAUQ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/circuit-breaker.ts","../src/event.ts"],"names":["getConfig","trace","getOperationContext","getValidationConfig","validateEvent","getLogger"],"mappings":"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","file":"chunk-77MSMAUQ.cjs","sourcesContent":["/**\n * Circuit breaker for event subscribers\n *\n * Prevents cascading failures by fast-failing when an (subscriber) is unhealthy.\n * Uses the circuit breaker pattern with three states:\n * - CLOSED: Normal operation ((subscriber) working)\n * - OPEN: Fast-fail mode ((subscriber) down)\n * - HALF_OPEN: Testing if (subscriber) recovered\n */\n\nexport interface CircuitBreakerConfig {\n /** Number of failures before opening circuit (default: 5) */\n failureThreshold: number;\n /** Time to wait before trying again in ms (default: 30000 = 30s) */\n resetTimeout: number;\n /** Time window for counting failures in ms (default: 60000 = 1min) */\n windowSize: number;\n}\n\nconst DEFAULT_CONFIG: CircuitBreakerConfig = {\n failureThreshold: 5,\n resetTimeout: 30_000, // 30 seconds\n windowSize: 60_000, // 1 minute\n};\n\nexport type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';\n\nexport const CircuitState = {\n CLOSED: 'CLOSED' as const, // Normal operation\n OPEN: 'OPEN' as const, // Fast-fail mode\n HALF_OPEN: 'HALF_OPEN' as const, // Testing recovery\n} as const;\n\ninterface FailureRecord {\n timestamp: number;\n error: string;\n}\n\n/**\n * Circuit breaker implementation\n *\n * Tracks failures and automatically opens the circuit to prevent\n * overwhelming failing subscribers.\n */\nexport class CircuitBreaker {\n private state: CircuitState = CircuitState.CLOSED;\n private failures: FailureRecord[] = [];\n private lastFailureTime: number = 0;\n private readonly config: CircuitBreakerConfig;\n private readonly name: string;\n\n constructor(name: string, config?: Partial<CircuitBreakerConfig>) {\n this.name = name;\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Execute a function with circuit breaker protection\n * Throws CircuitOpenError if circuit is open\n */\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n // Check if circuit is open\n if (this.state === CircuitState.OPEN) {\n // Check if we should transition to half-open\n const now = Date.now();\n if (now - this.lastFailureTime >= this.config.resetTimeout) {\n this.state = CircuitState.HALF_OPEN;\n } else {\n throw new CircuitOpenError(\n `Circuit breaker is OPEN for ${this.name}. ` +\n `Will retry in ${Math.ceil((this.config.resetTimeout - (now - this.lastFailureTime)) / 1000)}s`,\n );\n }\n }\n\n try {\n const result = await fn();\n\n // Success! Close circuit if it was half-open\n if (this.state === CircuitState.HALF_OPEN) {\n this.reset();\n }\n\n return result;\n } catch (error) {\n this.recordFailure(error);\n throw error;\n }\n }\n\n /**\n * Record a failure and potentially open the circuit\n */\n private recordFailure(error: unknown): void {\n const now = Date.now();\n\n // Remove old failures outside the time window\n this.failures = this.failures.filter(\n (f) => now - f.timestamp < this.config.windowSize,\n );\n\n // Record new failure\n this.failures.push({\n timestamp: now,\n error: error instanceof Error ? error.message : String(error),\n });\n\n this.lastFailureTime = now;\n\n // Check if we should open the circuit\n if (this.failures.length >= this.config.failureThreshold) {\n if (this.state === CircuitState.HALF_OPEN) {\n // Failed during test - reopen circuit\n this.state = CircuitState.OPEN;\n } else if (this.state === CircuitState.CLOSED) {\n // Too many failures - open circuit\n this.state = CircuitState.OPEN;\n }\n }\n }\n\n /**\n * Reset the circuit breaker (on success)\n */\n private reset(): void {\n this.state = CircuitState.CLOSED;\n this.failures = [];\n this.lastFailureTime = 0;\n }\n\n /**\n * Get current state (for monitoring)\n */\n getState(): CircuitState {\n return this.state;\n }\n\n /**\n * Get failure count in current window\n */\n getFailureCount(): number {\n const now = Date.now();\n // Clean up old failures\n this.failures = this.failures.filter(\n (f) => now - f.timestamp < this.config.windowSize,\n );\n return this.failures.length;\n }\n\n /**\n * Get recent failures (for debugging)\n */\n getRecentFailures(): FailureRecord[] {\n const now = Date.now();\n return this.failures.filter(\n (f) => now - f.timestamp < this.config.windowSize,\n );\n }\n\n /**\n * Manually reset the circuit breaker (for testing or manual intervention)\n */\n forceReset(): void {\n this.reset();\n }\n\n /**\n * Manually open the circuit (for testing or manual intervention)\n */\n forceOpen(): void {\n this.state = CircuitState.OPEN;\n this.lastFailureTime = Date.now();\n }\n}\n\n/**\n * Error thrown when circuit is open\n */\nexport class CircuitOpenError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CircuitOpenError';\n }\n}\n","/**\n * Events API for product events platforms\n *\n * Track user behavior, business events, and critical actions.\n * Sends to product events platforms (PostHog, Mixpanel, Amplitude) via subscribers.\n * For business people who think in events/funnels.\n *\n * For OpenTelemetry metrics (Prometheus/Grafana), use the Metrics class instead.\n *\n * @example Recommended: Configure subscribers in init(), use track() function\n * ```typescript\n * import { init, track } from 'autotel';\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * init({\n * service: 'my-app',\n * subscribers: [new PostHogSubscriber({ apiKey: 'phc_...' })]\n * });\n *\n * // Track events - uses subscribers from init()\n * track('application.submitted', { jobId: '123', userId: '456' });\n * ```\n *\n * @example Create Event instance (inherits subscribers from init)\n * ```typescript\n * import { Event } from 'autotel/event';\n *\n * // Uses subscribers configured in init()\n * const event = new Event('job-application');\n * event.trackEvent('application.submitted', { jobId: '123' });\n * ```\n *\n * @example Override subscribers for specific Event instance\n * ```typescript\n * import { Event } from 'autotel/event';\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * // Override: use different subscribers for this instance\n * const event = new Event('job-application', {\n * subscribers: [new PostHogSubscriber({ apiKey: 'phc_different_project' })]\n * });\n *\n * event.trackEvent('application.submitted', { jobId: '123' });\n * ```\n */\n\nimport { trace } from '@opentelemetry/api';\nimport { type Logger } from './logger';\nimport { getLogger, getValidationConfig, getConfig } from './init';\nimport {\n type EventSubscriber,\n type EventAttributes,\n type FunnelStatus,\n type OutcomeStatus,\n} from './event-subscriber';\nimport { type EventCollector } from './event-testing';\nimport { CircuitBreaker, CircuitOpenError } from './circuit-breaker';\nimport { validateEvent } from './validation';\nimport { getOperationContext } from './operation-context';\n\n// Re-export types for convenience\nexport type {\n EventAttributes,\n FunnelStatus,\n OutcomeStatus,\n} from './event-subscriber';\n\n/**\n * Events class for tracking user behavior and product events\n *\n * Track critical indicators such as:\n * - User events (signups, purchases, feature usage)\n * - Conversion funnels (signup → activation → purchase)\n * - Business outcomes (success/failure rates)\n * - Product metrics (revenue, engagement, retention)\n *\n * All events are sent to events platforms via subscribers (PostHog, Mixpanel, etc.).\n * For OpenTelemetry metrics, use the Metrics class instead.\n */\n/**\n * Events options\n */\nexport interface EventsOptions {\n /** Optional logger for audit trail */\n logger?: Logger;\n /** Optional collector for testing (captures events in memory) */\n collector?: EventCollector;\n /**\n * Optional subscribers to send events to other platforms\n * (e.g., PostHog, Mixpanel, Amplitude)\n *\n * **Subscriber Resolution**:\n * - If provided → uses these subscribers (instance override)\n * - If not provided → falls back to subscribers from `init()` (global config)\n * - If neither → no subscribers (events logged only)\n *\n * Install `autotel-subscribers` package for ready-made subscribers\n */\n subscribers?: EventSubscriber[];\n}\n\nexport class Event {\n private serviceName: string;\n private logger?: Logger;\n private collector?: EventCollector;\n private subscribers: EventSubscriber[];\n private hasSubscribers: boolean; // Cached for performance\n private circuitBreakers: Map<EventSubscriber, CircuitBreaker>; // One per subscriber\n\n /**\n * Create a new Event instance\n *\n * **Note**: Most users should use `init()` + `track()` instead of creating Event instances directly.\n *\n * **Subscriber Resolution**:\n * - If `subscribers` provided in options → uses those (instance override)\n * - If `subscribers` not provided → falls back to subscribers from `init()` (global config)\n * - If neither → no subscribers (events logged only)\n *\n * @param serviceName - Service name for identifying events\n * @param options - Optional configuration (logger, collector, subscribers)\n *\n * @example Recommended: Use track() with init()\n * ```typescript\n * import { init, track } from 'autotel';\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * init({\n * service: 'checkout',\n * subscribers: [new PostHogSubscriber({ apiKey: 'phc_...' })]\n * });\n *\n * track('purchase.completed', { amount: 99.99 });\n * ```\n *\n * @example Inherit subscribers from init()\n * ```typescript\n * // Uses subscribers configured in init()\n * const event = new Event('checkout');\n * event.trackEvent('purchase.completed', { amount: 99.99 });\n * ```\n *\n * @example Override subscribers for this instance\n * ```typescript\n * import { Event } from 'autotel/event';\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * // Override: use different subscribers for this instance only\n * const event = new Event('checkout', {\n * subscribers: [new PostHogSubscriber({ apiKey: 'phc_different_project' })]\n * });\n * ```\n */\n constructor(serviceName: string, options: EventsOptions = {}) {\n this.serviceName = serviceName;\n this.logger = options.logger;\n this.collector = options.collector;\n\n // Subscriber resolution: instance-level overrides global init() config\n // If subscribers provided to constructor, use those\n // Otherwise, fall back to subscribers from init()\n this.subscribers =\n options.subscribers === undefined\n ? getConfig()?.subscribers || []\n : options.subscribers;\n\n this.hasSubscribers = this.subscribers.length > 0; // Cache for hot path\n\n // Create circuit breaker for each subscriber\n this.circuitBreakers = new Map();\n for (const subscriber of this.subscribers) {\n const subscriberName = subscriber.name || 'Unknown';\n this.circuitBreakers.set(\n subscriber,\n new CircuitBreaker(subscriberName, {\n failureThreshold: 5,\n resetTimeout: 30_000, // 30s\n windowSize: 60_000, // 1min\n }),\n );\n }\n }\n\n /**\n * Automatically enrich attributes with all available telemetry context\n *\n * Auto-captures:\n * - Resource attributes: service.version, deployment.environment\n * - Trace context: traceId, spanId, correlationId\n * - Operation context: operation.name\n */\n private enrichWithTelemetryContext(\n attributes: EventAttributes = {},\n ): EventAttributes {\n const enriched: EventAttributes = {\n service: this.serviceName,\n ...attributes,\n };\n\n // 1. Resource attributes (service-level context)\n const config = getConfig();\n if (config) {\n if (config.version) {\n enriched['service.version'] = config.version;\n }\n if (config.environment) {\n enriched['deployment.environment'] = config.environment;\n }\n }\n\n // 2. Trace context (if inside a traced operation)\n const span = trace.getActiveSpan();\n const spanContext = span?.spanContext();\n if (spanContext) {\n enriched.traceId = spanContext.traceId;\n enriched.spanId = spanContext.spanId;\n // Add correlation ID (first 16 chars of trace ID) for easier log grouping\n enriched.correlationId = spanContext.traceId.slice(0, 16);\n }\n\n // 3. Operation context (if inside a trace/span)\n const operationContext = getOperationContext();\n if (operationContext) {\n enriched['operation.name'] = operationContext.name;\n }\n\n return enriched;\n }\n\n /**\n * Track a business event\n *\n * Use this for tracking user actions, business events, product usage:\n * - \"user.signup\"\n * - \"order.completed\"\n * - \"feature.used\"\n *\n * Events are sent to configured subscribers (PostHog, Mixpanel, etc.).\n *\n * @example\n * ```typescript\n * // Track user signup\n * events.trackEvent('user.signup', {\n * userId: '123',\n * plan: 'pro'\n * })\n *\n * // Track order\n * events.trackEvent('order.completed', {\n * orderId: 'ord_123',\n * amount: 99.99\n * })\n * ```\n */\n trackEvent(eventName: string, attributes?: EventAttributes): void {\n // Validate and sanitize input (with custom config if provided)\n const validationConfig = getValidationConfig();\n const validated = validateEvent(\n eventName,\n attributes,\n validationConfig || undefined,\n );\n\n // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext(\n validated.attributes,\n );\n\n this.logger?.info('Event tracked', {\n event: validated.eventName,\n attributes: enrichedAttributes,\n });\n\n // Record for testing\n this.collector?.recordEvent({\n event: validated.eventName,\n attributes: enrichedAttributes,\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers (zero overhead if no subscribers)\n // Run in background - don't block event recording\n if (this.hasSubscribers) {\n void this.notifySubscribers((subscriber) =>\n subscriber.trackEvent(validated.eventName, enrichedAttributes),\n );\n }\n }\n\n /**\n * Notify all subscribers concurrently without blocking\n * Uses circuit breakers to protect against failing subscribers\n * Uses Promise.allSettled to prevent subscriber errors from affecting other subscribers\n */\n private async notifySubscribers(\n fn: (subscriber: EventSubscriber) => Promise<void>,\n ): Promise<void> {\n const promises = this.subscribers.map(async (subscriber) => {\n const circuitBreaker = this.circuitBreakers.get(subscriber);\n if (!circuitBreaker) return; // Should never happen\n\n try {\n // Execute with circuit breaker protection\n await circuitBreaker.execute(() => fn(subscriber));\n } catch (error) {\n // Handle circuit open errors (expected behavior when subscriber is down)\n if (error instanceof CircuitOpenError) {\n // Circuit is open - subscriber is down, log at warn level for visibility (same behavior in all environments)\n getLogger().warn(`[Events] ${error.message}`, {\n subscriberName: subscriber.name || 'Unknown',\n });\n return;\n }\n\n // Log other subscriber errors but don't throw - event failures shouldn't break business logic\n getLogger().error(\n `[Events] Subscriber ${subscriber.name || 'Unknown'} failed`,\n error instanceof Error ? error : undefined,\n { subscriberName: subscriber.name || 'Unknown' },\n );\n }\n });\n\n // Wait for all subscribers (success or failure)\n await Promise.allSettled(promises);\n }\n\n /**\n * Track conversion funnel steps\n *\n * Monitor where users drop off in multi-step processes.\n *\n * @example\n * ```typescript\n * // Track signup funnel\n * events.trackFunnelStep('signup', 'started', { userId: '123' })\n * events.trackFunnelStep('signup', 'email_verified', { userId: '123' })\n * events.trackFunnelStep('signup', 'completed', { userId: '123' })\n *\n * // Track checkout flow\n * events.trackFunnelStep('checkout', 'started', { cartValue: 99.99 })\n * events.trackFunnelStep('checkout', 'payment_info', { cartValue: 99.99 })\n * events.trackFunnelStep('checkout', 'completed', { cartValue: 99.99 })\n * ```\n */\n trackFunnelStep(\n funnelName: string,\n status: FunnelStatus,\n attributes?: EventAttributes,\n ): void {\n // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext(attributes);\n\n this.logger?.info('Funnel step tracked', {\n funnel: funnelName,\n status,\n attributes: enrichedAttributes,\n });\n\n // Record for testing\n this.collector?.recordFunnelStep({\n funnel: funnelName,\n status,\n attributes: enrichedAttributes,\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers\n if (this.hasSubscribers) {\n void this.notifySubscribers((subscriber) =>\n subscriber.trackFunnelStep(funnelName, status, enrichedAttributes),\n );\n }\n }\n\n /**\n * Track outcomes (success/failure/partial)\n *\n * Monitor success rates of critical operations.\n *\n * @example\n * ```typescript\n * // Track email delivery\n * events.trackOutcome('email.delivery', 'success', {\n * recipientType: 'user',\n * emailType: 'welcome'\n * })\n *\n * events.trackOutcome('email.delivery', 'failure', {\n * recipientType: 'user',\n * errorCode: 'invalid_email'\n * })\n *\n * // Track payment processing\n * events.trackOutcome('payment.process', 'success', { amount: 99.99 })\n * events.trackOutcome('payment.process', 'failure', { error: 'insufficient_funds' })\n * ```\n */\n trackOutcome(\n operationName: string,\n status: OutcomeStatus,\n attributes?: EventAttributes,\n ): void {\n // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext(attributes);\n\n this.logger?.info('Outcome tracked', {\n operation: operationName,\n status,\n attributes: enrichedAttributes,\n });\n\n // Record for testing\n this.collector?.recordOutcome({\n operation: operationName,\n status,\n attributes: enrichedAttributes,\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers\n if (this.hasSubscribers) {\n void this.notifySubscribers((subscriber) =>\n subscriber.trackOutcome(operationName, status, enrichedAttributes),\n );\n }\n }\n\n /**\n * Track value metrics\n *\n * Record numerical values like revenue, transaction amounts,\n * item counts, processing times, engagement scores, etc.\n *\n * @example\n * ```typescript\n * // Track revenue\n * events.trackValue('order.revenue', 149.99, {\n * currency: 'USD',\n * productCategory: 'electronics'\n * })\n *\n * // Track items per cart\n * events.trackValue('cart.item_count', 5, {\n * userId: '123'\n * })\n *\n * // Track processing time\n * events.trackValue('api.response_time', 250, {\n * unit: 'ms',\n * endpoint: '/api/checkout'\n * })\n * ```\n */\n trackValue(\n metricName: string,\n value: number,\n attributes?: EventAttributes,\n ): void {\n // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext({\n metric: metricName,\n ...attributes,\n });\n\n this.logger?.debug('Value tracked', {\n metric: metricName,\n value,\n attributes: enrichedAttributes,\n });\n\n // Record for testing\n this.collector?.recordValue({\n metric: metricName,\n value,\n attributes: enrichedAttributes,\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers\n if (this.hasSubscribers) {\n void this.notifySubscribers((subscriber) =>\n subscriber.trackValue(metricName, value, enrichedAttributes),\n );\n }\n }\n\n /**\n * Flush all subscribers and wait for pending events\n *\n * Call this before shutdown to ensure all events are delivered.\n *\n * @example\n * ```typescript\n * const event =new Event('app', { subscribers: [...] });\n *\n * // Before shutdown\n * await events.flush();\n * ```\n */\n async flush(): Promise<void> {\n if (!this.hasSubscribers) return;\n\n const shutdownPromises = this.subscribers.map(async (subscriber) => {\n if (subscriber.shutdown) {\n try {\n await subscriber.shutdown();\n } catch (error) {\n getLogger().error(\n `[Events] Failed to shutdown subscriber ${subscriber.name || 'Unknown'}`,\n error instanceof Error ? error : undefined,\n { subscriberName: subscriber.name || 'Unknown' },\n );\n }\n }\n });\n\n await Promise.allSettled(shutdownPromises);\n }\n}\n\n/**\n * Global events instances (singleton pattern)\n */\nconst eventsInstances = new Map<string, Event>();\n\n/**\n * Get or create an Events instance for a service\n *\n * @param serviceName - Service name for identifying events\n * @param logger - Optional logger\n * @returns Events instance\n *\n * @example\n * ```typescript\n * const event =getEvents('job-application')\n * events.trackEvent('application.submitted', { jobId: '123' })\n * ```\n */\nexport function getEvents(serviceName: string, logger?: Logger): Event {\n if (!eventsInstances.has(serviceName)) {\n eventsInstances.set(serviceName, new Event(serviceName, { logger }));\n }\n return eventsInstances.get(serviceName)!;\n}\n\n/**\n * Reset all events instances (mainly for testing)\n */\nexport function resetEvents(): void {\n eventsInstances.clear();\n}\n"]}