{"version":3,"sources":["../src/otel.ts"],"names":[],"mappings":"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","file":"otel.cjs","sourcesContent":["/**\n * OpenTelemetry instrumentation — drop-in tracing + metrics for the MP toolkit.\n *\n * # Why a subpath?\n *\n * `@opentelemetry/api` is an OPTIONAL peer dep. Consumers who don't use\n * OpenTelemetry don't pay the bundle cost. Consumers who DO use it import\n * from `@ar-agents/mercadopago/otel` and get instant instrumentation:\n * spans for every MP request, metrics for latency/errors/rate-limit\n * remaining, and proper context propagation to your downstream traces.\n *\n * # Setup\n *\n * 1. Install: `pnpm add @opentelemetry/api`\n * 2. Wire your tracer + meter at app boot (per OpenTelemetry standard).\n * 3. Pass the instrumented hooks to MercadoPagoClient:\n *\n *    ```ts\n *    import { MercadoPagoClient } from \"@ar-agents/mercadopago\";\n *    import { createOtelHooks } from \"@ar-agents/mercadopago/otel\";\n *\n *    const otel = createOtelHooks({ serviceName: \"billing-bot\" });\n *    const client = new MercadoPagoClient({\n *      accessToken: process.env.MP_ACCESS_TOKEN!,\n *      onCall: otel.onCall,\n *      traceContext: otel.traceContext,\n *    });\n *    ```\n *\n * # What gets instrumented\n *\n * - **Spans**: one span per MP request, named `mp.{method}.{path}` (e.g.,\n *   `mp.GET./v1/payments/123`). Includes attributes: status code,\n *   request_id, retried count, success bool, MP rate-limit remaining,\n *   circuit breaker state.\n * - **Metrics**: `mp.requests.duration` histogram (ms), `mp.requests.count`\n *   counter (labeled by success/method/path/status), `mp.rate_limit.remaining`\n *   gauge.\n *\n * # No-op fallback\n *\n * If `@opentelemetry/api` isn't installed at runtime, the hooks degrade to\n * no-ops gracefully (without throwing) so the toolkit remains importable\n * even without OTEL configured.\n */\n\n// Types-only imports — `@opentelemetry/api` is an OPTIONAL peer dep.\n// We resolve at runtime via dynamic import; if absent, we run as no-ops.\ntype Tracer = {\n  startSpan(name: string, options?: unknown): {\n    setAttribute(k: string, v: unknown): void;\n    setStatus(s: { code: number; message?: string }): void;\n    end(): void;\n    spanContext(): { traceId: string; spanId: string; traceFlags: number };\n  };\n};\ntype Histogram = { record(v: number, attributes?: Record<string, unknown>): void };\ntype Counter = { add(v: number, attributes?: Record<string, unknown>): void };\ntype Gauge = { record(v: number, attributes?: Record<string, unknown>): void };\n\ninterface OtelApi {\n  trace: {\n    getTracer(name: string, version?: string): Tracer;\n    getActiveSpan(): { spanContext(): { traceId: string; spanId: string; traceFlags: number } } | undefined;\n  };\n  metrics: {\n    getMeter(name: string, version?: string): {\n      createHistogram(name: string, opts?: { description?: string; unit?: string }): Histogram;\n      createCounter(name: string, opts?: { description?: string }): Counter;\n      createGauge?(name: string, opts?: { description?: string; unit?: string }): Gauge;\n    };\n  };\n  SpanStatusCode: { OK: number; ERROR: number; UNSET: number };\n}\n\nlet cachedApi: OtelApi | null | undefined = undefined;\n\nasync function loadOtelApi(): Promise<OtelApi | null> {\n  if (cachedApi !== undefined) return cachedApi;\n  try {\n    const mod = (await import(\n      /* @vite-ignore */ \"@opentelemetry/api\"\n    )) as unknown as OtelApi;\n    cachedApi = mod;\n    return mod;\n  } catch {\n    cachedApi = null;\n    return null;\n  }\n}\n\nexport interface OtelHooksOptions {\n  /** Service name shown in trace UIs. Default \"ar-agents-mercadopago\". */\n  serviceName?: string;\n  /** Toolkit version (defaults to a static \"0.10.x\"). */\n  version?: string;\n  /**\n   * Attributes added to every span/metric (e.g., environment, deployment_id).\n   */\n  attributes?: Record<string, string | number | boolean>;\n}\n\n/**\n * Build OpenTelemetry-aware hooks for `MercadoPagoClient`. Returns:\n *\n * - `onCall`: wires every request into traces + metrics\n * - `traceContext`: extracts active span context for traceparent propagation\n *\n * Both degrade to no-ops if `@opentelemetry/api` isn't installed.\n */\nexport function createOtelHooks(opts: OtelHooksOptions = {}): {\n  onCall: (event: {\n    method: string;\n    path: string;\n    durationMs: number;\n    httpStatus: number | null;\n    retried: number;\n    success: boolean;\n    requestId?: string | null;\n    rateLimit?: { remaining: number | null; resetSeconds: number | null };\n    circuitState?: \"CLOSED\" | \"OPEN\" | \"HALF_OPEN\";\n    traceContext?: { traceId?: string; spanId?: string };\n  }) => void;\n  traceContext: () => { traceId?: string; spanId?: string; traceFlags?: number } | undefined;\n} {\n  const serviceName = opts.serviceName ?? \"ar-agents-mercadopago\";\n  const version = opts.version ?? \"0.10.0\";\n  const baseAttrs = opts.attributes ?? {};\n\n  // Lazy state — resolved on first invocation (so module load is sync + safe).\n  let initialized = false;\n  let api: OtelApi | null = null;\n  let durationHist: Histogram | null = null;\n  let requestCounter: Counter | null = null;\n  let rateLimitGauge: Gauge | null = null;\n\n  const ensureInit = async () => {\n    if (initialized) return;\n    initialized = true;\n    api = await loadOtelApi();\n    if (!api) return;\n    const meter = api.metrics.getMeter(serviceName, version);\n    durationHist = meter.createHistogram(\"mp.requests.duration\", {\n      description: \"MP API request duration\",\n      unit: \"ms\",\n    });\n    requestCounter = meter.createCounter(\"mp.requests.count\", {\n      description: \"MP API requests count by outcome\",\n    });\n    rateLimitGauge = meter.createGauge?.(\"mp.rate_limit.remaining\", {\n      description: \"MP-reported rate limit remaining at last response\",\n      unit: \"1\",\n    }) ?? null;\n  };\n\n  return {\n    onCall: (event) => {\n      // Don't await — fire-and-forget. Best-effort observability.\n      void (async () => {\n        await ensureInit();\n        if (!api) return;\n        const attrs: Record<string, unknown> = {\n          ...baseAttrs,\n          \"mp.method\": event.method,\n          \"mp.path\": event.path,\n          \"mp.success\": event.success,\n          \"mp.retried\": event.retried,\n        };\n        if (event.httpStatus !== null) attrs[\"http.status_code\"] = event.httpStatus;\n        if (event.requestId) attrs[\"mp.request_id\"] = event.requestId;\n        if (event.circuitState) attrs[\"mp.circuit_state\"] = event.circuitState;\n\n        durationHist?.record(event.durationMs, attrs);\n        requestCounter?.add(1, attrs);\n        if (event.rateLimit?.remaining !== null && event.rateLimit?.remaining !== undefined) {\n          rateLimitGauge?.record(event.rateLimit.remaining, {\n            \"mp.path\": event.path,\n          });\n        }\n\n        // Span: emit a synthetic child span scoped to this request's duration.\n        const tracer = api.trace.getTracer(serviceName, version);\n        const span = tracer.startSpan(`mp.${event.method}.${event.path}`, {\n          attributes: attrs,\n        });\n        if (event.success) {\n          span.setStatus({ code: api.SpanStatusCode.OK });\n        } else {\n          span.setStatus({\n            code: api.SpanStatusCode.ERROR,\n            message: `MP request failed (status=${event.httpStatus})`,\n          });\n        }\n        span.end();\n      })();\n    },\n\n    traceContext: () => {\n      // Synchronous getter — must be cheap. Resolves the OTEL active span\n      // context if available; returns undefined if OTEL isn't installed.\n      if (!api) {\n        // Try to use the cached api if loadOtelApi already ran\n        if (cachedApi) api = cachedApi;\n      }\n      if (!api) return undefined;\n      const span = api.trace.getActiveSpan();\n      if (!span) return undefined;\n      const ctx = span.spanContext();\n      return {\n        traceId: ctx.traceId,\n        spanId: ctx.spanId,\n        traceFlags: ctx.traceFlags,\n      };\n    },\n  };\n}\n"]}