logixia 1.12.6 → 1.13.3
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.
- package/README.md +19 -4
- package/dist/{async-context-CwBNMm2E.mjs → async-context-BzJrbfQs.mjs} +8 -4
- package/dist/async-context-BzJrbfQs.mjs.map +1 -0
- package/dist/{async-context-CSHWoHMB.js → async-context-DyQQYq3I.js} +8 -4
- package/dist/async-context-DyQQYq3I.js.map +1 -0
- package/dist/{azure-monitor.transport-CG7ClkYj.d.ts → azure-monitor.transport-BDFO4F-i.d.ts} +2 -2
- package/dist/{azure-monitor.transport-CG7ClkYj.d.ts.map → azure-monitor.transport-BDFO4F-i.d.ts.map} +1 -1
- package/dist/{azure-monitor.transport-Cm74kXZS.d.mts → azure-monitor.transport-D5wQEH04.d.mts} +2 -2
- package/dist/{azure-monitor.transport-Cm74kXZS.d.mts.map → azure-monitor.transport-D5wQEH04.d.mts.map} +1 -1
- package/dist/azure.d.mts +2 -2
- package/dist/azure.d.ts +2 -2
- package/dist/cli/index.js +98 -52
- package/dist/cloudwatch.d.mts +2 -2
- package/dist/cloudwatch.d.ts +2 -2
- package/dist/cloudwatch.js +2 -1
- package/dist/cloudwatch.mjs +2 -1
- package/dist/{cloudwatch.transport-CGHY4buE.d.mts → cloudwatch.transport-B00st6Xv.d.ts} +9 -2
- package/dist/cloudwatch.transport-B00st6Xv.d.ts.map +1 -0
- package/dist/{cloudwatch.transport-pNBNWJLt.js → cloudwatch.transport-BO1ahxw-.js} +26 -19
- package/dist/cloudwatch.transport-BO1ahxw-.js.map +1 -0
- package/dist/{cloudwatch.transport-DK_8cKd3.d.ts → cloudwatch.transport-Blc7Gtxt.d.mts} +9 -2
- package/dist/cloudwatch.transport-Blc7Gtxt.d.mts.map +1 -0
- package/dist/{cloudwatch.transport-yBwm4OeO.mjs → cloudwatch.transport-DZ6ies7g.mjs} +26 -19
- package/dist/cloudwatch.transport-DZ6ies7g.mjs.map +1 -0
- package/dist/{coerce.utils-BN980Y9V.js → coerce.utils-B-LilukN.js} +1 -1
- package/dist/{coerce.utils-BN980Y9V.js.map → coerce.utils-B-LilukN.js.map} +1 -1
- package/dist/{coerce.utils-Dxv4_ikC.mjs → coerce.utils-Cu6IUTgX.mjs} +1 -1
- package/dist/{coerce.utils-Dxv4_ikC.mjs.map → coerce.utils-Cu6IUTgX.mjs.map} +1 -1
- package/dist/correlation.d.mts +31 -6
- package/dist/correlation.d.mts.map +1 -1
- package/dist/correlation.d.ts +31 -6
- package/dist/correlation.d.ts.map +1 -1
- package/dist/correlation.js +45 -20
- package/dist/correlation.js.map +1 -1
- package/dist/correlation.mjs +45 -20
- package/dist/correlation.mjs.map +1 -1
- package/dist/entry-fields-BJkuPnIg.mjs +30 -0
- package/dist/entry-fields-BJkuPnIg.mjs.map +1 -0
- package/dist/entry-fields-BvOLFlJ4.js +36 -0
- package/dist/entry-fields-BvOLFlJ4.js.map +1 -0
- package/dist/gcp.d.mts +2 -2
- package/dist/gcp.d.ts +2 -2
- package/dist/gcp.js +2 -1
- package/dist/gcp.mjs +2 -1
- package/dist/{gcp.transport-SHdtc1n0.js → gcp.transport-CHV8MGgz.js} +5 -5
- package/dist/gcp.transport-CHV8MGgz.js.map +1 -0
- package/dist/{gcp.transport-BJuNRFe_.mjs → gcp.transport-DBX5TPIy.mjs} +5 -5
- package/dist/gcp.transport-DBX5TPIy.mjs.map +1 -0
- package/dist/{gcp.transport-BOhkVKlI.d.ts → gcp.transport-m4fVw0b1.d.ts} +2 -2
- package/dist/gcp.transport-m4fVw0b1.d.ts.map +1 -0
- package/dist/{gcp.transport-Bi6CUqZR.d.mts → gcp.transport-zzNyIoih.d.mts} +2 -2
- package/dist/gcp.transport-zzNyIoih.d.mts.map +1 -0
- package/dist/{index-BPKwwERo.d.mts → index-BPwEisy4.d.mts} +2 -2
- package/dist/{index-BPKwwERo.d.mts.map → index-BPwEisy4.d.mts.map} +1 -1
- package/dist/{index-C1X-TE5I.d.ts → index-nZEeCr3X.d.ts} +2 -2
- package/dist/{index-C1X-TE5I.d.ts.map → index-nZEeCr3X.d.ts.map} +1 -1
- package/dist/index.d.mts +63 -98
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +63 -98
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +167 -443
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +98 -377
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.d.mts +11 -4
- package/dist/middleware.d.mts.map +1 -1
- package/dist/middleware.d.ts +11 -4
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +148 -24
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +148 -24
- package/dist/middleware.mjs.map +1 -1
- package/dist/nest.d.mts +4 -4
- package/dist/nest.d.ts +4 -4
- package/dist/nest.js +20 -11
- package/dist/nest.mjs +7 -5
- package/dist/{logitron-logger.module-BsDZR5BH.js → nestjs-extras-DnJ6vrIC.js} +562 -444
- package/dist/nestjs-extras-DnJ6vrIC.js.map +1 -0
- package/dist/{logitron-logger.module-Bs3XYZgF.mjs → nestjs-extras-ImbuDhs1.mjs} +505 -349
- package/dist/nestjs-extras-ImbuDhs1.mjs.map +1 -0
- package/dist/{logitron-logger.module-zDKVd_o0.d.mts → nestjs-extras-OkC796py.d.mts} +113 -6
- package/dist/nestjs-extras-OkC796py.d.mts.map +1 -0
- package/dist/{logitron-logger.module-CxT1F6Cm.d.ts → nestjs-extras-mKrHE9S6.d.ts} +113 -6
- package/dist/nestjs-extras-mKrHE9S6.d.ts.map +1 -0
- package/dist/{search-njTI2hdo.js → search-B0MH1SJJ.js} +23 -10
- package/dist/search-B0MH1SJJ.js.map +1 -0
- package/dist/{search-DujhR94O.d.ts → search-BfBzDIT9.d.mts} +2 -3
- package/dist/search-BfBzDIT9.d.mts.map +1 -0
- package/dist/{search-CQOo-Iq6.d.mts → search-CHUZPl0h.d.ts} +2 -3
- package/dist/search-CHUZPl0h.d.ts.map +1 -0
- package/dist/{search-CFQRISUv.mjs → search-DjXSn6v5.mjs} +23 -10
- package/dist/search-DjXSn6v5.mjs.map +1 -0
- package/dist/search.d.mts +3 -3
- package/dist/search.d.ts +3 -3
- package/dist/search.js +2 -2
- package/dist/search.mjs +2 -2
- package/dist/testing.d.mts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/trace.utils-CvXgaiQT.mjs +278 -0
- package/dist/trace.utils-CvXgaiQT.mjs.map +1 -0
- package/dist/trace.utils-DpBOLDtq.js +358 -0
- package/dist/trace.utils-DpBOLDtq.js.map +1 -0
- package/dist/{transport.manager-D6KL3t9V.mjs → transport.manager-CpdVHEGP.mjs} +365 -86
- package/dist/transport.manager-CpdVHEGP.mjs.map +1 -0
- package/dist/{transport.manager-BivyUTtU.js → transport.manager-Crau3Rle.js} +370 -85
- package/dist/transport.manager-Crau3Rle.js.map +1 -0
- package/dist/{transport.types-B5vcyv2k.d.mts → transport.types-CtNr3D7S.d.mts} +19 -2
- package/dist/transport.types-CtNr3D7S.d.mts.map +1 -0
- package/dist/{transport.types-BuY84TwO.d.ts → transport.types-DqVLZTyf.d.ts} +19 -2
- package/dist/transport.types-DqVLZTyf.d.ts.map +1 -0
- package/dist/transports.d.mts +38 -13
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +38 -13
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +6 -5
- package/dist/transports.mjs +6 -5
- package/dist/worker-entry.js +307 -55
- package/dist/worker.d.mts +2 -2
- package/dist/worker.d.ts +2 -2
- package/dist/worker.js +1 -1
- package/dist/worker.mjs +1 -1
- package/dist/{worker.transport-BP8Q28Z2.d.mts → worker.transport-Bgv9heDI.d.mts} +2 -2
- package/dist/{worker.transport-BP8Q28Z2.d.mts.map → worker.transport-Bgv9heDI.d.mts.map} +1 -1
- package/dist/{worker.transport-mKXgNK0L.d.ts → worker.transport-Bj-i0P9V.d.ts} +2 -2
- package/dist/{worker.transport-mKXgNK0L.d.ts.map → worker.transport-Bj-i0P9V.d.ts.map} +1 -1
- package/dist/{worker.transport-CfH6z37Z.js → worker.transport-JP8YHBuU.js} +1 -1
- package/dist/{worker.transport-CfH6z37Z.js.map → worker.transport-JP8YHBuU.js.map} +1 -1
- package/dist/{worker.transport-BhMyvqQ2.mjs → worker.transport-otwhJ_Et.mjs} +1 -1
- package/dist/{worker.transport-BhMyvqQ2.mjs.map → worker.transport-otwhJ_Et.mjs.map} +1 -1
- package/package.json +10 -6
- package/dist/.tsbuildinfo +0 -1
- package/dist/async-context-CSHWoHMB.js.map +0 -1
- package/dist/async-context-CwBNMm2E.mjs.map +0 -1
- package/dist/cloudwatch.transport-CGHY4buE.d.mts.map +0 -1
- package/dist/cloudwatch.transport-DK_8cKd3.d.ts.map +0 -1
- package/dist/cloudwatch.transport-pNBNWJLt.js.map +0 -1
- package/dist/cloudwatch.transport-yBwm4OeO.mjs.map +0 -1
- package/dist/gcp.transport-BJuNRFe_.mjs.map +0 -1
- package/dist/gcp.transport-BOhkVKlI.d.ts.map +0 -1
- package/dist/gcp.transport-Bi6CUqZR.d.mts.map +0 -1
- package/dist/gcp.transport-SHdtc1n0.js.map +0 -1
- package/dist/logitron-logger.module-Bs3XYZgF.mjs.map +0 -1
- package/dist/logitron-logger.module-BsDZR5BH.js.map +0 -1
- package/dist/logitron-logger.module-CxT1F6Cm.d.ts.map +0 -1
- package/dist/logitron-logger.module-zDKVd_o0.d.mts.map +0 -1
- package/dist/search-CFQRISUv.mjs.map +0 -1
- package/dist/search-CQOo-Iq6.d.mts.map +0 -1
- package/dist/search-DujhR94O.d.ts.map +0 -1
- package/dist/search-njTI2hdo.js.map +0 -1
- package/dist/transport.manager-BivyUTtU.js.map +0 -1
- package/dist/transport.manager-D6KL3t9V.mjs.map +0 -1
- package/dist/transport.types-B5vcyv2k.d.mts.map +0 -1
- package/dist/transport.types-BuY84TwO.d.ts.map +0 -1
- package/scripts/postinstall.js +0 -61
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["TraceContext","cause: string | undefined","debug","errorBlock: LogixiaErrorResponse<TCode, TType>['error']","message: string","LOGIXIA_LOGGER_PREFIX","emit","logger","errLog: unknown","logger: LogixiaLoggerService | undefined","LogixiaLoggerModule","entry: Record<string, unknown>","exit: Record<string, unknown>","result","result: unknown","LogixiaExceptionFilter","logger?: LogixiaLoggerService","loggerConfig?: Partial<LoggerConfig>","TraceContext","requestMeta: Record<string, unknown>","logPromise: Promise<void> | void","resolveResponseHeader","LOGIXIA_LOGGER_CONFIG","formatted: Record<string, unknown>","serializeError","serialized: Record<string, unknown>","safeToString","parts: string[]","LogLevel","DEFAULT_CYCLE: readonly LogLevelString[]","logger","parsed: { level?: unknown; namespaceLevels?: unknown }","coerced: NamespaceLevels","warnings: string[]","logger","AsyncLocalStorage","payload: WideEventFields","getCurrentTraceId","logger","state: WideEventState","stringifyForLog","DEFAULT_REQUEST_TIMEOUT_MS","BatchBuffer","DEFAULT_MAX_BUFFER_SIZE","attrs: Record<string, unknown>","record: Record<string, unknown>","HttpError","DEFAULT_BUCKETS: readonly number[]","pairs: Record<string, string>","lines: string[]","LogLevel","createLoggerFromCore","LogixiaLoggerService","LogixiaLogger"],"sources":["../src/exceptions/exception.ts","../src/exceptions/builder.ts","../src/core/nestjs-extras.ts","../src/formatters/json.formatter.ts","../src/formatters/text.formatter.ts","../src/utils/runtime-control.ts","../src/utils/typed-logger.ts","../src/wide-events.ts","../src/transports/otlp.transport.ts","../src/metrics.ts","../src/index.ts"],"sourcesContent":["/**\n * LogixiaException — a strictly typed, framework-agnostic exception class.\n *\n * You bring your own error codes and types; logixia enforces the shape.\n * The `ErrorResponseBuilder` knows how to turn a `LogixiaException` into the\n * standard `LogixiaErrorResponse` wire format.\n *\n * @template TCode - Your error code union. e.g. `'PE-AUTH-001' | 'PE-USR-001'`\n * @template TType - Your error type union. e.g. `'authentication_error' | 'validation_error'`\n *\n * @example Basic throw\n * ```ts\n * throw new LogixiaException({\n * code: 'PE-AUTH-001',\n * type: 'authentication_error',\n * httpStatus: 401,\n * message: 'Invalid email or password.',\n * });\n * ```\n *\n * @example Fully typed with your own code/type unions\n * ```ts\n * type AppCode = 'PE-AUTH-001' | 'PE-VAL-001';\n * type AppType = 'authentication_error' | 'validation_error';\n *\n * throw new LogixiaException<AppCode, AppType>({\n * code: 'PE-VAL-001',\n * type: 'validation_error',\n * httpStatus: 400,\n * message: 'Validation failed.',\n * details: [\n * { field: 'email', message: 'must be a valid email', code: 'invalid_format' },\n * ],\n * });\n * ```\n *\n * @example With field pointer (Stripe param pattern)\n * ```ts\n * throw new LogixiaException({\n * code: 'PE-USR-002',\n * type: 'conflict_error',\n * httpStatus: 409,\n * message: 'This email is already in use.',\n * param: 'email',\n * });\n * ```\n *\n * @example With cause chain (ES 2022)\n * ```ts\n * try {\n * await db.save(user);\n * } catch (err) {\n * throw new LogixiaException({\n * code: 'PE-DB-001',\n * type: 'server_error',\n * httpStatus: 409,\n * message: 'Duplicate record.',\n * cause: err instanceof Error ? err : undefined,\n * });\n * }\n * ```\n */\n\nimport type { ErrorDetail } from './types.js';\n\n// ── Options ───────────────────────────────────────────────────────────────────\n\n/**\n * Constructor options for `LogixiaException`.\n *\n * All user-supplied fields — logixia only owns the wire format.\n */\nexport interface LogixiaExceptionOptions<\n TCode extends string = string,\n TType extends string = string,\n> {\n /**\n * Machine-readable, stable error code.\n * e.g. `'PE-AUTH-001'`\n */\n code: TCode;\n\n /**\n * Broad error type category.\n * e.g. `'authentication_error'` | `'validation_error'` | `'rate_limit_error'`\n */\n type: TType;\n\n /**\n * HTTP status code to send on the wire.\n * e.g. `401` | `400` | `429` | `500`\n */\n httpStatus: number;\n\n /**\n * Human-readable message for the end-user.\n * This IS sent in the response body — keep it safe (no internal paths, etc.).\n */\n message: string;\n\n /**\n * The specific request field that caused this error (Stripe pattern).\n * e.g. `'email'` | `'metadata.name'`\n */\n param?: string;\n\n /**\n * Array of per-field validation errors (GitHub pattern).\n * Typically used for `400` / `422` validation failures.\n */\n details?: ErrorDetail[];\n\n /**\n * Documentation URL for this error (Twilio pattern).\n * e.g. `'https://docs.example.com/errors/PE-AUTH-001'`\n */\n docUrl?: string;\n\n /**\n * Original error for ES 2022 cause chaining.\n * Serialised into `debug.cause` by the `ErrorResponseBuilder`.\n * Never sent to the client in production.\n */\n cause?: Error;\n\n /**\n * Arbitrary metadata for logging purposes only.\n * This is **never** included in the HTTP response — it is passed to your\n * logger / monitoring integrations through the exception object.\n *\n * @example\n * ```ts\n * metadata: { userId: 'u_abc', attemptCount: 3 }\n * ```\n */\n metadata?: Record<string, unknown>;\n}\n\n// ── Exception class ───────────────────────────────────────────────────────────\n\nexport class LogixiaException<\n TCode extends string = string,\n TType extends string = string,\n> extends Error {\n /** @readonly The machine-readable error code supplied by the caller. */\n public readonly errorCode: TCode;\n /** @readonly The error type category supplied by the caller. */\n public readonly errorType: TType;\n /** @readonly HTTP status code to respond with. */\n public readonly httpStatus: number;\n /** @readonly Field pointer that caused this error (Stripe pattern). */\n public readonly param: string | undefined;\n /** @readonly Field-level validation errors (GitHub pattern). */\n public readonly details: ErrorDetail[] | undefined;\n /** @readonly Documentation URL for this error (Twilio pattern). */\n public readonly docUrl: string | undefined;\n /**\n * @readonly Extra context for your logger — never serialised into the HTTP response.\n */\n public readonly metadata: Record<string, unknown> | undefined;\n\n constructor(options: LogixiaExceptionOptions<TCode, TType>) {\n // Pass `cause` through the standard ES 2022 Error options so native\n // tooling (Node.js, Sentry, etc.) can walk the full error chain.\n super(options.message, { cause: options.cause });\n\n this.name = 'LogixiaException';\n this.errorCode = options.code;\n this.errorType = options.type;\n this.httpStatus = options.httpStatus;\n this.param = options.param;\n this.details = options.details;\n this.docUrl = options.docUrl;\n this.metadata = options.metadata;\n\n // Restore the correct prototype chain when compiling to ES5.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ── Type guard ────────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` when `value` is a `LogixiaException` instance.\n *\n * Useful in exception filters or middleware that need to distinguish a\n * `LogixiaException` from a plain `Error` or a framework `HttpException`.\n *\n * @example\n * ```ts\n * if (isLogixiaException(err)) {\n * console.log(err.errorCode, err.httpStatus);\n * }\n * ```\n */\nexport function isLogixiaException<TCode extends string = string, TType extends string = string>(\n value?: unknown\n): value is LogixiaException<TCode, TType> {\n return value instanceof LogixiaException;\n}\n","/**\n * ErrorResponseBuilder — turns any exception into a `LogixiaErrorResponse`.\n *\n * Priority order:\n * 1. `LogixiaException` — typed fields used directly\n * 2. NestJS `HttpException` — duck-typed; status + message extracted\n * 3. Plain `Error` / unknown — falls back to 500 `server_error`\n *\n * The builder **always** populates `debug` when possible.\n * Your exception filter is responsible for stripping it in production:\n * ```ts\n * if (process.env.NODE_ENV === 'production') delete response.debug;\n * ```\n */\n\nimport { TraceContext } from '../utils/trace.utils.js';\nimport { isLogixiaException } from './exception.js';\nimport type { LogixiaErrorResponse } from './types.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\n/** Input parameters for `ErrorResponseBuilder.build`. */\nexport interface BuildParams {\n /** The caught exception — any type. */\n exception: unknown;\n /**\n * Trace ID to embed in `meta.trace_id`.\n * If omitted a UUID-based ID is auto-generated.\n */\n traceId?: string | undefined;\n /** Request path. e.g. `'/api/v1/auth/login'` */\n path: string;\n /**\n * Originating service name for `debug.service`.\n * e.g. `process.env.SERVICE_NAME` → `'gatekeeper'`\n */\n service?: string | undefined;\n /**\n * `Date.now()` captured at the start of the request.\n * When present, `debug.duration_ms` is computed automatically.\n */\n startTime?: number | undefined;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Generates a trace ID via the shared {@link TraceContext} generator.\n *\n * Delegates to `TraceContext.instance.generate()` so any custom generator\n * configured on `TraceIdConfig.generator` is respected — avoids the silent\n * \"builder has its own UUID source\" divergence this module had previously.\n *\n * @example `'550e8400-e29b-41d4-a716-446655440000'`\n */\nexport function generateTraceId(): string {\n return TraceContext.instance.generate();\n}\n\n/** @deprecated Use `generateTraceId` instead. */\nexport const generateRequestId = generateTraceId;\n\n/**\n * Maps an HTTP status code to a human-friendly error type string.\n * Used as a fallback when the exception is a generic `HttpException`.\n */\nfunction httpStatusToType(status: number): string {\n if (status === 400) return 'api_error';\n if (status === 401) return 'authentication_error';\n if (status === 402) return 'payment_error';\n if (status === 403) return 'authorization_error';\n if (status === 404) return 'not_found_error';\n if (status === 408) return 'timeout_error';\n if (status === 409) return 'conflict_error';\n if (status === 422) return 'validation_error';\n if (status === 429) return 'rate_limit_error';\n if (status >= 500) return 'server_error';\n if (status >= 400) return 'api_error';\n return 'server_error';\n}\n\n/**\n * Duck-type check for NestJS `HttpException`.\n * Avoids a hard import on `@nestjs/common` so the builder is usable outside NestJS.\n */\nfunction isHttpException(\n value: unknown\n): value is { getStatus(): number; getResponse(): unknown; message: string } {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Record<string, unknown>)['getStatus'] === 'function' &&\n typeof (value as Record<string, unknown>)['getResponse'] === 'function'\n );\n}\n\n/**\n * Builds the optional `debug` block from an error.\n * Returns `undefined` when there is nothing useful to include.\n */\nfunction buildDebug(\n error: unknown,\n service: string | undefined,\n durationMs: number | undefined\n): LogixiaErrorResponse['debug'] {\n const stack = error instanceof Error ? error.stack : undefined;\n\n const causeRaw =\n error instanceof Error ? (error as Error & { cause?: unknown }).cause : undefined;\n let cause: string | undefined;\n if (causeRaw instanceof Error) {\n cause = causeRaw.message;\n } else if (causeRaw !== undefined && causeRaw !== null) {\n cause = String(causeRaw);\n }\n\n if (!stack && !cause && !service && durationMs === undefined) return undefined;\n\n return {\n ...(stack !== undefined ? { stack } : {}),\n ...(cause !== undefined ? { cause } : {}),\n ...(service !== undefined ? { service } : {}),\n ...(durationMs !== undefined ? { duration_ms: durationMs } : {}),\n };\n}\n\n// ── Builder ───────────────────────────────────────────────────────────────────\n\nexport class ErrorResponseBuilder {\n /**\n * Build a `LogixiaErrorResponse` from any thrown value.\n *\n * @param params - See `BuildParams`.\n * @returns The unified error response and the HTTP status code to send.\n *\n * @remarks\n * `traceId` does not need to be supplied by the caller in most cases — the\n * builder reads `TraceContext.instance.getCurrentTraceId()` internally and\n * falls back to the explicit `traceId` argument only when AsyncLocalStorage\n * is empty. If neither source yields a value, `meta.trace_id` is omitted.\n *\n * @example In a NestJS exception filter\n * ```ts\n * const { response, httpStatus } = ErrorResponseBuilder.build<AppCode, AppType>({\n * exception,\n * // Omit traceId — it will be picked up from ALS automatically.\n * path: request.url,\n * service: process.env.SERVICE_NAME,\n * startTime: request.startTime,\n * });\n *\n * if (process.env.NODE_ENV === 'production') delete response.debug;\n * // Echo the resolved traceId back on whatever header your TraceIdConfig says —\n * // do NOT hardcode 'X-Trace-Id', use `resolveResponseHeader(config)` instead.\n * if (response.meta.trace_id) {\n * response.setHeader(resolveResponseHeader(config) ?? 'X-Trace-Id', response.meta.trace_id);\n * }\n * response.status(httpStatus).json(response);\n * ```\n */\n static build<TCode extends string = string, TType extends string = string>(\n params: BuildParams\n ): { response: LogixiaErrorResponse<TCode, TType>; httpStatus: number } {\n const { exception, path, service, startTime, traceId: rawTraceId } = params;\n // ALS is authoritative → caller-supplied → undefined (field omitted from response).\n const traceId = TraceContext.instance.getCurrentTraceId() ?? rawTraceId;\n const timestamp = new Date().toISOString();\n const durationMs = startTime !== undefined ? Date.now() - startTime : undefined;\n\n // ── 1. LogixiaException ────────────────────────────────────────────────\n if (isLogixiaException<TCode, TType>(exception)) {\n const debug = buildDebug(exception, service, durationMs);\n\n const errorBlock: LogixiaErrorResponse<TCode, TType>['error'] = {\n type: exception.errorType,\n code: exception.errorCode,\n message: exception.message,\n };\n if (exception.param !== undefined) errorBlock.param = exception.param;\n if (exception.details !== undefined && exception.details.length > 0) {\n errorBlock.details = exception.details;\n }\n if (exception.docUrl !== undefined) errorBlock.doc_url = exception.docUrl;\n\n const response: LogixiaErrorResponse<TCode, TType> = {\n success: false,\n error: errorBlock,\n meta: {\n ...(traceId !== undefined ? { trace_id: traceId } : {}),\n timestamp,\n path,\n status: exception.httpStatus,\n },\n ...(debug !== undefined ? { debug } : {}),\n };\n\n return { response, httpStatus: exception.httpStatus };\n }\n\n // ── 2. NestJS HttpException (duck-typed) ──────────────────────────────\n if (isHttpException(exception)) {\n const status = exception.getStatus();\n const exResponse = exception.getResponse();\n\n // NestJS nests the message: { statusCode, message, error } or a plain string\n let message: string;\n if (typeof exResponse === 'string') {\n message = exResponse;\n } else if (typeof exResponse === 'object' && exResponse !== null && 'message' in exResponse) {\n const raw = (exResponse as { message: unknown }).message;\n message = Array.isArray(raw) ? raw.join(', ') : String(raw);\n } else {\n message = exception.message;\n }\n\n const type = httpStatusToType(status) as TType;\n const code = `HTTP_${status}` as TCode;\n const debug = buildDebug(exception, service, durationMs);\n\n const response: LogixiaErrorResponse<TCode, TType> = {\n success: false,\n error: { type, code, message },\n meta: { ...(traceId !== undefined ? { trace_id: traceId } : {}), timestamp, path, status },\n ...(debug !== undefined ? { debug } : {}),\n };\n\n return { response, httpStatus: status };\n }\n\n // ── 3. Unknown / plain Error ──────────────────────────────────────────\n const err = exception instanceof Error ? exception : new Error(String(exception));\n const debug = buildDebug(err, service, durationMs);\n\n const response: LogixiaErrorResponse<TCode, TType> = {\n success: false,\n error: {\n type: 'server_error' as TType,\n code: 'INTERNAL_SERVER_ERROR' as TCode,\n message: 'An unexpected error occurred.',\n },\n meta: {\n ...(traceId !== undefined ? { trace_id: traceId } : {}),\n timestamp,\n path,\n status: 500,\n },\n ...(debug !== undefined ? { debug } : {}),\n };\n\n return { response, httpStatus: 500 };\n }\n}\n","/**\n * logixia — NestJS extras: @InjectLogger, @LogMethod, LogixiaExceptionFilter\n *\n * Completes the NestJS deep integration story:\n * - @InjectLogger() — inject the logger via NestJS DI without typing LOGIXIA_TOKEN manually\n * - @LogMethod() — auto-log method entry / exit with args and return value\n * - LogixiaExceptionFilter — catch-all exception filter that logs unhandled errors\n * with full request context before re-throwing\n *\n * @example Full setup\n * ```ts\n * // app.module.ts\n * import { LogixiaModule } from 'logixia';\n * @Module({ imports: [LogixiaModule.forRoot({ appName: 'api' })] })\n * export class AppModule {}\n *\n * // order.service.ts\n * import { Injectable } from '@nestjs/common';\n * import { InjectLogger, LogMethod, LogixiaLoggerService } from 'logixia';\n *\n * @Injectable()\n * export class OrderService {\n * constructor(@InjectLogger() private readonly logger: LogixiaLoggerService) {}\n *\n * @LogMethod()\n * async createOrder(dto: CreateOrderDto) { … }\n * }\n *\n * // main.ts\n * import { LogixiaExceptionFilter } from 'logixia';\n * app.useGlobalFilters(new LogixiaExceptionFilter(logger));\n * ```\n */\n\nimport type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';\nimport { Catch, Inject, Optional } from '@nestjs/common';\n\nimport { ErrorResponseBuilder } from '../exceptions/builder';\nimport { isLogixiaException } from '../exceptions/exception';\nimport type { LoggerConfig, LogLevelString, TraceIdConfig } from '../types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { TraceContext } from '../utils/trace.utils';\nimport {\n LOGIXIA_LOGGER_CONFIG,\n LOGIXIA_LOGGER_PREFIX,\n LogixiaLoggerModule,\n} from './logitron-logger.module';\nimport type { LogixiaLoggerService } from './logitron-nestjs.service';\nimport { resolveResponseHeader } from './trace.middleware';\n\n// ── @InjectLogger() ──────────────────────────────────────────────────────────\n\n/**\n * Inject the Logixia logger registered in the current NestJS DI container.\n *\n * Equivalent to `@Inject(LOGIXIA_LOGGER_TOKEN)` but without needing to import\n * the internal token constant yourself.\n *\n * @example\n * ```ts\n * constructor(@InjectLogger() private readonly logger: LogixiaLoggerService) {}\n * ```\n */\nexport const InjectLogger = (): ParameterDecorator => Inject(`${LOGIXIA_LOGGER_PREFIX}SERVICE`);\n\n// ── @LogMethod() ─────────────────────────────────────────────────────────────\n\nexport interface LogMethodOptions {\n /**\n * Log level to use for entry / exit messages.\n * @default 'debug'\n */\n level?: LogLevelString;\n /**\n * Whether to log the arguments passed to the method.\n * Disable for high-throughput hot paths.\n * @default true\n */\n logArgs?: boolean;\n /**\n * Whether to log the return value of the method.\n * @default false\n */\n logResult?: boolean;\n /**\n * Whether to log error stack traces for thrown errors.\n * @default true\n */\n logErrors?: boolean;\n /**\n * Custom label used in log messages instead of the auto-detected class.method name.\n */\n label?: string;\n}\n\n/**\n * Method decorator that auto-logs entry, exit, duration, and errors.\n *\n * Preserves the original method's sync/async contract: a synchronous method\n * stays synchronous (returns its value directly, with logs emitted\n * fire-and-forget), and an async method is awaited so exit/error logs reflect\n * the resolved result. Attaches to the logger found on the class instance via a\n * `logger` property (the conventional NestJS name).\n *\n * @example\n * ```ts\n * @LogMethod({ level: 'info', logArgs: true })\n * async processPayment(orderId: string): Promise<void> { … }\n * ```\n */\nexport function LogMethod(options: LogMethodOptions = {}): MethodDecorator {\n const { level = 'debug', logArgs = true, logResult = false, logErrors = true } = options;\n\n // Use PropertyDescriptor (not generic TypedPropertyDescriptor<T>) to avoid\n // strict generic inference issues with exactOptionalPropertyTypes.\n return function (\n target: object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n const originalMethod = descriptor.value as ((...args: unknown[]) => unknown) | undefined;\n if (typeof originalMethod !== 'function') return descriptor;\n\n const methodName = String(propertyKey);\n const className =\n (target as { constructor?: { name?: string } }).constructor?.name ?? 'Unknown';\n const label = options.label ?? `${className}.${methodName}`;\n\n let _warnedNoLogger = false;\n\n // Surface logger failures on stderr instead of silently swallowing them.\n const reportLogFailure = (phase: string, err: unknown): void => {\n internalError(`@LogMethod(${label}) ${phase} log failed: ${String(err)}`);\n };\n\n // Emit a log line fire-and-forget, routing transport failures to stderr.\n const emit = (\n logger: LogixiaLoggerService,\n phase: string,\n message: string,\n data: Record<string, unknown>\n ): void => {\n const logFnRaw = (\n logger as unknown as Record<\n string,\n (msg: string, data?: Record<string, unknown>) => Promise<void>\n >\n )[level];\n const logFn = (typeof logFnRaw === 'function' ? logFnRaw : logger.debug).bind(logger);\n const p = logFn(message, data);\n if (p && typeof (p as Promise<void>).catch === 'function') {\n (p as Promise<void>).catch((e: unknown) => reportLogFailure(phase, e));\n }\n };\n\n const emitError = (logger: LogixiaLoggerService, error: unknown, start: number): void => {\n const err = error instanceof Error ? error : new Error(String(error));\n const errLog: unknown = logger.error(err, { method: label, durationMs: Date.now() - start });\n if (errLog && typeof (errLog as Promise<void>).catch === 'function') {\n (errLog as Promise<void>).catch((e: unknown) => reportLogFailure('error', e));\n }\n };\n\n descriptor.value = function (this: { logger?: LogixiaLoggerService }, ...args: unknown[]) {\n // Prefer the instance's own logger; fall back to the global module logger.\n const logger: LogixiaLoggerService | undefined =\n this.logger ?? LogixiaLoggerModule._globalLogger ?? undefined;\n\n if (!logger && !_warnedNoLogger) {\n _warnedNoLogger = true;\n internalWarn(\n `@LogMethod on ${label}: no logger available. ` +\n `Either inject LogixiaLoggerService as this.logger or ensure LogixiaLoggerModule is initialised.`\n );\n }\n\n const start = Date.now();\n const entry: Record<string, unknown> = { method: label };\n if (logArgs && args.length > 0) entry['args'] = args;\n // Entry log is fire-and-forget so a SYNC method is not forced to become\n // async just to await it.\n if (logger) emit(logger, 'entry', `→ ${label}`, entry);\n\n const buildExit = (result: unknown): Record<string, unknown> => {\n const exit: Record<string, unknown> = { method: label, durationMs: Date.now() - start };\n if (logResult) exit['result'] = result;\n return exit;\n };\n\n let result: unknown;\n try {\n result = originalMethod.apply(this, args);\n } catch (error) {\n // Synchronous throw.\n if (logger && logErrors) emitError(logger, error, start);\n throw error;\n }\n\n // Async method → await via the returned thenable so exit/error reflect the\n // resolved outcome, and preserve the Promise return type.\n if (result && typeof (result as PromiseLike<unknown>).then === 'function') {\n return (result as Promise<unknown>).then(\n (resolved) => {\n if (logger) emit(logger, 'exit', `← ${label}`, buildExit(resolved));\n return resolved;\n },\n (error: unknown) => {\n if (logger && logErrors) emitError(logger, error, start);\n throw error;\n }\n );\n }\n\n // Synchronous method → log exit fire-and-forget and return the value as-is,\n // preserving the original synchronous contract.\n if (logger) emit(logger, 'exit', `← ${label}`, buildExit(result));\n return result;\n };\n\n return descriptor;\n };\n}\n\n// ── LogixiaExceptionFilter ───────────────────────────────────────────────────\n\n/**\n * Global NestJS exception filter that converts any exception into the standard\n * `LogixiaErrorResponse` wire format and logs it with full request context.\n *\n * Handles three exception types in priority order:\n * 1. `LogixiaException` — typed fields used directly\n * 2. NestJS `HttpException` — status + message extracted\n * 3. Unknown / plain Error — falls back to 500 `server_error`\n *\n * **Debug stripping in production:**\n * The `debug` block is automatically stripped when `NODE_ENV === 'production'`.\n *\n * **Trace ID headers:**\n * `X-Trace-ID` and `X-Request-ID` are echoed back on every error response so\n * clients can correlate with server logs.\n *\n * **Retry-After header:**\n * Automatically added for `429` responses (`Retry-After: 60`).\n *\n * Register in `main.ts`:\n * ```ts\n * const logger = app.get(LogixiaLoggerService);\n * app.useGlobalFilters(new LogixiaExceptionFilter(logger));\n * ```\n */\n@Catch()\nexport class LogixiaExceptionFilter implements ExceptionFilter {\n private readonly _logger: LogixiaLoggerService | undefined;\n private readonly _traceConfig: TraceIdConfig | undefined;\n\n constructor(\n @Optional()\n @Inject(`${LOGIXIA_LOGGER_PREFIX}SERVICE`)\n logger?: LogixiaLoggerService,\n @Optional()\n @Inject(LOGIXIA_LOGGER_CONFIG)\n loggerConfig?: Partial<LoggerConfig>\n ) {\n // Prefer the injected logger; fall back to the global module logger so\n // `new LogixiaExceptionFilter()` (registered in main.ts before DI resolves)\n // still logs without needing an explicit logger argument.\n this._logger = logger ?? LogixiaLoggerModule._globalLogger ?? undefined;\n // Pick up the user-configured trace settings (response header name, etc.)\n // so the filter echoes the same header the middleware writes.\n this._traceConfig =\n typeof loggerConfig?.traceId === 'object' ? loggerConfig.traceId : undefined;\n }\n\n catch(exception: unknown, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n\n const request = ctx.getRequest<{\n method?: string;\n url?: string;\n id?: string;\n startTime?: number;\n headers?: Record<string, string | string[] | undefined>;\n }>();\n\n interface MinimalResponse {\n status(code: number): MinimalResponse;\n json(body: unknown): void;\n setHeader(name: string, value: string): void;\n }\n const response = ctx.getResponse<MinimalResponse>();\n\n // ALS is the single source of truth (set by TraceMiddleware).\n // Fall back to header/request.id only if ALS has nothing (e.g. traceId disabled).\n const traceId =\n TraceContext.instance.getCurrentTraceId() ??\n (request.headers?.['x-trace-id'] as string | undefined) ??\n request.id ??\n undefined;\n\n const { response: errorResponse, httpStatus } = ErrorResponseBuilder.build({\n exception,\n traceId,\n path: request.url ?? '/',\n startTime: request.startTime,\n });\n\n // ── Logging ──────────────────────────────────────────────────────────────\n if (this._logger) {\n const requestMeta: Record<string, unknown> = {\n method: request.method ?? '',\n url: request.url ?? '',\n status: httpStatus,\n // Only include trace_id when it exists — keeps log records honest when\n // tracing is disabled (instead of writing `trace_id: undefined`).\n ...(errorResponse.meta.trace_id !== undefined\n ? { trace_id: errorResponse.meta.trace_id }\n : {}),\n };\n\n // `error()` / `warn()` return Promise<void> for the structured overload.\n // We cannot await inside a synchronous ExceptionFilter.catch(), so attach\n // a `.catch()` to prevent unhandledRejection if a transport fails — and\n // fall back to stderr so the error is never silently swallowed.\n const onLogFailure = (err: unknown): void => {\n internalError(`ExceptionFilter failed to write log entry: ${String(err)}`);\n };\n\n let logPromise: Promise<void> | void;\n if (httpStatus >= 500) {\n const err = exception instanceof Error ? exception : new Error(String(exception));\n logPromise = this._logger.error(err, requestMeta);\n } else if (isLogixiaException(exception)) {\n logPromise = this._logger.warn(\n `[${errorResponse.error.code}] ${errorResponse.error.message}`,\n requestMeta\n );\n } else {\n logPromise = this._logger.warn(\n `[${httpStatus}] ${errorResponse.error.message}`,\n requestMeta\n );\n }\n\n if (logPromise && typeof (logPromise as Promise<void>).catch === 'function') {\n (logPromise as Promise<void>).catch(onLogFailure);\n }\n }\n\n // ── Strip debug in production ─────────────────────────────────────────\n if (process.env['NODE_ENV'] === 'production') {\n delete errorResponse.debug;\n }\n\n // ── Response headers ──────────────────────────────────────────────────\n if (errorResponse.meta.trace_id) {\n const traceHeader = resolveResponseHeader(this._traceConfig);\n if (traceHeader) {\n response.setHeader(traceHeader, errorResponse.meta.trace_id);\n }\n }\n if (httpStatus === 429) {\n response.setHeader('Retry-After', '60');\n }\n\n response.status(httpStatus).json(errorResponse);\n }\n}\n","/**\n * JSON formatter for Logixia\n */\n\nimport type { ILogFormatter, LogEntry } from '../types';\nimport { serializeError } from '../utils/error.utils';\n\n/**\n * Build a JSON.stringify replacer that replaces circular references with the\n * string '[Circular]' instead of throwing. A fresh replacer must be created per\n * stringify call because it holds per-serialization state (the seen set).\n */\nfunction createCircularReplacer(): (key: string, value: unknown) => unknown {\n const seen = new WeakSet<object>();\n return function (this: unknown, _key: string, value: unknown): unknown {\n if (typeof value === 'object' && value !== null) {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n }\n return value;\n };\n}\n\nexport class JsonFormatter implements ILogFormatter {\n private includeTimestamp: boolean;\n private includeLevel: boolean;\n private includeAppName: boolean;\n private includeTraceId: boolean;\n private includeContext: boolean;\n private prettyPrint: boolean;\n\n constructor(\n options: {\n includeTimestamp?: boolean;\n includeLevel?: boolean;\n includeAppName?: boolean;\n includeTraceId?: boolean;\n includeContext?: boolean;\n prettyPrint?: boolean;\n } = {}\n ) {\n this.includeTimestamp = options.includeTimestamp ?? true;\n this.includeLevel = options.includeLevel ?? true;\n this.includeAppName = options.includeAppName ?? true;\n this.includeTraceId = options.includeTraceId ?? true;\n this.includeContext = options.includeContext ?? true;\n this.prettyPrint = options.prettyPrint ?? false;\n }\n\n format(entry: LogEntry): string {\n const formatted: Record<string, unknown> = {};\n\n // Add timestamp\n if (this.includeTimestamp) {\n formatted.timestamp = entry.timestamp;\n }\n\n // Add log level\n if (this.includeLevel) {\n formatted.level = entry.level.toLowerCase();\n formatted.levelValue = entry.level;\n }\n\n // Add app name\n if (this.includeAppName) {\n formatted.appName = entry.appName;\n }\n\n // Add trace ID\n if (this.includeTraceId && entry.traceId) {\n formatted.traceId = entry.traceId;\n }\n\n // Add context\n if (this.includeContext && entry.context) {\n formatted.context = entry.context;\n }\n\n // Add message\n formatted.message = entry.message;\n\n // Add payload\n if (entry.payload && Object.keys(entry.payload).length > 0) {\n formatted.payload = this.serializePayload(entry.payload);\n }\n\n // Add error if present\n if (entry.error) {\n formatted.error = serializeError(entry.error);\n }\n\n // Add metadata\n formatted.meta = {\n pid: process.pid,\n hostname: process.env.HOSTNAME || 'unknown',\n version: process.version,\n };\n\n // Use a circular-safe replacer: a payload containing a cycle (e.g. an Express\n // req/res, a DB connection, a Mongoose doc) would otherwise make JSON.stringify\n // throw and crash the entire log/transport path.\n const replacer = createCircularReplacer();\n return this.prettyPrint\n ? JSON.stringify(formatted, replacer, 2)\n : JSON.stringify(formatted, replacer);\n }\n\n private serializePayload(payload: Record<string, unknown>): Record<string, unknown> {\n const serialized: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(payload)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n try {\n if (value instanceof Error) {\n serialized[key] = serializeError(value);\n } else if (value instanceof Date) {\n serialized[key] = value.toISOString();\n } else if (typeof value === 'function') {\n serialized[key] = '[Function]';\n } else if (typeof value === 'symbol') {\n serialized[key] = value.toString();\n } else if (value === undefined) {\n serialized[key] = null;\n } else {\n serialized[key] = value;\n }\n } catch {\n serialized[key] = '[Unserializable]';\n }\n }\n\n return serialized;\n }\n}\n","/**\n * Text formatter for Logixia\n */\n\nimport type { ILogFormatter, LogEntry } from '../types';\nimport { LogLevel } from '../types';\nimport { safeToString } from '../utils/coerce.utils';\n\n// CWE-117 guard: strip ASCII control chars so attacker-supplied log data\n// cannot smuggle ANSI escapes through the text formatter.\n// eslint-disable-next-line no-control-regex\nconst CONTROL_CHARS_RE = /[\\x00-\\x08\\x0B-\\x1F\\x7F-\\x9F]/g;\n// Coerce non-strings (objects passed via setContext, Errors as messages, etc.)\n// before .replace() to avoid TypeError on the hot path.\nconst stripControls = (value: unknown): string => safeToString(value).replace(CONTROL_CHARS_RE, '');\n\nexport class TextFormatter implements ILogFormatter {\n private colorize: boolean;\n private includeTimestamp: boolean;\n private includeAppName: boolean;\n private includeTraceId: boolean;\n private includeContext: boolean;\n private timestampFormat: 'iso' | 'locale' | 'short';\n private colors: Record<string, string>;\n\n constructor(\n options: {\n colorize?: boolean;\n includeTimestamp?: boolean;\n includeAppName?: boolean;\n includeTraceId?: boolean;\n includeContext?: boolean;\n timestampFormat?: 'iso' | 'locale' | 'short';\n colors?: Record<string, string>;\n } = {}\n ) {\n this.colorize = options.colorize ?? true;\n this.includeTimestamp = options.includeTimestamp ?? true;\n this.includeAppName = options.includeAppName ?? true;\n this.includeTraceId = options.includeTraceId ?? true;\n this.includeContext = options.includeContext ?? true;\n this.timestampFormat = options.timestampFormat ?? 'locale';\n this.colors = {\n error: '\\x1b[31m', // Red\n warn: '\\x1b[33m', // Yellow\n info: '\\x1b[32m', // Green\n debug: '\\x1b[34m', // Blue\n trace: '\\x1b[35m', // Magenta\n verbose: '\\x1b[36m', // Cyan\n reset: '\\x1b[0m', // Reset\n bold: '\\x1b[1m', // Bold\n dim: '\\x1b[2m', // Dim\n ...options.colors,\n };\n }\n\n format(entry: LogEntry): string {\n const parts: string[] = [];\n\n // Add timestamp\n if (this.includeTimestamp) {\n const timestamp = this.formatTimestamp(entry.timestamp);\n parts.push(this.colorize ? `${this.colors.dim}${timestamp}${this.colors.reset}` : timestamp);\n }\n\n // Add log level\n const levelName = entry.level.toLowerCase();\n const levelColor = this.colors[levelName] || this.colors.reset;\n const formattedLevel = this.colorize\n ? `${levelColor}${this.colors.bold}${levelName.toUpperCase().padEnd(5)}${this.colors.reset}`\n : levelName.toUpperCase().padEnd(5);\n parts.push(`[${formattedLevel}]`);\n\n // Add app name\n if (this.includeAppName) {\n const safeAppName = stripControls(entry.appName);\n const appName = this.colorize\n ? `${this.colors.bold}${safeAppName}${this.colors.reset}`\n : safeAppName;\n parts.push(`[${appName}]`);\n }\n\n // Add trace ID\n if (this.includeTraceId && entry.traceId) {\n const safeTraceId = stripControls(entry.traceId);\n const traceId = this.colorize\n ? `${this.colors.dim}${safeTraceId}${this.colors.reset}`\n : safeTraceId;\n parts.push(`[${traceId}]`);\n }\n\n // Add context\n if (this.includeContext && entry.context) {\n const safeContext = stripControls(entry.context);\n const context = this.colorize\n ? `${this.colors.cyan}${safeContext}${this.colors.reset}`\n : safeContext;\n parts.push(`[${context}]`);\n }\n\n // Add message\n const safeMessage = stripControls(entry.message);\n const message =\n this.colorize && entry.level === LogLevel.ERROR\n ? `${this.colors.error}${safeMessage}${this.colors.reset}`\n : safeMessage;\n parts.push(message);\n\n // Add payload\n if (entry.payload && Object.keys(entry.payload).length > 0) {\n const payload = this.formatPayload(entry.payload);\n if (payload) {\n parts.push(this.colorize ? `${this.colors.dim}${payload}${this.colors.reset}` : payload);\n }\n }\n\n return parts.join(' ');\n }\n\n private formatTimestamp(timestamp: string): string {\n const date = new Date(timestamp);\n\n switch (this.timestampFormat) {\n case 'iso':\n return date.toISOString();\n case 'short':\n return date.toLocaleTimeString();\n case 'locale':\n default:\n return date.toLocaleString();\n }\n }\n\n private formatPayload(payload: Record<string, unknown>): string {\n try {\n // Handle simple objects\n if (Object.keys(payload).length === 1) {\n const entry = Object.entries(payload)[0];\n if (entry) {\n const [key, value] = entry;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return `${key}=${value}`;\n }\n }\n }\n\n // Handle multiple properties or complex objects\n const formatted = Object.entries(payload)\n .map(([key, value]) => {\n if (value === null || value === undefined) {\n return `${key}=${value}`;\n }\n if (typeof value === 'string') {\n return `${key}=\"${value}\"`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return `${key}=${value}`;\n }\n if (value instanceof Date) {\n return `${key}=${value.toISOString()}`;\n }\n if (typeof value === 'object') {\n // safeToString is circular-safe (raw JSON.stringify throws on a cycle\n // — e.g. an Express req/res — which would crash the log call) and the\n // control-char strip keeps the CWE-117 guarantee for object values.\n return `${key}=${stripControls(safeToString(value))}`;\n }\n return `${key}=${String(value)}`;\n })\n .join(' ');\n\n return formatted;\n } catch {\n // Last-resort fallback must also be circular-safe — the previous\n // JSON.stringify(payload) here threw again on the same cyclic payload.\n return stripControls(safeToString(payload));\n }\n }\n\n /**\n * Create a formatter with preset configurations\n */\n static createSimple(): TextFormatter {\n return new TextFormatter({\n colorize: true,\n includeTimestamp: true,\n includeAppName: false,\n includeTraceId: false,\n includeContext: true,\n timestampFormat: 'short',\n });\n }\n\n static createDetailed(): TextFormatter {\n return new TextFormatter({\n colorize: true,\n includeTimestamp: true,\n includeAppName: true,\n includeTraceId: true,\n includeContext: true,\n timestampFormat: 'locale',\n });\n }\n\n static createMinimal(): TextFormatter {\n return new TextFormatter({\n colorize: false,\n includeTimestamp: false,\n includeAppName: false,\n includeTraceId: false,\n includeContext: false,\n });\n }\n}\n","/**\n * logixia — Dynamic runtime log-level reconfiguration.\n *\n * Change log levels in a running process WITHOUT a restart — the single\n * most-requested logging feature across the Winston and Pino issue trackers\n * (winston#1107, pino#206/#677, nestjs-pino#371). The ecosystem even built a\n * standalone module for it (pino-arborsculpture); logixia ships it first-class.\n *\n * Two trigger surfaces:\n * - {@link registerLevelSignal} — flip levels via an OS signal (default SIGUSR2),\n * cycling through a level list. Zero HTTP surface, safe for any deployment.\n * - {@link createLevelControlHandler} — a tiny HTTP handler so an ops dashboard\n * can GET the current level and POST a new global / per-namespace level.\n *\n * @example Signal-based (cycle levels on each `kill -USR2 <pid>`)\n * ```ts\n * import { registerLevelSignal } from 'logixia';\n * const dispose = registerLevelSignal(logger);\n * // later: process.kill(process.pid, 'SIGUSR2') → info → debug → trace → info …\n * ```\n *\n * @example HTTP admin endpoint\n * ```ts\n * import { createLevelControlHandler } from 'logixia';\n * const handler = createLevelControlHandler(logger);\n * app.all('/admin/log-level', handler); // GET reads, POST { level, namespaceLevels } sets\n * ```\n */\n\nimport type { LogLevelString, NamespaceLevels } from '../types';\nimport { internalLog, internalWarn } from './internal-log';\n\n/** Minimal logger surface the runtime controls need. */\nexport interface ReconfigurableLogger {\n getLevel(): LogLevelString;\n setLevel(level: LogLevelString): void;\n setNamespaceLevels?(levels: NamespaceLevels): void;\n getNamespaceLevels?(): NamespaceLevels;\n}\n\nconst DEFAULT_CYCLE: readonly LogLevelString[] = [\n 'error',\n 'warn',\n 'info',\n 'debug',\n 'trace',\n 'verbose',\n] as unknown as readonly LogLevelString[];\n\nexport interface LevelSignalOptions {\n /** Signal to listen on. Default: 'SIGUSR2' (SIGUSR1 is used by the Node debugger). */\n signal?: NodeJS.Signals;\n /** Ordered levels to cycle through on each signal. Default: error→…→verbose. */\n cycle?: LogLevelString[];\n}\n\n/**\n * Register an OS-signal handler that cycles the logger's global level on each\n * signal. Returns a dispose function that removes the listener.\n *\n * Cycling (rather than jumping straight to a fixed level) means a single,\n * memorizable command (`kill -USR2 <pid>`) is enough to ratchet verbosity up\n * while chasing a bug and back down again — no value to remember.\n */\nexport function registerLevelSignal(\n logger: ReconfigurableLogger,\n options: LevelSignalOptions = {}\n): () => void {\n const signal = options.signal ?? 'SIGUSR2';\n const cycle = options.cycle && options.cycle.length > 0 ? options.cycle : [...DEFAULT_CYCLE];\n\n const handler = (): void => {\n const current = logger.getLevel();\n const idx = cycle.indexOf(current);\n const next = cycle[(idx + 1) % cycle.length]!;\n logger.setLevel(next);\n internalLog(`runtime level changed via ${signal}: ${current} → ${next}`);\n };\n\n process.on(signal, handler);\n return () => {\n process.removeListener(signal, handler);\n };\n}\n\n// ── HTTP admin handler ────────────────────────────────────────────────────────\n\ninterface MinimalReq {\n method?: string | undefined;\n on?: (event: string, cb: (chunk?: unknown) => void) => void;\n}\ninterface MinimalRes {\n statusCode?: number;\n setHeader?: (k: string, v: string) => void;\n end: (body?: string) => void;\n}\n\nconst VALID_LEVELS = new Set<string>([\n 'error',\n 'warn',\n 'info',\n 'debug',\n 'trace',\n 'verbose',\n 'fatal',\n]);\n\n/**\n * Create an HTTP handler (Node `http`/Express-compatible) that reads and sets\n * the logger's level at runtime.\n *\n * - `GET` → `{ level, namespaceLevels }`\n * - `POST` → body `{ level?, namespaceLevels? }` applies them, returns the new state\n *\n * Custom levels are accepted too: any level the logger already knows passes\n * through. Unknown levels are rejected with 400 so a typo can't silently mute\n * logging. Mount behind your own auth — this intentionally has none.\n */\nexport function createLevelControlHandler(\n logger: ReconfigurableLogger,\n options: { allowedLevels?: string[] } = {}\n): (req: MinimalReq, res: MinimalRes) => void {\n const allowed =\n options.allowedLevels && options.allowedLevels.length > 0\n ? new Set(options.allowedLevels.map((l) => l.toLowerCase()))\n : VALID_LEVELS;\n\n const snapshot = (): { level: LogLevelString; namespaceLevels: NamespaceLevels } => ({\n level: logger.getLevel(),\n namespaceLevels: logger.getNamespaceLevels?.() ?? {},\n });\n\n const send = (res: MinimalRes, status: number, body: unknown): void => {\n res.statusCode = status;\n res.setHeader?.('Content-Type', 'application/json');\n res.end(JSON.stringify(body));\n };\n\n const applyBody = (res: MinimalRes, raw: string): void => {\n let parsed: { level?: unknown; namespaceLevels?: unknown };\n try {\n parsed = raw ? (JSON.parse(raw) as typeof parsed) : {};\n } catch {\n send(res, 400, { error: 'invalid JSON body' });\n return;\n }\n\n if (parsed.level !== undefined) {\n const lvl = String(parsed.level).toLowerCase();\n if (!allowed.has(lvl)) {\n send(res, 400, { error: `unknown level \"${parsed.level}\"`, allowed: [...allowed] });\n return;\n }\n logger.setLevel(lvl as LogLevelString);\n }\n\n if (parsed.namespaceLevels !== undefined) {\n if (\n typeof parsed.namespaceLevels !== 'object' ||\n parsed.namespaceLevels === null ||\n Array.isArray(parsed.namespaceLevels)\n ) {\n send(res, 400, { error: 'namespaceLevels must be an object' });\n return;\n }\n const nl = parsed.namespaceLevels as Record<string, unknown>;\n for (const [pat, lvl] of Object.entries(nl)) {\n if (!allowed.has(String(lvl).toLowerCase())) {\n send(res, 400, { error: `unknown level \"${String(lvl)}\" for namespace \"${pat}\"` });\n return;\n }\n }\n if (logger.setNamespaceLevels) {\n const coerced: NamespaceLevels = {};\n for (const [pat, lvl] of Object.entries(nl)) {\n coerced[pat] = String(lvl).toLowerCase() as LogLevelString;\n }\n logger.setNamespaceLevels(coerced);\n } else {\n internalWarn('level control: logger does not support setNamespaceLevels — ignored');\n }\n }\n\n send(res, 200, snapshot());\n };\n\n return function levelControlHandler(req: MinimalReq, res: MinimalRes): void {\n const method = (req.method ?? 'GET').toUpperCase();\n\n if (method === 'GET') {\n send(res, 200, snapshot());\n return;\n }\n\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n // Express already-parsed body (req.body) vs raw stream.\n const maybeBody = (req as unknown as { body?: unknown }).body;\n if (maybeBody !== undefined && typeof req.on !== 'function') {\n applyBody(res, typeof maybeBody === 'string' ? maybeBody : JSON.stringify(maybeBody));\n return;\n }\n if (typeof req.on === 'function') {\n let raw = '';\n req.on('data', (chunk?: unknown) => {\n raw += String(chunk ?? '');\n });\n req.on('end', () => applyBody(res, raw));\n return;\n }\n applyBody(res, '');\n return;\n }\n\n send(res, 405, { error: `method ${method} not allowed` });\n };\n}\n","/**\n * logixia — TypeScript typed log fields\n *\n * Solves the type-safety gap: standard `Record<string, unknown>` metadata gives\n * zero IDE autocomplete and lets typos slip silently into production logs.\n *\n * Two complementary utilities:\n *\n * 1. `createTypedLogger<TFields>()` — wraps any IBaseLogger so that the second\n * argument of every log method is constrained to `TFields` (a typed object).\n * Compile-time safety, zero runtime overhead.\n *\n * 2. `defineLogSchema<TFields>(schema)` — declares expected fields with optional\n * validators. In development (NODE_ENV !== 'production') every log call is\n * validated against the schema and a warning is emitted for missing required\n * fields or type mismatches.\n *\n * @example\n * ```ts\n * import { createTypedLogger, defineLogSchema } from 'logixia';\n *\n * interface OrderFields {\n * orderId: string;\n * userId: string;\n * amount?: number;\n * currency?: string;\n * }\n *\n * const schema = defineLogSchema<OrderFields>({\n * orderId: { type: 'string', required: true },\n * userId: { type: 'string', required: true },\n * amount: { type: 'number' },\n * currency: { type: 'string' },\n * });\n *\n * const orderLogger = createTypedLogger<OrderFields>(baseLogger, schema);\n * await orderLogger.info('Order created', { orderId: 'ord_123', userId: 'usr_456', amount: 99.99 });\n * // ^^^^ fully typed autocomplete ^^^^\n * ```\n */\n\nimport { internalWarn } from './internal-log';\n\n// ── Schema types ─────────────────────────────────────────────────────────────\n\nexport type LogFieldType = 'string' | 'number' | 'boolean' | 'object' | 'array';\n\nexport interface LogFieldDef {\n type: LogFieldType;\n /** Emit a warning when this field is missing from the log call. Default: false */\n required?: boolean;\n /** Custom validator — return a string to emit it as a warning message. */\n validate?: (value: unknown) => string | undefined;\n}\n\nexport type LogSchema<TFields extends Record<string, unknown>> = {\n [K in keyof TFields]: LogFieldDef;\n};\n\nexport interface CompiledSchema<TFields extends Record<string, unknown>> {\n readonly fields: LogSchema<TFields>;\n /**\n * Validate a payload against the schema.\n * Returns an array of warning strings (empty = pass).\n * Only runs in non-production environments.\n */\n validate(payload: Partial<TFields>): string[];\n}\n\n// ── defineLogSchema ──────────────────────────────────────────────────────────\n\n/**\n * Define a typed schema for a category of log entries.\n *\n * Call this once at module initialisation and pass it to `createTypedLogger`.\n * In development, every log call is validated against the schema.\n */\nexport function defineLogSchema<TFields extends Record<string, unknown>>(\n fields: LogSchema<TFields>\n): CompiledSchema<TFields> {\n return {\n fields,\n validate(payload: Partial<TFields>): string[] {\n if (process.env['NODE_ENV'] === 'production') return [];\n\n const warnings: string[] = [];\n\n for (const [key, def] of Object.entries(fields) as [keyof TFields & string, LogFieldDef][]) {\n const value = payload[key];\n\n if (def.required && (value === undefined || value === null)) {\n warnings.push(`Required field \"${key}\" is missing`);\n continue;\n }\n\n if (value !== undefined && value !== null) {\n const actualType = Array.isArray(value) ? 'array' : typeof value;\n if (actualType !== def.type) {\n warnings.push(`Field \"${key}\" expected type \"${def.type}\" but got \"${actualType}\"`);\n }\n\n if (def.validate) {\n const msg = def.validate(value);\n if (msg) warnings.push(`Field \"${key}\": ${msg}`);\n }\n }\n }\n\n return warnings;\n },\n };\n}\n\n// ── TypedLogger ──────────────────────────────────────────────────────────────\n\n/** IBaseLogger-compatible subset used by TypedLogger */\nexport interface LoggerLike {\n error(message: string | 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 verbose?(message: string, data?: Record<string, unknown>): Promise<void>;\n trace?(message: string, data?: Record<string, unknown>): Promise<void>;\n}\n\n/**\n * A logger whose metadata is typed to `TFields`.\n *\n * The `error` overload still accepts a plain `Error` object as the first arg\n * (with optional `TFields` data) so the typed logger remains a drop-in replacement.\n */\nexport interface TypedLogger<TFields extends Record<string, unknown>> {\n error(error: Error, data?: Partial<TFields>): Promise<void>;\n error(message: string, data?: Partial<TFields>): Promise<void>;\n warn(message: string, data?: Partial<TFields>): Promise<void>;\n info(message: string, data?: Partial<TFields>): Promise<void>;\n debug(message: string, data?: Partial<TFields>): Promise<void>;\n verbose(message: string, data?: Partial<TFields>): Promise<void>;\n trace(message: string, data?: Partial<TFields>): Promise<void>;\n /** Access the underlying untyped logger if needed. */\n readonly raw: LoggerLike;\n}\n\n/**\n * Wrap any logixia logger with a type-safe field interface.\n *\n * @param logger Any object that implements `IBaseLogger` (e.g. the result of `createLogger()`)\n * @param schema Optional schema for dev-time validation. Created with `defineLogSchema()`.\n */\nexport function createTypedLogger<TFields extends Record<string, unknown>>(\n logger: LoggerLike,\n schema?: CompiledSchema<TFields>\n): TypedLogger<TFields> {\n function withValidation(\n level: string,\n fn: (msg: string, data?: Record<string, unknown>) => Promise<void>,\n message: string,\n data?: Partial<TFields>\n ): Promise<void> {\n if (schema && data) {\n const warnings = schema.validate(data);\n for (const w of warnings) {\n internalWarn(`[logixia/schema] ${w} — level=${level} message=\"${message}\"`);\n }\n }\n return fn(message, data as Record<string, unknown> | undefined);\n }\n\n return {\n raw: logger,\n error(messageOrError: string | Error, data?: Partial<TFields>): Promise<void> {\n if (schema && data) {\n const warnings = schema.validate(data);\n const display = messageOrError instanceof Error ? messageOrError.message : messageOrError;\n for (const w of warnings) {\n internalWarn(`[logixia/schema] ${w} — level=error message=\"${display}\"`);\n }\n }\n return logger.error(messageOrError, data as Record<string, unknown> | undefined);\n },\n warn: (m, d) => withValidation('warn', logger.warn.bind(logger), m, d),\n info: (m, d) => withValidation('info', logger.info.bind(logger), m, d),\n debug: (m, d) => withValidation('debug', logger.debug.bind(logger), m, d),\n verbose: (m, d) =>\n withValidation('verbose', (logger.verbose ?? logger.debug).bind(logger), m, d),\n trace: (m, d) => withValidation('trace', (logger.trace ?? logger.debug).bind(logger), m, d),\n };\n}\n","/**\n * logixia — Canonical Log Lines / Wide Events.\n *\n * Emit ONE dense, structured event per unit of work (usually a request) instead\n * of scattering details across many narrow log lines. Fields are accumulated as\n * the request flows through middleware and business logic, then the whole event\n * is emitted ONCE — in a `finally`/teardown path so it fires even on errors.\n *\n * This is the \"canonical log line\" pattern (Stripe) / \"wide events\" /\n * \"Observability 2.0\" (Honeycomb): one pre-joined, queryable record per request,\n * so operators never JOIN across log lines during an incident. It composes with\n * logixia's existing trace correlation — when a trace is active, the event\n * carries `traceId`/`spanId`, making it OTel-friendly.\n *\n * @example Manual scope\n * ```ts\n * import { withWideEvent, addEventFields } from 'logixia';\n *\n * await withWideEvent(logger, { route: '/checkout' }, async () => {\n * addEventFields({ userId, planTier }); // from anywhere in the call tree\n * addEventFields({ dbQueries: 4, cacheHit });\n * // ...one wide event is emitted automatically when this callback settles,\n * // even if it throws.\n * });\n * ```\n *\n * @example Express middleware (auto-emit on response finish/close)\n * ```ts\n * import { wideEventMiddleware } from 'logixia';\n * app.use(wideEventMiddleware(logger));\n * app.get('/x', (req, res) => { addEventFields({ handled: 'x' }); res.json({}); });\n * ```\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport { getCurrentTraceId } from './utils/trace.utils';\n\n/** The accumulating event fields for the current scope. */\nexport type WideEventFields = Record<string, unknown>;\n\ninterface WideEventState {\n fields: WideEventFields;\n startMs: number;\n emitted: boolean;\n}\n\nconst _storage = new AsyncLocalStorage<WideEventState>();\n\n/** Minimal logger surface a wide event needs to emit itself. */\nexport interface WideEventLogger {\n logLevel(level: string, message: string, data?: Record<string, unknown>): Promise<void> | void;\n}\n\nexport interface WideEventOptions {\n /** Level the canonical event is logged at. Default: 'info'. */\n level?: string;\n /** Message for the canonical event line. Default: 'request'. */\n message?: string;\n /**\n * Attach `traceId` (and `spanId` when present) from the active trace context.\n * Default: true.\n */\n includeTrace?: boolean;\n /** Field name for the auto-computed duration in ms. Default: 'durationMs'. */\n durationField?: string;\n}\n\n/**\n * Merge fields into the wide event for the current async scope. No-op (with no\n * throw) when called outside a `withWideEvent` / middleware scope, so business\n * code can call it unconditionally.\n */\nexport function addEventFields(fields: WideEventFields): void {\n const state = _storage.getStore();\n if (!state || state.emitted) return;\n Object.assign(state.fields, fields);\n}\n\n/** Set a single field on the current wide event. */\nexport function setEventField(key: string, value: unknown): void {\n addEventFields({ [key]: value });\n}\n\n/** Read a shallow copy of the wide event accumulated so far, or undefined. */\nexport function getEventFields(): WideEventFields | undefined {\n const state = _storage.getStore();\n return state ? { ...state.fields } : undefined;\n}\n\nfunction emit(\n logger: WideEventLogger,\n state: WideEventState,\n options: WideEventOptions,\n extra?: WideEventFields\n): void {\n if (state.emitted) return;\n state.emitted = true;\n\n const level = options.level ?? 'info';\n const message = options.message ?? 'request';\n const durationField = options.durationField ?? 'durationMs';\n const includeTrace = options.includeTrace ?? true;\n\n const payload: WideEventFields = { ...state.fields };\n if (extra) Object.assign(payload, extra);\n payload[durationField] = Date.now() - state.startMs;\n\n if (includeTrace) {\n const traceId = getCurrentTraceId();\n if (traceId !== undefined && payload['traceId'] === undefined) payload['traceId'] = traceId;\n }\n\n // logLevel may be async (transport-backed); fire-and-forget so the wide-event\n // emit never blocks request teardown. Swallow rejections (the logger surfaces\n // its own transport errors) so this never becomes an unhandled rejection.\n const p = logger.logLevel(level, message, payload);\n if (p && typeof (p as Promise<void>).catch === 'function') {\n (p as Promise<void>).catch(() => {});\n }\n}\n\n/**\n * Run `callback` inside a wide-event scope. `addEventFields` calls anywhere in\n * the (async) call tree accumulate onto one event, which is emitted exactly once\n * when the callback settles — on success OR error (the canonical \"emit in\n * finally\" guarantee). On error, `error` + `errorMessage` fields are added.\n */\nexport async function withWideEvent<T>(\n logger: WideEventLogger,\n initialFields: WideEventFields,\n callback: () => Promise<T> | T,\n options: WideEventOptions = {}\n): Promise<T> {\n const state: WideEventState = {\n fields: { ...initialFields },\n startMs: Date.now(),\n emitted: false,\n };\n\n return _storage.run(state, async () => {\n try {\n const result = await callback();\n emit(logger, state, options);\n return result;\n } catch (error) {\n emit(logger, state, options, {\n error: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n });\n}\n\n// ── HTTP middleware ─────────────────────────────────────────────────────────\n\ninterface MwReq {\n method?: string | undefined;\n url?: string | undefined;\n originalUrl?: string | undefined;\n ip?: string | undefined;\n headers?: Record<string, unknown> | undefined;\n socket?: { remoteAddress?: string } | undefined;\n}\ninterface MwRes {\n statusCode?: number;\n once?: (event: string, cb: () => void) => void;\n}\n\nexport interface WideEventMiddlewareOptions extends WideEventOptions {\n /** Derive extra initial fields from the request. */\n enrich?: (req: MwReq) => WideEventFields;\n /** Skip wide-event emission for a request (e.g. health checks). */\n skip?: (req: MwReq) => boolean;\n}\n\n/**\n * Express/Connect middleware that opens a wide-event scope per request and\n * emits ONE canonical event on response `finish`/`close` — even if the handler\n * throws or the client disconnects. Pre-populates method/url/ip; handlers add\n * more via `addEventFields`. The completion event includes `statusCode` and the\n * request duration.\n */\nexport function wideEventMiddleware(\n logger: WideEventLogger,\n options: WideEventMiddlewareOptions = {}\n): (req: MwReq, res: MwRes, next: () => void) => void {\n return function logixiaWideEventMiddleware(req: MwReq, res: MwRes, next: () => void): void {\n if (options.skip?.(req)) {\n next();\n return;\n }\n\n const base: WideEventFields = {\n method: req.method,\n url: req.originalUrl ?? req.url,\n ip: req.ip ?? req.socket?.remoteAddress,\n ...(options.enrich ? options.enrich(req) : {}),\n };\n\n const state: WideEventState = { fields: base, startMs: Date.now(), emitted: false };\n\n const finalize = (): void => {\n emit(logger, state, options, { statusCode: res.statusCode ?? 0 });\n };\n\n // Both 'finish' and 'close' may fire; emit() is idempotent (emitted guard),\n // so the canonical line is logged exactly once.\n res.once?.('finish', finalize);\n res.once?.('close', finalize);\n\n _storage.run(state, () => next());\n };\n}\n","/**\n * OTLP Logs transport — emit logixia logs as OpenTelemetry LogRecords.\n *\n * logixia already READS the active OTel span (the bridge injects traceId/spanId\n * into payloads). This transport closes the loop by EMITTING logs OUT in the\n * OTLP/HTTP JSON format to a collector, making logixia an OTel-Logs-native\n * source — logs land in any OTLP backend (Grafana Loki, OpenObserve, Better\n * Stack, Axiom, Datadog, SigNoz…) already correlated with traces.\n *\n * Dependency-free: builds the OTLP/HTTP JSON payload directly (no\n * @opentelemetry/* packages required — that JS API is still alpha), conforming\n * to the OTel logs data model: SeverityNumber (1–24), resource attributes\n * (service.name/version, deployment.environment), and TraceId/SpanId on each\n * record for correlation.\n *\n * @example\n * ```ts\n * transports: {\n * custom: [ new OtlpLogTransport({\n * url: 'http://localhost:4318/v1/logs',\n * serviceName: 'api',\n * headers: { 'x-api-key': process.env.OTLP_KEY! },\n * }) ],\n * }\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n HttpError,\n} from './batch-buffer';\n\nexport interface OtlpLogTransportConfig {\n /** OTLP/HTTP logs endpoint, e.g. `http://localhost:4318/v1/logs`. */\n url: string;\n /** Extra HTTP headers (auth, tenant, etc.). */\n headers?: Record<string, string>;\n /** `service.name` resource attribute. Default: 'logixia'. */\n serviceName?: string;\n /** `service.version` resource attribute. */\n serviceVersion?: string;\n /** `deployment.environment` resource attribute. */\n environment?: string;\n /** Extra resource attributes merged into the OTLP resource. */\n resourceAttributes?: Record<string, string | number | boolean>;\n /** Entries per batch / per POST. Default: 100. */\n batchSize?: number;\n /** Auto-flush interval (ms). Default: 5000. */\n flushIntervalMs?: number;\n /** Abort a request that has not finished after this many ms. Default: 10000. */\n requestTimeoutMs?: number;\n /** Max buffered entries; the oldest are dropped past this. Default: 10000. */\n maxBufferSize?: number;\n level?: string;\n}\n\nconst HEX_32 = /^[0-9a-f]{32}$/;\n\n/**\n * OTLP JSON wants a 32-char lowercase hex trace id. Dashed UUIDs are\n * normalised; anything else (or all zeros) returns undefined.\n */\nexport function toOtlpTraceId(id: string): string | undefined {\n const hex = id.replace(/-/g, '').toLowerCase();\n return HEX_32.test(hex) && hex !== '0'.repeat(32) ? hex : undefined;\n}\n\n/**\n * Map a logixia level name to an OTel SeverityNumber (1–24) and text.\n * Per the OTel logs SDK spec: TRACE=1, DEBUG=5, INFO=9, WARN=13, ERROR=17,\n * FATAL=21. Custom/unknown levels fall back to INFO (9).\n */\nexport function toOtelSeverity(level: string): { number: number; text: string } {\n switch (level.toLowerCase()) {\n case 'trace':\n return { number: 1, text: 'TRACE' };\n case 'verbose':\n return { number: 5, text: 'DEBUG' }; // verbose maps to DEBUG range\n case 'debug':\n return { number: 5, text: 'DEBUG' };\n case 'info':\n return { number: 9, text: 'INFO' };\n case 'warn':\n case 'warning':\n return { number: 13, text: 'WARN' };\n case 'error':\n return { number: 17, text: 'ERROR' };\n case 'fatal':\n return { number: 21, text: 'FATAL' };\n default:\n return { number: 9, text: 'INFO' };\n }\n}\n\n/** Coerce a JS value into an OTLP AnyValue. */\nfunction toAnyValue(value: unknown): Record<string, unknown> {\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'boolean') return { boolValue: value };\n if (typeof value === 'number') {\n return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };\n }\n if (typeof value === 'bigint') return { stringValue: value.toString() };\n if (value === null || value === undefined) return { stringValue: '' };\n // Objects/arrays → JSON string (kvlistValue would be richer but stringValue is\n // universally accepted and avoids deep recursion / circular issues here).\n try {\n return { stringValue: stringifyForLog(value) };\n } catch {\n return { stringValue: String(value) };\n }\n}\n\n/** Build the OTLP KeyValue attribute list from a flat record. */\nfunction toAttributes(rec: Record<string, unknown>): Array<{ key: string; value: unknown }> {\n return Object.entries(rec).map(([key, value]) => ({ key, value: toAnyValue(value) }));\n}\n\nexport class OtlpLogTransport implements IAsyncTransport {\n public readonly name = 'otlp';\n public readonly level: string | undefined;\n\n private readonly url: string;\n private readonly headers: Record<string, string>;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n private readonly requestTimeoutMs: number;\n private readonly resourceAttrs: Array<{ key: string; value: unknown }>;\n\n private readonly buffer: BatchBuffer<TransportLogEntry>;\n private flushTimer: NodeJS.Timeout | null = null;\n\n constructor(config: OtlpLogTransportConfig) {\n this.url = config.url;\n this.headers = config.headers ?? {};\n this.batchSize = config.batchSize ?? 100;\n this.flushIntervalMs = config.flushIntervalMs ?? 5000;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.level = config.level;\n this.buffer = new BatchBuffer<TransportLogEntry>({\n name: 'OtlpLogTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.send(entries),\n });\n\n const resource: Record<string, unknown> = {\n 'service.name': config.serviceName ?? 'logixia',\n ...(config.serviceVersion ? { 'service.version': config.serviceVersion } : {}),\n ...(config.environment ? { 'deployment.environment': config.environment } : {}),\n ...(config.resourceAttributes ?? {}),\n };\n this.resourceAttrs = toAttributes(resource);\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushIntervalMs);\n if (this.flushTimer.unref) this.flushTimer.unref();\n }\n\n write(entry: TransportLogEntry): void {\n this.buffer.push(entry);\n this.buffer.flushIfFull();\n }\n\n /** Convert one entry into an OTLP LogRecord. */\n private toLogRecord(entry: TransportLogEntry): Record<string, unknown> {\n const sev = toOtelSeverity(entry.level);\n const tsNanos = String(entry.timestamp.getTime() * 1_000_000);\n\n const attrs: Record<string, unknown> = { ...(entry.data ?? {}) };\n if (entry.context !== undefined) attrs['context'] = entry.context;\n if (entry.appName !== undefined) attrs['app.name'] = entry.appName;\n if (entry.environment !== undefined) attrs['deployment.environment'] = entry.environment;\n\n const record: Record<string, unknown> = {\n timeUnixNano: tsNanos,\n observedTimeUnixNano: tsNanos,\n severityNumber: sev.number,\n severityText: sev.text,\n body: { stringValue: entry.message },\n attributes: toAttributes(attrs),\n };\n\n if (entry.traceId) {\n const traceId = toOtlpTraceId(entry.traceId);\n if (traceId) record['traceId'] = traceId;\n else\n (record['attributes'] as unknown[]).push({\n key: 'trace_id',\n value: { stringValue: entry.traceId },\n });\n }\n\n return record;\n }\n\n private buildPayload(entries: TransportLogEntry[]): string {\n return stringifyForLog({\n resourceLogs: [\n {\n resource: { attributes: this.resourceAttrs },\n scopeLogs: [\n {\n scope: { name: 'logixia' },\n logRecords: entries.map((e) => this.toLogRecord(e)),\n },\n ],\n },\n ],\n });\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n private async send(entries: TransportLogEntry[]): Promise<void> {\n if (typeof fetch !== 'function') {\n internalWarn('OtlpLogTransport: global fetch unavailable — cannot export logs');\n return;\n }\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);\n try {\n const res = await fetch(this.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...this.headers },\n body: this.buildPayload(entries),\n signal: controller.signal,\n });\n if (!res.ok) {\n throw new HttpError(res.status, '', 'OTLP export');\n }\n } catch (err) {\n if (controller.signal.aborted) {\n throw new Error(`OTLP export timed out after ${this.requestTimeoutMs}ms`, { cause: err });\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.buffer.close();\n }\n}\n","/**\n * logixia — Metrics extraction → Prometheus\n *\n * Automatically extracts metrics from structured log fields and exposes them\n * in the Prometheus text exposition format (version 0.0.4).\n * No external dependency — the format is fully self-contained.\n *\n * Supported metric types:\n * - `counter` — incremented on each matching log entry\n * - `histogram` — observes a numeric payload field per entry\n * - `gauge` — tracks the most recent numeric value of a payload field\n *\n * Usage:\n * ```ts\n * import { createMetricsPlugin } from 'logixia';\n *\n * const metrics = createMetricsPlugin({\n * http_request_duration: {\n * type: 'histogram',\n * field: 'duration',\n * labels: ['method', 'statusCode'],\n * help: 'HTTP request duration in milliseconds',\n * },\n * error_count: {\n * type: 'counter',\n * levelFilter: 'error',\n * labels: ['context'],\n * help: 'Total error log entries',\n * },\n * active_connections: {\n * type: 'gauge',\n * field: 'connections',\n * help: 'Current active connection count',\n * },\n * });\n *\n * logger.use(metrics); // start collecting\n * app.get('/metrics', metrics.expressHandler()); // expose for Prometheus scrape\n * ```\n *\n * All metric names are automatically prefixed with `logixia_`.\n * Output follows https://prometheus.io/docs/instrumenting/exposition_formats/\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport type { LogixiaPlugin } from './plugin';\nimport type { LogEntry } from './types/index';\n\n// ── Metric config types ───────────────────────────────────────────────────────\n\n/**\n * Increment a counter on each matching log entry.\n *\n * @example Count every error entry, labelled by context:\n * ```ts\n * error_count: { type: 'counter', levelFilter: 'error', labels: ['context'] }\n * ```\n *\n * @example Count entries where payload.event === 'checkout':\n * ```ts\n * checkout_events: { type: 'counter', field: 'event', value: 'checkout' }\n * ```\n */\nexport interface CounterConfig {\n type: 'counter';\n /** Only increment when `entry.level` equals this value. Omit to count all entries. */\n levelFilter?: string;\n /**\n * Only increment when `entry.payload[field] === value`.\n * If omitted, every entry (matching `levelFilter`) is counted.\n */\n field?: string;\n value?: unknown;\n /** `entry.payload` fields used as Prometheus label dimensions. `'level'` is also valid. */\n labels?: string[];\n help?: string;\n}\n\n/**\n * Observe a numeric payload field and bucket it into a Prometheus histogram.\n *\n * @example Duration histogram labelled by HTTP method and status code:\n * ```ts\n * http_request_duration: {\n * type: 'histogram',\n * field: 'duration',\n * labels: ['method', 'statusCode'],\n * buckets: [10, 25, 50, 100, 250, 500, 1000],\n * }\n * ```\n */\nexport interface HistogramConfig {\n type: 'histogram';\n /** The `entry.payload` field containing the numeric value to observe. */\n field: string;\n /** `entry.payload` fields used as Prometheus label dimensions. */\n labels?: string[];\n help?: string;\n /**\n * Bucket upper bounds (inclusive). Sorted automatically.\n * Default: [1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000]\n */\n buckets?: number[];\n}\n\n/**\n * Track the most recent numeric value of a payload field as a gauge.\n *\n * @example Track live connection count:\n * ```ts\n * active_connections: { type: 'gauge', field: 'connections' }\n * ```\n */\nexport interface GaugeConfig {\n type: 'gauge';\n /** The `entry.payload` field containing the numeric value. */\n field: string;\n /** `entry.payload` fields used as Prometheus label dimensions. */\n labels?: string[];\n help?: string;\n}\n\nexport type MetricConfig = CounterConfig | HistogramConfig | GaugeConfig;\nexport type MetricsMap = Record<string, MetricConfig>;\n\n// ── Internal metric state ─────────────────────────────────────────────────────\n\ninterface CounterState {\n type: 'counter';\n values: Map<string, number>;\n}\n\ninterface HistogramState {\n type: 'histogram';\n buckets: number[];\n /** Parallel arrays; index = bucket index; last entry = +Inf cumulative count */\n counts: Map<string, number[]>;\n sums: Map<string, number>;\n observations: Map<string, number>;\n}\n\ninterface GaugeState {\n type: 'gauge';\n values: Map<string, number>;\n}\n\ntype MetricState = CounterState | HistogramState | GaugeState;\n\n// ── Defaults ──────────────────────────────────────────────────────────────────\n\nconst DEFAULT_BUCKETS: readonly number[] = [\n 1, 5, 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000,\n];\n\n// ── Label helpers ─────────────────────────────────────────────────────────────\n\nfunction buildLabelKey(config: MetricConfig, entry: LogEntry): string {\n const labelNames = config.labels ?? [];\n if (labelNames.length === 0) return '{}';\n const pairs: Record<string, string> = {};\n const payload = entry.payload ?? {};\n for (const name of labelNames) {\n const raw = name === 'level' ? entry.level : payload[name];\n // Sanitize the label NAME (the value is escaped at render time) so a label\n // like 'status code' can't emit unparseable Prometheus output.\n pairs[sanitizePromName(name)] = raw !== undefined && raw !== null ? String(raw) : '';\n }\n return JSON.stringify(pairs);\n}\n\nfunction renderLabels(labelKey: string): string {\n if (labelKey === '{}') return '';\n const obj = JSON.parse(labelKey) as Record<string, string>;\n const parts = Object.entries(obj).map(([k, v]) => `${k}=\"${escapeLabel(v)}\"`);\n return `{${parts.join(',')}}`;\n}\n\nfunction escapeLabel(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n');\n}\n\n/**\n * Coerce an arbitrary string into a valid Prometheus metric or label name\n * (`[a-zA-Z_][a-zA-Z0-9_]*`). Invalid characters become underscores and a\n * leading digit is prefixed with `_`. Without this, a user-supplied name like\n * `my-metric` or a label like `status code` would emit output Prometheus cannot\n * parse, breaking the ENTIRE scrape endpoint, not just that metric.\n */\nfunction sanitizePromName(name: string): string {\n let safe = name.replace(/\\W/g, '_');\n if (safe.length > 0 && /\\d/.test(safe[0]!)) safe = `_${safe}`;\n return safe.length > 0 ? safe : '_';\n}\n\n// ── MetricsPlugin ─────────────────────────────────────────────────────────────\n\n/**\n * A logixia plugin that extracts Prometheus-compatible metrics from log entries.\n *\n * Implements `LogixiaPlugin` — pass directly to `logger.use()`:\n * ```ts\n * const metrics = new MetricsPlugin({ ... });\n * logger.use(metrics);\n * ```\n *\n * Or use the `createMetricsPlugin()` factory (preferred):\n * ```ts\n * const metrics = createMetricsPlugin({ ... });\n * logger.use(metrics);\n * ```\n */\nexport class MetricsPlugin implements LogixiaPlugin {\n public readonly name = 'logixia-metrics';\n\n private readonly map: MetricsMap;\n private readonly metricState = new Map<string, MetricState>();\n\n constructor(map: MetricsMap) {\n this.map = map;\n this.initAllState();\n }\n\n // ── LogixiaPlugin lifecycle ────────────────────────────────────────────────\n\n onInit(): void {\n // State is initialised in the constructor; nothing extra needed here.\n }\n\n onLog(entry: LogEntry): LogEntry {\n const payload = entry.payload ?? {};\n\n for (const [rawName, config] of Object.entries(this.map)) {\n const state = this.metricState.get(rawName);\n if (!state) continue;\n\n const labelKey = buildLabelKey(config, entry);\n\n if (config.type === 'counter' && state.type === 'counter') {\n // Level filter\n if (config.levelFilter && entry.level !== config.levelFilter) continue;\n // Field/value filter\n if (config.field !== undefined && payload[config.field] !== config.value) continue;\n state.values.set(labelKey, (state.values.get(labelKey) ?? 0) + 1);\n } else if (config.type === 'histogram' && state.type === 'histogram') {\n const raw = payload[config.field];\n if (raw === undefined || raw === null) continue;\n const val = Number(raw);\n if (Number.isNaN(val)) continue;\n\n // Initialise per-label arrays on first observation\n if (!state.counts.has(labelKey)) {\n state.counts.set(\n labelKey,\n Array.from<number>({ length: state.buckets.length + 1 }).fill(0)\n );\n state.sums.set(labelKey, 0);\n state.observations.set(labelKey, 0);\n }\n\n const bucketArr = state.counts.get(labelKey)!;\n for (let i = 0; i < state.buckets.length; i++) {\n if (val <= state.buckets[i]!) bucketArr[i]!++;\n }\n // The +Inf bucket (last slot) always increments\n bucketArr[state.buckets.length]!++;\n state.sums.set(labelKey, (state.sums.get(labelKey) ?? 0) + val);\n state.observations.set(labelKey, (state.observations.get(labelKey) ?? 0) + 1);\n } else if (config.type === 'gauge' && state.type === 'gauge') {\n const raw = payload[config.field];\n if (raw === undefined || raw === null) continue;\n const val = Number(raw);\n if (Number.isNaN(val)) continue;\n state.values.set(labelKey, val);\n }\n }\n\n // Always pass the entry through unchanged\n return entry;\n }\n\n // ── Output ─────────────────────────────────────────────────────────────────\n\n /**\n * Render all registered metrics in the Prometheus text exposition format\n * (version 0.0.4).\n *\n * All metric names are prefixed with `logixia_`.\n *\n * @example\n * ```\n * # HELP logixia_error_count Total error log entries\n * # TYPE logixia_error_count counter\n * logixia_error_count{context=\"OrderService\"} 7\n * ```\n */\n render(): string {\n const lines: string[] = [];\n\n for (const [rawName, config] of Object.entries(this.map)) {\n const metricName = `logixia_${sanitizePromName(rawName)}`;\n const state = this.metricState.get(rawName);\n if (!state) continue;\n\n const helpText = config.help ?? rawName.replace(/_/g, ' ');\n lines.push(`# HELP ${metricName} ${helpText}`);\n lines.push(`# TYPE ${metricName} ${config.type}`);\n\n if (state.type === 'counter') {\n if (state.values.size === 0) {\n lines.push(`${metricName} 0`);\n } else {\n for (const [labelKey, count] of state.values) {\n lines.push(`${metricName}${renderLabels(labelKey)} ${count}`);\n }\n }\n } else if (state.type === 'histogram') {\n for (const [labelKey, bucketCounts] of state.counts) {\n const labelSuffix = renderLabels(labelKey);\n const baseObj = labelKey === '{}' ? {} : (JSON.parse(labelKey) as Record<string, string>);\n\n // One line per finite bucket\n for (let i = 0; i < state.buckets.length; i++) {\n const leKey = JSON.stringify({ ...baseObj, le: String(state.buckets[i]) });\n lines.push(`${metricName}_bucket${renderLabels(leKey)} ${bucketCounts[i] ?? 0}`);\n }\n // +Inf bucket\n const infKey = JSON.stringify({ ...baseObj, le: '+Inf' });\n lines.push(\n `${metricName}_bucket${renderLabels(infKey)} ${bucketCounts[state.buckets.length] ?? 0}`\n );\n lines.push(`${metricName}_sum${labelSuffix} ${state.sums.get(labelKey) ?? 0}`);\n lines.push(`${metricName}_count${labelSuffix} ${state.observations.get(labelKey) ?? 0}`);\n }\n } else if (state.type === 'gauge') {\n if (state.values.size === 0) {\n lines.push(`${metricName} 0`);\n } else {\n for (const [labelKey, val] of state.values) {\n lines.push(`${metricName}${renderLabels(labelKey)} ${val}`);\n }\n }\n }\n }\n\n return `${lines.join('\\n')}\\n`;\n }\n\n /**\n * Reset all metric counters and observations back to zero.\n * Useful between test runs or on a scheduled reset interval.\n */\n reset(): void {\n this.metricState.clear();\n this.initAllState();\n }\n\n /**\n * Express route handler — call `app.get('/metrics', metrics.expressHandler())`.\n *\n * @example\n * ```ts\n * import express from 'express';\n * const app = express();\n * app.get('/metrics', metrics.expressHandler());\n * ```\n */\n expressHandler(): (\n req: unknown,\n res: { set(k: string, v: string): unknown; end(body: string): void }\n ) => void {\n return (_req, res) => {\n res.set('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');\n res.end(this.render());\n };\n }\n\n /**\n * Raw Node.js `http` server handler.\n * Responds to `GET /metrics` with the Prometheus text payload.\n *\n * @example Start a dedicated metrics server on the standard port:\n * ```ts\n * import http from 'node:http';\n * const server = http.createServer(metrics.httpHandler());\n * server.listen(9464); // Prometheus default port for custom exporters\n * ```\n */\n httpHandler(): (req: IncomingMessage, res: ServerResponse) => void {\n return (req, res) => {\n const isMetricsPath = req.url === '/metrics' || req.url === '/metrics/';\n if (!isMetricsPath) {\n res.writeHead(404).end('Not found');\n return;\n }\n const body = this.render();\n res.writeHead(200, {\n 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',\n 'Content-Length': Buffer.byteLength(body),\n });\n res.end(body);\n };\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private initAllState(): void {\n for (const [rawName, config] of Object.entries(this.map)) {\n this.initSingleState(rawName, config);\n }\n }\n\n private initSingleState(rawName: string, config: MetricConfig): void {\n if (config.type === 'counter') {\n this.metricState.set(rawName, { type: 'counter', values: new Map() });\n } else if (config.type === 'histogram') {\n const buckets = [...(config.buckets ?? DEFAULT_BUCKETS)].sort((a, b) => a - b);\n this.metricState.set(rawName, {\n type: 'histogram',\n buckets,\n counts: new Map(),\n sums: new Map(),\n observations: new Map(),\n });\n } else {\n this.metricState.set(rawName, { type: 'gauge', values: new Map() });\n }\n }\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Create a `MetricsPlugin` from a metrics map and return it ready for\n * `logger.use()`.\n *\n * @example\n * ```ts\n * import { createMetricsPlugin } from 'logixia';\n *\n * const metrics = createMetricsPlugin({\n * http_request_duration: {\n * type: 'histogram',\n * field: 'duration',\n * labels: ['method', 'statusCode'],\n * help: 'HTTP request duration in milliseconds',\n * },\n * error_count: {\n * type: 'counter',\n * levelFilter: 'error',\n * labels: ['context'],\n * help: 'Total error log entries',\n * },\n * });\n *\n * logger.use(metrics);\n * app.get('/metrics', metrics.expressHandler());\n * ```\n */\nexport function createMetricsPlugin(map: MetricsMap): MetricsPlugin {\n return new MetricsPlugin(map);\n}\n","/**\n * Logixia - Advanced TypeScript Logger\n *\n * A comprehensive logging library with support for:\n * - Multiple output formats (console, file, JSON)\n * - Trace ID tracking\n * - Performance monitoring\n * - NestJS integration\n * - Customizable log levels and colors\n * - Intelligent log search and aggregation\n * - Natural language query processing\n * - Pattern recognition and anomaly detection\n */\n\nimport { createLogger as createLoggerFromCore, LogixiaLogger } from './core/logitron-logger';\nimport { LogixiaLoggerService } from './core/logitron-nestjs.service';\nimport type { Environment, LogColor, LoggerConfig } from './types';\nimport { LogLevel } from './types';\n\n// Type exports\nexport * from './core/logitron-logger.module';\nexport * from './core/logitron-nestjs.service';\nexport type { LogMethodOptions } from './core/nestjs-extras';\nexport { InjectLogger, LogixiaExceptionFilter, LogMethod } from './core/nestjs-extras';\nexport * from './formatters';\nexport * from './types';\nexport * from './utils/error.utils';\nexport type { OtelBridgeOptions, OtelSpanContext } from './utils/otel';\nexport {\n disableOtelBridge,\n getActiveOtelContext,\n getOtelMetaFields,\n initOtelBridge,\n} from './utils/otel';\nexport { applyRedaction, redactObject } from './utils/redact.utils';\nexport type { LevelSignalOptions, ReconfigurableLogger } from './utils/runtime-control';\nexport { createLevelControlHandler, registerLevelSignal } from './utils/runtime-control';\nexport type { SafeStringifyOptions } from './utils/safe-stringify';\nexport { decycleValue, retrocycle, safeStringify, stringifyForLog } from './utils/safe-stringify';\nexport * from './utils/shutdown.utils';\nexport * from './utils/trace.utils';\nexport type {\n CompiledSchema,\n LogFieldDef,\n LogFieldType,\n LoggerLike,\n LogSchema,\n TypedLogger,\n} from './utils/typed-logger';\nexport { createTypedLogger, defineLogSchema } from './utils/typed-logger';\nexport type {\n WideEventFields,\n WideEventLogger,\n WideEventMiddlewareOptions,\n WideEventOptions,\n} from './wide-events';\nexport {\n addEventFields,\n getEventFields,\n setEventField,\n wideEventMiddleware,\n withWideEvent,\n} from './wide-events';\n\n// OTLP logs export transport (OpenTelemetry-native log emission)\nexport type { OtlpLogTransportConfig } from './transports/otlp.transport';\nexport { OtlpLogTransport, toOtelSeverity } from './transports/otlp.transport';\n\n// Search module exports\nexport * from './search';\n\n// Context propagation (AsyncLocalStorage)\nexport type { LogContext } from './context/async-context';\nexport {\n createExpressContextMiddleware,\n createFastifyContextHook,\n LogixiaContext,\n} from './context/async-context';\n\n// Sampling stats (SamplingConfig is already exported via ./types)\nexport type { SamplingStats } from './utils/sampling.utils';\n\n// Plugin API (Feature 20)\nexport type { LogixiaPlugin } from './plugin';\nexport { globalPluginRegistry, PluginRegistry, usePlugin } from './plugin';\n\n// Exception system — typed LogixiaException + unified error response format\nexport type {\n BuildParams,\n ErrorDetail,\n LogixiaErrorResponse,\n LogixiaExceptionOptions,\n} from './exceptions';\nexport {\n ErrorResponseBuilder,\n generateRequestId,\n generateTraceId,\n isLogixiaException,\n LogixiaException,\n} from './exceptions';\n\n// Metrics extraction → Prometheus (Feature 21)\nexport type {\n CounterConfig,\n GaugeConfig,\n HistogramConfig,\n MetricConfig,\n MetricsMap,\n} from './metrics';\nexport { createMetricsPlugin, MetricsPlugin } from './metrics';\n\n// Core exports\nexport { DEFAULT_CONFIG, LogixiaLogger, LogixiaLoggerService };\n\n/**\n * Default configuration for Logixia logger\n */\nconst DEFAULT_CONFIG = {\n appName: 'App',\n environment: 'development' as Environment,\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 [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 } as Record<string, LogColor>,\n },\n fields: {\n timestamp: true,\n level: true,\n appName: true,\n traceId: true,\n message: true,\n payload: true,\n timeTaken: true,\n },\n outputs: ['console'],\n};\n\n/**\n * Create a new Logixia logger instance with TypeScript support for custom levels\n * @param config - Logger configuration\n * @returns Typed logger instance\n */\nexport const createLogger = createLoggerFromCore;\n\n/**\n * Create a new Logixia logger service for NestJS\n * @param config - Logger configuration\n * @returns LogixiaLoggerService instance\n */\nexport function createLoggerService(config?: Partial<LoggerConfig>): LogixiaLoggerService {\n return new LogixiaLoggerService({ ...DEFAULT_CONFIG, ...config });\n}\n\n/**\n * Default logger instance\n */\nexport const logger = new LogixiaLogger(DEFAULT_CONFIG);\n\n/**\n * Export default configuration for reference\n */\n"],"mappings":";;;;;;;;;;;;;;;AA4IA,IAAa,mBAAb,cAGU,MAAM;CAkBd,YAAY,SAAgD;AAG1D,QAAM,QAAQ,SAAS,EAAE,OAAO,QAAQ,OAAO,CAAC;AAEhD,OAAK,OAAO;AACZ,OAAK,YAAY,QAAQ;AACzB,OAAK,YAAY,QAAQ;AACzB,OAAK,aAAa,QAAQ;AAC1B,OAAK,QAAQ,QAAQ;AACrB,OAAK,UAAU,QAAQ;AACvB,OAAK,SAAS,QAAQ;AACtB,OAAK,WAAW,QAAQ;AAGxB,SAAO,eAAe,MAAM,IAAI,OAAO,UAAU;;;;;;;;;;;;;;;;AAmBrD,SAAgB,mBACd,OACyC;AACzC,QAAO,iBAAiB;;;;;;;;;;;;;;AC/I1B,SAAgB,kBAA0B;AACxC,QAAOA,4CAAa,SAAS,UAAU;;;AAIzC,MAAa,oBAAoB;;;;;AAMjC,SAAS,iBAAiB,QAAwB;AAChD,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,WAAW,IAAK,QAAO;AAC3B,KAAI,UAAU,IAAK,QAAO;AAC1B,KAAI,UAAU,IAAK,QAAO;AAC1B,QAAO;;;;;;AAOT,SAAS,gBACP,OAC2E;AAC3E,QACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAkC,iBAAiB,cAC3D,OAAQ,MAAkC,mBAAmB;;;;;;AAQjE,SAAS,WACP,OACA,SACA,YAC+B;CAC/B,MAAM,QAAQ,iBAAiB,QAAQ,MAAM,QAAQ;CAErD,MAAM,WACJ,iBAAiB,QAAS,MAAsC,QAAQ;CAC1E,IAAIC;AACJ,KAAI,oBAAoB,MACtB,SAAQ,SAAS;UACR,aAAa,UAAa,aAAa,KAChD,SAAQ,OAAO,SAAS;AAG1B,KAAI,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,eAAe,OAAW,QAAO;AAErE,QAAO;EACL,GAAI,UAAU,SAAY,EAAE,OAAO,GAAG,EAAE;EACxC,GAAI,UAAU,SAAY,EAAE,OAAO,GAAG,EAAE;EACxC,GAAI,YAAY,SAAY,EAAE,SAAS,GAAG,EAAE;EAC5C,GAAI,eAAe,SAAY,EAAE,aAAa,YAAY,GAAG,EAAE;EAChE;;AAKH,IAAa,uBAAb,MAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgChC,OAAO,MACL,QACsE;EACtE,MAAM,EAAE,WAAW,MAAM,SAAS,WAAW,SAAS,eAAe;EAErE,MAAM,UAAUD,4CAAa,SAAS,mBAAmB,IAAI;EAC7D,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa;EAC1C,MAAM,aAAa,cAAc,SAAY,KAAK,KAAK,GAAG,YAAY;AAGtE,MAAI,mBAAiC,UAAU,EAAE;GAC/C,MAAME,UAAQ,WAAW,WAAW,SAAS,WAAW;GAExD,MAAMC,aAA0D;IAC9D,MAAM,UAAU;IAChB,MAAM,UAAU;IAChB,SAAS,UAAU;IACpB;AACD,OAAI,UAAU,UAAU,OAAW,YAAW,QAAQ,UAAU;AAChE,OAAI,UAAU,YAAY,UAAa,UAAU,QAAQ,SAAS,EAChE,YAAW,UAAU,UAAU;AAEjC,OAAI,UAAU,WAAW,OAAW,YAAW,UAAU,UAAU;AAcnE,UAAO;IAAE,UAZ4C;KACnD,SAAS;KACT,OAAO;KACP,MAAM;MACJ,GAAI,YAAY,SAAY,EAAE,UAAU,SAAS,GAAG,EAAE;MACtD;MACA;MACA,QAAQ,UAAU;MACnB;KACD,GAAID,YAAU,SAAY,EAAE,gBAAO,GAAG,EAAE;KACzC;IAEkB,YAAY,UAAU;IAAY;;AAIvD,MAAI,gBAAgB,UAAU,EAAE;GAC9B,MAAM,SAAS,UAAU,WAAW;GACpC,MAAM,aAAa,UAAU,aAAa;GAG1C,IAAIE;AACJ,OAAI,OAAO,eAAe,SACxB,WAAU;YACD,OAAO,eAAe,YAAY,eAAe,QAAQ,aAAa,YAAY;IAC3F,MAAM,MAAO,WAAoC;AACjD,cAAU,MAAM,QAAQ,IAAI,GAAG,IAAI,KAAK,KAAK,GAAG,OAAO,IAAI;SAE3D,WAAU,UAAU;GAGtB,MAAM,OAAO,iBAAiB,OAAO;GACrC,MAAM,OAAO,QAAQ;GACrB,MAAMF,UAAQ,WAAW,WAAW,SAAS,WAAW;AASxD,UAAO;IAAE,UAP4C;KACnD,SAAS;KACT,OAAO;MAAE;MAAM;MAAM;MAAS;KAC9B,MAAM;MAAE,GAAI,YAAY,SAAY,EAAE,UAAU,SAAS,GAAG,EAAE;MAAG;MAAW;MAAM;MAAQ;KAC1F,GAAIA,YAAU,SAAY,EAAE,gBAAO,GAAG,EAAE;KACzC;IAEkB,YAAY;IAAQ;;EAKzC,MAAM,QAAQ,WADF,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,UAAU,CAAC,EACnD,SAAS,WAAW;AAkBlD,SAAO;GAAE,UAhB4C;IACnD,SAAS;IACT,OAAO;KACL,MAAM;KACN,MAAM;KACN,SAAS;KACV;IACD,MAAM;KACJ,GAAI,YAAY,SAAY,EAAE,UAAU,SAAS,GAAG,EAAE;KACtD;KACA;KACA,QAAQ;KACT;IACD,GAAI,UAAU,SAAY,EAAE,OAAO,GAAG,EAAE;IACzC;GAEkB,YAAY;GAAK;;;;;;;;;;;;;;;;;;AC1LxC,MAAa,iDAAgD,GAAGG,qDAAsB,SAAS;;;;;;;;;;;;;;;;AA+C/F,SAAgB,UAAU,UAA4B,EAAE,EAAmB;CACzE,MAAM,EAAE,QAAQ,SAAS,UAAU,MAAM,YAAY,OAAO,YAAY,SAAS;AAIjF,QAAO,SACL,QACA,aACA,YACoB;;EACpB,MAAM,iBAAiB,WAAW;AAClC,MAAI,OAAO,mBAAmB,WAAY,QAAO;EAEjD,MAAM,aAAa,OAAO,YAAY;EACtC,MAAM,6BACH,OAA+C,yEAAa,SAAQ;EACvE,MAAM,QAAQ,QAAQ,SAAS,GAAG,UAAU,GAAG;EAE/C,IAAI,kBAAkB;EAGtB,MAAM,oBAAoB,OAAe,QAAuB;AAC9D,sCAAc,cAAc,MAAM,IAAI,MAAM,eAAe,OAAO,IAAI,GAAG;;EAI3E,MAAMC,UACJ,UACA,OACA,SACA,SACS;GACT,MAAM,WACJC,SAIA;GAEF,MAAM,KADS,OAAO,aAAa,aAAa,WAAWA,SAAO,OAAO,KAAKA,SAAO,CACrE,SAAS,KAAK;AAC9B,OAAI,KAAK,OAAQ,EAAoB,UAAU,WAC7C,CAAC,EAAoB,OAAO,MAAe,iBAAiB,OAAO,EAAE,CAAC;;EAI1E,MAAM,aAAa,UAA8B,OAAgB,UAAwB;GACvF,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;GACrE,MAAMC,SAAkBD,SAAO,MAAM,KAAK;IAAE,QAAQ;IAAO,YAAY,KAAK,KAAK,GAAG;IAAO,CAAC;AAC5F,OAAI,UAAU,OAAQ,OAAyB,UAAU,WACvD,CAAC,OAAyB,OAAO,MAAe,iBAAiB,SAAS,EAAE,CAAC;;AAIjF,aAAW,QAAQ,SAAmD,GAAG,MAAiB;GAExF,MAAME,WACJ,KAAK,UAAUC,mDAAoB,iBAAiB;AAEtD,OAAI,CAACH,YAAU,CAAC,iBAAiB;AAC/B,sBAAkB;AAClB,sCACE,iBAAiB,MAAM,wHAExB;;GAGH,MAAM,QAAQ,KAAK,KAAK;GACxB,MAAMI,QAAiC,EAAE,QAAQ,OAAO;AACxD,OAAI,WAAW,KAAK,SAAS,EAAG,OAAM,UAAU;AAGhD,OAAIJ,SAAQ,QAAKA,UAAQ,SAAS,KAAK,SAAS,MAAM;GAEtD,MAAM,aAAa,aAA6C;IAC9D,MAAMK,OAAgC;KAAE,QAAQ;KAAO,YAAY,KAAK,KAAK,GAAG;KAAO;AACvF,QAAI,UAAW,MAAK,YAAYC;AAChC,WAAO;;GAGT,IAAIC;AACJ,OAAI;AACF,aAAS,eAAe,MAAM,MAAM,KAAK;YAClC,OAAO;AAEd,QAAIP,YAAU,UAAW,WAAUA,UAAQ,OAAO,MAAM;AACxD,UAAM;;AAKR,OAAI,UAAU,OAAQ,OAAgC,SAAS,WAC7D,QAAQ,OAA4B,MACjC,aAAa;AACZ,QAAIA,SAAQ,QAAKA,UAAQ,QAAQ,KAAK,SAAS,UAAU,SAAS,CAAC;AACnE,WAAO;OAER,UAAmB;AAClB,QAAIA,YAAU,UAAW,WAAUA,UAAQ,OAAO,MAAM;AACxD,UAAM;KAET;AAKH,OAAIA,SAAQ,QAAKA,UAAQ,QAAQ,KAAK,SAAS,UAAU,OAAO,CAAC;AACjE,UAAO;;AAGT,SAAO;;;AAgCJ,mCAAMQ,yBAAkD;CAI7D,YACE,AAEAC,UACA,AAEAC,cACA;AAIA,OAAK,UAAUV,YAAUG,mDAAoB,iBAAiB;AAG9D,OAAK,eACH,oEAAO,aAAc,aAAY,WAAW,aAAa,UAAU;;CAGvE,MAAM,WAAoB,MAA2B;;EACnD,MAAM,MAAM,KAAK,cAAc;EAE/B,MAAM,UAAU,IAAI,YAMhB;EAOJ,MAAM,WAAW,IAAI,aAA8B;EAInD,MAAM,UACJQ,4CAAa,SAAS,mBAAmB,yBACxC,QAAQ,6EAAU,kBACnB,QAAQ,MACR;EAEF,MAAM,EAAE,UAAU,eAAe,eAAe,qBAAqB,MAAM;GACzE;GACA;GACA,MAAM,QAAQ,OAAO;GACrB,WAAW,QAAQ;GACpB,CAAC;AAGF,MAAI,KAAK,SAAS;GAChB,MAAMC,cAAuC;IAC3C,QAAQ,QAAQ,UAAU;IAC1B,KAAK,QAAQ,OAAO;IACpB,QAAQ;IAGR,GAAI,cAAc,KAAK,aAAa,SAChC,EAAE,UAAU,cAAc,KAAK,UAAU,GACzC,EAAE;IACP;GAMD,MAAM,gBAAgB,QAAuB;AAC3C,uCAAc,8CAA8C,OAAO,IAAI,GAAG;;GAG5E,IAAIC;AACJ,OAAI,cAAc,KAAK;IACrB,MAAM,MAAM,qBAAqB,QAAQ,YAAY,IAAI,MAAM,OAAO,UAAU,CAAC;AACjF,iBAAa,KAAK,QAAQ,MAAM,KAAK,YAAY;cACxC,mBAAmB,UAAU,CACtC,cAAa,KAAK,QAAQ,KACxB,IAAI,cAAc,MAAM,KAAK,IAAI,cAAc,MAAM,WACrD,YACD;OAED,cAAa,KAAK,QAAQ,KACxB,IAAI,WAAW,IAAI,cAAc,MAAM,WACvC,YACD;AAGH,OAAI,cAAc,OAAQ,WAA6B,UAAU,WAC/D,CAAC,WAA6B,MAAM,aAAa;;AAKrD,MAAI,QAAQ,IAAI,gBAAgB,aAC9B,QAAO,cAAc;AAIvB,MAAI,cAAc,KAAK,UAAU;GAC/B,MAAM,cAAcC,qDAAsB,KAAK,aAAa;AAC5D,OAAI,YACF,UAAS,UAAU,aAAa,cAAc,KAAK,SAAS;;AAGhE,MAAI,eAAe,IACjB,UAAS,UAAU,eAAe,KAAK;AAGzC,WAAS,OAAO,WAAW,CAAC,KAAK,cAAc;;;;6BAlH3C;kFAMO;+EACH,GAAGhB,qDAAsB,SAAS;kFAE/B;+EACHiB,qDAAsB;;;;;;;;;;;ACxPlC,SAAS,yBAAmE;CAC1E,MAAM,uBAAO,IAAI,SAAiB;AAClC,QAAO,SAAyB,MAAc,OAAyB;AACrE,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,OAAI,KAAK,IAAI,MAAM,CAAE,QAAO;AAC5B,QAAK,IAAI,MAAM;;AAEjB,SAAO;;;AAIX,IAAa,gBAAb,MAAoD;CAQlD,YACE,UAOI,EAAE,EACN;AACA,OAAK,mBAAmB,QAAQ,oBAAoB;AACpD,OAAK,eAAe,QAAQ,gBAAgB;AAC5C,OAAK,iBAAiB,QAAQ,kBAAkB;AAChD,OAAK,iBAAiB,QAAQ,kBAAkB;AAChD,OAAK,iBAAiB,QAAQ,kBAAkB;AAChD,OAAK,cAAc,QAAQ,eAAe;;CAG5C,OAAO,OAAyB;EAC9B,MAAMC,YAAqC,EAAE;AAG7C,MAAI,KAAK,iBACP,WAAU,YAAY,MAAM;AAI9B,MAAI,KAAK,cAAc;AACrB,aAAU,QAAQ,MAAM,MAAM,aAAa;AAC3C,aAAU,aAAa,MAAM;;AAI/B,MAAI,KAAK,eACP,WAAU,UAAU,MAAM;AAI5B,MAAI,KAAK,kBAAkB,MAAM,QAC/B,WAAU,UAAU,MAAM;AAI5B,MAAI,KAAK,kBAAkB,MAAM,QAC/B,WAAU,UAAU,MAAM;AAI5B,YAAU,UAAU,MAAM;AAG1B,MAAI,MAAM,WAAW,OAAO,KAAK,MAAM,QAAQ,CAAC,SAAS,EACvD,WAAU,UAAU,KAAK,iBAAiB,MAAM,QAAQ;AAI1D,MAAI,MAAM,MACR,WAAU,QAAQC,8CAAe,MAAM,MAAM;AAI/C,YAAU,OAAO;GACf,KAAK,QAAQ;GACb,UAAU,QAAQ,IAAI,YAAY;GAClC,SAAS,QAAQ;GAClB;EAKD,MAAM,WAAW,wBAAwB;AACzC,SAAO,KAAK,cACR,KAAK,UAAU,WAAW,UAAU,EAAE,GACtC,KAAK,UAAU,WAAW,SAAS;;CAGzC,AAAQ,iBAAiB,SAA2D;EAClF,MAAMC,aAAsC,EAAE;AAE9C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,OAAI,QAAQ,eAAe,QAAQ,iBAAiB,QAAQ,YAAa;AACzE,OAAI;AACF,QAAI,iBAAiB,MACnB,YAAW,OAAOD,8CAAe,MAAM;aAC9B,iBAAiB,KAC1B,YAAW,OAAO,MAAM,aAAa;aAC5B,OAAO,UAAU,WAC1B,YAAW,OAAO;aACT,OAAO,UAAU,SAC1B,YAAW,OAAO,MAAM,UAAU;aACzB,UAAU,OACnB,YAAW,OAAO;QAElB,YAAW,OAAO;WAEd;AACN,eAAW,OAAO;;;AAItB,SAAO;;;;;;ACxHX,MAAM,mBAAmB;AAGzB,MAAM,iBAAiB,UAA2BE,kCAAa,MAAM,CAAC,QAAQ,kBAAkB,GAAG;AAEnG,IAAa,gBAAb,MAAa,cAAuC;CASlD,YACE,UAQI,EAAE,EACN;AACA,OAAK,WAAW,QAAQ,YAAY;AACpC,OAAK,mBAAmB,QAAQ,oBAAoB;AACpD,OAAK,iBAAiB,QAAQ,kBAAkB;AAChD,OAAK,iBAAiB,QAAQ,kBAAkB;AAChD,OAAK,iBAAiB,QAAQ,kBAAkB;AAChD,OAAK,kBAAkB,QAAQ,mBAAmB;AAClD,OAAK,SAAS;GACZ,OAAO;GACP,MAAM;GACN,MAAM;GACN,OAAO;GACP,OAAO;GACP,SAAS;GACT,OAAO;GACP,MAAM;GACN,KAAK;GACL,GAAG,QAAQ;GACZ;;CAGH,OAAO,OAAyB;EAC9B,MAAMC,QAAkB,EAAE;AAG1B,MAAI,KAAK,kBAAkB;GACzB,MAAM,YAAY,KAAK,gBAAgB,MAAM,UAAU;AACvD,SAAM,KAAK,KAAK,WAAW,GAAG,KAAK,OAAO,MAAM,YAAY,KAAK,OAAO,UAAU,UAAU;;EAI9F,MAAM,YAAY,MAAM,MAAM,aAAa;EAC3C,MAAM,aAAa,KAAK,OAAO,cAAc,KAAK,OAAO;EACzD,MAAM,iBAAiB,KAAK,WACxB,GAAG,aAAa,KAAK,OAAO,OAAO,UAAU,aAAa,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,UACnF,UAAU,aAAa,CAAC,OAAO,EAAE;AACrC,QAAM,KAAK,IAAI,eAAe,GAAG;AAGjC,MAAI,KAAK,gBAAgB;GACvB,MAAM,cAAc,cAAc,MAAM,QAAQ;GAChD,MAAM,UAAU,KAAK,WACjB,GAAG,KAAK,OAAO,OAAO,cAAc,KAAK,OAAO,UAChD;AACJ,SAAM,KAAK,IAAI,QAAQ,GAAG;;AAI5B,MAAI,KAAK,kBAAkB,MAAM,SAAS;GACxC,MAAM,cAAc,cAAc,MAAM,QAAQ;GAChD,MAAM,UAAU,KAAK,WACjB,GAAG,KAAK,OAAO,MAAM,cAAc,KAAK,OAAO,UAC/C;AACJ,SAAM,KAAK,IAAI,QAAQ,GAAG;;AAI5B,MAAI,KAAK,kBAAkB,MAAM,SAAS;GACxC,MAAM,cAAc,cAAc,MAAM,QAAQ;GAChD,MAAM,UAAU,KAAK,WACjB,GAAG,KAAK,OAAO,OAAO,cAAc,KAAK,OAAO,UAChD;AACJ,SAAM,KAAK,IAAI,QAAQ,GAAG;;EAI5B,MAAM,cAAc,cAAc,MAAM,QAAQ;EAChD,MAAM,UACJ,KAAK,YAAY,MAAM,UAAUC,wCAAS,QACtC,GAAG,KAAK,OAAO,QAAQ,cAAc,KAAK,OAAO,UACjD;AACN,QAAM,KAAK,QAAQ;AAGnB,MAAI,MAAM,WAAW,OAAO,KAAK,MAAM,QAAQ,CAAC,SAAS,GAAG;GAC1D,MAAM,UAAU,KAAK,cAAc,MAAM,QAAQ;AACjD,OAAI,QACF,OAAM,KAAK,KAAK,WAAW,GAAG,KAAK,OAAO,MAAM,UAAU,KAAK,OAAO,UAAU,QAAQ;;AAI5F,SAAO,MAAM,KAAK,IAAI;;CAGxB,AAAQ,gBAAgB,WAA2B;EACjD,MAAM,OAAO,IAAI,KAAK,UAAU;AAEhC,UAAQ,KAAK,iBAAb;GACE,KAAK,MACH,QAAO,KAAK,aAAa;GAC3B,KAAK,QACH,QAAO,KAAK,oBAAoB;GAClC,KAAK;GACL,QACE,QAAO,KAAK,gBAAgB;;;CAIlC,AAAQ,cAAc,SAA0C;AAC9D,MAAI;AAEF,OAAI,OAAO,KAAK,QAAQ,CAAC,WAAW,GAAG;IACrC,MAAM,QAAQ,OAAO,QAAQ,QAAQ,CAAC;AACtC,QAAI,OAAO;KACT,MAAM,CAAC,KAAK,SAAS;AACrB,SACE,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,UAEjB,QAAO,GAAG,IAAI,GAAG;;;AA8BvB,UAxBkB,OAAO,QAAQ,QAAQ,CACtC,KAAK,CAAC,KAAK,WAAW;AACrB,QAAI,UAAU,QAAQ,UAAU,OAC9B,QAAO,GAAG,IAAI,GAAG;AAEnB,QAAI,OAAO,UAAU,SACnB,QAAO,GAAG,IAAI,IAAI,MAAM;AAE1B,QAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,QAAO,GAAG,IAAI,GAAG;AAEnB,QAAI,iBAAiB,KACnB,QAAO,GAAG,IAAI,GAAG,MAAM,aAAa;AAEtC,QAAI,OAAO,UAAU,SAInB,QAAO,GAAG,IAAI,GAAG,cAAcF,kCAAa,MAAM,CAAC;AAErD,WAAO,GAAG,IAAI,GAAG,OAAO,MAAM;KAC9B,CACD,KAAK,IAAI;UAGN;AAGN,UAAO,cAAcA,kCAAa,QAAQ,CAAC;;;;;;CAO/C,OAAO,eAA8B;AACnC,SAAO,IAAI,cAAc;GACvB,UAAU;GACV,kBAAkB;GAClB,gBAAgB;GAChB,gBAAgB;GAChB,gBAAgB;GAChB,iBAAiB;GAClB,CAAC;;CAGJ,OAAO,iBAAgC;AACrC,SAAO,IAAI,cAAc;GACvB,UAAU;GACV,kBAAkB;GAClB,gBAAgB;GAChB,gBAAgB;GAChB,gBAAgB;GAChB,iBAAiB;GAClB,CAAC;;CAGJ,OAAO,gBAA+B;AACpC,SAAO,IAAI,cAAc;GACvB,UAAU;GACV,kBAAkB;GAClB,gBAAgB;GAChB,gBAAgB;GAChB,gBAAgB;GACjB,CAAC;;;;;;AC/KN,MAAMG,gBAA2C;CAC/C;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;AAiBD,SAAgB,oBACd,UACA,UAA8B,EAAE,EACpB;CACZ,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,QAAQ,QAAQ,SAAS,QAAQ,MAAM,SAAS,IAAI,QAAQ,QAAQ,CAAC,GAAG,cAAc;CAE5F,MAAM,gBAAsB;EAC1B,MAAM,UAAUC,SAAO,UAAU;EAEjC,MAAM,OAAO,OADD,MAAM,QAAQ,QAAQ,GACR,KAAK,MAAM;AACrC,WAAO,SAAS,KAAK;AACrB,mCAAY,6BAA6B,OAAO,IAAI,QAAQ,KAAK,OAAO;;AAG1E,SAAQ,GAAG,QAAQ,QAAQ;AAC3B,cAAa;AACX,UAAQ,eAAe,QAAQ,QAAQ;;;AAgB3C,MAAM,eAAe,IAAI,IAAY;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;AAaF,SAAgB,0BACd,UACA,UAAwC,EAAE,EACE;CAC5C,MAAM,UACJ,QAAQ,iBAAiB,QAAQ,cAAc,SAAS,IACpD,IAAI,IAAI,QAAQ,cAAc,KAAK,MAAM,EAAE,aAAa,CAAC,CAAC,GAC1D;CAEN,MAAM,iBAA8E;;SAAC;GACnF,OAAOA,SAAO,UAAU;GACxB,2CAAiBA,SAAO,gHAAsB,KAAI,EAAE;GACrD;;CAED,MAAM,QAAQ,KAAiB,QAAgB,SAAwB;;AACrE,MAAI,aAAa;AACjB,wBAAI,6EAAY,gBAAgB,mBAAmB;AACnD,MAAI,IAAI,KAAK,UAAU,KAAK,CAAC;;CAG/B,MAAM,aAAa,KAAiB,QAAsB;EACxD,IAAIC;AACJ,MAAI;AACF,YAAS,MAAO,KAAK,MAAM,IAAI,GAAqB,EAAE;UAChD;AACN,QAAK,KAAK,KAAK,EAAE,OAAO,qBAAqB,CAAC;AAC9C;;AAGF,MAAI,OAAO,UAAU,QAAW;GAC9B,MAAM,MAAM,OAAO,OAAO,MAAM,CAAC,aAAa;AAC9C,OAAI,CAAC,QAAQ,IAAI,IAAI,EAAE;AACrB,SAAK,KAAK,KAAK;KAAE,OAAO,kBAAkB,OAAO,MAAM;KAAI,SAAS,CAAC,GAAG,QAAQ;KAAE,CAAC;AACnF;;AAEF,YAAO,SAAS,IAAsB;;AAGxC,MAAI,OAAO,oBAAoB,QAAW;AACxC,OACE,OAAO,OAAO,oBAAoB,YAClC,OAAO,oBAAoB,QAC3B,MAAM,QAAQ,OAAO,gBAAgB,EACrC;AACA,SAAK,KAAK,KAAK,EAAE,OAAO,qCAAqC,CAAC;AAC9D;;GAEF,MAAM,KAAK,OAAO;AAClB,QAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,GAAG,CACzC,KAAI,CAAC,QAAQ,IAAI,OAAO,IAAI,CAAC,aAAa,CAAC,EAAE;AAC3C,SAAK,KAAK,KAAK,EAAE,OAAO,kBAAkB,OAAO,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC;AAClF;;AAGJ,OAAID,SAAO,oBAAoB;IAC7B,MAAME,UAA2B,EAAE;AACnC,SAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,GAAG,CACzC,SAAQ,OAAO,OAAO,IAAI,CAAC,aAAa;AAE1C,aAAO,mBAAmB,QAAQ;SAElC,mCAAa,sEAAsE;;AAIvF,OAAK,KAAK,KAAK,UAAU,CAAC;;AAG5B,QAAO,SAAS,oBAAoB,KAAiB,KAAuB;EAC1E,MAAM,UAAU,IAAI,UAAU,OAAO,aAAa;AAElD,MAAI,WAAW,OAAO;AACpB,QAAK,KAAK,KAAK,UAAU,CAAC;AAC1B;;AAGF,MAAI,WAAW,UAAU,WAAW,SAAS,WAAW,SAAS;GAE/D,MAAM,YAAa,IAAsC;AACzD,OAAI,cAAc,UAAa,OAAO,IAAI,OAAO,YAAY;AAC3D,cAAU,KAAK,OAAO,cAAc,WAAW,YAAY,KAAK,UAAU,UAAU,CAAC;AACrF;;AAEF,OAAI,OAAO,IAAI,OAAO,YAAY;IAChC,IAAI,MAAM;AACV,QAAI,GAAG,SAAS,UAAoB;AAClC,YAAO,OAAO,SAAS,GAAG;MAC1B;AACF,QAAI,GAAG,aAAa,UAAU,KAAK,IAAI,CAAC;AACxC;;AAEF,aAAU,KAAK,GAAG;AAClB;;AAGF,OAAK,KAAK,KAAK,EAAE,OAAO,UAAU,OAAO,eAAe,CAAC;;;;;;;;;;;;ACxI7D,SAAgB,gBACd,QACyB;AACzB,QAAO;EACL;EACA,SAAS,SAAqC;AAC5C,OAAI,QAAQ,IAAI,gBAAgB,aAAc,QAAO,EAAE;GAEvD,MAAMC,WAAqB,EAAE;AAE7B,QAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,OAAO,EAA6C;IAC1F,MAAM,QAAQ,QAAQ;AAEtB,QAAI,IAAI,aAAa,UAAU,UAAa,UAAU,OAAO;AAC3D,cAAS,KAAK,mBAAmB,IAAI,cAAc;AACnD;;AAGF,QAAI,UAAU,UAAa,UAAU,MAAM;KACzC,MAAM,aAAa,MAAM,QAAQ,MAAM,GAAG,UAAU,OAAO;AAC3D,SAAI,eAAe,IAAI,KACrB,UAAS,KAAK,UAAU,IAAI,mBAAmB,IAAI,KAAK,aAAa,WAAW,GAAG;AAGrF,SAAI,IAAI,UAAU;MAChB,MAAM,MAAM,IAAI,SAAS,MAAM;AAC/B,UAAI,IAAK,UAAS,KAAK,UAAU,IAAI,KAAK,MAAM;;;;AAKtD,UAAO;;EAEV;;;;;;;;AAuCH,SAAgB,kBACd,UACA,QACsB;CACtB,SAAS,eACP,OACA,IACA,SACA,MACe;AACf,MAAI,UAAU,MAAM;GAClB,MAAM,WAAW,OAAO,SAAS,KAAK;AACtC,QAAK,MAAM,KAAK,SACd,mCAAa,oBAAoB,EAAE,WAAW,MAAM,YAAY,QAAQ,GAAG;;AAG/E,SAAO,GAAG,SAAS,KAA4C;;AAGjE,QAAO;EACL,KAAKC;EACL,MAAM,gBAAgC,MAAwC;AAC5E,OAAI,UAAU,MAAM;IAClB,MAAM,WAAW,OAAO,SAAS,KAAK;IACtC,MAAM,UAAU,0BAA0B,QAAQ,eAAe,UAAU;AAC3E,SAAK,MAAM,KAAK,SACd,mCAAa,oBAAoB,EAAE,0BAA0B,QAAQ,GAAG;;AAG5E,UAAOA,SAAO,MAAM,gBAAgB,KAA4C;;EAElF,OAAO,GAAG,MAAM,eAAe,QAAQA,SAAO,KAAK,KAAKA,SAAO,EAAE,GAAG,EAAE;EACtE,OAAO,GAAG,MAAM,eAAe,QAAQA,SAAO,KAAK,KAAKA,SAAO,EAAE,GAAG,EAAE;EACtE,QAAQ,GAAG,MAAM,eAAe,SAASA,SAAO,MAAM,KAAKA,SAAO,EAAE,GAAG,EAAE;EACzE,UAAU,GAAG,MACX,eAAe,YAAYA,SAAO,WAAWA,SAAO,OAAO,KAAKA,SAAO,EAAE,GAAG,EAAE;EAChF,QAAQ,GAAG,MAAM,eAAe,UAAUA,SAAO,SAASA,SAAO,OAAO,KAAKA,SAAO,EAAE,GAAG,EAAE;EAC5F;;;;;AC3IH,MAAM,WAAW,IAAIC,oCAAmC;;;;;;AA0BxD,SAAgB,eAAe,QAA+B;CAC5D,MAAM,QAAQ,SAAS,UAAU;AACjC,KAAI,CAAC,SAAS,MAAM,QAAS;AAC7B,QAAO,OAAO,MAAM,QAAQ,OAAO;;;AAIrC,SAAgB,cAAc,KAAa,OAAsB;AAC/D,gBAAe,GAAG,MAAM,OAAO,CAAC;;;AAIlC,SAAgB,iBAA8C;CAC5D,MAAM,QAAQ,SAAS,UAAU;AACjC,QAAO,QAAQ,EAAE,GAAG,MAAM,QAAQ,GAAG;;AAGvC,SAAS,KACP,UACA,OACA,SACA,OACM;AACN,KAAI,MAAM,QAAS;AACnB,OAAM,UAAU;CAEhB,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,eAAe,QAAQ,gBAAgB;CAE7C,MAAMC,UAA2B,EAAE,GAAG,MAAM,QAAQ;AACpD,KAAI,MAAO,QAAO,OAAO,SAAS,MAAM;AACxC,SAAQ,iBAAiB,KAAK,KAAK,GAAG,MAAM;AAE5C,KAAI,cAAc;EAChB,MAAM,UAAUC,kDAAmB;AACnC,MAAI,YAAY,UAAa,QAAQ,eAAe,OAAW,SAAQ,aAAa;;CAMtF,MAAM,IAAIC,SAAO,SAAS,OAAO,SAAS,QAAQ;AAClD,KAAI,KAAK,OAAQ,EAAoB,UAAU,WAC7C,CAAC,EAAoB,YAAY,GAAG;;;;;;;;AAUxC,eAAsB,cACpB,UACA,eACA,UACA,UAA4B,EAAE,EAClB;CACZ,MAAMC,QAAwB;EAC5B,QAAQ,EAAE,GAAG,eAAe;EAC5B,SAAS,KAAK,KAAK;EACnB,SAAS;EACV;AAED,QAAO,SAAS,IAAI,OAAO,YAAY;AACrC,MAAI;GACF,MAAM,SAAS,MAAM,UAAU;AAC/B,QAAKD,UAAQ,OAAO,QAAQ;AAC5B,UAAO;WACA,OAAO;AACd,QAAKA,UAAQ,OAAO,SAAS;IAC3B,OAAO;IACP,cAAc,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IACrE,CAAC;AACF,SAAM;;GAER;;;;;;;;;AAgCJ,SAAgB,oBACd,UACA,UAAsC,EAAE,EACY;AACpD,QAAO,SAAS,2BAA2B,KAAY,KAAY,MAAwB;;AACzF,uBAAI,QAAQ,kFAAO,IAAI,EAAE;AACvB,SAAM;AACN;;EAUF,MAAMC,QAAwB;GAAE,QAPF;IAC5B,QAAQ,IAAI;IACZ,KAAK,IAAI,eAAe,IAAI;IAC5B,IAAI,IAAI,sBAAM,IAAI,kEAAQ;IAC1B,GAAI,QAAQ,SAAS,QAAQ,OAAO,IAAI,GAAG,EAAE;IAC9C;GAE6C,SAAS,KAAK,KAAK;GAAE,SAAS;GAAO;EAEnF,MAAM,iBAAuB;AAC3B,QAAKD,UAAQ,OAAO,SAAS,EAAE,YAAY,IAAI,cAAc,GAAG,CAAC;;AAKnE,mBAAI,8DAAO,UAAU,SAAS;AAC9B,oBAAI,gEAAO,SAAS,SAAS;AAE7B,WAAS,IAAI,aAAa,MAAM,CAAC;;;;;;ACvJrC,MAAM,SAAS;;;;;AAMf,SAAgB,cAAc,IAAgC;CAC5D,MAAM,MAAM,GAAG,QAAQ,MAAM,GAAG,CAAC,aAAa;AAC9C,QAAO,OAAO,KAAK,IAAI,IAAI,QAAQ,IAAI,OAAO,GAAG,GAAG,MAAM;;;;;;;AAQ5D,SAAgB,eAAe,OAAiD;AAC9E,SAAQ,MAAM,aAAa,EAA3B;EACE,KAAK,QACH,QAAO;GAAE,QAAQ;GAAG,MAAM;GAAS;EACrC,KAAK,UACH,QAAO;GAAE,QAAQ;GAAG,MAAM;GAAS;EACrC,KAAK,QACH,QAAO;GAAE,QAAQ;GAAG,MAAM;GAAS;EACrC,KAAK,OACH,QAAO;GAAE,QAAQ;GAAG,MAAM;GAAQ;EACpC,KAAK;EACL,KAAK,UACH,QAAO;GAAE,QAAQ;GAAI,MAAM;GAAQ;EACrC,KAAK,QACH,QAAO;GAAE,QAAQ;GAAI,MAAM;GAAS;EACtC,KAAK,QACH,QAAO;GAAE,QAAQ;GAAI,MAAM;GAAS;EACtC,QACE,QAAO;GAAE,QAAQ;GAAG,MAAM;GAAQ;;;;AAKxC,SAAS,WAAW,OAAyC;AAC3D,KAAI,OAAO,UAAU,SAAU,QAAO,EAAE,aAAa,OAAO;AAC5D,KAAI,OAAO,UAAU,UAAW,QAAO,EAAE,WAAW,OAAO;AAC3D,KAAI,OAAO,UAAU,SACnB,QAAO,OAAO,UAAU,MAAM,GAAG,EAAE,UAAU,OAAO,GAAG,EAAE,aAAa,OAAO;AAE/E,KAAI,OAAO,UAAU,SAAU,QAAO,EAAE,aAAa,MAAM,UAAU,EAAE;AACvE,KAAI,UAAU,QAAQ,UAAU,OAAW,QAAO,EAAE,aAAa,IAAI;AAGrE,KAAI;AACF,SAAO,EAAE,aAAaE,uCAAgB,MAAM,EAAE;SACxC;AACN,SAAO,EAAE,aAAa,OAAO,MAAM,EAAE;;;;AAKzC,SAAS,aAAa,KAAsE;AAC1F,QAAO,OAAO,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,YAAY;EAAE;EAAK,OAAO,WAAW,MAAM;EAAE,EAAE;;AAGvF,IAAa,mBAAb,MAAyD;CAcvD,YAAY,QAAgC;cAbrB;oBAWqB;AAG1C,OAAK,MAAM,OAAO;AAClB,OAAK,UAAU,OAAO,WAAW,EAAE;AACnC,OAAK,YAAY,OAAO,aAAa;AACrC,OAAK,kBAAkB,OAAO,mBAAmB;AACjD,OAAK,mBAAmB,OAAO,oBAAoBC;AACnD,OAAK,QAAQ,OAAO;AACpB,OAAK,SAAS,IAAIC,iCAA+B;GAC/C,MAAM;GACN,WAAW,KAAK;GAChB,eAAe,OAAO,iBAAiBC;GACvC,OAAO,YAAY,KAAK,KAAK,QAAQ;GACtC,CAAC;AAQF,OAAK,gBAAgB,aANqB;GACxC,gBAAgB,OAAO,eAAe;GACtC,GAAI,OAAO,iBAAiB,EAAE,mBAAmB,OAAO,gBAAgB,GAAG,EAAE;GAC7E,GAAI,OAAO,cAAc,EAAE,0BAA0B,OAAO,aAAa,GAAG,EAAE;GAC9E,GAAI,OAAO,sBAAsB,EAAE;GACpC,CAC0C;AAE3C,OAAK,aAAa,kBAAkB;AAClC,QAAK,OAAO,CAAC,YAAY,GAAG;KAC3B,KAAK,gBAAgB;AACxB,MAAI,KAAK,WAAW,MAAO,MAAK,WAAW,OAAO;;CAGpD,MAAM,OAAgC;AACpC,OAAK,OAAO,KAAK,MAAM;AACvB,OAAK,OAAO,aAAa;;;CAI3B,AAAQ,YAAY,OAAmD;EACrE,MAAM,MAAM,eAAe,MAAM,MAAM;EACvC,MAAM,UAAU,OAAO,MAAM,UAAU,SAAS,GAAG,IAAU;EAE7D,MAAMC,QAAiC,EAAE,GAAI,MAAM,QAAQ,EAAE,EAAG;AAChE,MAAI,MAAM,YAAY,OAAW,OAAM,aAAa,MAAM;AAC1D,MAAI,MAAM,YAAY,OAAW,OAAM,cAAc,MAAM;AAC3D,MAAI,MAAM,gBAAgB,OAAW,OAAM,4BAA4B,MAAM;EAE7E,MAAMC,SAAkC;GACtC,cAAc;GACd,sBAAsB;GACtB,gBAAgB,IAAI;GACpB,cAAc,IAAI;GAClB,MAAM,EAAE,aAAa,MAAM,SAAS;GACpC,YAAY,aAAa,MAAM;GAChC;AAED,MAAI,MAAM,SAAS;GACjB,MAAM,UAAU,cAAc,MAAM,QAAQ;AAC5C,OAAI,QAAS,QAAO,aAAa;OAE/B,CAAC,OAAO,cAA4B,KAAK;IACvC,KAAK;IACL,OAAO,EAAE,aAAa,MAAM,SAAS;IACtC,CAAC;;AAGN,SAAO;;CAGT,AAAQ,aAAa,SAAsC;AACzD,SAAOL,uCAAgB,EACrB,cAAc,CACZ;GACE,UAAU,EAAE,YAAY,KAAK,eAAe;GAC5C,WAAW,CACT;IACE,OAAO,EAAE,MAAM,WAAW;IAC1B,YAAY,QAAQ,KAAK,MAAM,KAAK,YAAY,EAAE,CAAC;IACpD,CACF;GACF,CACF,EACF,CAAC;;CAGJ,QAAuB;AACrB,SAAO,KAAK,OAAO,OAAO;;CAG5B,MAAc,KAAK,SAA6C;AAC9D,MAAI,OAAO,UAAU,YAAY;AAC/B,qCAAa,kEAAkE;AAC/E;;EAEF,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,KAAK,iBAAiB;AACzE,MAAI;GACF,MAAM,MAAM,MAAM,MAAM,KAAK,KAAK;IAChC,QAAQ;IACR,SAAS;KAAE,gBAAgB;KAAoB,GAAG,KAAK;KAAS;IAChE,MAAM,KAAK,aAAa,QAAQ;IAChC,QAAQ,WAAW;IACpB,CAAC;AACF,OAAI,CAAC,IAAI,GACP,OAAM,IAAIM,+BAAU,IAAI,QAAQ,IAAI,cAAc;WAE7C,KAAK;AACZ,OAAI,WAAW,OAAO,QACpB,OAAM,IAAI,MAAM,+BAA+B,KAAK,iBAAiB,KAAK,EAAE,OAAO,KAAK,CAAC;AAE3F,SAAM;YACE;AACR,gBAAa,MAAM;;;CAIvB,MAAM,QAAuB;AAC3B,MAAI,KAAK,YAAY;AACnB,iBAAc,KAAK,WAAW;AAC9B,QAAK,aAAa;;AAEpB,QAAM,KAAK,OAAO,OAAO;;;;;;ACtG7B,MAAMC,kBAAqC;CACzC;CAAG;CAAG;CAAI;CAAI;CAAI;CAAK;CAAK;CAAK;CAAO;CAAO;CAAO;CACvD;AAID,SAAS,cAAc,QAAsB,OAAyB;CACpE,MAAM,aAAa,OAAO,UAAU,EAAE;AACtC,KAAI,WAAW,WAAW,EAAG,QAAO;CACpC,MAAMC,QAAgC,EAAE;CACxC,MAAM,UAAU,MAAM,WAAW,EAAE;AACnC,MAAK,MAAM,QAAQ,YAAY;EAC7B,MAAM,MAAM,SAAS,UAAU,MAAM,QAAQ,QAAQ;AAGrD,QAAM,iBAAiB,KAAK,IAAI,QAAQ,UAAa,QAAQ,OAAO,OAAO,IAAI,GAAG;;AAEpF,QAAO,KAAK,UAAU,MAAM;;AAG9B,SAAS,aAAa,UAA0B;AAC9C,KAAI,aAAa,KAAM,QAAO;CAC9B,MAAM,MAAM,KAAK,MAAM,SAAS;AAEhC,QAAO,IADO,OAAO,QAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,IAAI,YAAY,EAAE,CAAC,GAAG,CAC5D,KAAK,IAAI,CAAC;;AAG7B,SAAS,YAAY,OAAuB;AAC1C,QAAO,MAAM,QAAQ,OAAO,OAAO,CAAC,QAAQ,MAAM,OAAM,CAAC,QAAQ,OAAO,MAAM;;;;;;;;;AAUhF,SAAS,iBAAiB,MAAsB;CAC9C,IAAI,OAAO,KAAK,QAAQ,OAAO,IAAI;AACnC,KAAI,KAAK,SAAS,KAAK,KAAK,KAAK,KAAK,GAAI,CAAE,QAAO,IAAI;AACvD,QAAO,KAAK,SAAS,IAAI,OAAO;;;;;;;;;;;;;;;;;AAoBlC,IAAa,gBAAb,MAAoD;CAMlD,YAAY,KAAiB;cALN;qCAGQ,IAAI,KAA0B;AAG3D,OAAK,MAAM;AACX,OAAK,cAAc;;CAKrB,SAAe;CAIf,MAAM,OAA2B;EAC/B,MAAM,UAAU,MAAM,WAAW,EAAE;AAEnC,OAAK,MAAM,CAAC,SAAS,WAAW,OAAO,QAAQ,KAAK,IAAI,EAAE;GACxD,MAAM,QAAQ,KAAK,YAAY,IAAI,QAAQ;AAC3C,OAAI,CAAC,MAAO;GAEZ,MAAM,WAAW,cAAc,QAAQ,MAAM;AAE7C,OAAI,OAAO,SAAS,aAAa,MAAM,SAAS,WAAW;AAEzD,QAAI,OAAO,eAAe,MAAM,UAAU,OAAO,YAAa;AAE9D,QAAI,OAAO,UAAU,UAAa,QAAQ,OAAO,WAAW,OAAO,MAAO;AAC1E,UAAM,OAAO,IAAI,WAAW,MAAM,OAAO,IAAI,SAAS,IAAI,KAAK,EAAE;cACxD,OAAO,SAAS,eAAe,MAAM,SAAS,aAAa;IACpE,MAAM,MAAM,QAAQ,OAAO;AAC3B,QAAI,QAAQ,UAAa,QAAQ,KAAM;IACvC,MAAM,MAAM,OAAO,IAAI;AACvB,QAAI,OAAO,MAAM,IAAI,CAAE;AAGvB,QAAI,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE;AAC/B,WAAM,OAAO,IACX,UACA,MAAM,KAAa,EAAE,QAAQ,MAAM,QAAQ,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,CACjE;AACD,WAAM,KAAK,IAAI,UAAU,EAAE;AAC3B,WAAM,aAAa,IAAI,UAAU,EAAE;;IAGrC,MAAM,YAAY,MAAM,OAAO,IAAI,SAAS;AAC5C,SAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,QAAQ,IACxC,KAAI,OAAO,MAAM,QAAQ,GAAK,WAAU;AAG1C,cAAU,MAAM,QAAQ;AACxB,UAAM,KAAK,IAAI,WAAW,MAAM,KAAK,IAAI,SAAS,IAAI,KAAK,IAAI;AAC/D,UAAM,aAAa,IAAI,WAAW,MAAM,aAAa,IAAI,SAAS,IAAI,KAAK,EAAE;cACpE,OAAO,SAAS,WAAW,MAAM,SAAS,SAAS;IAC5D,MAAM,MAAM,QAAQ,OAAO;AAC3B,QAAI,QAAQ,UAAa,QAAQ,KAAM;IACvC,MAAM,MAAM,OAAO,IAAI;AACvB,QAAI,OAAO,MAAM,IAAI,CAAE;AACvB,UAAM,OAAO,IAAI,UAAU,IAAI;;;AAKnC,SAAO;;;;;;;;;;;;;;;CAkBT,SAAiB;EACf,MAAMC,QAAkB,EAAE;AAE1B,OAAK,MAAM,CAAC,SAAS,WAAW,OAAO,QAAQ,KAAK,IAAI,EAAE;GACxD,MAAM,aAAa,WAAW,iBAAiB,QAAQ;GACvD,MAAM,QAAQ,KAAK,YAAY,IAAI,QAAQ;AAC3C,OAAI,CAAC,MAAO;GAEZ,MAAM,WAAW,OAAO,QAAQ,QAAQ,QAAQ,MAAM,IAAI;AAC1D,SAAM,KAAK,UAAU,WAAW,GAAG,WAAW;AAC9C,SAAM,KAAK,UAAU,WAAW,GAAG,OAAO,OAAO;AAEjD,OAAI,MAAM,SAAS,UACjB,KAAI,MAAM,OAAO,SAAS,EACxB,OAAM,KAAK,GAAG,WAAW,IAAI;OAE7B,MAAK,MAAM,CAAC,UAAU,UAAU,MAAM,OACpC,OAAM,KAAK,GAAG,aAAa,aAAa,SAAS,CAAC,GAAG,QAAQ;YAGxD,MAAM,SAAS,YACxB,MAAK,MAAM,CAAC,UAAU,iBAAiB,MAAM,QAAQ;IACnD,MAAM,cAAc,aAAa,SAAS;IAC1C,MAAM,UAAU,aAAa,OAAO,EAAE,GAAI,KAAK,MAAM,SAAS;AAG9D,SAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,QAAQ,KAAK;KAC7C,MAAM,QAAQ,KAAK,UAAU;MAAE,GAAG;MAAS,IAAI,OAAO,MAAM,QAAQ,GAAG;MAAE,CAAC;AAC1E,WAAM,KAAK,GAAG,WAAW,SAAS,aAAa,MAAM,CAAC,GAAG,aAAa,MAAM,IAAI;;IAGlF,MAAM,SAAS,KAAK,UAAU;KAAE,GAAG;KAAS,IAAI;KAAQ,CAAC;AACzD,UAAM,KACJ,GAAG,WAAW,SAAS,aAAa,OAAO,CAAC,GAAG,aAAa,MAAM,QAAQ,WAAW,IACtF;AACD,UAAM,KAAK,GAAG,WAAW,MAAM,YAAY,GAAG,MAAM,KAAK,IAAI,SAAS,IAAI,IAAI;AAC9E,UAAM,KAAK,GAAG,WAAW,QAAQ,YAAY,GAAG,MAAM,aAAa,IAAI,SAAS,IAAI,IAAI;;YAEjF,MAAM,SAAS,QACxB,KAAI,MAAM,OAAO,SAAS,EACxB,OAAM,KAAK,GAAG,WAAW,IAAI;OAE7B,MAAK,MAAM,CAAC,UAAU,QAAQ,MAAM,OAClC,OAAM,KAAK,GAAG,aAAa,aAAa,SAAS,CAAC,GAAG,MAAM;;AAMnE,SAAO,GAAG,MAAM,KAAK,KAAK,CAAC;;;;;;CAO7B,QAAc;AACZ,OAAK,YAAY,OAAO;AACxB,OAAK,cAAc;;;;;;;;;;;;CAarB,iBAGU;AACR,UAAQ,MAAM,QAAQ;AACpB,OAAI,IAAI,gBAAgB,2CAA2C;AACnE,OAAI,IAAI,KAAK,QAAQ,CAAC;;;;;;;;;;;;;;CAe1B,cAAmE;AACjE,UAAQ,KAAK,QAAQ;AAEnB,OAAI,EADkB,IAAI,QAAQ,cAAc,IAAI,QAAQ,cACxC;AAClB,QAAI,UAAU,IAAI,CAAC,IAAI,YAAY;AACnC;;GAEF,MAAM,OAAO,KAAK,QAAQ;AAC1B,OAAI,UAAU,KAAK;IACjB,gBAAgB;IAChB,kBAAkB,OAAO,WAAW,KAAK;IAC1C,CAAC;AACF,OAAI,IAAI,KAAK;;;CAMjB,AAAQ,eAAqB;AAC3B,OAAK,MAAM,CAAC,SAAS,WAAW,OAAO,QAAQ,KAAK,IAAI,CACtD,MAAK,gBAAgB,SAAS,OAAO;;CAIzC,AAAQ,gBAAgB,SAAiB,QAA4B;AACnE,MAAI,OAAO,SAAS,UAClB,MAAK,YAAY,IAAI,SAAS;GAAE,MAAM;GAAW,wBAAQ,IAAI,KAAK;GAAE,CAAC;WAC5D,OAAO,SAAS,aAAa;GACtC,MAAM,UAAU,CAAC,GAAI,OAAO,WAAW,gBAAiB,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE;AAC9E,QAAK,YAAY,IAAI,SAAS;IAC5B,MAAM;IACN;IACA,wBAAQ,IAAI,KAAK;IACjB,sBAAM,IAAI,KAAK;IACf,8BAAc,IAAI,KAAK;IACxB,CAAC;QAEF,MAAK,YAAY,IAAI,SAAS;GAAE,MAAM;GAAS,wBAAQ,IAAI,KAAK;GAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCzE,SAAgB,oBAAoB,KAAgC;AAClE,QAAO,IAAI,cAAc,IAAI;;;;;;;;ACvV/B,MAAM,iBAAiB;CACrB,SAAS;CACT,aAAa;CACb,SAAS;CACT,QAAQ;EACN,WAAW;EACX,UAAU;EACV,MAAM;EACP;CACD,QAAQ;CACR,cAAc;EACZ,OAAOC,wCAAS;EAChB,QAAQ;IACLA,wCAAS,QAAQ;IACjBA,wCAAS,OAAO;IAChBA,wCAAS,OAAO;IAChBA,wCAAS,QAAQ;IACjBA,wCAAS,QAAQ;IACjBA,wCAAS,UAAU;GACrB;EACD,QAAQ;IACLA,wCAAS,QAAQ;IACjBA,wCAAS,OAAO;IAChBA,wCAAS,OAAO;IAChBA,wCAAS,QAAQ;IACjBA,wCAAS,QAAQ;IACjBA,wCAAS,UAAU;GACrB;EACF;CACD,QAAQ;EACN,WAAW;EACX,OAAO;EACP,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;EACT,WAAW;EACZ;CACD,SAAS,CAAC,UAAU;CACrB;;;;;;AAOD,MAAa,eAAeC;;;;;;AAO5B,SAAgB,oBAAoB,QAAsD;AACxF,QAAO,IAAIC,oDAAqB;EAAE,GAAG;EAAgB,GAAG;EAAQ,CAAC;;;;;AAMnE,MAAa,SAAS,IAAIC,6CAAc,eAAe"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["formatted: Record<string, unknown>","serializeError","serialized: Record<string, unknown>","stripControlChars","safeToString","parts: string[]","LogLevel","DEFAULT_CYCLE: readonly LogLevelString[]","logger","parsed: { level?: unknown; namespaceLevels?: unknown }","coerced: NamespaceLevels","text: string","warnings: string[]","logger","AsyncLocalStorage","payload: WideEventFields","getCurrentTraceId","logger","state: WideEventState","stringifyForLog","DEFAULT_REQUEST_TIMEOUT_MS","BatchBuffer","DEFAULT_MAX_BUFFER_SIZE","attrs: Record<string, unknown>","record: Record<string, unknown>","HttpError","DEFAULT_BUCKETS: readonly number[]","pairs: Record<string, string>","lines: string[]","LogLevel","createLoggerFromCore","LogixiaLoggerService","LogixiaLogger"],"sources":["../src/formatters/json.formatter.ts","../src/formatters/text.formatter.ts","../src/utils/runtime-control.ts","../src/utils/typed-logger.ts","../src/wide-events.ts","../src/transports/otlp.transport.ts","../src/metrics.ts","../src/index.ts"],"sourcesContent":["/**\n * JSON formatter for Logixia\n */\n\nimport type { ILogFormatter, LogEntry } from '../types';\nimport { serializeError } from '../utils/error.utils';\n\n/**\n * Build a JSON.stringify replacer that replaces circular references with the\n * string '[Circular]' instead of throwing. A fresh replacer must be created per\n * stringify call because it holds per-serialization state (the seen set).\n */\nfunction createCircularReplacer(): (key: string, value: unknown) => unknown {\n const seen = new WeakSet<object>();\n return function (this: unknown, _key: string, value: unknown): unknown {\n if (typeof value === 'object' && value !== null) {\n if (seen.has(value)) return '[Circular]';\n seen.add(value);\n }\n return value;\n };\n}\n\nexport class JsonFormatter implements ILogFormatter {\n private includeTimestamp: boolean;\n private includeLevel: boolean;\n private includeAppName: boolean;\n private includeTraceId: boolean;\n private includeContext: boolean;\n private prettyPrint: boolean;\n\n constructor(\n options: {\n includeTimestamp?: boolean;\n includeLevel?: boolean;\n includeAppName?: boolean;\n includeTraceId?: boolean;\n includeContext?: boolean;\n prettyPrint?: boolean;\n } = {}\n ) {\n this.includeTimestamp = options.includeTimestamp ?? true;\n this.includeLevel = options.includeLevel ?? true;\n this.includeAppName = options.includeAppName ?? true;\n this.includeTraceId = options.includeTraceId ?? true;\n this.includeContext = options.includeContext ?? true;\n this.prettyPrint = options.prettyPrint ?? false;\n }\n\n format(entry: LogEntry): string {\n const formatted: Record<string, unknown> = {};\n\n // Add timestamp\n if (this.includeTimestamp) {\n formatted.timestamp = entry.timestamp;\n }\n\n // Add log level\n if (this.includeLevel) {\n formatted.level = entry.level.toLowerCase();\n formatted.levelValue = entry.level;\n }\n\n // Add app name\n if (this.includeAppName) {\n formatted.appName = entry.appName;\n }\n\n // Add trace ID\n if (this.includeTraceId && entry.traceId) {\n formatted.traceId = entry.traceId;\n }\n\n // Add context\n if (this.includeContext && entry.context) {\n formatted.context = entry.context;\n }\n\n // Add message\n formatted.message = entry.message;\n\n // Add payload\n if (entry.payload && Object.keys(entry.payload).length > 0) {\n formatted.payload = this.serializePayload(entry.payload);\n }\n\n // Add error if present\n if (entry.error) {\n formatted.error = serializeError(entry.error);\n }\n\n // Add metadata\n formatted.meta = {\n pid: process.pid,\n hostname: process.env.HOSTNAME || 'unknown',\n version: process.version,\n };\n\n // Use a circular-safe replacer: a payload containing a cycle (e.g. an Express\n // req/res, a DB connection, a Mongoose doc) would otherwise make JSON.stringify\n // throw and crash the entire log/transport path.\n const replacer = createCircularReplacer();\n return this.prettyPrint\n ? JSON.stringify(formatted, replacer, 2)\n : JSON.stringify(formatted, replacer);\n }\n\n private serializePayload(payload: Record<string, unknown>): Record<string, unknown> {\n const serialized: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(payload)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n try {\n if (value instanceof Error) {\n serialized[key] = serializeError(value);\n } else if (value instanceof Date) {\n serialized[key] = value.toISOString();\n } else if (typeof value === 'function') {\n serialized[key] = '[Function]';\n } else if (typeof value === 'symbol') {\n serialized[key] = value.toString();\n } else if (value === undefined) {\n serialized[key] = null;\n } else {\n serialized[key] = value;\n }\n } catch {\n serialized[key] = '[Unserializable]';\n }\n }\n\n return serialized;\n }\n}\n","/**\n * Text formatter for Logixia\n */\n\nimport type { ILogFormatter, LogEntry } from '../types';\nimport { LogLevel } from '../types';\nimport { safeToString } from '../utils/coerce.utils';\nimport { stripControlChars } from '../utils/sanitize.utils';\n\n// Coerce non-strings (objects passed via setContext, Errors as messages, etc.)\n// before .replace() to avoid TypeError on the hot path.\nconst stripControls = (value: unknown): string => stripControlChars(safeToString(value));\n\nexport class TextFormatter implements ILogFormatter {\n private colorize: boolean;\n private includeTimestamp: boolean;\n private includeAppName: boolean;\n private includeTraceId: boolean;\n private includeContext: boolean;\n private timestampFormat: 'iso' | 'locale' | 'short';\n private colors: Record<string, string>;\n\n constructor(\n options: {\n colorize?: boolean;\n includeTimestamp?: boolean;\n includeAppName?: boolean;\n includeTraceId?: boolean;\n includeContext?: boolean;\n timestampFormat?: 'iso' | 'locale' | 'short';\n colors?: Record<string, string>;\n } = {}\n ) {\n this.colorize = options.colorize ?? true;\n this.includeTimestamp = options.includeTimestamp ?? true;\n this.includeAppName = options.includeAppName ?? true;\n this.includeTraceId = options.includeTraceId ?? true;\n this.includeContext = options.includeContext ?? true;\n this.timestampFormat = options.timestampFormat ?? 'locale';\n this.colors = {\n error: '\\x1b[31m', // Red\n warn: '\\x1b[33m', // Yellow\n info: '\\x1b[32m', // Green\n debug: '\\x1b[34m', // Blue\n trace: '\\x1b[35m', // Magenta\n verbose: '\\x1b[36m', // Cyan\n reset: '\\x1b[0m', // Reset\n bold: '\\x1b[1m', // Bold\n dim: '\\x1b[2m', // Dim\n ...options.colors,\n };\n }\n\n format(entry: LogEntry): string {\n const parts: string[] = [];\n\n // Add timestamp\n if (this.includeTimestamp) {\n const timestamp = this.formatTimestamp(entry.timestamp);\n parts.push(this.colorize ? `${this.colors.dim}${timestamp}${this.colors.reset}` : timestamp);\n }\n\n // Add log level\n const levelName = entry.level.toLowerCase();\n const levelColor = this.colors[levelName] || this.colors.reset;\n const formattedLevel = this.colorize\n ? `${levelColor}${this.colors.bold}${levelName.toUpperCase().padEnd(5)}${this.colors.reset}`\n : levelName.toUpperCase().padEnd(5);\n parts.push(`[${formattedLevel}]`);\n\n // Add app name\n if (this.includeAppName) {\n const safeAppName = stripControls(entry.appName);\n const appName = this.colorize\n ? `${this.colors.bold}${safeAppName}${this.colors.reset}`\n : safeAppName;\n parts.push(`[${appName}]`);\n }\n\n // Add trace ID\n if (this.includeTraceId && entry.traceId) {\n const safeTraceId = stripControls(entry.traceId);\n const traceId = this.colorize\n ? `${this.colors.dim}${safeTraceId}${this.colors.reset}`\n : safeTraceId;\n parts.push(`[${traceId}]`);\n }\n\n // Add context\n if (this.includeContext && entry.context) {\n const safeContext = stripControls(entry.context);\n const context = this.colorize\n ? `${this.colors.cyan}${safeContext}${this.colors.reset}`\n : safeContext;\n parts.push(`[${context}]`);\n }\n\n // Add message\n const safeMessage = stripControls(entry.message);\n const message =\n this.colorize && entry.level === LogLevel.ERROR\n ? `${this.colors.error}${safeMessage}${this.colors.reset}`\n : safeMessage;\n parts.push(message);\n\n // Add payload\n if (entry.payload && Object.keys(entry.payload).length > 0) {\n const payload = this.formatPayload(entry.payload);\n if (payload) {\n parts.push(this.colorize ? `${this.colors.dim}${payload}${this.colors.reset}` : payload);\n }\n }\n\n return parts.join(' ');\n }\n\n private formatTimestamp(timestamp: string): string {\n const date = new Date(timestamp);\n\n switch (this.timestampFormat) {\n case 'iso':\n return date.toISOString();\n case 'short':\n return date.toLocaleTimeString();\n case 'locale':\n default:\n return date.toLocaleString();\n }\n }\n\n private formatPayload(payload: Record<string, unknown>): string {\n try {\n // Handle simple objects\n if (Object.keys(payload).length === 1) {\n const entry = Object.entries(payload)[0];\n if (entry) {\n const [key, value] = entry;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return `${key}=${value}`;\n }\n }\n }\n\n // Handle multiple properties or complex objects\n const formatted = Object.entries(payload)\n .map(([key, value]) => {\n if (value === null || value === undefined) {\n return `${key}=${value}`;\n }\n if (typeof value === 'string') {\n return `${key}=\"${value}\"`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return `${key}=${value}`;\n }\n if (value instanceof Date) {\n return `${key}=${value.toISOString()}`;\n }\n if (typeof value === 'object') {\n // safeToString is circular-safe (raw JSON.stringify throws on a cycle\n // — e.g. an Express req/res — which would crash the log call) and the\n // control-char strip keeps the CWE-117 guarantee for object values.\n return `${key}=${stripControls(safeToString(value))}`;\n }\n return `${key}=${String(value)}`;\n })\n .join(' ');\n\n return formatted;\n } catch {\n // Last-resort fallback must also be circular-safe — the previous\n // JSON.stringify(payload) here threw again on the same cyclic payload.\n return stripControls(safeToString(payload));\n }\n }\n\n /**\n * Create a formatter with preset configurations\n */\n static createSimple(): TextFormatter {\n return new TextFormatter({\n colorize: true,\n includeTimestamp: true,\n includeAppName: false,\n includeTraceId: false,\n includeContext: true,\n timestampFormat: 'short',\n });\n }\n\n static createDetailed(): TextFormatter {\n return new TextFormatter({\n colorize: true,\n includeTimestamp: true,\n includeAppName: true,\n includeTraceId: true,\n includeContext: true,\n timestampFormat: 'locale',\n });\n }\n\n static createMinimal(): TextFormatter {\n return new TextFormatter({\n colorize: false,\n includeTimestamp: false,\n includeAppName: false,\n includeTraceId: false,\n includeContext: false,\n });\n }\n}\n","/**\n * logixia — Dynamic runtime log-level reconfiguration.\n *\n * Change log levels in a running process WITHOUT a restart — the single\n * most-requested logging feature across the Winston and Pino issue trackers\n * (winston#1107, pino#206/#677, nestjs-pino#371). The ecosystem even built a\n * standalone module for it (pino-arborsculpture); logixia ships it first-class.\n *\n * Two trigger surfaces:\n * - {@link registerLevelSignal} — flip levels via an OS signal (default SIGUSR2),\n * cycling through a level list. Zero HTTP surface, safe for any deployment.\n * - {@link createLevelControlHandler} — a tiny HTTP handler so an ops dashboard\n * can GET the current level and POST a new global / per-namespace level.\n *\n * @example Signal-based (cycle levels on each `kill -USR2 <pid>`)\n * ```ts\n * import { registerLevelSignal } from 'logixia';\n * const dispose = registerLevelSignal(logger);\n * // later: process.kill(process.pid, 'SIGUSR2') → info → debug → trace → info …\n * ```\n *\n * @example HTTP admin endpoint\n * ```ts\n * import { createLevelControlHandler } from 'logixia';\n * const handler = createLevelControlHandler(logger);\n * app.all('/admin/log-level', handler); // GET reads, POST { level, namespaceLevels } sets\n * ```\n */\n\nimport type { LogLevelString, NamespaceLevels } from '../types';\nimport { internalLog, internalWarn } from './internal-log';\n\n/** Minimal logger surface the runtime controls need. */\nexport interface ReconfigurableLogger {\n getLevel(): LogLevelString;\n setLevel(level: LogLevelString): void;\n setNamespaceLevels?(levels: NamespaceLevels): void;\n getNamespaceLevels?(): NamespaceLevels;\n}\n\nconst DEFAULT_CYCLE: readonly LogLevelString[] = [\n 'error',\n 'warn',\n 'info',\n 'debug',\n 'trace',\n 'verbose',\n] as unknown as readonly LogLevelString[];\n\nexport interface LevelSignalOptions {\n /** Signal to listen on. Default: 'SIGUSR2' (SIGUSR1 is used by the Node debugger). */\n signal?: NodeJS.Signals;\n /** Ordered levels to cycle through on each signal. Default: error→…→verbose. */\n cycle?: LogLevelString[];\n}\n\n/**\n * Register an OS-signal handler that cycles the logger's global level on each\n * signal. Returns a dispose function that removes the listener.\n *\n * Cycling (rather than jumping straight to a fixed level) means a single,\n * memorizable command (`kill -USR2 <pid>`) is enough to ratchet verbosity up\n * while chasing a bug and back down again — no value to remember.\n */\nexport function registerLevelSignal(\n logger: ReconfigurableLogger,\n options: LevelSignalOptions = {}\n): () => void {\n const signal = options.signal ?? 'SIGUSR2';\n const cycle = options.cycle && options.cycle.length > 0 ? options.cycle : [...DEFAULT_CYCLE];\n\n const handler = (): void => {\n const current = logger.getLevel();\n const idx = cycle.indexOf(current);\n const next = cycle[(idx + 1) % cycle.length]!;\n logger.setLevel(next);\n internalLog(`runtime level changed via ${signal}: ${current} → ${next}`);\n };\n\n process.on(signal, handler);\n return () => {\n process.removeListener(signal, handler);\n };\n}\n\n// ── HTTP admin handler ────────────────────────────────────────────────────────\n\ninterface MinimalReq {\n method?: string | undefined;\n headers?: Record<string, string | string[] | undefined> | undefined;\n on?: (event: string, cb: (chunk?: unknown) => void) => void;\n}\ninterface MinimalRes {\n statusCode?: number;\n setHeader?: (k: string, v: string) => void;\n end: (body?: string) => void;\n}\n\n/** Maximum accepted request body size for the level control handler. */\nconst MAX_BODY_BYTES = 16 * 1024;\n\nfunction isJsonContentType(req: MinimalReq): boolean {\n const raw = req.headers?.['content-type'];\n const value = Array.isArray(raw) ? raw[0] : raw;\n if (typeof value !== 'string') return false;\n return value.split(';')[0]!.trim().toLowerCase() === 'application/json';\n}\n\nconst VALID_LEVELS = new Set<string>([\n 'error',\n 'warn',\n 'info',\n 'debug',\n 'trace',\n 'verbose',\n 'fatal',\n]);\n\n/**\n * Create an HTTP handler (Node `http`/Express-compatible) that reads and sets\n * the logger's level at runtime.\n *\n * - `GET` → `{ level, namespaceLevels }`\n * - `POST` → body `{ level?, namespaceLevels? }` applies them, returns the new state\n *\n * Custom levels are accepted too: any level the logger already knows passes\n * through. Unknown levels are rejected with 400 so a typo can't silently mute\n * logging.\n *\n * **SECURITY: this handler has NO authentication or authorization.** Anyone who\n * can reach it can silence logging or turn on verbose output. Always mount it\n * behind your own auth (and on an internal-only route/port), never on a public\n * listener.\n *\n * Mutating requests must send `Content-Type: application/json` (otherwise 415,\n * which also keeps cross-site form posts out) and a body of at most 16 KB\n * (otherwise 413).\n */\nexport function createLevelControlHandler(\n logger: ReconfigurableLogger,\n options: { allowedLevels?: string[] } = {}\n): (req: MinimalReq, res: MinimalRes) => void {\n const allowed =\n options.allowedLevels && options.allowedLevels.length > 0\n ? new Set(options.allowedLevels.map((l) => l.toLowerCase()))\n : VALID_LEVELS;\n\n const snapshot = (): { level: LogLevelString; namespaceLevels: NamespaceLevels } => ({\n level: logger.getLevel(),\n namespaceLevels: logger.getNamespaceLevels?.() ?? {},\n });\n\n const send = (res: MinimalRes, status: number, body: unknown): void => {\n res.statusCode = status;\n res.setHeader?.('Content-Type', 'application/json');\n res.end(JSON.stringify(body));\n };\n\n const applyBody = (res: MinimalRes, raw: string): void => {\n let parsed: { level?: unknown; namespaceLevels?: unknown };\n try {\n parsed = raw ? (JSON.parse(raw) as typeof parsed) : {};\n } catch {\n send(res, 400, { error: 'invalid JSON body' });\n return;\n }\n\n if (parsed.level !== undefined) {\n const lvl = String(parsed.level).toLowerCase();\n if (!allowed.has(lvl)) {\n send(res, 400, { error: `unknown level \"${parsed.level}\"`, allowed: [...allowed] });\n return;\n }\n logger.setLevel(lvl as LogLevelString);\n }\n\n if (parsed.namespaceLevels !== undefined) {\n if (\n typeof parsed.namespaceLevels !== 'object' ||\n parsed.namespaceLevels === null ||\n Array.isArray(parsed.namespaceLevels)\n ) {\n send(res, 400, { error: 'namespaceLevels must be an object' });\n return;\n }\n const nl = parsed.namespaceLevels as Record<string, unknown>;\n for (const [pat, lvl] of Object.entries(nl)) {\n if (!allowed.has(String(lvl).toLowerCase())) {\n send(res, 400, { error: `unknown level \"${String(lvl)}\" for namespace \"${pat}\"` });\n return;\n }\n }\n if (logger.setNamespaceLevels) {\n const coerced: NamespaceLevels = Object.fromEntries(\n Object.entries(nl).map(([pat, lvl]) => [pat, String(lvl).toLowerCase() as LogLevelString])\n );\n logger.setNamespaceLevels(coerced);\n } else {\n internalWarn('level control: logger does not support setNamespaceLevels — ignored');\n }\n }\n\n send(res, 200, snapshot());\n };\n\n return function levelControlHandler(req: MinimalReq, res: MinimalRes): void {\n const method = (req.method ?? 'GET').toUpperCase();\n\n if (method === 'GET') {\n send(res, 200, snapshot());\n return;\n }\n\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n if (!isJsonContentType(req)) {\n send(res, 415, { error: 'Content-Type must be application/json' });\n return;\n }\n const declared = Number(req.headers?.['content-length']);\n if (Number.isFinite(declared) && declared > MAX_BODY_BYTES) {\n send(res, 413, { error: 'request body too large' });\n return;\n }\n\n // Express already-parsed body (req.body) vs raw stream.\n const maybeBody = (req as unknown as { body?: unknown }).body;\n if (maybeBody !== undefined) {\n let text: string;\n if (typeof maybeBody === 'string') text = maybeBody;\n else if (Buffer.isBuffer(maybeBody)) text = maybeBody.toString('utf8');\n else text = JSON.stringify(maybeBody);\n if (Buffer.byteLength(text) > MAX_BODY_BYTES) {\n send(res, 413, { error: 'request body too large' });\n return;\n }\n applyBody(res, text);\n return;\n }\n if (typeof req.on === 'function') {\n let raw = '';\n let size = 0;\n let rejected = false;\n req.on('data', (chunk?: unknown) => {\n if (rejected) return;\n const piece = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk ?? '');\n size += Buffer.byteLength(piece);\n if (size > MAX_BODY_BYTES) {\n rejected = true;\n raw = '';\n send(res, 413, { error: 'request body too large' });\n return;\n }\n raw += piece;\n });\n req.on('end', () => {\n if (!rejected) applyBody(res, raw);\n });\n return;\n }\n applyBody(res, '');\n return;\n }\n\n send(res, 405, { error: `method ${method} not allowed` });\n };\n}\n","/**\n * logixia — TypeScript typed log fields\n *\n * Solves the type-safety gap: standard `Record<string, unknown>` metadata gives\n * zero IDE autocomplete and lets typos slip silently into production logs.\n *\n * Two complementary utilities:\n *\n * 1. `createTypedLogger<TFields>()` — wraps any IBaseLogger so that the second\n * argument of every log method is constrained to `TFields` (a typed object).\n * Compile-time safety, zero runtime overhead.\n *\n * 2. `defineLogSchema<TFields>(schema)` — declares expected fields with optional\n * validators. In development (NODE_ENV !== 'production') every log call is\n * validated against the schema and a warning is emitted for missing required\n * fields or type mismatches.\n *\n * @example\n * ```ts\n * import { createTypedLogger, defineLogSchema } from 'logixia';\n *\n * interface OrderFields {\n * orderId: string;\n * userId: string;\n * amount?: number;\n * currency?: string;\n * }\n *\n * const schema = defineLogSchema<OrderFields>({\n * orderId: { type: 'string', required: true },\n * userId: { type: 'string', required: true },\n * amount: { type: 'number' },\n * currency: { type: 'string' },\n * });\n *\n * const orderLogger = createTypedLogger<OrderFields>(baseLogger, schema);\n * await orderLogger.info('Order created', { orderId: 'ord_123', userId: 'usr_456', amount: 99.99 });\n * // ^^^^ fully typed autocomplete ^^^^\n * ```\n */\n\nimport { internalWarn } from './internal-log';\n\n// ── Schema types ─────────────────────────────────────────────────────────────\n\nexport type LogFieldType = 'string' | 'number' | 'boolean' | 'object' | 'array';\n\nexport interface LogFieldDef {\n type: LogFieldType;\n /** Emit a warning when this field is missing from the log call. Default: false */\n required?: boolean;\n /** Custom validator — return a string to emit it as a warning message. */\n validate?: (value: unknown) => string | undefined;\n}\n\nexport type LogSchema<TFields extends Record<string, unknown>> = {\n [K in keyof TFields]: LogFieldDef;\n};\n\nexport interface CompiledSchema<TFields extends Record<string, unknown>> {\n readonly fields: LogSchema<TFields>;\n /**\n * Validate a payload against the schema.\n * Returns an array of warning strings (empty = pass).\n * Only runs in non-production environments.\n */\n validate(payload: Partial<TFields>): string[];\n}\n\n// ── defineLogSchema ──────────────────────────────────────────────────────────\n\n/**\n * Define a typed schema for a category of log entries.\n *\n * Call this once at module initialisation and pass it to `createTypedLogger`.\n * In development, every log call is validated against the schema.\n */\nexport function defineLogSchema<TFields extends Record<string, unknown>>(\n fields: LogSchema<TFields>\n): CompiledSchema<TFields> {\n return {\n fields,\n validate(payload: Partial<TFields>): string[] {\n if (process.env['NODE_ENV'] === 'production') return [];\n\n const warnings: string[] = [];\n\n for (const [key, def] of Object.entries(fields) as [keyof TFields & string, LogFieldDef][]) {\n const value = payload[key];\n\n if (def.required && (value === undefined || value === null)) {\n warnings.push(`Required field \"${key}\" is missing`);\n continue;\n }\n\n if (value !== undefined && value !== null) {\n const actualType = Array.isArray(value) ? 'array' : typeof value;\n if (actualType !== def.type) {\n warnings.push(`Field \"${key}\" expected type \"${def.type}\" but got \"${actualType}\"`);\n }\n\n if (def.validate) {\n const msg = def.validate(value);\n if (msg) warnings.push(`Field \"${key}\": ${msg}`);\n }\n }\n }\n\n return warnings;\n },\n };\n}\n\n// ── TypedLogger ──────────────────────────────────────────────────────────────\n\n/** IBaseLogger-compatible subset used by TypedLogger */\nexport interface LoggerLike {\n error(message: string | 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 verbose?(message: string, data?: Record<string, unknown>): Promise<void>;\n trace?(message: string, data?: Record<string, unknown>): Promise<void>;\n}\n\n/**\n * A logger whose metadata is typed to `TFields`.\n *\n * The `error` overload still accepts a plain `Error` object as the first arg\n * (with optional `TFields` data) so the typed logger remains a drop-in replacement.\n */\nexport interface TypedLogger<TFields extends Record<string, unknown>> {\n error(error: Error, data?: Partial<TFields>): Promise<void>;\n error(message: string, data?: Partial<TFields>): Promise<void>;\n warn(message: string, data?: Partial<TFields>): Promise<void>;\n info(message: string, data?: Partial<TFields>): Promise<void>;\n debug(message: string, data?: Partial<TFields>): Promise<void>;\n verbose(message: string, data?: Partial<TFields>): Promise<void>;\n trace(message: string, data?: Partial<TFields>): Promise<void>;\n /** Access the underlying untyped logger if needed. */\n readonly raw: LoggerLike;\n}\n\n/**\n * Wrap any logixia logger with a type-safe field interface.\n *\n * @param logger Any object that implements `IBaseLogger` (e.g. the result of `createLogger()`)\n * @param schema Optional schema for dev-time validation. Created with `defineLogSchema()`.\n */\nexport function createTypedLogger<TFields extends Record<string, unknown>>(\n logger: LoggerLike,\n schema?: CompiledSchema<TFields>\n): TypedLogger<TFields> {\n function withValidation(\n level: string,\n fn: (msg: string, data?: Record<string, unknown>) => Promise<void>,\n message: string,\n data?: Partial<TFields>\n ): Promise<void> {\n if (schema && data) {\n const warnings = schema.validate(data);\n for (const w of warnings) {\n internalWarn(`[logixia/schema] ${w} — level=${level} message=\"${message}\"`);\n }\n }\n return fn(message, data as Record<string, unknown> | undefined);\n }\n\n return {\n raw: logger,\n error(messageOrError: string | Error, data?: Partial<TFields>): Promise<void> {\n if (schema && data) {\n const warnings = schema.validate(data);\n const display = messageOrError instanceof Error ? messageOrError.message : messageOrError;\n for (const w of warnings) {\n internalWarn(`[logixia/schema] ${w} — level=error message=\"${display}\"`);\n }\n }\n return logger.error(messageOrError, data as Record<string, unknown> | undefined);\n },\n warn: (m, d) => withValidation('warn', logger.warn.bind(logger), m, d),\n info: (m, d) => withValidation('info', logger.info.bind(logger), m, d),\n debug: (m, d) => withValidation('debug', logger.debug.bind(logger), m, d),\n verbose: (m, d) =>\n withValidation('verbose', (logger.verbose ?? logger.debug).bind(logger), m, d),\n trace: (m, d) => withValidation('trace', (logger.trace ?? logger.debug).bind(logger), m, d),\n };\n}\n","/**\n * logixia — Canonical Log Lines / Wide Events.\n *\n * Emit ONE dense, structured event per unit of work (usually a request) instead\n * of scattering details across many narrow log lines. Fields are accumulated as\n * the request flows through middleware and business logic, then the whole event\n * is emitted ONCE — in a `finally`/teardown path so it fires even on errors.\n *\n * This is the \"canonical log line\" pattern (Stripe) / \"wide events\" /\n * \"Observability 2.0\" (Honeycomb): one pre-joined, queryable record per request,\n * so operators never JOIN across log lines during an incident. It composes with\n * logixia's existing trace correlation — when a trace is active, the event\n * carries `traceId`/`spanId`, making it OTel-friendly.\n *\n * @example Manual scope\n * ```ts\n * import { withWideEvent, addEventFields } from 'logixia';\n *\n * await withWideEvent(logger, { route: '/checkout' }, async () => {\n * addEventFields({ userId, planTier }); // from anywhere in the call tree\n * addEventFields({ dbQueries: 4, cacheHit });\n * // ...one wide event is emitted automatically when this callback settles,\n * // even if it throws.\n * });\n * ```\n *\n * @example Express middleware (auto-emit on response finish/close)\n * ```ts\n * import { wideEventMiddleware } from 'logixia';\n * app.use(wideEventMiddleware(logger));\n * app.get('/x', (req, res) => { addEventFields({ handled: 'x' }); res.json({}); });\n * ```\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport { getCurrentTraceId } from './utils/trace.utils';\n\n/** The accumulating event fields for the current scope. */\nexport type WideEventFields = Record<string, unknown>;\n\ninterface WideEventState {\n fields: WideEventFields;\n startMs: number;\n emitted: boolean;\n}\n\nconst _storage = new AsyncLocalStorage<WideEventState>();\n\n/** Minimal logger surface a wide event needs to emit itself. */\nexport interface WideEventLogger {\n logLevel(level: string, message: string, data?: Record<string, unknown>): Promise<void> | void;\n}\n\nexport interface WideEventOptions {\n /** Level the canonical event is logged at. Default: 'info'. */\n level?: string;\n /** Message for the canonical event line. Default: 'request'. */\n message?: string;\n /**\n * Attach `traceId` (and `spanId` when present) from the active trace context.\n * Default: true.\n */\n includeTrace?: boolean;\n /** Field name for the auto-computed duration in ms. Default: 'durationMs'. */\n durationField?: string;\n}\n\n/**\n * Merge fields into the wide event for the current async scope. No-op (with no\n * throw) when called outside a `withWideEvent` / middleware scope, so business\n * code can call it unconditionally.\n */\nexport function addEventFields(fields: WideEventFields): void {\n const state = _storage.getStore();\n if (!state || state.emitted) return;\n Object.assign(state.fields, fields);\n}\n\n/** Set a single field on the current wide event. */\nexport function setEventField(key: string, value: unknown): void {\n addEventFields({ [key]: value });\n}\n\n/** Read a shallow copy of the wide event accumulated so far, or undefined. */\nexport function getEventFields(): WideEventFields | undefined {\n const state = _storage.getStore();\n return state ? { ...state.fields } : undefined;\n}\n\nfunction emit(\n logger: WideEventLogger,\n state: WideEventState,\n options: WideEventOptions,\n extra?: WideEventFields\n): void {\n if (state.emitted) return;\n state.emitted = true;\n\n const level = options.level ?? 'info';\n const message = options.message ?? 'request';\n const durationField = options.durationField ?? 'durationMs';\n const includeTrace = options.includeTrace ?? true;\n\n const payload: WideEventFields = { ...state.fields };\n if (extra) Object.assign(payload, extra);\n payload[durationField] = Date.now() - state.startMs;\n\n if (includeTrace) {\n const traceId = getCurrentTraceId();\n if (traceId !== undefined && payload['traceId'] === undefined) payload['traceId'] = traceId;\n }\n\n // logLevel may be async (transport-backed); fire-and-forget so the wide-event\n // emit never blocks request teardown. Swallow rejections (the logger surfaces\n // its own transport errors) so this never becomes an unhandled rejection.\n const p = logger.logLevel(level, message, payload);\n if (p && typeof (p as Promise<void>).catch === 'function') {\n (p as Promise<void>).catch(() => {});\n }\n}\n\n/**\n * Run `callback` inside a wide-event scope. `addEventFields` calls anywhere in\n * the (async) call tree accumulate onto one event, which is emitted exactly once\n * when the callback settles — on success OR error (the canonical \"emit in\n * finally\" guarantee). On error, `error` + `errorMessage` fields are added.\n */\nexport async function withWideEvent<T>(\n logger: WideEventLogger,\n initialFields: WideEventFields,\n callback: () => Promise<T> | T,\n options: WideEventOptions = {}\n): Promise<T> {\n const state: WideEventState = {\n fields: { ...initialFields },\n startMs: Date.now(),\n emitted: false,\n };\n\n return _storage.run(state, async () => {\n try {\n const result = await callback();\n emit(logger, state, options);\n return result;\n } catch (error) {\n emit(logger, state, options, {\n error: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n });\n}\n\n// ── HTTP middleware ─────────────────────────────────────────────────────────\n\ninterface MwReq {\n method?: string | undefined;\n url?: string | undefined;\n originalUrl?: string | undefined;\n ip?: string | undefined;\n headers?: Record<string, unknown> | undefined;\n socket?: { remoteAddress?: string } | undefined;\n}\ninterface MwRes {\n statusCode?: number;\n once?: (event: string, cb: () => void) => void;\n}\n\nexport interface WideEventMiddlewareOptions extends WideEventOptions {\n /** Derive extra initial fields from the request. */\n enrich?: (req: MwReq) => WideEventFields;\n /** Skip wide-event emission for a request (e.g. health checks). */\n skip?: (req: MwReq) => boolean;\n}\n\n/**\n * Express/Connect middleware that opens a wide-event scope per request and\n * emits ONE canonical event on response `finish`/`close` — even if the handler\n * throws or the client disconnects. Pre-populates method/url/ip; handlers add\n * more via `addEventFields`. The completion event includes `statusCode` and the\n * request duration.\n */\nexport function wideEventMiddleware(\n logger: WideEventLogger,\n options: WideEventMiddlewareOptions = {}\n): (req: MwReq, res: MwRes, next: () => void) => void {\n return function logixiaWideEventMiddleware(req: MwReq, res: MwRes, next: () => void): void {\n if (options.skip?.(req)) {\n next();\n return;\n }\n\n const base: WideEventFields = {\n method: req.method,\n url: req.originalUrl ?? req.url,\n ip: req.ip ?? req.socket?.remoteAddress,\n ...(options.enrich ? options.enrich(req) : {}),\n };\n\n const state: WideEventState = { fields: base, startMs: Date.now(), emitted: false };\n\n const finalize = (): void => {\n emit(logger, state, options, { statusCode: res.statusCode ?? 0 });\n };\n\n // Both 'finish' and 'close' may fire; emit() is idempotent (emitted guard),\n // so the canonical line is logged exactly once.\n res.once?.('finish', finalize);\n res.once?.('close', finalize);\n\n _storage.run(state, () => next());\n };\n}\n","/**\n * OTLP Logs transport — emit logixia logs as OpenTelemetry LogRecords.\n *\n * logixia already READS the active OTel span (the bridge injects traceId/spanId\n * into payloads). This transport closes the loop by EMITTING logs OUT in the\n * OTLP/HTTP JSON format to a collector, making logixia an OTel-Logs-native\n * source — logs land in any OTLP backend (Grafana Loki, OpenObserve, Better\n * Stack, Axiom, Datadog, SigNoz…) already correlated with traces.\n *\n * Dependency-free: builds the OTLP/HTTP JSON payload directly (no\n * @opentelemetry/* packages required — that JS API is still alpha), conforming\n * to the OTel logs data model: SeverityNumber (1–24), resource attributes\n * (service.name/version, deployment.environment), and TraceId/SpanId on each\n * record for correlation.\n *\n * @example\n * ```ts\n * transports: {\n * custom: [ new OtlpLogTransport({\n * url: 'http://localhost:4318/v1/logs',\n * serviceName: 'api',\n * headers: { 'x-api-key': process.env.OTLP_KEY! },\n * }) ],\n * }\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n HttpError,\n} from './batch-buffer';\n\nexport interface OtlpLogTransportConfig {\n /** OTLP/HTTP logs endpoint, e.g. `http://localhost:4318/v1/logs`. */\n url: string;\n /** Extra HTTP headers (auth, tenant, etc.). */\n headers?: Record<string, string>;\n /** `service.name` resource attribute. Default: 'logixia'. */\n serviceName?: string;\n /** `service.version` resource attribute. */\n serviceVersion?: string;\n /** `deployment.environment` resource attribute. */\n environment?: string;\n /** Extra resource attributes merged into the OTLP resource. */\n resourceAttributes?: Record<string, string | number | boolean>;\n /** Entries per batch / per POST. Default: 100. */\n batchSize?: number;\n /** Auto-flush interval (ms). Default: 5000. */\n flushIntervalMs?: number;\n /** Abort a request that has not finished after this many ms. Default: 10000. */\n requestTimeoutMs?: number;\n /** Max buffered entries; the oldest are dropped past this. Default: 10000. */\n maxBufferSize?: number;\n level?: string;\n}\n\nconst HEX_32 = /^[0-9a-f]{32}$/;\n\n/**\n * OTLP JSON wants a 32-char lowercase hex trace id. Dashed UUIDs are\n * normalised; anything else (or all zeros) returns undefined.\n */\nexport function toOtlpTraceId(id: string): string | undefined {\n const hex = id.replace(/-/g, '').toLowerCase();\n return HEX_32.test(hex) && hex !== '0'.repeat(32) ? hex : undefined;\n}\n\n/**\n * Map a logixia level name to an OTel SeverityNumber (1–24) and text.\n * Per the OTel logs SDK spec: TRACE=1, DEBUG=5, INFO=9, WARN=13, ERROR=17,\n * FATAL=21. Custom/unknown levels fall back to INFO (9).\n */\nexport function toOtelSeverity(level: string): { number: number; text: string } {\n switch (level.toLowerCase()) {\n case 'trace':\n return { number: 1, text: 'TRACE' };\n case 'verbose':\n return { number: 5, text: 'DEBUG' }; // verbose maps to DEBUG range\n case 'debug':\n return { number: 5, text: 'DEBUG' };\n case 'info':\n return { number: 9, text: 'INFO' };\n case 'warn':\n case 'warning':\n return { number: 13, text: 'WARN' };\n case 'error':\n return { number: 17, text: 'ERROR' };\n case 'fatal':\n return { number: 21, text: 'FATAL' };\n default:\n return { number: 9, text: 'INFO' };\n }\n}\n\n/** Coerce a JS value into an OTLP AnyValue. */\nfunction toAnyValue(value: unknown): Record<string, unknown> {\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'boolean') return { boolValue: value };\n if (typeof value === 'number') {\n return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };\n }\n if (typeof value === 'bigint') return { stringValue: value.toString() };\n if (value === null || value === undefined) return { stringValue: '' };\n // Objects/arrays → JSON string (kvlistValue would be richer but stringValue is\n // universally accepted and avoids deep recursion / circular issues here).\n try {\n return { stringValue: stringifyForLog(value) };\n } catch {\n return { stringValue: String(value) };\n }\n}\n\n/** Build the OTLP KeyValue attribute list from a flat record. */\nfunction toAttributes(rec: Record<string, unknown>): Array<{ key: string; value: unknown }> {\n return Object.entries(rec).map(([key, value]) => ({ key, value: toAnyValue(value) }));\n}\n\nexport class OtlpLogTransport implements IAsyncTransport {\n public readonly name = 'otlp';\n public readonly level: string | undefined;\n\n private readonly url: string;\n private readonly headers: Record<string, string>;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n private readonly requestTimeoutMs: number;\n private readonly resourceAttrs: Array<{ key: string; value: unknown }>;\n\n private readonly buffer: BatchBuffer<TransportLogEntry>;\n private flushTimer: NodeJS.Timeout | null = null;\n\n constructor(config: OtlpLogTransportConfig) {\n this.url = config.url;\n this.headers = config.headers ?? {};\n this.batchSize = config.batchSize ?? 100;\n this.flushIntervalMs = config.flushIntervalMs ?? 5000;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.level = config.level;\n this.buffer = new BatchBuffer<TransportLogEntry>({\n name: 'OtlpLogTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.send(entries),\n });\n\n const resource: Record<string, unknown> = {\n 'service.name': config.serviceName ?? 'logixia',\n ...(config.serviceVersion ? { 'service.version': config.serviceVersion } : {}),\n ...(config.environment ? { 'deployment.environment': config.environment } : {}),\n ...(config.resourceAttributes ?? {}),\n };\n this.resourceAttrs = toAttributes(resource);\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushIntervalMs);\n if (this.flushTimer.unref) this.flushTimer.unref();\n }\n\n write(entry: TransportLogEntry): void {\n this.buffer.push(entry);\n this.buffer.flushIfFull();\n }\n\n /** Convert one entry into an OTLP LogRecord. */\n private toLogRecord(entry: TransportLogEntry): Record<string, unknown> {\n const sev = toOtelSeverity(entry.level);\n const tsNanos = String(entry.timestamp.getTime() * 1_000_000);\n\n const attrs: Record<string, unknown> = { ...(entry.data ?? {}) };\n if (entry.context !== undefined) attrs['context'] = entry.context;\n if (entry.appName !== undefined) attrs['app.name'] = entry.appName;\n if (entry.environment !== undefined) attrs['deployment.environment'] = entry.environment;\n\n const record: Record<string, unknown> = {\n timeUnixNano: tsNanos,\n observedTimeUnixNano: tsNanos,\n severityNumber: sev.number,\n severityText: sev.text,\n body: { stringValue: entry.message },\n attributes: toAttributes(attrs),\n };\n\n if (entry.traceId) {\n const traceId = toOtlpTraceId(entry.traceId);\n if (traceId) record['traceId'] = traceId;\n else\n (record['attributes'] as unknown[]).push({\n key: 'trace_id',\n value: { stringValue: entry.traceId },\n });\n }\n\n return record;\n }\n\n private buildPayload(entries: TransportLogEntry[]): string {\n return stringifyForLog({\n resourceLogs: [\n {\n resource: { attributes: this.resourceAttrs },\n scopeLogs: [\n {\n scope: { name: 'logixia' },\n logRecords: entries.map((e) => this.toLogRecord(e)),\n },\n ],\n },\n ],\n });\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n private async send(entries: TransportLogEntry[]): Promise<void> {\n if (typeof fetch !== 'function') {\n internalWarn('OtlpLogTransport: global fetch unavailable — cannot export logs');\n return;\n }\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);\n try {\n const res = await fetch(this.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...this.headers },\n body: this.buildPayload(entries),\n signal: controller.signal,\n });\n if (!res.ok) {\n throw new HttpError(res.status, '', 'OTLP export');\n }\n } catch (err) {\n if (controller.signal.aborted) {\n throw new Error(`OTLP export timed out after ${this.requestTimeoutMs}ms`, { cause: err });\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.buffer.close();\n }\n}\n","/**\n * logixia — Metrics extraction → Prometheus\n *\n * Automatically extracts metrics from structured log fields and exposes them\n * in the Prometheus text exposition format (version 0.0.4).\n * No external dependency — the format is fully self-contained.\n *\n * Supported metric types:\n * - `counter` — incremented on each matching log entry\n * - `histogram` — observes a numeric payload field per entry\n * - `gauge` — tracks the most recent numeric value of a payload field\n *\n * Usage:\n * ```ts\n * import { createMetricsPlugin } from 'logixia';\n *\n * const metrics = createMetricsPlugin({\n * http_request_duration: {\n * type: 'histogram',\n * field: 'duration',\n * labels: ['method', 'statusCode'],\n * help: 'HTTP request duration in milliseconds',\n * },\n * error_count: {\n * type: 'counter',\n * levelFilter: 'error',\n * labels: ['context'],\n * help: 'Total error log entries',\n * },\n * active_connections: {\n * type: 'gauge',\n * field: 'connections',\n * help: 'Current active connection count',\n * },\n * });\n *\n * logger.use(metrics); // start collecting\n * app.get('/metrics', metrics.expressHandler()); // expose for Prometheus scrape\n * ```\n *\n * All metric names are automatically prefixed with `logixia_`.\n * Output follows https://prometheus.io/docs/instrumenting/exposition_formats/\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport type { LogixiaPlugin } from './plugin';\nimport type { LogEntry } from './types/index';\n\n// ── Metric config types ───────────────────────────────────────────────────────\n\n/**\n * Increment a counter on each matching log entry.\n *\n * @example Count every error entry, labelled by context:\n * ```ts\n * error_count: { type: 'counter', levelFilter: 'error', labels: ['context'] }\n * ```\n *\n * @example Count entries where payload.event === 'checkout':\n * ```ts\n * checkout_events: { type: 'counter', field: 'event', value: 'checkout' }\n * ```\n */\nexport interface CounterConfig {\n type: 'counter';\n /** Only increment when `entry.level` equals this value. Omit to count all entries. */\n levelFilter?: string;\n /**\n * Only increment when `entry.payload[field] === value`.\n * If omitted, every entry (matching `levelFilter`) is counted.\n */\n field?: string;\n value?: unknown;\n /** `entry.payload` fields used as Prometheus label dimensions. `'level'` is also valid. */\n labels?: string[];\n help?: string;\n}\n\n/**\n * Observe a numeric payload field and bucket it into a Prometheus histogram.\n *\n * @example Duration histogram labelled by HTTP method and status code:\n * ```ts\n * http_request_duration: {\n * type: 'histogram',\n * field: 'duration',\n * labels: ['method', 'statusCode'],\n * buckets: [10, 25, 50, 100, 250, 500, 1000],\n * }\n * ```\n */\nexport interface HistogramConfig {\n type: 'histogram';\n /** The `entry.payload` field containing the numeric value to observe. */\n field: string;\n /** `entry.payload` fields used as Prometheus label dimensions. */\n labels?: string[];\n help?: string;\n /**\n * Bucket upper bounds (inclusive). Sorted automatically.\n * Default: [1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000]\n */\n buckets?: number[];\n}\n\n/**\n * Track the most recent numeric value of a payload field as a gauge.\n *\n * @example Track live connection count:\n * ```ts\n * active_connections: { type: 'gauge', field: 'connections' }\n * ```\n */\nexport interface GaugeConfig {\n type: 'gauge';\n /** The `entry.payload` field containing the numeric value. */\n field: string;\n /** `entry.payload` fields used as Prometheus label dimensions. */\n labels?: string[];\n help?: string;\n}\n\nexport type MetricConfig = CounterConfig | HistogramConfig | GaugeConfig;\nexport type MetricsMap = Record<string, MetricConfig>;\n\n// ── Internal metric state ─────────────────────────────────────────────────────\n\ninterface CounterState {\n type: 'counter';\n values: Map<string, number>;\n}\n\ninterface HistogramState {\n type: 'histogram';\n buckets: number[];\n /** Parallel arrays; index = bucket index; last entry = +Inf cumulative count */\n counts: Map<string, number[]>;\n sums: Map<string, number>;\n observations: Map<string, number>;\n}\n\ninterface GaugeState {\n type: 'gauge';\n values: Map<string, number>;\n}\n\ntype MetricState = CounterState | HistogramState | GaugeState;\n\n// ── Defaults ──────────────────────────────────────────────────────────────────\n\nconst DEFAULT_BUCKETS: readonly number[] = [\n 1, 5, 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000,\n];\n\nconst MAX_LABEL_VALUE_LENGTH = 256;\nconst DEFAULT_MAX_LABEL_SETS = 1000;\n/** Label value used for the series that absorbs label sets beyond the cardinality cap. */\nexport const OVERFLOW_LABEL_VALUE = '__overflow__';\n\nexport interface MetricsPluginOptions {\n /**\n * Maximum number of distinct label sets tracked per metric. Once reached, new\n * label sets are folded into a single series whose label values are all\n * `__overflow__`, so payload-driven labels cannot grow memory or the scrape\n * output without bound.\n * @default 1000\n */\n maxLabelSets?: number;\n}\n\n// ── Label helpers ─────────────────────────────────────────────────────────────\n\nfunction buildLabelKey(config: MetricConfig, entry: LogEntry): string {\n const labelNames = config.labels ?? [];\n if (labelNames.length === 0) return '{}';\n const pairs: Record<string, string> = {};\n const payload = entry.payload ?? {};\n for (const name of labelNames) {\n const raw = name === 'level' ? entry.level : payload[name];\n // Sanitize the label NAME (the value is escaped at render time) so a label\n // like 'status code' can't emit unparseable Prometheus output.\n pairs[sanitizePromName(name)] =\n raw !== undefined && raw !== null ? String(raw).slice(0, MAX_LABEL_VALUE_LENGTH) : '';\n }\n return JSON.stringify(pairs);\n}\n\n/** Label key of the shared overflow series for a metric (all label values `__overflow__`). */\nfunction buildOverflowKey(config: MetricConfig): string {\n const names = config.labels ?? [];\n if (names.length === 0) return '{}';\n return JSON.stringify(\n Object.fromEntries(names.map((n) => [sanitizePromName(n), OVERFLOW_LABEL_VALUE]))\n );\n}\n\nfunction renderLabels(labelKey: string): string {\n if (labelKey === '{}') return '';\n const obj = JSON.parse(labelKey) as Record<string, string>;\n const parts = Object.entries(obj).map(([k, v]) => `${k}=\"${escapeLabel(v)}\"`);\n return `{${parts.join(',')}}`;\n}\n\nfunction escapeLabel(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n');\n}\n\n/**\n * Coerce an arbitrary string into a valid Prometheus metric or label name\n * (`[a-zA-Z_][a-zA-Z0-9_]*`). Invalid characters become underscores and a\n * leading digit is prefixed with `_`. Without this, a user-supplied name like\n * `my-metric` or a label like `status code` would emit output Prometheus cannot\n * parse, breaking the ENTIRE scrape endpoint, not just that metric.\n */\nfunction sanitizePromName(name: string): string {\n let safe = name.replace(/\\W/g, '_');\n if (safe.length > 0 && /\\d/.test(safe[0]!)) safe = `_${safe}`;\n return safe.length > 0 ? safe : '_';\n}\n\n// ── MetricsPlugin ─────────────────────────────────────────────────────────────\n\n/**\n * A logixia plugin that extracts Prometheus-compatible metrics from log entries.\n *\n * Implements `LogixiaPlugin` — pass directly to `logger.use()`:\n * ```ts\n * const metrics = new MetricsPlugin({ ... });\n * logger.use(metrics);\n * ```\n *\n * Or use the `createMetricsPlugin()` factory (preferred):\n * ```ts\n * const metrics = createMetricsPlugin({ ... });\n * logger.use(metrics);\n * ```\n */\nexport class MetricsPlugin implements LogixiaPlugin {\n public readonly name = 'logixia-metrics';\n\n private readonly map: MetricsMap;\n private readonly metricState = new Map<string, MetricState>();\n private readonly overflowKeys = new Map<string, string>();\n\n private readonly maxLabelSets: number;\n\n constructor(map: MetricsMap, options: MetricsPluginOptions = {}) {\n this.map = map;\n const max = options.maxLabelSets;\n this.maxLabelSets =\n typeof max === 'number' && Number.isFinite(max) && max >= 1\n ? Math.floor(max)\n : DEFAULT_MAX_LABEL_SETS;\n this.initAllState();\n }\n\n // ── LogixiaPlugin lifecycle ────────────────────────────────────────────────\n\n onInit(): void {\n // State is initialised in the constructor; nothing extra needed here.\n }\n\n onLog(entry: LogEntry): LogEntry {\n const payload = entry.payload ?? {};\n\n for (const [rawName, config] of Object.entries(this.map)) {\n const state = this.metricState.get(rawName);\n if (!state) continue;\n\n const rawLabelKey = buildLabelKey(config, entry);\n const overflowKey = this.overflowKeys.get(rawName) ?? '{}';\n\n if (config.type === 'counter' && state.type === 'counter') {\n // Level filter\n if (config.levelFilter && entry.level !== config.levelFilter) continue;\n // Field/value filter\n if (config.field !== undefined && payload[config.field] !== config.value) continue;\n const labelKey = this.capLabelKey(state.values, rawLabelKey, overflowKey);\n state.values.set(labelKey, (state.values.get(labelKey) ?? 0) + 1);\n } else if (config.type === 'histogram' && state.type === 'histogram') {\n const raw = payload[config.field];\n if (raw === undefined || raw === null) continue;\n const val = Number(raw);\n if (Number.isNaN(val)) continue;\n const labelKey = this.capLabelKey(state.counts, rawLabelKey, overflowKey);\n\n // Initialise per-label arrays on first observation\n if (!state.counts.has(labelKey)) {\n state.counts.set(\n labelKey,\n Array.from<number>({ length: state.buckets.length + 1 }).fill(0)\n );\n state.sums.set(labelKey, 0);\n state.observations.set(labelKey, 0);\n }\n\n const bucketArr = state.counts.get(labelKey)!;\n for (let i = 0; i < state.buckets.length; i++) {\n if (val <= state.buckets[i]!) bucketArr[i]!++;\n }\n // The +Inf bucket (last slot) always increments\n bucketArr[state.buckets.length]!++;\n state.sums.set(labelKey, (state.sums.get(labelKey) ?? 0) + val);\n state.observations.set(labelKey, (state.observations.get(labelKey) ?? 0) + 1);\n } else if (config.type === 'gauge' && state.type === 'gauge') {\n const raw = payload[config.field];\n if (raw === undefined || raw === null) continue;\n const val = Number(raw);\n if (Number.isNaN(val)) continue;\n state.values.set(this.capLabelKey(state.values, rawLabelKey, overflowKey), val);\n }\n }\n\n // Always pass the entry through unchanged\n return entry;\n }\n\n // ── Output ─────────────────────────────────────────────────────────────────\n\n /**\n * Render all registered metrics in the Prometheus text exposition format\n * (version 0.0.4).\n *\n * All metric names are prefixed with `logixia_`.\n *\n * @example\n * ```\n * # HELP logixia_error_count Total error log entries\n * # TYPE logixia_error_count counter\n * logixia_error_count{context=\"OrderService\"} 7\n * ```\n */\n render(): string {\n const lines: string[] = [];\n\n for (const [rawName, config] of Object.entries(this.map)) {\n const metricName = `logixia_${sanitizePromName(rawName)}`;\n const state = this.metricState.get(rawName);\n if (!state) continue;\n\n const helpText = config.help ?? rawName.replace(/_/g, ' ');\n lines.push(`# HELP ${metricName} ${helpText}`);\n lines.push(`# TYPE ${metricName} ${config.type}`);\n\n if (state.type === 'counter') {\n if (state.values.size === 0) {\n lines.push(`${metricName} 0`);\n } else {\n for (const [labelKey, count] of state.values) {\n lines.push(`${metricName}${renderLabels(labelKey)} ${count}`);\n }\n }\n } else if (state.type === 'histogram') {\n for (const [labelKey, bucketCounts] of state.counts) {\n const labelSuffix = renderLabels(labelKey);\n const baseObj = labelKey === '{}' ? {} : (JSON.parse(labelKey) as Record<string, string>);\n\n // One line per finite bucket\n for (let i = 0; i < state.buckets.length; i++) {\n const leKey = JSON.stringify({ ...baseObj, le: String(state.buckets[i]) });\n lines.push(`${metricName}_bucket${renderLabels(leKey)} ${bucketCounts[i] ?? 0}`);\n }\n // +Inf bucket\n const infKey = JSON.stringify({ ...baseObj, le: '+Inf' });\n lines.push(\n `${metricName}_bucket${renderLabels(infKey)} ${bucketCounts[state.buckets.length] ?? 0}`\n );\n lines.push(`${metricName}_sum${labelSuffix} ${state.sums.get(labelKey) ?? 0}`);\n lines.push(`${metricName}_count${labelSuffix} ${state.observations.get(labelKey) ?? 0}`);\n }\n } else if (state.type === 'gauge') {\n if (state.values.size === 0) {\n lines.push(`${metricName} 0`);\n } else {\n for (const [labelKey, val] of state.values) {\n lines.push(`${metricName}${renderLabels(labelKey)} ${val}`);\n }\n }\n }\n }\n\n return `${lines.join('\\n')}\\n`;\n }\n\n /**\n * Reset all metric counters and observations back to zero.\n * Useful between test runs or on a scheduled reset interval.\n */\n reset(): void {\n this.metricState.clear();\n this.initAllState();\n }\n\n /**\n * Express route handler — call `app.get('/metrics', metrics.expressHandler())`.\n *\n * @example\n * ```ts\n * import express from 'express';\n * const app = express();\n * app.get('/metrics', metrics.expressHandler());\n * ```\n */\n expressHandler(): (\n req: unknown,\n res: { set(k: string, v: string): unknown; end(body: string): void }\n ) => void {\n return (_req, res) => {\n res.set('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');\n res.end(this.render());\n };\n }\n\n /**\n * Raw Node.js `http` server handler.\n * Responds to `GET /metrics` with the Prometheus text payload.\n *\n * @example Start a dedicated metrics server on the standard port:\n * ```ts\n * import http from 'node:http';\n * const server = http.createServer(metrics.httpHandler());\n * server.listen(9464); // Prometheus default port for custom exporters\n * ```\n */\n httpHandler(): (req: IncomingMessage, res: ServerResponse) => void {\n return (req, res) => {\n const isMetricsPath = req.url === '/metrics' || req.url === '/metrics/';\n if (!isMetricsPath) {\n res.writeHead(404).end('Not found');\n return;\n }\n const body = this.render();\n res.writeHead(200, {\n 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8',\n 'Content-Length': Buffer.byteLength(body),\n });\n res.end(body);\n };\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n /**\n * Return `labelKey` when it is already tracked or there is room for it;\n * otherwise the shared overflow series key.\n */\n private capLabelKey(series: Map<string, unknown>, labelKey: string, overflowKey: string): string {\n if (series.has(labelKey) || series.size < this.maxLabelSets) return labelKey;\n return overflowKey;\n }\n\n private initAllState(): void {\n for (const [rawName, config] of Object.entries(this.map)) {\n this.initSingleState(rawName, config);\n }\n }\n\n private initSingleState(rawName: string, config: MetricConfig): void {\n this.overflowKeys.set(rawName, buildOverflowKey(config));\n if (config.type === 'counter') {\n this.metricState.set(rawName, { type: 'counter', values: new Map() });\n } else if (config.type === 'histogram') {\n const buckets = [...(config.buckets ?? DEFAULT_BUCKETS)].sort((a, b) => a - b);\n this.metricState.set(rawName, {\n type: 'histogram',\n buckets,\n counts: new Map(),\n sums: new Map(),\n observations: new Map(),\n });\n } else {\n this.metricState.set(rawName, { type: 'gauge', values: new Map() });\n }\n }\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Create a `MetricsPlugin` from a metrics map and return it ready for\n * `logger.use()`.\n *\n * @example\n * ```ts\n * import { createMetricsPlugin } from 'logixia';\n *\n * const metrics = createMetricsPlugin({\n * http_request_duration: {\n * type: 'histogram',\n * field: 'duration',\n * labels: ['method', 'statusCode'],\n * help: 'HTTP request duration in milliseconds',\n * },\n * error_count: {\n * type: 'counter',\n * levelFilter: 'error',\n * labels: ['context'],\n * help: 'Total error log entries',\n * },\n * });\n *\n * logger.use(metrics);\n * app.get('/metrics', metrics.expressHandler());\n * ```\n *\n * Pass `{ maxLabelSets }` to change the per-metric label cardinality cap (default 1000).\n */\nexport function createMetricsPlugin(\n map: MetricsMap,\n options?: MetricsPluginOptions\n): MetricsPlugin {\n return new MetricsPlugin(map, options);\n}\n","/**\n * Logixia - Advanced TypeScript Logger\n *\n * A comprehensive logging library with support for:\n * - Multiple output formats (console, file, JSON)\n * - Trace ID tracking\n * - Performance monitoring\n * - NestJS integration\n * - Customizable log levels and colors\n * - Intelligent log search and aggregation\n * - Natural language query processing\n * - Pattern recognition and anomaly detection\n */\n\nimport { createLogger as createLoggerFromCore, LogixiaLogger } from './core/logitron-logger';\nimport { LogixiaLoggerService } from './core/logitron-nestjs.service';\nimport type { Environment, LogColor, LoggerConfig } from './types';\nimport { LogLevel } from './types';\n\n// Type exports\nexport * from './core/logitron-logger.module';\nexport * from './core/logitron-nestjs.service';\nexport type { LogixiaExceptionFilterOptions, LogMethodOptions } from './core/nestjs-extras';\nexport {\n InjectLogger,\n LOGIXIA_EXCEPTION_FILTER_OPTIONS,\n LogixiaExceptionFilter,\n LogMethod,\n} from './core/nestjs-extras';\nexport * from './formatters';\nexport * from './types';\nexport * from './utils/error.utils';\nexport type { OtelBridgeOptions, OtelSpanContext } from './utils/otel';\nexport {\n disableOtelBridge,\n getActiveOtelContext,\n getOtelMetaFields,\n initOtelBridge,\n} from './utils/otel';\nexport { applyRedaction, redactObject } from './utils/redact.utils';\nexport type { LevelSignalOptions, ReconfigurableLogger } from './utils/runtime-control';\nexport { createLevelControlHandler, registerLevelSignal } from './utils/runtime-control';\nexport type { SafeStringifyOptions } from './utils/safe-stringify';\nexport { decycleValue, retrocycle, safeStringify, stringifyForLog } from './utils/safe-stringify';\nexport * from './utils/shutdown.utils';\nexport * from './utils/trace.utils';\nexport type {\n CompiledSchema,\n LogFieldDef,\n LogFieldType,\n LoggerLike,\n LogSchema,\n TypedLogger,\n} from './utils/typed-logger';\nexport { createTypedLogger, defineLogSchema } from './utils/typed-logger';\nexport type {\n WideEventFields,\n WideEventLogger,\n WideEventMiddlewareOptions,\n WideEventOptions,\n} from './wide-events';\nexport {\n addEventFields,\n getEventFields,\n setEventField,\n wideEventMiddleware,\n withWideEvent,\n} from './wide-events';\n\n// OTLP logs export transport (OpenTelemetry-native log emission)\nexport type { OtlpLogTransportConfig } from './transports/otlp.transport';\nexport { OtlpLogTransport, toOtelSeverity } from './transports/otlp.transport';\n\n// Search module exports\nexport * from './search';\n\n// Context propagation (AsyncLocalStorage)\nexport type { LogContext } from './context/async-context';\nexport {\n createExpressContextMiddleware,\n createFastifyContextHook,\n LogixiaContext,\n} from './context/async-context';\n\n// Sampling stats (SamplingConfig is already exported via ./types)\nexport type { SamplingStats } from './utils/sampling.utils';\n\n// Plugin API (Feature 20)\nexport type { LogixiaPlugin } from './plugin';\nexport { globalPluginRegistry, PluginRegistry, usePlugin } from './plugin';\n\n// Exception system — typed LogixiaException + unified error response format\nexport type {\n BuildParams,\n ErrorDetail,\n LogixiaErrorResponse,\n LogixiaExceptionOptions,\n} from './exceptions';\nexport {\n ErrorResponseBuilder,\n generateRequestId,\n generateTraceId,\n isLogixiaException,\n LogixiaException,\n} from './exceptions';\n\n// Metrics extraction → Prometheus (Feature 21)\nexport type {\n CounterConfig,\n GaugeConfig,\n HistogramConfig,\n MetricConfig,\n MetricsMap,\n} from './metrics';\nexport type { MetricsPluginOptions } from './metrics';\nexport { createMetricsPlugin, MetricsPlugin, OVERFLOW_LABEL_VALUE } from './metrics';\n\n// Core exports\nexport { DEFAULT_CONFIG, LogixiaLogger, LogixiaLoggerService };\n\n/**\n * Default configuration for Logixia logger\n */\nconst DEFAULT_CONFIG = {\n appName: 'App',\n environment: 'development' as Environment,\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 [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 } as Record<string, LogColor>,\n },\n fields: {\n timestamp: true,\n level: true,\n appName: true,\n traceId: true,\n message: true,\n payload: true,\n timeTaken: true,\n },\n outputs: ['console'],\n};\n\n/**\n * Create a new Logixia logger instance with TypeScript support for custom levels\n * @param config - Logger configuration\n * @returns Typed logger instance\n */\nexport const createLogger = createLoggerFromCore;\n\n/**\n * Create a new Logixia logger service for NestJS\n * @param config - Logger configuration\n * @returns LogixiaLoggerService instance\n */\nexport function createLoggerService(config?: Partial<LoggerConfig>): LogixiaLoggerService {\n return new LogixiaLoggerService({ ...DEFAULT_CONFIG, ...config });\n}\n\n/**\n * Default logger instance\n */\nexport const logger = new LogixiaLogger(DEFAULT_CONFIG);\n\n/**\n * Export default configuration for reference\n */\n"],"mappings":"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"}
|