autotel 2.26.2 → 3.0.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 (144) hide show
  1. package/README.md +29 -19
  2. package/dist/attributes.d.cts +3 -3
  3. package/dist/attributes.d.ts +3 -3
  4. package/dist/business-baggage.d.cts +1 -1
  5. package/dist/business-baggage.d.ts +1 -1
  6. package/dist/{chunk-YN7USLHW.js → chunk-3QMFLJHJ.js} +11 -10
  7. package/dist/chunk-3QMFLJHJ.js.map +1 -0
  8. package/dist/{chunk-BJ2XPN77.js → chunk-4DAG3RFS.js} +4 -4
  9. package/dist/{chunk-BJ2XPN77.js.map → chunk-4DAG3RFS.js.map} +1 -1
  10. package/dist/chunk-4P6ZOARG.cjs +33 -0
  11. package/dist/chunk-4P6ZOARG.cjs.map +1 -0
  12. package/dist/{chunk-U54FTVFH.js → chunk-7HNQYHK4.js} +3 -3
  13. package/dist/{chunk-U54FTVFH.js.map → chunk-7HNQYHK4.js.map} +1 -1
  14. package/dist/chunk-CJ4PD2TZ.cjs +1207 -0
  15. package/dist/chunk-CJ4PD2TZ.cjs.map +1 -0
  16. package/dist/{chunk-HPUGKUMZ.js → chunk-DAAJLUTO.js} +13 -640
  17. package/dist/chunk-DAAJLUTO.js.map +1 -0
  18. package/dist/{chunk-B3ZHLLMP.js → chunk-DSMSIVTG.js} +2 -2
  19. package/dist/chunk-DSMSIVTG.js.map +1 -0
  20. package/dist/{chunk-6YGUN7IY.cjs → chunk-DWOBIBLY.cjs} +18 -17
  21. package/dist/chunk-DWOBIBLY.cjs.map +1 -0
  22. package/dist/{chunk-QC5MNKVF.js → chunk-IUDXKLS4.js} +13 -12
  23. package/dist/chunk-IUDXKLS4.js.map +1 -0
  24. package/dist/{chunk-OBWXM4NN.cjs → chunk-KHGA4OST.cjs} +15 -14
  25. package/dist/chunk-KHGA4OST.cjs.map +1 -0
  26. package/dist/chunk-KIL5CUN6.js +31 -0
  27. package/dist/chunk-KIL5CUN6.js.map +1 -0
  28. package/dist/{chunk-YEVCD6DR.cjs → chunk-L7JDUDJD.cjs} +7 -7
  29. package/dist/{chunk-YEVCD6DR.cjs.map → chunk-L7JDUDJD.cjs.map} +1 -1
  30. package/dist/{chunk-UTZR7P7E.cjs → chunk-MOK3E54E.cjs} +29 -659
  31. package/dist/chunk-MOK3E54E.cjs.map +1 -0
  32. package/dist/{chunk-GML3FBOT.cjs → chunk-NCSMD3TK.cjs} +2 -2
  33. package/dist/chunk-NCSMD3TK.cjs.map +1 -0
  34. package/dist/chunk-QG3U5ONP.js +1183 -0
  35. package/dist/chunk-QG3U5ONP.js.map +1 -0
  36. package/dist/chunk-SEO6NAQT.js +14 -0
  37. package/dist/chunk-SEO6NAQT.js.map +1 -0
  38. package/dist/chunk-VQTCQKHQ.cjs +17 -0
  39. package/dist/chunk-VQTCQKHQ.cjs.map +1 -0
  40. package/dist/{chunk-WZOKY3PW.cjs → chunk-ZSABTI3C.cjs} +8 -8
  41. package/dist/{chunk-WZOKY3PW.cjs.map → chunk-ZSABTI3C.cjs.map} +1 -1
  42. package/dist/correlation-id.cjs +22 -10
  43. package/dist/correlation-id.js +14 -2
  44. package/dist/decorators.cjs +5 -6
  45. package/dist/decorators.cjs.map +1 -1
  46. package/dist/decorators.d.cts +1 -1
  47. package/dist/decorators.d.ts +1 -1
  48. package/dist/decorators.js +4 -5
  49. package/dist/decorators.js.map +1 -1
  50. package/dist/event.cjs +6 -7
  51. package/dist/event.js +3 -4
  52. package/dist/functional.cjs +11 -12
  53. package/dist/functional.d.cts +1 -1
  54. package/dist/functional.d.ts +1 -1
  55. package/dist/functional.js +4 -5
  56. package/dist/http.cjs +13 -2
  57. package/dist/http.cjs.map +1 -1
  58. package/dist/http.js +12 -1
  59. package/dist/http.js.map +1 -1
  60. package/dist/index.cjs +134 -243
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.d.cts +23 -8
  63. package/dist/index.d.ts +23 -8
  64. package/dist/index.js +52 -176
  65. package/dist/index.js.map +1 -1
  66. package/dist/messaging-adapters.d.cts +1 -1
  67. package/dist/messaging-adapters.d.ts +1 -1
  68. package/dist/messaging-testing.d.cts +1 -1
  69. package/dist/messaging-testing.d.ts +1 -1
  70. package/dist/messaging.cjs +9 -9
  71. package/dist/messaging.d.cts +1 -1
  72. package/dist/messaging.d.ts +1 -1
  73. package/dist/messaging.js +6 -6
  74. package/dist/semantic-helpers.cjs +9 -10
  75. package/dist/semantic-helpers.d.cts +1 -1
  76. package/dist/semantic-helpers.d.ts +1 -1
  77. package/dist/semantic-helpers.js +5 -6
  78. package/dist/{trace-context-t5X1AP-e.d.ts → trace-context-DbGKd1Rn.d.cts} +18 -5
  79. package/dist/{trace-context-t5X1AP-e.d.cts → trace-context-DbGKd1Rn.d.ts} +18 -5
  80. package/dist/trace-helpers.cjs +13 -13
  81. package/dist/trace-helpers.d.cts +2 -2
  82. package/dist/trace-helpers.d.ts +2 -2
  83. package/dist/trace-helpers.js +1 -1
  84. package/dist/{utils-CbUkl8r1.d.cts → utils-BahBCFtJ.d.cts} +1 -1
  85. package/dist/{utils-Buel3cj0.d.ts → utils-CLKwaUlG.d.ts} +1 -1
  86. package/dist/webhook.cjs +19 -10
  87. package/dist/webhook.cjs.map +1 -1
  88. package/dist/webhook.d.cts +1 -1
  89. package/dist/webhook.d.ts +1 -1
  90. package/dist/webhook.js +18 -9
  91. package/dist/webhook.js.map +1 -1
  92. package/dist/workflow-distributed.cjs +23 -19
  93. package/dist/workflow-distributed.cjs.map +1 -1
  94. package/dist/workflow-distributed.d.cts +1 -1
  95. package/dist/workflow-distributed.d.ts +1 -1
  96. package/dist/workflow-distributed.js +21 -17
  97. package/dist/workflow-distributed.js.map +1 -1
  98. package/dist/workflow.cjs +10 -10
  99. package/dist/workflow.d.cts +1 -1
  100. package/dist/workflow.d.ts +1 -1
  101. package/dist/workflow.js +6 -6
  102. package/package.json +38 -38
  103. package/skills/autotel-core/SKILL.md +2 -0
  104. package/skills/autotel-events/SKILL.md +2 -0
  105. package/skills/autotel-frameworks/SKILL.md +2 -0
  106. package/skills/autotel-instrumentation/SKILL.md +2 -0
  107. package/skills/autotel-request-logging/SKILL.md +2 -0
  108. package/skills/autotel-structured-errors/SKILL.md +2 -0
  109. package/src/correlated-events.test.ts +151 -0
  110. package/src/correlated-events.ts +47 -0
  111. package/src/functional.ts +2 -0
  112. package/src/gen-ai-events.ts +14 -5
  113. package/src/index.ts +20 -4
  114. package/src/messaging.ts +10 -9
  115. package/src/request-logger.ts +4 -3
  116. package/src/structured-error.test.ts +83 -1
  117. package/src/structured-error.ts +9 -2
  118. package/src/trace-context.ts +39 -11
  119. package/src/trace-helpers.ts +2 -2
  120. package/src/trace-hybrid.test.ts +42 -0
  121. package/src/trace-hybrid.ts +37 -0
  122. package/src/webhook.ts +16 -7
  123. package/src/workflow-distributed.ts +18 -13
  124. package/src/workflow.ts +7 -6
  125. package/dist/chunk-6YGUN7IY.cjs.map +0 -1
  126. package/dist/chunk-B3ZHLLMP.js.map +0 -1
  127. package/dist/chunk-BBBWDIYQ.js +0 -211
  128. package/dist/chunk-BBBWDIYQ.js.map +0 -1
  129. package/dist/chunk-D5LMF53P.cjs +0 -150
  130. package/dist/chunk-D5LMF53P.cjs.map +0 -1
  131. package/dist/chunk-GML3FBOT.cjs.map +0 -1
  132. package/dist/chunk-HPUGKUMZ.js.map +0 -1
  133. package/dist/chunk-HZ3FYBJG.cjs +0 -217
  134. package/dist/chunk-HZ3FYBJG.cjs.map +0 -1
  135. package/dist/chunk-JSNUWSBH.cjs +0 -62
  136. package/dist/chunk-JSNUWSBH.cjs.map +0 -1
  137. package/dist/chunk-OBWXM4NN.cjs.map +0 -1
  138. package/dist/chunk-QC5MNKVF.js.map +0 -1
  139. package/dist/chunk-S4OFEXLA.js +0 -53
  140. package/dist/chunk-S4OFEXLA.js.map +0 -1
  141. package/dist/chunk-UTZR7P7E.cjs.map +0 -1
  142. package/dist/chunk-WD4RP6IV.js +0 -146
  143. package/dist/chunk-WD4RP6IV.js.map +0 -1
  144. package/dist/chunk-YN7USLHW.js.map +0 -1
@@ -1,640 +1,13 @@
1
- import { setSpanName } from './chunk-B3ZHLLMP.js';
2
- import { validateEvent, runInOperationContext } from './chunk-WD4RP6IV.js';
3
- import { getOrCreateCorrelationId } from './chunk-S4OFEXLA.js';
4
- import { createTraceContext, getActiveContextWithBaggage, getContextStorage, enterOrRun } from './chunk-BBBWDIYQ.js';
5
- import { getValidationConfig, isInitialized, warnIfNotInitialized, getConfig as getConfig$1, getEventsConfig, getLogger, getSdk } from './chunk-W35FVJBC.js';
1
+ import { setSpanName } from './chunk-DSMSIVTG.js';
2
+ import { runInOperationContext } from './chunk-SEO6NAQT.js';
3
+ import { createTraceContext, getActiveContextWithBaggage, getContextStorage, getEventQueue, enterOrRun } from './chunk-QG3U5ONP.js';
4
+ import { getConfig as getConfig$1, getSdk } from './chunk-W35FVJBC.js';
6
5
  import { AlwaysSampler, AUTOTEL_SAMPLING_TAIL_KEEP, AUTOTEL_SAMPLING_TAIL_EVALUATED } from './chunk-DPSA4QLA.js';
7
6
  import { getConfig } from './chunk-J5QENANM.js';
8
- import { trace, SpanStatusCode, context, propagation } from '@opentelemetry/api';
7
+ import { trace as trace$1, SpanStatusCode, context, propagation } from '@opentelemetry/api';
9
8
  import { readFileSync } from 'fs';
10
9
  import { fileURLToPath } from 'url';
11
10
 
12
- // src/rate-limiter.ts
13
- var TokenBucketRateLimiter = class {
14
- tokens;
15
- maxTokens;
16
- refillRate;
17
- // tokens per millisecond
18
- lastRefill;
19
- constructor(config) {
20
- this.maxTokens = config.burstCapacity || config.maxEventsPerSecond * 2;
21
- this.tokens = this.maxTokens;
22
- this.refillRate = config.maxEventsPerSecond / 1e3;
23
- this.lastRefill = Date.now();
24
- }
25
- /**
26
- * Try to consume a token (allow an event)
27
- * Returns true if allowed, false if rate limit exceeded
28
- */
29
- tryConsume(count = 1) {
30
- this.refill();
31
- if (this.tokens >= count) {
32
- this.tokens -= count;
33
- return true;
34
- }
35
- return false;
36
- }
37
- /**
38
- * Wait until a token is available (async rate limiting)
39
- * Returns a promise that resolves when the event can be processed
40
- */
41
- async waitForToken(count = 1) {
42
- this.refill();
43
- if (this.tokens >= count) {
44
- this.tokens -= count;
45
- return;
46
- }
47
- const tokensNeeded = count - this.tokens;
48
- const waitMs = Math.ceil(tokensNeeded / this.refillRate);
49
- await new Promise((resolve) => setTimeout(resolve, waitMs));
50
- return this.waitForToken(count);
51
- }
52
- /**
53
- * Refill tokens based on elapsed time
54
- */
55
- refill() {
56
- const now = Date.now();
57
- const elapsed = now - this.lastRefill;
58
- const tokensToAdd = elapsed * this.refillRate;
59
- this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
60
- this.lastRefill = now;
61
- }
62
- /**
63
- * Get current available tokens (for testing/debugging)
64
- */
65
- getAvailableTokens() {
66
- this.refill();
67
- return Math.floor(this.tokens);
68
- }
69
- /**
70
- * Reset the rate limiter (for testing)
71
- */
72
- reset() {
73
- this.tokens = this.maxTokens;
74
- this.lastRefill = Date.now();
75
- }
76
- };
77
-
78
- // src/event-queue.ts
79
- var DEFAULT_CONFIG = {
80
- maxSize: 5e4,
81
- batchSize: 100,
82
- flushInterval: 1e4,
83
- maxRetries: 3,
84
- rateLimit: {
85
- maxEventsPerSecond: 100,
86
- burstCapacity: 200
87
- }
88
- };
89
- function getSubscriberName(subscriber) {
90
- if (subscriber.name) {
91
- return subscriber.name.toLowerCase();
92
- }
93
- const className = subscriber.constructor?.name || "unknown";
94
- return className.replace(/Subscriber$/i, "").toLowerCase();
95
- }
96
- var EventQueue = class {
97
- queue = [];
98
- flushTimer = null;
99
- config;
100
- subscribers;
101
- rateLimiter;
102
- flushPromise = null;
103
- isShuttingDown = false;
104
- // Metrics
105
- metrics = null;
106
- // Observable callback cleanup functions
107
- observableCleanups = [];
108
- // Subscriber health tracking (for observable gauges)
109
- subscriberHealthy = /* @__PURE__ */ new Map();
110
- constructor(subscribers, config) {
111
- this.subscribers = subscribers;
112
- this.config = { ...DEFAULT_CONFIG, ...config };
113
- this.rateLimiter = this.config.rateLimit ? new TokenBucketRateLimiter(this.config.rateLimit) : null;
114
- for (const subscriber of subscribers) {
115
- const name = getSubscriberName(subscriber);
116
- this.subscriberHealthy.set(name, true);
117
- }
118
- this.initMetrics();
119
- }
120
- /**
121
- * Initialize OTel metrics for queue observability
122
- */
123
- initMetrics() {
124
- const runtimeConfig = getConfig();
125
- const meter = runtimeConfig.meter;
126
- const queueSize = meter.createObservableGauge(
127
- "autotel.event_delivery.queue.size",
128
- {
129
- description: "Current number of events in the delivery queue",
130
- unit: "count"
131
- }
132
- );
133
- const queueSizeCallback = (observableResult) => {
134
- observableResult.observe(this.queue.length);
135
- };
136
- queueSize.addCallback(queueSizeCallback);
137
- this.observableCleanups.push(
138
- () => queueSize.removeCallback(queueSizeCallback)
139
- );
140
- const oldestAge = meter.createObservableGauge(
141
- "autotel.event_delivery.queue.oldest_age_ms",
142
- {
143
- description: "Age of the oldest event in the queue in milliseconds",
144
- unit: "ms"
145
- }
146
- );
147
- const oldestAgeCallback = (observableResult) => {
148
- if (this.queue.length > 0) {
149
- const oldest = this.queue[0];
150
- const ageMs = Date.now() - oldest.timestamp;
151
- observableResult.observe(ageMs);
152
- } else {
153
- observableResult.observe(0);
154
- }
155
- };
156
- oldestAge.addCallback(oldestAgeCallback);
157
- this.observableCleanups.push(
158
- () => oldestAge.removeCallback(oldestAgeCallback)
159
- );
160
- const delivered = meter.createCounter(
161
- "autotel.event_delivery.queue.delivered",
162
- {
163
- description: "Number of events successfully delivered to subscribers",
164
- unit: "count"
165
- }
166
- );
167
- const failed = meter.createCounter("autotel.event_delivery.queue.failed", {
168
- description: "Number of events that failed delivery after all retry attempts",
169
- unit: "count"
170
- });
171
- const dropped = meter.createCounter(
172
- "autotel.event_delivery.queue.dropped",
173
- {
174
- description: "Number of events dropped from the queue",
175
- unit: "count"
176
- }
177
- );
178
- const latency = meter.createHistogram(
179
- "autotel.event_delivery.queue.latency_ms",
180
- {
181
- description: "Event delivery latency from enqueue to successful send",
182
- unit: "ms"
183
- }
184
- );
185
- const subscriberHealth = meter.createObservableGauge(
186
- "autotel.event_delivery.subscriber.health",
187
- {
188
- description: "Subscriber health status (1=healthy, 0=unhealthy)",
189
- unit: "1"
190
- }
191
- );
192
- const subscriberHealthCallback = (observableResult) => {
193
- for (const [subscriberName, isHealthy] of this.subscriberHealthy) {
194
- observableResult.observe(isHealthy ? 1 : 0, {
195
- subscriber: subscriberName
196
- });
197
- }
198
- };
199
- subscriberHealth.addCallback(subscriberHealthCallback);
200
- this.observableCleanups.push(
201
- () => subscriberHealth.removeCallback(subscriberHealthCallback)
202
- );
203
- this.metrics = {
204
- queueSize,
205
- oldestAge,
206
- delivered,
207
- failed,
208
- dropped,
209
- latency,
210
- subscriberHealth
211
- };
212
- }
213
- /**
214
- * Record a dropped event with reason and emit debug breadcrumb
215
- */
216
- recordDropped(reason, event, subscriberName) {
217
- const attrs = { reason };
218
- if (subscriberName) {
219
- attrs.subscriber = subscriberName;
220
- }
221
- this.metrics?.dropped.add(1, attrs);
222
- const logLevel = reason === "payload_invalid" ? "error" : "warn";
223
- const logger = getLogger();
224
- if (logLevel === "error") {
225
- logger.error(
226
- {
227
- eventName: event?.name,
228
- subscriber: subscriberName,
229
- reason,
230
- correlationId: event?._correlationId,
231
- traceId: event?._traceId
232
- },
233
- `[autotel] Event dropped: ${reason}`
234
- );
235
- } else {
236
- logger.warn(
237
- {
238
- eventName: event?.name,
239
- subscriber: subscriberName,
240
- reason,
241
- correlationId: event?._correlationId,
242
- traceId: event?._traceId
243
- },
244
- `[autotel] Event dropped: ${reason}`
245
- );
246
- }
247
- }
248
- /**
249
- * Record permanent delivery failure (after all retries exhausted)
250
- * Increments failed counter and logs error
251
- */
252
- recordFailed(event, subscriberName, error) {
253
- this.metrics?.failed.add(1, { subscriber: subscriberName });
254
- this.subscriberHealthy.set(subscriberName, false);
255
- getLogger().error(
256
- {
257
- eventName: event.name,
258
- subscriber: subscriberName,
259
- correlationId: event._correlationId,
260
- traceId: event._traceId,
261
- err: error
262
- },
263
- `[autotel] Event delivery failed after all retries`
264
- );
265
- }
266
- /**
267
- * Mark subscriber as unhealthy on transient failure (without incrementing failed counter)
268
- * Used during retry attempts - only recordFailed should increment the counter
269
- */
270
- markSubscriberUnhealthy(subscriberName) {
271
- this.subscriberHealthy.set(subscriberName, false);
272
- }
273
- /**
274
- * Record successful delivery
275
- */
276
- recordDelivered(event, subscriberName, startTime) {
277
- const latencyMs = Date.now() - startTime;
278
- this.metrics?.delivered.add(1, { subscriber: subscriberName });
279
- this.metrics?.latency.record(latencyMs, { subscriber: subscriberName });
280
- this.subscriberHealthy.set(subscriberName, true);
281
- }
282
- /**
283
- * Enqueue an event for sending
284
- *
285
- * Backpressure policy:
286
- * - Drops oldest event and logs warning if queue is full (same behavior in all environments)
287
- */
288
- enqueue(event) {
289
- if (this.isShuttingDown) {
290
- this.recordDropped("shutdown", event);
291
- return;
292
- }
293
- if (this.queue.length >= this.config.maxSize) {
294
- const droppedEvent = this.queue.shift();
295
- this.recordDropped("rate_limit", droppedEvent);
296
- getLogger().warn(
297
- {
298
- droppedEvent: droppedEvent?.name
299
- },
300
- `[autotel] Events queue full (${this.config.maxSize} events). Dropping oldest event. Events are being produced faster than they can be sent. Check your subscribers or reduce tracking frequency.`
301
- );
302
- }
303
- const enrichedEvent = {
304
- ...event,
305
- _correlationId: event._correlationId || getOrCreateCorrelationId()
306
- };
307
- this.queue.push(enrichedEvent);
308
- this.scheduleBatchFlush();
309
- }
310
- /**
311
- * Schedule a batch flush if not already scheduled
312
- */
313
- scheduleBatchFlush() {
314
- if (this.flushTimer || this.flushPromise) return;
315
- this.flushTimer = setTimeout(() => {
316
- this.flushTimer = null;
317
- void this.flushBatch();
318
- }, this.config.flushInterval);
319
- }
320
- /**
321
- * Flush a batch of events
322
- * Uses promise-based concurrency control to prevent race conditions
323
- */
324
- async flushBatch() {
325
- if (this.queue.length === 0) return;
326
- if (this.flushPromise) {
327
- await this.flushPromise;
328
- return;
329
- }
330
- this.flushPromise = this.doFlushBatch();
331
- try {
332
- await this.flushPromise;
333
- } finally {
334
- this.flushPromise = null;
335
- if (this.queue.length > 0) {
336
- this.scheduleBatchFlush();
337
- }
338
- }
339
- }
340
- /**
341
- * Internal flush implementation
342
- */
343
- async doFlushBatch() {
344
- const batch = this.queue.splice(0, this.config.batchSize);
345
- await this.sendWithRetry(batch, this.config.maxRetries);
346
- }
347
- /**
348
- * Send events with exponential backoff retry
349
- * Tracks per-event, per-subscriber failures so failed counter reflects actual failed deliveries.
350
- * On retry, only failed (event, subscriber) pairs are re-sent to avoid double-counting delivered.
351
- */
352
- async sendWithRetry(events, retriesLeft, subscribersByEventIndex) {
353
- const failedDeliveries = await this.sendToSubscribers(
354
- events,
355
- subscribersByEventIndex
356
- );
357
- if (failedDeliveries.length > 0) {
358
- if (retriesLeft > 0) {
359
- const failedEventIndices = new Set(
360
- failedDeliveries.map((f) => f.eventIndex)
361
- );
362
- const failedEventIndicesOrdered = [...failedEventIndices].sort(
363
- (a, b) => a - b
364
- );
365
- const eventsToRetry = failedEventIndicesOrdered.map(
366
- (i) => events[i]
367
- );
368
- const failedSubscribersByRetryIndex = /* @__PURE__ */ new Map();
369
- for (let j = 0; j < failedEventIndicesOrdered.length; j++) {
370
- const origIndex = failedEventIndicesOrdered[j];
371
- const set = /* @__PURE__ */ new Set();
372
- for (const { eventIndex, subscriberName } of failedDeliveries) {
373
- if (eventIndex === origIndex) set.add(subscriberName);
374
- }
375
- failedSubscribersByRetryIndex.set(j, set);
376
- }
377
- const delay = Math.pow(2, this.config.maxRetries - retriesLeft) * 1e3;
378
- await new Promise((resolve) => setTimeout(resolve, delay));
379
- return this.sendWithRetry(
380
- eventsToRetry,
381
- retriesLeft - 1,
382
- failedSubscribersByRetryIndex
383
- );
384
- } else {
385
- for (const { eventIndex, subscriberName, error } of failedDeliveries) {
386
- const event = events[eventIndex];
387
- if (event) this.recordFailed(event, subscriberName, error);
388
- }
389
- const failedSubscriberNames = [
390
- ...new Set(failedDeliveries.map((f) => f.subscriberName))
391
- ];
392
- getLogger().error(
393
- {
394
- failedSubscribers: failedSubscriberNames,
395
- retriesAttempted: this.config.maxRetries
396
- },
397
- "[autotel] Failed to send events after retries"
398
- );
399
- }
400
- }
401
- }
402
- /**
403
- * Send events to configured subscribers with rate limiting and metrics.
404
- * When subscribersByEventIndex is provided (retry path), only those subscribers are tried per event.
405
- * Returns per-event, per-subscriber failures (empty if all succeeded).
406
- */
407
- async sendToSubscribers(events, subscribersByEventIndex) {
408
- const failedDeliveries = [];
409
- const sendOne = async (event, eventIndex) => {
410
- const subscriberNames = subscribersByEventIndex?.get(eventIndex);
411
- const failures = await this.sendEventToSubscribers(
412
- event,
413
- subscriberNames ?? void 0
414
- );
415
- for (const failure of failures) {
416
- failedDeliveries.push({
417
- eventIndex,
418
- subscriberName: failure.subscriberName,
419
- error: failure.error
420
- });
421
- }
422
- };
423
- if (!this.rateLimiter) {
424
- for (let i = 0; i < events.length; i++) {
425
- const event = events[i];
426
- if (event) await sendOne(event, i);
427
- }
428
- return failedDeliveries;
429
- }
430
- for (let i = 0; i < events.length; i++) {
431
- await this.rateLimiter.waitForToken();
432
- const event = events[i];
433
- if (event) await sendOne(event, i);
434
- }
435
- return failedDeliveries;
436
- }
437
- /**
438
- * Send a single event to subscribers.
439
- * - When subscriberNames is undefined (initial attempt): send to all subscribers.
440
- * - When subscriberNames is provided (retry): send only to those subscribers (never re-send to healthy ones).
441
- * Returns list of subscribers that failed (empty if all succeeded).
442
- */
443
- async sendEventToSubscribers(event, subscriberNames) {
444
- const startTime = event.timestamp;
445
- const failures = [];
446
- const subscribersToTry = subscriberNames === void 0 ? this.subscribers : this.subscribers.filter(
447
- (s) => subscriberNames.has(getSubscriberName(s))
448
- );
449
- const results = await Promise.allSettled(
450
- subscribersToTry.map(async (subscriber) => {
451
- const subscriberName = getSubscriberName(subscriber);
452
- try {
453
- await subscriber.trackEvent(event.name, event.attributes, {
454
- autotel: event.autotel
455
- });
456
- this.recordDelivered(event, subscriberName, startTime);
457
- return { subscriberName, success: true };
458
- } catch (error) {
459
- this.markSubscriberUnhealthy(subscriberName);
460
- return {
461
- subscriberName,
462
- success: false,
463
- error: error instanceof Error ? error : void 0
464
- };
465
- }
466
- })
467
- );
468
- for (const result of results) {
469
- if (result.status === "fulfilled" && !result.value.success) {
470
- failures.push({
471
- subscriberName: result.value.subscriberName,
472
- error: result.value.error
473
- });
474
- }
475
- }
476
- return failures;
477
- }
478
- /**
479
- * Flush all remaining events. Queue remains usable after flush (e.g. for
480
- * auto-flush at root span end). Use shutdown() when tearing down the queue.
481
- */
482
- async flush() {
483
- if (this.flushTimer) {
484
- clearTimeout(this.flushTimer);
485
- this.flushTimer = null;
486
- }
487
- if (this.flushPromise) {
488
- await this.flushPromise;
489
- }
490
- while (this.queue.length > 0) {
491
- await this.doFlushBatch();
492
- }
493
- }
494
- /**
495
- * Flush remaining events and permanently disable the queue (reject new events).
496
- * Use for process/SDK shutdown; use flush() for periodic or span-end drain.
497
- */
498
- async shutdown() {
499
- this.isShuttingDown = true;
500
- await this.flush();
501
- }
502
- /**
503
- * Cleanup observable metric callbacks to prevent memory leaks
504
- * Call this when destroying the EventQueue instance
505
- */
506
- cleanup() {
507
- for (const cleanupFn of this.observableCleanups) {
508
- try {
509
- cleanupFn();
510
- } catch {
511
- }
512
- }
513
- this.observableCleanups = [];
514
- }
515
- /**
516
- * Get queue size (for testing/debugging)
517
- */
518
- size() {
519
- return this.queue.length;
520
- }
521
- /**
522
- * Get subscriber health status (for testing/debugging)
523
- */
524
- getSubscriberHealth() {
525
- return new Map(this.subscriberHealthy);
526
- }
527
- /**
528
- * Check if a specific subscriber is healthy
529
- */
530
- isSubscriberHealthy(subscriberName) {
531
- return this.subscriberHealthy.get(subscriberName.toLowerCase()) ?? true;
532
- }
533
- /**
534
- * Manually mark a subscriber as healthy or unhealthy
535
- * (used for circuit breaker integration)
536
- */
537
- setSubscriberHealth(subscriberName, healthy) {
538
- this.subscriberHealthy.set(subscriberName.toLowerCase(), healthy);
539
- }
540
- };
541
-
542
- // src/track.ts
543
- var eventsQueue = null;
544
- function buildAutotelContext(span2) {
545
- const eventsConfig = getEventsConfig();
546
- const config = getConfig$1();
547
- const correlationId = getOrCreateCorrelationId();
548
- if (!eventsConfig?.includeTraceContext) {
549
- return {
550
- correlation_id: correlationId
551
- };
552
- }
553
- const autotelContext = {
554
- correlation_id: correlationId
555
- };
556
- const spanContext = span2?.spanContext();
557
- if (spanContext) {
558
- autotelContext.trace_id = spanContext.traceId;
559
- autotelContext.span_id = spanContext.spanId;
560
- autotelContext.trace_flags = spanContext.traceFlags.toString(16).padStart(2, "0");
561
- const traceState = spanContext.traceState;
562
- if (traceState) {
563
- try {
564
- if (typeof traceState.serialize === "function") {
565
- const traceStateStr = traceState.serialize();
566
- if (traceStateStr) {
567
- autotelContext.trace_state = traceStateStr;
568
- }
569
- }
570
- } catch {
571
- }
572
- }
573
- if (eventsConfig.traceUrl && config) {
574
- const traceUrl = eventsConfig.traceUrl({
575
- traceId: spanContext.traceId,
576
- spanId: spanContext.spanId,
577
- correlationId,
578
- serviceName: config.service,
579
- environment: config.environment
580
- });
581
- if (traceUrl) {
582
- autotelContext.trace_url = traceUrl;
583
- }
584
- }
585
- } else {
586
- if (eventsConfig.traceUrl && config) {
587
- const traceUrl = eventsConfig.traceUrl({
588
- correlationId,
589
- serviceName: config.service,
590
- environment: config.environment
591
- });
592
- if (traceUrl) {
593
- autotelContext.trace_url = traceUrl;
594
- }
595
- }
596
- }
597
- return autotelContext;
598
- }
599
- function getOrCreateQueue() {
600
- if (!isInitialized()) {
601
- warnIfNotInitialized("track()");
602
- return null;
603
- }
604
- if (!eventsQueue) {
605
- const config = getConfig$1();
606
- if (!config?.subscribers || config.subscribers.length === 0) {
607
- return null;
608
- }
609
- eventsQueue = new EventQueue(config.subscribers);
610
- }
611
- return eventsQueue;
612
- }
613
- function track(event, data) {
614
- const queue = getOrCreateQueue();
615
- if (!queue) return;
616
- const validationConfig = getValidationConfig();
617
- const validated = validateEvent(event, data, validationConfig || void 0);
618
- const span2 = trace.getActiveSpan();
619
- const enrichedData = span2 ? {
620
- ...validated.attributes,
621
- traceId: span2.spanContext().traceId,
622
- spanId: span2.spanContext().spanId
623
- } : validated.attributes;
624
- const autotelContext = buildAutotelContext(span2);
625
- queue.enqueue({
626
- name: validated.eventName,
627
- attributes: enrichedData,
628
- timestamp: Date.now(),
629
- autotel: autotelContext
630
- });
631
- }
632
- function getEventQueue() {
633
- return eventsQueue;
634
- }
635
- function resetEventQueue() {
636
- eventsQueue = null;
637
- }
638
11
  var inferenceCache = /* @__PURE__ */ new Map();
639
12
  var MAX_CACHE_SIZE = 50;
640
13
  function captureStackTrace() {
@@ -965,7 +338,7 @@ function shouldSkip(key, fn, skip) {
965
338
  return false;
966
339
  }
967
340
  function getCtxValue() {
968
- const activeSpan = trace.getActiveSpan();
341
+ const activeSpan = trace$1.getActiveSpan();
969
342
  if (!activeSpan) return null;
970
343
  return createTraceContext(activeSpan);
971
344
  }
@@ -1031,7 +404,7 @@ function wrapWithTracing(fnFactory, options, variableName) {
1031
404
  return await fn.call(this, ...args);
1032
405
  }
1033
406
  const startTime = performance.now();
1034
- const isRootSpan = options.startNewRoot || trace.getActiveSpan() === void 0;
407
+ const isRootSpan = options.startNewRoot || trace$1.getActiveSpan() === void 0;
1035
408
  const shouldAutoFlush = options.flushOnRootSpanEnd ?? getConfig$1()?.flushOnRootSpanEnd ?? true;
1036
409
  const shouldAutoFlushSpans = getConfig$1()?.forceFlushOnShutdown ?? false;
1037
410
  const flushIfNeeded = async () => {
@@ -1226,7 +599,7 @@ function wrapWithTracingSync(fnFactory, options, variableName) {
1226
599
  return fn.call(this, ...args);
1227
600
  }
1228
601
  const startTime = performance.now();
1229
- const isRootSpan = options.startNewRoot || trace.getActiveSpan() === void 0;
602
+ const isRootSpan = options.startNewRoot || trace$1.getActiveSpan() === void 0;
1230
603
  const shouldAutoFlush = options.flushOnRootSpanEnd ?? getConfig$1()?.flushOnRootSpanEnd ?? true;
1231
604
  const shouldAutoFlushSpans = getConfig$1()?.forceFlushOnShutdown ?? false;
1232
605
  const flushIfNeeded = () => {
@@ -1403,7 +776,7 @@ function executeImmediately(fn, options) {
1403
776
  return fn(createDummyCtx());
1404
777
  }
1405
778
  const startTime = performance.now();
1406
- const isRootSpan = options.startNewRoot || trace.getActiveSpan() === void 0;
779
+ const isRootSpan = options.startNewRoot || trace$1.getActiveSpan() === void 0;
1407
780
  const shouldAutoFlush = options.flushOnRootSpanEnd ?? getConfig$1()?.flushOnRootSpanEnd ?? true;
1408
781
  const shouldAutoFlushSpans = getConfig$1()?.forceFlushOnShutdown ?? false;
1409
782
  const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, {
@@ -1616,7 +989,7 @@ function executeImmediately(fn, options) {
1616
989
  }
1617
990
  );
1618
991
  }
1619
- function trace2(fnOrNameOrOptions, maybeFn) {
992
+ function trace(fnOrNameOrOptions, maybeFn) {
1620
993
  if (typeof fnOrNameOrOptions === "function") {
1621
994
  if (looksLikeTraceFactory(fnOrNameOrOptions) && !isFactoryReturningFunction(
1622
995
  fnOrNameOrOptions
@@ -1806,6 +1179,6 @@ function withBaggage(options) {
1806
1179
  return result;
1807
1180
  }
1808
1181
 
1809
- export { ctx, getEventQueue, instrument, resetEventQueue, span, trace2 as trace, track, withBaggage, withNewContext, withTracing };
1810
- //# sourceMappingURL=chunk-HPUGKUMZ.js.map
1811
- //# sourceMappingURL=chunk-HPUGKUMZ.js.map
1182
+ export { ctx, instrument, span, trace, withBaggage, withNewContext, withTracing };
1183
+ //# sourceMappingURL=chunk-DAAJLUTO.js.map
1184
+ //# sourceMappingURL=chunk-DAAJLUTO.js.map