logixia 1.8.2 → 1.8.4

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 (88) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/{index-CthBT3t8.d.mts → index-DknhKCCg.d.ts} +12 -1
  3. package/dist/index-DknhKCCg.d.ts.map +1 -0
  4. package/dist/{index-ClPZrfIU.d.ts → index-DnhKoNBG.d.mts} +12 -1
  5. package/dist/index-DnhKoNBG.d.mts.map +1 -0
  6. package/dist/index.d.mts +87 -16
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.d.ts +86 -15
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +78 -40
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +77 -37
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/{logitron-logger.module-Dx2mUz-g.d.ts → logitron-logger.module-BkPXi0He.d.ts} +97 -5
  15. package/dist/logitron-logger.module-BkPXi0He.d.ts.map +1 -0
  16. package/dist/logitron-logger.module-CMDM61Iz.js +2566 -0
  17. package/dist/logitron-logger.module-CMDM61Iz.js.map +1 -0
  18. package/dist/{logitron-logger.module-KU_L04y1.d.mts → logitron-logger.module-Cd5M_59H.d.mts} +97 -5
  19. package/dist/logitron-logger.module-Cd5M_59H.d.mts.map +1 -0
  20. package/dist/logitron-logger.module-uyg1-Khn.mjs +2285 -0
  21. package/dist/logitron-logger.module-uyg1-Khn.mjs.map +1 -0
  22. package/dist/middleware.d.mts +1 -1
  23. package/dist/middleware.d.mts.map +1 -1
  24. package/dist/middleware.d.ts +1 -1
  25. package/dist/middleware.d.ts.map +1 -1
  26. package/dist/middleware.js +4 -1
  27. package/dist/middleware.js.map +1 -1
  28. package/dist/middleware.mjs +4 -1
  29. package/dist/middleware.mjs.map +1 -1
  30. package/dist/nest.d.mts +4 -44
  31. package/dist/nest.d.mts.map +1 -1
  32. package/dist/nest.d.ts +4 -44
  33. package/dist/nest.d.ts.map +1 -1
  34. package/dist/nest.js +3 -93
  35. package/dist/nest.mjs +3 -92
  36. package/dist/search.d.mts +1 -1
  37. package/dist/search.js +1 -1
  38. package/dist/search.mjs +1 -1
  39. package/dist/testing.d.mts +1 -1
  40. package/dist/testing.d.ts +1 -1
  41. package/dist/{transport.manager-BCnLEmOy.mjs → transport.manager-D3U03fJg.mjs} +21 -11
  42. package/dist/transport.manager-D3U03fJg.mjs.map +1 -0
  43. package/dist/{transport.manager-DU1W0wV3.js → transport.manager-DPjB-dFH.js} +49 -15
  44. package/dist/transport.manager-DPjB-dFH.js.map +1 -0
  45. package/dist/transports.d.mts +3 -1
  46. package/dist/transports.d.mts.map +1 -1
  47. package/dist/transports.d.ts +3 -1
  48. package/dist/transports.d.ts.map +1 -1
  49. package/dist/transports.js +1 -1
  50. package/dist/transports.mjs +1 -1
  51. package/package.json +24 -23
  52. package/dist/build-DOx-YxF1.js +0 -536
  53. package/dist/build-DOx-YxF1.js.map +0 -1
  54. package/dist/build-DWmxA6A2.mjs +0 -536
  55. package/dist/build-DWmxA6A2.mjs.map +0 -1
  56. package/dist/chunk-BTgCAUrQ.js +0 -53
  57. package/dist/chunk-uEZWKkIX.mjs +0 -32
  58. package/dist/esm-1Ra90uql.mjs +0 -4256
  59. package/dist/esm-1Ra90uql.mjs.map +0 -1
  60. package/dist/esm-FNhqFIqG.js +0 -4267
  61. package/dist/esm-FNhqFIqG.js.map +0 -1
  62. package/dist/index-ClPZrfIU.d.ts.map +0 -1
  63. package/dist/index-CthBT3t8.d.mts.map +0 -1
  64. package/dist/lib-8XKCHDOH.mjs +0 -66301
  65. package/dist/lib-8XKCHDOH.mjs.map +0 -1
  66. package/dist/lib-BNEYXXTQ.js +0 -66304
  67. package/dist/lib-BNEYXXTQ.js.map +0 -1
  68. package/dist/logitron-logger.module-DucvDnxZ.mjs +0 -10986
  69. package/dist/logitron-logger.module-DucvDnxZ.mjs.map +0 -1
  70. package/dist/logitron-logger.module-Dx2mUz-g.d.ts.map +0 -1
  71. package/dist/logitron-logger.module-Fof9Er2E.js +0 -11260
  72. package/dist/logitron-logger.module-Fof9Er2E.js.map +0 -1
  73. package/dist/logitron-logger.module-KU_L04y1.d.mts.map +0 -1
  74. package/dist/nest.js.map +0 -1
  75. package/dist/nest.mjs.map +0 -1
  76. package/dist/promise-BI-3eI4n.js +0 -22743
  77. package/dist/promise-BI-3eI4n.js.map +0 -1
  78. package/dist/promise-BrZcjavs.mjs +0 -22740
  79. package/dist/promise-BrZcjavs.mjs.map +0 -1
  80. package/dist/sqlite3-CSkpjC90.js +0 -420
  81. package/dist/sqlite3-CSkpjC90.js.map +0 -1
  82. package/dist/sqlite3-DD2_nRRH.mjs +0 -417
  83. package/dist/sqlite3-DD2_nRRH.mjs.map +0 -1
  84. package/dist/transport.manager-BCnLEmOy.mjs.map +0 -1
  85. package/dist/transport.manager-DU1W0wV3.js.map +0 -1
  86. /package/dist/{search-DanSf_yc.d.mts → search-DN676Dnz.d.mts} +0 -0
  87. /package/dist/{search-1txemGPh.mjs → search-DOvSI-mb.mjs} +0 -0
  88. /package/dist/{search-DeZHhWxB.js → search-DoZF3RZj.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logitron-logger.module-CMDM61Iz.js","names":["AsyncLocalStorage","base: LogContext","current: LogEntry | null","serialized: Record<string, unknown>","out: Record<string, unknown>","_otelApi: OtelApi | null | undefined","_bridgeOptions: OtelBridgeOptions | null","PII_CONSERVATIVE_PATTERNS: readonly RegExp[]","PII_CONSERVATIVE_PATHS: readonly string[]","PII_AGGRESSIVE_PATTERNS: readonly RegExp[]","PII_AGGRESSIVE_PATHS: readonly string[]","result: Record<string, unknown>","native","AsyncLocalStorage","uuidv4","resolvedConfig: TraceIdConfig","traceId: string | undefined","TransportManager","normalized: GracefulShutdownConfig","_AUTO_PALETTE: readonly string[]","traceId","mergedData: typeof data","payload: Record<string, unknown> | undefined","entry: LogEntry","finalEntry: LogEntry | null","LogixiaLoggerService","internalError","errorData: Record<string, string>","Scope","KafkaTraceInterceptor","config?: TraceIdConfig","requireTraceId: boolean","traceId: string | undefined","EMPTY","Observable","TraceMiddleware","config?: TraceIdConfig","traceId: string | undefined","WebSocketTraceInterceptor","config?: TraceIdConfig","traceId: string | undefined","Observable","DEFAULT_ROUTES: RouteInfo[]","RequestMethod","LogixiaLoggerModule","resolvedTraceConfig: TraceIdConfig | undefined","traceConfig","defaultConfig: LoggerConfig"],"sources":["../src/context/async-context.ts","../src/plugin.ts","../src/types/index.ts","../src/utils/error.utils.ts","../src/utils/otel.ts","../src/utils/redact.utils.ts","../src/utils/sampling.utils.ts","../src/utils/shutdown.utils.ts","../node_modules/uuid/dist/esm-node/rng.js","../node_modules/uuid/dist/esm-node/stringify.js","../node_modules/uuid/dist/esm-node/native.js","../node_modules/uuid/dist/esm-node/v4.js","../src/utils/trace.utils.ts","../src/core/logitron-logger.ts","../src/core/logitron-nestjs.service.ts","../src/core/kafka-trace.interceptor.ts","../src/core/trace.middleware.ts","../src/core/websocket-trace.interceptor.ts","../src/core/logitron-logger.module.ts"],"sourcesContent":["/**\n * LogixiaContext — Zero-boilerplate AsyncLocalStorage context propagation.\n *\n * Lets every `logger.info()` call inside a context automatically pick up stored\n * fields (requestId, userId, tenantId, …) without passing them through every function.\n *\n * Works across `Promise.all`, `setTimeout`, and event emitters — anything that\n * inherits the async execution context.\n *\n * @example\n * ```ts\n * import { LogixiaContext } from 'logixia';\n *\n * // Wrap a request handler:\n * app.use((req, res, next) => {\n * LogixiaContext.run({ requestId: req.id, userId: req.user?.id }, next);\n * });\n *\n * // Anywhere deeper in the call tree:\n * async function processOrder(orderId: string) {\n * const ctx = LogixiaContext.get(); // { requestId, userId, orderId }\n * logger.info('Processing order', { orderId, ...ctx });\n * }\n * ```\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { randomUUID } from 'node:crypto';\n\n/** Generates an 8-character random ID using Node's crypto module. */\nfunction randomShortId(): string {\n return randomUUID().slice(0, 8);\n}\n\nexport interface LogContext {\n traceId?: string;\n spanId?: string;\n userId?: string;\n tenantId?: string;\n sessionId?: string;\n [key: string]: unknown;\n}\n\n// Module-level singleton so all loggers share the same ALS store.\nconst _storage = new AsyncLocalStorage<LogContext>();\n\nexport const LogixiaContext = {\n /**\n * Run `callback` inside an async context that carries `store` fields.\n * All loggers queried within `callback` (and its async descendants) will\n * automatically see these fields.\n *\n * @example\n * ```ts\n * LogixiaContext.run({ requestId: 'abc', userId: '42' }, async () => {\n * await processOrder(); // sees requestId + userId in every log\n * });\n * ```\n */\n run<T>(store: LogContext, callback: () => T): T {\n const parent = _storage.getStore() ?? {};\n return _storage.run({ ...parent, ...store }, callback);\n },\n\n /**\n * Return the context fields active in the current async scope.\n * Returns `undefined` when called outside any `LogixiaContext.run()`.\n */\n get(): LogContext | undefined {\n return _storage.getStore();\n },\n\n /**\n * Merge `fields` into the **existing** context for the current scope.\n * If called outside a `run()` context this is a no-op (logs a dev warning).\n */\n set(fields: Partial<LogContext>): void {\n const store = _storage.getStore();\n if (!store) {\n // Silently ignore — avoids crashing in code that may call setContext\n // before a request context is established (e.g. background jobs).\n return;\n }\n Object.assign(store, fields);\n },\n\n /**\n * Retrieve the underlying `AsyncLocalStorage` instance.\n * Useful for advanced use-cases like custom middleware.\n */\n getStorage(): AsyncLocalStorage<LogContext> {\n return _storage;\n },\n};\n\n/**\n * Create an Express/Connect-compatible middleware that wraps each request in\n * a `LogixiaContext.run()` scope populated with common request fields.\n *\n * @example\n * ```ts\n * import { createExpressContextMiddleware } from 'logixia';\n * app.use(createExpressContextMiddleware());\n * ```\n */\nexport function createExpressContextMiddleware(\n options: {\n /** Extract additional fields from the request. */\n enrich?: (req: Record<string, unknown>) => Partial<LogContext>;\n /** Header to read the traceId from. Default: 'x-trace-id'. */\n traceIdHeader?: string;\n } = {}\n) {\n const { enrich, traceIdHeader = 'x-trace-id' } = options;\n\n return function logixiaContextMiddleware(\n req: Record<string, unknown>,\n _res: unknown,\n next: () => void\n ): void {\n const headers = (req['headers'] ?? {}) as Record<string, string | undefined>;\n const traceId = headers[traceIdHeader] ?? randomShortId();\n const base: LogContext = { traceId };\n LogixiaContext.run({ ...base, ...(enrich ? enrich(req) : {}) }, next);\n };\n}\n\n/**\n * Create a Fastify lifecycle hook that wraps each request in a context scope.\n *\n * Register with `fastify.addHook('onRequest', createFastifyContextHook())`.\n */\nexport function createFastifyContextHook(\n options: {\n enrich?: (request: Record<string, unknown>) => Partial<LogContext>;\n traceIdHeader?: string;\n } = {}\n) {\n const { enrich, traceIdHeader = 'x-trace-id' } = options;\n\n return function logixiaFastifyHook(\n request: Record<string, unknown>,\n _reply: unknown,\n done: () => void\n ): void {\n const headers = (request['headers'] ?? {}) as Record<string, string | undefined>;\n const traceId =\n headers[traceIdHeader] ?? (request['id'] as string | undefined) ?? randomShortId();\n const base: LogContext = { traceId };\n LogixiaContext.run({ ...base, ...(enrich ? enrich(request) : {}) }, done);\n };\n}\n","/**\n * logixia — Plugin / Extension API\n *\n * Provides a formal plugin interface so community code and internal extensions\n * can add transports, transformers, and middleware without forking the core.\n *\n * Lifecycle hooks (in call order):\n * 1. `onInit` — called once immediately after `logger.use(plugin)`\n * 2. `onLog` — called for every log entry, post-redaction, pre-transport\n * 3. `onError` — called when a transport write fails\n * 4. `onShutdown` — called when the logger is closed\n *\n * @example Custom plugin\n * ```ts\n * import { LogixiaPlugin, usePlugin } from 'logixia';\n *\n * const sentryPlugin: LogixiaPlugin = {\n * name: 'sentry',\n * onError(err) { Sentry.captureException(err); },\n * onLog(entry) {\n * // enrich every entry with a build tag\n * return { ...entry, payload: { ...entry.payload, buildId: process.env.BUILD_ID } };\n * },\n * };\n *\n * logger.use(sentryPlugin); // per-instance\n * usePlugin(sentryPlugin); // global — applied to all future logger instances\n * ```\n */\n\nimport type { LogEntry } from './types/index';\n\n// ── Plugin interface ──────────────────────────────────────────────────────────\n\nexport interface LogixiaPlugin {\n /**\n * Unique plugin name.\n * logixia uses this to prevent duplicate registration of the same plugin\n * on the same logger instance.\n */\n name: string;\n\n /**\n * Called once immediately after `logger.use(plugin)` is called.\n * Use for one-time setup (open connections, allocate buffers, etc.).\n * Async `onInit` errors are silently swallowed to avoid blocking the logger.\n */\n onInit?(): void | Promise<void>;\n\n /**\n * Called for every log entry **after** PII redaction and **before** transport\n * dispatch. Plugins run in registration order; each receives the (possibly\n * modified) output of the previous plugin.\n *\n * - Return the entry (modified or unchanged) to continue processing.\n * - Return `null` to **drop** the entry — no transport will receive it.\n */\n onLog?(entry: LogEntry): LogEntry | null | Promise<LogEntry | null>;\n\n /**\n * Called when a transport write fails (after any configured retries).\n * Useful for routing transport errors to an alerting or metrics system.\n * Errors thrown inside `onError` are silently swallowed.\n */\n onError?(error: Error, entry?: LogEntry): void | Promise<void>;\n\n /**\n * Called when the logger is closed via `logger.close()` or the process\n * receives a shutdown signal (when `gracefulShutdown` is enabled).\n * Errors thrown inside `onShutdown` are silently swallowed.\n */\n onShutdown?(): void | Promise<void>;\n}\n\n// ── PluginRegistry ────────────────────────────────────────────────────────────\n\n/**\n * Holds an ordered list of `LogixiaPlugin` instances and dispatches lifecycle\n * events to them. One registry is created per logger instance; a global\n * singleton is also exported for process-wide registration.\n */\nexport class PluginRegistry {\n private readonly _plugins: LogixiaPlugin[] = [];\n\n /**\n * Register a plugin. Silently skips if a plugin with the same `name` is\n * already registered on this registry.\n */\n register(plugin: LogixiaPlugin): void {\n if (this._plugins.some((p) => p.name === plugin.name)) return;\n this._plugins.push(plugin);\n if (plugin.onInit) {\n const result = plugin.onInit();\n if (result instanceof Promise) result.catch(() => {});\n }\n }\n\n /** Remove a previously registered plugin by name. No-op if not found. */\n unregister(name: string): void {\n const idx = this._plugins.findIndex((p) => p.name === name);\n if (idx !== -1) this._plugins.splice(idx, 1);\n }\n\n /** Returns `true` if a plugin with the given name is registered. */\n has(name: string): boolean {\n return this._plugins.some((p) => p.name === name);\n }\n\n /** Number of currently registered plugins. */\n get size(): number {\n return this._plugins.length;\n }\n\n /**\n * Run all `onLog` hooks in order.\n * Returns `null` if any plugin cancels the entry; otherwise returns the\n * (possibly transformed) entry.\n */\n async runOnLog(entry: LogEntry): Promise<LogEntry | null> {\n let current: LogEntry | null = entry;\n for (const plugin of this._plugins) {\n if (!plugin.onLog) continue;\n current = await plugin.onLog(current);\n if (current === null) return null;\n }\n return current;\n }\n\n /**\n * Notify all `onError` hooks.\n * Errors thrown inside hooks are swallowed to prevent error cascades.\n */\n async runOnError(error: Error, entry?: LogEntry): Promise<void> {\n for (const plugin of this._plugins) {\n if (plugin.onError) {\n const r = plugin.onError(error, entry);\n if (r instanceof Promise) await r.catch(() => {});\n }\n }\n }\n\n /** Run all `onShutdown` hooks concurrently. Hook errors are swallowed. */\n async runOnShutdown(): Promise<void> {\n await Promise.all(\n this._plugins\n .filter((p) => Boolean(p.onShutdown))\n .map((p) => {\n const r = p.onShutdown!();\n return r instanceof Promise ? r.catch(() => {}) : Promise.resolve();\n })\n );\n }\n}\n\n// ── Global registry ───────────────────────────────────────────────────────────\n\n/**\n * Module-level singleton registry.\n *\n * Plugins registered here are automatically copied into every **new** logger\n * instance created after the registration. Already-created loggers are not\n * retroactively affected — use `logger.use(plugin)` for per-instance control.\n */\nexport const globalPluginRegistry = new PluginRegistry();\n\n/**\n * Register a plugin in the global registry so it applies to every future\n * logger instance.\n *\n * @example\n * ```ts\n * import { usePlugin } from 'logixia';\n *\n * usePlugin({\n * name: 'audit-sink',\n * onLog(entry) {\n * if (entry.level === 'error') auditQueue.push(entry);\n * return entry;\n * },\n * });\n * ```\n */\nexport function usePlugin(plugin: LogixiaPlugin): void {\n globalPluginRegistry.register(plugin);\n}\n","/**\n * Core type definitions for Logitron Logger\n */\n\nimport type { HttpRequest, HttpResponse } from './http.types';\nimport type { TransportConfig } from './transport.types';\n\n// Log levels const object for better flexibility\nexport const LogLevel = {\n ERROR: 'error',\n WARN: 'warn',\n INFO: 'info',\n DEBUG: 'debug',\n TRACE: 'trace',\n VERBOSE: 'verbose',\n} as const;\n\nexport type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];\nexport type LogLevelString = LogLevel | (string & {});\n\n// Predefined color types\nexport type LogColor =\n | 'black'\n | 'red'\n | 'green'\n | 'yellow'\n | 'blue'\n | 'magenta'\n | 'cyan'\n | 'white'\n | 'gray'\n | 'grey'\n | 'brightRed'\n | 'brightGreen'\n | 'brightYellow'\n | 'brightBlue'\n | 'brightMagenta'\n | 'brightCyan'\n | 'brightWhite';\n\n// Predefined field keys that can be enabled/disabled\nexport type LogFieldKey =\n | 'timestamp'\n | 'level'\n | 'appName'\n | 'service'\n | 'traceId'\n | 'message'\n | 'payload'\n | 'timeTaken'\n | 'context'\n | 'userId'\n | 'sessionId'\n | 'environment';\n\n// Environment types\nexport type Environment = 'development' | 'production';\n\n// Trace ID configuration\nexport interface TraceIdExtractorConfig {\n header?: string | string[];\n query?: string | string[];\n body?: string | string[];\n params?: string | string[];\n}\n\nexport interface TraceIdConfig {\n enabled: boolean;\n generator?: () => string;\n contextKey?: string;\n extractor?: TraceIdExtractorConfig;\n /**\n * Name of the response header the middleware writes the resolved traceId\n * into (and that the exception filter echoes back on error responses).\n *\n * Defaults to `'X-Trace-Id'`. Set to `false` to suppress the response\n * header entirely (useful when the caller already supplies one and you\n * don't want to echo it back).\n *\n * @default 'X-Trace-Id'\n */\n responseHeader?: string | false;\n}\n\n// ── Redaction ──────────────────────────────────────────────────────────────────\nexport interface RedactConfig {\n /**\n * Dot-notation field paths to redact. Supports `*` (one segment) and `**` (any depth).\n * @example `['req.headers.authorization', '*.password', 'user.creditCard']`\n */\n paths?: string[];\n /**\n * Regex patterns applied to string values — replaces matches with the censor string.\n * @example `[/Bearer\\s+\\S+/gi, /sk-[a-z0-9]{32,}/gi]`\n */\n patterns?: RegExp[];\n /** Replacement value for redacted content. Default: `\"[REDACTED]\"` */\n censor?: string;\n /**\n * Auto-detect and redact common PII and secret patterns without manual configuration.\n *\n * - `true` / `'conservative'` — JWT tokens, Bearer/API-key strings, common secret field names\n * (`password`, `token`, `secret`, `apiKey`, `authorization`, `credentials`)\n * - `'aggressive'` — everything above **plus** email addresses, credit-card numbers,\n * US SSNs, phone numbers, and IP addresses\n *\n * Auto-detected patterns are applied **in addition to** any explicit `paths` / `patterns`.\n *\n * @example\n * ```ts\n * const logger = createLogger({ redact: { autoDetect: 'aggressive' } });\n * await logger.info('user signed up', { email: 'alice@example.com', password: 'hunter2' });\n * // → email: '[REDACTED]', password: '[REDACTED]'\n * ```\n */\n autoDetect?: boolean | 'conservative' | 'aggressive';\n}\n\n// ── Log Sampling ───────────────────────────────────────────────────────────────\n/**\n * Built-in log sampling / rate limiting.\n *\n * @example\n * ```ts\n * sampling: {\n * rate: 0.1, // log 10% of all entries\n * perLevel: { debug: 0.01 }, // override: debug at 1%\n * maxLogsPerSecond: 500, // hard cap — extras dropped\n * traceConsistent: true, // if a traceId is sampled, keep all logs for that trace\n * }\n * ```\n */\nexport interface SamplingConfig {\n /**\n * Global sample rate for all log levels: 0.0 (drop all) → 1.0 (keep all).\n * Default: 1.0 (no sampling).\n */\n rate?: number;\n /**\n * Per-level rate overrides. Unmentioned levels fall back to `rate`.\n * ERROR and WARN default to 1.0 even when a lower global rate is set,\n * unless you explicitly override them here.\n * @example `{ debug: 0.05, info: 0.5 }`\n */\n perLevel?: Partial<Record<string, number>>;\n /**\n * Hard cap on logs emitted per second across all levels.\n * Excess entries are silently dropped and counted in the sampling stats.\n * Default: unlimited.\n */\n maxLogsPerSecond?: number;\n /**\n * When true, all log entries sharing a traceId are either all kept or all\n * dropped — preventing a sampled trace from having missing log entries.\n * Default: false.\n */\n traceConsistent?: boolean;\n /**\n * Emit a periodic sampling stats entry at this interval (ms).\n * Stats include: sampled, dropped, and rate per level over the window.\n * Set to 0 to disable. Default: 60_000 (60 s).\n */\n statsIntervalMs?: number;\n}\n\n// ── Namespace Levels ───────────────────────────────────────────────────────────\n/**\n * Per-namespace log level overrides.\n * Keys are namespace patterns (dot-separated, `*` wildcard); values are log levels.\n *\n * @example\n * ```ts\n * namespaceLevels: {\n * 'db.*': 'debug', // all db.* namespaces → DEBUG\n * 'http.*': 'warn', // all http.* → WARN only\n * 'payment': 'trace', // payment namespace → TRACE\n * }\n * ```\n * ENV overrides: `LOGIXIA_LEVEL_DB=debug` maps to namespace `db`.\n */\nexport type NamespaceLevels = Record<string, LogLevelString>;\n\n// ── Graceful Shutdown ──────────────────────────────────────────────────────────\nexport interface GracefulShutdownConfig {\n /** Auto-register SIGTERM/SIGINT handlers to flush this logger on exit. Default: false */\n enabled: boolean;\n /** Max ms to wait for transports to flush. Default: 5000 */\n timeout?: number;\n /** Signals to listen on. Default: ['SIGTERM', 'SIGINT'] */\n signals?: NodeJS.Signals[];\n}\n\nexport interface LoggerConfig<TLevels extends Record<string, number> = Record<string, number>> {\n appName?: string;\n environment?: Environment;\n traceId?: boolean | TraceIdConfig;\n format?: {\n timestamp?: boolean;\n colorize?: boolean;\n json?: boolean;\n };\n silent?: boolean;\n levelOptions?:\n | {\n level?: keyof TLevels | LogLevelString;\n levels?: TLevels;\n colors?: Partial<Record<keyof TLevels | LogLevel, LogColor>>;\n }\n | undefined;\n fields?: Partial<Record<LogFieldKey, string | boolean>>;\n /**\n * Built-in log redaction — masks sensitive fields before they reach any transport.\n * @see RedactConfig\n */\n redact?: RedactConfig;\n /**\n * Per-namespace log level overrides.\n * A child logger's context is used as its namespace for matching.\n * ENV vars `LOGIXIA_LEVEL_<NS>=<level>` also apply.\n */\n namespaceLevels?: NamespaceLevels;\n /**\n * Automatically register SIGTERM/SIGINT handlers that flush all transports\n * before the process exits — prevents losing the last N seconds of logs on\n * deployments / restarts.\n */\n gracefulShutdown?: boolean | GracefulShutdownConfig;\n /**\n * Built-in log sampling & rate limiting.\n * Completely missing from all existing loggers — eliminates the need to\n * implement sampling logic in application code.\n */\n sampling?: SamplingConfig;\n /** Transport configuration — console, file, database, analytics, etc. */\n transports?: TransportConfig;\n [key: string]: unknown;\n}\n\n// Base logger interface with standard methods\nexport interface IBaseLogger {\n error(message: string, data?: Record<string, unknown>): Promise<void>;\n error(error: Error, data?: Record<string, unknown>): Promise<void>;\n warn(message: string, data?: Record<string, unknown>): Promise<void>;\n info(message: string, data?: Record<string, unknown>): Promise<void>;\n debug(message: string, data?: Record<string, unknown>): Promise<void>;\n trace(message: string, data?: Record<string, unknown>): Promise<void>;\n verbose(message: string, data?: Record<string, unknown>): Promise<void>;\n logLevel(level: string, message: string, data?: Record<string, unknown>): Promise<void>;\n\n time(label: string): void;\n timeEnd(label: string): Promise<number | undefined>;\n timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T>;\n\n setLevel(level: LogLevel | string): void;\n getLevel(): string;\n setContext(context: string): void;\n getContext(): string | undefined;\n\n // Field Management Methods\n enableField(fieldName: string): void;\n disableField(fieldName: string): void;\n isFieldEnabled(fieldName: string): boolean;\n getFieldState(): Record<string, boolean>;\n resetFieldState(): void;\n\n // Transport Level Management Methods\n enableTransportLevelPrompting(): void;\n disableTransportLevelPrompting(): void;\n setTransportLevels(transportId: string, levels: string[]): void;\n getTransportLevels(transportId: string): string[] | undefined;\n clearTransportLevelPreferences(): void;\n getAvailableTransports(): string[];\n\n child(context: string, data?: Record<string, unknown>): ILogger;\n close(): Promise<void>;\n\n // Plugin API (Feature 20)\n // Typed as unknown to avoid a circular import between types/index.ts and plugin.ts.\n // The concrete LogixiaLogger class constrains this to LogixiaPlugin.\n use(plugin: { name: string }): this;\n unuse(pluginName: string): this;\n}\n\n// Type for custom level methods based on config\nexport type CustomLevelMethods<T extends Record<string, number>> = {\n [K in keyof T]: (message: string, data?: Record<string, unknown>) => Promise<void>;\n};\n\n// Generic logger type that combines base logger with custom level methods\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- empty default is intentional for optional generic\nexport type ILogger<TLevels extends Record<string, number> = {}> = IBaseLogger &\n CustomLevelMethods<TLevels>;\n\n// Default logger interface for backward compatibility\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- marker interface for backward compat\nexport interface ILoggerDefault extends IBaseLogger {}\n\n// Helper type to create logger with specific custom levels\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic type needs any to accept all LoggerConfig shapes\nexport type LoggerWithLevels<T extends LoggerConfig<any>> = T['levelOptions'] extends {\n levels: infer L;\n}\n ? L extends Record<string, number>\n ? ILogger<L>\n : ILoggerDefault\n : ILoggerDefault;\n\n// Helper type to extract levels from config for IntelliSense\nexport type ExtractLevels<T> = T extends LoggerConfig<infer L> ? L : Record<string, number>;\n\n// Log entry interface\nexport interface LogEntry {\n timestamp: string;\n level: string;\n appName: string;\n environment?: string;\n traceId?: string;\n message: string;\n payload?: Record<string, unknown>;\n context?: string;\n error?: Error;\n}\n\n// Error serialization options\nexport interface ErrorSerializationOptions {\n includeStack?: boolean;\n maxDepth?: number;\n excludeFields?: string[];\n}\n\n// Timing entry interface\nexport interface TimingEntry {\n label: string;\n startTime: number;\n endTime?: number;\n duration?: number;\n}\n\n// Context data interface\nexport interface ContextData {\n [key: string]: unknown;\n}\n\n// Log formatter interface\nexport interface ILogFormatter {\n format(entry: LogEntry): string;\n}\n\n// Request context interface for tracking request lifecycle\nexport interface RequestContext {\n traceId: string;\n startTime: number;\n endTime?: number;\n duration?: number;\n request: HttpRequest;\n response?: HttpResponse;\n error?: Error;\n userId?: string;\n sessionId?: string;\n userAgent?: string;\n ip?: string;\n}\n\n// Default log levels with colors\nexport const DEFAULT_LOG_LEVELS = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n trace: 4,\n verbose: 5,\n};\n\nexport const DEFAULT_LOG_COLORS = {\n error: 'red',\n warn: 'yellow',\n info: 'green',\n debug: 'blue',\n trace: 'magenta',\n verbose: 'cyan',\n};\n\n// Additional exports for compatibility\nexport type { LoggerConfig as LoggerConfigInterface };\n\n// Export all HTTP types\nexport * from './http.types';\n","/**\n * Error serialization utilities for Logixia\n *\n * v1.1 improvements:\n * - Full ES2022 `cause` chain serialization (recursive)\n * - AggregateError.errors array serialization\n * - Standard extra fields: code, statusCode, status, errno, syscall, path\n * - Circular-reference safe via a WeakSet seen guard\n */\n\nimport type { ErrorSerializationOptions } from '../types';\n\n// Well-known extra fields to always capture when present on an error\nconst EXTRA_FIELDS = [\n 'code',\n 'statusCode',\n 'status',\n 'errno',\n 'syscall',\n 'path',\n 'address',\n 'port',\n 'type',\n] as const;\n\n/**\n * Serialize an Error into a plain JSON-safe object, including:\n * - `name`, `message`, `stack`\n * - `cause` (recursively serialized, full ES2022 chain)\n * - `errors` (for AggregateError)\n * - standard Node.js error fields (`code`, `statusCode`, `errno`, …)\n * - any other enumerable own properties\n */\nexport function serializeError(\n error: Error,\n options: ErrorSerializationOptions = {}\n): Record<string, unknown> {\n const { includeStack = true, maxDepth = 5, excludeFields = [] } = options;\n const seen = new WeakSet<object>();\n return _serializeError(error, includeStack, maxDepth, excludeFields, 0, seen);\n}\n\nfunction _serializeError(\n error: Error,\n includeStack: boolean,\n maxDepth: number,\n excludeFields: string[],\n depth: number,\n seen: WeakSet<object>\n): Record<string, unknown> {\n if (depth >= maxDepth) {\n return { name: error.name, message: error.message, _truncated: true };\n }\n\n if (seen.has(error)) {\n return { name: error.name, message: error.message, _circular: true };\n }\n seen.add(error);\n\n const serialized: Record<string, unknown> = {\n name: error.name,\n message: error.message,\n };\n\n if (includeStack && error.stack) {\n serialized.stack = error.stack;\n }\n\n // ── ES2022 cause chain ──────────────────────────────────────────────────────\n const errorWithCause = error as Error & { cause?: unknown };\n if (errorWithCause.cause !== undefined) {\n if (errorWithCause.cause instanceof Error) {\n serialized.cause = _serializeError(\n errorWithCause.cause,\n includeStack,\n maxDepth,\n excludeFields,\n depth + 1,\n seen\n );\n } else {\n serialized.cause = serializeValue(errorWithCause.cause, maxDepth - depth - 1);\n }\n }\n\n // ── AggregateError.errors ───────────────────────────────────────────────────\n const aggregateError = error as Error & { errors?: unknown[] };\n if (Array.isArray(aggregateError.errors)) {\n serialized.errors = aggregateError.errors.map((e) =>\n e instanceof Error\n ? _serializeError(e, includeStack, maxDepth, excludeFields, depth + 1, seen)\n : serializeValue(e, maxDepth - depth - 1)\n );\n }\n\n // ── Standard Node.js / HTTP error fields ───────────────────────────────────\n const errorRecord = error as unknown as Record<string, unknown>;\n for (const field of EXTRA_FIELDS) {\n if (!excludeFields.includes(field) && field in error && errorRecord[field] !== undefined) {\n serialized[field] = errorRecord[field];\n }\n }\n\n // ── All other own enumerable properties ────────────────────────────────────\n const skip = new Set<string>([\n 'name',\n 'message',\n 'stack',\n 'cause',\n 'errors',\n ...EXTRA_FIELDS,\n ...excludeFields,\n ]);\n\n for (const key of Object.getOwnPropertyNames(error)) {\n if (skip.has(key)) continue;\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n try {\n serialized[key] = serializeValue(errorRecord[key], maxDepth - depth - 1);\n } catch {\n serialized[key] = '[Unserializable]';\n }\n }\n\n return serialized;\n}\n\n/**\n * Recursively serialize an arbitrary value to a JSON-safe representation.\n */\nfunction serializeValue(value: unknown, remainingDepth: number): unknown {\n if (remainingDepth <= 0) return '[Max Depth]';\n if (value === null || value === undefined) return value;\n\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n\n if (value instanceof Date) return value.toISOString();\n\n if (value instanceof Error) {\n return _serializeError(value, true, remainingDepth, [], 0, new WeakSet());\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => serializeValue(item, remainingDepth - 1));\n }\n\n if (typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n try {\n out[k] = serializeValue(v, remainingDepth - 1);\n } catch {\n out[k] = '[Unserializable]';\n }\n }\n return out;\n }\n\n return String(value);\n}\n\n/**\n * Type guard: returns true if the value looks like an Error object.\n */\nexport function isError(value: unknown): value is Error {\n return (\n value instanceof Error ||\n (Boolean(value) &&\n typeof value === 'object' &&\n 'name' in (value as object) &&\n 'message' in (value as object))\n );\n}\n\n/**\n * Coerce any thrown value into a proper Error instance.\n */\nexport function normalizeError(error: unknown): Error {\n if (isError(error)) return error;\n\n if (typeof error === 'string') return new Error(error);\n\n if (typeof error === 'object' && error !== null) {\n const e = error as Record<string, unknown>;\n const err = new Error(typeof e['message'] === 'string' ? e['message'] : 'Unknown error');\n Object.assign(err, error);\n return err;\n }\n\n return new Error(String(error));\n}\n","/**\n * logixia — OpenTelemetry auto trace-log correlation\n *\n * Zero-config: if `@opentelemetry/api` is installed, logixia automatically\n * reads the active span context and injects `traceId`, `spanId`, and\n * `traceFlags` into every log entry. No manual wiring needed.\n *\n * The integration is completely optional — if `@opentelemetry/api` is absent\n * (or the OTel SDK is not initialised), all helpers return `undefined` silently.\n *\n * Additionally exposes a `createOtelLogExporter()` factory that routes every\n * logixia log entry through the active OTel LoggerProvider (OTLP export).\n *\n * @example Auto bridge (zero config)\n * ```ts\n * import { createLogger } from 'logixia';\n * import { initOtelBridge } from 'logixia';\n *\n * // Call once at app startup, after your OTel SDK is initialised\n * initOtelBridge();\n *\n * const logger = createLogger({ appName: 'api' });\n * // Every logger.info / warn / error call now auto-injects traceId + spanId\n * // from the currently active OTel span — no per-call code needed.\n * ```\n *\n * @example Manual span context injection\n * ```ts\n * import { getActiveOtelContext } from 'logixia';\n *\n * const ctx = getActiveOtelContext();\n * await logger.info('Payment processed', { ...ctx, orderId: 'ord_123' });\n * // → { traceId: 'abc...', spanId: 'def...', traceFlags: 1, orderId: 'ord_123' }\n * ```\n */\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface OtelSpanContext {\n /** W3C 32-hex-char trace ID */\n traceId: string;\n /** W3C 16-hex-char span ID */\n spanId: string;\n /** W3C trace-flags integer (1 = sampled) */\n traceFlags: number;\n /** Whether this context is from a valid, sampled span */\n isSampled: boolean;\n}\n\nexport interface OtelBridgeOptions {\n /**\n * Field name written to log entries for the trace ID.\n * @default 'traceId'\n */\n traceIdField?: string;\n /**\n * Field name written to log entries for the span ID.\n * @default 'spanId'\n */\n spanIdField?: string;\n /**\n * Field name written to log entries for trace flags.\n * @default 'traceFlags'\n */\n traceFlagsField?: string;\n /**\n * Only inject context when the span is sampled (traceFlags bit 1 set).\n * @default false\n */\n sampledOnly?: boolean;\n}\n\n// ── OTel API dynamic import ───────────────────────────────────────────────────\n\ntype OtelApi = {\n context: { active(): unknown };\n trace: {\n getSpanContext(\n ctx: unknown\n ): { traceId: string; spanId: string; traceFlags: number } | undefined;\n isSpanContextValid(sc: { traceId: string; spanId: string; traceFlags: number }): boolean;\n TraceFlags: { SAMPLED: number };\n };\n};\n\nlet _otelApi: OtelApi | null | undefined; // undefined = not yet resolved; null = not available\n\nfunction tryLoadOtelApi(): OtelApi | null {\n if (_otelApi !== undefined) return _otelApi;\n try {\n // Dynamic require so the package stays optional — no hard peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _otelApi = require('@opentelemetry/api') as OtelApi;\n return _otelApi;\n } catch {\n _otelApi = null;\n return null;\n }\n}\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Read the currently active OTel span context (if any) and return its fields\n * in a plain object suitable for spreading into a log entry.\n *\n * Returns `undefined` when:\n * - `@opentelemetry/api` is not installed\n * - No active span exists (root context)\n * - The span context is invalid (all-zeros)\n */\nexport function getActiveOtelContext(opts: OtelBridgeOptions = {}): OtelSpanContext | undefined {\n const api = tryLoadOtelApi();\n if (!api) return undefined;\n\n const ctx = api.context.active();\n const sc = api.trace.getSpanContext(ctx);\n if (!sc || !api.trace.isSpanContextValid(sc)) return undefined;\n\n const isSampled = (sc.traceFlags & api.trace.TraceFlags.SAMPLED) === api.trace.TraceFlags.SAMPLED;\n\n if (opts.sampledOnly && !isSampled) return undefined;\n\n return {\n traceId: sc.traceId,\n spanId: sc.spanId,\n traceFlags: sc.traceFlags,\n isSampled,\n };\n}\n\n/**\n * Returns a metadata object with OTel context fields ready to merge into a log call,\n * using the configured field names.\n *\n * Returns `{}` when no active span exists (safe to spread unconditionally).\n *\n * @example\n * ```ts\n * await logger.info('Payment processed', {\n * ...getOtelMetaFields(),\n * orderId: 'ord_123',\n * });\n * ```\n */\nexport function getOtelMetaFields(opts: OtelBridgeOptions = {}): Record<string, unknown> {\n const { traceIdField = 'traceId', spanIdField = 'spanId', traceFlagsField = 'traceFlags' } = opts;\n\n const ctx = getActiveOtelContext(opts);\n if (!ctx) return {};\n\n return {\n [traceIdField]: ctx.traceId,\n [spanIdField]: ctx.spanId,\n [traceFlagsField]: ctx.traceFlags,\n };\n}\n\n// ── Module-level bridge state ─────────────────────────────────────────────────\n\nlet _bridgeOptions: OtelBridgeOptions | null = null;\n\n/**\n * Initialise the global OTel bridge.\n *\n * Once called, logixia's internal log pipeline checks for an active OTel span\n * on **every** log call and automatically merges the span context into the\n * entry's metadata — no per-call wiring needed.\n *\n * Call once at app startup, **after** the OTel SDK has been initialised:\n * ```ts\n * import { initOtelBridge } from 'logixia';\n * initOtelBridge();\n * ```\n */\nexport function initOtelBridge(opts: OtelBridgeOptions = {}): void {\n _bridgeOptions = opts;\n}\n\n/**\n * @internal\n * Used by the core logger to inject OTel context when the bridge is active.\n * Returns `{}` when the bridge is not initialised or no active span exists.\n */\nexport function _getOtelPayloadIfEnabled(): Record<string, unknown> {\n if (!_bridgeOptions) return {};\n return getOtelMetaFields(_bridgeOptions);\n}\n\n/**\n * Disable the OTel bridge (useful for tests).\n */\nexport function disableOtelBridge(): void {\n _bridgeOptions = null;\n}\n","/**\n * Log redaction utilities for Logixia\n *\n * Solves the #1 logging security problem: sensitive data (PII, secrets, tokens)\n * being logged to transports that shouldn't have them.\n *\n * Addresses Winston issue #1079 \"redacting secrets\" which has been open for years\n * with no built-in solution.\n *\n * @example\n * ```ts\n * const logger = createLogger({\n * redact: {\n * paths: ['req.headers.authorization', '*.password', 'user.creditCard', 'token'],\n * patterns: [/Bearer\\s+\\S+/gi, /sk-[a-z0-9]{32,}/gi],\n * censor: '[REDACTED]',\n * }\n * });\n * ```\n */\n\nimport type { RedactConfig } from '../types';\n\nexport type { RedactConfig };\n\nconst DEFAULT_CENSOR = '[REDACTED]';\n\n// ── Built-in PII / secret patterns ──────────────────────────────────────────\n\n/**\n * Conservative patterns: tokens and secrets that should NEVER appear in logs.\n * Applied when `autoDetect: true` or `autoDetect: 'conservative'`.\n */\nconst PII_CONSERVATIVE_PATTERNS: readonly RegExp[] = [\n // JWT tokens (three base64url segments separated by dots)\n /eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]*/g,\n // Bearer / Token auth header values\n // eslint-disable-next-line sonarjs/duplicates-in-character-class\n /Bearer\\s+[A-Za-z0-9._~+/-]+=*/gi,\n // Generic API key patterns (sk-, pk-, api-, etc.)\n // eslint-disable-next-line sonarjs/duplicates-in-character-class\n /\\b(?:sk|pk|api|key|secret|token)-[A-Za-z0-9_-]{16,}/gi,\n // AWS-style access key IDs and secret access keys\n /\\bAKIA[0-9A-Z]{16}\\b/g,\n // eslint-disable-next-line sonarjs/duplicates-in-character-class\n /\\b[A-Za-z0-9/+]{40}\\b(?=.*aws)/gi,\n];\n\n/**\n * Conservative field-name paths auto-redacted by name regardless of value.\n */\nconst PII_CONSERVATIVE_PATHS: readonly string[] = [\n '**.password',\n '**.passwd',\n '**.secret',\n '**.token',\n '**.apiKey',\n '**.api_key',\n '**.accessToken',\n '**.access_token',\n '**.refreshToken',\n '**.refresh_token',\n '**.authorization',\n '**.credentials',\n '**.privateKey',\n '**.private_key',\n '**.clientSecret',\n '**.client_secret',\n];\n\n/**\n * Aggressive patterns: also catch personal data that could identify a person.\n * Applied when `autoDetect: 'aggressive'`.\n */\nconst PII_AGGRESSIVE_PATTERNS: readonly RegExp[] = [\n // Email addresses\n /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g,\n // US Social Security Numbers (XXX-XX-XXXX or XXXXXXXXX)\n /\\b\\d{3}-?\\d{2}-?\\d{4}\\b/g,\n // Credit / debit card numbers (13-19 digits, optional spaces/dashes)\n /\\b(?:\\d[ -]?){13,19}\\b/g,\n // US phone numbers\n /\\b(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b/g,\n // IPv4 addresses\n /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g,\n];\n\nconst PII_AGGRESSIVE_PATHS: readonly string[] = [\n ...PII_CONSERVATIVE_PATHS,\n '**.email',\n '**.emailAddress',\n '**.email_address',\n '**.phone',\n '**.phoneNumber',\n '**.phone_number',\n '**.mobile',\n '**.ssn',\n '**.sin',\n '**.dob',\n '**.dateOfBirth',\n '**.date_of_birth',\n '**.creditCard',\n '**.credit_card',\n '**.cardNumber',\n '**.card_number',\n '**.cvv',\n '**.cvc',\n '**.ipAddress',\n '**.ip_address',\n];\n\n/**\n * Build an effective config that merges `autoDetect` PII rules into the\n * explicit `paths` and `patterns` the caller provided.\n */\nfunction resolveConfig(config: RedactConfig): RedactConfig {\n const { autoDetect } = config;\n if (!autoDetect) return config;\n\n const aggressive = autoDetect === 'aggressive';\n\n const extraPaths = aggressive ? PII_AGGRESSIVE_PATHS : PII_CONSERVATIVE_PATHS;\n const extraPatterns = aggressive\n ? [...PII_CONSERVATIVE_PATTERNS, ...PII_AGGRESSIVE_PATTERNS]\n : PII_CONSERVATIVE_PATTERNS;\n\n const { paths = [], patterns = [] } = config;\n return {\n ...config,\n paths: [...paths, ...extraPaths],\n patterns: [...patterns, ...extraPatterns],\n };\n}\n\n/**\n * Convert a dot-notation path pattern to a RegExp.\n * Supports `*` (one segment) and `**` (zero or more segments).\n *\n * Examples:\n * \"password\" → /^password$/\n * \"user.password\" → /^user\\.password$/\n * \"*.token\" → /^[^.]+\\.token$/\n * \"req.headers.*\" → /^req\\.headers\\.[^.]+$/\n * \"**\" → /^.*$/\n */\nfunction pathToRegExp(pattern: string): RegExp {\n const regexStr = pattern\n .split('.')\n .map((segment) => {\n if (segment === '**') return '.*';\n if (segment === '*') return '[^.]+';\n // Escape regex special chars in the segment\n return segment.replace(/[$()*+.?[\\\\\\]^{|}]/g, '\\\\$&');\n })\n .join('\\\\.');\n\n return new RegExp(`^${regexStr}$`);\n}\n\n/** Pre-compiled path matchers keyed by pattern string for perf */\nconst patternCache = new Map<string, RegExp>();\n\nfunction matchesPath(fullPath: string, pattern: string): boolean {\n let re = patternCache.get(pattern);\n if (!re) {\n re = pathToRegExp(pattern);\n patternCache.set(pattern, re);\n }\n return re.test(fullPath);\n}\n\n/**\n * Deep-clone and redact an object according to the given RedactConfig.\n * Non-objects are returned as-is (with pattern replacement on strings).\n *\n * The function is intentionally non-mutating — it returns a new object.\n */\nexport function redactObject(\n obj: Record<string, unknown>,\n config: RedactConfig,\n _currentPath = ''\n): Record<string, unknown> {\n const { paths = [], patterns = [], censor = DEFAULT_CENSOR } = config;\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n // Prototype pollution guard: skip forbidden keys that could mutate\n // Object.prototype when assigned via bracket notation on a literal object.\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n continue;\n }\n const fullPath = _currentPath ? `${_currentPath}.${key}` : key;\n\n // ── 1. Path-based redaction ──────────────────────────────────────────────\n if (paths.length > 0 && paths.some((p) => matchesPath(fullPath, p))) {\n result[key] = censor;\n continue;\n }\n\n // ── 2. Recurse into plain objects ────────────────────────────────────────\n if (isPlainObject(value)) {\n result[key] = redactObject(value as Record<string, unknown>, config, fullPath);\n continue;\n }\n\n // ── 3. Pattern-based redaction on string values ──────────────────────────\n if (typeof value === 'string' && patterns.length > 0) {\n let redacted = value;\n for (const pattern of patterns) {\n redacted = redacted.replace(pattern, censor);\n }\n result[key] = redacted;\n continue;\n }\n\n // ── 4. Recurse into arrays ───────────────────────────────────────────────\n if (Array.isArray(value)) {\n result[key] = value.map((item) => {\n if (isPlainObject(item)) {\n return redactObject(item as Record<string, unknown>, config, fullPath);\n }\n if (typeof item === 'string' && patterns.length > 0) {\n return patterns.reduce((s, p) => s.replace(p, censor), item);\n }\n return item;\n });\n continue;\n }\n\n // ── 5. Pass-through ──────────────────────────────────────────────────────\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Apply redaction to a log payload (top-level call convenience wrapper).\n * Returns a new object — never mutates the input.\n */\nexport function applyRedaction(\n payload: Record<string, unknown> | undefined,\n config: RedactConfig | undefined\n): Record<string, unknown> | undefined {\n if (!payload || !config) return payload;\n const resolved = resolveConfig(config);\n if (\n (!resolved.paths || resolved.paths.length === 0) &&\n (!resolved.patterns || resolved.patterns.length === 0)\n ) {\n return payload;\n }\n return redactObject(payload, resolved);\n}\n\nfunction isPlainObject(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date) &&\n !(value instanceof Error) &&\n !(value instanceof RegExp)\n );\n}\n","/**\n * Log sampling & rate-limiting engine.\n *\n * Completely stateless between log calls — all state lives in the Sampler instance\n * so multiple loggers can have independent sampling budgets.\n */\n\nimport type { SamplingConfig } from '../types';\n\n// Safety-critical levels that are NEVER sampled below 100 % by default.\nconst ALWAYS_EMIT_LEVELS = new Set(['error', 'fatal']);\n\n// ── Sampler ───────────────────────────────────────────────────────────────────\n\nexport interface SamplingStats {\n /** Total calls evaluated since last reset. */\n evaluated: number;\n /** Total calls emitted (passed through). */\n emitted: number;\n /** Total calls dropped. */\n dropped: number;\n /** Per-level breakdown. */\n byLevel: Record<string, { evaluated: number; emitted: number }>;\n /** Window start (ms since epoch). */\n windowStart: number;\n}\n\nexport class Sampler {\n private readonly config: SamplingConfig;\n /** Trace IDs that have been sampled in this window → always emit. */\n private readonly sampledTraces = new Set<string>();\n /** Trace IDs that have been dropped in this window → always drop. */\n private readonly droppedTraces = new Set<string>();\n\n // Rate-limiting state\n private _tokenBucket = 0;\n private _lastRefillMs = Date.now();\n\n // Stats\n private _stats: SamplingStats = {\n evaluated: 0,\n emitted: 0,\n dropped: 0,\n byLevel: {},\n windowStart: Date.now(),\n };\n private _statsTimer?: ReturnType<typeof setInterval>;\n\n constructor(config: SamplingConfig, onStats?: (stats: SamplingStats) => void) {\n this.config = config;\n\n // Initialise token bucket to the max cap\n if (config.maxLogsPerSecond && config.maxLogsPerSecond > 0) {\n this._tokenBucket = config.maxLogsPerSecond;\n }\n\n // Periodic stats reporter\n const interval = config.statsIntervalMs ?? 60_000;\n if (interval > 0 && onStats) {\n this._statsTimer = setInterval(() => {\n const snapshot = this.getStats();\n onStats(snapshot);\n this.resetStats();\n }, interval);\n // Don't prevent process exit\n if (this._statsTimer.unref) this._statsTimer.unref();\n }\n }\n\n /**\n * Decide whether a given log entry should be emitted.\n *\n * @param level Log level string (lowercase)\n * @param traceId Active trace ID, if any\n * @returns true → emit, false → drop\n */\n shouldEmit(level: string, traceId?: string): boolean {\n const lvl = level.toLowerCase();\n this._trackEvaluated(lvl);\n\n // ── 1. Safety: error/fatal always pass (unless explicitly overridden) ─────\n if (ALWAYS_EMIT_LEVELS.has(lvl) && this.config.perLevel?.[lvl] === undefined) {\n this._trackEmitted(lvl);\n return true;\n }\n\n // ── 2. Trace-consistent sampling ──────────────────────────────────────────\n if (this.config.traceConsistent && traceId) {\n if (this.sampledTraces.has(traceId)) {\n this._trackEmitted(lvl);\n return true;\n }\n if (this.droppedTraces.has(traceId)) {\n this._trackDropped(lvl);\n return false;\n }\n // First time we see this traceId — make the sampling decision now\n const emit = this._sampleByRate(lvl);\n if (emit) {\n this.sampledTraces.add(traceId);\n } else {\n this.droppedTraces.add(traceId);\n }\n if (emit) this._trackEmitted(lvl);\n else this._trackDropped(lvl);\n return emit;\n }\n\n // ── 3. Rate-based sampling ─────────────────────────────────────────────────\n const emit = this._sampleByRate(lvl);\n if (!emit) {\n this._trackDropped(lvl);\n return false;\n }\n\n // ── 4. Hard cap (token bucket) ────────────────────────────────────────────\n if (this.config.maxLogsPerSecond && this.config.maxLogsPerSecond > 0) {\n if (!this._consumeToken()) {\n this._trackDropped(lvl);\n return false;\n }\n }\n\n this._trackEmitted(lvl);\n return true;\n }\n\n getStats(): SamplingStats {\n return { ...this._stats, byLevel: { ...this._stats.byLevel } };\n }\n\n resetStats(): void {\n this._stats = {\n evaluated: 0,\n emitted: 0,\n dropped: 0,\n byLevel: {},\n windowStart: Date.now(),\n };\n // Clear trace sets so long-running processes don't accumulate unbounded memory\n this.sampledTraces.clear();\n this.droppedTraces.clear();\n }\n\n destroy(): void {\n if (this._statsTimer) clearInterval(this._statsTimer);\n }\n\n // ── Private helpers ─────────────────────────────────────────────────────────\n\n private _sampleByRate(level: string): boolean {\n const rate =\n this.config.perLevel?.[level] ?? this.config.perLevel?.['*'] ?? this.config.rate ?? 1.0;\n\n if (rate >= 1.0) return true;\n if (rate <= 0.0) return false;\n // eslint-disable-next-line sonarjs/pseudo-random -- probabilistic sampling, not security-sensitive\n return Math.random() < rate;\n }\n\n private _consumeToken(): boolean {\n const now = Date.now();\n const elapsed = (now - this._lastRefillMs) / 1000;\n const max = this.config.maxLogsPerSecond!;\n\n // Refill tokens proportional to elapsed time\n this._tokenBucket = Math.min(max, this._tokenBucket + elapsed * max);\n this._lastRefillMs = now;\n\n if (this._tokenBucket >= 1) {\n this._tokenBucket -= 1;\n return true;\n }\n return false;\n }\n\n private _ensure(level: string): void {\n if (!this._stats.byLevel[level]) {\n this._stats.byLevel[level] = { evaluated: 0, emitted: 0 };\n }\n }\n\n private _trackEvaluated(level: string): void {\n this._stats.evaluated++;\n this._ensure(level);\n this._stats.byLevel[level]!.evaluated++;\n }\n\n private _trackEmitted(level: string): void {\n this._stats.emitted++;\n this._ensure(level);\n this._stats.byLevel[level]!.emitted++;\n }\n\n private _trackDropped(_level: string): void {\n this._stats.dropped++;\n }\n}\n","/**\n * Graceful shutdown utilities for Logixia\n *\n * Ensures all in-flight logs are flushed to every transport before the process\n * exits on SIGTERM / SIGINT — solving the \"last N seconds of logs are lost on\n * deployment\" problem reported in Pino issue #2002 and LogDNA issue #15.\n */\n\nexport interface FlushOnExitOptions {\n /** How long (ms) to wait for transports to flush before force-exiting. Default: 5000 */\n timeout?: number;\n /** OS signals that trigger a graceful flush. Default: ['SIGTERM', 'SIGINT'] */\n signals?: NodeJS.Signals[];\n /** Called just before flushing starts — useful for stopping traffic intake */\n beforeFlush?: () => void | Promise<void>;\n /** Called after all loggers have flushed successfully */\n afterFlush?: () => void | Promise<void>;\n}\n\ntype Closeable = { close(): Promise<void> };\n\n/** Module-level registry of all logger instances that have opted into graceful shutdown */\nconst registry = new Set<Closeable>();\nlet shutdownHandlerRegistered = false;\n\n/**\n * Register a logger instance so it is included in the graceful shutdown flush.\n * Called automatically when `gracefulShutdown: true` is set in logger config.\n */\nexport function registerForShutdown(logger: Closeable): void {\n registry.add(logger);\n}\n\n/**\n * Deregister a logger (e.g. after `logger.close()` is called manually).\n */\nexport function deregisterFromShutdown(logger: Closeable): void {\n registry.delete(logger);\n}\n\n/**\n * Register SIGTERM / SIGINT handlers that flush all registered loggers before\n * the process exits. Idempotent — calling multiple times is safe.\n *\n * @example\n * ```ts\n * import { flushOnExit } from 'logixia';\n * flushOnExit({ timeout: 5000 });\n * ```\n */\nexport function flushOnExit(options: FlushOnExitOptions = {}): void {\n if (shutdownHandlerRegistered) return;\n shutdownHandlerRegistered = true;\n\n const { timeout = 5000, signals = ['SIGTERM', 'SIGINT'], beforeFlush, afterFlush } = options;\n\n const handler = async (signal: NodeJS.Signals) => {\n // Force-exit safety net — if flushing hangs, don't block the process forever\n const forceExitTimer = setTimeout(() => {\n process.stderr.write(\n `[logixia] Graceful shutdown timed out after ${timeout}ms on ${signal}. Force-exiting.\\n`\n );\n process.exit(1);\n }, timeout).unref();\n\n try {\n if (beforeFlush) await beforeFlush();\n\n // Flush all registered loggers concurrently, ignoring individual failures\n await Promise.allSettled([...registry].map((logger) => logger.close()));\n\n if (afterFlush) await afterFlush();\n } finally {\n clearTimeout(forceExitTimer);\n process.exit(0);\n }\n };\n\n for (const signal of signals) {\n // Only add if not already handled (avoids double-handling in long-lived processes)\n if (process.listenerCount(signal) === 0) {\n process.on(signal, handler);\n }\n }\n}\n\n/**\n * Remove all graceful shutdown handlers and clear the registry.\n * Primarily useful in tests.\n */\nexport function resetShutdownHandlers(): void {\n registry.clear();\n shutdownHandlerRegistered = false;\n}\n","import crypto from 'crypto';\nconst rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate\n\nlet poolPtr = rnds8Pool.length;\nexport default function rng() {\n if (poolPtr > rnds8Pool.length - 16) {\n crypto.randomFillSync(rnds8Pool);\n poolPtr = 0;\n }\n\n return rnds8Pool.slice(poolPtr, poolPtr += 16);\n}","import validate from './validate.js';\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\n\nconst byteToHex = [];\n\nfor (let i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\n\nexport function unsafeStringify(arr, offset = 0) {\n // Note: Be careful editing this code! It's been tuned for performance\n // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434\n return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];\n}\n\nfunction stringify(arr, offset = 0) {\n const uuid = unsafeStringify(arr, offset); // Consistency check for valid UUID. If this throws, it's likely due to one\n // of the following:\n // - One or more input array values don't map to a hex octet (leading to\n // \"undefined\" in the uuid)\n // - Invalid input values for the RFC `version` or `variant` fields\n\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n\n return uuid;\n}\n\nexport default stringify;","import crypto from 'crypto';\nexport default {\n randomUUID: crypto.randomUUID\n};","import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\n\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n\n options = options || {};\n const rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n\n rnds[6] = rnds[6] & 0x0f | 0x40;\n rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided\n\n if (buf) {\n offset = offset || 0;\n\n for (let i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n\n return buf;\n }\n\n return unsafeStringify(rnds);\n}\n\nexport default v4;","/**\n * Trace ID utilities for Logitron\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport { v4 as uuidv4 } from 'uuid';\n\nimport type { TraceIdConfig, TraceIdExtractorConfig } from '../types';\n\n/** Default key used to store the trace ID in AsyncLocalStorage. */\nexport const TRACE_CONTEXT_KEY = 'traceId' as const;\n\n// ── TraceContext class ────────────────────────────────────────────────────────\n\n/**\n * Singleton that owns the AsyncLocalStorage and the active contextKey.\n *\n * Why a class instead of bare module-level variables?\n * - The contextKey is user-configurable; encapsulating it here means there is\n * exactly one place that reads and writes it.\n * - All helpers (getCurrentTraceId, runWithTraceId, etc.) delegate to this\n * instance, so the key is always in sync without hidden global state.\n * - Easier to test: you can reset the singleton between test cases.\n *\n * Usage (advanced):\n * import { TraceContext } from 'logixia';\n * TraceContext.instance.contextKey // → 'traceId' (or custom)\n * TraceContext.instance.getCurrentTraceId() // same as standalone fn\n */\nexport class TraceContext {\n private static _instance: TraceContext | null = null;\n\n readonly storage = new AsyncLocalStorage<Record<string, unknown>>();\n private _contextKey: string = TRACE_CONTEXT_KEY;\n\n private constructor() {}\n\n /** The process-wide singleton. */\n static get instance(): TraceContext {\n if (!TraceContext._instance) {\n TraceContext._instance = new TraceContext();\n }\n return TraceContext._instance;\n }\n\n /** @internal Reset the singleton (useful in tests). */\n static _reset(): void {\n TraceContext._instance = null;\n }\n\n // ── Key management ──────────────────────────────────────────────────────────\n\n /** The AsyncLocalStorage key that holds the trace ID value. */\n get contextKey(): string {\n return this._contextKey;\n }\n\n /** @internal Called by TraceMiddleware when it boots to register the user's key. */\n setContextKey(key: string): void {\n this._contextKey = key;\n }\n\n // ── Core operations ─────────────────────────────────────────────────────────\n\n /** UUID v4 generator (default). */\n generate(): string {\n return uuidv4();\n }\n\n /** Read the trace ID from the current async context. */\n getCurrentTraceId(): string | undefined {\n return this.storage.getStore()?.[this._contextKey] as string | undefined;\n }\n\n /**\n * Mutate the CURRENT async context in-place.\n *\n * ⚠️ DEPRECATED — unsafe for concurrent requests.\n *\n * Uses `AsyncLocalStorage.enterWith()`, which mutates the current async\n * execution context and every Promise chain spawned from it. In a server\n * processing overlapping requests this can cause a trace ID set for one\n * request to bleed into *other* in-flight requests that share the same\n * async parent (e.g. a module-level setup function or a cached handler).\n *\n * Use {@link run} instead — it scopes the context to a callback so there\n * is no risk of cross-request leakage.\n *\n * @deprecated Use `TraceContext.instance.run(traceId, fn)` — do not call\n * `setTraceId` from request-handling code.\n */\n setTraceId(traceId: string, data?: Record<string, unknown>): void {\n const current = this.storage.getStore() ?? {};\n this.storage.enterWith({ ...current, [this._contextKey]: traceId, ...data });\n }\n\n /** Run `fn` inside a new async context that carries `traceId`. */\n run<T>(traceId: string, fn: () => T, data?: Record<string, unknown>): T {\n return this.storage.run({ [this._contextKey]: traceId, ...data }, fn);\n }\n}\n\n// ── Module-level aliases (backwards-compatible public API) ────────────────────\n\n/**\n * The shared AsyncLocalStorage instance.\n * @deprecated Prefer `TraceContext.instance.storage` for new code.\n */\nexport const traceStorage = TraceContext.instance.storage;\n\n/** Returns the key currently used to store the trace ID in AsyncLocalStorage. */\nexport function getTraceContextKey(): string {\n return TraceContext.instance.contextKey;\n}\n\n/** @internal Called by TraceMiddleware / traceMiddleware() when they boot. */\nexport function _setActiveContextKey(key: string): void {\n TraceContext.instance.setContextKey(key);\n}\n\n/** Generate a UUID v4 trace ID. */\nexport function generateTraceId(): string {\n return TraceContext.instance.generate();\n}\n\n/**\n * Get the current trace ID from async context.\n * Returns `undefined` when called outside a traced request.\n */\nexport function getCurrentTraceId(): string | undefined {\n return TraceContext.instance.getCurrentTraceId();\n}\n\n/**\n * Set trace ID in the CURRENT async context without starting a new one.\n *\n * ⚠️ DEPRECATED — unsafe for concurrent requests.\n *\n * Uses `AsyncLocalStorage.enterWith()`, which mutates the current async\n * execution context and every Promise chain spawned from it. Under load this\n * can cause a trace ID from one request to bleed into others sharing the same\n * async parent.\n *\n * Use {@link runWithTraceId} instead. Migration patterns:\n *\n * **Express / Fastify middleware — before:**\n * ```ts\n * app.use((req, _res, next) => {\n * setTraceId(req.headers['x-trace-id'] ?? generateTraceId());\n * next();\n * });\n * ```\n * **after:**\n * ```ts\n * app.use((req, _res, next) => {\n * runWithTraceId(req.headers['x-trace-id'] ?? generateTraceId(), () => next());\n * });\n * ```\n *\n * **Kafka / queue consumer — before:**\n * ```ts\n * async function handle(msg) {\n * setTraceId(msg.headers['x-trace-id']);\n * await processMessage(msg);\n * }\n * ```\n * **after:**\n * ```ts\n * async function handle(msg) {\n * await runWithTraceId(msg.headers['x-trace-id'], () => processMessage(msg));\n * }\n * ```\n *\n * **Background job — before:**\n * ```ts\n * setTraceId(generateTraceId(), { jobId: job.id });\n * await job.run();\n * ```\n * **after:**\n * ```ts\n * await runWithTraceId(generateTraceId(), () => job.run(), { jobId: job.id });\n * ```\n *\n * @deprecated Use `runWithTraceId(traceId, fn)` — do not call `setTraceId`\n * from request-handling code.\n */\nexport function setTraceId(traceId: string, data?: Record<string, unknown>): void {\n TraceContext.instance.setTraceId(traceId, data);\n}\n\n/** Run `fn` inside a new async context carrying `traceId`. */\nexport function runWithTraceId<T>(traceId: string, fn: () => T, data?: Record<string, unknown>): T {\n return TraceContext.instance.run(traceId, fn, data);\n}\n\n// ── Request extraction ────────────────────────────────────────────────────────\n\n/** Shape that extractTraceId accepts (Express-compatible) */\ninterface RequestLike {\n headers?: Record<string, string | string[] | undefined>;\n query?: Record<string, string | string[] | undefined>;\n body?: Record<string, string | undefined>;\n params?: Record<string, string | undefined>;\n}\n\n/**\n * Coerce an arbitrary value to a non-empty trace ID string, or `undefined`.\n * Rejects empty/whitespace-only strings, non-strings, and non-first array elements.\n */\nfunction toValidTraceId(value: unknown): string | undefined {\n const first = Array.isArray(value) ? value[0] : value;\n if (typeof first !== 'string') return undefined;\n const trimmed = first.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\n/** Extract trace ID from request using configuration (header → query → body → params). */\nexport function extractTraceId(\n request: unknown,\n config: TraceIdExtractorConfig\n): string | undefined {\n const req = request as RequestLike;\n\n if (config.header) {\n const headers = Array.isArray(config.header) ? config.header : [config.header];\n for (const header of headers) {\n const value = toValidTraceId(req.headers?.[header.toLowerCase()]);\n if (value) return value;\n }\n }\n\n if (config.query) {\n const queries = Array.isArray(config.query) ? config.query : [config.query];\n for (const query of queries) {\n const value = toValidTraceId(req.query?.[query]);\n if (value) return value;\n }\n }\n\n if (config.body) {\n const bodyFields = Array.isArray(config.body) ? config.body : [config.body];\n for (const field of bodyFields) {\n const value = toValidTraceId(req.body?.[field]);\n if (value) return value;\n }\n }\n\n if (config.params) {\n const paramFields = Array.isArray(config.params) ? config.params : [config.params];\n for (const param of paramFields) {\n const value = toValidTraceId(req.params?.[param]);\n if (value) return value;\n }\n }\n\n return undefined;\n}\n\n/**\n * Default headers checked for incoming trace ID propagation, in priority order:\n * traceparent (W3C/OTel) → x-trace-id → x-request-id → x-correlation-id → trace-id\n */\nexport const DEFAULT_TRACE_HEADERS = [\n 'traceparent',\n 'x-trace-id',\n 'x-request-id',\n 'x-correlation-id',\n 'trace-id',\n];\n\n/**\n * Create trace ID middleware for Express/NestJS\n */\nexport function createTraceMiddleware(config: TraceIdConfig) {\n const defaultExtractor = {\n header: DEFAULT_TRACE_HEADERS,\n query: ['traceId', 'trace_id'],\n };\n const resolvedConfig: TraceIdConfig = {\n ...config,\n extractor: config?.extractor ? { ...defaultExtractor, ...config.extractor } : defaultExtractor,\n };\n\n _setActiveContextKey(resolvedConfig.contextKey ?? TRACE_CONTEXT_KEY);\n\n return (req: unknown, res: unknown, next: () => void) => {\n let traceId: string | undefined;\n\n if (resolvedConfig.extractor) {\n traceId = extractTraceId(req, resolvedConfig.extractor);\n }\n\n if (!traceId) {\n traceId = resolvedConfig.generator ? resolvedConfig.generator() : generateTraceId();\n }\n\n (req as Record<string, unknown>).traceId = traceId;\n (res as { setHeader: (k: string, v: string) => void }).setHeader('X-Trace-Id', traceId);\n\n runWithTraceId(traceId, () => next());\n };\n}\n","/**\n * Core Logixia Logger implementation\n *\n * v1.1 additions:\n * - Feature 1: Graceful shutdown / flushOnExit (auto-registered via config)\n * - Feature 2: Built-in log redaction (path-based + regex)\n * - Feature 3: Per-namespace log levels + LOGIXIA_LEVEL* ENV overrides\n * - Feature 4: Cause-chain error serialization (handled in error.utils)\n * - Feature 5: Adaptive log level based on NODE_ENV / CI detection\n */\n\nimport buildFastStringify from 'fast-json-stringify';\n\n// ── Module-level JSON serializer (built once, reused for all loggers) ─────────\n// Covers the fixed LogEntry shape; unknown payload fields handled by additionalProperties\nconst _fastStringifyEntry = buildFastStringify({\n type: 'object',\n properties: {\n timestamp: { type: 'string' },\n level: { type: 'string' },\n appName: { type: 'string' },\n environment: { type: 'string' },\n message: { type: 'string' },\n context: { type: 'string' },\n traceId: { type: 'string' },\n payload: { type: 'object', additionalProperties: true },\n },\n additionalProperties: true,\n});\n\nimport { LogixiaContext } from '../context/async-context';\nimport type { LogixiaPlugin } from '../plugin';\nimport { globalPluginRegistry, PluginRegistry } from '../plugin';\nimport { TransportManager } from '../transports/transport.manager';\nimport type {\n ContextData,\n GracefulShutdownConfig,\n ILogger,\n ILoggerDefault,\n LogEntry,\n LoggerConfig,\n LoggerWithLevels,\n LogLevelString,\n TimingEntry,\n} from '../types';\nimport { LogLevel } from '../types';\nimport { isError, serializeError } from '../utils/error.utils';\nimport { internalError, internalLog, internalWarn } from '../utils/internal-log';\nimport { _getOtelPayloadIfEnabled } from '../utils/otel';\nimport { applyRedaction } from '../utils/redact.utils';\nimport { Sampler } from '../utils/sampling.utils';\nimport { deregisterFromShutdown, flushOnExit, registerForShutdown } from '../utils/shutdown.utils';\nimport { TraceContext } from '../utils/trace.utils';\n\n// ── Namespace level helpers ──────────────────────────────────────────────────\n\nfunction namespacePatternToRegex(pattern: string): RegExp {\n const escaped = pattern\n .split('.')\n .map((s) => (s === '*' ? '[^.]+' : s.replace(/[$()*+.?[\\\\\\]^{|}]/g, '\\\\$&')))\n .join('\\\\.');\n return new RegExp(`^${escaped}$`);\n}\n\n/** Max compiled patterns to keep in memory. Oldest entry is evicted when full. */\nconst _NS_CACHE_MAX = 1000;\nconst _nsPatternCache = new Map<string, RegExp>();\n/**\n * One-shot warning so operators notice runaway pattern growth.\n * If the cache is being thrashed (>N evictions) the namespace level config is\n * almost certainly wrong — likely dynamic/unique patterns being registered.\n */\nlet _nsCacheEvictionWarned = false;\nlet _nsCacheEvictionCount = 0;\n\nfunction matchesNamespacePattern(ns: string, pattern: string): boolean {\n let re = _nsPatternCache.get(pattern);\n if (!re) {\n re = namespacePatternToRegex(pattern);\n // Evict the oldest entry when the cache hits its limit\n if (_nsPatternCache.size >= _NS_CACHE_MAX) {\n const firstKey = _nsPatternCache.keys().next().value;\n if (firstKey !== undefined) _nsPatternCache.delete(firstKey);\n _nsCacheEvictionCount++;\n if (!_nsCacheEvictionWarned) {\n _nsCacheEvictionWarned = true;\n process.stderr.write(\n `[logixia] namespace pattern cache hit ${_NS_CACHE_MAX} entries — evicting. ` +\n `This usually means dynamic/unique patterns are being registered at runtime; ` +\n `review your levelOptions.namespaces configuration.\\n`\n );\n }\n }\n _nsPatternCache.set(pattern, re);\n }\n return re.test(ns);\n}\n\n/** @internal Test helper to inspect/reset namespace pattern cache state. */\nexport const _nsCacheInternal = {\n size: (): number => _nsPatternCache.size,\n evictionCount: (): number => _nsCacheEvictionCount,\n reset: (): void => {\n _nsPatternCache.clear();\n _nsCacheEvictionCount = 0;\n _nsCacheEvictionWarned = false;\n },\n};\n\n// ── Feature 5: Adaptive level resolution ────────────────────────────────────\n\nfunction resolveInitialLevel(config: LoggerConfig): LogLevelString {\n // 1. Hard env override\n const envLevel = process.env['LOGIXIA_LEVEL'];\n if (envLevel) return envLevel as LogLevelString;\n\n // 2. Explicit config value\n if (config.levelOptions?.level) return config.levelOptions.level as LogLevelString;\n\n // 3. NODE_ENV smart defaults\n const nodeEnv = process.env['NODE_ENV'];\n if (nodeEnv === 'development') return LogLevel.DEBUG;\n if (nodeEnv === 'test') return LogLevel.WARN;\n if (nodeEnv === 'production') return LogLevel.INFO;\n\n // 4. CI detection\n if (process.env['CI']) return LogLevel.INFO;\n\n return LogLevel.INFO;\n}\n\n// ── Logger class ─────────────────────────────────────────────────────────────\n\nexport class LogixiaLogger<\n TConfig extends LoggerConfig<Record<string, number>> = LoggerConfig,\n> implements ILoggerDefault {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- index signature for dynamic custom level methods\n [K: string]: any;\n\n private config: TConfig;\n private context?: string;\n private timers: Map<string, TimingEntry> = new Map();\n private contextData: ContextData = {};\n private transportManager?: TransportManager;\n private fieldState: Map<string, boolean> = new Map();\n\n /** Stable fallback trace ID generated ONCE per logger instance. */\n private readonly fallbackTraceId: string = TraceContext.instance.generate();\n\n // ── Performance: hot-path caches (rebuilt when config changes) ───────────────\n /** Numeric value for each known log level — built once, read on every log call. */\n private _levelValues: Map<string, number> = new Map();\n /** Numeric threshold for the currently active log level. */\n private _minLevelValue = 2;\n /** Pre-built ANSI color codes to avoid recreating the object in colorize(). */\n private _colorMap: Map<string, string> = new Map();\n /** Pre-computed field-enabled booleans so isFieldEnabled() isn't called per log. */\n private _fieldCache: Map<string, boolean> = new Map();\n /** True when contextData is non-empty — avoids Object.keys() check on hot path. */\n private _hasContextData = false;\n /**\n * Pre-computed `[INFO] `, `[WARN] ` etc. strings — avoids a colorize() call per log.\n * Key: lowercase level name. Value: formatted bracket string ready to concatenate.\n */\n private _formattedLevels: Map<string, string> = new Map();\n /** Pre-computed `[appName] ` string — avoids template literal allocation per log. */\n private _formattedAppName = '';\n /** True when a redact config is present — short-circuits applyRedaction when false. */\n private _hasRedact = false;\n /** Sampling engine — only created when sampling config is present. */\n private _sampler?: Sampler;\n /** Per-instance plugin registry — also inherits from the global registry at creation time. */\n private readonly _pluginRegistry = new PluginRegistry();\n\n constructor(config: TConfig, context?: string) {\n const defaultConfig: LoggerConfig = {\n appName: 'App',\n environment: 'development',\n traceId: true,\n format: { timestamp: true, colorize: true, json: false },\n silent: false,\n levelOptions: {\n level: LogLevel.INFO,\n levels: {\n [LogLevel.ERROR]: 0,\n [LogLevel.WARN]: 1,\n [LogLevel.INFO]: 2,\n [LogLevel.DEBUG]: 3,\n [LogLevel.TRACE]: 4,\n [LogLevel.VERBOSE]: 5,\n },\n colors: {\n [LogLevel.ERROR]: 'red',\n [LogLevel.WARN]: 'yellow',\n [LogLevel.INFO]: 'blue',\n [LogLevel.DEBUG]: 'green',\n [LogLevel.TRACE]: 'gray',\n [LogLevel.VERBOSE]: 'cyan',\n },\n },\n };\n\n this.config = { ...defaultConfig, ...config };\n\n // ── Feature 5: Adaptive log level ───────────────────────────────────────\n const resolvedLevel = resolveInitialLevel(this.config);\n this.config.levelOptions = { ...this.config.levelOptions, level: resolvedLevel };\n\n if (!this.config.fields) {\n this.config.fields = {\n timestamp: '[yyyy-mm-dd HH:MM:ss.MS]',\n level: '[log_level]',\n appName: '[app_name]',\n traceId: '[trace_id]',\n message: '[message]',\n payload: '[payload]',\n timeTaken: '[time_taken_MS]',\n };\n }\n\n this.context = context ?? '';\n\n if (this.config.transports) {\n // Inject levelOptions.colors into the console transport config so\n // ConsoleTransport can colorize custom levels (e.g. kafka, mongo)\n // using the same colors the user configured on the logger.\n const transportsWithColors = { ...this.config.transports };\n const userColors = this.config.levelOptions?.colors as Record<string, string> | undefined;\n if (userColors && transportsWithColors.console) {\n const consoleBase =\n typeof transportsWithColors.console === 'object' ? transportsWithColors.console : {};\n transportsWithColors.console = { ...consoleBase, levelColors: userColors };\n }\n this.transportManager = new TransportManager(transportsWithColors);\n }\n\n // ── Feature 8: Log sampling ───────────────────────────────────────────────\n if (this.config.sampling) {\n this._sampler = new Sampler(this.config.sampling, (stats) => {\n // Emit sampling stats as an INFO log via direct stdout to avoid recursion\n process.stdout.write(\n JSON.stringify({\n level: 'info',\n message: '[logixia/sampling] stats',\n ...stats,\n }) + '\\n'\n );\n });\n }\n\n // ── Feature 1: Graceful shutdown ─────────────────────────────────────────\n this.setupGracefulShutdown();\n\n this.createCustomLevelMethods();\n\n // ── Feature 20: Seed with any plugins already in the global registry ─────\n // Plugins registered via usePlugin() before this logger was created are\n // automatically included in this instance's registry.\n for (const p of (globalPluginRegistry as unknown as { _plugins: LogixiaPlugin[] })._plugins) {\n this._pluginRegistry.register(p);\n }\n\n // ── Build hot-path caches after all config is finalised ──────────────────\n this._buildPerfCaches();\n }\n\n // ── Feature 1 ────────────────────────────────────────────────────────────────\n\n private setupGracefulShutdown(): void {\n const shutdownCfg = this.config.gracefulShutdown;\n if (!shutdownCfg) return;\n\n const normalized: GracefulShutdownConfig =\n shutdownCfg === true ? { enabled: true } : (shutdownCfg as GracefulShutdownConfig);\n\n if (!normalized.enabled) return;\n\n registerForShutdown(this);\n flushOnExit({\n timeout: normalized.timeout ?? 5000,\n signals: normalized.signals ?? ['SIGTERM', 'SIGINT'],\n });\n }\n\n private createCustomLevelMethods(): void {\n if (this.config.levelOptions?.levels) {\n for (const levelName of Object.keys(this.config.levelOptions.levels)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!(this as any)[levelName.toLowerCase()]) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this as any)[levelName.toLowerCase()] = async (\n message: string,\n data?: Record<string, unknown>\n ) => {\n await this.logLevel(levelName.toLowerCase(), message, data);\n };\n }\n }\n }\n }\n\n /**\n * Rebuild all hot-path caches after any config mutation or level change.\n * Keeps the actual log() call free of allocations in the common case.\n */\n private _buildPerfCaches(): void {\n // 1. Level value map — pre-merge built-ins + custom levels once\n const customLevelEntries = Object.entries(\n (this.config.levelOptions?.levels ?? {}) as Record<string, number>\n );\n this._levelValues = new Map<string, number>([\n [LogLevel.ERROR, 0],\n [LogLevel.WARN, 1],\n [LogLevel.INFO, 2],\n [LogLevel.DEBUG, 3],\n [LogLevel.TRACE, 4],\n [LogLevel.VERBOSE, 5],\n ...customLevelEntries,\n ]);\n const effectiveLevel = this.resolveEffectiveLevel();\n this._minLevelValue = this._levelValues.get(effectiveLevel) ?? 2;\n\n // 2. ANSI color map — static, built once\n this._colorMap = new Map([\n ['black', '\\x1b[30m'],\n ['red', '\\x1b[31m'],\n ['green', '\\x1b[32m'],\n ['yellow', '\\x1b[33m'],\n ['blue', '\\x1b[34m'],\n ['magenta', '\\x1b[35m'],\n ['cyan', '\\x1b[36m'],\n ['white', '\\x1b[37m'],\n ['gray', '\\x1b[90m'],\n ['grey', '\\x1b[90m'],\n ['brightred', '\\x1b[91m'],\n ['brightgreen', '\\x1b[92m'],\n ['brightyellow', '\\x1b[93m'],\n ['brightblue', '\\x1b[94m'],\n ['brightmagenta', '\\x1b[95m'],\n ['brightcyan', '\\x1b[96m'],\n ['brightwhite', '\\x1b[97m'],\n ['reset', '\\x1b[0m'],\n ]);\n\n // 3. Field enabled cache — replaces per-call isFieldEnabled() calls\n const fieldNames = [\n 'timestamp',\n 'level',\n 'appName',\n 'traceId',\n 'context',\n 'message',\n 'payload',\n ];\n for (const f of fieldNames) {\n if (this.fieldState.has(f)) {\n this._fieldCache.set(f, this.fieldState.get(f)!);\n } else if (this.config.fields?.[f as keyof typeof this.config.fields] !== undefined) {\n this._fieldCache.set(f, this.config.fields[f as keyof typeof this.config.fields] !== false);\n } else {\n this._fieldCache.set(f, true);\n }\n }\n\n // 4. Pre-computed formatted level strings (e.g. \"[INFO] \" with ANSI codes baked in)\n // Eliminates a colorize() call + template literal allocation on every log call.\n const colorize = this.config.format?.colorize ?? true;\n this._formattedLevels = new Map();\n\n // Palette cycled for custom levels that have no explicit color configured.\n // Skips 'white' because it looks identical to the terminal default (appears \"uncolored\").\n const _AUTO_PALETTE: readonly string[] = ['magenta', 'cyan', 'yellow', 'green', 'blue'];\n const _BUILTIN_LEVELS = new Set(['error', 'warn', 'info', 'debug', 'trace', 'verbose']);\n let _paletteIdx = 0;\n\n for (const [lvl] of this._levelValues) {\n const upper = lvl.toUpperCase();\n if (colorize && this._fieldCache.get('level') !== false) {\n let colorName = this.config.levelOptions?.colors?.[lvl] as string | undefined;\n if (!colorName) {\n if (_BUILTIN_LEVELS.has(lvl)) {\n colorName = 'white'; // built-in fallback (shouldn't happen with default config)\n } else {\n // Custom level — auto-assign a distinctive color from the palette\n colorName = _AUTO_PALETTE[_paletteIdx % _AUTO_PALETTE.length]!;\n _paletteIdx++;\n }\n }\n const code = this._colorMap.get(colorName.toLowerCase()) ?? this._colorMap.get('white')!;\n const reset = this._colorMap.get('reset')!;\n this._formattedLevels.set(lvl, `[${code}${upper}${reset}] `);\n } else {\n this._formattedLevels.set(lvl, `[${upper}] `);\n }\n }\n\n // 5. Pre-computed \"[appName] \" string (gray when colorize is on)\n const appNameRaw = `[${this.config.appName ?? 'App'}]`;\n if (this._fieldCache.get('appName') !== false) {\n this._formattedAppName = colorize\n ? `${this._colorMap.get('gray')!}${appNameRaw}${this._colorMap.get('reset')!} `\n : `${appNameRaw} `;\n } else {\n this._formattedAppName = '';\n }\n\n // 6. Redact flag — if no redact config, skip applyRedaction entirely\n this._hasRedact = !!(\n this.config.redact &&\n ((this.config.redact.paths?.length ?? 0) > 0 ||\n (this.config.redact.patterns?.length ?? 0) > 0)\n );\n }\n\n // ── Public logging API ───────────────────────────────────────────────────────\n\n async error(messageOrError: string | Error, data?: Record<string, unknown>): Promise<void> {\n if (isError(messageOrError)) {\n await this.log('error', messageOrError.message, {\n ...data,\n error: serializeError(messageOrError),\n });\n } else {\n await this.log('error', messageOrError, data);\n }\n }\n\n async warn(message: string, data?: Record<string, unknown>): Promise<void> {\n await this.log('warn', message, data);\n }\n\n async info(message: string, data?: Record<string, unknown>): Promise<void> {\n await this.log('info', message, data);\n }\n\n async debug(message: string, data?: Record<string, unknown>): Promise<void> {\n await this.log('debug', message, data);\n }\n\n async trace(message: string, data?: Record<string, unknown>): Promise<void> {\n await this.log('trace', message, data);\n }\n\n async verbose(message: string, data?: Record<string, unknown>): Promise<void> {\n await this.log('verbose', message, data);\n }\n\n async logLevel(level: string, message: string, data?: Record<string, unknown>): Promise<void> {\n await this.log(level, message, data);\n }\n\n // ── Timer API ────────────────────────────────────────────────────────────────\n\n time(label: string): void {\n this.timers.set(label, { label, startTime: Date.now() });\n }\n\n async timeEnd(label: string): Promise<number | undefined> {\n const timer = this.timers.get(label);\n if (!timer) {\n await this.warn(`Timer '${label}' does not exist`);\n return undefined;\n }\n const endTime = Date.now();\n const duration = endTime - timer.startTime;\n timer.endTime = endTime;\n timer.duration = duration;\n await this.info(`Timer '${label}' finished`, {\n duration: `${duration}ms`,\n startTime: new Date(timer.startTime).toISOString(),\n endTime: new Date(endTime).toISOString(),\n });\n this.timers.delete(label);\n return duration;\n }\n\n async timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T> {\n this.time(label);\n try {\n const result = await fn();\n await this.timeEnd(label);\n return result;\n } catch (error) {\n await this.timeEnd(label);\n throw error;\n }\n }\n\n // ── Level & context management ───────────────────────────────────────────────\n\n setLevel(level: LogLevelString): void {\n this.config.levelOptions = this.config.levelOptions ?? {};\n this.config.levelOptions.level = level as string;\n // Refresh the cached numeric threshold so shouldLog() stays accurate\n this._minLevelValue = this._levelValues.get(level) ?? this._minLevelValue;\n // Rebuild level string cache in case colours are keyed per-level\n this._buildPerfCaches();\n }\n\n getLevel(): LogLevelString {\n return (this.config.levelOptions?.level as LogLevelString) ?? LogLevel.INFO;\n }\n\n setContext(context: string): void {\n this.context = context;\n }\n\n getContext(): string | undefined {\n return this.context;\n }\n\n // ── Field management ─────────────────────────────────────────────────────────\n\n enableField(fieldName: string): void {\n this.fieldState.set(fieldName, true);\n this._fieldCache.set(fieldName, true);\n // Rebuild derivative caches that depend on field visibility\n if (fieldName === 'level' || fieldName === 'appName') this._buildPerfCaches();\n internalLog(`Field '${fieldName}' enabled`);\n }\n\n disableField(fieldName: string): void {\n this.fieldState.set(fieldName, false);\n this._fieldCache.set(fieldName, false);\n // Rebuild derivative caches that depend on field visibility\n if (fieldName === 'level' || fieldName === 'appName') this._buildPerfCaches();\n internalLog(`Field '${fieldName}' disabled`);\n }\n\n isFieldEnabled(fieldName: string): boolean {\n if (this.fieldState.has(fieldName)) return this.fieldState.get(fieldName)!;\n if (this.config.fields?.[fieldName as keyof typeof this.config.fields] !== undefined) {\n return this.config.fields[fieldName as keyof typeof this.config.fields] !== false;\n }\n return true;\n }\n\n getFieldState(): Record<string, boolean> {\n const allFields = [\n 'timestamp',\n 'level',\n 'appName',\n 'service',\n 'traceId',\n 'message',\n 'payload',\n 'timeTaken',\n 'context',\n 'userId',\n 'sessionId',\n 'environment',\n ];\n return Object.fromEntries(allFields.map((f) => [f, this.isFieldEnabled(f)]));\n }\n\n resetFieldState(): void {\n this.fieldState.clear();\n internalLog('Field state reset to configuration defaults');\n }\n\n // ── Transport level management ───────────────────────────────────────────────\n\n enableTransportLevelPrompting(): void {\n if (this.transportManager) this.transportManager.enableLevelPrompting();\n else internalWarn('Transport manager not initialized');\n }\n\n disableTransportLevelPrompting(): void {\n if (this.transportManager) this.transportManager.disableLevelPrompting();\n else internalWarn('Transport manager not initialized');\n }\n\n setTransportLevels(transportId: string, levels: string[]): void {\n if (this.transportManager) this.transportManager.setTransportLevels(transportId, levels);\n else internalWarn('Transport manager not initialized');\n }\n\n getTransportLevels(transportId: string): string[] | undefined {\n if (this.transportManager) return this.transportManager.getTransportLevels(transportId);\n internalWarn('Transport manager not initialized');\n return undefined;\n }\n\n clearTransportLevelPreferences(): void {\n if (this.transportManager) this.transportManager.clearTransportLevelPreferences();\n else internalWarn('Transport manager not initialized');\n }\n\n getAvailableTransports(): string[] {\n return this.transportManager?.getTransports() ?? [];\n }\n\n // ── Child logger ─────────────────────────────────────────────────────────────\n\n child(context: string, data?: Record<string, unknown>): ILogger {\n const childLogger = new LogixiaLogger(this.config, context);\n if (data) childLogger.contextData = { ...this.contextData, ...data };\n return childLogger;\n }\n\n // ── Feature 20: Plugin API ────────────────────────────────────────────────────\n\n /**\n * Register a plugin on this logger instance.\n *\n * @example\n * ```ts\n * logger.use({\n * name: 'audit',\n * onLog(entry) { auditQueue.push(entry); return entry; },\n * });\n * ```\n */\n use(plugin: LogixiaPlugin): this {\n this._pluginRegistry.register(plugin);\n return this;\n }\n\n /**\n * Remove a previously registered plugin by name.\n * No-op if the plugin is not registered on this instance.\n */\n unuse(pluginName: string): this {\n this._pluginRegistry.unregister(pluginName);\n return this;\n }\n\n // ── Flush / health / close ───────────────────────────────────────────────────\n\n async flush(): Promise<void> {\n if (this.transportManager) await this.transportManager.flush();\n }\n\n async healthCheck(): Promise<{ healthy: boolean; details: Record<string, unknown> }> {\n if (!this.transportManager) {\n return { healthy: false, details: { error: 'TransportManager not initialized' } };\n }\n return this.transportManager.healthCheck();\n }\n\n async close(): Promise<void> {\n for (const [label, timer] of this.timers) {\n await this.warn(`Timer '${label}' was not ended properly`, {\n startTime: new Date(timer.startTime).toISOString(),\n duration: `${Date.now() - timer.startTime}ms (incomplete)`,\n });\n }\n this.timers.clear();\n\n if (this.transportManager) {\n await this.transportManager.flush();\n await this.transportManager.close();\n }\n\n this._sampler?.destroy();\n await this._pluginRegistry.runOnShutdown();\n deregisterFromShutdown(this);\n }\n\n // ── Core log method ──────────────────────────────────────────────────────────\n\n private async log(level: string, message: string, data?: Record<string, unknown>): Promise<void> {\n if (this.config.silent) return;\n if (!this.shouldLog(level)) return;\n\n // ── Feature 8: Sampling ───────────────────────────────────────────────────\n if (this._sampler) {\n const traceId = this.config.traceId\n ? (TraceContext.instance.getCurrentTraceId() ?? this.fallbackTraceId)\n : undefined;\n if (!this._sampler.shouldEmit(level, traceId)) return;\n }\n\n // ── Feature 6: AsyncLocalStorage context auto-merge ───────────────────────\n // Merge ALS-stored fields (requestId, userId, …) into the payload so every\n // log call inside a LogixiaContext.run() scope automatically carries them.\n const alsContext = LogixiaContext.get();\n // ── Feature 14: OTel auto trace-log correlation ────────────────────────────\n // If initOtelBridge() was called, read the active OTel span and merge its\n // context fields (traceId, spanId, traceFlags) into the payload automatically.\n const otelFields = _getOtelPayloadIfEnabled();\n const hasOtel = Object.keys(otelFields).length > 0;\n let mergedData: typeof data;\n if (alsContext && Object.keys(alsContext).length > 0) {\n mergedData = { ...alsContext, ...(hasOtel ? otelFields : {}), ...data };\n } else if (hasOtel) {\n mergedData = { ...otelFields, ...data };\n } else {\n mergedData = data;\n }\n\n // ── Feature 2: Redaction ─────────────────────────────────────────────────\n // Avoid spread allocation when contextData is empty (the common case)\n const rawPayload = this._hasContextData ? { ...this.contextData, ...mergedData } : mergedData;\n // _hasRedact is pre-computed in _buildPerfCaches() — skips the entire redaction\n // code path when no redact config is present (fast path for the common case).\n let payload: Record<string, unknown> | undefined;\n if (rawPayload !== undefined && rawPayload !== null) {\n payload = this._hasRedact\n ? (applyRedaction(rawPayload, this.config.redact) ?? rawPayload)\n : rawPayload;\n }\n\n // Use a monomorphic LogEntry shape — always the same fields, some may be undefined.\n // Consistent shape lets V8 inline-cache property accesses across calls.\n const traceId = this.config.traceId\n ? (TraceContext.instance.getCurrentTraceId() ?? this.fallbackTraceId)\n : undefined;\n const entry: LogEntry = {\n timestamp: new Date().toISOString(),\n level,\n appName: this.config.appName ?? 'App',\n environment: this.config.environment ?? 'development',\n message,\n };\n if (this.context) entry.context = this.context;\n if (payload !== undefined) entry.payload = payload;\n if (traceId !== undefined) entry.traceId = traceId;\n\n // ── Feature 20: Plugin onLog hooks ────────────────────────────────────────\n // Plugins run post-redaction, pre-transport. Any plugin may mutate or cancel\n // the entry by returning null. We only run the pipeline when plugins are registered.\n let finalEntry: LogEntry | null = entry;\n if (this._pluginRegistry.size > 0) {\n finalEntry = await this._pluginRegistry.runOnLog(entry);\n if (finalEntry === null) return; // entry cancelled by a plugin\n }\n\n const formattedLog = this.formatLog(finalEntry);\n await this.output(formattedLog, level, finalEntry);\n }\n\n // ── Feature 3: Namespace-aware shouldLog ─────────────────────────────────────\n\n /**\n * Hot-path level check: a single Map lookup + integer compare.\n * The level map and threshold are pre-built in _buildPerfCaches().\n */\n private shouldLog(level: string): boolean {\n const v = this._levelValues.get(level);\n return v !== undefined && v <= this._minLevelValue;\n }\n\n /**\n * Feature 3: Resolve the effective log level for this logger instance.\n *\n * Priority:\n * 1. ENV `LOGIXIA_LEVEL_<NS_UPPER>` (e.g. LOGIXIA_LEVEL_DB for ns \"db\" or \"db.queries\")\n * 2. Matching `namespaceLevels` config entry (longer pattern = more specific, wins)\n * 3. Global `LOGIXIA_LEVEL` ENV override\n * 4. `levelOptions.level` (resolved via Feature 5 in constructor)\n */\n private resolveEffectiveLevel(): LogLevelString {\n const ns = this.context;\n\n if (ns) {\n // 1. ENV namespace override: LOGIXIA_LEVEL_DB → context \"db\" or \"db.queries\"\n const nsKey = ns.split('.')[0]!.toUpperCase();\n const envNsLevel = process.env[`LOGIXIA_LEVEL_${nsKey}`];\n if (envNsLevel) return envNsLevel as LogLevelString;\n\n // 2. namespaceLevels config\n const namespaceLevels = this.config.namespaceLevels;\n if (namespaceLevels) {\n const sortedPatterns = Object.keys(namespaceLevels).sort((a, b) => b.length - a.length);\n for (const pattern of sortedPatterns) {\n if (matchesNamespacePattern(ns, pattern)) {\n return namespaceLevels[pattern]!;\n }\n }\n }\n }\n\n // 3. Global ENV override\n const globalEnv = process.env['LOGIXIA_LEVEL'];\n if (globalEnv) return globalEnv as LogLevelString;\n\n // 4. Config-resolved level\n return this.getLevel();\n }\n\n // ── Formatters ───────────────────────────────────────────────────────────────\n\n private formatLog(entry: LogEntry): string {\n // JSON mode: use fast-json-stringify (pre-compiled serializer, ~59% faster than JSON.stringify)\n if (this.config.format?.json) return _fastStringifyEntry(entry);\n\n let formatted = '';\n\n const doColorize = this.config.format?.colorize ?? true;\n const gray = doColorize ? this._colorMap.get('gray')! : '';\n const cyan = doColorize ? this._colorMap.get('cyan')! : '';\n const yellow = doColorize ? this._colorMap.get('yellow')! : '';\n const reset = doColorize ? this._colorMap.get('reset')! : '';\n\n // Use _fieldCache instead of calling isFieldEnabled() per field\n if (this.config.format?.timestamp !== false && this._fieldCache.get('timestamp') !== false) {\n // entry.timestamp is already an ISO string — no need to re-parse it\n formatted += `${gray}[${entry.timestamp}]${reset} `;\n }\n\n // Use pre-computed level string (avoids colorize() call + template literal allocation)\n if (this._fieldCache.get('level') !== false) {\n formatted += this._formattedLevels.get(entry.level) ?? `[${entry.level.toUpperCase()}] `;\n }\n\n // Use pre-computed \"[appName] \" string (built once in _buildPerfCaches)\n formatted += this._formattedAppName;\n\n if (entry.traceId && this._fieldCache.get('traceId') !== false)\n formatted += `${cyan}[${entry.traceId}]${reset} `;\n if (entry.context && this._fieldCache.get('context') !== false)\n formatted += `${yellow}[${entry.context}]${reset} `;\n if (this._fieldCache.get('message') !== false) formatted += entry.message;\n\n if (entry.payload !== undefined && this._fieldCache.get('payload') !== false) {\n formatted += ` ${JSON.stringify(entry.payload)}`;\n }\n\n return formatted;\n }\n\n private colorize(text: string, color: string): string {\n if (!this.config.format?.colorize) return text;\n // Use pre-built _colorMap instead of recreating the colors object every call\n const code = this._colorMap.get(color.toLowerCase()) ?? this._colorMap.get('white')!;\n return `${code}${text}${this._colorMap.get('reset')!}`;\n }\n\n private async output(message: string, level: string, entry: LogEntry): Promise<void> {\n if (this.transportManager) {\n try {\n await this.transportManager.write(entry);\n return;\n } catch (error) {\n internalError('Transport write failed', error);\n }\n }\n\n // Fallback: direct stdout/stderr write — faster than console wrappers\n const out = level === LogLevel.ERROR ? process.stderr : process.stdout;\n out.write(message + '\\n');\n }\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function createLogger<T extends LoggerConfig<Record<string, number>>>(\n config: T,\n context?: string\n): LoggerWithLevels<T> {\n const logger = new LogixiaLogger<T>(config, context);\n const mutableLogger = logger as unknown as Record<string, unknown>;\n\n if (config.levelOptions?.levels) {\n for (const levelName of Object.keys(config.levelOptions.levels)) {\n if (!mutableLogger[levelName]) {\n mutableLogger[levelName] = async (message: string, data?: Record<string, unknown>) => {\n await logger.logLevel(levelName, message, data);\n };\n }\n }\n }\n\n return logger as unknown as LoggerWithLevels<T>;\n}\n","/**\n * NestJS Service integration for Logixia Logger\n *\n * Implements NestJS's `LoggerService` interface for framework compat, while\n * simultaneously exposing native async overloads that accept structured data:\n *\n * // NestJS compat — void, string context\n * logger.warn('message', 'AuthService');\n *\n * // Native async — Promise<void>, structured metadata\n * await logger.warn('message', { userId: 'u_abc', action: 'login' });\n *\n * TypeScript resolves the correct overload automatically based on the type of\n * the second argument:\n * - second arg is `Record<string, unknown>` → native path (returns Promise<void>)\n * - second arg is `string | undefined` → NestJS compat (returns void)\n *\n * No wrapper, no casting required on the calling side.\n */\n\nimport type { LoggerService } from '@nestjs/common';\nimport { Injectable, Scope } from '@nestjs/common';\n\nimport type { LoggerConfig, LogLevelString } from '../types';\nimport { LogLevel } from '../types';\n\n// ── Custom-level IntelliSense helpers ─────────────────────────────────────────\n\n/**\n * Level names that already have proper typed implementations on LogixiaLoggerService.\n * These are excluded from the auto-generated method type so we don't produce\n * duplicate / conflicting signatures.\n */\ntype _ServiceBuiltinLevels =\n | 'error'\n | 'warn'\n | 'info'\n | 'debug'\n | 'verbose'\n | 'trace'\n | 'log'\n | 'logLevel';\n\n/**\n * Mapped type that adds one method per *custom* level (i.e. every key in TLevels\n * that is not already a built-in method on LogixiaLoggerService).\n *\n * @example\n * ServiceCustomLevelMethods<{ kafka: 3; mysql: 4; payment: 5 }>\n * // → { kafka(msg, data?): Promise<void>; mysql(...): …; payment(...): … }\n */\ntype ServiceCustomLevelMethods<TLevels extends Record<string, number>> = {\n readonly [K in keyof TLevels as K extends _ServiceBuiltinLevels ? never : K & string]: (\n message: string,\n data?: Record<string, unknown>\n ) => Promise<void>;\n};\n\n/**\n * The return type of `LogixiaLoggerService.create<T>(config)`.\n *\n * When `T` carries `levelOptions.levels`, this type intersects\n * `LogixiaLoggerService` with a method for every *custom* level so the IDE\n * suggests `service.kafka(...)`, `service.payment(...)`, etc.\n */\nexport type LogixiaServiceWithLevels<T extends LoggerConfig<Record<string, number>>> =\n T['levelOptions'] extends { levels: infer L }\n ? L extends Record<string, number>\n ? LogixiaLoggerService & ServiceCustomLevelMethods<L>\n : LogixiaLoggerService\n : LogixiaLoggerService;\n\n/**\n * Helper — extracts custom level names from a config object's `levelOptions.levels`.\n */\ntype _ExtractCustomLevelNames<T> = T extends { levelOptions?: { levels?: infer L } }\n ? L extends Record<string, number>\n ? Exclude<keyof L & string, _ServiceBuiltinLevels>\n : never\n : never;\n\n/**\n * Typed `LogixiaLoggerService` with autocomplete for custom levels.\n *\n * Accepts either:\n * - **string union** of custom level names: `LogixiaServiceWith<'kafka' | 'payment'>`\n * - **config object type** (via `typeof`): `LogixiaServiceWith<typeof logixiaConfig>`\n *\n * Define your config with `as const` once, derive the type everywhere:\n *\n * @example\n * ```ts\n * // logger.config.ts — define once\n * export const logixiaConfig = {\n * levelOptions: {\n * levels: { error: 0, warn: 1, info: 2, debug: 3, verbose: 4, kafka: 5, payment: 6 },\n * },\n * } as const;\n * export type AppLogger = LogixiaServiceWith<typeof logixiaConfig>;\n *\n * // any.controller.ts — use everywhere, no casting\n * constructor(private readonly logger: AppLogger) {}\n * // this.logger.kafka('msg') ← fully typed\n * // this.logger.payment('msg') ← fully typed\n * ```\n *\n * @example\n * ```ts\n * // Or use string union directly:\n * type AppLogger = LogixiaServiceWith<'kafka' | 'payment'>;\n * ```\n */\nexport type LogixiaServiceWith<T extends string | Record<string, unknown>> =\n LogixiaLoggerService & {\n readonly [K in T extends string\n ? Exclude<T, _ServiceBuiltinLevels>\n : _ExtractCustomLevelNames<T>]: (\n message: string,\n data?: Record<string, unknown>\n ) => Promise<void>;\n };\nimport { internalError } from '../utils/internal-log';\nimport { getTraceContextKey, TraceContext } from '../utils/trace.utils';\nimport { LogixiaLogger } from './logitron-logger';\n\n@Injectable({ scope: Scope.TRANSIENT })\nexport class LogixiaLoggerService implements LoggerService {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- index signature required for dynamic custom-level methods\n [K: string]: any;\n\n private logger: LogixiaLogger;\n private context?: string;\n private _mergedConfig: LoggerConfig;\n\n constructor(config?: LoggerConfig) {\n const defaultConfig: LoggerConfig = {\n appName: 'NestJS-App',\n environment: 'development',\n traceId: true,\n format: {\n timestamp: true,\n colorize: true,\n json: false,\n },\n silent: false,\n levelOptions: {\n level: LogLevel.INFO,\n levels: {\n error: 0,\n warn: 1,\n log: 2,\n debug: 3,\n verbose: 4,\n },\n colors: {\n error: 'red',\n warn: 'yellow',\n log: 'green',\n debug: 'blue',\n verbose: 'cyan',\n },\n },\n fields: {\n timestamp: '[yyyy-mm-dd HH:MM:ss.MS]',\n level: '[log_level]',\n appName: '[app_name]',\n traceId: '[trace_id]',\n message: '[message]',\n payload: '[payload]',\n timeTaken: '[time_taken_MS]',\n },\n };\n\n this._mergedConfig = { ...defaultConfig, ...config };\n this.logger = new LogixiaLogger(this._mergedConfig);\n this._createCustomLevelMethods();\n }\n\n // Dynamically adds a proxy method for every custom level defined in levelOptions.levels\n // so that `service.payment('msg')` works the same as `service.logLevel('payment', 'msg')`.\n private _createCustomLevelMethods(): void {\n const levels = this._mergedConfig.levelOptions?.levels;\n if (!levels) return;\n for (const levelName of Object.keys(levels)) {\n const lower = levelName.toLowerCase();\n // Skip levels that already have a built-in implementation on this class\n if (typeof (this as Record<string, unknown>)[lower] !== 'undefined') continue;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this as any)[lower] = async (\n message: string,\n data?: Record<string, unknown>\n ): Promise<void> => {\n return this.logger.logLevel(lower, message, data);\n };\n }\n }\n\n // ── log / info ──────────────────────────────────────────────────────────────\n\n /**\n * NestJS `LoggerService.log` — void, string context.\n * Maps internally to `info`.\n *\n * @example `logger.log('User signed up', 'AuthService')`\n */\n log(message: unknown, context?: string): void {\n this.setContextIfProvided(context);\n this.logger\n .info(this.formatMessage(message))\n .catch((err: unknown) => internalError('LogixiaLoggerService.log failed', err));\n }\n\n /**\n * Native async `info` — structured data, returns `Promise<void>`.\n *\n * @example `await logger.info('User signed up', { userId: 'u_abc' })`\n */\n async info(message: string, data?: Record<string, unknown>): Promise<void> {\n return this.logger.info(message, data);\n }\n\n // ── error ──────────────────────────────────────────────────────────────────\n\n /**\n * Native async overload — structured metadata, returns `Promise<void>`.\n *\n * @example `await logger.error(new Error('DB timeout'), { requestId: 'req_abc' })`\n * @example `await logger.error('Login failed', { userId: 'u_abc' })`\n */\n error(message: string | Error, data: Record<string, unknown>): Promise<void>;\n\n /**\n * NestJS compat overload — string trace + optional context, returns `void`.\n *\n * @example `logger.error('Something failed', err.stack, 'AuthService')`\n */\n error(message: unknown, trace?: string, context?: string): void;\n\n error(\n message: unknown,\n dataOrTrace?: Record<string, unknown> | string,\n context?: string\n ): void | Promise<void> {\n // ── Native path: second arg is a structured-data object ──────────────────\n if (typeof dataOrTrace === 'object' && dataOrTrace !== null) {\n return message instanceof Error\n ? this.logger.error(message, dataOrTrace)\n : this.logger.error(this.formatMessage(message), dataOrTrace);\n }\n\n // ── NestJS compat path: second arg is trace string or undefined ──────────\n this.setContextIfProvided(context);\n const errorData: Record<string, string> = {};\n if (typeof dataOrTrace === 'string' && dataOrTrace) {\n errorData.stack = dataOrTrace;\n }\n\n const logPromise =\n message instanceof Error\n ? this.logger.error(message, errorData)\n : this.logger.error(this.formatMessage(message), errorData);\n\n logPromise.catch((err: unknown) => internalError('LogixiaLoggerService.error failed', err));\n }\n\n // ── warn ───────────────────────────────────────────────────────────────────\n\n /**\n * Native async overload — structured metadata, returns `Promise<void>`.\n *\n * @example `await logger.warn('Rate limit approaching', { userId: 'u_abc', count: 90 })`\n */\n warn(message: string, data: Record<string, unknown>): Promise<void>;\n\n /**\n * NestJS compat overload — optional context string, returns `void`.\n *\n * @example `logger.warn('Deprecated API used', 'PaymentService')`\n */\n warn(message: unknown, context?: string): void;\n\n warn(message: unknown, dataOrContext?: Record<string, unknown> | string): void | Promise<void> {\n if (typeof dataOrContext === 'object' && dataOrContext !== null) {\n return this.logger.warn(this.formatMessage(message), dataOrContext);\n }\n this.setContextIfProvided(typeof dataOrContext === 'string' ? dataOrContext : undefined);\n this.logger\n .warn(this.formatMessage(message))\n .catch((err: unknown) => internalError('LogixiaLoggerService.warn failed', err));\n }\n\n // ── debug ──────────────────────────────────────────────────────────────────\n\n /**\n * Native async overload — structured metadata, returns `Promise<void>`.\n *\n * @example `await logger.debug('Cache miss', { key: 'user:abc', ttl: 300 })`\n */\n debug(message: string, data: Record<string, unknown>): Promise<void>;\n\n /**\n * NestJS compat overload — optional context string, returns `void`.\n *\n * @example `logger.debug('Processing request', 'OrderService')`\n */\n debug(message: unknown, context?: string): void;\n\n debug(message: unknown, dataOrContext?: Record<string, unknown> | string): void | Promise<void> {\n if (typeof dataOrContext === 'object' && dataOrContext !== null) {\n return this.logger.debug(this.formatMessage(message), dataOrContext);\n }\n this.setContextIfProvided(typeof dataOrContext === 'string' ? dataOrContext : undefined);\n this.logger\n .debug(this.formatMessage(message))\n .catch((err: unknown) => internalError('LogixiaLoggerService.debug failed', err));\n }\n\n // ── verbose ────────────────────────────────────────────────────────────────\n\n /**\n * Native async overload — structured metadata, returns `Promise<void>`.\n * Maps to `trace` level internally.\n *\n * @example `await logger.verbose('Socket message received', { event: 'ping', size: 42 })`\n */\n verbose(message: string, data: Record<string, unknown>): Promise<void>;\n\n /**\n * NestJS compat overload — optional context string, returns `void`.\n *\n * @example `logger.verbose('Connection established', 'WebSocketGateway')`\n */\n verbose(message: unknown, context?: string): void;\n\n verbose(\n message: unknown,\n dataOrContext?: Record<string, unknown> | string\n ): void | Promise<void> {\n if (typeof dataOrContext === 'object' && dataOrContext !== null) {\n return this.logger.trace(this.formatMessage(message), dataOrContext);\n }\n this.setContextIfProvided(typeof dataOrContext === 'string' ? dataOrContext : undefined);\n this.logger\n .trace(this.formatMessage(message))\n .catch((err: unknown) => internalError('LogixiaLoggerService.verbose failed', err));\n }\n\n // ── trace / logLevel ───────────────────────────────────────────────────────\n\n /**\n * Native async `trace` — lowest verbosity level, structured data.\n */\n async trace(message: string, data?: Record<string, unknown>): Promise<void> {\n return this.logger.trace(message, data);\n }\n\n /**\n * Log at any named level with structured data.\n */\n logLevel(level: string, message: string, data?: Record<string, unknown>): Promise<void> {\n return this.logger.logLevel(level, message, data);\n }\n\n // ── Timing ─────────────────────────────────────────────────────────────────\n\n time(label: string): void {\n this.logger.time(label);\n }\n\n async timeEnd(label: string): Promise<number | undefined> {\n return this.logger.timeEnd(label);\n }\n\n async timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T> {\n return this.logger.timeAsync(label, fn);\n }\n\n // ── Context / level management ─────────────────────────────────────────────\n\n setContext(context: string): void {\n this.context = context;\n this.logger.setContext(context);\n }\n\n getContext(): string | undefined {\n return this.context;\n }\n\n setLevel(level: LogLevelString): void {\n this.logger.setLevel(level);\n }\n\n getLevel(): LogLevelString {\n return this.logger.getLevel();\n }\n\n // ── Child logger ───────────────────────────────────────────────────────────\n\n child(context: string, data?: Record<string, unknown>): LogixiaLoggerService {\n const childService = new LogixiaLoggerService(this._mergedConfig);\n childService.logger = this.logger.child(context, data) as LogixiaLogger;\n childService.context = context;\n return childService;\n }\n\n // ── Misc ───────────────────────────────────────────────────────────────────\n\n getCurrentTraceId(): string | undefined {\n return TraceContext.instance.getCurrentTraceId();\n }\n\n /** Returns the AsyncLocalStorage key currently used to store the trace ID. */\n get traceContextKey(): string {\n return getTraceContextKey();\n }\n\n async close(): Promise<void> {\n return this.logger.close();\n }\n\n static create<T extends LoggerConfig<Record<string, number>>>(\n config?: T\n ): LogixiaServiceWithLevels<T> {\n return new LogixiaLoggerService(config) as unknown as LogixiaServiceWithLevels<T>;\n }\n\n getLogger(): LogixiaLogger {\n return this.logger;\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private setContextIfProvided(context?: string): void {\n if (context && context !== this.context) {\n this.setContext(context);\n }\n }\n\n private formatMessage(message: unknown): string {\n if (typeof message === 'string') return message;\n if (typeof message === 'object') return JSON.stringify(message);\n return String(message);\n }\n}\n","/** @format */\n\nimport type { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';\nimport { Injectable } from '@nestjs/common';\nimport { EMPTY, Observable } from 'rxjs';\n\nimport type { TraceIdConfig } from '../types';\nimport { extractTraceId, TraceContext } from '../utils/trace.utils';\nimport { LogixiaLoggerModule } from './logitron-logger.module';\n\n/**\n * Observable counters exposed for monitoring Kafka consumer trace hygiene.\n *\n * Usage:\n * import { KafkaTraceInterceptor } from 'logixia/nest';\n * setInterval(() => {\n * myMetrics.gauge('kafka.trace.dropped', KafkaTraceInterceptor.metrics.dropped);\n * }, 10_000);\n */\nexport interface KafkaTraceMetrics {\n /** Messages processed where a traceId was successfully resolved (body/header/ALS). */\n accepted: number;\n /** Messages handled without any traceId — `requireTraceId: false`. */\n acceptedWithoutTrace: number;\n /** Messages dropped because `requireTraceId: true` and no traceId was found. */\n dropped: number;\n}\n\n@Injectable()\nexport class KafkaTraceInterceptor implements NestInterceptor {\n /**\n * Process-wide counters. Readable from anywhere — scrape into your metrics\n * system (Prometheus, Datadog, CloudWatch) on an interval.\n */\n static readonly metrics: KafkaTraceMetrics = {\n accepted: 0,\n acceptedWithoutTrace: 0,\n dropped: 0,\n };\n\n /** Reset counters (tests). */\n static resetMetrics(): void {\n KafkaTraceInterceptor.metrics.accepted = 0;\n KafkaTraceInterceptor.metrics.acceptedWithoutTrace = 0;\n KafkaTraceInterceptor.metrics.dropped = 0;\n }\n\n private readonly ctx = TraceContext.instance;\n\n /**\n * @param config - TraceIdConfig options (extractor keys, contextKey, etc.)\n * @param requireTraceId - When true, messages with no traceId are silently skipped\n * (EMPTY Observable — message is ack'd, consumer stays alive).\n * A WARN is logged AND `KafkaTraceInterceptor.metrics.dropped`\n * increments so the missing traceId is observable end-to-end.\n * Default: false (handler runs without trace context).\n */\n constructor(\n private readonly config?: TraceIdConfig,\n private readonly requireTraceId: boolean = false\n ) {\n this.config = {\n enabled: true,\n contextKey: 'traceId',\n extractor: {\n body: ['traceId', 'trace_id', 'x-trace-id'],\n header: ['x-trace-id', 'trace-id'],\n },\n ...config,\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- NestJS interceptor interface requires Observable<any>\n intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n if (!this.config?.enabled) {\n return next.handle();\n }\n\n const rpcContext = context.switchToRpc();\n const data = rpcContext.getData();\n const rpcData = rpcContext.getContext();\n\n let traceId: string | undefined;\n\n if (this.config.extractor) {\n traceId = extractTraceId(\n { body: data, headers: rpcData?.headers ?? {}, query: {}, params: {} },\n this.config.extractor\n );\n }\n\n if (!traceId) {\n traceId = this.ctx.getCurrentTraceId();\n }\n\n if (!traceId) {\n if (this.requireTraceId) {\n // Warn via global logger (set when LogixiaLoggerModule boots) and ack\n // the message by returning EMPTY — consumer stays alive, no retry loop.\n KafkaTraceInterceptor.metrics.dropped++;\n LogixiaLoggerModule.getGlobalLogger()?.warn(\n `[KafkaTraceInterceptor] Missing traceId on topic \"${rpcData?.topic}\" — message skipped.`\n );\n return EMPTY;\n }\n KafkaTraceInterceptor.metrics.acceptedWithoutTrace++;\n return next.handle();\n }\n\n KafkaTraceInterceptor.metrics.accepted++;\n\n const kafkaContext = {\n messageType: 'kafka',\n topic: rpcData?.topic,\n partition: rpcData?.partition,\n offset: rpcData?.offset,\n key: rpcData?.key,\n timestamp: rpcData?.timestamp,\n };\n\n return new Observable((subscriber) => {\n this.ctx.run(\n traceId!,\n () => {\n next.handle().subscribe({\n next: (value) => subscriber.next(value),\n error: (err) => subscriber.error(err),\n complete: () => subscriber.complete(),\n });\n },\n kafkaContext\n );\n });\n }\n}\n","/**\n * Trace ID middleware for NestJS integration\n */\n\nimport type { NestMiddleware } from '@nestjs/common';\nimport { Injectable, Optional } from '@nestjs/common';\nimport type { NextFunction, Request, Response } from 'express';\n\nimport type { TraceIdConfig } from '../types';\nimport { DEFAULT_TRACE_HEADERS, extractTraceId, TraceContext } from '../utils/trace.utils';\n\n/** Default response header used to echo the resolved traceId back to the caller. */\nexport const DEFAULT_TRACE_RESPONSE_HEADER = 'X-Trace-Id';\n\n/**\n * Resolve the response header name from config.\n * - `undefined` → default `'X-Trace-Id'`\n * - `string` → user's custom header\n * - `false` → `null` (suppress entirely)\n */\nexport function resolveResponseHeader(config?: TraceIdConfig): string | null {\n if (config?.responseHeader === false) return null;\n return config?.responseHeader ?? DEFAULT_TRACE_RESPONSE_HEADER;\n}\n\n// Extend Express Request interface — requires namespace to augment Express typings\n/* eslint-disable @typescript-eslint/no-namespace */\ndeclare global {\n namespace Express {\n interface Request {\n traceId?: string;\n }\n }\n}\n/* eslint-enable @typescript-eslint/no-namespace */\n\n@Injectable()\nexport class TraceMiddleware implements NestMiddleware {\n private readonly ctx = TraceContext.instance;\n\n constructor(@Optional() private readonly config?: TraceIdConfig) {\n const defaultExtractor = {\n header: DEFAULT_TRACE_HEADERS,\n query: ['traceId', 'trace_id'],\n };\n this.config = {\n enabled: true,\n generator: () => this.ctx.generate(),\n contextKey: 'traceId',\n ...config,\n extractor: config?.extractor\n ? { ...defaultExtractor, ...config.extractor }\n : defaultExtractor,\n };\n // Only mutate the process-wide context key when tracing is actually enabled —\n // otherwise a disabled middleware would still change global state.\n if (this.config.enabled) {\n this.ctx.setContextKey(this.config.contextKey ?? 'traceId');\n }\n }\n\n use(req: Request, res: Response, next: NextFunction): void {\n if (!this.config?.enabled) {\n return next();\n }\n\n let traceId: string | undefined;\n\n if (this.config.extractor) {\n traceId = extractTraceId(req, this.config.extractor);\n }\n\n if (!traceId && this.config.generator) {\n const candidate = this.config.generator();\n // Guard against a user-supplied generator that returns a bad value —\n // fall back to the built-in UUID generator so the request always has\n // a valid, non-empty traceId downstream.\n if (typeof candidate === 'string' && candidate.trim().length > 0) {\n traceId = candidate;\n } else {\n process.stderr.write(\n '[logixia] TraceIdConfig.generator returned a non-string/empty value — using built-in generator.\\n'\n );\n }\n }\n if (!traceId) {\n traceId = this.ctx.generate();\n }\n\n req.traceId = traceId;\n\n const header = resolveResponseHeader(this.config);\n if (header) res.setHeader(header, traceId);\n\n this.ctx.run(traceId, () => next(), {\n method: req.method,\n url: req.url,\n userAgent: req.get('User-Agent'),\n ip: req.ip || req.connection.remoteAddress,\n });\n }\n}\n\n/**\n * Factory function to create trace middleware with configuration\n */\nexport function createTraceMiddleware(config?: TraceIdConfig): TraceMiddleware {\n return new TraceMiddleware(config);\n}\n\n/**\n * Functional middleware for Express-style usage\n */\nexport function traceMiddleware(config?: TraceIdConfig) {\n const ctx = TraceContext.instance;\n const defaultExtractor = {\n header: DEFAULT_TRACE_HEADERS,\n query: ['traceId', 'trace_id'],\n };\n const traceConfig = {\n enabled: true,\n generator: () => ctx.generate(),\n contextKey: 'traceId',\n ...config,\n extractor: config?.extractor ? { ...defaultExtractor, ...config.extractor } : defaultExtractor,\n };\n\n // Only mutate the process-wide context key when the middleware is actually\n // enabled — otherwise a disabled instance would still change global state.\n if (traceConfig.enabled) {\n ctx.setContextKey(traceConfig.contextKey ?? 'traceId');\n }\n\n return (req: Request, res: Response, next: NextFunction) => {\n if (!traceConfig.enabled) {\n return next();\n }\n\n let traceId: string | undefined;\n\n if (traceConfig.extractor) {\n traceId = extractTraceId(req, traceConfig.extractor);\n }\n\n if (!traceId && traceConfig.generator) {\n const candidate = traceConfig.generator();\n if (typeof candidate === 'string' && candidate.trim().length > 0) {\n traceId = candidate;\n } else {\n process.stderr.write(\n '[logixia] TraceIdConfig.generator returned a non-string/empty value — using built-in generator.\\n'\n );\n }\n }\n if (!traceId) {\n traceId = ctx.generate();\n }\n\n req.traceId = traceId;\n\n const header = resolveResponseHeader(traceConfig);\n if (header) res.setHeader(header, traceId);\n\n ctx.run(traceId, () => next(), {\n method: req.method,\n url: req.url,\n userAgent: req.get('User-Agent'),\n ip: req.ip || req.connection.remoteAddress,\n });\n };\n}\n","/** @format */\n\nimport type { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';\nimport { Injectable } from '@nestjs/common';\nimport { Observable } from 'rxjs';\n\nimport type { TraceIdConfig } from '../types';\nimport { extractTraceId, TraceContext } from '../utils/trace.utils';\n\n@Injectable()\nexport class WebSocketTraceInterceptor implements NestInterceptor {\n private readonly ctx = TraceContext.instance;\n\n constructor(private readonly config?: TraceIdConfig) {\n this.config = {\n enabled: true,\n contextKey: 'traceId',\n extractor: {\n body: ['traceId', 'trace_id', 'x-trace-id'],\n header: ['x-trace-id', 'trace-id'],\n query: ['traceId', 'trace_id'],\n },\n ...config,\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- NestJS interceptor interface requires Observable<any>\n intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n // Check if trace ID is enabled\n if (!this.config?.enabled) {\n return next.handle();\n }\n\n const wsContext = context.switchToWs();\n const data = wsContext.getData();\n const client = wsContext.getClient();\n\n let traceId: string | undefined;\n\n // Try to extract existing trace ID using extractor\n if (this.config.extractor) {\n // Create a request-like object for extractTraceId\n const requestLike = {\n body: data,\n headers: client?.handshake?.headers || {},\n query: client?.handshake?.query || {},\n params: {},\n };\n traceId = extractTraceId(requestLike, this.config.extractor);\n }\n\n // Only use current trace ID if no extraction happened and we have one\n if (!traceId) {\n traceId = this.ctx.getCurrentTraceId();\n }\n\n // If still no trace ID and enabled, skip (don't generate)\n if (!traceId) {\n return next.handle();\n }\n\n // Set up WebSocket-specific context data\n const wsContextData = {\n messageType: 'websocket',\n event: data?.event,\n socketId: client?.id,\n rooms: client?.rooms ? Array.from(client.rooms) : [],\n clientAddress: client?.handshake?.address,\n };\n\n // Run the handler with trace ID context\n return new Observable((observer) => {\n this.ctx.run(\n traceId!,\n () => {\n const result = next.handle();\n result.subscribe({\n next: (value) => observer.next(value),\n error: (err) => observer.error(err),\n complete: () => observer.complete(),\n });\n },\n wsContextData\n );\n });\n }\n}\n","import type {\n InjectionToken,\n MiddlewareConsumer,\n ModuleMetadata,\n NestModule,\n OptionalFactoryDependency,\n Type,\n} from '@nestjs/common';\nimport { Module, RequestMethod } from '@nestjs/common';\nimport type { RouteInfo } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';\nimport type { NextFunction, Request, Response } from 'express';\n\nimport type { LoggerConfig, TraceIdConfig } from '../types';\nimport { TraceContext } from '../utils/trace.utils';\nimport { KafkaTraceInterceptor } from './kafka-trace.interceptor';\nimport { LogixiaLoggerService } from './logitron-nestjs.service';\nimport { TraceMiddleware } from './trace.middleware';\nimport { WebSocketTraceInterceptor } from './websocket-trace.interceptor';\n\nconst DEFAULT_ROUTES: RouteInfo[] = [{ path: '*', method: RequestMethod.ALL }];\n\n// Constants for provider tokens\nexport const LOGIXIA_LOGGER_CONFIG = 'LOGIXIA_LOGGER_CONFIG';\nexport const LOGIXIA_LOGGER_PREFIX = 'LOGIXIA_LOGGER_';\n\n// Export the service and interceptors for external use\nexport { KafkaTraceInterceptor } from './kafka-trace.interceptor';\nexport { LogixiaLoggerService } from './logitron-nestjs.service';\nexport { WebSocketTraceInterceptor } from './websocket-trace.interceptor';\n\n// Interface for module configuration\ninterface LogixiaModuleConfig {\n forRoutes?: RouteInfo[];\n exclude?: RouteInfo[];\n}\n\n// Interface for async configuration\nexport interface LogixiaAsyncOptions extends Pick<ModuleMetadata, 'imports'> {\n useExisting?: Type<LogixiaOptionsFactory>;\n useClass?: Type<LogixiaOptionsFactory>;\n useFactory?: (...args: unknown[]) => Promise<Partial<LoggerConfig>> | Partial<LoggerConfig>;\n inject?: Array<InjectionToken | OptionalFactoryDependency>;\n}\n\n// Interface for options factory\nexport interface LogixiaOptionsFactory {\n createLogixiaOptions(): Promise<Partial<LoggerConfig>> | Partial<LoggerConfig>;\n}\n\n/**\n * Logixia Logger Module for NestJS dependency injection\n */\n@Module({})\nexport class LogixiaLoggerModule implements NestModule {\n private config: LogixiaModuleConfig = {};\n private static loggerConfig: Partial<LoggerConfig> = {};\n\n /**\n * @internal Backing field for the global logger. Do not read or write\n * directly — use {@link getGlobalLogger} / {@link _setGlobalLogger} which\n * enforce single-init semantics.\n */\n // Retained for backwards compatibility with internal callers that read the\n // field directly (e.g. nestjs-extras). Treat as read-only — writes must go\n // through _setGlobalLogger / _resetGlobalLogger.\n // eslint-disable-next-line sonarjs/public-static-readonly -- intentional: we need to swap this in tests via _resetGlobalLogger, so `readonly` is too strict\n static _globalLogger: LogixiaLoggerService | null = null;\n\n /**\n * @internal Set the global logger exactly once.\n *\n * Called from the module's forRoot / forRootAsync factory. If the module is\n * initialised more than once in the same process (nested DI context, test\n * harness creating multiple apps, hot reload, etc.) a warning is written to\n * stderr and the first logger wins — silently overwriting would allow the\n * newer instance's transport config to replace the live one while the old\n * one is still being used by registered shutdown hooks, decorators, etc.\n *\n * Use {@link _resetGlobalLogger} in tests to reset between runs.\n */\n static _setGlobalLogger(service: LogixiaLoggerService): void {\n if (LogixiaLoggerModule._globalLogger !== null) {\n process.stderr.write(\n '[logixia] LogixiaLoggerModule.forRoot() was called more than once — ignoring the second init. ' +\n 'If this is intentional (e.g. in tests), call LogixiaLoggerModule._resetGlobalLogger() first.\\n'\n );\n return;\n }\n LogixiaLoggerModule._globalLogger = service;\n }\n\n /** @internal Clear the global logger. Tests only. */\n static _resetGlobalLogger(): void {\n LogixiaLoggerModule._globalLogger = null;\n }\n\n /**\n * Returns the global LogixiaLoggerService instance that was created when the\n * module booted. Useful for logging outside of NestJS DI — utility functions,\n * plain scripts, decorators — without injecting the service everywhere.\n *\n * Returns `null` if called before `LogixiaLoggerModule.forRoot[Async]()` has\n * been initialised (i.e. before the NestJS app has started).\n *\n * @example\n * ```ts\n * // some-util.ts\n * import { LogixiaLoggerModule } from 'logixia/nest';\n *\n * export function doSomething() {\n * LogixiaLoggerModule.getGlobalLogger()?.info('doing something');\n * }\n * ```\n */\n static getGlobalLogger(): LogixiaLoggerService | null {\n return LogixiaLoggerModule._globalLogger;\n }\n\n configure(consumer: MiddlewareConsumer) {\n const { forRoutes = DEFAULT_ROUTES, exclude } = this.config;\n\n // Resolve the trace config ONCE at configure() time — it does not change\n // per request, so constructing a fresh TraceMiddleware on every invocation\n // (the old behaviour) was just allocation churn.\n let resolvedTraceConfig: TraceIdConfig | undefined;\n if (typeof LogixiaLoggerModule.loggerConfig.traceId === 'object') {\n resolvedTraceConfig = LogixiaLoggerModule.loggerConfig.traceId as TraceIdConfig;\n } else if (LogixiaLoggerModule.loggerConfig.traceId === true) {\n resolvedTraceConfig = {\n enabled: true,\n contextKey: 'traceId',\n generator: () => TraceContext.instance.generate(),\n };\n }\n\n const middleware = new TraceMiddleware(resolvedTraceConfig);\n const middlewareConfig = (req: Request, res: Response, next: NextFunction) =>\n middleware.use(req, res, next);\n\n if (exclude) {\n consumer\n .apply(middlewareConfig)\n .exclude(...exclude)\n .forRoutes(...forRoutes);\n } else {\n consumer.apply(middlewareConfig).forRoutes(...forRoutes);\n }\n }\n\n /**\n * Configure the module with synchronous options\n */\n static forRoot(config?: Partial<LoggerConfig>) {\n // Store config for middleware access\n LogixiaLoggerModule.loggerConfig = config || {};\n\n const traceConfig =\n config?.traceId && typeof config.traceId === 'object'\n ? config.traceId\n : { enabled: !!config?.traceId };\n\n return {\n module: LogixiaLoggerModule,\n providers: [\n {\n provide: LOGIXIA_LOGGER_CONFIG,\n useValue: config || {},\n },\n {\n provide: 'TRACE_CONFIG',\n useValue: traceConfig,\n },\n {\n provide: LogixiaLoggerService,\n useFactory: (loggerConfig: Partial<LoggerConfig>) => {\n const defaultConfig: LoggerConfig = {\n level: 'info',\n service: 'NestJSApp',\n environment: 'development',\n fields: {},\n formatters: ['text'],\n outputs: ['console'],\n levelOptions: {\n level: 'info', // INFO level\n levels: {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n verbose: 4,\n },\n colors: {\n error: 'red',\n warn: 'yellow',\n info: 'green',\n debug: 'blue',\n verbose: 'cyan',\n },\n },\n ...loggerConfig,\n };\n const service = new LogixiaLoggerService(defaultConfig);\n LogixiaLoggerModule._setGlobalLogger(service);\n return service;\n },\n inject: [LOGIXIA_LOGGER_CONFIG],\n },\n {\n provide: KafkaTraceInterceptor,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- NestJS DI injects typed config\n useFactory: (traceConfig: any) => new KafkaTraceInterceptor(traceConfig),\n inject: ['TRACE_CONFIG'],\n },\n {\n provide: WebSocketTraceInterceptor,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- NestJS DI injects typed config\n useFactory: (traceConfig: any) => new WebSocketTraceInterceptor(traceConfig),\n inject: ['TRACE_CONFIG'],\n },\n ],\n exports: [\n LogixiaLoggerService,\n LOGIXIA_LOGGER_CONFIG,\n KafkaTraceInterceptor,\n WebSocketTraceInterceptor,\n ],\n global: true,\n };\n }\n\n /**\n * Configure the module with asynchronous options\n */\n static forRootAsync(options: LogixiaAsyncOptions) {\n return {\n module: LogixiaLoggerModule,\n imports: options.imports || [],\n providers: [\n ...this.createAsyncProviders(options),\n {\n provide: 'TRACE_CONFIG',\n useFactory: (loggerConfig: Partial<LoggerConfig>) => {\n return typeof loggerConfig?.traceId === 'object'\n ? loggerConfig.traceId\n : { enabled: !!loggerConfig?.traceId };\n },\n inject: [LOGIXIA_LOGGER_CONFIG],\n },\n {\n provide: LogixiaLoggerService,\n useFactory: (loggerConfig: Partial<LoggerConfig>) => {\n const defaultConfig: LoggerConfig = {\n level: 'info',\n service: 'NestJSApp',\n environment: 'development',\n fields: {},\n formatters: ['text'],\n outputs: ['console'],\n levelOptions: {\n level: 'info', // INFO level\n levels: {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n verbose: 4,\n },\n colors: {\n error: 'red',\n warn: 'yellow',\n info: 'green',\n debug: 'blue',\n verbose: 'cyan',\n },\n },\n ...loggerConfig,\n };\n // Store config for middleware access\n LogixiaLoggerModule.loggerConfig = defaultConfig;\n const service = new LogixiaLoggerService(defaultConfig);\n LogixiaLoggerModule._setGlobalLogger(service);\n return service;\n },\n inject: [LOGIXIA_LOGGER_CONFIG],\n },\n {\n provide: KafkaTraceInterceptor,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- NestJS DI injects typed config\n useFactory: (traceConfig: any) => new KafkaTraceInterceptor(traceConfig),\n inject: ['TRACE_CONFIG'],\n },\n {\n provide: WebSocketTraceInterceptor,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- NestJS DI injects typed config\n useFactory: (traceConfig: any) => new WebSocketTraceInterceptor(traceConfig),\n inject: ['TRACE_CONFIG'],\n },\n ],\n exports: [\n LogixiaLoggerService,\n LOGIXIA_LOGGER_CONFIG,\n KafkaTraceInterceptor,\n WebSocketTraceInterceptor,\n ],\n global: true,\n };\n }\n\n /**\n * Create feature-specific logger instances\n */\n static forFeature(context: string) {\n const providerToken = `${LOGIXIA_LOGGER_PREFIX}${context.toUpperCase()}`;\n return {\n module: LogixiaLoggerModule,\n providers: [\n {\n provide: providerToken,\n useFactory: (baseLogger: LogixiaLoggerService) => {\n return baseLogger.child(context);\n },\n inject: [LogixiaLoggerService],\n },\n ],\n exports: [providerToken],\n };\n }\n\n private static createAsyncProviders(options: LogixiaAsyncOptions) {\n if (options.useExisting || options.useFactory) {\n return [this.createAsyncOptionsProvider(options)];\n }\n return [\n this.createAsyncOptionsProvider(options),\n {\n provide: options.useClass!,\n useClass: options.useClass!,\n },\n ];\n }\n\n private static createAsyncOptionsProvider(options: LogixiaAsyncOptions) {\n if (options.useFactory) {\n return {\n provide: LOGIXIA_LOGGER_CONFIG,\n useFactory: options.useFactory,\n inject: options.inject || [],\n };\n }\n return {\n provide: LOGIXIA_LOGGER_CONFIG,\n useFactory: async (optionsFactory: LogixiaOptionsFactory) =>\n await optionsFactory.createLogixiaOptions(),\n inject: [options.useExisting || options.useClass!],\n };\n }\n}\n"],"x_google_ignoreList":[8,9,10,11],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { T as TraceIdConfig, _ as LogLevelString, d as ILogger, f as ILoggerDefault, m as LogEntry, v as LoggerConfig, y as LoggerWithLevels } from "./index-CthBT3t8.mjs";
1
+ import { T as TraceIdConfig, _ as LogLevelString, d as ILogger, f as ILoggerDefault, m as LogEntry, v as LoggerConfig, y as LoggerWithLevels } from "./index-DnhKoNBG.mjs";
2
2
  import * as _nestjs_common0 from "@nestjs/common";
3
3
  import { CallHandler, ExecutionContext, InjectionToken, LoggerService, MiddlewareConsumer, ModuleMetadata, NestInterceptor, NestModule, OptionalFactoryDependency, Type } from "@nestjs/common";
4
4
  import { Observable } from "rxjs";
@@ -238,6 +238,46 @@ type ServiceCustomLevelMethods<TLevels extends Record<string, number>> = { reado
238
238
  type LogixiaServiceWithLevels<T extends LoggerConfig<Record<string, number>>> = T['levelOptions'] extends {
239
239
  levels: infer L;
240
240
  } ? L extends Record<string, number> ? LogixiaLoggerService & ServiceCustomLevelMethods<L> : LogixiaLoggerService : LogixiaLoggerService;
241
+ /**
242
+ * Helper — extracts custom level names from a config object's `levelOptions.levels`.
243
+ */
244
+ type _ExtractCustomLevelNames<T> = T extends {
245
+ levelOptions?: {
246
+ levels?: infer L;
247
+ };
248
+ } ? L extends Record<string, number> ? Exclude<keyof L & string, _ServiceBuiltinLevels> : never : never;
249
+ /**
250
+ * Typed `LogixiaLoggerService` with autocomplete for custom levels.
251
+ *
252
+ * Accepts either:
253
+ * - **string union** of custom level names: `LogixiaServiceWith<'kafka' | 'payment'>`
254
+ * - **config object type** (via `typeof`): `LogixiaServiceWith<typeof logixiaConfig>`
255
+ *
256
+ * Define your config with `as const` once, derive the type everywhere:
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * // logger.config.ts — define once
261
+ * export const logixiaConfig = {
262
+ * levelOptions: {
263
+ * levels: { error: 0, warn: 1, info: 2, debug: 3, verbose: 4, kafka: 5, payment: 6 },
264
+ * },
265
+ * } as const;
266
+ * export type AppLogger = LogixiaServiceWith<typeof logixiaConfig>;
267
+ *
268
+ * // any.controller.ts — use everywhere, no casting
269
+ * constructor(private readonly logger: AppLogger) {}
270
+ * // this.logger.kafka('msg') ← fully typed
271
+ * // this.logger.payment('msg') ← fully typed
272
+ * ```
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * // Or use string union directly:
277
+ * type AppLogger = LogixiaServiceWith<'kafka' | 'payment'>;
278
+ * ```
279
+ */
280
+ type LogixiaServiceWith<T extends string | Record<string, unknown>> = LogixiaLoggerService & { readonly [K in T extends string ? Exclude<T, _ServiceBuiltinLevels> : _ExtractCustomLevelNames<T>]: (message: string, data?: Record<string, unknown>) => Promise<void> };
241
281
  declare class LogixiaLoggerService implements LoggerService {
242
282
  [K: string]: any;
243
283
  private logger;
@@ -335,10 +375,43 @@ declare class LogixiaLoggerService implements LoggerService {
335
375
  }
336
376
  //#endregion
337
377
  //#region src/core/kafka-trace.interceptor.d.ts
378
+ /**
379
+ * Observable counters exposed for monitoring Kafka consumer trace hygiene.
380
+ *
381
+ * Usage:
382
+ * import { KafkaTraceInterceptor } from 'logixia/nest';
383
+ * setInterval(() => {
384
+ * myMetrics.gauge('kafka.trace.dropped', KafkaTraceInterceptor.metrics.dropped);
385
+ * }, 10_000);
386
+ */
387
+ interface KafkaTraceMetrics {
388
+ /** Messages processed where a traceId was successfully resolved (body/header/ALS). */
389
+ accepted: number;
390
+ /** Messages handled without any traceId — `requireTraceId: false`. */
391
+ acceptedWithoutTrace: number;
392
+ /** Messages dropped because `requireTraceId: true` and no traceId was found. */
393
+ dropped: number;
394
+ }
338
395
  declare class KafkaTraceInterceptor implements NestInterceptor {
339
396
  private readonly config?;
397
+ private readonly requireTraceId;
398
+ /**
399
+ * Process-wide counters. Readable from anywhere — scrape into your metrics
400
+ * system (Prometheus, Datadog, CloudWatch) on an interval.
401
+ */
402
+ static readonly metrics: KafkaTraceMetrics;
403
+ /** Reset counters (tests). */
404
+ static resetMetrics(): void;
340
405
  private readonly ctx;
341
- constructor(config?: TraceIdConfig | undefined);
406
+ /**
407
+ * @param config - TraceIdConfig options (extractor keys, contextKey, etc.)
408
+ * @param requireTraceId - When true, messages with no traceId are silently skipped
409
+ * (EMPTY Observable — message is ack'd, consumer stays alive).
410
+ * A WARN is logged AND `KafkaTraceInterceptor.metrics.dropped`
411
+ * increments so the missing traceId is observable end-to-end.
412
+ * Default: false (handler runs without trace context).
413
+ */
414
+ constructor(config?: TraceIdConfig | undefined, requireTraceId?: boolean);
342
415
  intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
343
416
  }
344
417
  //#endregion
@@ -368,8 +441,27 @@ interface LogixiaOptionsFactory {
368
441
  declare class LogixiaLoggerModule implements NestModule {
369
442
  private config;
370
443
  private static loggerConfig;
371
- /** @internal Global logger instance set when the module boots. Used by @LogMethod fallback. */
444
+ /**
445
+ * @internal Backing field for the global logger. Do not read or write
446
+ * directly — use {@link getGlobalLogger} / {@link _setGlobalLogger} which
447
+ * enforce single-init semantics.
448
+ */
372
449
  static _globalLogger: LogixiaLoggerService | null;
450
+ /**
451
+ * @internal Set the global logger exactly once.
452
+ *
453
+ * Called from the module's forRoot / forRootAsync factory. If the module is
454
+ * initialised more than once in the same process (nested DI context, test
455
+ * harness creating multiple apps, hot reload, etc.) a warning is written to
456
+ * stderr and the first logger wins — silently overwriting would allow the
457
+ * newer instance's transport config to replace the live one while the old
458
+ * one is still being used by registered shutdown hooks, decorators, etc.
459
+ *
460
+ * Use {@link _resetGlobalLogger} in tests to reset between runs.
461
+ */
462
+ static _setGlobalLogger(service: LogixiaLoggerService): void;
463
+ /** @internal Clear the global logger. Tests only. */
464
+ static _resetGlobalLogger(): void;
373
465
  /**
374
466
  * Returns the global LogixiaLoggerService instance that was created when the
375
467
  * module booted. Useful for logging outside of NestJS DI — utility functions,
@@ -477,5 +569,5 @@ declare class LogixiaLoggerModule implements NestModule {
477
569
  private static createAsyncOptionsProvider;
478
570
  }
479
571
  //#endregion
480
- export { LogixiaOptionsFactory as a, LogixiaLoggerService as c, createLogger as d, LogixiaPlugin as f, usePlugin as h, LogixiaLoggerModule as i, LogixiaServiceWithLevels as l, globalPluginRegistry as m, LOGIXIA_LOGGER_PREFIX as n, WebSocketTraceInterceptor as o, PluginRegistry as p, LogixiaAsyncOptions as r, KafkaTraceInterceptor as s, LOGIXIA_LOGGER_CONFIG as t, LogixiaLogger as u };
481
- //# sourceMappingURL=logitron-logger.module-KU_L04y1.d.mts.map
572
+ export { LogixiaOptionsFactory as a, LogixiaLoggerService as c, LogixiaLogger as d, createLogger as f, usePlugin as g, globalPluginRegistry as h, LogixiaLoggerModule as i, LogixiaServiceWith as l, PluginRegistry as m, LOGIXIA_LOGGER_PREFIX as n, WebSocketTraceInterceptor as o, LogixiaPlugin as p, LogixiaAsyncOptions as r, KafkaTraceInterceptor as s, LOGIXIA_LOGGER_CONFIG as t, LogixiaServiceWithLevels as u };
573
+ //# sourceMappingURL=logitron-logger.module-Cd5M_59H.d.mts.map