autotel 5.0.0 → 6.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.
Files changed (85) hide show
  1. package/dist/auto.cjs +1 -1
  2. package/dist/auto.js +1 -1
  3. package/dist/correlation-id.cjs +9 -9
  4. package/dist/correlation-id.js +1 -1
  5. package/dist/decorators.cjs +2 -2
  6. package/dist/decorators.cjs.map +1 -1
  7. package/dist/decorators.js +1 -1
  8. package/dist/event.cjs +4 -4
  9. package/dist/event.js +1 -1
  10. package/dist/{functional-Bit6noRu.js → functional-BnDUxnSl.js} +2 -4
  11. package/dist/{functional-Bit6noRu.js.map → functional-BnDUxnSl.js.map} +1 -1
  12. package/dist/{functional-Cj7PSItS.cjs → functional-DcLBuCPz.cjs} +12 -14
  13. package/dist/{functional-Cj7PSItS.cjs.map → functional-DcLBuCPz.cjs.map} +1 -1
  14. package/dist/functional.cjs +1 -1
  15. package/dist/functional.js +1 -1
  16. package/dist/http.cjs +2 -2
  17. package/dist/http.cjs.map +1 -1
  18. package/dist/http.js +1 -1
  19. package/dist/index.cjs +23 -164
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +2 -2
  22. package/dist/index.d.cts.map +1 -1
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +4 -145
  26. package/dist/index.js.map +1 -1
  27. package/dist/init-C3RApHKJ.cjs +3371 -0
  28. package/dist/init-C3RApHKJ.cjs.map +1 -0
  29. package/dist/{init-CTyqG9qR.d.ts → init-D6n2diBy.d.ts} +21 -2
  30. package/dist/init-D6n2diBy.d.ts.map +1 -0
  31. package/dist/init-R-gSEXkJ.js +3190 -0
  32. package/dist/init-R-gSEXkJ.js.map +1 -0
  33. package/dist/{init-a4yPDYqC.d.cts → init-XzXzrarP.d.cts} +21 -2
  34. package/dist/init-XzXzrarP.d.cts.map +1 -0
  35. package/dist/instrumentation.cjs +1 -1
  36. package/dist/instrumentation.js +1 -1
  37. package/dist/messaging.cjs +1 -1
  38. package/dist/messaging.js +1 -1
  39. package/dist/semantic-helpers.cjs +1 -1
  40. package/dist/semantic-helpers.js +1 -1
  41. package/dist/webhook.cjs +1 -1
  42. package/dist/webhook.js +1 -1
  43. package/dist/workflow-distributed.cjs +1 -1
  44. package/dist/workflow-distributed.js +1 -1
  45. package/dist/workflow.cjs +1 -1
  46. package/dist/workflow.js +1 -1
  47. package/dist/yaml-config.d.cts +1 -1
  48. package/dist/yaml-config.d.ts +1 -1
  49. package/package.json +2 -3
  50. package/dist/event-CwIpFdoY.cjs +0 -778
  51. package/dist/event-CwIpFdoY.cjs.map +0 -1
  52. package/dist/event-pLMF5b3d.js +0 -761
  53. package/dist/event-pLMF5b3d.js.map +0 -1
  54. package/dist/init-B2uvZReD.cjs +0 -1120
  55. package/dist/init-B2uvZReD.cjs.map +0 -1
  56. package/dist/init-CTyqG9qR.d.ts.map +0 -1
  57. package/dist/init-CdEWjhvl.js +0 -1043
  58. package/dist/init-CdEWjhvl.js.map +0 -1
  59. package/dist/init-a4yPDYqC.d.cts.map +0 -1
  60. package/dist/operation-context-CKBoA4Qy.js +0 -59
  61. package/dist/operation-context-CKBoA4Qy.js.map +0 -1
  62. package/dist/operation-context-hzKbTKnt.cjs +0 -72
  63. package/dist/operation-context-hzKbTKnt.cjs.map +0 -1
  64. package/dist/track-BBcxEJEC.js +0 -1110
  65. package/dist/track-BBcxEJEC.js.map +0 -1
  66. package/dist/track-BI6dmZ2X.cjs +0 -1207
  67. package/dist/track-BI6dmZ2X.cjs.map +0 -1
  68. package/skills/analyze-traces/SKILL.md +0 -209
  69. package/skills/autotel-core/SKILL.md +0 -166
  70. package/skills/autotel-events/SKILL.md +0 -84
  71. package/skills/autotel-frameworks/SKILL.md +0 -125
  72. package/skills/autotel-instrumentation/SKILL.md +0 -150
  73. package/skills/autotel-request-logging/SKILL.md +0 -99
  74. package/skills/autotel-structured-errors/SKILL.md +0 -125
  75. package/skills/build-audit-trails/SKILL.md +0 -460
  76. package/skills/build-audit-trails/references/audit-queries.md +0 -73
  77. package/skills/build-audit-trails/references/framework-wiring.md +0 -196
  78. package/skills/debug-missing-spans/SKILL.md +0 -250
  79. package/skills/migrate-to-autotel/SKILL.md +0 -269
  80. package/skills/review-otel-patterns/SKILL.md +0 -540
  81. package/skills/review-otel-patterns/references/code-review.md +0 -76
  82. package/skills/review-otel-patterns/references/processor-pipeline.md +0 -205
  83. package/skills/review-otel-patterns/references/structured-errors.md +0 -102
  84. package/skills/review-otel-patterns/references/wide-spans.md +0 -85
  85. package/skills/tune-sampling/SKILL.md +0 -290
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-C3RApHKJ.cjs","names":["safeRequire","propagation","otelContext","requireModule","REDACTOR_PRESETS","AlwaysOnSampler","AlwaysOffSampler","TraceIdRatioBasedSampler","ParentBasedSampler","constants","nodeAsyncHooks","context","contextWithManager","propagation","nodeAsyncHooks","context","propagation","trace","DEFAULT_CONFIG","getRuntimeConfig","DEFAULT_CONFIG","trace","nodeAsyncHooks","trace","context","propagation","getForceFlushableProvider","SamplingDecision","AUTOTEL_SAMPLING_RATE","requireModule","OTLPTraceExporterHTTP","OTLPMetricExporterHTTP","OTLPLogExporterHTTP","safeRequire","loadYamlConfig","normalizeAttributeRedactorConfig","ATTR_SERVICE_NAME","ATTR_SERVICE_VERSION","TailSamplingSpanProcessor","BatchSpanProcessor","SimpleSpanProcessor","PrettyConsoleExporter","ConsoleSpanExporter","CanonicalLogLineProcessor","AttributeRedactingProcessor","SpanNameNormalizingProcessor","FilteringSpanProcessor","PeriodicExportingMetricReader","BatchLogRecordProcessor","resolveSamplingPreset","samplingPresets","NodeSDK"],"sources":["../src/posthog-logs.ts","../src/baggage-span-processor.ts","../src/redact-values.ts","../src/env-config.ts","../src/devtools.ts","../src/process-handlers.ts","../src/rate-limiter.ts","../src/trace-context.ts","../src/correlation-id.ts","../src/event-queue.ts","../src/validation.ts","../src/track.ts","../src/circuit-breaker.ts","../src/operation-context.ts","../src/events-config.ts","../src/event.ts","../src/shutdown.ts","../src/init.ts"],"sourcesContent":["import type { LogRecordProcessor, SdkLogRecord } from '@opentelemetry/sdk-logs';\nimport type { Context } from '@opentelemetry/api';\nimport type { AnyValue } from '@opentelemetry/api-logs';\nimport { safeRequire } from './node-require';\nimport type { StringRedactor } from './redact-values';\n\nexport class RedactingLogRecordProcessor implements LogRecordProcessor {\n constructor(\n private wrapped: LogRecordProcessor,\n private redact: StringRedactor,\n ) {}\n\n onEmit(logRecord: SdkLogRecord, context?: Context): void {\n if (logRecord.body && typeof logRecord.body === 'string') {\n logRecord.body = this.redact(logRecord.body);\n }\n if (logRecord.attributes) {\n for (const [key, value] of Object.entries(logRecord.attributes)) {\n if (typeof value === 'string') {\n logRecord.attributes[key] = this.redact(value);\n } else if (Array.isArray(value)) {\n logRecord.attributes[key] = value.map((item: AnyValue) =>\n typeof item === 'string' ? this.redact(item) : item,\n );\n }\n }\n }\n this.wrapped.onEmit(logRecord, context);\n }\n\n shutdown(): Promise<void> {\n return this.wrapped.shutdown();\n }\n\n forceFlush(): Promise<void> {\n return this.wrapped.forceFlush();\n }\n}\n\nexport interface PostHogConfig {\n /** OTLP logs endpoint URL (e.g., https://us.i.posthog.com/i/v1/logs?token=phc_xxx) */\n url: string;\n}\n\n/**\n * Build log record processors for PostHog OTLP logs integration.\n *\n * Resolution order:\n * 1. config.url if provided\n * 2. POSTHOG_LOGS_URL env var\n * 3. Empty array (disabled)\n */\nexport function buildPostHogLogProcessors(\n config: PostHogConfig | undefined,\n stringRedactor?: StringRedactor | null,\n): LogRecordProcessor[] {\n const url = config?.url || process.env.POSTHOG_LOGS_URL;\n if (!url) return [];\n\n const sdkLogs = safeRequire<{\n BatchLogRecordProcessor: new (options: {\n exporter: unknown;\n }) => LogRecordProcessor;\n }>('@opentelemetry/sdk-logs');\n\n const exporterModule = safeRequire<{\n OTLPLogExporter: new (config: { url: string }) => unknown;\n }>('@opentelemetry/exporter-logs-otlp-http');\n\n if (!sdkLogs || !exporterModule) return [];\n\n const exporter = new exporterModule.OTLPLogExporter({ url });\n let processor: LogRecordProcessor = new sdkLogs.BatchLogRecordProcessor({\n exporter,\n });\n if (stringRedactor) {\n processor = new RedactingLogRecordProcessor(processor, stringRedactor);\n }\n\n return [processor];\n}\n","/**\n * Span processor that copies baggage entries to span attributes\n *\n * This makes baggage visible in trace UIs without manual attribute setting.\n * Enabled via init({ baggage: true }) or init({ baggage: 'custom-prefix' })\n */\n\nimport type { Span, Context } from '@opentelemetry/api';\nimport { propagation, context as otelContext } from '@opentelemetry/api';\nimport type { SpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport type { ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { requireModule } from './node-require';\n\nexport interface BaggageSpanProcessorOptions {\n /**\n * Prefix for baggage attributes\n * @default 'baggage.'\n */\n prefix?: string;\n}\n\n/**\n * Span processor that automatically copies baggage entries to span attributes\n *\n * This makes baggage visible in trace UIs (Jaeger, Grafana, DataDog, etc.)\n * without manually calling ctx.setAttribute() for each baggage entry.\n *\n * @example Enable in init()\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: true // Uses default 'baggage.' prefix\n * });\n *\n * // Now baggage automatically appears as span attributes\n * await withBaggage({\n * baggage: { 'tenant.id': 't1', 'user.id': 'u1' },\n * fn: async () => {\n * // Span has baggage.tenant.id and baggage.user.id attributes!\n * }\n * });\n * ```\n *\n * @example Custom prefix\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: 'ctx' // Uses 'ctx.' prefix\n * });\n * // Creates attributes: ctx.tenant.id, ctx.user.id\n * ```\n */\nexport class BaggageSpanProcessor implements SpanProcessor {\n private readonly prefix: string;\n\n constructor(options: BaggageSpanProcessorOptions = {}) {\n this.prefix = options.prefix ?? 'baggage.';\n }\n\n onStart(span: Span, parentContext: Context): void {\n // Read baggage from parentContext first (spans created with explicit context)\n // Then fall back to active context (spans created without explicit context)\n // Also check getActiveContextWithBaggage() to see baggage set via ctx.setBaggage()\n let baggage = propagation.getBaggage(parentContext);\n if (!baggage) {\n baggage = propagation.getBaggage(otelContext.active());\n }\n // Check stored context from ctx.setBaggage() if still no baggage\n if (!baggage) {\n try {\n const { getActiveContextWithBaggage } = requireModule<{\n getActiveContextWithBaggage: () => Context;\n }>('./trace-context');\n const storedContext = getActiveContextWithBaggage();\n baggage = propagation.getBaggage(storedContext);\n } catch {\n // Fallback if trace-context isn't available\n }\n }\n if (!baggage) return;\n\n // Copy all baggage entries to span attributes\n for (const [key, entry] of baggage.getAllEntries()) {\n span.setAttribute(`${this.prefix}${key}`, entry.value);\n }\n }\n\n onEnd(_span: ReadableSpan): void {\n // No-op - required by SpanProcessor interface\n }\n\n async shutdown(): Promise<void> {\n // No-op\n }\n\n async forceFlush(): Promise<void> {\n // No-op\n }\n}\n","/** Standalone string redaction for use outside the span processor pipeline. */\n\nimport {\n REDACTOR_PRESETS,\n type AttributeRedactorConfig,\n type AttributeRedactorPreset,\n type ValuePatternConfig,\n} from './attribute-redacting-processor';\n\nexport type StringRedactor = (value: string) => string;\nexport function createStringRedactor(\n config: AttributeRedactorConfig | AttributeRedactorPreset,\n): StringRedactor {\n const resolved =\n typeof config === 'string' ? REDACTOR_PRESETS[config] : config;\n const valuePatterns: ValuePatternConfig[] = resolved.valuePatterns ?? [];\n const defaultReplacement = resolved.replacement ?? '[REDACTED]';\n\n return (value: string): string => {\n let result = value;\n for (const { pattern, replacement, mask } of valuePatterns) {\n pattern.lastIndex = 0;\n // Smart masks (e.g. email → a***@***.com) take precedence over the\n // static replacement so callers see the same output as the\n // span-attribute redactor does.\n result = mask\n ? result.replaceAll(pattern, (match) => mask(match))\n : result.replaceAll(pattern, replacement ?? defaultReplacement);\n }\n return result;\n };\n}\n","/**\n * Standard OpenTelemetry environment variables\n */\nimport type { Sampler as OtelSampler } from '@opentelemetry/sdk-trace-base';\nimport {\n AlwaysOffSampler,\n AlwaysOnSampler,\n ParentBasedSampler,\n TraceIdRatioBasedSampler,\n} from '@opentelemetry/sdk-trace-base';\n\nexport interface OtelEnvVars {\n OTEL_SERVICE_NAME?: string;\n OTEL_EXPORTER_OTLP_ENDPOINT?: string;\n OTEL_EXPORTER_OTLP_HEADERS?: string;\n OTEL_RESOURCE_ATTRIBUTES?: string;\n OTEL_EXPORTER_OTLP_PROTOCOL?: 'http' | 'grpc';\n OTEL_TRACES_SAMPLER?: string;\n OTEL_TRACES_SAMPLER_ARG?: string;\n}\n\n/**\n * Parsed resource attributes as key-value pairs\n */\nexport interface ResourceAttributes {\n [key: string]: string;\n}\n\n/**\n * Parsed OTLP headers as key-value pairs\n */\nexport interface OtlpHeaders {\n [key: string]: string;\n}\n\n/**\n * Environment-resolved configuration (subset of AutotelConfig)\n * Defined locally to avoid circular dependency with init.ts\n */\nexport interface EnvConfig {\n service?: string;\n endpoint?: string;\n protocol?: 'http' | 'grpc';\n headers?: Record<string, string>;\n resourceAttributes?: Record<string, string>;\n otelSampler?: OtelSampler;\n}\n\n/**\n * Validate URL format\n */\nfunction isValidUrl(urlString: string): boolean {\n try {\n const url = new URL(urlString);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve OpenTelemetry environment variables from process.env\n */\nexport function resolveOtelEnv(): OtelEnvVars {\n const env: OtelEnvVars = {};\n\n // OTEL_SERVICE_NAME - optional string\n if (process.env.OTEL_SERVICE_NAME) {\n const value = process.env.OTEL_SERVICE_NAME.trim();\n if (value) {\n env.OTEL_SERVICE_NAME = value;\n }\n }\n\n // OTEL_EXPORTER_OTLP_ENDPOINT - optional URL\n if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {\n const value = process.env.OTEL_EXPORTER_OTLP_ENDPOINT.trim();\n if (value && isValidUrl(value)) {\n env.OTEL_EXPORTER_OTLP_ENDPOINT = value;\n }\n }\n\n // OTEL_EXPORTER_OTLP_HEADERS - optional string\n if (process.env.OTEL_EXPORTER_OTLP_HEADERS) {\n const value = process.env.OTEL_EXPORTER_OTLP_HEADERS.trim();\n if (value) {\n env.OTEL_EXPORTER_OTLP_HEADERS = value;\n }\n }\n\n // OTEL_RESOURCE_ATTRIBUTES - optional string\n if (process.env.OTEL_RESOURCE_ATTRIBUTES) {\n const value = process.env.OTEL_RESOURCE_ATTRIBUTES.trim();\n if (value) {\n env.OTEL_RESOURCE_ATTRIBUTES = value;\n }\n }\n\n // OTEL_EXPORTER_OTLP_PROTOCOL - optional enum ('http' | 'grpc')\n if (process.env.OTEL_EXPORTER_OTLP_PROTOCOL) {\n const value = process.env.OTEL_EXPORTER_OTLP_PROTOCOL.trim().toLowerCase();\n if (value === 'http' || value === 'grpc') {\n env.OTEL_EXPORTER_OTLP_PROTOCOL = value;\n }\n }\n\n if (process.env.OTEL_TRACES_SAMPLER) {\n const value = process.env.OTEL_TRACES_SAMPLER.trim();\n if (value) {\n env.OTEL_TRACES_SAMPLER = value;\n }\n }\n\n if (process.env.OTEL_TRACES_SAMPLER_ARG) {\n const value = process.env.OTEL_TRACES_SAMPLER_ARG.trim();\n if (value) {\n env.OTEL_TRACES_SAMPLER_ARG = value;\n }\n }\n\n return env;\n}\n\nfunction parseRatioSamplerArg(\n samplerName: string,\n samplerArg: string | undefined,\n): number {\n if (samplerArg === undefined) {\n return 1;\n }\n\n const ratio = Number(samplerArg);\n if (!Number.isFinite(ratio) || ratio < 0 || ratio > 1) {\n console.error(\n `[autotel] Invalid OTEL_TRACES_SAMPLER_ARG=\"${samplerArg}\" for ${samplerName}. Expected a number in [0..1]. Falling back to 1.0.`,\n );\n return 1;\n }\n\n return ratio;\n}\n\nfunction warnOnUnusedSamplerArg(\n samplerName: string,\n samplerArg: string | undefined,\n): void {\n if (samplerArg !== undefined) {\n console.error(\n `[autotel] OTEL_TRACES_SAMPLER_ARG is not used by OTEL_TRACES_SAMPLER=\"${samplerName}\". Ignoring value \"${samplerArg}\".`,\n );\n }\n}\n\nexport function createSamplerFromEnv(\n env: Pick<OtelEnvVars, 'OTEL_TRACES_SAMPLER' | 'OTEL_TRACES_SAMPLER_ARG'>,\n): OtelSampler | undefined {\n const samplerName = env.OTEL_TRACES_SAMPLER;\n if (!samplerName) {\n return undefined;\n }\n\n switch (samplerName) {\n case 'always_on': {\n warnOnUnusedSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG);\n return new AlwaysOnSampler();\n }\n case 'always_off': {\n warnOnUnusedSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG);\n return new AlwaysOffSampler();\n }\n case 'traceidratio': {\n return new TraceIdRatioBasedSampler(\n parseRatioSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG),\n );\n }\n case 'parentbased_always_on': {\n warnOnUnusedSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG);\n return new ParentBasedSampler({ root: new AlwaysOnSampler() });\n }\n case 'parentbased_always_off': {\n warnOnUnusedSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG);\n return new ParentBasedSampler({ root: new AlwaysOffSampler() });\n }\n case 'parentbased_traceidratio': {\n return new ParentBasedSampler({\n root: new TraceIdRatioBasedSampler(\n parseRatioSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG),\n ),\n });\n }\n case 'jaeger_remote':\n case 'parentbased_jaeger_remote':\n case 'xray': {\n console.error(\n `[autotel] OTEL_TRACES_SAMPLER=\"${samplerName}\" is not supported yet by autotel. Falling back to the next sampler source.`,\n );\n return undefined;\n }\n default: {\n console.error(\n `[autotel] Unknown OTEL_TRACES_SAMPLER=\"${samplerName}\". Falling back to the next sampler source.`,\n );\n return undefined;\n }\n }\n}\n\n/**\n * Parse OTEL_RESOURCE_ATTRIBUTES from comma-separated key=value pairs\n * Example: \"service.version=1.0.0,deployment.environment=production\"\n */\nexport function parseResourceAttributes(\n input: string | undefined,\n): ResourceAttributes {\n if (!input || input.trim() === '') {\n return {};\n }\n\n const attributes: ResourceAttributes = {};\n const pairs = input.split(',');\n\n for (const pair of pairs) {\n const trimmedPair = pair.trim();\n if (!trimmedPair) continue;\n\n const equalIndex = trimmedPair.indexOf('=');\n if (equalIndex === -1) {\n // Invalid format, skip this pair\n continue;\n }\n\n const key = trimmedPair.slice(0, equalIndex).trim();\n const value = trimmedPair.slice(equalIndex + 1).trim();\n\n if (key && value) {\n attributes[key] = value;\n }\n }\n\n return attributes;\n}\n\n/**\n * Parse OTEL_EXPORTER_OTLP_HEADERS from comma-separated key=value pairs\n * Example: \"api-key=secret123,x-custom-header=value\"\n */\nexport function parseOtlpHeaders(input: string | undefined): OtlpHeaders {\n if (!input || input.trim() === '') {\n return {};\n }\n\n const headers: OtlpHeaders = {};\n const pairs = input.split(',');\n\n for (const pair of pairs) {\n const trimmedPair = pair.trim();\n if (!trimmedPair) continue;\n\n const equalIndex = trimmedPair.indexOf('=');\n if (equalIndex === -1) {\n // Invalid format, skip this pair\n continue;\n }\n\n const key = trimmedPair.slice(0, equalIndex).trim();\n const value = trimmedPair.slice(equalIndex + 1).trim();\n\n if (key && value) {\n headers[key] = value;\n }\n }\n\n return headers;\n}\n\n/**\n * Convert resolved environment variables to config\n */\nexport function envToConfig(env: OtelEnvVars): EnvConfig {\n const config: EnvConfig = {};\n\n if (env.OTEL_SERVICE_NAME) {\n config.service = env.OTEL_SERVICE_NAME;\n }\n\n if (env.OTEL_EXPORTER_OTLP_ENDPOINT) {\n config.endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT;\n }\n\n if (env.OTEL_EXPORTER_OTLP_PROTOCOL) {\n config.protocol = env.OTEL_EXPORTER_OTLP_PROTOCOL;\n }\n\n if (env.OTEL_EXPORTER_OTLP_HEADERS) {\n config.headers = parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS);\n }\n\n const resourceAttrs = parseResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);\n if (Object.keys(resourceAttrs).length > 0) {\n config.resourceAttributes = resourceAttrs;\n }\n\n const sampler = createSamplerFromEnv(env);\n if (sampler) {\n config.otelSampler = sampler;\n }\n\n return config;\n}\n\n/**\n * Main function to resolve config from environment variables\n */\nexport function resolveConfigFromEnv(): EnvConfig {\n const env = resolveOtelEnv();\n return envToConfig(env);\n}\n","export interface AutotelDevtoolsConfig {\n enabled?: boolean;\n endpoint?: string;\n embedded?: boolean;\n host?: string;\n port?: number;\n verbose?: boolean;\n}\n\nexport interface ResolvedAutotelDevtoolsConfig {\n enabled: boolean;\n endpoint?: string;\n embedded: boolean;\n host: string;\n port: number;\n verbose: boolean;\n}\n\nconst defaultHost = '127.0.0.1';\nconst defaultPort = 4318;\n\nexport function resolveDevtoolsConfig(\n config: boolean | AutotelDevtoolsConfig | undefined,\n): ResolvedAutotelDevtoolsConfig {\n if (!config) {\n return {\n enabled: false,\n endpoint: undefined,\n embedded: false,\n host: defaultHost,\n port: defaultPort,\n verbose: false,\n };\n }\n\n if (config === true) {\n return {\n enabled: true,\n endpoint: `http://${defaultHost}:${defaultPort}`,\n embedded: false,\n host: defaultHost,\n port: defaultPort,\n verbose: false,\n };\n }\n\n const enabled = config.enabled ?? true;\n const host = config.host ?? defaultHost;\n const port = config.port ?? defaultPort;\n const endpoint = config.endpoint ?? `http://${host}:${port}`;\n\n return {\n enabled,\n endpoint: enabled ? endpoint : undefined,\n embedded: enabled && (config.embedded ?? false),\n host,\n port,\n verbose: config.verbose ?? false,\n };\n}\n","import { constants } from 'node:os';\n\nlet removeOwnedHandlers: Array<() => void> = [];\n\nexport interface ProcessHandlersConfig {\n /** Signals that should trigger telemetry shutdown before process exit. Default: `['SIGTERM', 'SIGINT']`. */\n signals?: NodeJS.Signals[];\n /** Flush telemetry before exiting on uncaught exceptions or rejections. Default: `true`. */\n fatalErrors?: boolean;\n /** Maximum time to wait for telemetry shutdown before exiting. Default: 2000. */\n shutdownTimeoutMs?: number;\n}\n\nconst DEFAULT_SIGNALS: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n\nfunction signalExitCode(signal: NodeJS.Signals): number {\n return 128 + constants.signals[signal];\n}\n\n/**\n * Surface a fatal error before shutting down.\n *\n * Registering an `uncaughtException` / `unhandledRejection` listener overrides\n * Node's default of printing the stack to stderr, so without this a crash under\n * `fatalErrors` would exit silently. Autotel's own logger is silent by default,\n * so we print to stderr directly to guarantee the crash stays visible.\n */\nfunction reportFatalError(error: unknown, event: string): void {\n const err = error instanceof Error ? error : new Error(String(error));\n console.error(`[autotel] ${event}, flushing telemetry then exiting`, err);\n}\n\nexport function installProcessHandlers(\n config: ProcessHandlersConfig,\n shutdown: () => Promise<void>,\n): void {\n uninstallProcessHandlers();\n\n let shutdownInFlight: Promise<void> | undefined;\n const runShutdownOnce = (): Promise<void> => {\n if (shutdownInFlight) {\n return shutdownInFlight;\n }\n\n const timeoutMs = config.shutdownTimeoutMs ?? 2000;\n let timeoutHandle: NodeJS.Timeout | undefined;\n const shutdownAttempt = Promise.resolve()\n .then(shutdown)\n .catch(() => undefined);\n const timeout = new Promise<void>((resolve) => {\n timeoutHandle = setTimeout(resolve, timeoutMs);\n timeoutHandle.unref();\n });\n\n shutdownInFlight = Promise.race([shutdownAttempt, timeout]).then(() => {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n });\n return shutdownInFlight;\n };\n\n let exitScheduled = false;\n let resolvedExitCode = 0;\n let fatalLatched = false;\n\n // Exit once, after shutdown completes (or times out). A fatal error's code (1)\n // takes precedence over a signal's code, so a crash is never masked by a\n // concurrent SIGTERM/SIGINT arriving in the same shutdown window.\n const shutdownAndExit = (exitCode: number, fatal: boolean): void => {\n if (fatal) {\n if (!fatalLatched) {\n resolvedExitCode = exitCode;\n fatalLatched = true;\n }\n } else if (!exitScheduled && !fatalLatched) {\n resolvedExitCode = exitCode;\n }\n\n if (exitScheduled) {\n return;\n }\n exitScheduled = true;\n void runShutdownOnce().then(() => {\n // eslint-disable-next-line unicorn/no-process-exit\n process.exit(resolvedExitCode);\n });\n };\n\n for (const signal of config.signals ?? DEFAULT_SIGNALS) {\n const listener = () => {\n shutdownAndExit(signalExitCode(signal), false);\n };\n process.on(signal, listener);\n removeOwnedHandlers.push(() => {\n process.removeListener(signal, listener);\n });\n }\n\n if (config.fatalErrors ?? true) {\n const uncaughtExceptionListener = (error: unknown) => {\n reportFatalError(error, 'uncaughtException');\n shutdownAndExit(1, true);\n };\n const unhandledRejectionListener = (reason: unknown) => {\n reportFatalError(reason, 'unhandledRejection');\n shutdownAndExit(1, true);\n };\n process.on('uncaughtException', uncaughtExceptionListener);\n process.on('unhandledRejection', unhandledRejectionListener);\n removeOwnedHandlers.push(\n () => {\n process.removeListener('uncaughtException', uncaughtExceptionListener);\n },\n () => {\n process.removeListener(\n 'unhandledRejection',\n unhandledRejectionListener,\n );\n },\n );\n }\n}\n\nexport function uninstallProcessHandlers(): void {\n for (const removeHandler of removeOwnedHandlers) {\n removeHandler();\n }\n removeOwnedHandlers = [];\n}\n","/**\n * Token bucket rate limiter for event subscribers\n *\n * Prevents overwhelming downstream events platforms with too many events.\n * Uses token bucket algorithm for smooth rate limiting with burst capacity.\n */\n\nexport interface RateLimiterConfig {\n /** Maximum events per second (default: 100) */\n maxEventsPerSecond: number;\n /** Burst capacity - max events in a single burst (default: 2x rate) */\n burstCapacity?: number;\n}\n\n/**\n * Token bucket rate limiter\n *\n * Allows bursts up to burstCapacity, then smooths to maxEventsPerSecond.\n * Thread-safe for async operations.\n */\nexport class TokenBucketRateLimiter {\n private tokens: number;\n private readonly maxTokens: number;\n private readonly refillRate: number; // tokens per millisecond\n private lastRefill: number;\n\n constructor(config: RateLimiterConfig) {\n this.maxTokens = config.burstCapacity || config.maxEventsPerSecond * 2;\n this.tokens = this.maxTokens; // Start with full bucket\n this.refillRate = config.maxEventsPerSecond / 1000; // Convert to per-ms\n this.lastRefill = Date.now();\n }\n\n /**\n * Try to consume a token (allow an event)\n * Returns true if allowed, false if rate limit exceeded\n */\n tryConsume(count = 1): boolean {\n this.refill();\n\n if (this.tokens >= count) {\n this.tokens -= count;\n return true;\n }\n\n return false;\n }\n\n /**\n * Wait until a token is available (async rate limiting)\n * Returns a promise that resolves when the event can be processed\n */\n async waitForToken(count = 1): Promise<void> {\n this.refill();\n\n if (this.tokens >= count) {\n this.tokens -= count;\n return;\n }\n\n // Calculate wait time until we have enough tokens\n const tokensNeeded = count - this.tokens;\n const waitMs = Math.ceil(tokensNeeded / this.refillRate);\n\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n\n // After waiting, try again (recursive)\n return this.waitForToken(count);\n }\n\n /**\n * Refill tokens based on elapsed time\n */\n private refill(): void {\n const now = Date.now();\n const elapsed = now - this.lastRefill;\n const tokensToAdd = elapsed * this.refillRate;\n\n this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);\n this.lastRefill = now;\n }\n\n /**\n * Get current available tokens (for testing/debugging)\n */\n getAvailableTokens(): number {\n this.refill();\n return Math.floor(this.tokens);\n }\n\n /**\n * Reset the rate limiter (for testing)\n */\n reset(): void {\n this.tokens = this.maxTokens;\n this.lastRefill = Date.now();\n }\n}\n","/**\n * Trace context types and utilities\n */\n\nimport type {\n AttributeValue as OtelAttributeValue,\n Attributes,\n Span,\n SpanStatusCode,\n BaggageEntry,\n Context,\n Link,\n} from '@opentelemetry/api';\nimport { context, propagation } from '@opentelemetry/api';\n// namespace import for browser-bundler compat; `import type` is erased — see node-require.ts\nimport type { AsyncLocalStorage } from 'node:async_hooks';\nimport * as nodeAsyncHooks from 'node:async_hooks';\nimport { recordStructuredError } from './structured-error';\nimport { track } from './track';\n\ntype AsyncLocalBox<T> = {\n value: T;\n};\n\n/**\n * AsyncLocalStorage for storing the active context with baggage\n * This allows setters to update the context and have it persist\n */\nconst contextStorage = new nodeAsyncHooks.AsyncLocalStorage<\n AsyncLocalBox<Context>\n>();\n\n/**\n * Get the context storage instance (for initialization in functional.ts)\n */\nexport function getContextStorage(): AsyncLocalStorage<AsyncLocalBox<Context>> {\n return contextStorage;\n}\n\n/**\n * Get the active context, checking our stored context first\n * This ensures baggage setters work with OpenTelemetry's propagation\n */\nexport function getActiveContextWithBaggage(): Context {\n // Check stored context first (from setters), then fall back to active context\n // This ensures ctx.setBaggage() changes are visible to OpenTelemetry operations\n const stored = contextStorage.getStore()?.value;\n return stored ?? context.active();\n}\n\n/**\n * Set a value in AsyncLocalStorage, preferring enterWith() when available\n * (Node.js) and falling back to run() for environments that only support\n * run() (e.g. Cloudflare Workers).\n *\n * On runtimes without enterWith() we mutate the existing run() scope when one\n * exists. This is what allows baggage/correlation updates to remain visible\n * for the rest of the traced callback in Workers.\n */\nexport function enterOrRun<T>(\n storage: AsyncLocalStorage<AsyncLocalBox<T>>,\n value: T,\n): void {\n const existingStore = storage.getStore();\n if (existingStore) {\n existingStore.value = value;\n return;\n }\n\n const boxedValue = { value };\n try {\n storage.enterWith(boxedValue);\n } catch {\n // Cloudflare Workers define enterWith but throw at runtime\n storage.run(boxedValue, () => {});\n }\n}\n\n/**\n * Try to keep OpenTelemetry's context manager in sync with baggage updates\n */\ntype ContextManagerLike = {\n with?: (ctx: Context, fn: () => void) => void;\n _asyncLocalStorage?: { enterWith?: (ctx: Context) => void };\n};\n\nfunction updateActiveContext(newContext: Context): void {\n // Update our storage first so any helper reads see the new context\n enterOrRun(contextStorage, newContext);\n\n const contextWithManager = context as unknown as {\n _getContextManager?: () => ContextManagerLike;\n };\n\n const manager = contextWithManager._getContextManager?.();\n if (!manager) return;\n\n const asyncLocal =\n (manager as { _asyncLocalStorage?: { enterWith?: (ctx: Context) => void } })\n ._asyncLocalStorage ?? undefined;\n if (asyncLocal?.enterWith) {\n asyncLocal.enterWith(newContext);\n return;\n }\n\n if (typeof manager.with === 'function') {\n manager.with(newContext, () => {});\n }\n}\n\n/**\n * Base trace context containing trace identifiers\n */\nexport interface TraceContextBase {\n traceId: string;\n spanId: string;\n correlationId: string;\n}\n\n/**\n * Attribute value types following OpenTelemetry specification.\n * Supports primitive values and arrays of homogeneous primitives.\n */\nexport type AttributeValue = OtelAttributeValue;\n\n/**\n * Span methods available on trace context\n */\nexport interface SpanMethods {\n /** Set a single attribute on the span */\n setAttribute(key: string, value: AttributeValue): void;\n /** Set multiple attributes on the span */\n setAttributes(attrs: Attributes): void;\n /** Set the status of the span */\n setStatus(status: { code: SpanStatusCode; message?: string }): void;\n /** Add a link to another span */\n addLink(link: Link): void;\n /** Add multiple links to other spans */\n addLinks(links: Link[]): void;\n /** Update the span name dynamically */\n updateName(name: string): void;\n /** Check if the span is recording */\n isRecording(): boolean;\n /**\n * Record an error on the span: sets ERROR status, structured `error.*`\n * attributes (including `why`/`fix`/`link` from `createStructuredError`),\n * and during the OTel Span Event API back-compat window also records the\n * exception via the legacy span event API.\n *\n * Replaces the deprecated `recordException` (OTEP 4430). Accepts `unknown`\n * so it can be called directly with the value caught from a `catch` block.\n */\n recordError(error: unknown): void;\n /**\n * Emit a tracked event correlated to this span. Equivalent to the standalone\n * `track(event, data)` but reads naturally on `ctx`. Replaces the deprecated\n * `ctx.addEvent` (OTEP 4430) — events become correlated logs rather than\n * span events.\n */\n track<Events extends Record<string, unknown> = Record<string, unknown>>(\n event: keyof Events & string,\n data?: Events[keyof Events & string],\n ): void;\n}\n\n/**\n * Baggage methods available on trace context\n *\n * @template TBaggage - Optional type for typed baggage (defaults to undefined for untyped)\n */\nexport interface BaggageMethods<\n TBaggage extends Record<string, unknown> | undefined = undefined,\n> {\n /**\n * Get a baggage entry by key\n * @param key - Baggage key\n * @returns Baggage entry value or undefined\n */\n getBaggage(key: string): string | undefined;\n\n /**\n * Set a baggage entry\n *\n * Note: OpenTelemetry contexts are immutable. For proper scoping across async\n * boundaries, use withBaggage() instead. This method updates baggage in the\n * current context which may not propagate to all child operations.\n *\n * @param key - Baggage key\n * @param value - Baggage value\n * @returns The baggage value that was set (for chaining)\n *\n * @example Using withBaggage() (recommended)\n * ```typescript\n * await withBaggage({ baggage: { 'key': 'value' }, fn: async () => {\n * // Baggage is available here and in child spans\n * });\n * ```\n */\n setBaggage(key: string, value: string): string;\n\n /**\n * Delete a baggage entry\n *\n * Note: OpenTelemetry contexts are immutable. For proper scoping across async\n * boundaries, use withBaggage() with only the entries you want instead.\n *\n * @param key - Baggage key\n */\n deleteBaggage(key: string): void;\n\n /**\n * Get all baggage entries\n * @returns Map of all baggage entries\n */\n getAllBaggage(): Map<string, BaggageEntry>;\n\n /**\n * Get typed baggage (only available when TBaggage is defined)\n * This is used internally by defineBaggageSchema()\n *\n * @internal\n */\n getTypedBaggage?: TBaggage extends Record<string, unknown>\n ? <T extends TBaggage>(namespace?: string) => Partial<T> | undefined\n : never;\n\n /**\n * Set typed baggage (only available when TBaggage is defined)\n * This is used internally by defineBaggageSchema()\n *\n * @internal\n */\n setTypedBaggage?: TBaggage extends Record<string, unknown>\n ? <T extends TBaggage>(\n namespace: string | undefined,\n value: Partial<T>,\n ) => void\n : never;\n}\n\n/**\n * Complete trace context that merges base context, span methods, and baggage methods\n *\n * This is the ctx parameter passed to `withTracing()` factories and the value\n * returned by `getActiveTraceContext()` inside plain `trace()` wrappers.\n *\n * @template TBaggage - Optional type for typed baggage support\n *\n * @example Untyped (default)\n * ```typescript\n * export const handler = withTracing({})((ctx) => async () => {\n * ctx.getBaggage('key'); // returns string | undefined\n * });\n * ```\n *\n * @example Typed baggage\n * ```typescript\n * type TenantBaggage = { tenantId: string; region?: string };\n *\n * export const handler = trace(async () => {\n * const ctx = getActiveTraceContext<TenantBaggage>()!;\n * // Use typed schema helper for type-safe access\n * const schema = defineBaggageSchema<TenantBaggage>('tenant');\n * const tenant = schema.get(ctx); // Partial<TenantBaggage> | undefined\n * });\n * ```\n */\nexport type TraceContext<\n TBaggage extends Record<string, unknown> | undefined = undefined,\n> = TraceContextBase & SpanMethods & BaggageMethods<TBaggage>;\n\n/**\n * Create a TraceContext from an OpenTelemetry Span\n *\n * This utility extracts trace context information from a span\n * and provides span manipulation methods and baggage operations in a consistent format.\n *\n * Note: Baggage methods always operate on the currently active context,\n * which may differ from the context when createTraceContext was called.\n */\nexport function createTraceContext<\n TBaggage extends Record<string, unknown> | undefined = undefined,\n>(span: Span): TraceContext<TBaggage> {\n const spanContext = span.spanContext();\n\n // Store the current active context in AsyncLocalStorage so baggage setters can update it\n // This ensures ctx.setBaggage() changes persist and are visible to OpenTelemetry operations\n // IMPORTANT: Only initialize if not already set (preserve baggage updates from parent spans)\n const existingStored = contextStorage.getStore()?.value;\n if (!existingStored) {\n const activeContext = context.active();\n enterOrRun(contextStorage, activeContext);\n }\n\n // Baggage helpers that always use the current active context\n // This ensures baggage operations work correctly even if context changes\n const baggageHelpers: BaggageMethods<TBaggage> = {\n getBaggage(key: string): string | undefined {\n // Check active context first (from withBaggage, context.with, etc.)\n // Then check stored context (from setters)\n // This ensures both withBaggage() and ctx.setBaggage() work correctly\n const activeCtx = context.active();\n let baggage = propagation.getBaggage(activeCtx);\n if (!baggage) {\n const storedContext = contextStorage.getStore()?.value;\n if (storedContext) {\n baggage = propagation.getBaggage(storedContext);\n }\n }\n return baggage?.getEntry(key)?.value;\n },\n\n setBaggage(key: string, value: string): string {\n // OpenTelemetry contexts are immutable, so we create a new context with updated baggage\n // Check active context first (may have baggage from withBaggage), then stored context\n const activeCtx = context.active();\n const storedContext = contextStorage.getStore()?.value;\n const currentContext = storedContext ?? activeCtx;\n const baggage =\n propagation.getBaggage(currentContext) ?? propagation.createBaggage();\n const updated = baggage.setEntry(key, { value });\n const newContext = propagation.setBaggage(currentContext, updated);\n\n updateActiveContext(newContext);\n\n return value;\n },\n\n deleteBaggage(key: string): void {\n // Check active context first, then stored context\n const activeCtx = context.active();\n const storedContext = contextStorage.getStore()?.value;\n const currentContext = storedContext ?? activeCtx;\n const baggage = propagation.getBaggage(currentContext);\n if (baggage) {\n const updated = baggage.removeEntry(key);\n const newContext = propagation.setBaggage(currentContext, updated);\n\n updateActiveContext(newContext);\n }\n },\n\n getAllBaggage(): Map<string, BaggageEntry> {\n // Check active context first, then stored context\n const activeCtx = context.active();\n let baggage = propagation.getBaggage(activeCtx);\n if (!baggage) {\n const storedContext = contextStorage.getStore()?.value;\n if (storedContext) {\n baggage = propagation.getBaggage(storedContext);\n }\n }\n if (!baggage) {\n return new Map();\n }\n\n // Convert baggage entries to a Map\n const entries = new Map<string, BaggageEntry>();\n for (const [key, entry] of baggage.getAllEntries()) {\n entries.set(key, entry);\n }\n return entries;\n },\n\n // Typed baggage helpers (used by defineBaggageSchema)\n getTypedBaggage: (<T extends Record<string, unknown>>(\n namespace?: string,\n ) => {\n // Check active context first, then stored context\n const activeCtx = context.active();\n let baggage = propagation.getBaggage(activeCtx);\n if (!baggage) {\n const storedContext = contextStorage.getStore()?.value;\n if (storedContext) {\n baggage = propagation.getBaggage(storedContext);\n }\n }\n if (!baggage) return;\n\n const prefix = namespace ? `${namespace}.` : '';\n const result: Record<string, unknown> = {};\n\n for (const [key, entry] of baggage.getAllEntries()) {\n if (namespace && key.startsWith(prefix)) {\n const fieldName = key.slice(prefix.length);\n result[fieldName] = entry.value;\n } else if (!namespace) {\n result[key] = entry.value;\n }\n }\n\n return Object.keys(result).length > 0\n ? (result as Partial<T>)\n : undefined;\n }) as TBaggage extends Record<string, unknown>\n ? <T extends TBaggage>(namespace?: string) => Partial<T> | undefined\n : never,\n\n setTypedBaggage: (<T extends Record<string, unknown>>(\n namespace: string | undefined,\n value: Partial<T>,\n ) => {\n // Check active context first, then stored context\n const activeCtx = context.active();\n const storedContext = contextStorage.getStore()?.value;\n const currentContext = storedContext ?? activeCtx;\n let baggage =\n propagation.getBaggage(currentContext) ?? propagation.createBaggage();\n\n const prefix = namespace ? `${namespace}.` : '';\n for (const [key, val] of Object.entries(value)) {\n if (val !== undefined) {\n const baggageKey = `${prefix}${key}`;\n baggage = baggage.setEntry(baggageKey, { value: String(val) });\n }\n }\n\n const newContext = propagation.setBaggage(currentContext, baggage);\n updateActiveContext(newContext);\n }) as TBaggage extends Record<string, unknown>\n ? <T extends TBaggage>(\n namespace: string | undefined,\n value: Partial<T>,\n ) => void\n : never,\n };\n\n // `recordException` and `addEvent` are intentionally bound at runtime but\n // omitted from the `SpanMethods` type. They exist solely so existing call\n // sites keep working through the OTel Span Event API deprecation window\n // (see MIGRATION.md). New code MUST go through `recordStructuredError`,\n // `emitCorrelatedEvent`, or the request logger. The cast below is what hides\n // these compatibility-only fields from the public type.\n const traceCtx = {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n correlationId: spanContext.traceId.slice(0, 16),\n setAttribute: span.setAttribute.bind(span),\n setAttributes: span.setAttributes.bind(span),\n setStatus: span.setStatus.bind(span),\n recordException: span.recordException.bind(span),\n addEvent: span.addEvent.bind(span),\n addLink: span.addLink.bind(span),\n addLinks: span.addLinks.bind(span),\n updateName: span.updateName.bind(span),\n isRecording: span.isRecording.bind(span),\n recordError: (error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n recordStructuredError(traceCtx, err);\n },\n track: (event: string, data?: Record<string, unknown>) => {\n track(event, data);\n },\n ...baggageHelpers,\n } as unknown as TraceContext<TBaggage>;\n\n return traceCtx;\n}\n\n/**\n * Define a typed baggage schema for type-safe baggage operations\n *\n * This helper provides a type-safe API for working with baggage entries.\n * The namespace parameter is optional and prefixes all keys to avoid collisions.\n *\n * @template T - The baggage schema type (all fields are treated as optional)\n * @param namespace - Optional namespace to prefix baggage keys\n *\n * @example Basic usage\n * ```typescript\n * type TenantBaggage = { tenantId: string; region?: string };\n * const tenantBaggage = defineBaggageSchema<TenantBaggage>('tenant');\n *\n * export const handler = trace<TenantBaggage>((ctx) => async () => {\n * // Get typed baggage\n * const tenant = tenantBaggage.get(ctx);\n * if (tenant?.tenantId) {\n * console.log('Tenant:', tenant.tenantId);\n * }\n *\n * // Set typed baggage\n * tenantBaggage.set(ctx, { tenantId: 't1', region: 'us-east-1' });\n * });\n * ```\n *\n * @example With withBaggage helper\n * ```typescript\n * const tenantBaggage = defineBaggageSchema<TenantBaggage>('tenant');\n *\n * export const handler = trace<TenantBaggage>((ctx) => async () => {\n * return await tenantBaggage.with(ctx, { tenantId: 't1' }, async () => {\n * // Baggage is available here and in child spans\n * const tenant = tenantBaggage.get(ctx);\n * });\n * });\n * ```\n */\nexport function defineBaggageSchema<T extends Record<string, unknown>>(\n namespace?: string,\n) {\n return {\n /**\n * Get typed baggage from context\n * @param ctx - Trace context\n * @returns Partial baggage object or undefined if no baggage is set\n */\n get: (ctx: TraceContext<T>): Partial<T> | undefined => {\n if (!ctx.getTypedBaggage) return undefined;\n return ctx.getTypedBaggage<T>(namespace);\n },\n\n /**\n * Set typed baggage in context\n *\n * Note: For proper scoping across async boundaries, use the `with` method instead\n *\n * @param ctx - Trace context\n * @param value - Partial baggage object to set\n */\n set: (ctx: TraceContext<T>, value: Partial<T>): void => {\n if (!ctx.setTypedBaggage) return;\n ctx.setTypedBaggage<T>(namespace, value);\n },\n\n /**\n * Run a function with typed baggage properly scoped\n *\n * This is the recommended way to set baggage as it ensures proper\n * scoping across async boundaries.\n *\n * @param ctx - Trace context (can be omitted, will use active context)\n * @param value - Partial baggage object to set\n * @param fn - Function to execute with the baggage\n */\n with: <R>(\n ctxOrValue: TraceContext<T> | Partial<T>,\n valueOrFn: Partial<T> | (() => R | Promise<R>),\n maybeFn?: () => R | Promise<R>,\n ): R | Promise<R> => {\n // Support both with(ctx, value, fn) and with(value, fn)\n const value = maybeFn\n ? (valueOrFn as Partial<T>)\n : (ctxOrValue as Partial<T>);\n const fn = maybeFn || (valueOrFn as () => R | Promise<R>);\n\n // Serialize typed baggage to flat key-value pairs\n const prefix = namespace ? `${namespace}.` : '';\n const flatBaggage: Record<string, string> = {};\n for (const [key, val] of Object.entries(value)) {\n if (val !== undefined) {\n flatBaggage[`${prefix}${key}`] = String(val);\n }\n }\n\n // Use the existing withBaggage helper\n const currentContext = context.active();\n let baggage =\n propagation.getBaggage(currentContext) ?? propagation.createBaggage();\n\n for (const [key, val] of Object.entries(flatBaggage)) {\n baggage = baggage.setEntry(key, { value: val });\n }\n\n const newContext = propagation.setBaggage(currentContext, baggage);\n return context.with(newContext, fn);\n },\n };\n}\n","/**\n * Correlation ID utilities for event-driven observability\n *\n * Provides a stable join key across events, logs, and spans even when traces fragment.\n * Format: 16 hex chars (64 bits), crypto-random, URL-safe.\n *\n * Lifecycle:\n * 1. Generated at boundary root (HTTP server span, message process span, cron job span)\n * 2. Reused within context (nested work shares it via AsyncLocalStorage)\n * 3. Propagated via baggage (optional, default OFF to avoid header bloat)\n *\n * @example Basic usage\n * ```typescript\n * import { generateCorrelationId, getCorrelationId } from 'autotel/correlation-id';\n *\n * // Generate a new correlation ID\n * const id = generateCorrelationId();\n * // Returns: 'a1b2c3d4e5f67890'\n *\n * // Get current correlation ID from context\n * const currentId = getCorrelationId();\n * ```\n */\n\nimport { trace, propagation, context } from '@opentelemetry/api';\n// namespace import for browser-bundler compat; `import type` is erased — see node-require.ts\nimport type { AsyncLocalStorage } from 'node:async_hooks';\nimport * as nodeAsyncHooks from 'node:async_hooks';\nimport { enterOrRun } from './trace-context';\n\ntype CorrelationStore = {\n value: string;\n};\n\n/**\n * AsyncLocalStorage for storing correlation ID\n * This allows correlation IDs to persist across async boundaries\n */\nconst correlationStorage =\n new nodeAsyncHooks.AsyncLocalStorage<CorrelationStore>();\n\n/**\n * Baggage key for correlation ID propagation\n */\nexport const CORRELATION_ID_BAGGAGE_KEY = 'autotel.correlation_id';\n\n/**\n * Generate a new correlation ID\n *\n * Format: 16 hex chars (64 bits), crypto-random, URL-safe\n *\n * @returns A new correlation ID\n *\n * @example\n * ```typescript\n * const id = generateCorrelationId();\n * // Returns: 'a1b2c3d4e5f67890'\n * ```\n */\nexport function generateCorrelationId(): string {\n // Use crypto.getRandomValues for secure random bytes\n const bytes = new Uint8Array(8); // 64 bits\n crypto.getRandomValues(bytes);\n\n // Convert to hex string\n return [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Get the current correlation ID from context\n *\n * Resolution order:\n * 1. AsyncLocalStorage (from explicit setCorrelationId or runWithCorrelationId)\n * 2. Baggage (if propagated from upstream)\n * 3. Active span's trace ID (first 16 chars as fallback)\n * 4. undefined (if not in any context)\n *\n * @returns Current correlation ID or undefined\n *\n * @example\n * ```typescript\n * const id = getCorrelationId();\n * if (id) {\n * console.log('Correlation ID:', id);\n * }\n * ```\n */\nexport function getCorrelationId(): string | undefined {\n // 1. Check AsyncLocalStorage first (explicit correlation ID)\n const storedId = correlationStorage.getStore()?.value;\n if (storedId) {\n return storedId;\n }\n\n // 2. Check baggage (propagated from upstream)\n const activeContext = context.active();\n const baggage = propagation.getBaggage(activeContext);\n const baggageEntry = baggage?.getEntry(CORRELATION_ID_BAGGAGE_KEY);\n if (baggageEntry?.value) {\n return baggageEntry.value;\n }\n\n // 3. Fall back to active span's trace ID (first 16 chars)\n const span = trace.getActiveSpan();\n if (span) {\n const spanContext = span.spanContext();\n return spanContext.traceId.slice(0, 16);\n }\n\n // 4. No context available\n return undefined;\n}\n\n/**\n * Get or create a correlation ID\n *\n * If a correlation ID exists in the current context, returns it.\n * Otherwise, generates a new one.\n *\n * @returns Existing or new correlation ID\n *\n * @example\n * ```typescript\n * const id = getOrCreateCorrelationId();\n * // Always returns a valid correlation ID\n * ```\n */\nexport function getOrCreateCorrelationId(): string {\n return getCorrelationId() ?? generateCorrelationId();\n}\n\n/**\n * Run a function with a specific correlation ID in context\n *\n * The correlation ID will be available via getCorrelationId() throughout\n * the execution of the function and any async operations it spawns.\n *\n * @param correlationId - Correlation ID to use\n * @param fn - Function to execute\n * @returns The return value of the function\n *\n * @example\n * ```typescript\n * await runWithCorrelationId('abc123', async () => {\n * // getCorrelationId() returns 'abc123' here\n * await processRequest();\n * });\n * ```\n */\nexport function runWithCorrelationId<T>(correlationId: string, fn: () => T): T {\n return correlationStorage.run({ value: correlationId }, fn);\n}\n\n/**\n * Set correlation ID in the current context (mutates context)\n *\n * Note: This updates the AsyncLocalStorage context. For proper scoping\n * across async boundaries, prefer runWithCorrelationId() instead.\n *\n * @param correlationId - Correlation ID to set\n *\n * @example\n * ```typescript\n * setCorrelationId('abc123');\n * // Now getCorrelationId() returns 'abc123'\n * ```\n */\nexport function setCorrelationId(correlationId: string): void {\n enterOrRun(correlationStorage, correlationId);\n}\n\n/**\n * Set correlation ID in baggage for propagation\n *\n * This adds the correlation ID to the W3C baggage header, allowing it\n * to be propagated to downstream services.\n *\n * Note: Only use this when you explicitly want cross-service propagation.\n * Default is OFF to avoid header bloat.\n *\n * @param correlationId - Correlation ID to propagate\n * @returns New context with baggage set\n *\n * @example\n * ```typescript\n * const newContext = setCorrelationIdInBaggage('abc123');\n * context.with(newContext, () => {\n * // Baggage will be propagated in outgoing requests\n * });\n * ```\n */\nexport function setCorrelationIdInBaggage(\n correlationId: string,\n): import('@opentelemetry/api').Context {\n const activeContext = context.active();\n let baggage =\n propagation.getBaggage(activeContext) ?? propagation.createBaggage();\n baggage = baggage.setEntry(CORRELATION_ID_BAGGAGE_KEY, {\n value: correlationId,\n });\n return propagation.setBaggage(activeContext, baggage);\n}\n\n/**\n * Get the correlation storage instance (for internal use in init/shutdown)\n */\nexport function getCorrelationStorage(): AsyncLocalStorage<CorrelationStore> {\n return correlationStorage;\n}\n","/**\n * Events event queue with batching, backpressure, retry logic, rate limiting, and OTel metrics\n *\n * Exposes delivery pipeline metrics for observability:\n * - autotel.event_delivery.queue.size - Current queue size\n * - autotel.event_delivery.queue.oldest_age_ms - Age of oldest event in queue\n * - autotel.event_delivery.queue.delivered - Successfully delivered events\n * - autotel.event_delivery.queue.failed - Failed event deliveries\n * - autotel.event_delivery.queue.dropped - Dropped events with reason\n * - autotel.event_delivery.queue.latency_ms - Delivery latency histogram\n * - autotel.event_delivery.subscriber.health - Subscriber health (1=healthy, 0=unhealthy)\n */\n\nimport type {\n Counter,\n Histogram,\n ObservableGauge,\n Attributes,\n} from '@opentelemetry/api';\nimport type { ObservableResult } from '@opentelemetry/api';\nimport type {\n EventSubscriber,\n EventAttributes,\n AutotelEventContext,\n EventSchemaMetadata,\n} from './event-subscriber';\nimport { getLogger } from './init';\nimport { getConfig as getRuntimeConfig } from './config';\nimport { TokenBucketRateLimiter, type RateLimiterConfig } from './rate-limiter';\nimport { getOrCreateCorrelationId } from './correlation-id';\n\nexport interface EventData {\n name: string;\n attributes?: EventAttributes;\n timestamp: number;\n /** Internal: correlation ID for debug breadcrumbs */\n _correlationId?: string;\n /** Internal: trace ID for debug breadcrumbs */\n _traceId?: string;\n /** Autotel context for trace correlation (passed to subscribers) */\n autotel?: AutotelEventContext;\n /** Optional schema metadata for contract-aware subscribers. */\n schema?: EventSchemaMetadata;\n}\n\n/**\n * Drop reasons for event delivery queue metrics\n * LOW CARDINALITY: Only these 4 values allowed in metric labels\n */\nexport type EventDropReason =\n 'rate_limit' | 'circuit_open' | 'payload_invalid' | 'shutdown';\n\nexport interface QueueConfig {\n maxSize: number; // Max events in queue (default: 50,000)\n batchSize: number; // Events per batch (default: 100)\n flushInterval: number; // Flush interval in ms (default: 10,000)\n maxRetries: number; // Max retry attempts (default: 3)\n rateLimit?: RateLimiterConfig; // Optional rate limiting (default: 100 events/sec)\n}\n\nconst DEFAULT_CONFIG: QueueConfig = {\n maxSize: 50_000,\n batchSize: 100,\n flushInterval: 10_000,\n maxRetries: 3,\n rateLimit: {\n maxEventsPerSecond: 100,\n burstCapacity: 200,\n },\n};\n\n/**\n * Metrics for event delivery queue observability\n *\n * All metrics use low-cardinality labels only:\n * - subscriber: stable identifier (e.g., 'posthog', 'mixpanel')\n * - reason: one of EventDropReason values\n */\ninterface EventQueueMetrics {\n /** Current queue size (observable gauge) */\n queueSize: ObservableGauge;\n /** Age of oldest event in queue in ms (observable gauge) */\n oldestAge: ObservableGauge;\n /** Successfully delivered events (counter) */\n delivered: Counter;\n /** Failed event deliveries after all retries (counter) */\n failed: Counter;\n /** Dropped events (counter with reason label) */\n dropped: Counter;\n /** Event delivery latency histogram in ms */\n latency: Histogram;\n /** Subscriber health: 1=healthy, 0=unhealthy (observable gauge) */\n subscriberHealth: ObservableGauge;\n}\n\n/**\n * Get subscriber name for metrics (stable, low-cardinality)\n *\n * Priority:\n * 1. Explicit config: subscriber.name\n * 2. Class static property (if available)\n * 3. Fallback: lowercase class name without \"Subscriber\" suffix\n */\nfunction getSubscriberName(subscriber: EventSubscriber): string {\n // Use explicit name if provided\n if (subscriber.name) {\n return subscriber.name.toLowerCase();\n }\n\n // Fallback: derive from class name\n const className = subscriber.constructor?.name || 'unknown';\n return className.replace(/Subscriber$/i, '').toLowerCase();\n}\n\n/**\n * Events queue with batching and backpressure\n *\n * Features:\n * - Batches events for efficient sending\n * - Bounded queue with drop-oldest policy (prod) or blocking (dev)\n * - Exponential backoff retry\n * - Rate limiting to prevent overwhelming subscribers\n * - Graceful flush on shutdown\n */\nexport class EventQueue {\n private queue: EventData[] = [];\n private flushTimer: NodeJS.Timeout | null = null;\n private readonly config: QueueConfig;\n private readonly subscribers: EventSubscriber[];\n private readonly rateLimiter: TokenBucketRateLimiter | null;\n private flushPromise: Promise<void> | null = null;\n private isShuttingDown = false;\n\n // Metrics\n private metrics: EventQueueMetrics | null = null;\n\n // Observable callback cleanup functions\n private observableCleanups: Array<() => void> = [];\n\n // Subscriber health tracking (for observable gauges)\n private subscriberHealthy: Map<string, boolean> = new Map();\n\n constructor(subscribers: EventSubscriber[], config?: Partial<QueueConfig>) {\n this.subscribers = subscribers;\n this.config = { ...DEFAULT_CONFIG, ...config };\n\n // Initialize rate limiter if configured\n this.rateLimiter = this.config.rateLimit\n ? new TokenBucketRateLimiter(this.config.rateLimit)\n : null;\n\n // Initialize subscriber health tracking\n for (const subscriber of subscribers) {\n const name = getSubscriberName(subscriber);\n this.subscriberHealthy.set(name, true);\n }\n\n // Initialize metrics\n this.initMetrics();\n }\n\n /**\n * Initialize OTel metrics for queue observability\n */\n private initMetrics(): void {\n const runtimeConfig = getRuntimeConfig();\n const meter = runtimeConfig.meter;\n\n // Queue size gauge - observe current queue length\n const queueSize = meter.createObservableGauge(\n 'autotel.event_delivery.queue.size',\n {\n description: 'Current number of events in the delivery queue',\n unit: 'count',\n },\n );\n const queueSizeCallback = (observableResult: ObservableResult) => {\n observableResult.observe(this.queue.length);\n };\n queueSize.addCallback(queueSizeCallback);\n this.observableCleanups.push(() =>\n queueSize.removeCallback(queueSizeCallback),\n );\n\n // Oldest event age gauge - observe wait time of oldest event\n const oldestAge = meter.createObservableGauge(\n 'autotel.event_delivery.queue.oldest_age_ms',\n {\n description: 'Age of the oldest event in the queue in milliseconds',\n unit: 'ms',\n },\n );\n const oldestAgeCallback = (observableResult: ObservableResult) => {\n if (this.queue.length > 0) {\n const oldest = this.queue[0]!;\n const ageMs = Date.now() - oldest.timestamp;\n observableResult.observe(ageMs);\n } else {\n observableResult.observe(0);\n }\n };\n oldestAge.addCallback(oldestAgeCallback);\n this.observableCleanups.push(() =>\n oldestAge.removeCallback(oldestAgeCallback),\n );\n\n // Delivered counter\n const delivered = meter.createCounter(\n 'autotel.event_delivery.queue.delivered',\n {\n description: 'Number of events successfully delivered to subscribers',\n unit: 'count',\n },\n );\n\n // Failed counter\n const failed = meter.createCounter('autotel.event_delivery.queue.failed', {\n description:\n 'Number of events that failed delivery after all retry attempts',\n unit: 'count',\n });\n\n // Dropped counter (with reason label)\n const dropped = meter.createCounter(\n 'autotel.event_delivery.queue.dropped',\n {\n description: 'Number of events dropped from the queue',\n unit: 'count',\n },\n );\n\n // Latency histogram\n const latency = meter.createHistogram(\n 'autotel.event_delivery.queue.latency_ms',\n {\n description: 'Event delivery latency from enqueue to successful send',\n unit: 'ms',\n },\n );\n\n // Subscriber health gauge\n const subscriberHealth = meter.createObservableGauge(\n 'autotel.event_delivery.subscriber.health',\n {\n description: 'Subscriber health status (1=healthy, 0=unhealthy)',\n unit: '1',\n },\n );\n const subscriberHealthCallback = (observableResult: ObservableResult) => {\n for (const [subscriberName, isHealthy] of this.subscriberHealthy) {\n observableResult.observe(isHealthy ? 1 : 0, {\n subscriber: subscriberName,\n });\n }\n };\n subscriberHealth.addCallback(subscriberHealthCallback);\n this.observableCleanups.push(() =>\n subscriberHealth.removeCallback(subscriberHealthCallback),\n );\n\n this.metrics = {\n queueSize,\n oldestAge,\n delivered,\n failed,\n dropped,\n latency,\n subscriberHealth,\n };\n }\n\n /**\n * Record a dropped event with reason and emit debug breadcrumb\n */\n private recordDropped(\n reason: EventDropReason,\n event?: EventData,\n subscriberName?: string,\n ): void {\n // Increment metric\n const attrs: Attributes = { reason };\n if (subscriberName) {\n attrs.subscriber = subscriberName;\n }\n this.metrics?.dropped.add(1, attrs);\n\n // Debug breadcrumb log (rate-limited via existing logger)\n const logLevel = reason === 'payload_invalid' ? 'error' : 'warn';\n const logger = getLogger();\n\n if (logLevel === 'error') {\n logger.error(\n {\n eventName: event?.name,\n subscriber: subscriberName,\n reason,\n correlationId: event?._correlationId,\n traceId: event?._traceId,\n },\n `[autotel] Event dropped: ${reason}`,\n );\n } else {\n logger.warn(\n {\n eventName: event?.name,\n subscriber: subscriberName,\n reason,\n correlationId: event?._correlationId,\n traceId: event?._traceId,\n },\n `[autotel] Event dropped: ${reason}`,\n );\n }\n }\n\n /**\n * Record permanent delivery failure (after all retries exhausted)\n * Increments failed counter and logs error\n */\n private recordFailed(\n event: EventData,\n subscriberName: string,\n error?: Error,\n ): void {\n this.metrics?.failed.add(1, { subscriber: subscriberName });\n\n // Mark subscriber as unhealthy\n this.subscriberHealthy.set(subscriberName, false);\n\n // Debug breadcrumb log\n getLogger().error(\n {\n eventName: event.name,\n subscriber: subscriberName,\n correlationId: event._correlationId,\n traceId: event._traceId,\n err: error,\n },\n `[autotel] Event delivery failed after all retries`,\n );\n }\n\n /**\n * Mark subscriber as unhealthy on transient failure (without incrementing failed counter)\n * Used during retry attempts - only recordFailed should increment the counter\n */\n private markSubscriberUnhealthy(subscriberName: string): void {\n this.subscriberHealthy.set(subscriberName, false);\n }\n\n /**\n * Record successful delivery\n */\n private recordDelivered(\n event: EventData,\n subscriberName: string,\n startTime: number,\n ): void {\n const latencyMs = Date.now() - startTime;\n\n this.metrics?.delivered.add(1, { subscriber: subscriberName });\n this.metrics?.latency.record(latencyMs, { subscriber: subscriberName });\n\n // Mark subscriber as healthy\n this.subscriberHealthy.set(subscriberName, true);\n }\n\n /**\n * Enqueue an event for sending\n *\n * Backpressure policy:\n * - Drops oldest event and logs warning if queue is full (same behavior in all environments)\n */\n enqueue(event: EventData): void {\n // Reject events during shutdown\n if (this.isShuttingDown) {\n this.recordDropped('shutdown', event);\n return;\n }\n\n // Check queue size\n if (this.queue.length >= this.config.maxSize) {\n // Drop oldest event and log warning (same behavior in all environments)\n const droppedEvent = this.queue.shift();\n this.recordDropped('rate_limit', droppedEvent);\n getLogger().warn(\n {\n droppedEvent: droppedEvent?.name,\n },\n `[autotel] Events queue full (${this.config.maxSize} events). ` +\n 'Dropping oldest event. Events are being produced faster than they can be sent. ' +\n 'Check your subscribers or reduce tracking frequency.',\n );\n }\n\n // Enrich event with correlation context for debug breadcrumbs\n const enrichedEvent: EventData = {\n ...event,\n _correlationId: event._correlationId || getOrCreateCorrelationId(),\n };\n\n this.queue.push(enrichedEvent);\n this.scheduleBatchFlush();\n }\n\n /**\n * Schedule a batch flush if not already scheduled\n */\n private scheduleBatchFlush(): void {\n if (this.flushTimer || this.flushPromise) return;\n\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n void this.flushBatch();\n }, this.config.flushInterval);\n }\n\n /**\n * Flush a batch of events\n * Uses promise-based concurrency control to prevent race conditions\n */\n private async flushBatch(): Promise<void> {\n if (this.queue.length === 0) return;\n\n // If already flushing, wait for existing flush\n if (this.flushPromise) {\n await this.flushPromise;\n return;\n }\n\n this.flushPromise = this.doFlushBatch();\n\n try {\n await this.flushPromise;\n } finally {\n this.flushPromise = null;\n\n // Schedule next flush if more events\n if (this.queue.length > 0) {\n this.scheduleBatchFlush();\n }\n }\n }\n\n /**\n * Internal flush implementation\n */\n private async doFlushBatch(): Promise<void> {\n const batch = this.queue.splice(0, this.config.batchSize);\n await this.sendWithRetry(batch, this.config.maxRetries);\n }\n\n /**\n * Send events with exponential backoff retry\n * Tracks per-event, per-subscriber failures so failed counter reflects actual failed deliveries.\n * On retry, only failed (event, subscriber) pairs are re-sent to avoid double-counting delivered.\n */\n private async sendWithRetry(\n events: EventData[],\n retriesLeft: number,\n subscribersByEventIndex?: Map<number, Set<string>>,\n ): Promise<void> {\n const failedDeliveries = await this.sendToSubscribers(\n events,\n subscribersByEventIndex,\n );\n\n if (failedDeliveries.length > 0) {\n if (retriesLeft > 0) {\n // Retry only events that had at least one failure, and only to subscribers that failed (avoid re-sending to healthy subscribers and double-counting delivered)\n const failedEventIndices = new Set(\n failedDeliveries.map((f) => f.eventIndex),\n );\n const failedEventIndicesOrdered = [...failedEventIndices].toSorted(\n (a, b) => a - b,\n );\n const eventsToRetry = failedEventIndicesOrdered.map(\n (i) => events[i],\n ) as EventData[];\n const failedSubscribersByRetryIndex = new Map<number, Set<string>>();\n for (const [j, origIndex] of failedEventIndicesOrdered.entries()) {\n const set = new Set<string>();\n for (const { eventIndex, subscriberName } of failedDeliveries) {\n if (eventIndex === origIndex) set.add(subscriberName);\n }\n failedSubscribersByRetryIndex.set(j, set);\n }\n const delay = Math.pow(2, this.config.maxRetries - retriesLeft) * 1000;\n await new Promise((resolve) => setTimeout(resolve, delay));\n return this.sendWithRetry(\n eventsToRetry,\n retriesLeft - 1,\n failedSubscribersByRetryIndex,\n );\n } else {\n // Give up after max retries - record one failure per (event, subscriber) pair\n for (const { eventIndex, subscriberName, error } of failedDeliveries) {\n const event = events[eventIndex];\n if (event) this.recordFailed(event, subscriberName, error);\n }\n\n const failedSubscriberNames = [\n ...new Set(failedDeliveries.map((f) => f.subscriberName)),\n ];\n getLogger().error(\n {\n failedSubscribers: failedSubscriberNames,\n retriesAttempted: this.config.maxRetries,\n },\n '[autotel] Failed to send events after retries',\n );\n }\n }\n }\n\n /**\n * Send events to configured subscribers with rate limiting and metrics.\n * When subscribersByEventIndex is provided (retry path), only those subscribers are tried per event.\n * Returns per-event, per-subscriber failures (empty if all succeeded).\n */\n private async sendToSubscribers(\n events: EventData[],\n subscribersByEventIndex?: Map<number, Set<string>>,\n ): Promise<\n Array<{ eventIndex: number; subscriberName: string; error?: Error }>\n > {\n const failedDeliveries: Array<{\n eventIndex: number;\n subscriberName: string;\n error?: Error;\n }> = [];\n\n const sendOne = async (event: EventData, eventIndex: number) => {\n // On retry, only try subscribers that failed for this event (never re-send to healthy subscribers)\n const subscriberNames = subscribersByEventIndex?.get(eventIndex);\n const failures = await this.sendEventToSubscribers(\n event,\n subscriberNames ?? undefined,\n );\n for (const failure of failures) {\n failedDeliveries.push({\n eventIndex,\n subscriberName: failure.subscriberName,\n error: failure.error,\n });\n }\n };\n\n if (!this.rateLimiter) {\n for (const [i, event] of events.entries()) {\n if (event) await sendOne(event, i);\n }\n return failedDeliveries;\n }\n\n for (const [i, event] of events.entries()) {\n await this.rateLimiter.waitForToken();\n if (event) await sendOne(event, i);\n }\n\n return failedDeliveries;\n }\n\n /**\n * Send a single event to subscribers.\n * - When subscriberNames is undefined (initial attempt): send to all subscribers.\n * - When subscriberNames is provided (retry): send only to those subscribers (never re-send to healthy ones).\n * Returns list of subscribers that failed (empty if all succeeded).\n */\n private async sendEventToSubscribers(\n event: EventData,\n subscriberNames?: Set<string>,\n ): Promise<Array<{ subscriberName: string; error?: Error }>> {\n const startTime = event.timestamp;\n const failures: Array<{ subscriberName: string; error?: Error }> = [];\n\n const subscribersToTry =\n subscriberNames === undefined\n ? this.subscribers\n : this.subscribers.filter((s) =>\n subscriberNames.has(getSubscriberName(s)),\n );\n\n const results = await Promise.allSettled(\n subscribersToTry.map(async (subscriber) => {\n const subscriberName = getSubscriberName(subscriber);\n\n try {\n await subscriber.trackEvent(event.name, event.attributes, {\n autotel: event.autotel,\n schema: event.schema,\n });\n this.recordDelivered(event, subscriberName, startTime);\n return { subscriberName, success: true };\n } catch (error) {\n this.markSubscriberUnhealthy(subscriberName);\n return {\n subscriberName,\n success: false,\n error: error instanceof Error ? error : undefined,\n };\n }\n }),\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled' && !result.value.success) {\n failures.push({\n subscriberName: result.value.subscriberName,\n error: result.value.error,\n });\n }\n }\n\n return failures;\n }\n\n /**\n * Flush all remaining events. Queue remains usable after flush (e.g. for\n * auto-flush at root span end). Use shutdown() when tearing down the queue.\n */\n async flush(): Promise<void> {\n // Cancel any pending timer\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Wait for any in-progress flush to complete\n if (this.flushPromise) {\n await this.flushPromise;\n }\n\n // Flush all remaining batches\n while (this.queue.length > 0) {\n await this.doFlushBatch();\n }\n }\n\n /**\n * Flush remaining events and permanently disable the queue (reject new events).\n * Use for process/SDK shutdown; use flush() for periodic or span-end drain.\n */\n async shutdown(): Promise<void> {\n this.isShuttingDown = true;\n await this.flush();\n }\n\n /**\n * Cleanup observable metric callbacks to prevent memory leaks\n * Call this when destroying the EventQueue instance\n */\n cleanup(): void {\n // Remove all observable callbacks\n for (const cleanupFn of this.observableCleanups) {\n try {\n cleanupFn();\n } catch {\n // Ignore cleanup errors\n }\n }\n this.observableCleanups = [];\n }\n\n /**\n * Get queue size (for testing/debugging)\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Get subscriber health status (for testing/debugging)\n */\n getSubscriberHealth(): Map<string, boolean> {\n return new Map(this.subscriberHealthy);\n }\n\n /**\n * Check if a specific subscriber is healthy\n */\n isSubscriberHealthy(subscriberName: string): boolean {\n return this.subscriberHealthy.get(subscriberName.toLowerCase()) ?? true;\n }\n\n /**\n * Manually mark a subscriber as healthy or unhealthy\n * (used for circuit breaker integration)\n */\n setSubscriberHealth(subscriberName: string, healthy: boolean): void {\n this.subscriberHealthy.set(subscriberName.toLowerCase(), healthy);\n }\n}\n","/**\n * Input validation for events events and attributes\n *\n * Prevents:\n * - Invalid event names\n * - Oversized payloads\n * - Circular references\n * - Sensitive data leaks\n */\n\nimport type { EventAttributes } from './event-subscriber';\n\nexport interface ValidationConfig {\n /** Max event name length (default: 100) */\n maxEventNameLength: number;\n /** Max attribute key length (default: 100) */\n maxAttributeKeyLength: number;\n /** Max attribute value length for strings (default: 1000) */\n maxAttributeValueLength: number;\n /** Max total attributes per event (default: 50) */\n maxAttributeCount: number;\n /** Max nesting depth for objects (default: 3) */\n maxNestingDepth: number;\n /** Sensitive field patterns to redact */\n sensitivePatterns: RegExp[];\n}\n\nconst DEFAULT_CONFIG: ValidationConfig = {\n maxEventNameLength: 100,\n maxAttributeKeyLength: 100,\n maxAttributeValueLength: 1000,\n maxAttributeCount: 50,\n maxNestingDepth: 3,\n sensitivePatterns: [\n /password/i,\n /secret/i,\n /token/i,\n /api[_-]?key/i,\n /access[_-]?key/i,\n /private[_-]?key/i,\n /auth/i,\n /credential/i,\n /ssn/i,\n /credit[_-]?card/i,\n ],\n};\n\nexport class ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Validate and sanitize event name\n * Throws ValidationError if invalid\n */\nexport function validateEventName(\n eventName: string,\n config: ValidationConfig = DEFAULT_CONFIG,\n): string {\n // Check type\n if (typeof eventName !== 'string') {\n throw new ValidationError(\n `Event name must be a string, got ${typeof eventName}`,\n );\n }\n\n // Check non-empty\n const trimmed = eventName.trim();\n if (trimmed.length === 0) {\n throw new ValidationError('Event name cannot be empty');\n }\n\n // Check length\n if (trimmed.length > config.maxEventNameLength) {\n throw new ValidationError(\n `Event name too long (${trimmed.length} chars). ` +\n `Max: ${config.maxEventNameLength}`,\n );\n }\n\n // Check valid characters (alphanumeric, dots, underscores, hyphens)\n if (!/^[a-zA-Z0-9._-]+$/.test(trimmed)) {\n throw new ValidationError(\n `Event name contains invalid characters: \"${trimmed}\". ` +\n 'Use only letters, numbers, dots, underscores, and hyphens.',\n );\n }\n\n return trimmed;\n}\n\n/**\n * Validate and sanitize attributes\n * Returns sanitized attributes (sensitive data redacted)\n */\nexport function validateAttributes(\n attributes: EventAttributes | undefined,\n config: ValidationConfig = DEFAULT_CONFIG,\n): EventAttributes | undefined {\n if (attributes === undefined || attributes === null) {\n return undefined;\n }\n\n // Check type\n if (typeof attributes !== 'object' || Array.isArray(attributes)) {\n throw new ValidationError('Attributes must be an object');\n }\n\n // Count attributes\n const keys = Object.keys(attributes);\n if (keys.length > config.maxAttributeCount) {\n throw new ValidationError(\n `Too many attributes (${keys.length}). ` +\n `Max: ${config.maxAttributeCount}`,\n );\n }\n\n // Validate and sanitize each attribute\n const sanitized: EventAttributes = {};\n\n for (const key of keys) {\n // Validate key\n if (key.length > config.maxAttributeKeyLength) {\n throw new ValidationError(\n `Attribute key too long: \"${key.slice(0, 20)}...\" ` +\n `(${key.length} chars). Max: ${config.maxAttributeKeyLength}`,\n );\n }\n\n const value = attributes[key];\n\n // Redact sensitive *strings* only. Numeric/boolean values are not\n // credentials and replacing them with the literal string '[REDACTED]'\n // both leaks no useful signal and breaks downstream type expectations\n // (e.g. an LLM `promptTokens` counter becoming a string poisons every\n // consumer that treats it as a number).\n const isSensitive =\n typeof value === 'string' &&\n config.sensitivePatterns.some((pattern) => pattern.test(key));\n\n if (isSensitive) {\n sanitized[key] = '[REDACTED]';\n continue;\n }\n\n sanitized[key] = sanitizeValue(value, config, 1) as\n string | number | boolean;\n }\n\n return sanitized;\n}\n\n/**\n * Sanitize attribute value (recursive)\n */\nfunction sanitizeValue(\n value: unknown,\n config: ValidationConfig,\n depth: number,\n): unknown {\n // Check nesting depth\n if (depth > config.maxNestingDepth) {\n return '[MAX_DEPTH_EXCEEDED]';\n }\n\n // Handle null/undefined\n if (value === null || value === undefined) {\n return value;\n }\n\n // Handle primitives\n if (typeof value === 'string') {\n if (value.length > config.maxAttributeValueLength) {\n return value.slice(0, config.maxAttributeValueLength) + '...';\n }\n return value;\n }\n\n if (typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeValue(item, config, depth + 1));\n }\n\n // Handle objects\n if (typeof value === 'object') {\n try {\n // Check for circular references\n JSON.stringify(value);\n\n const sanitized: Record<string, unknown> = {};\n for (const key in value) {\n if (Object.prototype.hasOwnProperty.call(value, key)) {\n const nested = (value as Record<string, unknown>)[key];\n // See top-level branch above: only string values are redacted.\n const isSensitive =\n typeof nested === 'string' &&\n config.sensitivePatterns.some((pattern) => pattern.test(key));\n\n sanitized[key] = isSensitive\n ? '[REDACTED]'\n : sanitizeValue(nested, config, depth + 1);\n }\n }\n return sanitized;\n } catch {\n // Circular reference detected\n return '[CIRCULAR]';\n }\n }\n\n // Unsupported type (function, symbol, etc.)\n return `[${typeof value}]`;\n}\n\n/**\n * Validate and sanitize an events event\n * Returns { eventName, attributes } with sanitized values\n */\nexport function validateEvent(\n eventName: string,\n attributes?: EventAttributes,\n config?: Partial<ValidationConfig>,\n): { eventName: string; attributes?: EventAttributes } {\n const fullConfig = { ...DEFAULT_CONFIG, ...config };\n\n return {\n eventName: validateEventName(eventName, fullConfig),\n attributes: validateAttributes(attributes, fullConfig),\n };\n}\n\n/**\n * Get default validation config (for testing/customization)\n */\nexport function getDefaultValidationConfig(): ValidationConfig {\n return { ...DEFAULT_CONFIG };\n}\n","/**\n * Global track() function for business events\n *\n * Simple, no instantiation needed, auto-attaches trace context\n */\n\nimport { trace } from '@opentelemetry/api';\nimport { EventQueue } from './event-queue';\nimport {\n getConfig,\n warnIfNotInitialized,\n isInitialized,\n getValidationConfig,\n getEventsConfig,\n} from './init';\nimport { validateEvent } from './validation';\nimport { getOrCreateCorrelationId } from './correlation-id';\nimport type { EventTrackingOptions } from './event-subscriber';\nimport type { AutotelEventContext } from './event-subscriber';\n\n// Global events queue (initialized on first track call)\nlet eventsQueue: EventQueue | null = null;\n\n/**\n * Build autotel event context for trace correlation\n *\n * Works in multiple contexts:\n * 1. Inside a span → use current span's trace_id + span_id\n * 2. Outside span → use correlation_id only\n * 3. With trace URL config → include clickable trace URL\n */\nfunction buildAutotelContext(\n span: ReturnType<typeof trace.getActiveSpan>,\n): AutotelEventContext | undefined {\n const eventsConfig = getEventsConfig();\n const config = getConfig();\n\n // Always generate a correlation_id\n const correlationId = getOrCreateCorrelationId();\n\n // Return minimal context if trace context is not enabled\n if (!eventsConfig?.includeTraceContext) {\n return {\n correlation_id: correlationId,\n };\n }\n\n // Build base context\n const autotelContext: AutotelEventContext = {\n correlation_id: correlationId,\n };\n\n // Add trace context if inside a span\n const spanContext = span?.spanContext();\n if (spanContext) {\n autotelContext.trace_id = spanContext.traceId;\n autotelContext.span_id = spanContext.spanId;\n\n // Trace flags as 2-char hex string (canonical format)\n autotelContext.trace_flags = spanContext.traceFlags\n .toString(16)\n .padStart(2, '0');\n\n // Tracestate if present\n // Defensive: serialize() is standard OTel API but may be missing in some runtimes\n const traceState = spanContext.traceState;\n if (traceState) {\n try {\n if (typeof traceState.serialize === 'function') {\n const traceStateStr = traceState.serialize();\n if (traceStateStr) {\n autotelContext.trace_state = traceStateStr;\n }\n }\n } catch {\n // Silently ignore serialization errors\n }\n }\n\n // Generate trace URL if configured\n if (eventsConfig.traceUrl && config) {\n const traceUrl = eventsConfig.traceUrl({\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n correlationId,\n serviceName: config.service,\n environment: config.environment,\n });\n if (traceUrl) {\n autotelContext.trace_url = traceUrl;\n }\n }\n } else {\n // Outside span but may still have trace URL generator\n if (eventsConfig.traceUrl && config) {\n const traceUrl = eventsConfig.traceUrl({\n correlationId,\n serviceName: config.service,\n environment: config.environment,\n });\n if (traceUrl) {\n autotelContext.trace_url = traceUrl;\n }\n }\n }\n\n return autotelContext;\n}\n\n/**\n * Initialize events queue lazily\n */\nfunction getOrCreateQueue(): EventQueue | null {\n if (!isInitialized()) {\n warnIfNotInitialized('track()');\n return null;\n }\n\n if (!eventsQueue) {\n const config = getConfig();\n if (!config?.subscribers || config.subscribers.length === 0) {\n // No subscribers configured - no-op\n return null;\n }\n\n eventsQueue = new EventQueue(config.subscribers);\n }\n\n return eventsQueue;\n}\n\n/**\n * Track a business events event\n *\n * Features:\n * - Auto-attaches traceId and spanId if in active span\n * - Batched sending with retry\n * - Type-safe with optional generic\n * - No-op if init() not called or no subscribers configured\n *\n * @example Basic usage\n * ```typescript\n * track('user.signup', { userId: '123', plan: 'pro' })\n * ```\n *\n * @example With type safety\n * ```typescript\n * interface EventDatas {\n * 'user.signup': { userId: string; plan: string }\n * 'plan.upgraded': { userId: string; revenue: number }\n * }\n *\n * track<EventDatas>('user.signup', { userId: '123', plan: 'pro' })\n * ```\n *\n * @example Trace correlation (automatic)\n * ```typescript\n * @Instrumented()\n * class UserService {\n * async createUser(data: CreateUserData) {\n * // This track call automatically includes traceId + spanId\n * track('user.signup', { userId: data.id })\n * }\n * }\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function track<Events extends Record<string, any> = Record<string, any>>(\n event: keyof Events & string,\n data?: Events[typeof event],\n options?: EventTrackingOptions,\n): void {\n const queue = getOrCreateQueue();\n if (!queue) return; // No-op if not initialized or no subscribers\n\n // Validate and sanitize input (with custom config if provided)\n const validationConfig = getValidationConfig();\n const validated = validateEvent(event, data, validationConfig || undefined);\n\n // Auto-attach trace context if available (free win!)\n const span = trace.getActiveSpan();\n const enrichedData = span\n ? {\n ...validated.attributes,\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n }\n : validated.attributes;\n\n // Build autotel context (same as Event class)\n const autotelContext = buildAutotelContext(span);\n\n queue.enqueue({\n name: validated.eventName,\n attributes: enrichedData,\n timestamp: Date.now(),\n autotel: autotelContext,\n schema: options?.schema,\n });\n}\n\n/**\n * Get events queue (for flush/shutdown)\n * @internal\n */\nexport function getEventQueue(): EventQueue | null {\n return eventsQueue;\n}\n\n/**\n * Reset events queue (for shutdown/cleanup)\n * @internal\n */\nexport function resetEventQueue(): void {\n eventsQueue = null;\n}\n","/**\n * Circuit breaker for event subscribers\n *\n * Prevents cascading failures by fast-failing when an (subscriber) is unhealthy.\n * Uses the circuit breaker pattern with three states:\n * - CLOSED: Normal operation ((subscriber) working)\n * - OPEN: Fast-fail mode ((subscriber) down)\n * - HALF_OPEN: Testing if (subscriber) recovered\n */\n\nexport interface CircuitBreakerConfig {\n /** Number of failures before opening circuit (default: 5) */\n failureThreshold: number;\n /** Time to wait before trying again in ms (default: 30000 = 30s) */\n resetTimeout: number;\n /** Time window for counting failures in ms (default: 60000 = 1min) */\n windowSize: number;\n}\n\nconst DEFAULT_CONFIG: CircuitBreakerConfig = {\n failureThreshold: 5,\n resetTimeout: 30_000, // 30 seconds\n windowSize: 60_000, // 1 minute\n};\n\nexport type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';\n\nexport const CircuitState = {\n CLOSED: 'CLOSED' as const, // Normal operation\n OPEN: 'OPEN' as const, // Fast-fail mode\n HALF_OPEN: 'HALF_OPEN' as const, // Testing recovery\n} as const;\n\ninterface FailureRecord {\n timestamp: number;\n error: string;\n}\n\n/**\n * Circuit breaker implementation\n *\n * Tracks failures and automatically opens the circuit to prevent\n * overwhelming failing subscribers.\n */\nexport class CircuitBreaker {\n private state: CircuitState = CircuitState.CLOSED;\n private failures: FailureRecord[] = [];\n private lastFailureTime: number = 0;\n private readonly config: CircuitBreakerConfig;\n private readonly name: string;\n\n constructor(name: string, config?: Partial<CircuitBreakerConfig>) {\n this.name = name;\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Execute a function with circuit breaker protection\n * Throws CircuitOpenError if circuit is open\n */\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n // Check if circuit is open\n if (this.state === CircuitState.OPEN) {\n // Check if we should transition to half-open\n const now = Date.now();\n if (now - this.lastFailureTime >= this.config.resetTimeout) {\n this.state = CircuitState.HALF_OPEN;\n } else {\n throw new CircuitOpenError(\n `Circuit breaker is OPEN for ${this.name}. ` +\n `Will retry in ${Math.ceil((this.config.resetTimeout - (now - this.lastFailureTime)) / 1000)}s`,\n );\n }\n }\n\n try {\n const result = await fn();\n\n // Success! Close circuit if it was half-open\n if (this.state === CircuitState.HALF_OPEN) {\n this.reset();\n }\n\n return result;\n } catch (error) {\n this.recordFailure(error);\n throw error;\n }\n }\n\n /**\n * Record a failure and potentially open the circuit\n */\n private recordFailure(error: unknown): void {\n const now = Date.now();\n\n // Remove old failures outside the time window\n this.failures = this.failures.filter(\n (f) => now - f.timestamp < this.config.windowSize,\n );\n\n // Record new failure\n this.failures.push({\n timestamp: now,\n error: error instanceof Error ? error.message : String(error),\n });\n\n this.lastFailureTime = now;\n\n // Check if we should open the circuit\n if (this.failures.length >= this.config.failureThreshold) {\n if (this.state === CircuitState.HALF_OPEN) {\n // Failed during test - reopen circuit\n this.state = CircuitState.OPEN;\n } else if (this.state === CircuitState.CLOSED) {\n // Too many failures - open circuit\n this.state = CircuitState.OPEN;\n }\n }\n }\n\n /**\n * Reset the circuit breaker (on success)\n */\n private reset(): void {\n this.state = CircuitState.CLOSED;\n this.failures = [];\n this.lastFailureTime = 0;\n }\n\n /**\n * Get current state (for monitoring)\n */\n getState(): CircuitState {\n return this.state;\n }\n\n /**\n * Get failure count in current window\n */\n getFailureCount(): number {\n const now = Date.now();\n // Clean up old failures\n this.failures = this.failures.filter(\n (f) => now - f.timestamp < this.config.windowSize,\n );\n return this.failures.length;\n }\n\n /**\n * Get recent failures (for debugging)\n */\n getRecentFailures(): FailureRecord[] {\n const now = Date.now();\n return this.failures.filter(\n (f) => now - f.timestamp < this.config.windowSize,\n );\n }\n\n /**\n * Manually reset the circuit breaker (for testing or manual intervention)\n */\n forceReset(): void {\n this.reset();\n }\n\n /**\n * Manually open the circuit (for testing or manual intervention)\n */\n forceOpen(): void {\n this.state = CircuitState.OPEN;\n this.lastFailureTime = Date.now();\n }\n}\n\n/**\n * Error thrown when circuit is open\n */\nexport class CircuitOpenError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CircuitOpenError';\n }\n}\n","/**\n * Operation context tracking using AsyncLocalStorage\n *\n * This module provides a way to track operation names across async boundaries\n * so they can be automatically captured in events events.\n *\n * We cannot read span attributes from OpenTelemetry's API (it's write-only),\n * so we maintain our own async context storage.\n */\n\n// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeAsyncHooks from 'node:async_hooks';\n\n/**\n * Operation context that flows through async operations\n */\nexport interface OperationContext {\n /**\n * The name of the current operation\n * This is set by trace() or span() and can be read by events\n */\n name: string;\n}\n\n/**\n * AsyncLocalStorage instance for tracking operation context\n */\nconst operationStorage =\n new nodeAsyncHooks.AsyncLocalStorage<OperationContext>();\n\n/**\n * Get the current operation context (if any)\n *\n * @returns The current operation context, or undefined if not in an operation\n *\n * @example\n * ```typescript\n * const ctx = getOperationContext();\n * if (ctx) {\n * console.log('Current operation:', ctx.name);\n * }\n * ```\n */\nexport function getOperationContext(): OperationContext | undefined {\n return operationStorage.getStore();\n}\n\n/**\n * Run a function within an operation context\n *\n * This sets the operation name for the duration of the function execution,\n * including all async operations spawned from it.\n *\n * @param name - The operation name to set\n * @param fn - The function to execute within the context\n * @returns The result of the function\n *\n * @example\n * ```typescript\n * const result = await runInOperationContext('user.create', async () => {\n * // Any events.trackEvent() calls here will automatically capture\n * // 'operation.name': 'user.create'\n * await createUser();\n * return 'success';\n * });\n * ```\n */\nexport function runInOperationContext<T>(name: string, fn: () => T): T {\n return operationStorage.run({ name }, fn);\n}\n\n/**\n * Update the operation name in the current context\n *\n * This is useful when you want to change the operation name within\n * an already-established context (e.g., when entering a nested span).\n *\n * @param name - The new operation name\n *\n * @example\n * ```typescript\n * runInOperationContext('parent.operation', () => {\n * // operation.name is 'parent.operation'\n *\n * updateOperationName('nested.operation');\n * // operation.name is now 'nested.operation'\n * });\n * ```\n */\nexport function updateOperationName(name: string): void {\n const store = operationStorage.getStore();\n if (store) {\n store.name = name;\n }\n}\n","/**\n * Events configuration types for trace context, correlation IDs, and enrichment\n *\n * @example Basic usage\n * ```typescript\n * import { init } from 'autotel';\n *\n * init({\n * service: 'my-app',\n * events: {\n * includeTraceContext: true,\n * traceUrl: (ctx) => `https://grafana.internal/explore?traceId=${ctx.traceId}`\n * }\n * });\n * ```\n */\n\n/**\n * Context passed to the traceUrl function for generating clickable trace URLs\n */\nexport interface TraceUrlContext {\n /** Trace ID (32 hex chars) - may be undefined outside a trace */\n traceId?: string;\n /** Span ID (16 hex chars) - may be undefined outside a trace */\n spanId?: string;\n /** Correlation ID (always present, 16 hex chars) */\n correlationId: string;\n /** Service name from init config */\n serviceName: string;\n /** Environment from init config */\n environment?: string;\n}\n\n/**\n * Per-key transform options for baggage enrichment\n */\nexport type BaggageTransform = 'plain' | 'hash' | ((value: string) => string);\n\n/**\n * Baggage enrichment configuration with guardrails\n */\nexport interface EnrichFromBaggageConfig {\n /**\n * Allowlist of baggage keys to include in events\n * Supports exact matches and patterns (e.g., 'tenant.*')\n */\n allow: string[];\n\n /**\n * Optional denylist of baggage keys to exclude\n * Takes precedence over allow list\n */\n deny?: string[];\n\n /**\n * Optional prefix to add to all enriched keys\n * @example 'ctx.' results in 'ctx.tenant.id'\n */\n prefix?: string;\n\n /**\n * Maximum number of keys to include (default: 10)\n * Prevents payload bloat from excessive baggage\n */\n maxKeys?: number;\n\n /**\n * Maximum total bytes for enriched values (default: 1024)\n * Prevents payload bloat from large baggage values\n */\n maxBytes?: number;\n\n /**\n * Per-key transform options\n * - 'plain': Include value as-is\n * - 'hash': Hash the value (for PII protection)\n * - function: Custom transform function\n *\n * @example\n * ```typescript\n * transform: {\n * 'user.id': 'hash', // Hash user ID for privacy\n * 'tenant.id': 'plain', // Include tenant ID as-is\n * 'session.id': (v) => v.slice(0, 8) // Custom truncation\n * }\n * ```\n */\n transform?: Record<string, BaggageTransform>;\n}\n\n/**\n * Events configuration for trace context and enrichment\n */\nexport interface EventsConfig {\n /**\n * Include trace context in events (default: false)\n *\n * When enabled, events automatically include:\n * - autotel.trace_id (32 hex chars)\n * - autotel.span_id (16 hex chars)\n * - autotel.trace_flags (2 hex chars)\n * - autotel.trace_state (raw tracestate string, if present)\n * - autotel.correlation_id (always present, 16 hex chars)\n *\n * Subscribers map these to platform-specific names:\n * - PostHog: $trace_id, $span_id\n * - Mixpanel: trace_id, span_id\n */\n includeTraceContext?: boolean;\n\n /**\n * Include full array of linked trace IDs for batch/fan-in scenarios (default: false)\n *\n * When false (default), batch/fan-in events include:\n * - autotel.linked_trace_id_count: Number of linked parents\n * - autotel.linked_trace_id_hash: Stable hash of sorted IDs (keeps payload lean)\n *\n * When true, events also include:\n * - autotel.linked_trace_ids: Full array of linked trace IDs\n */\n includeLinkedTraceIds?: boolean;\n\n /**\n * Generate clickable trace URL from context\n *\n * @param ctx - Trace context with traceId, spanId, correlationId, serviceName, environment\n * @returns URL string or undefined to skip\n *\n * @example Grafana Tempo\n * ```typescript\n * traceUrl: (ctx) => ctx.traceId\n * ? `https://grafana.internal/explore?traceId=${ctx.traceId}`\n * : undefined\n * ```\n *\n * @example Datadog\n * ```typescript\n * traceUrl: (ctx) => ctx.traceId\n * ? `https://app.datadoghq.com/apm/traces?traceId=${ctx.traceId}`\n * : undefined\n * ```\n *\n * @example Jaeger\n * ```typescript\n * traceUrl: (ctx) => ctx.traceId\n * ? `https://jaeger.internal/trace/${ctx.traceId}`\n * : undefined\n * ```\n */\n traceUrl?: (ctx: TraceUrlContext) => string | undefined;\n\n /**\n * Auto-enrich events from baggage with guardrails\n *\n * Automatically includes baggage entries in events without manual code.\n * Apply allow/deny lists and per-key transforms for PII protection.\n *\n * @example Basic allowlist\n * ```typescript\n * enrichFromBaggage: {\n * allow: ['tenant.id', 'user.id', 'request.id']\n * }\n * // Events include: tenant.id, user.id, request.id from baggage\n * ```\n *\n * @example With prefix and transforms\n * ```typescript\n * enrichFromBaggage: {\n * allow: ['tenant.id', 'user.id', 'user.email'],\n * deny: ['user.ssn'],\n * prefix: 'ctx.',\n * transform: {\n * 'user.id': 'hash',\n * 'user.email': 'hash'\n * }\n * }\n * // Events include: ctx.tenant.id, ctx.user.id (hashed), ctx.user.email (hashed)\n * ```\n */\n enrichFromBaggage?: EnrichFromBaggageConfig;\n}\n\n/**\n * Autotel context object attached to event envelopes\n *\n * This structured object is attached to events and subscribers\n * decide how to map/flatten for their platform.\n */\nexport interface AutotelEventContext {\n /** Trace ID (32 hex chars) - present when inside a trace */\n trace_id?: string;\n /** Span ID (16 hex chars) - present when inside a span */\n span_id?: string;\n /** Trace flags (2 hex chars, e.g., '01' for sampled) */\n trace_flags?: string;\n /** Raw tracestate string - present if tracestate exists */\n trace_state?: string;\n /** Clickable trace URL - present if traceUrl config is set */\n trace_url?: string;\n /** Correlation ID (always present, 16 hex chars) */\n correlation_id: string;\n /** Number of linked parent traces (batch/fan-in scenarios) */\n linked_trace_id_count?: number;\n /** Stable hash of linked trace IDs (default for batch/fan-in) */\n linked_trace_id_hash?: string;\n /** Full array of linked trace IDs (only if includeLinkedTraceIds: true) */\n linked_trace_ids?: string[];\n}\n\n/**\n * Hash a string value for PII protection\n *\n * Uses a simple, fast hash function suitable for correlation.\n * NOT cryptographically secure - use for PII masking, not security.\n */\nexport function hashValue(value: string): string {\n let hash = 0;\n for (let i = 0; i < value.length; i++) {\n // Keep charCodeAt. codePointAt returns a different number for astral\n // characters, which would change the hash of any value containing one and\n // break correlation with hashes already emitted.\n // eslint-disable-next-line unicorn/prefer-code-point\n const char = value.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n // Convert to positive hex string\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Create a stable hash of an array of trace IDs\n *\n * Sorts the array first to ensure deterministic output regardless of order.\n */\nexport function hashLinkedTraceIds(traceIds: string[]): string {\n const sorted = traceIds.toSorted();\n return hashValue(sorted.join(','));\n}\n","/**\n * Events API for product events platforms\n *\n * Track user behavior, business events, and critical actions.\n * Sends to product events platforms (PostHog, Mixpanel, Amplitude) via subscribers.\n * For business people who think in events/funnels.\n *\n * For OpenTelemetry metrics (Prometheus/Grafana), use the Metrics class instead.\n *\n * @example Recommended: Configure subscribers in init(), use track() function\n * ```typescript\n * import { init, track } from 'autotel';\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * init({\n * service: 'my-app',\n * subscribers: [new PostHogSubscriber({ apiKey: 'phc_...' })]\n * });\n *\n * // Track events - uses subscribers from init()\n * track('application.submitted', { jobId: '123', userId: '456' });\n * ```\n *\n * @example Create Event instance (inherits subscribers from init)\n * ```typescript\n * import { Event } from 'autotel/event';\n *\n * // Uses subscribers configured in init()\n * const event = new Event('job-application');\n * event.trackEvent('application.submitted', { jobId: '123' });\n * ```\n *\n * @example Override subscribers for specific Event instance\n * ```typescript\n * import { Event } from 'autotel/event';\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * // Override: use different subscribers for this instance\n * const event = new Event('job-application', {\n * subscribers: [new PostHogSubscriber({ apiKey: 'phc_different_project' })]\n * });\n *\n * event.trackEvent('application.submitted', { jobId: '123' });\n * ```\n */\n\nimport { trace, propagation, context } from '@opentelemetry/api';\nimport { type Logger } from './logger';\nimport {\n getLogger,\n getValidationConfig,\n getConfig,\n getEventsConfig,\n} from './init';\nimport {\n type EventSubscriber,\n type EventAttributes,\n type EventAttributesInput,\n type FunnelStatus,\n type OutcomeStatus,\n type AutotelEventContext,\n} from './event-subscriber';\nimport { type EventCollector } from './event-testing';\nimport { CircuitBreaker, CircuitOpenError } from './circuit-breaker';\nimport { validateEvent } from './validation';\nimport { getOperationContext } from './operation-context';\nimport { type EnrichFromBaggageConfig, hashValue } from './events-config';\nimport { getOrCreateCorrelationId } from './correlation-id';\n\n// Re-export types for convenience\nexport type {\n EventAttributes,\n EventAttributesInput,\n FunnelStatus,\n OutcomeStatus,\n} from './event-subscriber';\n\n/**\n * Events class for tracking user behavior and product events\n *\n * Track critical indicators such as:\n * - User events (signups, purchases, feature usage)\n * - Conversion funnels (signup → activation → purchase)\n * - Business outcomes (success/failure rates)\n * - Product metrics (revenue, engagement, retention)\n *\n * All events are sent to events platforms via subscribers (PostHog, Mixpanel, etc.).\n * For OpenTelemetry metrics, use the Metrics class instead.\n */\n/**\n * Events options\n */\nexport interface EventsOptions {\n /** Optional logger for audit trail */\n logger?: Logger;\n /** Optional collector for testing (captures events in memory) */\n collector?: EventCollector;\n /**\n * Optional subscribers to send events to other platforms\n * (e.g., PostHog, Mixpanel, Amplitude)\n *\n * **Subscriber Resolution**:\n * - If provided → uses these subscribers (instance override)\n * - If not provided → falls back to subscribers from `init()` (global config)\n * - If neither → no subscribers (events logged only)\n *\n * Install `autotel-subscribers` package for ready-made subscribers\n */\n subscribers?: EventSubscriber[];\n}\n\nexport class Event {\n private serviceName: string;\n private logger?: Logger;\n private collector?: EventCollector;\n private subscribers: EventSubscriber[];\n private hasSubscribers: boolean; // Cached for performance\n private circuitBreakers: Map<EventSubscriber, CircuitBreaker>; // One per subscriber\n\n /**\n * Create a new Event instance\n *\n * **Note**: Most users should use `init()` + `track()` instead of creating Event instances directly.\n *\n * **Subscriber Resolution**:\n * - If `subscribers` provided in options → uses those (instance override)\n * - If `subscribers` not provided → falls back to subscribers from `init()` (global config)\n * - If neither → no subscribers (events logged only)\n *\n * @param serviceName - Service name for identifying events\n * @param options - Optional configuration (logger, collector, subscribers)\n *\n * @example Recommended: Use track() with init()\n * ```typescript\n * import { init, track } from 'autotel';\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * init({\n * service: 'checkout',\n * subscribers: [new PostHogSubscriber({ apiKey: 'phc_...' })]\n * });\n *\n * track('purchase.completed', { amount: 99.99 });\n * ```\n *\n * @example Inherit subscribers from init()\n * ```typescript\n * // Uses subscribers configured in init()\n * const event = new Event('checkout');\n * event.trackEvent('purchase.completed', { amount: 99.99 });\n * ```\n *\n * @example Override subscribers for this instance\n * ```typescript\n * import { Event } from 'autotel/event';\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * // Override: use different subscribers for this instance only\n * const event = new Event('checkout', {\n * subscribers: [new PostHogSubscriber({ apiKey: 'phc_different_project' })]\n * });\n * ```\n */\n constructor(serviceName: string, options: EventsOptions = {}) {\n this.serviceName = serviceName;\n this.logger = options.logger;\n this.collector = options.collector;\n\n // Subscriber resolution: instance-level overrides global init() config\n // If subscribers provided to constructor, use those\n // Otherwise, fall back to subscribers from init()\n this.subscribers =\n options.subscribers === undefined\n ? getConfig()?.subscribers || []\n : options.subscribers;\n\n this.hasSubscribers = this.subscribers.length > 0; // Cache for hot path\n\n // Create circuit breaker for each subscriber\n this.circuitBreakers = new Map();\n for (const subscriber of this.subscribers) {\n const subscriberName = subscriber.name || 'Unknown';\n this.circuitBreakers.set(\n subscriber,\n new CircuitBreaker(subscriberName, {\n failureThreshold: 5,\n resetTimeout: 30_000, // 30s\n windowSize: 60_000, // 1min\n }),\n );\n }\n }\n\n /**\n * Automatically enrich attributes with all available telemetry context\n *\n * Auto-captures:\n * - Resource attributes: service.version, deployment.environment\n * - Trace context: traceId, spanId, correlationId\n * - Operation context: operation.name\n */\n private enrichWithTelemetryContext(\n attributes: EventAttributes = {},\n ): EventAttributes {\n const enriched: EventAttributes = {\n service: this.serviceName,\n ...attributes,\n };\n\n // 1. Resource attributes (service-level context)\n const config = getConfig();\n if (config) {\n if (config.version) {\n enriched['service.version'] = config.version;\n }\n if (config.environment) {\n enriched['deployment.environment'] = config.environment;\n }\n }\n\n // 2. Trace context (if inside a traced operation)\n const span = trace.getActiveSpan();\n const spanContext = span?.spanContext();\n if (spanContext) {\n enriched.traceId = spanContext.traceId;\n enriched.spanId = spanContext.spanId;\n // Add correlation ID (first 16 chars of trace ID) for easier log grouping\n enriched.correlationId = spanContext.traceId.slice(0, 16);\n }\n\n // 3. Operation context (if inside a trace/span)\n const operationContext = getOperationContext();\n if (operationContext) {\n enriched['operation.name'] = operationContext.name;\n }\n\n return enriched;\n }\n\n /**\n * Build autotel event context for trace correlation\n *\n * Works in 4 contexts:\n * 1. Inside a span → use current span's trace_id + span_id\n * 2. Outside span but in AsyncLocalStorage context → use trace_id + correlation_id\n * 3. Totally standalone → use correlation_id + service/env/version\n * 4. Batch/fan-in (multiple linked parents) → use count + hash or full array\n *\n * @returns AutotelEventContext or undefined if trace context is disabled\n */\n private buildAutotelContext(): AutotelEventContext | undefined {\n const eventsConfig = getEventsConfig();\n\n // Return undefined if trace context is not enabled\n if (!eventsConfig?.includeTraceContext) {\n // Still generate correlation_id even without full trace context\n // This provides a stable join key across events/logs/spans\n return {\n correlation_id: getOrCreateCorrelationId(),\n };\n }\n\n const config = getConfig();\n const span = trace.getActiveSpan();\n const spanContext = span?.spanContext();\n\n // Always generate a correlation_id\n const correlationId = getOrCreateCorrelationId();\n\n // Build base context\n const autotelContext: AutotelEventContext = {\n correlation_id: correlationId,\n };\n\n // Add trace context if inside a span\n if (spanContext) {\n autotelContext.trace_id = spanContext.traceId;\n autotelContext.span_id = spanContext.spanId;\n\n // Trace flags as 2-char hex string (canonical format)\n autotelContext.trace_flags = spanContext.traceFlags\n .toString(16)\n .padStart(2, '0');\n\n // Tracestate if present\n const traceState = spanContext.traceState;\n if (traceState) {\n // Convert TraceState to string representation safely\n let traceStateStr = '';\n try {\n if (typeof traceState.serialize === 'function') {\n traceStateStr = traceState.serialize();\n }\n } catch {\n // Silently ignore serialization errors - traceState is optional metadata\n }\n if (traceStateStr) {\n autotelContext.trace_state = traceStateStr;\n }\n }\n\n // Generate trace URL if configured\n if (eventsConfig.traceUrl) {\n const traceUrl = eventsConfig.traceUrl({\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n correlationId,\n serviceName: config?.service || this.serviceName,\n environment: config?.environment,\n });\n if (traceUrl) {\n autotelContext.trace_url = traceUrl;\n }\n }\n\n // Handle linked spans (batch/fan-in scenarios)\n // Note: This would require access to span links which are not easily accessible\n // from the public OpenTelemetry API. For now, we skip this unless we have\n // explicit linked trace IDs passed in.\n } else {\n // Outside span but may still have trace URL generator\n if (eventsConfig.traceUrl && config) {\n const traceUrl = eventsConfig.traceUrl({\n correlationId,\n serviceName: config.service,\n environment: config.environment,\n });\n if (traceUrl) {\n autotelContext.trace_url = traceUrl;\n }\n }\n }\n\n return autotelContext;\n }\n\n /**\n * Enrich event attributes from baggage with guardrails\n *\n * @param attributes - Current event attributes\n * @returns Enriched attributes with baggage values\n */\n private enrichFromBaggage(attributes: EventAttributes): EventAttributes {\n const eventsConfig = getEventsConfig();\n const enrichConfig = eventsConfig?.enrichFromBaggage;\n\n if (!enrichConfig) {\n return attributes;\n }\n\n const enriched = { ...attributes };\n const activeContext = context.active();\n const baggage = propagation.getBaggage(activeContext);\n\n if (!baggage) {\n return enriched;\n }\n\n let keyCount = 0;\n let byteCount = 0;\n const maxKeys = enrichConfig.maxKeys ?? 10;\n const maxBytes = enrichConfig.maxBytes ?? 1024;\n const prefix = enrichConfig.prefix ?? '';\n\n // Get all baggage entries\n for (const [key, entry] of baggage.getAllEntries()) {\n // Check if key is allowed\n if (!this.isBaggageKeyAllowed(key, enrichConfig)) {\n continue;\n }\n\n // Check limits\n if (keyCount >= maxKeys) {\n break;\n }\n\n const value = entry.value;\n\n // Apply transform first so maxBytes is checked against transformed size (e.g. hash output)\n const transform = enrichConfig.transform?.[key];\n let transformedValue: string;\n\n if (transform === 'hash') {\n transformedValue = hashValue(value);\n } else if (transform === 'plain' || !transform) {\n transformedValue = value;\n } else if (typeof transform === 'function') {\n transformedValue = transform(value);\n } else {\n transformedValue = value;\n }\n\n const valueBytes = new TextEncoder().encode(transformedValue).length;\n\n if (byteCount + valueBytes > maxBytes) {\n continue; // Skip this entry if transformed value would exceed byte limit\n }\n\n // Add to enriched attributes with prefix\n const enrichedKey = `${prefix}${key}`;\n enriched[enrichedKey] = transformedValue;\n\n keyCount++;\n byteCount += valueBytes;\n }\n\n return enriched;\n }\n\n /**\n * Check if a baggage key is allowed based on config\n */\n private isBaggageKeyAllowed(\n key: string,\n config: EnrichFromBaggageConfig,\n ): boolean {\n // Check deny list first (takes precedence)\n if (config.deny) {\n for (const pattern of config.deny) {\n if (this.matchesBaggagePattern(key, pattern)) {\n return false;\n }\n }\n }\n\n // Check allow list\n for (const pattern of config.allow) {\n if (this.matchesBaggagePattern(key, pattern)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Check if a key matches a baggage pattern\n * Supports exact matches and wildcard patterns (e.g., 'tenant.*')\n */\n private matchesBaggagePattern(key: string, pattern: string): boolean {\n if (pattern.endsWith('.*')) {\n const prefix = pattern.slice(0, -2);\n return key.startsWith(prefix + '.');\n }\n return key === pattern;\n }\n\n /**\n * Track a business event\n *\n * Use this for tracking user actions, business events, product usage:\n * - \"user.signup\"\n * - \"order.completed\"\n * - \"feature.used\"\n *\n * Events are sent to configured subscribers (PostHog, Mixpanel, etc.).\n *\n * @example\n * ```typescript\n * // Track user signup\n * events.trackEvent('user.signup', {\n * userId: '123',\n * plan: 'pro'\n * })\n *\n * // Track order\n * events.trackEvent('order.completed', {\n * orderId: 'ord_123',\n * amount: 99.99\n * })\n * ```\n */\n trackEvent(eventName: string, attributes?: EventAttributes): void {\n // Validate and sanitize input (with custom config if provided)\n const validationConfig = getValidationConfig();\n const validated = validateEvent(\n eventName,\n attributes,\n validationConfig || undefined,\n );\n\n // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext(\n validated.attributes,\n );\n\n this.logger?.info(\n {\n event: validated.eventName,\n attributes: enrichedAttributes,\n },\n 'Event tracked',\n );\n\n // Record for testing\n this.collector?.recordEvent({\n event: validated.eventName,\n attributes: enrichedAttributes,\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers (zero overhead if no subscribers)\n // Run in background - don't block event recording\n if (this.hasSubscribers) {\n // Build autotel context for trace correlation\n const autotelContext = this.buildAutotelContext();\n\n // Enrich from baggage if configured\n const finalAttributes = this.enrichFromBaggage(enrichedAttributes);\n\n void this.notifySubscribers((subscriber) =>\n subscriber.trackEvent(validated.eventName, finalAttributes, {\n autotel: autotelContext,\n }),\n );\n }\n }\n\n /**\n * Notify all subscribers concurrently without blocking\n * Uses circuit breakers to protect against failing subscribers\n * Uses Promise.allSettled to prevent subscriber errors from affecting other subscribers\n */\n private async notifySubscribers(\n fn: (subscriber: EventSubscriber) => Promise<void>,\n ): Promise<void> {\n const promises = this.subscribers.map(async (subscriber) => {\n const circuitBreaker = this.circuitBreakers.get(subscriber);\n if (!circuitBreaker) return; // Should never happen\n\n try {\n // Execute with circuit breaker protection\n await circuitBreaker.execute(() => fn(subscriber));\n } catch (error) {\n // Handle circuit open errors (expected behavior when subscriber is down)\n if (error instanceof CircuitOpenError) {\n // Circuit is open - subscriber is down, log at warn level for visibility (same behavior in all environments)\n getLogger().warn(\n {\n subscriberName: subscriber.name || 'Unknown',\n },\n `[Events] ${error.message}`,\n );\n return;\n }\n\n // Log other subscriber errors but don't throw - event failures shouldn't break business logic\n getLogger().error(\n {\n err: error instanceof Error ? error : undefined,\n subscriberName: subscriber.name || 'Unknown',\n },\n `[Events] Subscriber ${subscriber.name || 'Unknown'} failed`,\n );\n }\n });\n\n // Wait for all subscribers (success or failure)\n await Promise.allSettled(promises);\n }\n\n /**\n * Track conversion funnel steps\n *\n * Monitor where users drop off in multi-step processes.\n *\n * @example\n * ```typescript\n * // Track signup funnel\n * events.trackFunnelStep('signup', 'started', { userId: '123' })\n * events.trackFunnelStep('signup', 'email_verified', { userId: '123' })\n * events.trackFunnelStep('signup', 'completed', { userId: '123' })\n *\n * // Track checkout flow\n * events.trackFunnelStep('checkout', 'started', { cartValue: 99.99 })\n * events.trackFunnelStep('checkout', 'payment_info', { cartValue: 99.99 })\n * events.trackFunnelStep('checkout', 'completed', { cartValue: 99.99 })\n * ```\n */\n trackFunnelStep(\n funnelName: string,\n status: FunnelStatus,\n attributes?: EventAttributes,\n ): void {\n // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext(attributes);\n\n this.logger?.info(\n {\n funnel: funnelName,\n status,\n attributes: enrichedAttributes,\n },\n 'Funnel step tracked',\n );\n\n // Record for testing\n this.collector?.recordFunnelStep({\n funnel: funnelName,\n status,\n attributes: enrichedAttributes,\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers\n if (this.hasSubscribers) {\n const autotelContext = this.buildAutotelContext();\n const finalAttributes = this.enrichFromBaggage(enrichedAttributes);\n\n void this.notifySubscribers((subscriber) =>\n subscriber.trackFunnelStep(funnelName, status, finalAttributes, {\n autotel: autotelContext,\n }),\n );\n }\n }\n\n /**\n * Track outcomes (success/failure/partial)\n *\n * Monitor success rates of critical operations.\n *\n * @example\n * ```typescript\n * // Track email delivery\n * events.trackOutcome('email.delivery', 'success', {\n * recipientType: 'user',\n * emailType: 'welcome'\n * })\n *\n * events.trackOutcome('email.delivery', 'failure', {\n * recipientType: 'user',\n * errorCode: 'invalid_email'\n * })\n *\n * // Track payment processing\n * events.trackOutcome('payment.process', 'success', { amount: 99.99 })\n * events.trackOutcome('payment.process', 'failure', { error: 'insufficient_funds' })\n * ```\n */\n trackOutcome(\n operationName: string,\n status: OutcomeStatus,\n attributes?: EventAttributes,\n ): void {\n // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext(attributes);\n\n this.logger?.info(\n {\n operation: operationName,\n status,\n attributes: enrichedAttributes,\n },\n 'Outcome tracked',\n );\n\n // Record for testing\n this.collector?.recordOutcome({\n operation: operationName,\n status,\n attributes: enrichedAttributes,\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers\n if (this.hasSubscribers) {\n const autotelContext = this.buildAutotelContext();\n const finalAttributes = this.enrichFromBaggage(enrichedAttributes);\n\n void this.notifySubscribers((subscriber) =>\n subscriber.trackOutcome(operationName, status, finalAttributes, {\n autotel: autotelContext,\n }),\n );\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 * events.trackValue('order.revenue', 149.99, {\n * currency: 'USD',\n * productCategory: 'electronics'\n * })\n *\n * // Track items per cart\n * events.trackValue('cart.item_count', 5, {\n * userId: '123'\n * })\n *\n * // Track processing time\n * events.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 // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext({\n metric: metricName,\n ...attributes,\n });\n\n this.logger?.debug(\n {\n metric: metricName,\n value,\n attributes: enrichedAttributes,\n },\n 'Value tracked',\n );\n\n // Record for testing\n this.collector?.recordValue({\n metric: metricName,\n value,\n attributes: enrichedAttributes,\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers\n if (this.hasSubscribers) {\n const autotelContext = this.buildAutotelContext();\n const finalAttributes = this.enrichFromBaggage(enrichedAttributes);\n\n void this.notifySubscribers((subscriber) =>\n subscriber.trackValue(metricName, value, finalAttributes, {\n autotel: autotelContext,\n }),\n );\n }\n }\n\n /**\n * Flush all subscribers and wait for pending events\n *\n * Call this before shutdown to ensure all events are delivered.\n *\n * @example\n * ```typescript\n * const event =new Event('app', { subscribers: [...] });\n *\n * // Before shutdown\n * await events.flush();\n * ```\n */\n async flush(): Promise<void> {\n if (!this.hasSubscribers) return;\n\n const shutdownPromises = this.subscribers.map(async (subscriber) => {\n if (subscriber.shutdown) {\n try {\n await subscriber.shutdown();\n } catch (error) {\n getLogger().error(\n {\n err: error instanceof Error ? error : undefined,\n subscriberName: subscriber.name || 'Unknown',\n },\n `[Events] Failed to shutdown subscriber ${subscriber.name || 'Unknown'}`,\n );\n }\n }\n });\n\n await Promise.allSettled(shutdownPromises);\n }\n\n /**\n * Shutdown the Event instance and all subscribers\n *\n * Unlike `flush()`, this method:\n * - Shuts down all subscribers\n * - Prevents further event tracking (hasSubscribers becomes false)\n * - Should only be called once at application shutdown\n *\n * @example\n * ```typescript\n * // In Next.js API route with after()\n * import { after } from 'next/server';\n *\n * export async function POST(req: Request) {\n * const event = new Event('checkout', { subscribers: [...] });\n * event.trackEvent('order.completed', { orderId: '123' });\n *\n * after(async () => {\n * await event.shutdown();\n * });\n *\n * return Response.json({ success: true });\n * }\n * ```\n */\n async shutdown(): Promise<void> {\n if (!this.hasSubscribers) return;\n\n await Promise.allSettled(\n this.subscribers.map(async (subscriber) => {\n if (subscriber.shutdown) {\n try {\n await subscriber.shutdown();\n } catch (error) {\n getLogger().error(\n {\n err: error instanceof Error ? error : undefined,\n subscriberName: subscriber.name || 'Unknown',\n },\n `[Events] Failed to shutdown subscriber ${subscriber.name || 'Unknown'}`,\n );\n }\n }\n }),\n );\n\n // Prevent further tracking after shutdown\n this.hasSubscribers = false;\n }\n\n /**\n * Track funnel progression with custom step names\n *\n * Unlike trackFunnelStep which uses FunnelStatus enum values,\n * this method allows any string as the step name for flexible funnel tracking.\n *\n * @param funnelName - Name of the funnel (e.g., \"checkout\", \"onboarding\")\n * @param stepName - Custom step name (e.g., \"cart_viewed\", \"payment_entered\")\n * @param stepNumber - Optional numeric position in the funnel\n * @param attributes - Optional event attributes\n *\n * @example\n * ```typescript\n * // Track custom checkout steps\n * event.trackFunnelProgression('checkout', 'cart_viewed', 1);\n * event.trackFunnelProgression('checkout', 'shipping_selected', 2);\n * event.trackFunnelProgression('checkout', 'payment_entered', 3);\n * event.trackFunnelProgression('checkout', 'order_confirmed', 4);\n * ```\n */\n trackFunnelProgression(\n funnelName: string,\n stepName: string,\n stepNumber?: number,\n attributes?: EventAttributes,\n ): void {\n // Auto-attach all available telemetry context\n const enrichedAttributes = this.enrichWithTelemetryContext(attributes);\n\n this.logger?.info(\n {\n funnel: funnelName,\n stepName,\n stepNumber,\n attributes: enrichedAttributes,\n },\n 'Funnel progression tracked',\n );\n\n // Record for testing (as funnel step with custom name)\n this.collector?.recordFunnelStep({\n funnel: funnelName,\n status: stepName as FunnelStatus, // Cast for testing collector\n attributes: {\n ...enrichedAttributes,\n step_name: stepName,\n ...(stepNumber === undefined ? {} : { step_number: stepNumber }),\n },\n service: this.serviceName,\n timestamp: Date.now(),\n });\n\n // Notify subscribers that support trackFunnelProgression\n if (this.hasSubscribers) {\n const autotelContext = this.buildAutotelContext();\n const finalAttributes = this.enrichFromBaggage(enrichedAttributes);\n\n void this.notifySubscribers(async (subscriber) => {\n await (subscriber.trackFunnelProgression\n ? subscriber.trackFunnelProgression(\n funnelName,\n stepName,\n stepNumber,\n finalAttributes,\n { autotel: autotelContext },\n )\n : // Fall back to trackFunnelStep with step as custom name (cast)\n subscriber.trackFunnelStep(\n funnelName,\n stepName as FunnelStatus,\n {\n ...finalAttributes,\n step_name: stepName,\n ...(stepNumber === undefined\n ? {}\n : { step_number: stepNumber }),\n },\n { autotel: autotelContext },\n ));\n });\n }\n }\n\n /**\n * Track multiple events in a batch\n *\n * Useful for bulk event tracking with consistent timestamps.\n * Events are sent to subscribers individually but processed together.\n *\n * @param events - Array of events to track\n *\n * @example\n * ```typescript\n * event.trackBatch([\n * { name: 'item.viewed', attributes: { itemId: '1' } },\n * { name: 'item.viewed', attributes: { itemId: '2' } },\n * { name: 'cart.updated', attributes: { itemCount: 2 } },\n * ]);\n * ```\n */\n trackBatch(\n events: Array<{ name: string; attributes?: EventAttributesInput }>,\n ): void {\n // Filter attributes and track each event\n for (const event of events) {\n // Filter undefined/null values from attributes\n const filteredAttributes = event.attributes\n ? (Object.fromEntries(\n Object.entries(event.attributes).filter(\n ([, v]) => v !== undefined && v !== null,\n ),\n ) as EventAttributes)\n : undefined;\n\n this.trackEvent(event.name, filteredAttributes);\n }\n }\n}\n\n/**\n * Global events instances (singleton pattern)\n */\nconst eventsInstances = new Map<string, Event>();\n\n/**\n * Get or create an Events instance for a service\n *\n * @param serviceName - Service name for identifying events\n * @param logger - Optional logger\n * @returns Events instance\n *\n * @example\n * ```typescript\n * const event =getEvents('job-application')\n * events.trackEvent('application.submitted', { jobId: '123' })\n * ```\n */\nexport function getEvents(serviceName: string, logger?: Logger): Event {\n if (!eventsInstances.has(serviceName)) {\n eventsInstances.set(serviceName, new Event(serviceName, { logger }));\n }\n return eventsInstances.get(serviceName)!;\n}\n\n/**\n * Reset all events instances (mainly for testing)\n */\nexport function resetEvents(): void {\n eventsInstances.clear();\n}\n","/**\n * Graceful shutdown with flush and cleanup\n */\n\nimport { getSdk, getLogger, _closeEmbeddedDevtools } from './init';\nimport { getEventQueue, resetEventQueue } from './track';\nimport { resetEvents } from './event';\nimport { resetMetrics } from './metric';\nimport { getForceFlushableProvider } from './tracer-provider';\nimport { uninstallProcessHandlers } from './process-handlers';\n\n/**\n * Error codes that mean \"the OTLP endpoint wasn't reachable\" — expected and\n * harmless when no collector is configured. Deliberately limited to\n * connection-establishment failures (refused / DNS), not post-connection\n * errors like ECONNRESET or ETIMEDOUT, which can indicate a real problem\n * talking to a configured backend and should surface.\n */\nconst UNREACHABLE_ENDPOINT_CODES = new Set([\n 'ECONNREFUSED',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n]);\n\nfunction errorCode(error: unknown): string | undefined {\n if (typeof error === 'object' && error !== null && 'code' in error) {\n const code = (error as { code?: unknown }).code;\n if (typeof code === 'string') return code;\n }\n return undefined;\n}\n\n/**\n * True when the error (or every error it wraps) is an unreachable-endpoint\n * failure. Traverses `AggregateError.errors` and the `cause` chain, since the\n * SDK often wraps the underlying network error.\n */\nfunction isUnreachableEndpointError(error: unknown, depth = 0): boolean {\n if (depth > 5 || error === null || typeof error !== 'object') return false;\n\n if (error instanceof AggregateError) {\n return (\n error.errors.length > 0 &&\n error.errors.every((e) => isUnreachableEndpointError(e, depth + 1))\n );\n }\n\n const code = errorCode(error);\n if (code && UNREACHABLE_ENDPOINT_CODES.has(code)) return true;\n\n const cause = (error as { cause?: unknown }).cause;\n return cause !== undefined && cause !== error\n ? isUnreachableEndpointError(cause, depth + 1)\n : false;\n}\n\n/**\n * Flush all pending telemetry\n *\n * Flushes both events events and OpenTelemetry spans to their destinations.\n * Includes timeout protection to prevent hanging in serverless environments.\n *\n * Safe to call multiple times.\n *\n * @param options - Optional configuration\n * @param options.timeout - Timeout in milliseconds (default: 2000ms)\n * @param options.forShutdown - If true, permanently disables the events queue after flush (used internally by shutdown())\n *\n * @example Manual flush in serverless\n * ```typescript\n * import { flush } from 'autotel';\n *\n * export const handler = async (event) => {\n * // ... process event\n * await flush(); // Flush before function returns\n * return result;\n * };\n * ```\n *\n * @example With custom timeout\n * ```typescript\n * await flush({ timeout: 5000 }); // 5 second timeout\n * ```\n */\nexport async function flush(options?: {\n timeout?: number;\n forShutdown?: boolean;\n}): Promise<void> {\n const timeout = options?.timeout ?? 2000;\n const forShutdown = options?.forShutdown ?? false;\n\n const doFlush = async () => {\n // Flush events queue (or shutdown queue when tearing down)\n const eventsQueue = getEventQueue();\n if (eventsQueue) {\n await (forShutdown ? eventsQueue.shutdown() : eventsQueue.flush());\n }\n\n // Flush OpenTelemetry spans\n // This ensures spans are exported immediately, critical for serverless.\n // NodeSDK.getTracerProvider() returns undefined on sdk-node 0.220+, so\n // resolve a force-flushable provider from the global registry too.\n try {\n const tracerProvider = getForceFlushableProvider(getSdk());\n if (tracerProvider) {\n await tracerProvider.forceFlush();\n }\n } catch {\n // Ignore errors when accessing tracer provider (may not be available in test mocks)\n }\n };\n\n // Add timeout protection to prevent hanging\n let timeoutHandle: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n doFlush().finally(() => {\n // Clear timeout as soon as flush completes\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }),\n new Promise<void>((_, reject) => {\n timeoutHandle = setTimeout(\n () => reject(new Error('Flush timeout')),\n timeout,\n );\n // Use unref() to allow Node to exit if flush completes first\n // This prevents the 2s delay in serverless when flush succeeds immediately\n timeoutHandle.unref();\n }),\n ]);\n } catch (error) {\n // Clear timeout on error too\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n const logger = getLogger();\n logger.error(\n {\n err: error instanceof Error ? error : new Error(String(error)),\n },\n '[autotel] Flush error',\n );\n throw error;\n }\n}\n\n/**\n * Shutdown telemetry and cleanup resources\n *\n * - Flushes all pending data\n * - Shuts down OpenTelemetry SDK\n * - Cleans up resources\n *\n * Call this before process exit.\n *\n * Always performs cleanup even if flush fails, preventing resource leaks\n * in serverless handlers or tests.\n *\n * @example Express server\n * ```typescript\n * const server = app.listen(3000)\n *\n * process.on('SIGTERM', async () => {\n * await server.close()\n * await shutdown()\n * process.exit(0)\n * })\n * ```\n */\nexport async function shutdown(): Promise<void> {\n const logger = getLogger();\n let shutdownError: Error | null = null;\n\n // Attempt to flush (with queue shutdown so new events are rejected), but continue with cleanup even if it fails\n try {\n await flush({ forShutdown: true });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n shutdownError = err;\n logger.error(\n {\n err,\n },\n '[autotel] Flush failed during shutdown, continuing cleanup',\n );\n }\n\n // Always shutdown SDK and clean up resources\n try {\n // Shutdown OpenTelemetry SDK\n const sdk = getSdk();\n if (sdk) {\n await sdk.shutdown();\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Ignore unreachable-endpoint errors - this happens when no OTLP endpoint\n // was configured. The SDK tries to flush exporters that can't connect,\n // which is harmless. Checks the cause chain / AggregateError too, since the\n // SDK usually wraps the underlying network error.\n if (!isUnreachableEndpointError(error)) {\n // Only store/log real shutdown errors\n if (!shutdownError) {\n shutdownError = err;\n }\n logger.error({ err }, '[autotel] SDK shutdown failed');\n }\n } finally {\n uninstallProcessHandlers();\n await _closeEmbeddedDevtools();\n\n // Clean up singleton Maps and queues to prevent memory leaks\n // This runs even if SDK shutdown fails\n const eventsQueue = getEventQueue();\n if (eventsQueue && typeof eventsQueue.cleanup === 'function') {\n eventsQueue.cleanup();\n }\n resetEvents();\n resetMetrics();\n resetEventQueue();\n }\n\n // Rethrow first error after cleanup completes\n // This allows tests and CI to detect failures while still ensuring cleanup\n if (shutdownError) {\n throw shutdownError;\n }\n}\n","/**\n * Simplified initialization for autotel\n *\n * Single init() function with sensible defaults.\n * Replaces initInstrumentation() and separate events config.\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport type { NodeSDKConfiguration } from '@opentelemetry/sdk-node';\nimport {\n BatchSpanProcessor,\n type SpanProcessor,\n SimpleSpanProcessor,\n ConsoleSpanExporter,\n SamplingDecision,\n type SpanExporter,\n type Sampler as OtelSampler,\n type SamplingResult,\n} from '@opentelemetry/sdk-trace-base';\nimport {\n resourceFromAttributes,\n type Resource,\n} from '@opentelemetry/resources';\nimport {\n ATTR_SERVICE_NAME,\n ATTR_SERVICE_VERSION,\n} from '@opentelemetry/semantic-conventions';\nimport type { Sampler, SamplingPreset } from './sampling';\nimport {\n samplingPresets,\n resolveSamplingPreset,\n AUTOTEL_SAMPLING_RATE,\n} from './sampling';\nimport type { EventSubscriber } from './event-subscriber';\nimport type { Logger } from './logger';\nimport type { Attributes, Context, SpanKind, Link } from '@opentelemetry/api';\nimport type { ValidationConfig } from './validation';\nimport {\n PeriodicExportingMetricReader,\n type MetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport { OTLPMetricExporter as OTLPMetricExporterHTTP } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { OTLPTraceExporter as OTLPTraceExporterHTTP } from '@opentelemetry/exporter-trace-otlp-http';\nimport { OTLPLogExporter as OTLPLogExporterHTTP } from '@opentelemetry/exporter-logs-otlp-http';\nimport type { PushMetricExporter } from '@opentelemetry/sdk-metrics';\nimport {\n BatchLogRecordProcessor,\n type LogRecordExporter,\n type LogRecordProcessor,\n} from '@opentelemetry/sdk-logs';\nimport {\n buildPostHogLogProcessors,\n RedactingLogRecordProcessor,\n} from './posthog-logs';\nimport { TailSamplingSpanProcessor } from './tail-sampling-processor';\nimport { BaggageSpanProcessor } from './baggage-span-processor';\nimport {\n FilteringSpanProcessor,\n type SpanFilterPredicate,\n} from './filtering-span-processor';\nimport {\n SpanNameNormalizingProcessor,\n type SpanNameNormalizerConfig,\n} from './span-name-normalizer';\nimport {\n AttributeRedactingProcessor,\n normalizeAttributeRedactorConfig,\n type AttributeRedactorConfig,\n type AttributeRedactorPreset,\n} from './attribute-redacting-processor';\nimport { createStringRedactor, type StringRedactor } from './redact-values';\nimport { PrettyConsoleExporter } from './pretty-console-exporter';\nimport { resolveConfigFromEnv } from './env-config';\nimport { loadYamlConfig } from './yaml-config';\nimport { requireModule, safeRequire } from './node-require';\nimport {\n CanonicalLogLineProcessor,\n type CanonicalLogLineOptions,\n} from './processors/canonical-log-line-processor';\nimport type { EventsConfig } from './events-config';\nimport { resolveDevtoolsConfig, type AutotelDevtoolsConfig } from './devtools';\nimport {\n installProcessHandlers,\n uninstallProcessHandlers,\n type ProcessHandlersConfig,\n} from './process-handlers';\nimport { shutdown } from './shutdown';\n\n/**\n * Silent logger (no-op) - used as default when user doesn't provide one.\n * Internal autotel logs are silent by default to avoid spam.\n * Users can import { autotelLogger } from 'autotel/logger' to create their own.\n */\nconst silentLogger: Logger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n};\n\n/**\n * Adapts an Autotel Sampler to the OTel SDK Sampler interface.\n */\nfunction toOtelSampler(sampler: Sampler): OtelSampler {\n return {\n shouldSample(\n _context: Context,\n _traceId: string,\n spanName: string,\n _spanKind: SpanKind,\n _attributes: Attributes,\n links: Link[],\n ): SamplingResult {\n const samplingContext = {\n operationName: spanName,\n args: [],\n links,\n };\n const shouldTrace = sampler.shouldSample(samplingContext);\n const rate = sampler.sampleRate?.(samplingContext);\n return {\n decision: shouldTrace\n ? SamplingDecision.RECORD_AND_SAMPLED\n : SamplingDecision.NOT_RECORD,\n // Let a query reweight counts back to the true population.\n ...(shouldTrace && rate !== undefined && rate > 1\n ? { attributes: { [AUTOTEL_SAMPLING_RATE]: rate } }\n : {}),\n };\n },\n toString(): string {\n return `AutotelSamplerAdapter`;\n },\n };\n}\n\n// Type imports for exporters\ntype OTLPExporterConfig = {\n url?: string;\n headers?: Record<string, string>;\n timeoutMillis?: number;\n concurrencyLimit?: number;\n};\n\nexport type OtlpSignal = 'traces' | 'metrics' | 'logs';\n\nexport interface OtlpDestinationConfig {\n /**\n * Base OTLP endpoint for this destination.\n * HTTP destinations may omit `/v1/{signal}`; autotel appends it automatically.\n * gRPC destinations should point at the collector host:port.\n */\n endpoint: string;\n\n /**\n * Headers for this destination. Falls back to top-level `headers`.\n */\n headers?: Record<string, string> | string;\n\n /**\n * Protocol for this destination. Falls back to top-level `protocol`.\n */\n protocol?: 'http' | 'grpc';\n\n /**\n * Signals to send to this destination.\n * Defaults to all signals supported by the current init() config.\n */\n signals?: OtlpSignal[];\n}\n\n// Lazy-load gRPC exporters (optional peer dependencies)\nlet OTLPTraceExporterGRPC:\n (new (config: OTLPExporterConfig) => SpanExporter) | undefined;\nlet OTLPMetricExporterGRPC:\n (new (config: OTLPExporterConfig) => PushMetricExporter) | undefined;\nlet OTLPLogExporterGRPC:\n (new (config: OTLPExporterConfig) => LogRecordExporter) | undefined;\n\n/**\n * Helper: Lazy-load gRPC trace exporter\n */\nfunction loadGRPCTraceExporter(): new (\n config: OTLPExporterConfig,\n) => SpanExporter {\n if (OTLPTraceExporterGRPC) return OTLPTraceExporterGRPC;\n\n try {\n // Dynamic import for optional peer dependency\n const grpcModule = requireModule<{\n OTLPTraceExporter: new (config: OTLPExporterConfig) => SpanExporter;\n }>('@opentelemetry/exporter-trace-otlp-grpc');\n OTLPTraceExporterGRPC = grpcModule.OTLPTraceExporter;\n return OTLPTraceExporterGRPC;\n } catch {\n throw new Error(\n 'gRPC trace exporter not found. Install @opentelemetry/exporter-trace-otlp-grpc',\n );\n }\n}\n\n/**\n * Helper: Lazy-load gRPC metric exporter\n */\nfunction loadGRPCMetricExporter(): new (\n config: OTLPExporterConfig,\n) => PushMetricExporter {\n if (OTLPMetricExporterGRPC) return OTLPMetricExporterGRPC;\n\n try {\n // Dynamic import for optional peer dependency\n const grpcModule = requireModule<{\n OTLPMetricExporter: new (\n config: OTLPExporterConfig,\n ) => PushMetricExporter;\n }>('@opentelemetry/exporter-metrics-otlp-grpc');\n OTLPMetricExporterGRPC = grpcModule.OTLPMetricExporter;\n return OTLPMetricExporterGRPC;\n } catch {\n throw new Error(\n 'gRPC metric exporter not found. Install @opentelemetry/exporter-metrics-otlp-grpc',\n );\n }\n}\n\n/**\n * Helper: Create trace exporter based on protocol\n */\nfunction createTraceExporter(\n protocol: 'http' | 'grpc',\n config: OTLPExporterConfig,\n): SpanExporter {\n if (protocol === 'grpc') {\n const Exporter = loadGRPCTraceExporter();\n return new Exporter(config);\n }\n\n // Default: HTTP\n return new OTLPTraceExporterHTTP(config);\n}\n\n/**\n * Helper: Create metric exporter based on protocol\n */\nfunction createMetricExporter(\n protocol: 'http' | 'grpc',\n config: OTLPExporterConfig,\n): PushMetricExporter {\n if (protocol === 'grpc') {\n const Exporter = loadGRPCMetricExporter();\n return new Exporter(config);\n }\n\n // Default: HTTP\n return new OTLPMetricExporterHTTP(config);\n}\n\n/**\n * Helper: Lazy-load gRPC log exporter\n */\nfunction loadGRPCLogExporter(): new (\n config: OTLPExporterConfig,\n) => LogRecordExporter {\n if (OTLPLogExporterGRPC) return OTLPLogExporterGRPC;\n\n try {\n const grpcModule = requireModule<{\n OTLPLogExporter: new (config: OTLPExporterConfig) => LogRecordExporter;\n }>('@opentelemetry/exporter-logs-otlp-grpc');\n OTLPLogExporterGRPC = grpcModule.OTLPLogExporter;\n return OTLPLogExporterGRPC;\n } catch {\n throw new Error(\n 'gRPC log exporter not found. Install @opentelemetry/exporter-logs-otlp-grpc',\n );\n }\n}\n\n/**\n * Helper: Create log exporter based on protocol\n */\nfunction createLogExporter(\n protocol: 'http' | 'grpc',\n config: OTLPExporterConfig,\n): LogRecordExporter {\n if (protocol === 'grpc') {\n const Exporter = loadGRPCLogExporter();\n return new Exporter(config);\n }\n\n // Default: HTTP\n return new OTLPLogExporterHTTP(config);\n}\n\n/**\n * Helper: Resolve protocol from config and environment\n */\nfunction resolveProtocol(configProtocol?: 'http' | 'grpc'): 'http' | 'grpc' {\n // 1. Check config parameter (highest priority)\n if (configProtocol === 'grpc' || configProtocol === 'http') {\n return configProtocol;\n }\n\n // 2. Check OTEL_EXPORTER_OTLP_PROTOCOL env var\n const envProtocol = process.env.OTEL_EXPORTER_OTLP_PROTOCOL;\n if (envProtocol === 'grpc') return 'grpc';\n if (envProtocol === 'http/protobuf' || envProtocol === 'http') return 'http';\n\n // 3. Default to HTTP\n return 'http';\n}\n\n/**\n * Helper: Adjust endpoint URL for protocol\n * gRPC exporters don't need the /v1/traces or /v1/metrics path\n * HTTP exporters need the full path\n */\nfunction formatEndpointUrl(\n endpoint: string,\n signal: 'traces' | 'metrics' | 'logs',\n protocol: 'http' | 'grpc',\n): string {\n if (protocol === 'grpc') {\n // gRPC: strip any paths, return base endpoint\n return endpoint.replace(/\\/(v1\\/)?(traces|metrics|logs)$/, '');\n }\n\n // HTTP: append signal path if not present\n if (!endpoint.endsWith(`/v1/${signal}`)) {\n return `${endpoint}/v1/${signal}`;\n }\n\n return endpoint;\n}\n\n// Built-in logger is created dynamically in init() with service name\n\nexport interface AutotelConfig {\n /** Service name (required) */\n service: string;\n\n /**\n * Opt-in Node.js process handlers installed after telemetry is initialized.\n *\n * `true` (or `{}`) enables the defaults: flush-and-exit on SIGTERM/SIGINT\n * and on fatal errors, bounded by a 2s shutdown timeout. Pass a config\n * object to override individual defaults. Omit (or pass `false`) to leave\n * process lifecycle management to the application.\n */\n processHandlers?: boolean | ProcessHandlersConfig;\n\n /**\n * Local developer UX for autotel-devtools.\n *\n * - `true`: send traces, metrics, and logs to `http://127.0.0.1:4318`\n * - `{ embedded: true }`: attempt to start `autotel-devtools` automatically\n *\n * When enabled:\n * - `endpoint` defaults to the local devtools URL\n * - `logs` default to `true` unless explicitly set\n *\n * This keeps production config unchanged while making local debugging\n * effectively zero-config.\n */\n devtools?: boolean | AutotelDevtoolsConfig;\n\n /** Event subscribers - bring your own (PostHog, Mixpanel, etc.) */\n subscribers?: EventSubscriber[];\n\n /**\n * Additional OpenTelemetry instrumentations to register (raw OTel classes).\n * Useful when you need custom instrumentation configs or instrumentations\n * not covered by autoInstrumentations.\n *\n * **Important:** If you need custom instrumentation configs (like `requireParentSpan: false`),\n * use EITHER manual instrumentations OR autoInstrumentations, not both for the same library.\n * Manual instrumentations always take precedence over auto-instrumentations.\n *\n * @example Manual instrumentations with custom config\n * ```typescript\n * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'\n *\n * init({\n * service: 'my-app',\n * autoInstrumentations: false, // Disable auto-instrumentations\n * instrumentations: [\n * new MongoDBInstrumentation({\n * requireParentSpan: false // Custom config\n * })\n * ]\n * })\n * ```\n *\n * @example Mix auto + manual (auto for most, manual for specific configs)\n * ```typescript\n * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'\n *\n * init({\n * service: 'my-app',\n * autoInstrumentations: ['http', 'express'], // Auto for these\n * instrumentations: [\n * new MongoDBInstrumentation({\n * requireParentSpan: false // Manual config for MongoDB\n * })\n * ]\n * })\n * ```\n */\n instrumentations?: NodeSDKConfiguration['instrumentations'];\n\n /**\n * Simple names for auto-instrumentation.\n * Uses @opentelemetry/auto-instrumentations-node (peer dependency).\n *\n * **Important:** If you provide manual instrumentations for the same library,\n * the manual config takes precedence and auto-instrumentation for that library is disabled.\n *\n * @example Enable all auto-instrumentations (simple approach)\n * ```typescript\n * init({\n * service: 'my-app',\n * autoInstrumentations: true // Enable all with defaults\n * })\n * ```\n *\n * @example Enable specific auto-instrumentations\n * ```typescript\n * init({\n * service: 'my-app',\n * autoInstrumentations: ['express', 'pino', 'http']\n * })\n * ```\n *\n * @example Configure specific auto-instrumentations\n * ```typescript\n * init({\n * service: 'my-app',\n * autoInstrumentations: {\n * express: { enabled: true },\n * pino: { enabled: true },\n * http: { enabled: false }\n * }\n * })\n * ```\n *\n * @example Manual config when you need custom settings\n * ```typescript\n * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'\n *\n * init({\n * service: 'my-app',\n * autoInstrumentations: false, // Use manual control\n * instrumentations: [\n * new MongoDBInstrumentation({\n * requireParentSpan: false // Custom config not available with auto\n * })\n * ]\n * })\n * ```\n */\n autoInstrumentations?:\n string[] | boolean | Record<string, { enabled?: boolean }>;\n\n /**\n * OTLP endpoint for traces/metrics/logs\n * Single-destination shorthand. For multi-backend OTLP fan-out, use\n * `destinations` instead.\n * Only used if you don't provide custom exporters/processors\n * @default process.env.OTLP_ENDPOINT || 'http://localhost:4318'\n */\n endpoint?: string;\n\n /**\n * Declarative OTLP multi-destination config.\n * Each destination can override endpoint, headers, protocol, and signals.\n *\n * This is the high-level alternative to wiring `spanExporters`,\n * `spanProcessors`, `metricReaders`, and `logRecordProcessors` manually when\n * you want to fan telemetry out to multiple OTLP backends.\n *\n * When provided, `destinations` takes precedence over the single `endpoint`\n * shorthand for built-in OTLP exporters/readers/processors.\n *\n * @example Grafana + Honeycomb for traces, Grafana only for metrics/logs\n * ```typescript\n * init({\n * service: 'my-app',\n * logs: true,\n * destinations: [\n * {\n * endpoint: 'https://otlp-gateway-prod-eu-west-2.grafana.net/otlp',\n * headers: { Authorization: 'Basic ...' },\n * },\n * {\n * endpoint: 'https://api.honeycomb.io',\n * headers: { 'x-honeycomb-team': '...' },\n * signals: ['traces'],\n * },\n * ],\n * })\n * ```\n */\n destinations?: OtlpDestinationConfig[];\n\n /**\n * Custom span processors for traces (supports multiple processors)\n * Allows you to use any backend: Jaeger, Zipkin, Datadog, New Relic, etc.\n * If not provided, defaults to OTLP with tail sampling\n *\n * @example Multiple processors\n * ```typescript\n * import { JaegerExporter } from '@opentelemetry/exporter-jaeger'\n * import { BatchSpanProcessor, SimpleSpanProcessor, ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base'\n *\n * init({\n * service: 'my-app',\n * spanProcessors: [\n * new BatchSpanProcessor(new JaegerExporter()),\n * new SimpleSpanProcessor(new ConsoleSpanExporter()) // Debug alongside production\n * ]\n * })\n * ```\n *\n * @example Single processor\n * ```typescript\n * import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'\n *\n * init({\n * service: 'my-app',\n * spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())]\n * })\n * ```\n */\n spanProcessors?: SpanProcessor[];\n\n /**\n * Custom span processor for traces (single-item alias of spanProcessors)\n * @deprecated Use spanProcessors for consistency with other multi-value config fields\n */\n spanProcessor?: SpanProcessor;\n\n /**\n * Custom span exporters for traces (alternative to spanProcessors, supports multiple exporters)\n * Provide either spanProcessors OR spanExporters, not both\n * Each exporter will be wrapped in TailSamplingSpanProcessor + BatchSpanProcessor\n *\n * @example Multiple exporters\n * ```typescript\n * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'\n * import { JaegerExporter } from '@opentelemetry/exporter-jaeger'\n *\n * init({\n * service: 'my-app',\n * spanExporters: [\n * new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' }),\n * new JaegerExporter() // Send to multiple backends simultaneously\n * ]\n * })\n * ```\n *\n * @example Single exporter\n * ```typescript\n * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'\n *\n * init({\n * service: 'my-app',\n * spanExporters: [new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' })]\n * })\n * ```\n */\n spanExporters?: SpanExporter[];\n\n /**\n * Custom span exporter for traces (single-item alias of spanExporters)\n * @deprecated Use spanExporters for consistency with other multi-value config fields\n */\n spanExporter?: SpanExporter;\n\n /**\n * Custom metric readers (supports multiple readers)\n * Allows sending metrics to multiple backends: OTLP, Prometheus, custom readers\n * Defaults to OTLP metrics exporter when metrics are enabled.\n *\n * @example Multiple metric readers\n * ```typescript\n * import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'\n * import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'\n * import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'\n *\n * init({\n * service: 'my-app',\n * metricReaders: [\n * new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter() }),\n * new PrometheusExporter() // Export to multiple backends\n * ]\n * })\n * ```\n */\n metricReaders?: MetricReader[];\n\n /**\n * Custom metric reader (single-item alias of metricReaders)\n * @deprecated Use metricReaders for consistency with other multi-value config fields\n */\n metricReader?: MetricReader;\n\n /**\n * Custom log record processors. When omitted, logs are not configured.\n */\n logRecordProcessors?: LogRecordProcessor[];\n\n /**\n * Custom log record processor (single-item alias of logRecordProcessors)\n * @deprecated Use logRecordProcessors for consistency with other multi-value config fields\n */\n logRecordProcessor?: LogRecordProcessor;\n\n /**\n * PostHog integration - auto-configures OTLP log exporter.\n *\n * @example\n * ```typescript\n * init({\n * service: 'my-app',\n * posthog: { url: 'https://us.i.posthog.com/i/v1/logs?token=phc_xxx' }\n * });\n * ```\n *\n * Also reads from POSTHOG_LOGS_URL environment variable as fallback.\n */\n posthog?: { url: string };\n\n /** Additional resource attributes to merge with defaults. */\n resourceAttributes?: Attributes;\n\n /** Provide a fully custom Resource to merge (advanced use case). */\n resource?: Resource;\n\n /**\n * Headers for OTLP exporters. Accepts either an object map or\n * a \"key=value\" comma separated string.\n *\n * @example\n * ```typescript\n * init({\n * service: 'my-app',\n * endpoint: 'https://api.honeycomb.io',\n * headers: { 'x-honeycomb-team': 'YOUR_API_KEY' }\n * })\n * ```\n */\n headers?: Record<string, string> | string;\n\n /**\n * OTLP protocol to use for traces, metrics, and logs\n * - 'http': HTTP/protobuf (default, uses port 4318)\n * - 'grpc': gRPC (uses port 4317)\n *\n * Can be overridden with OTEL_EXPORTER_OTLP_PROTOCOL env var.\n *\n * Note: gRPC exporters are optional peer dependencies. Install them with:\n * ```bash\n * pnpm add @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-otlp-grpc\n * ```\n *\n * @example HTTP (default)\n * ```typescript\n * init({\n * service: 'my-app',\n * protocol: 'http', // or omit (defaults to http)\n * endpoint: 'http://localhost:4318'\n * })\n * ```\n *\n * @example gRPC\n * ```typescript\n * init({\n * service: 'my-app',\n * protocol: 'grpc',\n * endpoint: 'grpc://localhost:4317'\n * })\n * ```\n *\n * @default 'http'\n */\n protocol?: 'http' | 'grpc';\n\n /**\n * Optional factory to build a customised NodeSDK instance from our defaults.\n */\n sdkFactory?: (defaults: Partial<NodeSDKConfiguration>) => NodeSDK;\n\n /**\n * Infrastructure metrics configuration\n * - true: always enabled (default)\n * - false: always disabled\n * - 'auto': always enabled (same as true)\n *\n * Can be overridden with AUTOTEL_METRICS=on|off env var\n */\n metrics?: boolean | 'auto';\n\n /**\n * OTLP logs configuration\n * - true: auto-configure OTLP log exporter from endpoint\n * - false: disabled (default)\n * - 'auto': same as false (opt-in only)\n *\n * When enabled and an endpoint is configured, autotel will automatically\n * create a BatchLogRecordProcessor with an OTLPLogExporter - no manual\n * imports needed. Works alongside logRecordProcessors (additive).\n *\n * Requires @opentelemetry/sdk-logs and @opentelemetry/exporter-logs-otlp-http\n * (or -grpc) as peer dependencies.\n *\n * Can be overridden with AUTOTEL_LOGS=on|off env var.\n *\n * @example\n * ```typescript\n * init({\n * service: 'my-app',\n * endpoint: 'http://localhost:4318',\n * logs: true,\n * });\n * ```\n */\n logs?: boolean | 'auto';\n\n /** Sampling strategy - takes precedence over `sampling` preset */\n sampler?: Sampler;\n\n /**\n * Sampling preset shorthand — resolves to a pre-configured sampler.\n * If both `sampler` and `sampling` are provided, `sampler` takes precedence.\n *\n * @default 'production' (10% baseline + always-on for errors/slow)\n *\n * **Footgun for one-shot scripts:** the default samples ~90% of spans away,\n * which means a fixture-capture script that emits a single normal span\n * almost always sees zero output. For local debugging and capture use:\n *\n * ```typescript\n * init({\n * service: 'fixture-capture',\n * spanProcessors: [new SimpleSpanProcessor(new InMemorySpanExporter())],\n * sampling: 'development', // capture EVERY span\n * });\n * ```\n *\n * Read `exporter.getFinishedSpans()` **before** calling `shutdown()` —\n * `InMemorySpanExporter.shutdown()` resets state.\n */\n sampling?: SamplingPreset;\n\n /** Service version (default: auto-detect from package.json or '1.0.0') */\n version?: string;\n\n /** Environment (default: process.env.NODE_ENV || 'development') */\n environment?: string;\n\n /**\n * Logger instance for internal autotel diagnostic messages\n *\n * This logger is used by autotel internally to log initialization, warnings,\n * and debug information. Any logger with info/warn/error/debug methods works.\n *\n * **For OTel instrumentation of your application logs**, use the `autoInstrumentations` option:\n * - `autoInstrumentations: ['pino']` - Injects traceId/spanId into Pino logs\n * - `autoInstrumentations: ['winston']` - Injects traceId/spanId into Winston logs\n *\n * Default: silent logger (no-op)\n *\n * @example Pino with OTel instrumentation\n * ```typescript\n * import pino from 'pino'\n * import { init } from 'autotel'\n *\n * const logger = pino({ level: 'info' })\n * init({\n * service: 'my-app',\n * logger, // For autotel's internal logs\n * autoInstrumentations: ['pino'] // For OTel trace context in YOUR logs\n * })\n * ```\n *\n * @example Custom logger for autotel diagnostics\n * ```typescript\n * const logger = {\n * info: (msg, extra) => console.log(msg, extra),\n * warn: (msg, extra) => console.warn(msg, extra),\n * error: (msg, err, extra) => console.error(msg, err, extra),\n * debug: (msg, extra) => console.debug(msg, extra),\n * }\n * init({ service: 'my-app', logger })\n * ```\n */\n logger?: Logger;\n\n /**\n * Flush events queue when root spans end\n * - true: Flush on root span completion (default)\n * - false: Use batching (events flush every 10 seconds automatically)\n *\n * Only flushes on root spans to avoid excessive network calls.\n * Default is true for serverless/short-lived processes. Set to false\n * for long-running services where batching is more efficient.\n */\n flushOnRootSpanEnd?: boolean;\n\n /**\n * Force-flush OpenTelemetry spans on shutdown (default: false)\n *\n * When enabled, spans are force-flushed along with events on root\n * span completion. This is useful for serverless/short-lived processes where\n * spans may not export before the process ends.\n *\n * - true: Force-flush spans on root span completion (~50-200ms latency)\n * - false: Spans export via normal batch processor (default behavior)\n *\n * Only applies when flushOnRootSpanEnd is also enabled.\n *\n * Note: For edge runtimes (Cloudflare Workers, Vercel Edge), use the\n * 'autotel-edge' package instead, which handles this automatically.\n *\n * @example Serverless with force-flush\n * ```typescript\n * init({\n * service: 'my-lambda',\n * flushOnRootSpanEnd: true,\n * forceFlushOnShutdown: true, // Force-flush spans\n * });\n * ```\n */\n forceFlushOnShutdown?: boolean;\n\n /**\n * Automatically copy baggage entries to span attributes\n *\n * When enabled, all baggage entries are automatically added as span attributes,\n * making them visible in trace UIs (Jaeger, Grafana, DataDog, etc.) without\n * manually calling ctx.setAttribute() for each entry.\n *\n * - `true`: adds baggage with 'baggage.' prefix (e.g. baggage.tenant.id)\n * - `string`: uses custom prefix (e.g. 'ctx' → ctx.tenant.id, '' → tenant.id)\n * - `false` or omit: disabled (default)\n *\n * @default false\n *\n * @example Enable with default prefix\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: true\n * });\n *\n * // Now baggage automatically appears as span attributes\n * await withBaggage({\n * baggage: { 'tenant.id': 't1', 'user.id': 'u1' },\n * fn: async () => {\n * // Span has baggage.tenant.id and baggage.user.id attributes!\n * }\n * });\n * ```\n *\n * @example Custom prefix\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: 'ctx' // Uses 'ctx.' prefix\n * });\n * // Creates attributes: ctx.tenant.id, ctx.user.id\n * ```\n *\n * @example No prefix\n * ```typescript\n * init({\n * service: 'my-app',\n * baggage: '' // No prefix\n * });\n * // Creates attributes: tenant.id, user.id\n * ```\n */\n baggage?: boolean | string;\n\n /**\n * Validation configuration for events events\n * - Override default sensitive field patterns for redaction\n * - Customize max lengths, nesting depth, etc.\n *\n * @example Disable redaction for development\n * ```typescript\n * init({\n * service: 'my-app',\n * validation: {\n * sensitivePatterns: [] // Disable all redaction\n * }\n * })\n * ```\n *\n * @example Add custom patterns\n * ```typescript\n * init({\n * service: 'my-app',\n * validation: {\n * sensitivePatterns: [\n * /password/i,\n * /apiKey/i,\n * /customSecret/i // Your custom pattern\n * ]\n * }\n * })\n * ```\n */\n validation?: Partial<ValidationConfig>;\n\n /**\n * Events configuration for trace context, correlation IDs, and enrichment\n *\n * Controls how product events integrate with distributed tracing:\n * - `includeTraceContext`: Automatically include trace context in events\n * - `includeLinkedTraceIds`: Include full array of linked trace IDs (for batch/fan-in)\n * - `traceUrl`: Generate clickable trace URLs in events\n * - `enrichFromBaggage`: Auto-enrich events from baggage with guardrails\n *\n * @example Basic trace context\n * ```typescript\n * init({\n * service: 'my-app',\n * events: {\n * includeTraceContext: true\n * }\n * });\n * // Events now include autotel.trace_id, autotel.span_id, autotel.correlation_id\n * ```\n *\n * @example With clickable trace URLs\n * ```typescript\n * init({\n * service: 'my-app',\n * events: {\n * includeTraceContext: true,\n * traceUrl: (ctx) => `https://grafana.internal/explore?traceId=${ctx.traceId}`\n * }\n * });\n * ```\n *\n * @example With baggage enrichment\n * ```typescript\n * init({\n * service: 'my-app',\n * events: {\n * includeTraceContext: true,\n * enrichFromBaggage: {\n * allow: ['tenant.id', 'user.id'],\n * prefix: 'ctx.',\n * maxKeys: 10,\n * maxBytes: 1024\n * }\n * }\n * });\n * ```\n */\n events?: EventsConfig;\n\n /**\n * Debug mode for local span inspection.\n * Enables console output to help you see spans as they're created.\n *\n * - `true`: Raw JSON output (ConsoleSpanExporter)\n * - `'pretty'`: Colorized, hierarchical output (PrettyConsoleExporter)\n * - `false`/undefined: No console output (default)\n *\n * When enabled: Outputs spans to console AND sends to backend (if endpoint/exporter configured)\n *\n * Perfect for progressive development:\n * - Start with debug: 'pretty' (no endpoint) → see traces immediately with nice formatting\n * - Add endpoint later → console + backend, verify before choosing provider\n * - Remove debug in production → backend only, clean production config\n *\n * Can be overridden with AUTOTEL_DEBUG environment variable.\n *\n * @example Pretty debug output (recommended for development)\n * ```typescript\n * init({\n * service: 'my-app',\n * debug: 'pretty' // Colorized, hierarchical output\n * })\n * ```\n *\n * @example Raw JSON output (verbose)\n * ```typescript\n * init({\n * service: 'my-app',\n * debug: true // Raw ConsoleSpanExporter output\n * })\n * ```\n *\n * @example Environment variable\n * ```bash\n * AUTOTEL_DEBUG=pretty node server.js\n * AUTOTEL_DEBUG=true node server.js\n * ```\n */\n debug?: boolean | 'pretty';\n\n /**\n * Filter predicate to drop unwanted spans before processing.\n *\n * Useful for filtering out noisy spans from specific instrumentations\n * (e.g., Next.js internal spans, health check endpoints).\n *\n * The filter runs on completed spans (onEnd), so you have access to:\n * - `span.name` - Span name\n * - `span.attributes` - All span attributes\n * - `span.instrumentationScope` - `{ name, version }` of the instrumentation\n * - `span.status` - Span status code and message\n * - `span.duration` - Span duration as `[seconds, nanoseconds]`\n *\n * Return `true` to keep the span, `false` to drop it.\n *\n * @example Filter out Next.js instrumentation spans\n * ```typescript\n * init({\n * service: 'my-app',\n * spanFilter: (span) => span.instrumentationScope.name !== 'next.js'\n * })\n * ```\n *\n * @example Filter out health check spans\n * ```typescript\n * init({\n * service: 'my-app',\n * spanFilter: (span) => !span.name.includes('/health')\n * })\n * ```\n *\n * @example Complex filtering (multiple conditions)\n * ```typescript\n * init({\n * service: 'my-app',\n * spanFilter: (span) => {\n * // Drop Next.js internal spans\n * if (span.instrumentationScope.name === 'next.js') return false;\n * // Drop health checks\n * if (span.name.includes('/health')) return false;\n * // Drop very short spans (less than 1ms)\n * const [secs, nanos] = span.duration;\n * if (secs === 0 && nanos < 1_000_000) return false;\n * return true;\n * }\n * })\n * ```\n */\n spanFilter?: SpanFilterPredicate;\n\n /**\n * Normalize span names to reduce cardinality from dynamic path segments.\n *\n * High-cardinality span names (e.g., `/users/123/posts/456`) cause issues:\n * - Cost explosions in observability backends\n * - Cardinality limits exceeded\n * - Poor UX when searching/filtering traces\n *\n * The normalizer transforms dynamic segments into placeholders:\n * - `/users/123` → `/users/:id`\n * - `/items/550e8400-e29b-...` → `/items/:uuid`\n *\n * Provide either a custom function or use a built-in preset:\n * - `'rest-api'` - Numeric IDs, UUIDs, ObjectIds, dates, timestamps, emails\n * - `'graphql'` - GraphQL operation name normalization\n * - `'minimal'` - Only numeric IDs and UUIDs\n *\n * @example Custom normalizer function\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: (name) => {\n * return name\n * .replace(/\\/[0-9]+/g, '/:id')\n * .replace(/\\/[a-f0-9-]{36}/gi, '/:uuid');\n * }\n * })\n * ```\n *\n * @example Using built-in preset\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: 'rest-api'\n * })\n * ```\n *\n * @example Combining with spanFilter\n * ```typescript\n * init({\n * service: 'my-app',\n * spanNameNormalizer: 'rest-api',\n * spanFilter: (span) => span.instrumentationScope.name !== 'next.js'\n * })\n * ```\n */\n spanNameNormalizer?: SpanNameNormalizerConfig;\n\n /**\n * Automatically redact PII and sensitive data from span attributes before export.\n * Critical for compliance (GDPR, PCI-DSS, HIPAA) and data security.\n *\n * Auto-enabled in production: when this is left unset and the resolved\n * environment is `production`, the `'default'` preset is applied. Override\n * with the `AUTOTEL_REDACT_PII` env var (`off` / `strict` / `pci-dss` / ...)\n * or pass `false` to disable redaction entirely.\n *\n * Can be a preset name, custom configuration, or `false` to disable:\n * - `'default'`: Emails, phones, SSNs, credit cards, sensitive keys (password, secret, token)\n * - `'strict'`: Default + Bearer tokens, JWTs, API keys in values\n * - `'pci-dss'`: Payment card industry focus (credit cards, CVV, card-related keys)\n *\n * @example Use default preset\n * ```typescript\n * init({\n * service: 'my-app',\n * attributeRedactor: 'default'\n * })\n * ```\n *\n * @example Custom patterns\n * ```typescript\n * init({\n * service: 'my-app',\n * attributeRedactor: {\n * keyPatterns: [/password/i, /secret/i],\n * valuePatterns: [\n * { name: 'customerId', pattern: /CUST-\\d{8}/g, replacement: 'CUST-***' }\n * ]\n * }\n * })\n * ```\n *\n * @example Custom redactor function\n * ```typescript\n * init({\n * service: 'my-app',\n * attributeRedactor: {\n * redactor: (key, value) => {\n * if (key === 'user.email' && typeof value === 'string') {\n * return value.replace(/@.+/, '@[REDACTED]');\n * }\n * return value;\n * }\n * }\n * })\n * ```\n */\n attributeRedactor?: AttributeRedactorConfig | AttributeRedactorPreset | false;\n\n /**\n * OpenLLMetry integration for LLM observability.\n * Requires @traceloop/node-server-sdk as an optional peer dependency.\n *\n * @example Enable OpenLLMetry with default settings\n * ```typescript\n * init({\n * service: 'my-app',\n * openllmetry: { enabled: true }\n * })\n * ```\n *\n * @example Enable with custom options\n * ```typescript\n * init({\n * service: 'my-app',\n * openllmetry: {\n * enabled: true,\n * options: {\n * disableBatch: process.env.NODE_ENV !== 'production',\n * apiKey: process.env.TRACELOOP_API_KEY\n * }\n * }\n * })\n * ```\n */\n openllmetry?: {\n enabled: boolean;\n options?: Record<string, unknown>;\n };\n\n /**\n * Canonical log lines - automatically emit spans as wide events (canonical log lines)\n *\n * When enabled, each span (or root span only) is automatically emitted as a\n * comprehensive log record with ALL span attributes. This implements the\n * \"canonical log line\" pattern: one comprehensive event per request with all context.\n *\n * **Benefits:**\n * - One log line per request with all context (wide event)\n * - High-cardinality, high-dimensionality data for powerful queries\n * - Automatic - no manual logging needed\n * - Queryable as structured data instead of string search\n *\n * @example Basic usage (one canonical log line per request)\n * ```typescript\n * init({\n * service: 'checkout-api',\n * canonicalLogLines: {\n * enabled: true,\n * rootSpansOnly: true, // One canonical log line per request\n * },\n * });\n * ```\n *\n * @example With custom logger\n * ```typescript\n * import pino from 'pino';\n * const logger = pino();\n * init({\n * service: 'my-app',\n * logger,\n * canonicalLogLines: {\n * enabled: true,\n * logger, // Use Pino for canonical log lines\n * rootSpansOnly: true,\n * },\n * });\n * ```\n *\n * @example Custom message format\n * ```typescript\n * init({\n * service: 'my-app',\n * canonicalLogLines: {\n * enabled: true,\n * messageFormat: (span) => {\n * const status = span.status.code === 2 ? 'ERROR' : 'SUCCESS';\n * return `${span.name} [${status}]`;\n * },\n * },\n * });\n * ```\n */\n canonicalLogLines?: {\n enabled: boolean;\n /** Logger to use for emitting canonical log lines (defaults to OTel Logs API) */\n logger?: Logger;\n /** Only emit canonical log lines for root spans (default: false) */\n rootSpansOnly?: boolean;\n /** Minimum log level for canonical log lines (default: 'info') */\n minLevel?: 'debug' | 'info' | 'warn' | 'error';\n /** Custom message format (default: uses span name) */\n messageFormat?: (\n span: import('@opentelemetry/sdk-trace-base').ReadableSpan,\n ) => string;\n /** Whether to include resource attributes (default: true) */\n includeResourceAttributes?: boolean;\n /** Predicate to decide whether to emit (runs after event is built). */\n shouldEmit?: CanonicalLogLineOptions['shouldEmit'];\n /**\n * Declarative tail sampling conditions (OR logic).\n * Ignored when `shouldEmit` is provided.\n * @example keep: [{ status: 500 }, { durationMs: 1000 }]\n */\n keep?: CanonicalLogLineOptions['keep'];\n /** Callback invoked after emit for custom fan-out. */\n drain?: CanonicalLogLineOptions['drain'];\n /** Handler for drain failures. */\n onDrainError?: CanonicalLogLineOptions['onDrainError'];\n /**\n * Pretty-print canonical log lines to console.\n * Defaults to true when NODE_ENV is 'development'.\n */\n pretty?: boolean;\n };\n\n /**\n * Suppress console output while keeping OTel exporters running.\n * Useful for platforms like GCP Cloud Run / AWS Lambda where stdout\n * is managed externally by the platform's log collector.\n *\n * @default false\n */\n silent?: boolean;\n\n /**\n * Minimum log level for internal autotel diagnostic messages.\n * Messages below this level are dropped before processing.\n *\n * @default 'info'\n */\n minLevel?: 'debug' | 'info' | 'warn' | 'error';\n}\n\n// Internal state\nlet initialized = false;\nlet locked = false;\nlet config: AutotelConfig | null = null;\nlet sdk: NodeSDK | null = null;\nlet warnedOnce = false;\nlet logger: Logger = silentLogger; // Silent by default - no spam\nlet validationConfig: Partial<ValidationConfig> | null = null;\nlet eventsConfig: EventsConfig | null = null;\nlet _stringRedactor: StringRedactor | null = null;\n\n/** Subscribers may opt into value redaction by exposing this setter. */\ninterface StringRedactorAware {\n setStringRedactor: (redact: StringRedactor) => void;\n}\n\nfunction acceptsStringRedactor(\n subscriber: unknown,\n): subscriber is StringRedactorAware {\n return (\n typeof subscriber === 'object' &&\n subscriber !== null &&\n 'setStringRedactor' in subscriber &&\n typeof (subscriber as StringRedactorAware).setStringRedactor === 'function'\n );\n}\nlet _optionalRequire: typeof safeRequire = safeRequire;\nlet _devtoolsClose: (() => Promise<void> | void) | null = null;\n\nconst LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 } as const;\ntype LogLevelKey = keyof typeof LOG_LEVELS;\n\n/**\n * Lock the logger to prevent further `init()` calls.\n * Use this when framework plugins set up instrumentation and you want\n * to prevent accidental re-initialization from user code.\n */\nexport function lockLogger(): void {\n locked = true;\n}\n\n/**\n * Check if the logger has been locked.\n */\nexport function isLoggerLocked(): boolean {\n return locked;\n}\n\nfunction createSilentLogger(): Logger {\n return {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n };\n}\n\nfunction wrapLogger(\n base: Logger,\n silent: boolean,\n minLevel: LogLevelKey,\n): Logger {\n if (silent) return createSilentLogger();\n const threshold = LOG_LEVELS[minLevel];\n const wrap = (fn: Logger['info'], level: LogLevelKey): Logger['info'] => {\n if (LOG_LEVELS[level] < threshold) {\n return (() => {}) as Logger['info'];\n }\n return ((...args: Parameters<Logger['info']>) =>\n fn(...args)) as Logger['info'];\n };\n return {\n debug: wrap(base.debug, 'debug'),\n info: wrap(base.info, 'info'),\n warn: wrap(base.warn, 'warn'),\n error: wrap(base.error, 'error'),\n };\n}\n\n/**\n * Resolve the effective attribute redactor. Explicit config wins (`false`\n * disables). Otherwise the `AUTOTEL_REDACT_PII` env var controls it, and as a\n * final default PII redaction is auto-enabled in production.\n */\nexport function resolveAttributeRedactor(\n explicit:\n AttributeRedactorConfig | AttributeRedactorPreset | false | undefined,\n environment: string,\n): AttributeRedactorConfig | AttributeRedactorPreset | undefined {\n if (explicit === false) return undefined;\n if (explicit !== undefined) return explicit;\n\n const flag = process.env.AUTOTEL_REDACT_PII?.trim().toLowerCase();\n if (flag) {\n if (['off', 'false', '0', 'none', 'disabled'].includes(flag)) {\n return undefined;\n }\n if (flag === 'default' || flag === 'strict' || flag === 'pci-dss') {\n return flag;\n }\n if (['on', 'true', '1', 'enabled'].includes(flag)) {\n return 'default';\n }\n }\n\n return environment === 'production' ? 'default' : undefined;\n}\n\nfunction detectEnvironmentAttributes(): Record<string, string> {\n const attrs: Record<string, string> = {};\n\n const commitSha =\n process.env.COMMIT_SHA ||\n process.env.GITHUB_SHA ||\n process.env.VERCEL_GIT_COMMIT_SHA ||\n process.env.CF_PAGES_COMMIT_SHA ||\n process.env.AWS_CODEPIPELINE_EXECUTION_ID;\n if (commitSha) attrs['service.commit.sha'] = commitSha;\n\n const region =\n process.env.VERCEL_REGION ||\n process.env.AWS_REGION ||\n process.env.AWS_DEFAULT_REGION ||\n process.env.FLY_REGION ||\n process.env.CF_REGION ||\n process.env.GOOGLE_CLOUD_REGION;\n if (region) attrs['service.region'] = region;\n\n const version =\n process.env.APP_VERSION ||\n process.env.HEROKU_RELEASE_VERSION ||\n process.env.VERCEL_GIT_COMMIT_REF;\n if (version) attrs['service.deploy.version'] = version;\n\n return attrs;\n}\n\n/**\n * Resolve metrics flag with env var override support\n */\nexport function resolveMetricsFlag(\n configFlag: boolean | 'auto' = 'auto',\n): boolean {\n // 1. Check env var override (highest priority)\n const envFlag = process.env.AUTOTEL_METRICS;\n if (envFlag === 'on' || envFlag === 'true') return true;\n if (envFlag === 'off' || envFlag === 'false') return false;\n\n // 2. Check config flag\n if (configFlag === true) return true;\n if (configFlag === false) return false;\n\n // 3. Default: enabled in all environments (simpler)\n return true;\n}\n\n/**\n * Resolve logs flag with env var override support.\n * Defaults to disabled (opt-in only) to avoid unexpected log export\n * and to preserve the upstream SDK's OTEL_LOGS_EXPORTER handling.\n */\nexport function resolveLogsFlag(\n configFlag: boolean | 'auto' = 'auto',\n): boolean {\n // 1. Check env var override (highest priority)\n const envFlag = process.env.AUTOTEL_LOGS;\n if (envFlag === 'on' || envFlag === 'true') return true;\n if (envFlag === 'off' || envFlag === 'false') return false;\n\n // 2. Check config flag\n if (configFlag === true) return true;\n if (configFlag === false) return false;\n\n // 3. Default: disabled (opt-in only)\n return false;\n}\n\n/**\n * Resolve debug flag with env var override support\n *\n * Supports:\n * - `'pretty'`: Colorized, hierarchical output (PrettyConsoleExporter)\n * - `true` / `'true'` / `'1'`: Raw JSON output (ConsoleSpanExporter)\n * - `false` / `'false'` / `'0'`: Disabled\n */\nexport function resolveDebugFlag(\n configFlag?: boolean | 'pretty',\n): boolean | 'pretty' {\n // 1. Check env var override (highest priority)\n const envFlag = process.env.AUTOTEL_DEBUG;\n if (envFlag === 'pretty') return 'pretty';\n if (envFlag === 'true' || envFlag === '1') return true;\n if (envFlag === 'false' || envFlag === '0') return false;\n\n // 2. Return config flag (defaults to false)\n return configFlag ?? false;\n}\n\nfunction normalizeOtlpHeaders(\n headers?: Record<string, string> | string,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n if (typeof headers !== 'string') return headers;\n\n const parsed: Record<string, string> = {};\n for (const pair of headers.split(',')) {\n const [key, ...valueParts] = pair.split('=');\n if (!key || valueParts.length === 0) continue;\n parsed[key.trim()] = valueParts.join('=').trim();\n }\n return parsed;\n}\n\ntype ResolvedOtlpDestination = {\n endpoint: string;\n protocol: 'http' | 'grpc';\n headers?: Record<string, string>;\n signals?: Set<OtlpSignal>;\n};\n\nfunction resolveOtlpDestinations(\n config: AutotelConfig,\n fallbackEndpoint?: string,\n): ResolvedOtlpDestination[] {\n const rawDestinations =\n config.destinations === undefined\n ? fallbackEndpoint\n ? [\n {\n endpoint: fallbackEndpoint,\n headers: config.headers,\n protocol: config.protocol,\n },\n ]\n : []\n : config.destinations;\n\n return rawDestinations.map((destination) => ({\n endpoint: destination.endpoint,\n protocol: resolveProtocol(destination.protocol ?? config.protocol),\n headers: normalizeOtlpHeaders(destination.headers ?? config.headers),\n signals: destination.signals ? new Set(destination.signals) : undefined,\n }));\n}\n\nfunction destinationSupportsSignal(\n destination: ResolvedOtlpDestination,\n signal: OtlpSignal,\n): boolean {\n return destination.signals ? destination.signals.has(signal) : true;\n}\n\n/**\n * Initialize autotel - Write Once, Observe Everywhere\n *\n * Follows OpenTelemetry standards: opinionated defaults with full flexibility\n * Idempotent: multiple calls are safe, last one wins\n *\n * @example Minimal setup (OTLP default)\n * ```typescript\n * init({ service: 'my-app' })\n * ```\n *\n * @example With events (observe in PostHog, Mixpanel, etc.)\n * ```typescript\n * import { PostHogSubscriber } from 'autotel-subscribers/posthog';\n *\n * init({\n * service: 'my-app',\n * subscribers: [new PostHogSubscriber({ apiKey: '...' })]\n * })\n * ```\n *\n * @example Observe in Jaeger\n * ```typescript\n * import { JaegerExporter } from '@opentelemetry/exporter-jaeger'\n *\n * init({\n * service: 'my-app',\n * spanExporter: new JaegerExporter({ endpoint: 'http://localhost:14268/api/traces' })\n * })\n * ```\n *\n * @example Observe in Zipkin\n * ```typescript\n * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'\n *\n * init({\n * service: 'my-app',\n * spanExporter: new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' })\n * })\n * ```\n *\n * @example Observe in Datadog\n * ```typescript\n * import { DatadogSpanProcessor } from '@opentelemetry/exporter-datadog'\n *\n * init({\n * service: 'my-app',\n * spanProcessor: new DatadogSpanProcessor({ ... })\n * })\n * ```\n *\n * @example Console output (dev)\n * ```typescript\n * import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'\n *\n * init({\n * service: 'my-app',\n * spanProcessor: new SimpleSpanProcessor(new ConsoleSpanExporter())\n * })\n * ```\n */\n\nexport function init(cfg: AutotelConfig): void {\n if (locked) {\n return;\n }\n\n // Resolve configs in priority order: explicit > yaml > env > defaults\n const envConfig = resolveConfigFromEnv();\n const yamlConfig = loadYamlConfig() ?? {};\n\n // Merge configs: explicit config > yaml file > env vars > defaults\n const mergedConfig: AutotelConfig = {\n ...envConfig, // Environment variables (lowest priority)\n ...yamlConfig, // YAML file (middle priority)\n ...cfg, // Explicit config (highest priority)\n // Deep merge for resourceAttributes\n resourceAttributes: {\n ...envConfig.resourceAttributes,\n ...yamlConfig.resourceAttributes,\n ...detectEnvironmentAttributes(),\n ...cfg.resourceAttributes,\n },\n // Handle headers merge (can be string or object)\n headers: cfg.headers ?? yamlConfig.headers ?? envConfig.headers,\n } as AutotelConfig;\n\n const resolvedRedactor = resolveAttributeRedactor(\n mergedConfig.attributeRedactor,\n mergedConfig.environment || process.env.NODE_ENV || 'development',\n );\n if (resolvedRedactor === undefined) {\n mergedConfig.attributeRedactor = undefined;\n } else {\n const normalizedRedactor =\n normalizeAttributeRedactorConfig(resolvedRedactor);\n if (!normalizedRedactor) {\n throw new Error('Invalid attributeRedactor config');\n }\n mergedConfig.attributeRedactor = normalizedRedactor;\n }\n\n const devtoolsConfig = resolveDevtoolsConfig(mergedConfig.devtools);\n if (devtoolsConfig.enabled && mergedConfig.logs === undefined) {\n mergedConfig.logs = true;\n }\n\n const silent = mergedConfig.silent ?? false;\n const minLevel = mergedConfig.minLevel ?? 'info';\n const baseLogger = mergedConfig.logger || silentLogger;\n logger = wrapLogger(baseLogger, silent, minLevel);\n\n // Warn if re-initializing (same behavior in all environments)\n if (initialized) {\n logger.warn(\n {},\n '[autotel] init() called again - last config wins. This may cause unexpected behavior.',\n );\n }\n\n config = mergedConfig;\n validationConfig = mergedConfig.validation || null;\n eventsConfig = mergedConfig.events || null;\n\n // Initialize OpenTelemetry\n // Only use endpoint if explicitly configured (no default fallback)\n let endpoint = mergedConfig.endpoint ?? devtoolsConfig.endpoint;\n const version = mergedConfig.version || detectVersion();\n const environment =\n mergedConfig.environment || process.env.NODE_ENV || 'development';\n const metricsEnabled = resolveMetricsFlag(mergedConfig.metrics);\n const logsEnabled = resolveLogsFlag(mergedConfig.logs);\n\n if (devtoolsConfig.enabled && devtoolsConfig.embedded) {\n const devtoolsModule = _optionalRequire<{\n createDevtools?: (options?: {\n port?: number;\n host?: string;\n verbose?: boolean;\n }) => { port: number; close: () => Promise<void> | void };\n }>('autotel-devtools');\n\n if (devtoolsModule?.createDevtools) {\n const devtoolsInstance = devtoolsModule.createDevtools({\n port: devtoolsConfig.port,\n host: devtoolsConfig.host,\n verbose: devtoolsConfig.verbose,\n });\n _devtoolsClose = devtoolsInstance.close;\n endpoint = `http://${devtoolsConfig.host}:${devtoolsInstance.port}`;\n logger.info(\n {},\n `[autotel] autotel-devtools embedded server started at ${endpoint}`,\n );\n } else {\n logger.warn(\n {},\n '[autotel] devtools.embedded requested but autotel-devtools is not installed. Falling back to endpoint-only mode.',\n );\n }\n }\n\n // Detect hostname for proper Datadog correlation and Service Catalog discovery\n const hostname = detectHostname();\n\n let resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: mergedConfig.service,\n [ATTR_SERVICE_VERSION]: version,\n // Support both old and new OpenTelemetry semantic conventions for environment\n 'deployment.environment': environment, // Deprecated but widely supported\n 'deployment.environment.name': environment, // OTel v1.27.0+ standard\n });\n\n // Add hostname attributes for Datadog Service Catalog and infrastructure correlation\n if (hostname) {\n resource = resource.merge(\n resourceFromAttributes({\n 'host.name': hostname, // OpenTelemetry standard\n 'datadog.host.name': hostname, // Datadog-specific, highest priority for Datadog\n }),\n );\n }\n\n if (mergedConfig.resource) {\n resource = resource.merge(mergedConfig.resource);\n }\n\n if (mergedConfig.resourceAttributes) {\n resource = resource.merge(\n resourceFromAttributes(mergedConfig.resourceAttributes),\n );\n }\n\n const otlpDestinations = resolveOtlpDestinations(mergedConfig, endpoint);\n\n // Backward-compatible singular aliases. Plural forms take precedence when both are provided.\n const configuredSpanProcessors =\n mergedConfig.spanProcessors && mergedConfig.spanProcessors.length > 0\n ? mergedConfig.spanProcessors\n : mergedConfig.spanProcessor\n ? [mergedConfig.spanProcessor]\n : undefined;\n const configuredSpanExporters =\n mergedConfig.spanExporters && mergedConfig.spanExporters.length > 0\n ? mergedConfig.spanExporters\n : mergedConfig.spanExporter\n ? [mergedConfig.spanExporter]\n : undefined;\n const configuredMetricReaders =\n mergedConfig.metricReaders && mergedConfig.metricReaders.length > 0\n ? mergedConfig.metricReaders\n : mergedConfig.metricReader\n ? [mergedConfig.metricReader]\n : undefined;\n const configuredLogRecordProcessors =\n mergedConfig.logRecordProcessors &&\n mergedConfig.logRecordProcessors.length > 0\n ? mergedConfig.logRecordProcessors\n : mergedConfig.logRecordProcessor\n ? [mergedConfig.logRecordProcessor]\n : undefined;\n\n // Build array of span processors (supports multiple)\n let spanProcessors: SpanProcessor[] = [];\n\n if (configuredSpanProcessors && configuredSpanProcessors.length > 0) {\n // User provided custom processors (full control)\n spanProcessors.push(...configuredSpanProcessors);\n } else if (configuredSpanExporters && configuredSpanExporters.length > 0) {\n // User provided custom exporters (wrap each with tail sampling)\n for (const exporter of configuredSpanExporters) {\n spanProcessors.push(\n new TailSamplingSpanProcessor(new BatchSpanProcessor(exporter)),\n );\n }\n } else {\n for (const destination of otlpDestinations) {\n if (!destinationSupportsSignal(destination, 'traces')) continue;\n\n const traceExporter = createTraceExporter(destination.protocol, {\n url: formatEndpointUrl(\n destination.endpoint,\n 'traces',\n destination.protocol,\n ),\n headers: destination.headers,\n });\n\n spanProcessors.push(\n new TailSamplingSpanProcessor(new BatchSpanProcessor(traceExporter)),\n );\n }\n }\n // If no endpoint and no custom processors/exporters, array remains empty\n // SDK will still work but won't export traces\n\n // Add baggage span processor if enabled\n if (mergedConfig.baggage) {\n const prefix =\n typeof mergedConfig.baggage === 'string'\n ? mergedConfig.baggage\n ? `${mergedConfig.baggage}.`\n : ''\n : 'baggage.';\n spanProcessors.push(new BaggageSpanProcessor({ prefix }));\n }\n\n // Apply debug mode configuration\n const debugMode = resolveDebugFlag(mergedConfig.debug);\n\n if (debugMode === 'pretty') {\n // Pretty debug: colorized, hierarchical output\n spanProcessors.push(new SimpleSpanProcessor(new PrettyConsoleExporter()));\n } else if (debugMode === true) {\n // Raw debug: JSON output\n spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter()));\n }\n\n // Add canonical log line processor BEFORE wrapping processors\n // This ensures it gets wrapped with the same filter/normalizer/redactor as other processors,\n // so canonical logs respect spanFilter (filtered spans aren't logged), spanNameNormalizer\n // (normalized names are used), and attributeRedactor (sensitive data is redacted).\n if (mergedConfig.canonicalLogLines?.enabled) {\n const canonicalOptions: CanonicalLogLineOptions = {\n logger: mergedConfig.canonicalLogLines.logger || mergedConfig.logger,\n rootSpansOnly: mergedConfig.canonicalLogLines.rootSpansOnly,\n minLevel: mergedConfig.canonicalLogLines.minLevel,\n messageFormat: mergedConfig.canonicalLogLines.messageFormat,\n includeResourceAttributes:\n mergedConfig.canonicalLogLines.includeResourceAttributes,\n shouldEmit: mergedConfig.canonicalLogLines.shouldEmit,\n keep: mergedConfig.canonicalLogLines.keep,\n drain: mergedConfig.canonicalLogLines.drain,\n onDrainError: mergedConfig.canonicalLogLines.onDrainError,\n pretty: mergedConfig.canonicalLogLines.pretty,\n };\n spanProcessors.push(new CanonicalLogLineProcessor(canonicalOptions));\n }\n\n // Wrap processors in order: redactor (innermost) → normalizer → filter (outermost)\n // This ensures onEnd() execution order is: filter → normalizer → redactor\n // So filtering sees original attributes, and redaction happens last before export.\n\n // Step 1: Wrap with AttributeRedactingProcessor (innermost - executes last in onEnd)\n if (mergedConfig.attributeRedactor && spanProcessors.length > 0) {\n const redactor = mergedConfig.attributeRedactor;\n spanProcessors = spanProcessors.map(\n (processor) =>\n new AttributeRedactingProcessor(processor, {\n redactor,\n }),\n );\n }\n\n // Store string redactor for use by PostHog log/subscriber paths\n if (mergedConfig.attributeRedactor) {\n _stringRedactor = createStringRedactor(mergedConfig.attributeRedactor);\n }\n\n // Wire string redactor to subscribers that support it (e.g., PostHogSubscriber)\n if (_stringRedactor && mergedConfig.subscribers) {\n for (const subscriber of mergedConfig.subscribers) {\n if (acceptsStringRedactor(subscriber)) {\n subscriber.setStringRedactor(_stringRedactor);\n }\n }\n }\n\n // Step 2: Wrap with SpanNameNormalizingProcessor (middle)\n // Normalizer runs in onStart(), so span names are normalized before any onEnd processing\n if (mergedConfig.spanNameNormalizer && spanProcessors.length > 0) {\n spanProcessors = spanProcessors.map(\n (processor) =>\n new SpanNameNormalizingProcessor(processor, {\n normalizer: mergedConfig.spanNameNormalizer!,\n }),\n );\n }\n\n // Step 3: Wrap with FilteringSpanProcessor (outermost - executes first in onEnd)\n // Filter sees original (unredacted) attributes, so it can match on sensitive values\n if (mergedConfig.spanFilter && spanProcessors.length > 0) {\n spanProcessors = spanProcessors.map(\n (processor) =>\n new FilteringSpanProcessor(processor, {\n filter: mergedConfig.spanFilter!,\n }),\n );\n }\n\n // Build array of metric readers (supports multiple)\n const metricReaders: MetricReader[] = [];\n\n if (configuredMetricReaders && configuredMetricReaders.length > 0) {\n // User provided custom metric readers\n metricReaders.push(...configuredMetricReaders);\n } else if (metricsEnabled) {\n for (const destination of otlpDestinations) {\n if (!destinationSupportsSignal(destination, 'metrics')) continue;\n\n const metricExporter = createMetricExporter(destination.protocol, {\n url: formatEndpointUrl(\n destination.endpoint,\n 'metrics',\n destination.protocol,\n ),\n headers: destination.headers,\n });\n\n metricReaders.push(\n new PeriodicExportingMetricReader({\n exporter: metricExporter,\n }),\n );\n }\n }\n\n let logRecordProcessors: LogRecordProcessor[] | undefined;\n if (\n configuredLogRecordProcessors &&\n configuredLogRecordProcessors.length > 0\n ) {\n logRecordProcessors = [...configuredLogRecordProcessors];\n }\n\n // Auto-configure OTLP log exporters when logs are enabled.\n if (logsEnabled) {\n for (const destination of otlpDestinations) {\n if (!destinationSupportsSignal(destination, 'logs')) continue;\n\n const logExporter = createLogExporter(destination.protocol, {\n url: formatEndpointUrl(\n destination.endpoint,\n 'logs',\n destination.protocol,\n ),\n headers: destination.headers,\n });\n\n let processor: LogRecordProcessor = new BatchLogRecordProcessor({\n exporter: logExporter,\n });\n if (_stringRedactor) {\n processor = new RedactingLogRecordProcessor(processor, _stringRedactor);\n }\n if (!logRecordProcessors) {\n logRecordProcessors = [];\n }\n logRecordProcessors.push(processor);\n }\n\n if (\n otlpDestinations.some((destination) =>\n destinationSupportsSignal(destination, 'logs'),\n )\n ) {\n logger.info({}, '[autotel] OTLP log exporter configured');\n }\n }\n\n // PostHog OTLP logs integration\n const posthogProcessors = buildPostHogLogProcessors(\n mergedConfig.posthog,\n _stringRedactor,\n );\n if (posthogProcessors.length > 0) {\n if (!logRecordProcessors) {\n logRecordProcessors = [];\n }\n logRecordProcessors.push(...posthogProcessors);\n logger.info({}, '[autotel] PostHog OTLP logs configured');\n }\n\n // Handle instrumentations: merge manual instrumentations with auto-instrumentations\n let finalInstrumentations: NodeSDKConfiguration['instrumentations'] =\n mergedConfig.instrumentations ? [...mergedConfig.instrumentations] : [];\n\n if (\n mergedConfig.autoInstrumentations !== undefined &&\n mergedConfig.autoInstrumentations !== false\n ) {\n // Check for ESM mode and provide guidance\n const isESM = isESMMode();\n if (isESM) {\n logger.info(\n {},\n '[autotel] ESM mode detected. For auto-instrumentation to work:\\n' +\n ' 1. Install @opentelemetry/auto-instrumentations-node as a direct dependency\\n' +\n ' 2. Import autotel/register FIRST in your instrumentation file\\n' +\n ' 3. Use getNodeAutoInstrumentations() directly instead of autoInstrumentations\\n' +\n ' See: https://github.com/jagreehal/autotel#esm-setup',\n );\n }\n\n try {\n // Detect manual instrumentations to avoid conflicts\n const manualInstrumentationNames = getInstrumentationNames(\n mergedConfig.instrumentations ?? [],\n );\n\n // Warn if both autoInstrumentations and manual instrumentations are provided\n if (manualInstrumentationNames.size > 0) {\n const manualNames = [...manualInstrumentationNames].join(', ');\n logger.info(\n {},\n `[autotel] Detected manual instrumentations (${manualNames}). ` +\n 'These will take precedence over auto-instrumentations. ' +\n 'Tip: Set autoInstrumentations:false if you want full manual control, or remove manual configs to use auto-instrumentations.',\n );\n }\n\n const autoInstrumentations = getAutoInstrumentations(\n mergedConfig.autoInstrumentations,\n manualInstrumentationNames,\n );\n if (autoInstrumentations && autoInstrumentations.length > 0) {\n // Cast to proper type - getNodeAutoInstrumentations returns the correct type\n finalInstrumentations = [\n ...finalInstrumentations,\n ...(autoInstrumentations as NodeSDKConfiguration['instrumentations']),\n ];\n }\n } catch (error) {\n logger.warn(\n {},\n `[autotel] Failed to configure auto-instrumentations: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n const autotelSampler =\n mergedConfig.sampler ??\n (mergedConfig.sampling\n ? resolveSamplingPreset(mergedConfig.sampling)\n : undefined);\n if (autotelSampler) {\n mergedConfig.sampler = autotelSampler;\n }\n const sampler: OtelSampler = autotelSampler\n ? toOtelSampler(autotelSampler)\n : (envConfig.otelSampler ?? toOtelSampler(samplingPresets.production()));\n\n const sdkOptions: Partial<NodeSDKConfiguration> = {\n resource,\n // NodeSDK runs its environment resource detector after merging `resource`.\n // Passing serviceName separately reapplies the resolved Autotel value last,\n // preserving our documented explicit > YAML > environment precedence.\n serviceName: mergedConfig.service,\n sampler,\n instrumentations: finalInstrumentations,\n };\n\n if (spanProcessors.length > 0) {\n sdkOptions.spanProcessors = spanProcessors;\n }\n\n if (metricReaders.length > 0) {\n sdkOptions.metricReaders = metricReaders;\n }\n\n if (logRecordProcessors && logRecordProcessors.length > 0) {\n sdkOptions.logRecordProcessors = logRecordProcessors;\n }\n\n sdk = mergedConfig.sdkFactory\n ? mergedConfig.sdkFactory(sdkOptions)\n : new NodeSDK(sdkOptions);\n\n if (!sdk) {\n throw new Error('[autotel] sdkFactory must return a NodeSDK instance');\n }\n\n sdk.start();\n\n // Initialize OpenLLMetry if enabled (after SDK starts to reuse tracer provider)\n if (mergedConfig.openllmetry?.enabled) {\n const traceloop = _optionalRequire<{\n initialize?: (options?: Record<string, unknown>) => void;\n }>('@traceloop/node-server-sdk');\n\n if (traceloop) {\n const initOptions: Record<string, unknown> = {\n ...mergedConfig.openllmetry.options,\n };\n\n // Reuse autotel's tracer provider\n try {\n // Type assertion needed as getTracerProvider is not in the public NodeSDK interface\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tracerProvider = (sdk as any).getTracerProvider();\n initOptions.tracerProvider = tracerProvider;\n } catch {\n // Ignore if tracer provider not available\n }\n\n // Pass span exporter to OpenLLMetry if provided\n if (configuredSpanExporters?.[0]) {\n initOptions.exporter = configuredSpanExporters[0];\n }\n\n if (typeof traceloop.initialize === 'function') {\n traceloop.initialize(initOptions);\n logger.info({}, '[autotel] OpenLLMetry initialized successfully');\n } else {\n logger.warn(\n {},\n '[autotel] OpenLLMetry initialize function not found. Check @traceloop/node-server-sdk version.',\n );\n }\n } else {\n logger.warn(\n {},\n '[autotel] OpenLLMetry enabled but @traceloop/node-server-sdk is not installed. ' +\n 'Install it as a peer dependency to use OpenLLMetry integration.',\n );\n }\n }\n\n initialized = true;\n const processHandlers = mergedConfig.processHandlers;\n if (processHandlers) {\n installProcessHandlers(\n processHandlers === true ? {} : processHandlers,\n shutdown,\n );\n } else {\n uninstallProcessHandlers();\n }\n}\n\n/**\n * Extract instrumentation class names from instrumentation instances\n * Used to detect duplicates between manual and auto instrumentations\n */\nfunction getInstrumentationNames(\n instrumentations: NodeSDKConfiguration['instrumentations'],\n): Set<string> {\n const names = new Set<string>();\n\n if (!instrumentations) return names;\n\n for (const instrumentation of instrumentations) {\n if (instrumentation && typeof instrumentation === 'object') {\n names.add(instrumentation.constructor.name);\n }\n }\n\n return names;\n}\n\n/**\n * Map common instrumentation class names to their package names\n * Used to disable auto-instrumentations when user provides manual configs\n */\nconst INSTRUMENTATION_CLASS_TO_PACKAGE: Record<string, string> = {\n HttpInstrumentation: '@opentelemetry/instrumentation-http',\n HttpsInstrumentation: '@opentelemetry/instrumentation-http',\n ExpressInstrumentation: '@opentelemetry/instrumentation-express',\n FastifyInstrumentation: '@opentelemetry/instrumentation-fastify',\n MongoDBInstrumentation: '@opentelemetry/instrumentation-mongodb',\n MongooseInstrumentation: '@opentelemetry/instrumentation-mongoose',\n PrismaInstrumentation: '@opentelemetry/instrumentation-prisma',\n PinoInstrumentation: '@opentelemetry/instrumentation-pino',\n WinstonInstrumentation: '@opentelemetry/instrumentation-winston',\n RedisInstrumentation: '@opentelemetry/instrumentation-redis',\n GraphQLInstrumentation: '@opentelemetry/instrumentation-graphql',\n GrpcInstrumentation: '@opentelemetry/instrumentation-grpc',\n IORedisInstrumentation: '@opentelemetry/instrumentation-ioredis',\n KnexInstrumentation: '@opentelemetry/instrumentation-knex',\n NestJsInstrumentation: '@opentelemetry/instrumentation-nestjs-core',\n PgInstrumentation: '@opentelemetry/instrumentation-pg',\n MySQLInstrumentation: '@opentelemetry/instrumentation-mysql',\n MySQL2Instrumentation: '@opentelemetry/instrumentation-mysql2',\n};\n\n/**\n * Type for the auto-instrumentations loader function\n * @internal Used for testing injection\n */\nexport type AutoInstrumentationsLoader = (\n config?: Record<string, { enabled?: boolean }>,\n) => unknown[];\n\n/**\n * Detect if we're running in ESM mode\n */\nfunction isESMMode(): boolean {\n // Check if we're in an ESM context by looking for common ESM indicators\n try {\n // In ESM, module.exports doesn't exist in the global scope the same way\n // Also check if the package.json type is \"module\"\n const fs = requireModule<typeof import('node:fs')>('node:fs');\n try {\n const pkg = JSON.parse(\n fs.readFileSync(`${process.cwd()}/package.json`, 'utf8'),\n );\n return pkg.type === 'module';\n } catch {\n return false;\n }\n } catch {\n return false;\n }\n}\n\n/**\n * Lazy-load auto-instrumentations (optional peer dependency)\n * Only loads when integrations config is truthy, avoiding ~40+ package imports at startup.\n */\nfunction loadNodeAutoInstrumentations(): AutoInstrumentationsLoader {\n try {\n const mod = requireModule<{\n getNodeAutoInstrumentations: AutoInstrumentationsLoader;\n }>('@opentelemetry/auto-instrumentations-node');\n return mod.getNodeAutoInstrumentations;\n } catch {\n const isESM = isESMMode();\n const baseMessage = '@opentelemetry/auto-instrumentations-node not found.';\n\n if (isESM) {\n throw new Error(\n `${baseMessage}\\n\\n` +\n 'ESM Setup Required:\\n' +\n '1. Install as a direct dependency: pnpm add @opentelemetry/auto-instrumentations-node\\n' +\n '2. Create instrumentation.mjs with:\\n' +\n \" import 'autotel/register'; // MUST be first!\\n\" +\n \" import { init } from 'autotel';\\n\" +\n \" import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\\n\" +\n ' init({ service: \"my-app\", instrumentations: getNodeAutoInstrumentations() });\\n' +\n '3. Run with: tsx --import ./instrumentation.mjs src/index.ts\\n\\n' +\n 'See: https://github.com/jagreehal/autotel#esm-setup',\n );\n }\n\n throw new Error(\n `${baseMessage} Install it: pnpm add @opentelemetry/auto-instrumentations-node`,\n );\n }\n}\n\n/**\n * Injectable loader for testing. Set to override the default loader.\n * @internal\n */\nlet _autoInstrumentationsLoader: (() => AutoInstrumentationsLoader) | null =\n null;\n\n/**\n * @internal Set custom loader (for testing)\n */\nexport function _setAutoInstrumentationsLoader(\n loader: (() => AutoInstrumentationsLoader) | null,\n): void {\n _autoInstrumentationsLoader = loader;\n}\n\n/**\n * @internal Reset loader to default (for testing cleanup)\n */\nexport function _resetAutoInstrumentationsLoader(): void {\n _autoInstrumentationsLoader = null;\n}\n\n/**\n * Get auto-instrumentations based on simple integration names\n * Excludes instrumentations that are manually provided to avoid conflicts\n */\nfunction getAutoInstrumentations(\n integrations: string[] | boolean | Record<string, { enabled?: boolean }>,\n manualInstrumentationNames: Set<string> = new Set(),\n): unknown[] {\n if (integrations === false) {\n return [];\n }\n\n // Use injected loader if set (for testing), otherwise lazy-load\n const getNodeAutoInstrumentations = _autoInstrumentationsLoader\n ? _autoInstrumentationsLoader()\n : loadNodeAutoInstrumentations();\n\n // Build exclusion config for manual instrumentations\n const exclusionConfig: Record<string, { enabled: boolean }> = {};\n for (const className of manualInstrumentationNames) {\n const packageName = INSTRUMENTATION_CLASS_TO_PACKAGE[className];\n if (packageName) {\n exclusionConfig[packageName] = { enabled: false };\n }\n }\n\n if (integrations === true) {\n // If exclusions exist, pass them to getNodeAutoInstrumentations\n if (Object.keys(exclusionConfig).length > 0) {\n return getNodeAutoInstrumentations(exclusionConfig);\n }\n return getNodeAutoInstrumentations();\n }\n\n if (Array.isArray(integrations)) {\n const config: Record<string, { enabled: boolean }> = { ...exclusionConfig };\n for (const name of integrations) {\n const packageName = `@opentelemetry/instrumentation-${name}`;\n // Don't override exclusions\n if (!exclusionConfig[packageName]) {\n config[packageName] = { enabled: true };\n }\n }\n return getNodeAutoInstrumentations(config);\n }\n\n const config: Record<string, { enabled?: boolean }> = {\n ...exclusionConfig,\n ...integrations,\n };\n\n // Override any integrations that conflict with manual instrumentations\n for (const packageName of Object.keys(exclusionConfig)) {\n const integrationsKey = Object.keys(integrations).find((key) =>\n packageName.includes(key),\n );\n if (integrationsKey) {\n // Manual instrumentation takes precedence\n config[packageName] = { enabled: false };\n }\n }\n\n return getNodeAutoInstrumentations(config);\n}\n\n/**\n * Check if autotel has been initialized\n */\nexport function isInitialized(): boolean {\n return initialized;\n}\n\n/**\n * Get current config (internal use)\n */\nexport function getConfig(): AutotelConfig | null {\n return config;\n}\n\n/**\n * Get current logger (internal use)\n */\nexport function getLogger(): Logger {\n return logger;\n}\n\n/**\n * Get validation config (internal use)\n */\nexport function getValidationConfig(): Partial<ValidationConfig> | null {\n return validationConfig;\n}\n\n/**\n * Get events config (internal use)\n */\nexport function getEventsConfig(): EventsConfig | null {\n return eventsConfig;\n}\n\n/**\n * Warn once if not initialized (same behavior in all environments)\n */\nexport function warnIfNotInitialized(context: string): void {\n if (!initialized && !warnedOnce) {\n logger.warn(\n {},\n `[autotel] ${context} used before init() called. ` +\n 'Call init({ service: \"...\" }) first. See: https://docs.autotel.dev/quickstart',\n );\n warnedOnce = true;\n }\n}\n\n/**\n * Get default sampler\n */\nexport function getDefaultSampler(): Sampler {\n return config?.sampler || samplingPresets.production();\n}\n\n/**\n * Auto-detect version from package.json\n */\nfunction detectVersion(): string {\n try {\n // Try to read package.json from cwd using fs\n const fs = requireModule<typeof import('node:fs')>('node:fs');\n const pkg = JSON.parse(\n fs.readFileSync(`${process.cwd()}/package.json`, 'utf8'),\n );\n return pkg.version || '1.0.0';\n } catch {\n return '1.0.0';\n }\n}\n\n/**\n * Detect hostname for resource attributes.\n * Supports Datadog conventions (DD_HOSTNAME) and falls back to system hostname.\n *\n * Priority order:\n * 1. DD_HOSTNAME environment variable (Datadog convention)\n * 2. HOSTNAME environment variable (common Unix convention)\n * 3. os.hostname() (system hostname)\n *\n * @returns hostname string or undefined if detection fails\n */\nfunction detectHostname(): string | undefined {\n // Priority 1: DD_HOSTNAME (Datadog convention)\n if (process.env.DD_HOSTNAME) {\n return process.env.DD_HOSTNAME;\n }\n\n // Priority 2: HOSTNAME (common in containers and Unix systems)\n if (process.env.HOSTNAME) {\n return process.env.HOSTNAME;\n }\n\n // Priority 3: System hostname\n try {\n const os = requireModule<typeof import('node:os')>('node:os');\n return os.hostname();\n } catch {\n // os module not available (edge runtime, browser, etc.)\n return undefined;\n }\n}\n\n/**\n * Get the string redactor configured via init({ attributeRedactor }).\n * Returns null if no redactor was configured.\n */\nexport function getStringRedactor(): StringRedactor | null {\n return _stringRedactor;\n}\n\n/**\n * @internal Override optional require for deterministic tests.\n */\nexport function _setOptionalRequireForTesting(\n loader: typeof safeRequire,\n): void {\n _optionalRequire = loader;\n}\n\n/**\n * @internal Reset optional require override.\n */\nexport function _resetOptionalRequireForTesting(): void {\n _optionalRequire = safeRequire;\n}\n\n/**\n * @internal Close embedded devtools if running.\n */\nexport async function _closeEmbeddedDevtools(): Promise<void> {\n if (_devtoolsClose) {\n await _devtoolsClose();\n _devtoolsClose = null;\n }\n}\n\n/**\n * @internal Get embedded devtools close handle.\n */\nexport function _getEmbeddedDevtoolsCloseForTesting():\n (() => Promise<void> | void) | null {\n return _devtoolsClose;\n}\n\n/**\n * Get SDK instance (for shutdown)\n */\nexport function getSdk(): NodeSDK | null {\n return sdk;\n}\n"],"mappings":"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"}