{"version":3,"file":"headers-Crw-o-No.mjs","names":[],"sources":["../src/shared/middleware.ts","../src/shared/headers.ts"],"sourcesContent":["import type { DrainContext, EnrichContext, RequestLogger, RouteConfig, TailSamplingContext, WideEvent } from '../types'\nimport { createRequestLogger, isEnabled, shouldKeep } from '../logger'\nimport { extractErrorStatus } from './errors'\nimport { shouldLog, getServiceForPath } from './routes'\n\n/**\n * Base options shared by all framework integrations.\n *\n * Every framework-specific options interface (e.g. `MxllogExpressOptions`)\n * extends this type. If a framework needs extra fields it can add them\n * on top; otherwise the base is used as-is.\n *\n * @beta Part of `@safaricom-mxl/log/toolkit` — the public API for building custom integrations.\n */\nexport interface BaseMxllogOptions {\n  /** Route patterns to include in logging (glob). If not set, all routes are logged */\n  include?: string[]\n  /** Route patterns to exclude from logging. Exclusions take precedence over inclusions */\n  exclude?: string[]\n  /** Route-specific service configuration */\n  routes?: Record<string, RouteConfig>\n  /**\n   * Drain callback called with every emitted event.\n   * Use with drain adapters (Axiom, OTLP, Sentry, etc.) or custom endpoints.\n   */\n  drain?: (ctx: DrainContext) => void | Promise<void>\n  /**\n   * Enrich callback called after emit, before drain.\n   * Use to add derived context (geo, deployment info, user agent, etc.).\n   */\n  enrich?: (ctx: EnrichContext) => void | Promise<void>\n  /**\n   * Custom tail sampling callback.\n   * Set `ctx.shouldKeep = true` to force-keep the log regardless of head sampling.\n   */\n  keep?: (ctx: TailSamplingContext) => void | Promise<void>\n}\n\n/**\n * Internal options consumed by `createMiddlewareLogger`.\n * Extends `BaseMxllogOptions` with the request-specific fields\n * that framework adapters must provide.\n */\nexport interface MiddlewareLoggerOptions extends BaseMxllogOptions {\n  method: string\n  path: string\n  requestId?: string\n  /** Pre-filtered safe request headers (used for enrich/drain context) */\n  headers?: Record<string, string>\n}\n\nexport interface MiddlewareLoggerResult {\n  logger: RequestLogger\n  finish: (opts?: { status?: number; error?: Error }) => Promise<WideEvent | null>\n  skipped: boolean\n}\n\nconst noopResult: MiddlewareLoggerResult = {\n  logger: {\n    set() {},\n    error() {},\n    info() {},\n    warn() {},\n    emit() {\n      return null \n    },\n    getContext() {\n      return {} \n    },\n  },\n  finish: () => Promise.resolve(null),\n  skipped: true,\n}\n\nasync function runEnrichAndDrain(\n  emittedEvent: WideEvent,\n  options: MiddlewareLoggerOptions,\n  requestInfo: { method: string; path: string; requestId?: string },\n  responseStatus?: number,\n): Promise<void> {\n  if (options.enrich) {\n    const enrichCtx: EnrichContext = {\n      event: emittedEvent,\n      request: requestInfo,\n      headers: options.headers,\n      response: { status: responseStatus },\n    }\n    try {\n      await options.enrich(enrichCtx)\n    } catch (err) {\n      console.error('[mxllog] enrich failed:', err)\n    }\n  }\n\n  if (options.drain) {\n    const drainCtx: DrainContext = {\n      event: emittedEvent,\n      request: requestInfo,\n      headers: options.headers,\n    }\n    try {\n      await options.drain(drainCtx)\n    } catch (err) {\n      console.error('[mxllog] drain failed:', err)\n    }\n  }\n}\n\n/**\n * Create a middleware-aware request logger with full lifecycle management.\n *\n * Handles the complete pipeline shared across all framework integrations:\n * route filtering, logger creation, service overrides, duration tracking,\n * tail sampling evaluation, event emission, enrichment, and draining.\n *\n * Framework adapters only need to:\n * 1. Extract method/path/requestId/headers from the framework request\n * 2. Call `createMiddlewareLogger()` with those + user options\n * 3. Check `skipped` — if true, skip to next middleware\n * 4. Store `logger` in framework-specific context (e.g., `c.set('log', logger)`)\n * 5. Call `finish({ status })` or `finish({ error })` at response end\n *\n * @beta Part of `@safaricom-mxl/log/toolkit` — the public API for building custom integrations.\n */\nexport function createMiddlewareLogger(options: MiddlewareLoggerOptions): MiddlewareLoggerResult {\n  if (!isEnabled()) return noopResult\n\n  const { method, path, requestId, include, exclude, routes, keep } = options\n\n  if (!shouldLog(path, include, exclude)) {\n    return noopResult\n  }\n\n  const resolvedRequestId = requestId || crypto.randomUUID()\n\n  const logger = createRequestLogger({\n    method,\n    path,\n    requestId: resolvedRequestId,\n  })\n\n  const routeService = getServiceForPath(path, routes)\n  if (routeService) {\n    logger.set({ service: routeService })\n  }\n\n  const startTime = Date.now()\n  const requestInfo = { method, path, requestId: resolvedRequestId }\n\n  const finish = async (opts?: { status?: number; error?: Error }): Promise<WideEvent | null> => {\n    const { status, error } = opts ?? {}\n\n    if (error) {\n      logger.error(error)\n      const errorStatus = extractErrorStatus(error)\n      logger.set({ status: errorStatus })\n    } else if (status !== undefined) {\n      logger.set({ status })\n    }\n\n    const durationMs = Date.now() - startTime\n\n    const resolvedStatus = error\n      ? extractErrorStatus(error)\n      : status ?? (logger.getContext().status as number | undefined)\n\n    const tailCtx: TailSamplingContext = {\n      status: resolvedStatus,\n      duration: durationMs,\n      path,\n      method,\n      context: logger.getContext(),\n      shouldKeep: false,\n    }\n\n    if (keep) {\n      await keep(tailCtx)\n    }\n\n    const forceKeep = tailCtx.shouldKeep || shouldKeep(tailCtx)\n    const emittedEvent = logger.emit({ _forceKeep: forceKeep })\n\n    if (emittedEvent && (options.enrich || options.drain)) {\n      await runEnrichAndDrain(emittedEvent, options, requestInfo, resolvedStatus)\n    }\n\n    return emittedEvent\n  }\n\n  return { logger, finish, skipped: false }\n}\n","import { filterSafeHeaders } from '../utils'\n\n/**\n * Extract headers from a Web API `Headers` object and filter out sensitive ones.\n * Works with any runtime that supports the standard `Headers` API (Hono, Elysia,\n * Nitro v3, Cloudflare Workers, Bun, Deno, etc.).\n */\nexport function extractSafeHeaders(headers: Headers): Record<string, string> {\n  const raw: Record<string, string> = {}\n  headers.forEach((value, key) => {\n    raw[key] = value\n  })\n  return filterSafeHeaders(raw)\n}\n\n/**\n * Extract headers from Node.js `IncomingHttpHeaders` and filter out sensitive ones.\n * Works with Express, Fastify, and any Node.js HTTP server using `req.headers`.\n */\nexport function extractSafeNodeHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string> {\n  const raw: Record<string, string> = {}\n  for (const [key, value] of Object.entries(headers)) {\n    if (value === undefined) continue\n    raw[key] = Array.isArray(value) ? value.join(', ') : value\n  }\n  return filterSafeHeaders(raw)\n}\n"],"mappings":";;;;;AAyDA,MAAM,aAAqC;CACzC,QAAQ;EACN,MAAM;EACN,QAAQ;EACR,OAAO;EACP,OAAO;EACP,OAAO;AACL,UAAO;;EAET,aAAa;AACX,UAAO,EAAE;;EAEZ;CACD,cAAc,QAAQ,QAAQ,KAAK;CACnC,SAAS;CACV;AAED,eAAe,kBACb,cACA,SACA,aACA,gBACe;AACf,KAAI,QAAQ,QAAQ;EAClB,MAAM,YAA2B;GAC/B,OAAO;GACP,SAAS;GACT,SAAS,QAAQ;GACjB,UAAU,EAAE,QAAQ,gBAAgB;GACrC;AACD,MAAI;AACF,SAAM,QAAQ,OAAO,UAAU;WACxB,KAAK;AACZ,WAAQ,MAAM,2BAA2B,IAAI;;;AAIjD,KAAI,QAAQ,OAAO;EACjB,MAAM,WAAyB;GAC7B,OAAO;GACP,SAAS;GACT,SAAS,QAAQ;GAClB;AACD,MAAI;AACF,SAAM,QAAQ,MAAM,SAAS;WACtB,KAAK;AACZ,WAAQ,MAAM,0BAA0B,IAAI;;;;;;;;;;;;;;;;;;;;AAqBlD,SAAgB,uBAAuB,SAA0D;AAC/F,KAAI,CAAC,WAAW,CAAE,QAAO;CAEzB,MAAM,EAAE,QAAQ,MAAM,WAAW,SAAS,SAAS,QAAQ,SAAS;AAEpE,KAAI,CAAC,UAAU,MAAM,SAAS,QAAQ,CACpC,QAAO;CAGT,MAAM,oBAAoB,aAAa,OAAO,YAAY;CAE1D,MAAM,SAAS,oBAAoB;EACjC;EACA;EACA,WAAW;EACZ,CAAC;CAEF,MAAM,eAAe,kBAAkB,MAAM,OAAO;AACpD,KAAI,aACF,QAAO,IAAI,EAAE,SAAS,cAAc,CAAC;CAGvC,MAAM,YAAY,KAAK,KAAK;CAC5B,MAAM,cAAc;EAAE;EAAQ;EAAM,WAAW;EAAmB;CAElE,MAAM,SAAS,OAAO,SAAyE;EAC7F,MAAM,EAAE,QAAQ,UAAU,QAAQ,EAAE;AAEpC,MAAI,OAAO;AACT,UAAO,MAAM,MAAM;GACnB,MAAM,cAAc,mBAAmB,MAAM;AAC7C,UAAO,IAAI,EAAE,QAAQ,aAAa,CAAC;aAC1B,WAAW,KAAA,EACpB,QAAO,IAAI,EAAE,QAAQ,CAAC;EAGxB,MAAM,aAAa,KAAK,KAAK,GAAG;EAEhC,MAAM,iBAAiB,QACnB,mBAAmB,MAAM,GACzB,UAAW,OAAO,YAAY,CAAC;EAEnC,MAAM,UAA+B;GACnC,QAAQ;GACR,UAAU;GACV;GACA;GACA,SAAS,OAAO,YAAY;GAC5B,YAAY;GACb;AAED,MAAI,KACF,OAAM,KAAK,QAAQ;EAGrB,MAAM,YAAY,QAAQ,cAAc,WAAW,QAAQ;EAC3D,MAAM,eAAe,OAAO,KAAK,EAAE,YAAY,WAAW,CAAC;AAE3D,MAAI,iBAAiB,QAAQ,UAAU,QAAQ,OAC7C,OAAM,kBAAkB,cAAc,SAAS,aAAa,eAAe;AAG7E,SAAO;;AAGT,QAAO;EAAE;EAAQ;EAAQ,SAAS;EAAO;;;;;;;;;ACtL3C,SAAgB,mBAAmB,SAA0C;CAC3E,MAAM,MAA8B,EAAE;AACtC,SAAQ,SAAS,OAAO,QAAQ;AAC9B,MAAI,OAAO;GACX;AACF,QAAO,kBAAkB,IAAI;;;;;;AAO/B,SAAgB,uBAAuB,SAAgF;CACrH,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,MAAI,UAAU,KAAA,EAAW;AACzB,MAAI,OAAO,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,KAAK,GAAG;;AAEvD,QAAO,kBAAkB,IAAI"}