autotel
Version:
Write Once, Observe Anywhere
1 lines • 7.81 kB
Source Map (JSON)
{"version":3,"sources":["../src/correlation-id.ts"],"names":["AsyncLocalStorage","context","propagation","trace","enterOrRun"],"mappings":";;;;;;AAoCA,IAAM,kBAAA,GAAqB,IAAIA,6BAAA,EAAoC;AAK5D,IAAM,0BAAA,GAA6B;AAenC,SAAS,qBAAA,GAAgC;AAE9C,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,CAAC,CAAA;AAC9B,EAAA,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAG5B,EAAA,OAAO,CAAC,GAAG,KAAK,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AACvE;AAqBO,SAAS,gBAAA,GAAuC;AAErD,EAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,QAAA,EAAS,EAAG,KAAA;AAChD,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,QAAA;AAAA,EACT;AAGA,EAAA,MAAM,aAAA,GAAgBC,YAAQ,MAAA,EAAO;AACrC,EAAA,MAAM,OAAA,GAAUC,eAAA,CAAY,UAAA,CAAW,aAAa,CAAA;AACpD,EAAA,MAAM,YAAA,GAAe,OAAA,EAAS,QAAA,CAAS,0BAA0B,CAAA;AACjE,EAAA,IAAI,cAAc,KAAA,EAAO;AACvB,IAAA,OAAO,YAAA,CAAa,KAAA;AAAA,EACtB;AAGA,EAAA,MAAM,IAAA,GAAOC,UAAM,aAAA,EAAc;AACjC,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,MAAM,WAAA,GAAc,KAAK,WAAA,EAAY;AACrC,IAAA,OAAO,WAAA,CAAY,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAAA,EACxC;AAGA,EAAA,OAAO,MAAA;AACT;AAgBO,SAAS,wBAAA,GAAmC;AACjD,EAAA,OAAO,gBAAA,MAAsB,qBAAA,EAAsB;AACrD;AAoBO,SAAS,oBAAA,CAAwB,eAAuB,EAAA,EAAgB;AAC7E,EAAA,OAAO,mBAAmB,GAAA,CAAI,EAAE,KAAA,EAAO,aAAA,IAAiB,EAAE,CAAA;AAC5D;AAgBO,SAAS,iBAAiB,aAAA,EAA6B;AAC5D,EAAAC,4BAAA,CAAW,oBAAoB,aAAa,CAAA;AAC9C;AAsBO,SAAS,0BACd,aAAA,EACsC;AACtC,EAAA,MAAM,aAAA,GAAgBH,YAAQ,MAAA,EAAO;AACrC,EAAA,IAAI,UACFC,eAAA,CAAY,UAAA,CAAW,aAAa,CAAA,IAAKA,gBAAY,aAAA,EAAc;AACrE,EAAA,OAAA,GAAU,OAAA,CAAQ,SAAS,0BAAA,EAA4B;AAAA,IACrD,KAAA,EAAO;AAAA,GACR,CAAA;AACD,EAAA,OAAOA,eAAA,CAAY,UAAA,CAAW,aAAA,EAAe,OAAO,CAAA;AACtD;AAKO,SAAS,qBAAA,GAA6D;AAC3E,EAAA,OAAO,kBAAA;AACT","file":"chunk-JSNUWSBH.cjs","sourcesContent":["/**\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';\nimport { AsyncLocalStorage } 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 = new 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"]}