autotel 2.26.3 → 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.
- package/README.md +29 -19
- package/dist/attributes.d.cts +3 -3
- package/dist/attributes.d.ts +3 -3
- package/dist/business-baggage.d.cts +1 -1
- package/dist/business-baggage.d.ts +1 -1
- package/dist/{chunk-YN7USLHW.js → chunk-3QMFLJHJ.js} +11 -10
- package/dist/chunk-3QMFLJHJ.js.map +1 -0
- package/dist/{chunk-BJ2XPN77.js → chunk-4DAG3RFS.js} +4 -4
- package/dist/{chunk-BJ2XPN77.js.map → chunk-4DAG3RFS.js.map} +1 -1
- package/dist/chunk-4P6ZOARG.cjs +33 -0
- package/dist/chunk-4P6ZOARG.cjs.map +1 -0
- package/dist/{chunk-U54FTVFH.js → chunk-7HNQYHK4.js} +3 -3
- package/dist/{chunk-U54FTVFH.js.map → chunk-7HNQYHK4.js.map} +1 -1
- package/dist/chunk-CJ4PD2TZ.cjs +1207 -0
- package/dist/chunk-CJ4PD2TZ.cjs.map +1 -0
- package/dist/{chunk-HPUGKUMZ.js → chunk-DAAJLUTO.js} +13 -640
- package/dist/chunk-DAAJLUTO.js.map +1 -0
- package/dist/{chunk-B3ZHLLMP.js → chunk-DSMSIVTG.js} +2 -2
- package/dist/chunk-DSMSIVTG.js.map +1 -0
- package/dist/{chunk-6YGUN7IY.cjs → chunk-DWOBIBLY.cjs} +18 -17
- package/dist/chunk-DWOBIBLY.cjs.map +1 -0
- package/dist/{chunk-QC5MNKVF.js → chunk-IUDXKLS4.js} +13 -12
- package/dist/chunk-IUDXKLS4.js.map +1 -0
- package/dist/{chunk-OBWXM4NN.cjs → chunk-KHGA4OST.cjs} +15 -14
- package/dist/chunk-KHGA4OST.cjs.map +1 -0
- package/dist/chunk-KIL5CUN6.js +31 -0
- package/dist/chunk-KIL5CUN6.js.map +1 -0
- package/dist/{chunk-YEVCD6DR.cjs → chunk-L7JDUDJD.cjs} +7 -7
- package/dist/{chunk-YEVCD6DR.cjs.map → chunk-L7JDUDJD.cjs.map} +1 -1
- package/dist/{chunk-UTZR7P7E.cjs → chunk-MOK3E54E.cjs} +29 -659
- package/dist/chunk-MOK3E54E.cjs.map +1 -0
- package/dist/{chunk-GML3FBOT.cjs → chunk-NCSMD3TK.cjs} +2 -2
- package/dist/chunk-NCSMD3TK.cjs.map +1 -0
- package/dist/chunk-QG3U5ONP.js +1183 -0
- package/dist/chunk-QG3U5ONP.js.map +1 -0
- package/dist/chunk-SEO6NAQT.js +14 -0
- package/dist/chunk-SEO6NAQT.js.map +1 -0
- package/dist/chunk-VQTCQKHQ.cjs +17 -0
- package/dist/chunk-VQTCQKHQ.cjs.map +1 -0
- package/dist/{chunk-WZOKY3PW.cjs → chunk-ZSABTI3C.cjs} +8 -8
- package/dist/{chunk-WZOKY3PW.cjs.map → chunk-ZSABTI3C.cjs.map} +1 -1
- package/dist/correlation-id.cjs +22 -10
- package/dist/correlation-id.js +14 -2
- package/dist/decorators.cjs +5 -6
- package/dist/decorators.cjs.map +1 -1
- package/dist/decorators.d.cts +1 -1
- package/dist/decorators.d.ts +1 -1
- package/dist/decorators.js +4 -5
- package/dist/decorators.js.map +1 -1
- package/dist/event.cjs +6 -7
- package/dist/event.js +3 -4
- package/dist/functional.cjs +11 -12
- package/dist/functional.d.cts +1 -1
- package/dist/functional.d.ts +1 -1
- package/dist/functional.js +4 -5
- package/dist/http.cjs +13 -2
- package/dist/http.cjs.map +1 -1
- package/dist/http.js +12 -1
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +134 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -8
- package/dist/index.d.ts +23 -8
- package/dist/index.js +52 -176
- package/dist/index.js.map +1 -1
- package/dist/messaging-adapters.d.cts +1 -1
- package/dist/messaging-adapters.d.ts +1 -1
- package/dist/messaging-testing.d.cts +1 -1
- package/dist/messaging-testing.d.ts +1 -1
- package/dist/messaging.cjs +9 -9
- package/dist/messaging.d.cts +1 -1
- package/dist/messaging.d.ts +1 -1
- package/dist/messaging.js +6 -6
- package/dist/semantic-helpers.cjs +9 -10
- package/dist/semantic-helpers.d.cts +1 -1
- package/dist/semantic-helpers.d.ts +1 -1
- package/dist/semantic-helpers.js +5 -6
- package/dist/{trace-context-t5X1AP-e.d.ts → trace-context-DbGKd1Rn.d.cts} +18 -5
- package/dist/{trace-context-t5X1AP-e.d.cts → trace-context-DbGKd1Rn.d.ts} +18 -5
- package/dist/trace-helpers.cjs +13 -13
- package/dist/trace-helpers.d.cts +2 -2
- package/dist/trace-helpers.d.ts +2 -2
- package/dist/trace-helpers.js +1 -1
- package/dist/{utils-CbUkl8r1.d.cts → utils-BahBCFtJ.d.cts} +1 -1
- package/dist/{utils-Buel3cj0.d.ts → utils-CLKwaUlG.d.ts} +1 -1
- package/dist/webhook.cjs +19 -10
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +1 -1
- package/dist/webhook.d.ts +1 -1
- package/dist/webhook.js +18 -9
- package/dist/webhook.js.map +1 -1
- package/dist/workflow-distributed.cjs +23 -19
- package/dist/workflow-distributed.cjs.map +1 -1
- package/dist/workflow-distributed.d.cts +1 -1
- package/dist/workflow-distributed.d.ts +1 -1
- package/dist/workflow-distributed.js +21 -17
- package/dist/workflow-distributed.js.map +1 -1
- package/dist/workflow.cjs +10 -10
- package/dist/workflow.d.cts +1 -1
- package/dist/workflow.d.ts +1 -1
- package/dist/workflow.js +6 -6
- package/package.json +1 -1
- package/skills/autotel-core/SKILL.md +2 -0
- package/skills/autotel-events/SKILL.md +2 -0
- package/skills/autotel-frameworks/SKILL.md +2 -0
- package/skills/autotel-instrumentation/SKILL.md +2 -0
- package/skills/autotel-request-logging/SKILL.md +2 -0
- package/skills/autotel-structured-errors/SKILL.md +2 -0
- package/src/correlated-events.test.ts +151 -0
- package/src/correlated-events.ts +47 -0
- package/src/functional.ts +2 -0
- package/src/gen-ai-events.ts +14 -5
- package/src/index.ts +20 -4
- package/src/messaging.ts +10 -9
- package/src/request-logger.ts +4 -3
- package/src/structured-error.test.ts +83 -1
- package/src/structured-error.ts +9 -2
- package/src/trace-context.ts +39 -11
- package/src/trace-helpers.ts +2 -2
- package/src/trace-hybrid.test.ts +42 -0
- package/src/trace-hybrid.ts +37 -0
- package/src/webhook.ts +16 -7
- package/src/workflow-distributed.ts +18 -13
- package/src/workflow.ts +7 -6
- package/dist/chunk-6YGUN7IY.cjs.map +0 -1
- package/dist/chunk-B3ZHLLMP.js.map +0 -1
- package/dist/chunk-BBBWDIYQ.js +0 -211
- package/dist/chunk-BBBWDIYQ.js.map +0 -1
- package/dist/chunk-D5LMF53P.cjs +0 -150
- package/dist/chunk-D5LMF53P.cjs.map +0 -1
- package/dist/chunk-GML3FBOT.cjs.map +0 -1
- package/dist/chunk-HPUGKUMZ.js.map +0 -1
- package/dist/chunk-HZ3FYBJG.cjs +0 -217
- package/dist/chunk-HZ3FYBJG.cjs.map +0 -1
- package/dist/chunk-JSNUWSBH.cjs +0 -62
- package/dist/chunk-JSNUWSBH.cjs.map +0 -1
- package/dist/chunk-OBWXM4NN.cjs.map +0 -1
- package/dist/chunk-QC5MNKVF.js.map +0 -1
- package/dist/chunk-S4OFEXLA.js +0 -53
- package/dist/chunk-S4OFEXLA.js.map +0 -1
- package/dist/chunk-UTZR7P7E.cjs.map +0 -1
- package/dist/chunk-WD4RP6IV.js +0 -146
- package/dist/chunk-WD4RP6IV.js.map +0 -1
- package/dist/chunk-YN7USLHW.js.map +0 -1
|
@@ -1,640 +1,13 @@
|
|
|
1
|
-
import { setSpanName } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
|
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,
|
|
1810
|
-
//# sourceMappingURL=chunk-
|
|
1811
|
-
//# sourceMappingURL=chunk-
|
|
1182
|
+
export { ctx, instrument, span, trace, withBaggage, withNewContext, withTracing };
|
|
1183
|
+
//# sourceMappingURL=chunk-DAAJLUTO.js.map
|
|
1184
|
+
//# sourceMappingURL=chunk-DAAJLUTO.js.map
|