autotel 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/package.json +1 -2
  2. package/src/attribute-redacting-processor.test.ts +0 -763
  3. package/src/attribute-redacting-processor.ts +0 -621
  4. package/src/attributes/attachers.ts +0 -161
  5. package/src/attributes/builders.ts +0 -529
  6. package/src/attributes/domains.ts +0 -42
  7. package/src/attributes/index.ts +0 -81
  8. package/src/attributes/registry.ts +0 -323
  9. package/src/attributes/types.ts +0 -211
  10. package/src/attributes/utils.ts +0 -64
  11. package/src/attributes/validators.ts +0 -266
  12. package/src/attributes.test.ts +0 -292
  13. package/src/auto.ts +0 -67
  14. package/src/autotel-logger.test.ts +0 -548
  15. package/src/autotel-logger.ts +0 -364
  16. package/src/baggage-span-processor.test.ts +0 -202
  17. package/src/baggage-span-processor.ts +0 -100
  18. package/src/business-baggage.test.ts +0 -500
  19. package/src/business-baggage.ts +0 -669
  20. package/src/circuit-breaker.test.ts +0 -341
  21. package/src/circuit-breaker.ts +0 -184
  22. package/src/config.test.ts +0 -94
  23. package/src/config.ts +0 -172
  24. package/src/correlated-events.test.ts +0 -151
  25. package/src/correlated-events.ts +0 -47
  26. package/src/correlation-id.test.ts +0 -163
  27. package/src/correlation-id.ts +0 -206
  28. package/src/db.test.ts +0 -252
  29. package/src/db.ts +0 -447
  30. package/src/decorators.test.ts +0 -153
  31. package/src/decorators.ts +0 -188
  32. package/src/define-event.test.ts +0 -41
  33. package/src/define-event.ts +0 -58
  34. package/src/devtools.ts +0 -60
  35. package/src/drain-pipeline.test.ts +0 -68
  36. package/src/drain-pipeline.ts +0 -199
  37. package/src/drain-toolkit.test.ts +0 -113
  38. package/src/drain-toolkit.ts +0 -129
  39. package/src/enricher-toolkit.test.ts +0 -67
  40. package/src/enricher-toolkit.ts +0 -79
  41. package/src/enrichers.test.ts +0 -150
  42. package/src/enrichers.ts +0 -145
  43. package/src/env-config.test.ts +0 -323
  44. package/src/env-config.ts +0 -309
  45. package/src/error-catalog.test.ts +0 -133
  46. package/src/error-catalog.ts +0 -262
  47. package/src/event-queue.test.ts +0 -864
  48. package/src/event-queue.ts +0 -699
  49. package/src/event-subscriber.ts +0 -262
  50. package/src/event-testing.ts +0 -197
  51. package/src/event.test.ts +0 -1104
  52. package/src/event.ts +0 -988
  53. package/src/events-config.ts +0 -235
  54. package/src/exporters.ts +0 -165
  55. package/src/filtering-span-processor.test.ts +0 -281
  56. package/src/filtering-span-processor.ts +0 -111
  57. package/src/flatten-attributes.test.ts +0 -76
  58. package/src/flatten-attributes.ts +0 -80
  59. package/src/functional.strict-types.typecheck.ts +0 -53
  60. package/src/functional.test.ts +0 -1464
  61. package/src/functional.ts +0 -2539
  62. package/src/functional.types.test.ts +0 -135
  63. package/src/hook.mjs +0 -15
  64. package/src/http.test.ts +0 -485
  65. package/src/http.ts +0 -424
  66. package/src/index.ts +0 -433
  67. package/src/init-auto-redactor.test.ts +0 -53
  68. package/src/init-redactor.test.ts +0 -8
  69. package/src/init.customization.test.ts +0 -665
  70. package/src/init.integrations.test.ts +0 -399
  71. package/src/init.openllmetry.test.ts +0 -194
  72. package/src/init.protocol.test.ts +0 -215
  73. package/src/init.ts +0 -2439
  74. package/src/instrumentation.test.ts +0 -108
  75. package/src/instrumentation.ts +0 -319
  76. package/src/logger.test.ts +0 -125
  77. package/src/logger.ts +0 -341
  78. package/src/messaging-adapters.test.ts +0 -595
  79. package/src/messaging-adapters.ts +0 -583
  80. package/src/messaging-testing.test.ts +0 -573
  81. package/src/messaging-testing.ts +0 -935
  82. package/src/messaging.test.ts +0 -1646
  83. package/src/messaging.ts +0 -2245
  84. package/src/metric-helpers.ts +0 -47
  85. package/src/metric-testing.ts +0 -197
  86. package/src/metric.ts +0 -446
  87. package/src/metrics.test.ts +0 -241
  88. package/src/node-require.ts +0 -123
  89. package/src/operation-context.ts +0 -93
  90. package/src/parse-error.test.ts +0 -73
  91. package/src/parse-error.ts +0 -112
  92. package/src/posthog-logs.test.ts +0 -115
  93. package/src/posthog-logs.ts +0 -77
  94. package/src/pretty-console-exporter.test.ts +0 -545
  95. package/src/pretty-console-exporter.ts +0 -413
  96. package/src/pretty-log-formatter.test.ts +0 -123
  97. package/src/pretty-log-formatter.ts +0 -210
  98. package/src/processors/canonical-log-line-processor.test.ts +0 -523
  99. package/src/processors/canonical-log-line-processor.ts +0 -396
  100. package/src/processors.ts +0 -152
  101. package/src/rate-limiter.test.ts +0 -199
  102. package/src/rate-limiter.ts +0 -98
  103. package/src/redact-values.test.ts +0 -90
  104. package/src/redact-values.ts +0 -34
  105. package/src/register.ts +0 -37
  106. package/src/request-logger.test.ts +0 -545
  107. package/src/request-logger.ts +0 -342
  108. package/src/sampling.test.ts +0 -1060
  109. package/src/sampling.ts +0 -737
  110. package/src/security-schema.test.ts +0 -45
  111. package/src/security-schema.ts +0 -107
  112. package/src/semantic-conventions.ts +0 -15
  113. package/src/semantic-helpers.test.ts +0 -226
  114. package/src/semantic-helpers.ts +0 -438
  115. package/src/shutdown.test.ts +0 -364
  116. package/src/shutdown.ts +0 -246
  117. package/src/span-name-normalizer.test.ts +0 -377
  118. package/src/span-name-normalizer.ts +0 -213
  119. package/src/stable-hash.ts +0 -27
  120. package/src/structured-error.test.ts +0 -191
  121. package/src/structured-error.ts +0 -157
  122. package/src/stub.integration.test.ts +0 -361
  123. package/src/tail-sampling-processor.test.ts +0 -230
  124. package/src/tail-sampling-processor.ts +0 -55
  125. package/src/test-span-collector.test.ts +0 -234
  126. package/src/test-span-collector.ts +0 -150
  127. package/src/testing.ts +0 -705
  128. package/src/trace-context.test.ts +0 -73
  129. package/src/trace-context.ts +0 -567
  130. package/src/trace-helpers.new.test.ts +0 -278
  131. package/src/trace-helpers.test.ts +0 -290
  132. package/src/trace-helpers.ts +0 -710
  133. package/src/trace-hybrid.test.ts +0 -42
  134. package/src/trace-hybrid.ts +0 -37
  135. package/src/tracer-provider.test.ts +0 -183
  136. package/src/tracer-provider.ts +0 -266
  137. package/src/track.test.ts +0 -154
  138. package/src/track.ts +0 -216
  139. package/src/validate.test.ts +0 -287
  140. package/src/validate.ts +0 -307
  141. package/src/validation-attributes.ts +0 -43
  142. package/src/validation.test.ts +0 -330
  143. package/src/validation.ts +0 -246
  144. package/src/variable-name-inference.test.ts +0 -178
  145. package/src/variable-name-inference.ts +0 -242
  146. package/src/webhook.test.ts +0 -649
  147. package/src/webhook.ts +0 -637
  148. package/src/workflow-distributed.test.ts +0 -786
  149. package/src/workflow-distributed.ts +0 -916
  150. package/src/workflow.async-safety.integration.test.ts +0 -345
  151. package/src/workflow.test.ts +0 -647
  152. package/src/workflow.ts +0 -810
  153. package/src/yaml-config.test.ts +0 -373
  154. package/src/yaml-config.ts +0 -351
package/src/webhook.ts DELETED
@@ -1,637 +0,0 @@
1
- /**
2
- * Webhook and callback tracing with the "Parking Lot" pattern
3
- *
4
- * When initiating async operations that return hours/days later (webhooks,
5
- * payment callbacks, human approvals), you can't keep a span open. This module
6
- * provides utilities to "park" trace context and retrieve it when callbacks arrive.
7
- *
8
- * @example Stripe payment webhook
9
- * ```typescript
10
- * import { createParkingLot, InMemoryTraceContextStore } from 'autotel/webhook';
11
- *
12
- * const parkingLot = createParkingLot({
13
- * store: new InMemoryTraceContextStore(),
14
- * defaultTTLMs: 24 * 60 * 60 * 1000, // 24 hours
15
- * });
16
- *
17
- * // When initiating payment
18
- * export const initiatePayment = trace(ctx => async (orderId: string) => {
19
- * await parkingLot.park(`payment:${orderId}`, { orderId });
20
- * await stripeClient.createPaymentIntent({ metadata: { orderId } });
21
- * });
22
- *
23
- * // When Stripe webhook arrives (hours later)
24
- * export const handleStripeWebhook = parkingLot.traceCallback({
25
- * name: 'stripe.webhook.payment_intent.succeeded',
26
- * correlationKeyFrom: (event) => `payment:${event.data.object.metadata.orderId}`,
27
- * })(ctx => async (event: Stripe.Event) => {
28
- * // ctx.parkedContext contains the original trace context
29
- * // ctx.elapsedMs shows time since payment was initiated
30
- * await fulfillOrder(event.data.object);
31
- * });
32
- * ```
33
- *
34
- * @module
35
- */
36
-
37
- import { SpanKind, trace as otelTrace } from '@opentelemetry/api';
38
- import type { SpanContext, Link } from '@opentelemetry/api';
39
- import { emitCorrelatedEvent } from './correlated-events';
40
- import { trace } from './functional';
41
- import type { AttributeValue, TraceContext } from './trace-context';
42
- import { recordStructuredError } from './structured-error';
43
-
44
- // ============================================================================
45
- // Types
46
- // ============================================================================
47
-
48
- /**
49
- * Stored trace context for parking lot pattern
50
- */
51
- export interface StoredTraceContext {
52
- /** Trace ID from the original span */
53
- traceId: string;
54
-
55
- /** Span ID from the original span */
56
- spanId: string;
57
-
58
- /** Trace flags (sampling decision) */
59
- traceFlags: number;
60
-
61
- /** When the context was parked */
62
- parkedAt: number;
63
-
64
- /** Optional TTL in milliseconds */
65
- ttlMs?: number;
66
-
67
- /** User-provided metadata */
68
- metadata?: Record<string, string>;
69
- }
70
-
71
- /**
72
- * Interface for trace context storage backends
73
- *
74
- * Implement this interface to use different storage backends (Redis, DynamoDB, etc.)
75
- */
76
- export interface TraceContextStore {
77
- /**
78
- * Save trace context with a correlation key
79
- *
80
- * @param key - Unique correlation key (e.g., "payment:order-123")
81
- * @param context - The trace context to store
82
- */
83
- save(key: string, context: StoredTraceContext): Promise<void>;
84
-
85
- /**
86
- * Load trace context by correlation key
87
- *
88
- * @param key - The correlation key used when parking
89
- * @returns The stored context, or null if not found/expired
90
- */
91
- load(key: string): Promise<StoredTraceContext | null>;
92
-
93
- /**
94
- * Delete trace context by correlation key
95
- *
96
- * @param key - The correlation key to delete
97
- */
98
- delete(key: string): Promise<void>;
99
- }
100
-
101
- /**
102
- * Configuration for creating a parking lot
103
- */
104
- export interface ParkingLotConfig {
105
- /** Storage backend for parked contexts */
106
- store: TraceContextStore;
107
-
108
- /** Default TTL in milliseconds (default: 24 hours) */
109
- defaultTTLMs?: number;
110
-
111
- /** Prefix for all correlation keys (default: "parkingLot:") */
112
- keyPrefix?: string;
113
-
114
- /** Whether to auto-delete after retrieval (default: true) */
115
- autoDeleteOnRetrieve?: boolean;
116
-
117
- /** Callback when context expires or is not found */
118
- onMiss?: (correlationKey: string) => void;
119
- }
120
-
121
- /**
122
- * Configuration for traceCallback wrapper
123
- */
124
- export interface CallbackConfig {
125
- /** Span name for the callback handler */
126
- name: string;
127
-
128
- /**
129
- * Extract correlation key from callback arguments
130
- *
131
- * @example
132
- * ```typescript
133
- * correlationKeyFrom: (event) => `payment:${event.data.orderId}`
134
- * ```
135
- */
136
- correlationKeyFrom: (args: unknown[]) => string;
137
-
138
- /** Additional span attributes */
139
- attributes?: Record<string, string | number | boolean>;
140
-
141
- /** Whether to fail if parked context is not found (default: false) */
142
- requireParkedContext?: boolean;
143
- }
144
-
145
- /**
146
- * Extended context for callback handlers
147
- */
148
- export interface CallbackContext extends TraceContext {
149
- /** The retrieved parked context, if found */
150
- parkedContext: StoredTraceContext | null;
151
-
152
- /** Time elapsed since context was parked (ms), or null if not found */
153
- elapsedMs: number | null;
154
-
155
- /** The correlation key used for retrieval */
156
- correlationKey: string;
157
- }
158
-
159
- /**
160
- * The parking lot instance
161
- */
162
- export interface ParkingLot {
163
- /**
164
- * Park current trace context before initiating async operation
165
- *
166
- * Call this before sending a webhook, initiating a payment, or starting
167
- * any operation that will complete via callback.
168
- *
169
- * @param correlationKey - Unique key to retrieve context later (e.g., "payment:order-123")
170
- * @param metadata - Optional metadata to store with the context
171
- * @returns The correlation key (with prefix applied)
172
- *
173
- * @example
174
- * ```typescript
175
- * await parkingLot.park(`payment:${orderId}`, {
176
- * customerId: customer.id,
177
- * amount: payment.amount.toString(),
178
- * });
179
- * ```
180
- */
181
- park(
182
- correlationKey: string,
183
- metadata?: Record<string, string>,
184
- ): Promise<string>;
185
-
186
- /**
187
- * Retrieve parked context when callback arrives
188
- *
189
- * @param correlationKey - The key used when parking
190
- * @returns The stored context, or null if not found/expired
191
- */
192
- retrieve(correlationKey: string): Promise<StoredTraceContext | null>;
193
-
194
- /**
195
- * Wrap a callback handler with automatic context retrieval and linking
196
- *
197
- * Creates a traced function that:
198
- * 1. Extracts correlation key from arguments
199
- * 2. Retrieves parked context from storage
200
- * 3. Creates a span link to the original trace
201
- * 4. Provides elapsed time since parking
202
- *
203
- * @param config - Callback configuration
204
- * @returns Factory function for the callback handler
205
- *
206
- * @example
207
- * ```typescript
208
- * export const handleWebhook = parkingLot.traceCallback({
209
- * name: 'webhook.payment.completed',
210
- * correlationKeyFrom: (args) => `payment:${args[0].orderId}`,
211
- * })(ctx => async (event) => {
212
- * console.log(`Payment completed after ${ctx.elapsedMs}ms`);
213
- * await processPayment(event);
214
- * });
215
- * ```
216
- */
217
- traceCallback<TArgs extends unknown[], TReturn>(
218
- config: CallbackConfig,
219
- ): (
220
- fnFactory: (ctx: CallbackContext) => (...args: TArgs) => Promise<TReturn>,
221
- ) => (...args: TArgs) => Promise<TReturn>;
222
-
223
- /**
224
- * Manually create a span link from stored context
225
- *
226
- * Useful when you need more control over span creation.
227
- *
228
- * @param storedContext - The stored trace context
229
- * @returns A span link that can be added to a span
230
- */
231
- createLink(storedContext: StoredTraceContext): Link;
232
-
233
- /**
234
- * Check if a parked context exists (without retrieving/deleting it)
235
- *
236
- * @param correlationKey - The key to check
237
- * @returns True if context exists and hasn't expired
238
- */
239
- exists(correlationKey: string): Promise<boolean>;
240
- }
241
-
242
- // ============================================================================
243
- // In-Memory Store (for testing and development)
244
- // ============================================================================
245
-
246
- /**
247
- * In-memory trace context store
248
- *
249
- * Useful for testing and development. For production, use a persistent
250
- * store like Redis or DynamoDB.
251
- *
252
- * @example
253
- * ```typescript
254
- * const store = new InMemoryTraceContextStore();
255
- * const parkingLot = createParkingLot({ store });
256
- * ```
257
- */
258
- export class InMemoryTraceContextStore implements TraceContextStore {
259
- private store = new Map<string, StoredTraceContext>();
260
- private cleanupInterval: ReturnType<typeof setInterval> | null = null;
261
-
262
- constructor(
263
- private options: {
264
- /** Cleanup interval in ms (default: 60000) */
265
- cleanupIntervalMs?: number;
266
- } = {},
267
- ) {
268
- // Start periodic cleanup of expired entries
269
- const cleanupMs = options.cleanupIntervalMs ?? 60_000;
270
- if (cleanupMs > 0) {
271
- this.cleanupInterval = setInterval(() => this.cleanup(), cleanupMs);
272
- // Don't prevent process exit
273
- if (this.cleanupInterval.unref) {
274
- this.cleanupInterval.unref();
275
- }
276
- }
277
- }
278
-
279
- async save(key: string, context: StoredTraceContext): Promise<void> {
280
- this.store.set(key, context);
281
- }
282
-
283
- async load(key: string): Promise<StoredTraceContext | null> {
284
- const context = this.store.get(key);
285
- if (!context) {
286
- return null;
287
- }
288
-
289
- // Check TTL expiration
290
- if (context.ttlMs) {
291
- const age = Date.now() - context.parkedAt;
292
- if (age > context.ttlMs) {
293
- this.store.delete(key);
294
- return null;
295
- }
296
- }
297
-
298
- return context;
299
- }
300
-
301
- async delete(key: string): Promise<void> {
302
- this.store.delete(key);
303
- }
304
-
305
- /**
306
- * Get number of stored contexts (for testing)
307
- */
308
- get size(): number {
309
- return this.store.size;
310
- }
311
-
312
- /**
313
- * Clear all stored contexts (for testing)
314
- */
315
- clear(): void {
316
- this.store.clear();
317
- }
318
-
319
- /**
320
- * Stop the cleanup interval
321
- */
322
- destroy(): void {
323
- if (this.cleanupInterval) {
324
- clearInterval(this.cleanupInterval);
325
- this.cleanupInterval = null;
326
- }
327
- }
328
-
329
- private cleanup(): void {
330
- const now = Date.now();
331
- for (const [key, context] of this.store.entries()) {
332
- if (context.ttlMs) {
333
- const age = now - context.parkedAt;
334
- if (age > context.ttlMs) {
335
- this.store.delete(key);
336
- }
337
- }
338
- }
339
- }
340
- }
341
-
342
- // ============================================================================
343
- // Parking Lot Factory
344
- // ============================================================================
345
-
346
- /**
347
- * Create a parking lot for trace context storage and retrieval
348
- *
349
- * @param config - Parking lot configuration
350
- * @returns A parking lot instance
351
- *
352
- * @example Basic usage
353
- * ```typescript
354
- * const parkingLot = createParkingLot({
355
- * store: new InMemoryTraceContextStore(),
356
- * defaultTTLMs: 24 * 60 * 60 * 1000, // 24 hours
357
- * });
358
- * ```
359
- *
360
- * @example With Redis store
361
- * ```typescript
362
- * class RedisTraceContextStore implements TraceContextStore {
363
- * constructor(private redis: Redis) {}
364
- *
365
- * async save(key: string, context: StoredTraceContext) {
366
- * const ttlSeconds = context.ttlMs ? Math.ceil(context.ttlMs / 1000) : 86400;
367
- * await this.redis.setex(key, ttlSeconds, JSON.stringify(context));
368
- * }
369
- *
370
- * async load(key: string) {
371
- * const data = await this.redis.get(key);
372
- * return data ? JSON.parse(data) : null;
373
- * }
374
- *
375
- * async delete(key: string) {
376
- * await this.redis.del(key);
377
- * }
378
- * }
379
- *
380
- * const parkingLot = createParkingLot({
381
- * store: new RedisTraceContextStore(redis),
382
- * });
383
- * ```
384
- */
385
- export function createParkingLot(config: ParkingLotConfig): ParkingLot {
386
- const {
387
- store,
388
- defaultTTLMs = 24 * 60 * 60 * 1000, // 24 hours
389
- keyPrefix = 'parkingLot:',
390
- autoDeleteOnRetrieve = true,
391
- onMiss,
392
- } = config;
393
-
394
- /**
395
- * Get current span context from active context
396
- */
397
- function getCurrentSpanContext(): SpanContext | null {
398
- const activeSpan = otelTrace.getActiveSpan();
399
- if (!activeSpan) {
400
- return null;
401
- }
402
- return activeSpan.spanContext();
403
- }
404
-
405
- /**
406
- * Apply key prefix
407
- */
408
- function prefixKey(key: string): string {
409
- return `${keyPrefix}${key}`;
410
- }
411
-
412
- const parkingLot: ParkingLot = {
413
- async park(
414
- correlationKey: string,
415
- metadata?: Record<string, string>,
416
- ): Promise<string> {
417
- const spanContext = getCurrentSpanContext();
418
- const fullKey = prefixKey(correlationKey);
419
-
420
- const storedContext: StoredTraceContext = {
421
- traceId: spanContext?.traceId ?? '',
422
- spanId: spanContext?.spanId ?? '',
423
- traceFlags: spanContext?.traceFlags ?? 0,
424
- parkedAt: Date.now(),
425
- ttlMs: defaultTTLMs,
426
- metadata,
427
- };
428
-
429
- await store.save(fullKey, storedContext);
430
-
431
- const activeSpan = otelTrace.getActiveSpan();
432
- if (activeSpan) {
433
- const parkAttrs: Record<string, AttributeValue> = {
434
- 'parking_lot.correlation_key': correlationKey,
435
- 'parking_lot.ttl_ms': defaultTTLMs,
436
- ...(metadata &&
437
- Object.fromEntries(
438
- Object.entries(metadata).map(([k, v]) => [
439
- `parking_lot.metadata.${k}`,
440
- v,
441
- ]),
442
- )),
443
- };
444
- emitCorrelatedEvent(
445
- {
446
- setAttribute: (k, v) => activeSpan.setAttribute(k, v),
447
- setAttributes: (a) => activeSpan.setAttributes(a),
448
- addEvent: (n, a) => activeSpan.addEvent(n, a),
449
- },
450
- 'trace_context_parked',
451
- parkAttrs,
452
- );
453
- }
454
-
455
- // Return the unprefixed key so callers can use the same key for retrieve()
456
- return correlationKey;
457
- },
458
-
459
- async retrieve(correlationKey: string): Promise<StoredTraceContext | null> {
460
- const fullKey = prefixKey(correlationKey);
461
- const storedContext = await store.load(fullKey);
462
-
463
- if (!storedContext) {
464
- onMiss?.(correlationKey);
465
- return null;
466
- }
467
-
468
- if (autoDeleteOnRetrieve) {
469
- await store.delete(fullKey);
470
- }
471
-
472
- return storedContext;
473
- },
474
-
475
- traceCallback<TArgs extends unknown[], TReturn>(
476
- callbackConfig: CallbackConfig,
477
- ): (
478
- fnFactory: (ctx: CallbackContext) => (...args: TArgs) => Promise<TReturn>,
479
- ) => (...args: TArgs) => Promise<TReturn> {
480
- return (
481
- fnFactory: (
482
- ctx: CallbackContext,
483
- ) => (...args: TArgs) => Promise<TReturn>,
484
- ): ((...args: TArgs) => Promise<TReturn>) => {
485
- return trace<TArgs, TReturn>(
486
- {
487
- name: callbackConfig.name,
488
- spanKind: SpanKind.SERVER,
489
- },
490
- (baseCtx) => {
491
- return async (...args: TArgs) => {
492
- // Extract correlation key from arguments
493
- const correlationKey = callbackConfig.correlationKeyFrom(args);
494
-
495
- // Retrieve parked context
496
- const parkedContext = await parkingLot.retrieve(correlationKey);
497
-
498
- // Calculate elapsed time
499
- const elapsedMs = parkedContext
500
- ? Date.now() - parkedContext.parkedAt
501
- : null;
502
-
503
- // Set span attributes
504
- baseCtx.setAttribute(
505
- 'parking_lot.correlation_key',
506
- correlationKey,
507
- );
508
-
509
- if (parkedContext) {
510
- baseCtx.setAttribute('parking_lot.elapsed_ms', elapsedMs!);
511
- baseCtx.setAttribute(
512
- 'parking_lot.original_trace_id',
513
- parkedContext.traceId,
514
- );
515
- baseCtx.setAttribute(
516
- 'parking_lot.original_span_id',
517
- parkedContext.spanId,
518
- );
519
-
520
- // Add metadata as attributes
521
- if (parkedContext.metadata) {
522
- for (const [key, value] of Object.entries(
523
- parkedContext.metadata,
524
- )) {
525
- baseCtx.setAttribute(`parking_lot.metadata.${key}`, value);
526
- }
527
- }
528
-
529
- // Create span link to original trace
530
- const link = parkingLot.createLink(parkedContext);
531
- baseCtx.addLinks([link]);
532
-
533
- emitCorrelatedEvent(baseCtx, 'parked_context_retrieved', {
534
- 'parking_lot.correlation_key': correlationKey,
535
- 'parking_lot.elapsed_ms': elapsedMs!,
536
- 'parking_lot.original_trace_id': parkedContext.traceId,
537
- });
538
- } else {
539
- baseCtx.setAttribute('parking_lot.context_found', false);
540
-
541
- if (callbackConfig.requireParkedContext) {
542
- const error = new Error(
543
- `Required parked context not found for key: ${correlationKey}`,
544
- );
545
- recordStructuredError(baseCtx, error);
546
- throw error;
547
- }
548
- }
549
-
550
- // Apply custom attributes
551
- if (callbackConfig.attributes) {
552
- for (const [key, value] of Object.entries(
553
- callbackConfig.attributes,
554
- )) {
555
- baseCtx.setAttribute(key, value);
556
- }
557
- }
558
-
559
- // Create extended context
560
- const callbackCtx: CallbackContext = {
561
- ...baseCtx,
562
- parkedContext,
563
- elapsedMs,
564
- correlationKey,
565
- };
566
-
567
- // Execute user's function
568
- const userFn = fnFactory(callbackCtx);
569
- return userFn(...args);
570
- };
571
- },
572
- );
573
- };
574
- },
575
-
576
- createLink(storedContext: StoredTraceContext): Link {
577
- return {
578
- context: {
579
- traceId: storedContext.traceId,
580
- spanId: storedContext.spanId,
581
- traceFlags: storedContext.traceFlags,
582
- isRemote: true,
583
- },
584
- attributes: {
585
- 'link.type': 'parking_lot',
586
- 'parking_lot.parked_at': storedContext.parkedAt,
587
- ...(storedContext.metadata && {
588
- 'parking_lot.has_metadata': true,
589
- }),
590
- },
591
- };
592
- },
593
-
594
- async exists(correlationKey: string): Promise<boolean> {
595
- const fullKey = prefixKey(correlationKey);
596
- const context = await store.load(fullKey);
597
- return context !== null;
598
- },
599
- };
600
-
601
- return parkingLot;
602
- }
603
-
604
- // ============================================================================
605
- // Utility Functions
606
- // ============================================================================
607
-
608
- /**
609
- * Create a correlation key from multiple parts
610
- *
611
- * @param parts - Key parts to join
612
- * @returns A correlation key string
613
- *
614
- * @example
615
- * ```typescript
616
- * const key = createCorrelationKey('payment', orderId, 'stripe');
617
- * // Returns: "payment:order-123:stripe"
618
- * ```
619
- */
620
- export function createCorrelationKey(...parts: (string | number)[]): string {
621
- return parts.map(String).join(':');
622
- }
623
-
624
- /**
625
- * Extract span context from stored context for manual linking
626
- *
627
- * @param storedContext - The stored trace context
628
- * @returns SpanContext compatible object
629
- */
630
- export function toSpanContext(storedContext: StoredTraceContext): SpanContext {
631
- return {
632
- traceId: storedContext.traceId,
633
- spanId: storedContext.spanId,
634
- traceFlags: storedContext.traceFlags,
635
- isRemote: true,
636
- };
637
- }