{"version":3,"file":"metric.cjs","names":["getConfig"],"sources":["../src/metric.ts"],"sourcesContent":["/**\n * Metrics API for OpenTelemetry\n *\n * Track business metrics for OpenTelemetry (Prometheus/Grafana).\n * For business people who think in metrics.\n *\n * @example Track business metrics\n * ```typescript\n * const metrics = new Metric('checkout')\n *\n * // Track events as metrics\n * metrics.trackEvent('order.completed', {\n *   amount: 99.99,\n *   currency: 'USD'\n * })\n *\n * // Track conversion funnels\n * metrics.trackFunnelStep('checkout', 'started', { cartValue: 99.99 })\n * metrics.trackFunnelStep('checkout', 'completed', { cartValue: 99.99 })\n *\n * // Track outcomes\n * metrics.trackOutcome('payment.process', 'success', { amount: 99.99 })\n * metrics.trackOutcome('payment.process', 'failure', { error: 'insufficient_funds' })\n *\n * // Track values\n * metrics.trackValue('revenue', 149.99, { currency: 'USD' })\n * ```\n */\n\nimport {\n  type Counter,\n  type Histogram,\n  type Attributes,\n} from '@opentelemetry/api';\nimport { getConfig } from './config';\nimport { type Logger } from './logger';\nimport {\n  type EventAttributes,\n  type FunnelStatus,\n  type OutcomeStatus,\n} from './event-subscriber';\nimport { type MetricsCollector } from './metric-testing';\n\n// Re-export types for convenience\nexport type {\n  EventAttributes,\n  FunnelStatus,\n  OutcomeStatus,\n} from './event-subscriber';\n\n/**\n * Metrics class for tracking business metrics in OpenTelemetry\n *\n * Track critical business indicators such as:\n * - User events (signups, purchases, feature usage) as metrics\n * - Conversion funnels (signup → activation → purchase)\n * - Business outcomes (success/failure rates)\n * - Value metrics (revenue, counts, etc.)\n *\n * All metrics are sent to OpenTelemetry (OTLP/Prometheus/Grafana).\n */\n/**\n * Metric configuration for customizing metric names and descriptions\n */\nexport interface MetricConfig {\n  /** Metric name (e.g., 'metrics.events' or 'custom.events') */\n  name?: string;\n  /** Metric description */\n  description?: string;\n  /** Metric unit (default: '1') */\n  unit?: string;\n}\n\n/**\n * Metrics options\n */\nexport interface MetricsOptions {\n  /** Optional logger for audit trail */\n  logger?: Logger;\n  /** Optional collector for testing (captures metrics in memory) */\n  collector?: MetricsCollector;\n\n  /**\n   * Namespace for metrics (default: 'metrics')\n   * Results in metrics like: {serviceName}.{namespace}.events\n   */\n  namespace?: string;\n\n  /**\n   * Custom metric configurations\n   * Override metric names, descriptions, and units\n   */\n  metrics?: {\n    events?: MetricConfig;\n    funnel?: MetricConfig;\n    outcomes?: MetricConfig;\n    value?: MetricConfig;\n  };\n}\n\nexport class Metric {\n  private serviceName: string;\n  private eventCounter: Counter;\n  private funnelCounter: Counter;\n  private outcomeCounter: Counter;\n  private valueHistogram: Histogram;\n  private logger?: Logger;\n  private collector?: MetricsCollector;\n\n  /**\n   * Create a new Metrics instance\n   *\n   * @param serviceName - Service name for metric namespacing\n   * @param options - Optional configuration (logger, collector, namespace, metrics)\n   *\n   * @example Basic usage (default 'metrics' namespace)\n   * ```typescript\n   * const metrics = new Metric('checkout');\n   * // Creates: checkout.metrics.events, checkout.metrics.funnel, etc.\n   * ```\n   *\n   * @example Custom namespace\n   * ```typescript\n   * const metrics = new Metric('api', { namespace: 'business' });\n   * // Creates: api.business.events, api.business.funnel, etc.\n   * ```\n   *\n   * @example Custom metric names and descriptions\n   * ```typescript\n   * const metrics = new Metric('payments', {\n   *   metrics: {\n   *     outcomes: {\n   *       name: 'payments.transactions',\n   *       description: 'Payment transaction outcomes',\n   *       unit: 'transactions'\n   *     },\n   *     value: {\n   *       name: 'payments.revenue',\n   *       description: 'Payment revenue in USD',\n   *       unit: 'USD'\n   *     }\n   *   }\n   * });\n   * ```\n   */\n  constructor(serviceName: string, options: MetricsOptions = {}) {\n    this.serviceName = serviceName;\n    this.logger = options.logger;\n    this.collector = options.collector;\n\n    const config = getConfig();\n    const meter = config.meter;\n\n    // Default namespace and metric configurations\n    const namespace = options.namespace || 'metrics';\n    const metricsConfig = options.metrics || {};\n\n    // Event counter configuration\n    const eventsConfig = metricsConfig.events || {};\n    this.eventCounter = meter.createCounter(\n      eventsConfig.name || `${serviceName}.${namespace}.events`,\n      {\n        description: eventsConfig.description || 'Count of business events',\n        unit: eventsConfig.unit || '1',\n      },\n    );\n\n    // Funnel counter configuration\n    const funnelConfig = metricsConfig.funnel || {};\n    this.funnelCounter = meter.createCounter(\n      funnelConfig.name || `${serviceName}.${namespace}.funnel`,\n      {\n        description: funnelConfig.description || 'Conversion funnel tracking',\n        unit: funnelConfig.unit || '1',\n      },\n    );\n\n    // Outcome counter configuration\n    const outcomesConfig = metricsConfig.outcomes || {};\n    this.outcomeCounter = meter.createCounter(\n      outcomesConfig.name || `${serviceName}.${namespace}.outcomes`,\n      {\n        description:\n          outcomesConfig.description || 'Outcome tracking (success/failure)',\n        unit: outcomesConfig.unit || '1',\n      },\n    );\n\n    // Value histogram configuration\n    const valueConfig = metricsConfig.value || {};\n    this.valueHistogram = meter.createHistogram(\n      valueConfig.name || `${serviceName}.${namespace}.value`,\n      {\n        description:\n          valueConfig.description || 'Value metrics (revenue, counts, etc.)',\n        unit: valueConfig.unit || '1',\n      },\n    );\n  }\n\n  /**\n   * Track a business event as a metric\n   *\n   * Use this for tracking user actions, business events, product usage as metrics:\n   * - \"user.signup\"\n   * - \"order.completed\"\n   * - \"feature.used\"\n   *\n   * @example\n   * ```typescript\n   * // Track user signup as metric\n   * metrics.trackEvent('user.signup', {\n   *   userId: '123',\n   *   plan: 'pro'\n   * })\n   *\n   * // Track order as metric\n   * metrics.trackEvent('order.completed', {\n   *   orderId: 'ord_123',\n   *   amount: 99.99\n   * })\n   * ```\n   */\n  trackEvent(eventName: string, attributes?: EventAttributes): void {\n    const attrs: Attributes = {\n      service: this.serviceName,\n      event: eventName,\n      ...attributes,\n    };\n\n    this.eventCounter.add(1, attrs);\n\n    this.logger?.info(\n      {\n        event: eventName,\n        attributes,\n      },\n      'Metric event tracked',\n    );\n\n    // Record for testing\n    this.collector?.recordEvent({\n      event: eventName,\n      attributes,\n      service: this.serviceName,\n      timestamp: Date.now(),\n    });\n  }\n\n  /**\n   * Track conversion funnel steps as metrics\n   *\n   * Monitor where users drop off in multi-step processes.\n   *\n   * @example\n   * ```typescript\n   * // Track signup funnel\n   * metrics.trackFunnelStep('signup', 'started', { userId: '123' })\n   * metrics.trackFunnelStep('signup', 'email_verified', { userId: '123' })\n   * metrics.trackFunnelStep('signup', 'completed', { userId: '123' })\n   *\n   * // Track checkout flow\n   * metrics.trackFunnelStep('checkout', 'started', { cartValue: 99.99 })\n   * metrics.trackFunnelStep('checkout', 'payment_info', { cartValue: 99.99 })\n   * metrics.trackFunnelStep('checkout', 'completed', { cartValue: 99.99 })\n   * ```\n   */\n  trackFunnelStep(\n    funnelName: string,\n    status: FunnelStatus,\n    attributes?: EventAttributes,\n  ): void {\n    const attrs: Attributes = {\n      service: this.serviceName,\n      funnel: funnelName,\n      status,\n      ...attributes,\n    };\n\n    this.funnelCounter.add(1, attrs);\n\n    this.logger?.info(\n      {\n        funnel: funnelName,\n        status,\n        attributes,\n      },\n      'Funnel step tracked',\n    );\n\n    // Record for testing\n    this.collector?.recordFunnelStep({\n      funnel: funnelName,\n      status,\n      attributes,\n      service: this.serviceName,\n      timestamp: Date.now(),\n    });\n  }\n\n  /**\n   * Track outcomes (success/failure/partial) as metrics\n   *\n   * Monitor success rates of critical operations.\n   *\n   * @example\n   * ```typescript\n   * // Track email delivery\n   * metrics.trackOutcome('email.delivery', 'success', {\n   *   recipientType: 'user',\n   *   emailType: 'welcome'\n   * })\n   *\n   * metrics.trackOutcome('email.delivery', 'failure', {\n   *   recipientType: 'user',\n   *   errorCode: 'invalid_email'\n   * })\n   *\n   * // Track payment processing\n   * metrics.trackOutcome('payment.process', 'success', { amount: 99.99 })\n   * metrics.trackOutcome('payment.process', 'failure', { error: 'insufficient_funds' })\n   * ```\n   */\n  trackOutcome(\n    operationName: string,\n    status: OutcomeStatus,\n    attributes?: EventAttributes,\n  ): void {\n    const attrs: Attributes = {\n      service: this.serviceName,\n      operation: operationName,\n      status,\n      ...attributes,\n    };\n\n    this.outcomeCounter.add(1, attrs);\n\n    this.logger?.info(\n      {\n        operation: operationName,\n        status,\n        attributes,\n      },\n      'Outcome tracked',\n    );\n\n    // Record for testing\n    this.collector?.recordOutcome({\n      operation: operationName,\n      status,\n      attributes,\n      service: this.serviceName,\n      timestamp: Date.now(),\n    });\n  }\n\n  /**\n   * Track value metrics\n   *\n   * Record numerical values like revenue, transaction amounts,\n   * item counts, processing times, engagement scores, etc.\n   *\n   * @example\n   * ```typescript\n   * // Track revenue\n   * metrics.trackValue('order.revenue', 149.99, {\n   *   currency: 'USD',\n   *   productCategory: 'electronics'\n   * })\n   *\n   * // Track items per cart\n   * metrics.trackValue('cart.item_count', 5, {\n   *   userId: '123'\n   * })\n   *\n   * // Track processing time\n   * metrics.trackValue('api.response_time', 250, {\n   *   unit: 'ms',\n   *   endpoint: '/api/checkout'\n   * })\n   * ```\n   */\n  trackValue(\n    metricName: string,\n    value: number,\n    attributes?: EventAttributes,\n  ): void {\n    const attrs: Attributes = {\n      service: this.serviceName,\n      metric: metricName,\n      ...attributes,\n    };\n\n    this.valueHistogram.record(value, attrs);\n\n    this.logger?.debug(\n      {\n        metric: metricName,\n        value,\n        attributes,\n      },\n      'Value metric tracked',\n    );\n\n    // Record for testing\n    this.collector?.recordValue({\n      metric: metricName,\n      value,\n      attributes,\n      service: this.serviceName,\n      timestamp: Date.now(),\n    });\n  }\n}\n\n/**\n * Global metrics instances (singleton pattern)\n */\nconst metricsInstances = new Map<string, Metric>();\n\n/**\n * Get or create a Metrics instance for a service\n *\n * @param serviceName - Service name for metric namespacing\n * @param logger - Optional logger\n * @returns Metrics instance\n *\n * @example\n * ```typescript\n * const metrics = getMetrics('checkout')\n * metrics.trackEvent('order.completed', { orderId: '123' })\n * ```\n */\nexport function getMetrics(serviceName: string, logger?: Logger): Metric {\n  if (!metricsInstances.has(serviceName)) {\n    metricsInstances.set(serviceName, new Metric(serviceName, { logger }));\n  }\n  return metricsInstances.get(serviceName)!;\n}\n\n/**\n * Reset all metrics instances (mainly for testing)\n */\nexport function resetMetrics(): void {\n  metricsInstances.clear();\n}\n"],"mappings":"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"}