{"version":3,"sources":["../src/webhook.ts"],"names":["otelTrace","trace","SpanKind"],"mappings":"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","file":"webhook.cjs","sourcesContent":["/**\n * Webhook and callback tracing with the \"Parking Lot\" pattern\n *\n * When initiating async operations that return hours/days later (webhooks,\n * payment callbacks, human approvals), you can't keep a span open. This module\n * provides utilities to \"park\" trace context and retrieve it when callbacks arrive.\n *\n * @example Stripe payment webhook\n * ```typescript\n * import { createParkingLot, InMemoryTraceContextStore } from 'autotel/webhook';\n *\n * const parkingLot = createParkingLot({\n *   store: new InMemoryTraceContextStore(),\n *   defaultTTLMs: 24 * 60 * 60 * 1000, // 24 hours\n * });\n *\n * // When initiating payment\n * export const initiatePayment = trace(ctx => async (orderId: string) => {\n *   await parkingLot.park(`payment:${orderId}`, { orderId });\n *   await stripeClient.createPaymentIntent({ metadata: { orderId } });\n * });\n *\n * // When Stripe webhook arrives (hours later)\n * export const handleStripeWebhook = parkingLot.traceCallback({\n *   name: 'stripe.webhook.payment_intent.succeeded',\n *   correlationKeyFrom: (event) => `payment:${event.data.object.metadata.orderId}`,\n * })(ctx => async (event: Stripe.Event) => {\n *   // ctx.parkedContext contains the original trace context\n *   // ctx.elapsedMs shows time since payment was initiated\n *   await fulfillOrder(event.data.object);\n * });\n * ```\n *\n * @module\n */\n\nimport { SpanKind, trace as otelTrace } from '@opentelemetry/api';\nimport type { SpanContext, Link } from '@opentelemetry/api';\nimport { trace } from './functional';\nimport type { TraceContext } from './trace-context';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Stored trace context for parking lot pattern\n */\nexport interface StoredTraceContext {\n  /** Trace ID from the original span */\n  traceId: string;\n\n  /** Span ID from the original span */\n  spanId: string;\n\n  /** Trace flags (sampling decision) */\n  traceFlags: number;\n\n  /** When the context was parked */\n  parkedAt: number;\n\n  /** Optional TTL in milliseconds */\n  ttlMs?: number;\n\n  /** User-provided metadata */\n  metadata?: Record<string, string>;\n}\n\n/**\n * Interface for trace context storage backends\n *\n * Implement this interface to use different storage backends (Redis, DynamoDB, etc.)\n */\nexport interface TraceContextStore {\n  /**\n   * Save trace context with a correlation key\n   *\n   * @param key - Unique correlation key (e.g., \"payment:order-123\")\n   * @param context - The trace context to store\n   */\n  save(key: string, context: StoredTraceContext): Promise<void>;\n\n  /**\n   * Load trace context by correlation key\n   *\n   * @param key - The correlation key used when parking\n   * @returns The stored context, or null if not found/expired\n   */\n  load(key: string): Promise<StoredTraceContext | null>;\n\n  /**\n   * Delete trace context by correlation key\n   *\n   * @param key - The correlation key to delete\n   */\n  delete(key: string): Promise<void>;\n}\n\n/**\n * Configuration for creating a parking lot\n */\nexport interface ParkingLotConfig {\n  /** Storage backend for parked contexts */\n  store: TraceContextStore;\n\n  /** Default TTL in milliseconds (default: 24 hours) */\n  defaultTTLMs?: number;\n\n  /** Prefix for all correlation keys (default: \"parkingLot:\") */\n  keyPrefix?: string;\n\n  /** Whether to auto-delete after retrieval (default: true) */\n  autoDeleteOnRetrieve?: boolean;\n\n  /** Callback when context expires or is not found */\n  onMiss?: (correlationKey: string) => void;\n}\n\n/**\n * Configuration for traceCallback wrapper\n */\nexport interface CallbackConfig {\n  /** Span name for the callback handler */\n  name: string;\n\n  /**\n   * Extract correlation key from callback arguments\n   *\n   * @example\n   * ```typescript\n   * correlationKeyFrom: (event) => `payment:${event.data.orderId}`\n   * ```\n   */\n  correlationKeyFrom: (args: unknown[]) => string;\n\n  /** Additional span attributes */\n  attributes?: Record<string, string | number | boolean>;\n\n  /** Whether to fail if parked context is not found (default: false) */\n  requireParkedContext?: boolean;\n}\n\n/**\n * Extended context for callback handlers\n */\nexport interface CallbackContext extends TraceContext {\n  /** The retrieved parked context, if found */\n  parkedContext: StoredTraceContext | null;\n\n  /** Time elapsed since context was parked (ms), or null if not found */\n  elapsedMs: number | null;\n\n  /** The correlation key used for retrieval */\n  correlationKey: string;\n}\n\n/**\n * The parking lot instance\n */\nexport interface ParkingLot {\n  /**\n   * Park current trace context before initiating async operation\n   *\n   * Call this before sending a webhook, initiating a payment, or starting\n   * any operation that will complete via callback.\n   *\n   * @param correlationKey - Unique key to retrieve context later (e.g., \"payment:order-123\")\n   * @param metadata - Optional metadata to store with the context\n   * @returns The correlation key (with prefix applied)\n   *\n   * @example\n   * ```typescript\n   * await parkingLot.park(`payment:${orderId}`, {\n   *   customerId: customer.id,\n   *   amount: payment.amount.toString(),\n   * });\n   * ```\n   */\n  park(\n    correlationKey: string,\n    metadata?: Record<string, string>,\n  ): Promise<string>;\n\n  /**\n   * Retrieve parked context when callback arrives\n   *\n   * @param correlationKey - The key used when parking\n   * @returns The stored context, or null if not found/expired\n   */\n  retrieve(correlationKey: string): Promise<StoredTraceContext | null>;\n\n  /**\n   * Wrap a callback handler with automatic context retrieval and linking\n   *\n   * Creates a traced function that:\n   * 1. Extracts correlation key from arguments\n   * 2. Retrieves parked context from storage\n   * 3. Creates a span link to the original trace\n   * 4. Provides elapsed time since parking\n   *\n   * @param config - Callback configuration\n   * @returns Factory function for the callback handler\n   *\n   * @example\n   * ```typescript\n   * export const handleWebhook = parkingLot.traceCallback({\n   *   name: 'webhook.payment.completed',\n   *   correlationKeyFrom: (args) => `payment:${args[0].orderId}`,\n   * })(ctx => async (event) => {\n   *   console.log(`Payment completed after ${ctx.elapsedMs}ms`);\n   *   await processPayment(event);\n   * });\n   * ```\n   */\n  traceCallback<TArgs extends unknown[], TReturn>(\n    config: CallbackConfig,\n  ): (\n    fnFactory: (ctx: CallbackContext) => (...args: TArgs) => Promise<TReturn>,\n  ) => (...args: TArgs) => Promise<TReturn>;\n\n  /**\n   * Manually create a span link from stored context\n   *\n   * Useful when you need more control over span creation.\n   *\n   * @param storedContext - The stored trace context\n   * @returns A span link that can be added to a span\n   */\n  createLink(storedContext: StoredTraceContext): Link;\n\n  /**\n   * Check if a parked context exists (without retrieving/deleting it)\n   *\n   * @param correlationKey - The key to check\n   * @returns True if context exists and hasn't expired\n   */\n  exists(correlationKey: string): Promise<boolean>;\n}\n\n// ============================================================================\n// In-Memory Store (for testing and development)\n// ============================================================================\n\n/**\n * In-memory trace context store\n *\n * Useful for testing and development. For production, use a persistent\n * store like Redis or DynamoDB.\n *\n * @example\n * ```typescript\n * const store = new InMemoryTraceContextStore();\n * const parkingLot = createParkingLot({ store });\n * ```\n */\nexport class InMemoryTraceContextStore implements TraceContextStore {\n  private store = new Map<string, StoredTraceContext>();\n  private cleanupInterval: ReturnType<typeof setInterval> | null = null;\n\n  constructor(\n    private options: {\n      /** Cleanup interval in ms (default: 60000) */\n      cleanupIntervalMs?: number;\n    } = {},\n  ) {\n    // Start periodic cleanup of expired entries\n    const cleanupMs = options.cleanupIntervalMs ?? 60_000;\n    if (cleanupMs > 0) {\n      this.cleanupInterval = setInterval(() => this.cleanup(), cleanupMs);\n      // Don't prevent process exit\n      if (this.cleanupInterval.unref) {\n        this.cleanupInterval.unref();\n      }\n    }\n  }\n\n  async save(key: string, context: StoredTraceContext): Promise<void> {\n    this.store.set(key, context);\n  }\n\n  async load(key: string): Promise<StoredTraceContext | null> {\n    const context = this.store.get(key);\n    if (!context) {\n      return null;\n    }\n\n    // Check TTL expiration\n    if (context.ttlMs) {\n      const age = Date.now() - context.parkedAt;\n      if (age > context.ttlMs) {\n        this.store.delete(key);\n        return null;\n      }\n    }\n\n    return context;\n  }\n\n  async delete(key: string): Promise<void> {\n    this.store.delete(key);\n  }\n\n  /**\n   * Get number of stored contexts (for testing)\n   */\n  get size(): number {\n    return this.store.size;\n  }\n\n  /**\n   * Clear all stored contexts (for testing)\n   */\n  clear(): void {\n    this.store.clear();\n  }\n\n  /**\n   * Stop the cleanup interval\n   */\n  destroy(): void {\n    if (this.cleanupInterval) {\n      clearInterval(this.cleanupInterval);\n      this.cleanupInterval = null;\n    }\n  }\n\n  private cleanup(): void {\n    const now = Date.now();\n    for (const [key, context] of this.store.entries()) {\n      if (context.ttlMs) {\n        const age = now - context.parkedAt;\n        if (age > context.ttlMs) {\n          this.store.delete(key);\n        }\n      }\n    }\n  }\n}\n\n// ============================================================================\n// Parking Lot Factory\n// ============================================================================\n\n/**\n * Create a parking lot for trace context storage and retrieval\n *\n * @param config - Parking lot configuration\n * @returns A parking lot instance\n *\n * @example Basic usage\n * ```typescript\n * const parkingLot = createParkingLot({\n *   store: new InMemoryTraceContextStore(),\n *   defaultTTLMs: 24 * 60 * 60 * 1000, // 24 hours\n * });\n * ```\n *\n * @example With Redis store\n * ```typescript\n * class RedisTraceContextStore implements TraceContextStore {\n *   constructor(private redis: Redis) {}\n *\n *   async save(key: string, context: StoredTraceContext) {\n *     const ttlSeconds = context.ttlMs ? Math.ceil(context.ttlMs / 1000) : 86400;\n *     await this.redis.setex(key, ttlSeconds, JSON.stringify(context));\n *   }\n *\n *   async load(key: string) {\n *     const data = await this.redis.get(key);\n *     return data ? JSON.parse(data) : null;\n *   }\n *\n *   async delete(key: string) {\n *     await this.redis.del(key);\n *   }\n * }\n *\n * const parkingLot = createParkingLot({\n *   store: new RedisTraceContextStore(redis),\n * });\n * ```\n */\nexport function createParkingLot(config: ParkingLotConfig): ParkingLot {\n  const {\n    store,\n    defaultTTLMs = 24 * 60 * 60 * 1000, // 24 hours\n    keyPrefix = 'parkingLot:',\n    autoDeleteOnRetrieve = true,\n    onMiss,\n  } = config;\n\n  /**\n   * Get current span context from active context\n   */\n  function getCurrentSpanContext(): SpanContext | null {\n    const activeSpan = otelTrace.getActiveSpan();\n    if (!activeSpan) {\n      return null;\n    }\n    return activeSpan.spanContext();\n  }\n\n  /**\n   * Apply key prefix\n   */\n  function prefixKey(key: string): string {\n    return `${keyPrefix}${key}`;\n  }\n\n  const parkingLot: ParkingLot = {\n    async park(\n      correlationKey: string,\n      metadata?: Record<string, string>,\n    ): Promise<string> {\n      const spanContext = getCurrentSpanContext();\n      const fullKey = prefixKey(correlationKey);\n\n      const storedContext: StoredTraceContext = {\n        traceId: spanContext?.traceId ?? '',\n        spanId: spanContext?.spanId ?? '',\n        traceFlags: spanContext?.traceFlags ?? 0,\n        parkedAt: Date.now(),\n        ttlMs: defaultTTLMs,\n        metadata,\n      };\n\n      await store.save(fullKey, storedContext);\n\n      // Add event to current span\n      const activeSpan = otelTrace.getActiveSpan();\n      if (activeSpan) {\n        activeSpan.addEvent('trace_context_parked', {\n          'parking_lot.correlation_key': correlationKey,\n          'parking_lot.ttl_ms': defaultTTLMs,\n          ...(metadata &&\n            Object.fromEntries(\n              Object.entries(metadata).map(([k, v]) => [\n                `parking_lot.metadata.${k}`,\n                v,\n              ]),\n            )),\n        });\n      }\n\n      // Return the unprefixed key so callers can use the same key for retrieve()\n      return correlationKey;\n    },\n\n    async retrieve(correlationKey: string): Promise<StoredTraceContext | null> {\n      const fullKey = prefixKey(correlationKey);\n      const storedContext = await store.load(fullKey);\n\n      if (!storedContext) {\n        onMiss?.(correlationKey);\n        return null;\n      }\n\n      if (autoDeleteOnRetrieve) {\n        await store.delete(fullKey);\n      }\n\n      return storedContext;\n    },\n\n    traceCallback<TArgs extends unknown[], TReturn>(\n      callbackConfig: CallbackConfig,\n    ): (\n      fnFactory: (ctx: CallbackContext) => (...args: TArgs) => Promise<TReturn>,\n    ) => (...args: TArgs) => Promise<TReturn> {\n      return (\n        fnFactory: (\n          ctx: CallbackContext,\n        ) => (...args: TArgs) => Promise<TReturn>,\n      ): ((...args: TArgs) => Promise<TReturn>) => {\n        return trace<TArgs, TReturn>(\n          {\n            name: callbackConfig.name,\n            spanKind: SpanKind.SERVER,\n          },\n          (baseCtx) => {\n            return async (...args: TArgs) => {\n              // Extract correlation key from arguments\n              const correlationKey = callbackConfig.correlationKeyFrom(args);\n\n              // Retrieve parked context\n              const parkedContext = await parkingLot.retrieve(correlationKey);\n\n              // Calculate elapsed time\n              const elapsedMs = parkedContext\n                ? Date.now() - parkedContext.parkedAt\n                : null;\n\n              // Set span attributes\n              baseCtx.setAttribute(\n                'parking_lot.correlation_key',\n                correlationKey,\n              );\n\n              if (parkedContext) {\n                baseCtx.setAttribute('parking_lot.elapsed_ms', elapsedMs!);\n                baseCtx.setAttribute(\n                  'parking_lot.original_trace_id',\n                  parkedContext.traceId,\n                );\n                baseCtx.setAttribute(\n                  'parking_lot.original_span_id',\n                  parkedContext.spanId,\n                );\n\n                // Add metadata as attributes\n                if (parkedContext.metadata) {\n                  for (const [key, value] of Object.entries(\n                    parkedContext.metadata,\n                  )) {\n                    baseCtx.setAttribute(`parking_lot.metadata.${key}`, value);\n                  }\n                }\n\n                // Create span link to original trace\n                const link = parkingLot.createLink(parkedContext);\n                baseCtx.addLinks([link]);\n\n                // Add event\n                baseCtx.addEvent('parked_context_retrieved', {\n                  'parking_lot.correlation_key': correlationKey,\n                  'parking_lot.elapsed_ms': elapsedMs!,\n                  'parking_lot.original_trace_id': parkedContext.traceId,\n                });\n              } else {\n                baseCtx.setAttribute('parking_lot.context_found', false);\n\n                if (callbackConfig.requireParkedContext) {\n                  const error = new Error(\n                    `Required parked context not found for key: ${correlationKey}`,\n                  );\n                  baseCtx.recordException(error);\n                  throw error;\n                }\n              }\n\n              // Apply custom attributes\n              if (callbackConfig.attributes) {\n                for (const [key, value] of Object.entries(\n                  callbackConfig.attributes,\n                )) {\n                  baseCtx.setAttribute(key, value);\n                }\n              }\n\n              // Create extended context\n              const callbackCtx: CallbackContext = {\n                ...baseCtx,\n                parkedContext,\n                elapsedMs,\n                correlationKey,\n              };\n\n              // Execute user's function\n              const userFn = fnFactory(callbackCtx);\n              return userFn(...args);\n            };\n          },\n        );\n      };\n    },\n\n    createLink(storedContext: StoredTraceContext): Link {\n      return {\n        context: {\n          traceId: storedContext.traceId,\n          spanId: storedContext.spanId,\n          traceFlags: storedContext.traceFlags,\n          isRemote: true,\n        },\n        attributes: {\n          'link.type': 'parking_lot',\n          'parking_lot.parked_at': storedContext.parkedAt,\n          ...(storedContext.metadata && {\n            'parking_lot.has_metadata': true,\n          }),\n        },\n      };\n    },\n\n    async exists(correlationKey: string): Promise<boolean> {\n      const fullKey = prefixKey(correlationKey);\n      const context = await store.load(fullKey);\n      return context !== null;\n    },\n  };\n\n  return parkingLot;\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Create a correlation key from multiple parts\n *\n * @param parts - Key parts to join\n * @returns A correlation key string\n *\n * @example\n * ```typescript\n * const key = createCorrelationKey('payment', orderId, 'stripe');\n * // Returns: \"payment:order-123:stripe\"\n * ```\n */\nexport function createCorrelationKey(...parts: (string | number)[]): string {\n  return parts.map(String).join(':');\n}\n\n/**\n * Extract span context from stored context for manual linking\n *\n * @param storedContext - The stored trace context\n * @returns SpanContext compatible object\n */\nexport function toSpanContext(storedContext: StoredTraceContext): SpanContext {\n  return {\n    traceId: storedContext.traceId,\n    spanId: storedContext.spanId,\n    traceFlags: storedContext.traceFlags,\n    isRemote: true,\n  };\n}\n"]}