logixia 1.13.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -16
- 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-Cn_3Usvu.d.ts} +2 -2
- package/dist/{azure-monitor.transport-CG7ClkYj.d.ts.map → azure-monitor.transport-Cn_3Usvu.d.ts.map} +1 -1
- package/dist/{azure-monitor.transport-Cm74kXZS.d.mts → azure-monitor.transport-D7Da4RAb.d.mts} +2 -2
- package/dist/{azure-monitor.transport-Cm74kXZS.d.mts.map → azure-monitor.transport-D7Da4RAb.d.mts.map} +1 -1
- package/dist/azure.d.mts +2 -2
- package/dist/azure.d.ts +2 -2
- package/dist/browser.d.mts +9 -0
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.d.ts +9 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +43 -12
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +43 -12
- package/dist/browser.mjs.map +1 -1
- package/dist/{logitron-logger.module-Bs3XYZgF.mjs → builder-0gEzRopq.mjs} +383 -1021
- package/dist/builder-0gEzRopq.mjs.map +1 -0
- package/dist/{logitron-logger.module-BsDZR5BH.js → builder-D44QktIo.js} +411 -1155
- package/dist/builder-D44QktIo.js.map +1 -0
- package/dist/cli/index.js +130 -65
- 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-pNBNWJLt.js → cloudwatch.transport-BO1ahxw-.js} +26 -19
- package/dist/cloudwatch.transport-BO1ahxw-.js.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/{cloudwatch.transport-CGHY4buE.d.mts → cloudwatch.transport-DsEq633o.d.ts} +9 -2
- package/dist/cloudwatch.transport-DsEq633o.d.ts.map +1 -0
- package/dist/{cloudwatch.transport-DK_8cKd3.d.ts → cloudwatch.transport-UlyZFWMn.d.mts} +9 -2
- package/dist/cloudwatch.transport-UlyZFWMn.d.mts.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 +24 -6
- package/dist/correlation.d.mts.map +1 -1
- package/dist/correlation.d.ts +24 -6
- package/dist/correlation.d.ts.map +1 -1
- package/dist/correlation.js +41 -16
- package/dist/correlation.js.map +1 -1
- package/dist/correlation.mjs +41 -16
- 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-BOhkVKlI.d.ts → gcp.transport-B7oc97sG.d.ts} +2 -2
- package/dist/gcp.transport-B7oc97sG.d.ts.map +1 -0
- 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-Bi6CUqZR.d.mts → gcp.transport-GrJurKo8.d.mts} +2 -2
- package/dist/gcp.transport-GrJurKo8.d.mts.map +1 -0
- package/dist/{index-BPKwwERo.d.mts → index-3mw5gaCw.d.mts} +2 -2
- package/dist/{index-BPKwwERo.d.mts.map → index-3mw5gaCw.d.mts.map} +1 -1
- package/dist/{index-C1X-TE5I.d.ts → index-DhIoploR.d.ts} +2 -2
- package/dist/{index-C1X-TE5I.d.ts.map → index-DhIoploR.d.ts.map} +1 -1
- package/dist/index.d.mts +99 -137
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +99 -137
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +146 -510
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +99 -431
- package/dist/index.mjs.map +1 -1
- package/dist/logitron-logger-BFDSO8Io.d.mts +257 -0
- package/dist/logitron-logger-BFDSO8Io.d.mts.map +1 -0
- package/dist/logitron-logger-UVeN-mQm.d.ts +257 -0
- package/dist/logitron-logger-UVeN-mQm.d.ts.map +1 -0
- 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 +481 -6
- package/dist/nest.d.mts.map +1 -1
- package/dist/nest.d.ts +481 -6
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +884 -13
- package/dist/nest.js.map +1 -0
- package/dist/nest.mjs +863 -6
- package/dist/nest.mjs.map +1 -0
- package/dist/{search-CFQRISUv.mjs → search-BFbu_oVn.mjs} +35 -12
- package/dist/search-BFbu_oVn.mjs.map +1 -0
- package/dist/{search-njTI2hdo.js → search-C4V0OMEn.js} +35 -12
- package/dist/search-C4V0OMEn.js.map +1 -0
- package/dist/{search-DujhR94O.d.ts → search-CNh9uVwR.d.ts} +2 -3
- package/dist/search-CNh9uVwR.d.ts.map +1 -0
- package/dist/{search-CQOo-Iq6.d.mts → search-DXANsQ2n.d.mts} +2 -3
- package/dist/search-DXANsQ2n.d.mts.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/slack.transport-BZzoXDPn.d.mts +112 -0
- package/dist/slack.transport-BZzoXDPn.d.mts.map +1 -0
- package/dist/slack.transport-BfJRouB2.js +654 -0
- package/dist/slack.transport-BfJRouB2.js.map +1 -0
- package/dist/slack.transport-Bzw7sucI.mjs +643 -0
- package/dist/slack.transport-Bzw7sucI.mjs.map +1 -0
- package/dist/slack.transport-CXjQYOes.d.ts +112 -0
- package/dist/slack.transport-CXjQYOes.d.ts.map +1 -0
- 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-BiuCAJd2.mjs} +526 -160
- package/dist/transport.manager-BiuCAJd2.mjs.map +1 -0
- package/dist/{transport.manager-BivyUTtU.js → transport.manager-CXbKKdVE.js} +537 -159
- package/dist/transport.manager-CXbKKdVE.js.map +1 -0
- package/dist/{transport.types-B5vcyv2k.d.mts → transport.types-BMnurPcc.d.mts} +23 -3
- package/dist/transport.types-BMnurPcc.d.mts.map +1 -0
- package/dist/{transport.types-BuY84TwO.d.ts → transport.types-CGoHpWQR.d.ts} +23 -3
- package/dist/transport.types-CGoHpWQR.d.ts.map +1 -0
- package/dist/transports.d.mts +50 -20
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +50 -20
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +9 -5
- package/dist/transports.mjs +8 -6
- 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-CfH6z37Z.js → worker.transport-BdPxu3PN.js} +1 -1
- package/dist/{worker.transport-CfH6z37Z.js.map → worker.transport-BdPxu3PN.js.map} +1 -1
- package/dist/{worker.transport-mKXgNK0L.d.ts → worker.transport-CdzSZJmw.d.ts} +2 -2
- package/dist/{worker.transport-mKXgNK0L.d.ts.map → worker.transport-CdzSZJmw.d.ts.map} +1 -1
- package/dist/{worker.transport-BP8Q28Z2.d.mts → worker.transport-JvGPog3C.d.mts} +2 -2
- package/dist/{worker.transport-BP8Q28Z2.d.mts.map → worker.transport-JvGPog3C.d.mts.map} +1 -1
- package/dist/{worker.transport-BhMyvqQ2.mjs → worker.transport-lPxO2MVL.mjs} +1 -1
- package/dist/{worker.transport-BhMyvqQ2.mjs.map → worker.transport-lPxO2MVL.mjs.map} +1 -1
- package/package.json +27 -14
- 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-BdG1Jnpk.d.mts +0 -618
- package/dist/logitron-logger.module-BdG1Jnpk.d.mts.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-CbXy2gfW.d.ts +0 -618
- package/dist/logitron-logger.module-CbXy2gfW.d.ts.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/middleware.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.mjs","names":["out: Record<string, unknown>","fields: Record<string, unknown>"],"sources":["../src/middleware/http-logger.ts"],"sourcesContent":["/**\n * HTTP request/response logging middleware — Morgan replacement.\n *\n * Fixes every documented Morgan bug:\n * - statusCode always captured correctly, even for requests > 20 s\n * - Logs request START (with traceId) and response FINISH (with duration)\n * - Captures errors before and after response\n * - Auto-redacts Authorization / Cookie / Set-Cookie headers\n * - Slow-request warnings\n * - Skip predicates for health-check routes / static assets\n *\n * @example Express\n * ```ts\n * import { createExpressMiddleware } from 'logixia/middleware';\n * app.use(createExpressMiddleware(logger));\n * ```\n *\n * @example Fastify\n * ```ts\n * import { createFastifyPlugin } from 'logixia/middleware';\n * await fastify.register(createFastifyPlugin(logger));\n * ```\n */\n\nimport type { IBaseLogger } from '../types';\nimport { stringifyForLog } from '../utils/safe-stringify';\n\n// ── Shared types ─────────────────────────────────────────────────────────────\n\nexport interface HttpLoggerOptions {\n /**\n * Skip logging for a request. Called before any I/O.\n * @example `skip: (req) => req.url === '/health'`\n */\n skip?: (req: IncomingRequest) => boolean;\n /**\n * Log request body (POST/PUT/PATCH). Capped at `bodyMaxBytes` (default: 4096).\n * Redaction still applies to the captured body.\n * Default: false.\n */\n logBody?: boolean;\n /** Max bytes of body to capture. Default: 4096. */\n bodyMaxBytes?: number;\n /**\n * Emit a WARN log when a request duration exceeds this threshold (ms).\n * Default: 1000.\n */\n slowRequestThresholdMs?: number;\n /**\n * Additional fields to include in every log entry.\n * @example `extraFields: (req) => ({ tenantId: req.headers['x-tenant-id'] })`\n */\n extraFields?: (req: IncomingRequest) => Record<string, unknown>;\n /**\n * Trace ID header. Default: 'x-trace-id'.\n * If the header is absent, a short random ID is generated.\n */\n traceIdHeader?: string;\n /**\n * Headers to redact from logged output.\n * Default: ['authorization', 'cookie', 'set-cookie', 'x-api-key'].\n */\n redactHeaders?: string[];\n /**\n * Log level for request-start entries. Default: 'debug'.\n * Set to 'silent' to suppress request-start logs entirely.\n */\n requestLevel?: string;\n /** Log level for successful response entries. Default: 'info'. */\n responseLevel?: string;\n /** Log level for error responses (status ≥ 500). Default: 'error'. */\n errorLevel?: string;\n}\n\n// Minimal structural types so we don't need @types/express / fastify in core\nexport interface IncomingRequest {\n method?: string;\n url?: string;\n headers?: Record<string, string | string[] | undefined>;\n body?: unknown;\n socket?: { remoteAddress?: string };\n ip?: string;\n}\n\nexport interface OutgoingResponse {\n statusCode?: number;\n on?: (event: string, cb: () => void) => void;\n once?: (event: string, cb: () => void) => void;\n getHeader?: (name: string) => string | number | string[] | undefined;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nconst DEFAULT_REDACT_HEADERS = new Set(['authorization', 'cookie', 'set-cookie', 'x-api-key']);\n\nfunction sanitizeHeaders(\n headers: Record<string, string | string[] | undefined> | undefined,\n redactSet: Set<string>\n): Record<string, unknown> {\n if (!headers) return {};\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(headers)) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n out[k] = redactSet.has(k.toLowerCase()) ? '[REDACTED]' : v;\n }\n return out;\n}\n\nfunction shortId(): string {\n // eslint-disable-next-line sonarjs/pseudo-random -- non-security request ID\n return Math.random().toString(36).slice(2, 10);\n}\n\nfunction buildBaseFields(\n req: IncomingRequest,\n traceId: string,\n options: HttpLoggerOptions\n): Record<string, unknown> {\n const redactSet = options.redactHeaders\n ? new Set(options.redactHeaders.map((h) => h.toLowerCase()))\n : DEFAULT_REDACT_HEADERS;\n\n const fields: Record<string, unknown> = {\n traceId,\n method: req.method?.toUpperCase() ?? 'UNKNOWN',\n url: req.url ?? '/',\n ip: req.ip ?? req.socket?.remoteAddress ?? 'unknown',\n headers: sanitizeHeaders(req.headers, redactSet),\n };\n\n if (options.extraFields) {\n Object.assign(fields, options.extraFields(req));\n }\n\n return fields;\n}\n\n// ── Express middleware ────────────────────────────────────────────────────────\n\n/**\n * Create an Express / Connect compatible middleware that replaces Morgan.\n */\nexport function createExpressMiddleware(\n logger: IBaseLogger,\n options: HttpLoggerOptions = {}\n): (req: IncomingRequest, res: OutgoingResponse, next: () => void) => void {\n const {\n skip,\n logBody,\n bodyMaxBytes,\n traceIdHeader = 'x-trace-id',\n requestLevel = 'debug',\n responseLevel = 'info',\n errorLevel = 'error',\n slowRequestThresholdMs = 1000,\n } = options;\n\n return function logixiaHttpMiddleware(\n req: IncomingRequest,\n res: OutgoingResponse,\n next: () => void\n ): void {\n if (skip?.(req)) {\n next();\n return;\n }\n\n const traceId = (req.headers?.[traceIdHeader] as string | undefined) ?? shortId();\n const startMs = Date.now();\n const baseFields = buildBaseFields(req, traceId, options);\n\n // Log request start\n if (requestLevel !== 'silent') {\n void logger.logLevel(requestLevel, 'request started', {\n ...baseFields,\n ...(logBody && req.body ? { body: truncateBody(req.body, bodyMaxBytes) } : {}),\n });\n }\n\n // Hook into the response 'finish' event — fires after headers + body are sent.\n // This is what Morgan gets wrong for slow requests (it uses 'close' which may\n // fire before the status code is set on some Node versions).\n //\n // BOTH 'finish' and 'close' fire on a normal response, so guard against\n // logging the completion twice (which would duplicate the completed entry and\n // the slow-request warning).\n let completed = false;\n const onFinish = (): void => {\n if (completed) return;\n completed = true;\n\n const duration = Date.now() - startMs;\n const status = res.statusCode ?? 0;\n const level = status >= 500 ? errorLevel : responseLevel;\n\n void logger.logLevel(level, 'request completed', {\n ...baseFields,\n statusCode: status,\n duration,\n });\n\n if (duration > slowRequestThresholdMs) {\n void logger.warn('slow request detected', {\n ...baseFields,\n statusCode: status,\n duration,\n threshold: slowRequestThresholdMs,\n });\n }\n };\n\n res.once?.('finish', onFinish);\n // Fallback: also covers a client that disconnects before 'finish' fires. The\n // `completed` guard ensures a normal finish+close pair logs only once.\n res.once?.('close', onFinish);\n\n next();\n };\n}\n\n// ── Fastify plugin ─────────────────────────────────────────────────────────────\n\nexport interface FastifyInstance {\n addHook: (name: string, fn: (req: unknown, reply: unknown, done: () => void) => void) => void;\n}\n\n/**\n * Create a Fastify plugin (a function you pass to `fastify.register()`).\n *\n * @example\n * ```ts\n * await fastify.register(createFastifyPlugin(logger, { slowRequestThresholdMs: 500 }));\n * ```\n */\nexport function createFastifyPlugin(logger: IBaseLogger, options: HttpLoggerOptions = {}) {\n const {\n skip,\n traceIdHeader = 'x-trace-id',\n requestLevel = 'debug',\n responseLevel = 'info',\n errorLevel = 'error',\n slowRequestThresholdMs = 1000,\n } = options;\n\n function logixiaFastifyPlugin(fastify: FastifyInstance, _opts: unknown, done: () => void): void {\n fastify.addHook('onRequest', (request: unknown, _reply: unknown, hookDone: () => void) => {\n const req = request as IncomingRequest & { _logixiaStart?: number; _logixiaId?: string };\n if (skip?.(req)) {\n hookDone();\n return;\n }\n\n const traceId = (req.headers?.[traceIdHeader] as string | undefined) ?? shortId();\n req._logixiaStart = Date.now();\n req._logixiaId = traceId;\n\n if (requestLevel !== 'silent') {\n void logger.logLevel(\n requestLevel,\n 'request started',\n buildBaseFields(req, traceId, options)\n );\n }\n hookDone();\n });\n\n fastify.addHook('onResponse', (request: unknown, reply: unknown, hookDone: () => void) => {\n const req = request as IncomingRequest & { _logixiaStart?: number; _logixiaId?: string };\n if (req._logixiaStart === undefined) {\n hookDone();\n return;\n }\n\n const rep = reply as { statusCode?: number };\n const duration = Date.now() - req._logixiaStart;\n const status = rep.statusCode ?? 0;\n const traceId = req._logixiaId ?? shortId();\n const level = status >= 500 ? errorLevel : responseLevel;\n\n void logger.logLevel(level, 'request completed', {\n ...buildBaseFields(req, traceId, options),\n statusCode: status,\n duration,\n });\n\n if (duration > slowRequestThresholdMs) {\n void logger.warn('slow request detected', {\n traceId,\n url: req.url,\n method: req.method,\n duration,\n threshold: slowRequestThresholdMs,\n });\n }\n\n hookDone();\n });\n\n done();\n }\n\n return Object.assign(logixiaFastifyPlugin, {\n [Symbol.for('skip-override')]: true,\n [Symbol.for('fastify.display-name')]: 'logixia',\n });\n}\n\n// ── Internal helpers ──────────────────────────────────────────────────────────\n\nfunction truncateBody(body: unknown, maxBytes = 4096): unknown {\n if (typeof body === 'string') {\n return body.length > maxBytes ? body.slice(0, maxBytes) + '…[truncated]' : body;\n }\n if (body && typeof body === 'object') {\n const str = stringifyForLog(body);\n if (str.length > maxBytes) {\n return str.slice(0, maxBytes) + '…[truncated]';\n }\n }\n return body;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"middleware.mjs","names":["out: Record<string, unknown>","fields: Record<string, unknown>","items: unknown[]","json: unknown","record: Record<string, unknown>"],"sources":["../src/middleware/http-logger.ts"],"sourcesContent":["/**\n * HTTP request/response logging middleware — Morgan replacement.\n *\n * Fixes every documented Morgan bug:\n * - statusCode always captured correctly, even for requests > 20 s\n * - Logs request START (with traceId) and response FINISH (with duration)\n * - Captures errors before and after response\n * - Auto-redacts Authorization / Cookie / Set-Cookie headers\n * - Slow-request warnings\n * - Skip predicates for health-check routes / static assets\n *\n * @example Express\n * ```ts\n * import { createExpressMiddleware } from 'logixia/middleware';\n * app.use(createExpressMiddleware(logger));\n * ```\n *\n * @example Fastify\n * ```ts\n * import { createFastifyPlugin } from 'logixia/middleware';\n * await fastify.register(createFastifyPlugin(logger));\n * ```\n */\n\nimport type { IBaseLogger } from '../types';\nimport { internalError } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { sanitizeIncomingId } from '../utils/trace.utils';\n\n// ── Shared types ─────────────────────────────────────────────────────────────\n\nexport interface HttpLoggerOptions {\n /**\n * Skip logging for a request. Called before any I/O.\n * @example `skip: (req) => req.url === '/health'`\n */\n skip?: (req: IncomingRequest) => boolean;\n /**\n * Log request body (POST/PUT/PATCH). Capped at `bodyMaxBytes` (default: 4096).\n * Redaction still applies to the captured body.\n * Default: false.\n */\n logBody?: boolean;\n /** Max bytes of body to capture. Default: 4096. */\n bodyMaxBytes?: number;\n /**\n * Emit a WARN log when a request duration exceeds this threshold (ms).\n * Default: 1000.\n */\n slowRequestThresholdMs?: number;\n /**\n * Additional fields to include in every log entry.\n * @example `extraFields: (req) => ({ tenantId: req.headers['x-tenant-id'] })`\n */\n extraFields?: (req: IncomingRequest) => Record<string, unknown>;\n /**\n * Trace ID header. Default: 'x-trace-id'.\n * If the header is absent, a short random ID is generated.\n */\n traceIdHeader?: string;\n /**\n * Additional headers to redact from logged output. Merged with the defaults\n * (authorization, cookie, set-cookie, x-api-key, proxy-authorization,\n * x-auth-token, x-access-token, x-csrf-token, x-amz-security-token).\n */\n redactHeaders?: string[];\n /**\n * Additional query-string parameter names whose values are redacted from the\n * logged URL. Merged with the defaults (token, access_token, refresh_token,\n * code, password, secret, api_key, apikey, key, signature, sig, auth).\n */\n redactQueryParams?: string[];\n /**\n * Log level for request-start entries. Default: 'debug'.\n * Set to 'silent' to suppress request-start logs entirely.\n */\n requestLevel?: string;\n /** Log level for successful response entries. Default: 'info'. */\n responseLevel?: string;\n /** Log level for error responses (status ≥ 500). Default: 'error'. */\n errorLevel?: string;\n}\n\n// Minimal structural types so we don't need @types/express / fastify in core\nexport interface IncomingRequest {\n method?: string;\n url?: string;\n headers?: Record<string, string | string[] | undefined>;\n body?: unknown;\n socket?: { remoteAddress?: string };\n ip?: string;\n}\n\nexport interface OutgoingResponse {\n statusCode?: number;\n on?: (event: string, cb: () => void) => void;\n once?: (event: string, cb: () => void) => void;\n getHeader?: (name: string) => string | number | string[] | undefined;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nconst DEFAULT_REDACT_HEADERS = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'proxy-authorization',\n 'x-auth-token',\n 'x-access-token',\n 'x-csrf-token',\n 'x-amz-security-token',\n];\n\nconst DEFAULT_REDACT_QUERY_PARAMS = [\n 'token',\n 'access_token',\n 'refresh_token',\n 'code',\n 'password',\n 'secret',\n 'api_key',\n 'apikey',\n 'key',\n 'signature',\n 'sig',\n 'auth',\n];\n\nfunction buildNameSet(defaults: readonly string[], extra: readonly string[] | undefined) {\n return new Set([...defaults, ...(extra ?? [])].map((n) => n.toLowerCase()));\n}\n\nfunction safeDecode(value: string): string {\n try {\n return decodeURIComponent(value.replace(/\\+/g, ' '));\n } catch {\n return value;\n }\n}\n\n/** Replace the values of sensitive query parameters, leaving the rest verbatim. */\nfunction sanitizeUrl(url: string, redactParams: Set<string>): string {\n const q = url.indexOf('?');\n if (q === -1) return url;\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const cleaned = query\n .split('&')\n .map((pair) => {\n const eq = pair.indexOf('=');\n const rawKey = eq === -1 ? pair : pair.slice(0, eq);\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n const name = (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n return redactParams.has(name) && eq !== -1 ? `${rawKey}=[REDACTED]` : pair;\n })\n .join('&');\n return `${url.slice(0, q)}?${cleaned}${hash}`;\n}\n\nfunction sanitizeHeaders(\n headers: Record<string, string | string[] | undefined> | undefined,\n redactSet: Set<string>\n): Record<string, unknown> {\n if (!headers) return {};\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(headers)) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n out[k] = redactSet.has(k.toLowerCase()) ? '[REDACTED]' : v;\n }\n return out;\n}\n\n/** Fire-and-forget a logger call; a failing log must never become an unhandled rejection. */\nfunction fire(result: unknown): void {\n if (result && typeof (result as Promise<unknown>).catch === 'function') {\n (result as Promise<unknown>).catch((error: unknown) =>\n internalError('http-logger: log call failed', error)\n );\n }\n}\n\nfunction shortId(): string {\n // eslint-disable-next-line sonarjs/pseudo-random -- non-security request ID\n return Math.random().toString(36).slice(2, 10);\n}\n\ninterface NameSets {\n headers: Set<string>;\n queryParams: Set<string>;\n}\n\nfunction resolveNameSets(options: HttpLoggerOptions): NameSets {\n return {\n headers: buildNameSet(DEFAULT_REDACT_HEADERS, options.redactHeaders),\n queryParams: buildNameSet(DEFAULT_REDACT_QUERY_PARAMS, options.redactQueryParams),\n };\n}\n\nfunction buildBaseFields(\n req: IncomingRequest,\n traceId: string,\n options: HttpLoggerOptions,\n { headers: redactSet, queryParams: paramSet }: NameSets\n): Record<string, unknown> {\n const fields: Record<string, unknown> = {\n traceId,\n method: req.method?.toUpperCase() ?? 'UNKNOWN',\n url: sanitizeUrl(req.url ?? '/', paramSet),\n ip: req.ip ?? req.socket?.remoteAddress ?? 'unknown',\n headers: sanitizeHeaders(req.headers, redactSet),\n };\n\n if (options.extraFields) {\n Object.assign(fields, options.extraFields(req));\n }\n\n return fields;\n}\n\n// ── Express middleware ────────────────────────────────────────────────────────\n\n/**\n * Create an Express / Connect compatible middleware that replaces Morgan.\n */\nexport function createExpressMiddleware(\n logger: IBaseLogger,\n options: HttpLoggerOptions = {}\n): (req: IncomingRequest, res: OutgoingResponse, next: () => void) => void {\n const {\n skip,\n logBody,\n bodyMaxBytes,\n traceIdHeader = 'x-trace-id',\n requestLevel = 'debug',\n responseLevel = 'info',\n errorLevel = 'error',\n slowRequestThresholdMs = 1000,\n } = options;\n const nameSets = resolveNameSets(options);\n const traceHeaderName = traceIdHeader.toLowerCase();\n\n return function logixiaHttpMiddleware(\n req: IncomingRequest,\n res: OutgoingResponse,\n next: () => void\n ): void {\n if (skip?.(req)) {\n next();\n return;\n }\n\n const traceId = sanitizeIncomingId(req.headers?.[traceHeaderName]) ?? shortId();\n const startMs = Date.now();\n const baseFields = buildBaseFields(req, traceId, options, nameSets);\n\n // Log request start\n if (requestLevel !== 'silent') {\n fire(\n logger.logLevel(requestLevel, 'request started', {\n ...baseFields,\n ...(logBody && req.body ? { body: truncateBody(req.body, bodyMaxBytes) } : {}),\n })\n );\n }\n\n // Hook into the response 'finish' event — fires after headers + body are sent.\n // This is what Morgan gets wrong for slow requests (it uses 'close' which may\n // fire before the status code is set on some Node versions).\n //\n // BOTH 'finish' and 'close' fire on a normal response, so guard against\n // logging the completion twice (which would duplicate the completed entry and\n // the slow-request warning).\n let completed = false;\n const onFinish = (): void => {\n if (completed) return;\n completed = true;\n\n const duration = Date.now() - startMs;\n const status = res.statusCode ?? 0;\n const level = status >= 500 ? errorLevel : responseLevel;\n\n fire(\n logger.logLevel(level, 'request completed', {\n ...baseFields,\n statusCode: status,\n duration,\n })\n );\n\n if (duration > slowRequestThresholdMs) {\n fire(\n logger.warn('slow request detected', {\n ...baseFields,\n statusCode: status,\n duration,\n threshold: slowRequestThresholdMs,\n })\n );\n }\n };\n\n res.once?.('finish', onFinish);\n // Fallback: also covers a client that disconnects before 'finish' fires. The\n // `completed` guard ensures a normal finish+close pair logs only once.\n res.once?.('close', onFinish);\n\n next();\n };\n}\n\n// ── Fastify plugin ─────────────────────────────────────────────────────────────\n\nexport interface FastifyInstance {\n addHook: (name: string, fn: (req: unknown, reply: unknown, done: () => void) => void) => void;\n}\n\n/**\n * Create a Fastify plugin (a function you pass to `fastify.register()`).\n *\n * @example\n * ```ts\n * await fastify.register(createFastifyPlugin(logger, { slowRequestThresholdMs: 500 }));\n * ```\n */\nexport function createFastifyPlugin(logger: IBaseLogger, options: HttpLoggerOptions = {}) {\n const {\n skip,\n traceIdHeader = 'x-trace-id',\n requestLevel = 'debug',\n responseLevel = 'info',\n errorLevel = 'error',\n slowRequestThresholdMs = 1000,\n } = options;\n const nameSets = resolveNameSets(options);\n const traceHeaderName = traceIdHeader.toLowerCase();\n\n function logixiaFastifyPlugin(fastify: FastifyInstance, _opts: unknown, done: () => void): void {\n fastify.addHook('onRequest', (request: unknown, _reply: unknown, hookDone: () => void) => {\n const req = request as IncomingRequest & { _logixiaStart?: number; _logixiaId?: string };\n if (skip?.(req)) {\n hookDone();\n return;\n }\n\n const traceId = sanitizeIncomingId(req.headers?.[traceHeaderName]) ?? shortId();\n req._logixiaStart = Date.now();\n req._logixiaId = traceId;\n\n if (requestLevel !== 'silent') {\n fire(\n logger.logLevel(\n requestLevel,\n 'request started',\n buildBaseFields(req, traceId, options, nameSets)\n )\n );\n }\n hookDone();\n });\n\n fastify.addHook('onResponse', (request: unknown, reply: unknown, hookDone: () => void) => {\n const req = request as IncomingRequest & { _logixiaStart?: number; _logixiaId?: string };\n if (req._logixiaStart === undefined) {\n hookDone();\n return;\n }\n\n const rep = reply as { statusCode?: number };\n const duration = Date.now() - req._logixiaStart;\n const status = rep.statusCode ?? 0;\n const traceId = req._logixiaId ?? shortId();\n const level = status >= 500 ? errorLevel : responseLevel;\n const baseFields = buildBaseFields(req, traceId, options, nameSets);\n\n fire(\n logger.logLevel(level, 'request completed', {\n ...baseFields,\n statusCode: status,\n duration,\n })\n );\n\n if (duration > slowRequestThresholdMs) {\n fire(\n logger.warn('slow request detected', {\n traceId,\n url: baseFields.url,\n method: req.method,\n duration,\n threshold: slowRequestThresholdMs,\n })\n );\n }\n\n hookDone();\n });\n\n done();\n }\n\n return Object.assign(logixiaFastifyPlugin, {\n [Symbol.for('skip-override')]: true,\n [Symbol.for('fastify.display-name')]: 'logixia',\n });\n}\n\n// ── Internal helpers ──────────────────────────────────────────────────────────\n\n/**\n * Bound the captured body without turning it into a string, so the logger's\n * path-based redaction still sees the original keys. Oversized structures are\n * pruned (extra keys/items dropped, long strings cut) rather than serialized.\n */\nfunction truncateBody(body: unknown, maxBytes = 4096): unknown {\n if (typeof body === 'string') {\n return body.length > maxBytes ? body.slice(0, maxBytes) + '…[truncated]' : body;\n }\n if (body && typeof body === 'object') {\n const str = stringifyForLog(body);\n if (str.length <= maxBytes) return body;\n return pruneValue(body, { left: maxBytes }, 0, new Set());\n }\n return body;\n}\n\nconst TRUNCATED = '…[truncated]';\nconst MAX_PRUNE_DEPTH = 8;\n\nfunction pruneValue(\n value: unknown,\n budget: { left: number },\n depth: number,\n ancestors: Set<object>\n): unknown {\n if (typeof value === 'string') {\n const cut = value.slice(0, Math.max(0, budget.left));\n budget.left -= cut.length;\n return cut.length < value.length ? cut + TRUNCATED : cut;\n }\n if (value === null || typeof value !== 'object') {\n budget.left -= 8;\n return value;\n }\n if (value instanceof Date) {\n budget.left -= 24;\n return Number.isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString();\n }\n if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {\n budget.left -= 16;\n return `[Buffer ${value.byteLength} bytes]`;\n }\n if (value instanceof Error) return value;\n if (ancestors.has(value) || depth >= MAX_PRUNE_DEPTH) return TRUNCATED;\n ancestors.add(value);\n try {\n if (Array.isArray(value) || value instanceof Set || value instanceof Map) {\n const items: unknown[] = [];\n for (const item of value instanceof Map ? value.entries() : value) {\n if (budget.left <= 0) {\n items.push(TRUNCATED);\n break;\n }\n items.push(pruneValue(item, budget, depth + 1, ancestors));\n }\n return items;\n }\n const toJSON = (value as { toJSON?: unknown }).toJSON;\n if (typeof toJSON === 'function') {\n const json: unknown = toJSON.call(value);\n if (json !== value) return pruneValue(json, budget, depth + 1, ancestors);\n }\n const record: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n if (budget.left <= 0) {\n record['…'] = TRUNCATED;\n break;\n }\n budget.left -= k.length;\n record[k] = pruneValue(v, budget, depth + 1, ancestors);\n }\n return record;\n } catch {\n return TRUNCATED;\n } finally {\n ancestors.delete(value);\n }\n}\n"],"mappings":";;;;;AAsGA,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,8BAA8B;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAS,aAAa,UAA6B,OAAsC;AACvF,QAAO,IAAI,IAAI,CAAC,GAAG,UAAU,GAAI,SAAS,EAAE,CAAE,CAAC,KAAK,MAAM,EAAE,aAAa,CAAC,CAAC;;AAG7E,SAAS,WAAW,OAAuB;AACzC,KAAI;AACF,SAAO,mBAAmB,MAAM,QAAQ,OAAO,IAAI,CAAC;SAC9C;AACN,SAAO;;;;AAKX,SAAS,YAAY,KAAa,cAAmC;CACnE,MAAM,IAAI,IAAI,QAAQ,IAAI;AAC1B,KAAI,MAAM,GAAI,QAAO;CACrB,MAAM,SAAS,IAAI,QAAQ,KAAK,EAAE;CAClC,MAAM,QAAQ,IAAI,MAAM,IAAI,GAAG,WAAW,KAAK,SAAY,OAAO;CAClE,MAAM,OAAO,WAAW,KAAK,KAAK,IAAI,MAAM,OAAO;CACnD,MAAM,UAAU,MACb,MAAM,IAAI,CACV,KAAK,SAAS;EACb,MAAM,KAAK,KAAK,QAAQ,IAAI;EAC5B,MAAM,SAAS,OAAO,KAAK,OAAO,KAAK,MAAM,GAAG,GAAG;EACnD,MAAM,UAAU,WAAW,OAAO;EAClC,MAAM,UAAU,QAAQ,QAAQ,IAAI;EACpC,MAAM,QAAQ,YAAY,KAAK,UAAU,QAAQ,MAAM,GAAG,QAAQ,EAAE,aAAa;AACjF,SAAO,aAAa,IAAI,KAAK,IAAI,OAAO,KAAK,GAAG,OAAO,eAAe;GACtE,CACD,KAAK,IAAI;AACZ,QAAO,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,GAAG,UAAU;;AAGzC,SAAS,gBACP,SACA,WACyB;AACzB,KAAI,CAAC,QAAS,QAAO,EAAE;CACvB,MAAMA,MAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,QAAQ,EAAE;AAC5C,MAAI,MAAM,eAAe,MAAM,iBAAiB,MAAM,YAAa;AACnE,MAAI,KAAK,UAAU,IAAI,EAAE,aAAa,CAAC,GAAG,eAAe;;AAE3D,QAAO;;;AAIT,SAAS,KAAK,QAAuB;AACnC,KAAI,UAAU,OAAQ,OAA4B,UAAU,WAC1D,CAAC,OAA4B,OAAO,UAClC,cAAc,gCAAgC,MAAM,CACrD;;AAIL,SAAS,UAAkB;AAEzB,QAAO,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,GAAG;;AAQhD,SAAS,gBAAgB,SAAsC;AAC7D,QAAO;EACL,SAAS,aAAa,wBAAwB,QAAQ,cAAc;EACpE,aAAa,aAAa,6BAA6B,QAAQ,kBAAkB;EAClF;;AAGH,SAAS,gBACP,KACA,SACA,SACA,EAAE,SAAS,WAAW,aAAa,YACV;;CACzB,MAAMC,SAAkC;EACtC;EACA,wBAAQ,IAAI,kEAAQ,aAAa,KAAI;EACrC,KAAK,YAAY,IAAI,OAAO,KAAK,SAAS;EAC1C,IAAI,IAAI,sBAAM,IAAI,kEAAQ,kBAAiB;EAC3C,SAAS,gBAAgB,IAAI,SAAS,UAAU;EACjD;AAED,KAAI,QAAQ,YACV,QAAO,OAAO,QAAQ,QAAQ,YAAY,IAAI,CAAC;AAGjD,QAAO;;;;;AAQT,SAAgB,wBACd,QACA,UAA6B,EAAE,EAC0C;CACzE,MAAM,EACJ,MACA,SACA,cACA,gBAAgB,cAChB,eAAe,SACf,gBAAgB,QAChB,aAAa,SACb,yBAAyB,QACvB;CACJ,MAAM,WAAW,gBAAgB,QAAQ;CACzC,MAAM,kBAAkB,cAAc,aAAa;AAEnD,QAAO,SAAS,sBACd,KACA,KACA,MACM;;AACN,kDAAI,KAAO,IAAI,EAAE;AACf,SAAM;AACN;;EAGF,MAAM,UAAU,mCAAmB,IAAI,qEAAU,iBAAiB,IAAI,SAAS;EAC/E,MAAM,UAAU,KAAK,KAAK;EAC1B,MAAM,aAAa,gBAAgB,KAAK,SAAS,SAAS,SAAS;AAGnE,MAAI,iBAAiB,SACnB,MACE,OAAO,SAAS,cAAc,mBAAmB;GAC/C,GAAG;GACH,GAAI,WAAW,IAAI,OAAO,EAAE,MAAM,aAAa,IAAI,MAAM,aAAa,EAAE,GAAG,EAAE;GAC9E,CAAC,CACH;EAUH,IAAI,YAAY;EAChB,MAAM,iBAAuB;AAC3B,OAAI,UAAW;AACf,eAAY;GAEZ,MAAM,WAAW,KAAK,KAAK,GAAG;GAC9B,MAAM,SAAS,IAAI,cAAc;GACjC,MAAM,QAAQ,UAAU,MAAM,aAAa;AAE3C,QACE,OAAO,SAAS,OAAO,qBAAqB;IAC1C,GAAG;IACH,YAAY;IACZ;IACD,CAAC,CACH;AAED,OAAI,WAAW,uBACb,MACE,OAAO,KAAK,yBAAyB;IACnC,GAAG;IACH,YAAY;IACZ;IACA,WAAW;IACZ,CAAC,CACH;;AAIL,mBAAI,8DAAO,UAAU,SAAS;AAG9B,oBAAI,gEAAO,SAAS,SAAS;AAE7B,QAAM;;;;;;;;;;;AAkBV,SAAgB,oBAAoB,QAAqB,UAA6B,EAAE,EAAE;CACxF,MAAM,EACJ,MACA,gBAAgB,cAChB,eAAe,SACf,gBAAgB,QAChB,aAAa,SACb,yBAAyB,QACvB;CACJ,MAAM,WAAW,gBAAgB,QAAQ;CACzC,MAAM,kBAAkB,cAAc,aAAa;CAEnD,SAAS,qBAAqB,SAA0B,OAAgB,MAAwB;AAC9F,UAAQ,QAAQ,cAAc,SAAkB,QAAiB,aAAyB;;GACxF,MAAM,MAAM;AACZ,mDAAI,KAAO,IAAI,EAAE;AACf,cAAU;AACV;;GAGF,MAAM,UAAU,oCAAmB,IAAI,uEAAU,iBAAiB,IAAI,SAAS;AAC/E,OAAI,gBAAgB,KAAK,KAAK;AAC9B,OAAI,aAAa;AAEjB,OAAI,iBAAiB,SACnB,MACE,OAAO,SACL,cACA,mBACA,gBAAgB,KAAK,SAAS,SAAS,SAAS,CACjD,CACF;AAEH,aAAU;IACV;AAEF,UAAQ,QAAQ,eAAe,SAAkB,OAAgB,aAAyB;GACxF,MAAM,MAAM;AACZ,OAAI,IAAI,kBAAkB,QAAW;AACnC,cAAU;AACV;;GAGF,MAAM,MAAM;GACZ,MAAM,WAAW,KAAK,KAAK,GAAG,IAAI;GAClC,MAAM,SAAS,IAAI,cAAc;GACjC,MAAM,UAAU,IAAI,cAAc,SAAS;GAC3C,MAAM,QAAQ,UAAU,MAAM,aAAa;GAC3C,MAAM,aAAa,gBAAgB,KAAK,SAAS,SAAS,SAAS;AAEnE,QACE,OAAO,SAAS,OAAO,qBAAqB;IAC1C,GAAG;IACH,YAAY;IACZ;IACD,CAAC,CACH;AAED,OAAI,WAAW,uBACb,MACE,OAAO,KAAK,yBAAyB;IACnC;IACA,KAAK,WAAW;IAChB,QAAQ,IAAI;IACZ;IACA,WAAW;IACZ,CAAC,CACH;AAGH,aAAU;IACV;AAEF,QAAM;;AAGR,QAAO,OAAO,OAAO,sBAAsB;GACxC,OAAO,IAAI,gBAAgB,GAAG;GAC9B,OAAO,IAAI,uBAAuB,GAAG;EACvC,CAAC;;;;;;;AAUJ,SAAS,aAAa,MAAe,WAAW,MAAe;AAC7D,KAAI,OAAO,SAAS,SAClB,QAAO,KAAK,SAAS,WAAW,KAAK,MAAM,GAAG,SAAS,GAAG,iBAAiB;AAE7E,KAAI,QAAQ,OAAO,SAAS,UAAU;AAEpC,MADY,gBAAgB,KAAK,CACzB,UAAU,SAAU,QAAO;AACnC,SAAO,WAAW,MAAM,EAAE,MAAM,UAAU,EAAE,mBAAG,IAAI,KAAK,CAAC;;AAE3D,QAAO;;AAGT,MAAM,YAAY;AAClB,MAAM,kBAAkB;AAExB,SAAS,WACP,OACA,QACA,OACA,WACS;AACT,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,MAAM,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,OAAO,KAAK,CAAC;AACpD,SAAO,QAAQ,IAAI;AACnB,SAAO,IAAI,SAAS,MAAM,SAAS,MAAM,YAAY;;AAEvD,KAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/C,SAAO,QAAQ;AACf,SAAO;;AAET,KAAI,iBAAiB,MAAM;AACzB,SAAO,QAAQ;AACf,SAAO,OAAO,MAAM,MAAM,SAAS,CAAC,GAAG,iBAAiB,MAAM,aAAa;;AAE7E,KAAI,iBAAiB,eAAe,YAAY,OAAO,MAAM,EAAE;AAC7D,SAAO,QAAQ;AACf,SAAO,WAAW,MAAM,WAAW;;AAErC,KAAI,iBAAiB,MAAO,QAAO;AACnC,KAAI,UAAU,IAAI,MAAM,IAAI,SAAS,gBAAiB,QAAO;AAC7D,WAAU,IAAI,MAAM;AACpB,KAAI;AACF,MAAI,MAAM,QAAQ,MAAM,IAAI,iBAAiB,OAAO,iBAAiB,KAAK;GACxE,MAAMC,QAAmB,EAAE;AAC3B,QAAK,MAAM,QAAQ,iBAAiB,MAAM,MAAM,SAAS,GAAG,OAAO;AACjE,QAAI,OAAO,QAAQ,GAAG;AACpB,WAAM,KAAK,UAAU;AACrB;;AAEF,UAAM,KAAK,WAAW,MAAM,QAAQ,QAAQ,GAAG,UAAU,CAAC;;AAE5D,UAAO;;EAET,MAAM,SAAU,MAA+B;AAC/C,MAAI,OAAO,WAAW,YAAY;GAChC,MAAMC,OAAgB,OAAO,KAAK,MAAM;AACxC,OAAI,SAAS,MAAO,QAAO,WAAW,MAAM,QAAQ,QAAQ,GAAG,UAAU;;EAE3E,MAAMC,SAAkC,EAAE;AAC1C,OAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,EAAE;AAC1C,OAAI,MAAM,eAAe,MAAM,iBAAiB,MAAM,YAAa;AACnE,OAAI,OAAO,QAAQ,GAAG;AACpB,WAAO,OAAO;AACd;;AAEF,UAAO,QAAQ,EAAE;AACjB,UAAO,KAAK,WAAW,GAAG,QAAQ,QAAQ,GAAG,UAAU;;AAEzD,SAAO;SACD;AACN,SAAO;WACC;AACR,YAAU,OAAO,MAAM"}
|
package/dist/nest.d.mts
CHANGED
|
@@ -1,11 +1,478 @@
|
|
|
1
|
-
import "./transport.types-
|
|
2
|
-
import { E as TraceIdConfig } from "./index-
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
1
|
+
import "./transport.types-BMnurPcc.mjs";
|
|
2
|
+
import { E as TraceIdConfig, v as LogLevelString, y as LoggerConfig } from "./index-3mw5gaCw.mjs";
|
|
3
|
+
import { t as LogixiaLogger } from "./logitron-logger-BFDSO8Io.mjs";
|
|
4
|
+
import * as _nestjs_common0 from "@nestjs/common";
|
|
5
|
+
import { ArgumentsHost, CallHandler, ExceptionFilter, ExecutionContext, InjectionToken, LoggerService, MiddlewareConsumer, ModuleMetadata, NestInterceptor, NestMiddleware, NestModule, OptionalFactoryDependency, Type } from "@nestjs/common";
|
|
6
|
+
import { Observable } from "rxjs";
|
|
5
7
|
import { NextFunction, Request, Response } from "express";
|
|
6
8
|
|
|
7
|
-
//#region src/core/
|
|
9
|
+
//#region src/core/logitron-nestjs.service.d.ts
|
|
8
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Level names that already have proper typed implementations on LogixiaLoggerService.
|
|
13
|
+
* These are excluded from the auto-generated method type so we don't produce
|
|
14
|
+
* duplicate / conflicting signatures.
|
|
15
|
+
*/
|
|
16
|
+
type _ServiceBuiltinLevels = 'error' | 'warn' | 'info' | 'debug' | 'verbose' | 'trace' | 'log' | 'logLevel';
|
|
17
|
+
/**
|
|
18
|
+
* Mapped type that adds one method per *custom* level (i.e. every key in TLevels
|
|
19
|
+
* that is not already a built-in method on LogixiaLoggerService).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ServiceCustomLevelMethods<{ kafka: 3; mysql: 4; payment: 5 }>
|
|
23
|
+
* // → { kafka(msg, data?): Promise<void>; mysql(...): …; payment(...): … }
|
|
24
|
+
*/
|
|
25
|
+
type ServiceCustomLevelMethods<TLevels extends Record<string, number>> = { readonly [K in keyof TLevels as K extends _ServiceBuiltinLevels ? never : K & string]: (message: string, data?: Record<string, unknown>) => Promise<void> };
|
|
26
|
+
/**
|
|
27
|
+
* The return type of `LogixiaLoggerService.create<T>(config)`.
|
|
28
|
+
*
|
|
29
|
+
* When `T` carries `levelOptions.levels`, this type intersects
|
|
30
|
+
* `LogixiaLoggerService` with a method for every *custom* level so the IDE
|
|
31
|
+
* suggests `service.kafka(...)`, `service.payment(...)`, etc.
|
|
32
|
+
*/
|
|
33
|
+
type LogixiaServiceWithLevels<T extends LoggerConfig<Record<string, number>>> = T['levelOptions'] extends {
|
|
34
|
+
levels: infer L;
|
|
35
|
+
} ? L extends Record<string, number> ? LogixiaLoggerService & ServiceCustomLevelMethods<L> : LogixiaLoggerService : LogixiaLoggerService;
|
|
36
|
+
/**
|
|
37
|
+
* Helper — extracts custom level names from a config object's `levelOptions.levels`.
|
|
38
|
+
*/
|
|
39
|
+
type _ExtractCustomLevelNames<T> = T extends {
|
|
40
|
+
levelOptions?: {
|
|
41
|
+
levels?: infer L;
|
|
42
|
+
};
|
|
43
|
+
} ? L extends Record<string, number> ? Exclude<keyof L & string, _ServiceBuiltinLevels> : never : never;
|
|
44
|
+
/**
|
|
45
|
+
* Typed `LogixiaLoggerService` with autocomplete for custom levels.
|
|
46
|
+
*
|
|
47
|
+
* Accepts either:
|
|
48
|
+
* - **string union** of custom level names: `LogixiaServiceWith<'kafka' | 'payment'>`
|
|
49
|
+
* - **config object type** (via `typeof`): `LogixiaServiceWith<typeof logixiaConfig>`
|
|
50
|
+
*
|
|
51
|
+
* Define your config with `as const` once, derive the type everywhere:
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* // logger.config.ts — define once
|
|
56
|
+
* export const logixiaConfig = {
|
|
57
|
+
* levelOptions: {
|
|
58
|
+
* levels: { error: 0, warn: 1, info: 2, debug: 3, verbose: 4, kafka: 5, payment: 6 },
|
|
59
|
+
* },
|
|
60
|
+
* } as const;
|
|
61
|
+
* export type AppLogger = LogixiaServiceWith<typeof logixiaConfig>;
|
|
62
|
+
*
|
|
63
|
+
* // any.controller.ts — use everywhere, no casting
|
|
64
|
+
* constructor(private readonly logger: AppLogger) {}
|
|
65
|
+
* // this.logger.kafka('msg') ← fully typed
|
|
66
|
+
* // this.logger.payment('msg') ← fully typed
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* // Or use string union directly:
|
|
72
|
+
* type AppLogger = LogixiaServiceWith<'kafka' | 'payment'>;
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
type LogixiaServiceWith<T extends string | Record<string, unknown>> = LogixiaLoggerService & { readonly [K in T extends string ? Exclude<T, _ServiceBuiltinLevels> : _ExtractCustomLevelNames<T>]: (message: string, data?: Record<string, unknown>) => Promise<void> };
|
|
76
|
+
declare class LogixiaLoggerService implements LoggerService {
|
|
77
|
+
[K: string]: any;
|
|
78
|
+
private logger;
|
|
79
|
+
private context?;
|
|
80
|
+
private _mergedConfig;
|
|
81
|
+
constructor(config?: LoggerConfig);
|
|
82
|
+
private _createCustomLevelMethods;
|
|
83
|
+
/**
|
|
84
|
+
* NestJS `LoggerService.log` — void, string context.
|
|
85
|
+
* Maps internally to `info`.
|
|
86
|
+
*
|
|
87
|
+
* @example `logger.log('User signed up', 'AuthService')`
|
|
88
|
+
*/
|
|
89
|
+
log(message: unknown, context?: string): void;
|
|
90
|
+
/**
|
|
91
|
+
* Native async `info` — structured data, returns `Promise<void>`.
|
|
92
|
+
*
|
|
93
|
+
* @example `await logger.info('User signed up', { userId: 'u_abc' })`
|
|
94
|
+
*/
|
|
95
|
+
info(message: string, data?: Record<string, unknown>): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Native async overload — structured metadata, returns `Promise<void>`.
|
|
98
|
+
*
|
|
99
|
+
* @example `await logger.error(new Error('DB timeout'), { requestId: 'req_abc' })`
|
|
100
|
+
* @example `await logger.error('Login failed', { userId: 'u_abc' })`
|
|
101
|
+
*/
|
|
102
|
+
error(message: string | Error, data: Record<string, unknown>): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* NestJS compat overload — string trace + optional context, returns `void`.
|
|
105
|
+
*
|
|
106
|
+
* @example `logger.error('Something failed', err.stack, 'AuthService')`
|
|
107
|
+
*/
|
|
108
|
+
error(message: unknown, trace?: string, context?: string): void;
|
|
109
|
+
/**
|
|
110
|
+
* Native async overload — structured metadata, returns `Promise<void>`.
|
|
111
|
+
*
|
|
112
|
+
* @example `await logger.warn('Rate limit approaching', { userId: 'u_abc', count: 90 })`
|
|
113
|
+
*/
|
|
114
|
+
warn(message: string, data: Record<string, unknown>): Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* NestJS compat overload — optional context string, returns `void`.
|
|
117
|
+
*
|
|
118
|
+
* @example `logger.warn('Deprecated API used', 'PaymentService')`
|
|
119
|
+
*/
|
|
120
|
+
warn(message: unknown, context?: string): void;
|
|
121
|
+
/**
|
|
122
|
+
* Native async overload — structured metadata, returns `Promise<void>`.
|
|
123
|
+
*
|
|
124
|
+
* @example `await logger.debug('Cache miss', { key: 'user:abc', ttl: 300 })`
|
|
125
|
+
*/
|
|
126
|
+
debug(message: string, data: Record<string, unknown>): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* NestJS compat overload — optional context string, returns `void`.
|
|
129
|
+
*
|
|
130
|
+
* @example `logger.debug('Processing request', 'OrderService')`
|
|
131
|
+
*/
|
|
132
|
+
debug(message: unknown, context?: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Native async overload — structured metadata, returns `Promise<void>`.
|
|
135
|
+
* Maps to `trace` level internally.
|
|
136
|
+
*
|
|
137
|
+
* @example `await logger.verbose('Socket message received', { event: 'ping', size: 42 })`
|
|
138
|
+
*/
|
|
139
|
+
verbose(message: string, data: Record<string, unknown>): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* NestJS compat overload — optional context string, returns `void`.
|
|
142
|
+
*
|
|
143
|
+
* @example `logger.verbose('Connection established', 'WebSocketGateway')`
|
|
144
|
+
*/
|
|
145
|
+
verbose(message: unknown, context?: string): void;
|
|
146
|
+
/**
|
|
147
|
+
* Native async `trace` — lowest verbosity level, structured data.
|
|
148
|
+
*/
|
|
149
|
+
trace(message: string, data?: Record<string, unknown>): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Log at any named level with structured data.
|
|
152
|
+
*/
|
|
153
|
+
logLevel(level: string, message: string, data?: Record<string, unknown>): Promise<void>;
|
|
154
|
+
time(label: string): void;
|
|
155
|
+
timeEnd(label: string): Promise<number | undefined>;
|
|
156
|
+
timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T>;
|
|
157
|
+
setContext(context: string): void;
|
|
158
|
+
getContext(): string | undefined;
|
|
159
|
+
setLevel(level: LogLevelString): void;
|
|
160
|
+
getLevel(): LogLevelString;
|
|
161
|
+
child(context: string, data?: Record<string, unknown>): LogixiaLoggerService;
|
|
162
|
+
getCurrentTraceId(): string | undefined;
|
|
163
|
+
/** Returns the AsyncLocalStorage key currently used to store the trace ID. */
|
|
164
|
+
get traceContextKey(): string;
|
|
165
|
+
close(): Promise<void>;
|
|
166
|
+
static create<T extends LoggerConfig<Record<string, number>>>(config?: T): LogixiaServiceWithLevels<T>;
|
|
167
|
+
getLogger(): LogixiaLogger;
|
|
168
|
+
private setContextIfProvided;
|
|
169
|
+
private formatMessage;
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/core/kafka-trace.interceptor.d.ts
|
|
173
|
+
/**
|
|
174
|
+
* Observable counters exposed for monitoring Kafka consumer trace hygiene.
|
|
175
|
+
*
|
|
176
|
+
* Usage:
|
|
177
|
+
* import { KafkaTraceInterceptor } from 'logixia/nest';
|
|
178
|
+
* setInterval(() => {
|
|
179
|
+
* myMetrics.gauge('kafka.trace.dropped', KafkaTraceInterceptor.metrics.dropped);
|
|
180
|
+
* }, 10_000);
|
|
181
|
+
*/
|
|
182
|
+
interface KafkaTraceMetrics {
|
|
183
|
+
/** Messages processed where a traceId was successfully resolved (body/header/ALS). */
|
|
184
|
+
accepted: number;
|
|
185
|
+
/** Messages handled without any traceId — `requireTraceId: false`. */
|
|
186
|
+
acceptedWithoutTrace: number;
|
|
187
|
+
/** Messages dropped because `requireTraceId: true` and no traceId was found. */
|
|
188
|
+
dropped: number;
|
|
189
|
+
}
|
|
190
|
+
declare class KafkaTraceInterceptor implements NestInterceptor {
|
|
191
|
+
private readonly config?;
|
|
192
|
+
private readonly requireTraceId;
|
|
193
|
+
/**
|
|
194
|
+
* Process-wide counters. Readable from anywhere — scrape into your metrics
|
|
195
|
+
* system (Prometheus, Datadog, CloudWatch) on an interval.
|
|
196
|
+
*/
|
|
197
|
+
static readonly metrics: KafkaTraceMetrics;
|
|
198
|
+
/** Reset counters (tests). */
|
|
199
|
+
static resetMetrics(): void;
|
|
200
|
+
private readonly ctx;
|
|
201
|
+
/**
|
|
202
|
+
* @param config - TraceIdConfig options (extractor keys, contextKey, etc.)
|
|
203
|
+
* @param requireTraceId - When true, messages with no traceId are silently skipped
|
|
204
|
+
* (EMPTY Observable — message is ack'd, consumer stays alive).
|
|
205
|
+
* A WARN is logged AND `KafkaTraceInterceptor.metrics.dropped`
|
|
206
|
+
* increments so the missing traceId is observable end-to-end.
|
|
207
|
+
* Default: false (handler runs without trace context).
|
|
208
|
+
*/
|
|
209
|
+
constructor(config?: TraceIdConfig | undefined, requireTraceId?: boolean);
|
|
210
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/core/websocket-trace.interceptor.d.ts
|
|
214
|
+
declare class WebSocketTraceInterceptor implements NestInterceptor {
|
|
215
|
+
private readonly config?;
|
|
216
|
+
private readonly ctx;
|
|
217
|
+
constructor(config?: TraceIdConfig | undefined);
|
|
218
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/core/logitron-logger.module.d.ts
|
|
222
|
+
declare const LOGIXIA_LOGGER_CONFIG = "LOGIXIA_LOGGER_CONFIG";
|
|
223
|
+
declare const LOGIXIA_TRACE_CONFIG = "TRACE_CONFIG";
|
|
224
|
+
declare const LOGIXIA_LOGGER_PREFIX = "LOGIXIA_LOGGER_";
|
|
225
|
+
interface LogixiaAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
|
226
|
+
useExisting?: Type<LogixiaOptionsFactory>;
|
|
227
|
+
useClass?: Type<LogixiaOptionsFactory>;
|
|
228
|
+
useFactory?: (...args: unknown[]) => Promise<Partial<LoggerConfig>> | Partial<LoggerConfig>;
|
|
229
|
+
inject?: Array<InjectionToken | OptionalFactoryDependency>;
|
|
230
|
+
}
|
|
231
|
+
interface LogixiaOptionsFactory {
|
|
232
|
+
createLogixiaOptions(): Promise<Partial<LoggerConfig>> | Partial<LoggerConfig>;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Logixia Logger Module for NestJS dependency injection
|
|
236
|
+
*/
|
|
237
|
+
declare class LogixiaLoggerModule implements NestModule {
|
|
238
|
+
private config;
|
|
239
|
+
private static loggerConfig;
|
|
240
|
+
/**
|
|
241
|
+
* @internal Backing field for the global logger. Do not read or write
|
|
242
|
+
* directly — use {@link getGlobalLogger} / {@link _setGlobalLogger} which
|
|
243
|
+
* enforce single-init semantics.
|
|
244
|
+
*/
|
|
245
|
+
static _globalLogger: LogixiaLoggerService | null;
|
|
246
|
+
/**
|
|
247
|
+
* @internal Set the global logger exactly once.
|
|
248
|
+
*
|
|
249
|
+
* Called from the module's forRoot / forRootAsync factory. If the module is
|
|
250
|
+
* initialised more than once in the same process (nested DI context, test
|
|
251
|
+
* harness creating multiple apps, hot reload, etc.) a warning is written to
|
|
252
|
+
* stderr and the first logger wins — silently overwriting would allow the
|
|
253
|
+
* newer instance's transport config to replace the live one while the old
|
|
254
|
+
* one is still being used by registered shutdown hooks, decorators, etc.
|
|
255
|
+
*
|
|
256
|
+
* Use {@link _resetGlobalLogger} in tests to reset between runs.
|
|
257
|
+
*/
|
|
258
|
+
static _setGlobalLogger(service: LogixiaLoggerService): void;
|
|
259
|
+
/** @internal Clear the global logger. Tests only. */
|
|
260
|
+
static _resetGlobalLogger(): void;
|
|
261
|
+
/**
|
|
262
|
+
* Returns the global LogixiaLoggerService instance that was created when the
|
|
263
|
+
* module booted. Useful for logging outside of NestJS DI — utility functions,
|
|
264
|
+
* plain scripts, decorators — without injecting the service everywhere.
|
|
265
|
+
*
|
|
266
|
+
* Returns `null` if called before `LogixiaLoggerModule.forRoot[Async]()` has
|
|
267
|
+
* been initialised (i.e. before the NestJS app has started).
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```ts
|
|
271
|
+
* // some-util.ts
|
|
272
|
+
* import { LogixiaLoggerModule } from 'logixia/nest';
|
|
273
|
+
*
|
|
274
|
+
* export function doSomething() {
|
|
275
|
+
* LogixiaLoggerModule.getGlobalLogger()?.info('doing something');
|
|
276
|
+
* }
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
static getGlobalLogger(): LogixiaLoggerService | null;
|
|
280
|
+
configure(consumer: MiddlewareConsumer): void;
|
|
281
|
+
/**
|
|
282
|
+
* Configure the module with synchronous options
|
|
283
|
+
*/
|
|
284
|
+
static forRoot(config?: Partial<LoggerConfig>): {
|
|
285
|
+
module: typeof LogixiaLoggerModule;
|
|
286
|
+
providers: ({
|
|
287
|
+
provide: string;
|
|
288
|
+
useValue: Partial<LoggerConfig<Record<string, number>>>;
|
|
289
|
+
useFactory?: never;
|
|
290
|
+
inject?: never;
|
|
291
|
+
} | {
|
|
292
|
+
provide: string;
|
|
293
|
+
useValue: TraceIdConfig;
|
|
294
|
+
useFactory?: never;
|
|
295
|
+
inject?: never;
|
|
296
|
+
} | {
|
|
297
|
+
provide: typeof LogixiaLoggerService;
|
|
298
|
+
useFactory: (loggerConfig: Partial<LoggerConfig>) => LogixiaLoggerService;
|
|
299
|
+
inject: string[];
|
|
300
|
+
useValue?: never;
|
|
301
|
+
} | {
|
|
302
|
+
provide: typeof KafkaTraceInterceptor;
|
|
303
|
+
useFactory: (traceConfig: TraceIdConfig | undefined) => KafkaTraceInterceptor;
|
|
304
|
+
inject: string[];
|
|
305
|
+
useValue?: never;
|
|
306
|
+
} | {
|
|
307
|
+
provide: typeof WebSocketTraceInterceptor;
|
|
308
|
+
useFactory: (traceConfig: TraceIdConfig | undefined) => WebSocketTraceInterceptor;
|
|
309
|
+
inject: string[];
|
|
310
|
+
useValue?: never;
|
|
311
|
+
})[];
|
|
312
|
+
exports: (string | typeof LogixiaLoggerService | typeof KafkaTraceInterceptor | typeof WebSocketTraceInterceptor)[];
|
|
313
|
+
global: boolean;
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* Configure the module with asynchronous options
|
|
317
|
+
*/
|
|
318
|
+
static forRootAsync(options: LogixiaAsyncOptions): {
|
|
319
|
+
module: typeof LogixiaLoggerModule;
|
|
320
|
+
imports: (Type<any> | _nestjs_common0.DynamicModule | Promise<_nestjs_common0.DynamicModule> | _nestjs_common0.ForwardReference<any>)[];
|
|
321
|
+
providers: ({
|
|
322
|
+
provide: string;
|
|
323
|
+
useFactory: (...args: unknown[]) => Promise<Partial<LoggerConfig>> | Partial<LoggerConfig>;
|
|
324
|
+
inject: (InjectionToken | OptionalFactoryDependency)[];
|
|
325
|
+
} | {
|
|
326
|
+
provide: string;
|
|
327
|
+
useFactory: (optionsFactory: LogixiaOptionsFactory) => Promise<Partial<LoggerConfig<Record<string, number>>>>;
|
|
328
|
+
inject: Type<LogixiaOptionsFactory>[];
|
|
329
|
+
} | {
|
|
330
|
+
provide: Type<LogixiaOptionsFactory>;
|
|
331
|
+
useClass: Type<LogixiaOptionsFactory>;
|
|
332
|
+
} | {
|
|
333
|
+
provide: string;
|
|
334
|
+
useFactory: (loggerConfig: Partial<LoggerConfig>) => TraceIdConfig;
|
|
335
|
+
inject: string[];
|
|
336
|
+
} | {
|
|
337
|
+
provide: typeof LogixiaLoggerService;
|
|
338
|
+
useFactory: (loggerConfig: Partial<LoggerConfig>) => LogixiaLoggerService;
|
|
339
|
+
inject: string[];
|
|
340
|
+
} | {
|
|
341
|
+
provide: typeof KafkaTraceInterceptor;
|
|
342
|
+
useFactory: (traceConfig: TraceIdConfig | undefined) => KafkaTraceInterceptor;
|
|
343
|
+
inject: string[];
|
|
344
|
+
} | {
|
|
345
|
+
provide: typeof WebSocketTraceInterceptor;
|
|
346
|
+
useFactory: (traceConfig: TraceIdConfig | undefined) => WebSocketTraceInterceptor;
|
|
347
|
+
inject: string[];
|
|
348
|
+
})[];
|
|
349
|
+
exports: (string | typeof LogixiaLoggerService | typeof KafkaTraceInterceptor | typeof WebSocketTraceInterceptor)[];
|
|
350
|
+
global: boolean;
|
|
351
|
+
};
|
|
352
|
+
/**
|
|
353
|
+
* Create feature-specific logger instances
|
|
354
|
+
*/
|
|
355
|
+
static forFeature(context: string): {
|
|
356
|
+
module: typeof LogixiaLoggerModule;
|
|
357
|
+
providers: {
|
|
358
|
+
provide: string;
|
|
359
|
+
useFactory: (baseLogger: LogixiaLoggerService) => LogixiaLoggerService;
|
|
360
|
+
inject: (typeof LogixiaLoggerService)[];
|
|
361
|
+
}[];
|
|
362
|
+
exports: string[];
|
|
363
|
+
};
|
|
364
|
+
private static createAsyncProviders;
|
|
365
|
+
private static createAsyncOptionsProvider;
|
|
366
|
+
}
|
|
367
|
+
//#endregion
|
|
368
|
+
//#region src/core/nestjs-extras.d.ts
|
|
369
|
+
/**
|
|
370
|
+
* Inject the Logixia logger registered in the current NestJS DI container.
|
|
371
|
+
*
|
|
372
|
+
* Equivalent to `@Inject(LOGIXIA_LOGGER_TOKEN)` but without needing to import
|
|
373
|
+
* the internal token constant yourself.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```ts
|
|
377
|
+
* constructor(@InjectLogger() private readonly logger: LogixiaLoggerService) {}
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
declare const InjectLogger: () => ParameterDecorator;
|
|
381
|
+
interface LogMethodOptions {
|
|
382
|
+
/**
|
|
383
|
+
* Log level to use for entry / exit messages.
|
|
384
|
+
* @default 'debug'
|
|
385
|
+
*/
|
|
386
|
+
level?: LogLevelString;
|
|
387
|
+
/**
|
|
388
|
+
* Whether to log the arguments passed to the method.
|
|
389
|
+
* Disable for high-throughput hot paths.
|
|
390
|
+
* @default true
|
|
391
|
+
*/
|
|
392
|
+
logArgs?: boolean;
|
|
393
|
+
/**
|
|
394
|
+
* Whether to log the return value of the method.
|
|
395
|
+
* @default false
|
|
396
|
+
*/
|
|
397
|
+
logResult?: boolean;
|
|
398
|
+
/**
|
|
399
|
+
* Whether to log error stack traces for thrown errors.
|
|
400
|
+
* @default true
|
|
401
|
+
*/
|
|
402
|
+
logErrors?: boolean;
|
|
403
|
+
/**
|
|
404
|
+
* Custom label used in log messages instead of the auto-detected class.method name.
|
|
405
|
+
*/
|
|
406
|
+
label?: string;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Method decorator that auto-logs entry, exit, duration, and errors.
|
|
410
|
+
*
|
|
411
|
+
* Preserves the original method's sync/async contract: a synchronous method
|
|
412
|
+
* stays synchronous (returns its value directly, with logs emitted
|
|
413
|
+
* fire-and-forget), and an async method is awaited so exit/error logs reflect
|
|
414
|
+
* the resolved result. Attaches to the logger found on the class instance via a
|
|
415
|
+
* `logger` property (the conventional NestJS name).
|
|
416
|
+
*
|
|
417
|
+
* @example
|
|
418
|
+
* ```ts
|
|
419
|
+
* @LogMethod({ level: 'info', logArgs: true })
|
|
420
|
+
* async processPayment(orderId: string): Promise<void> { … }
|
|
421
|
+
* ```
|
|
422
|
+
*/
|
|
423
|
+
declare function LogMethod(options?: LogMethodOptions): MethodDecorator;
|
|
424
|
+
/**
|
|
425
|
+
* Global NestJS exception filter that converts any exception into the standard
|
|
426
|
+
* `LogixiaErrorResponse` wire format and logs it with full request context.
|
|
427
|
+
*
|
|
428
|
+
* Handles three exception types in priority order:
|
|
429
|
+
* 1. `LogixiaException` — typed fields used directly
|
|
430
|
+
* 2. NestJS `HttpException` — status + message extracted
|
|
431
|
+
* 3. Unknown / plain Error — falls back to 500 `server_error`
|
|
432
|
+
*
|
|
433
|
+
* **Debug block (off by default):**
|
|
434
|
+
* Responses never include the `debug` block (stack trace, service, duration)
|
|
435
|
+
* unless `exposeDebug: true` is passed in the filter options. The underlying
|
|
436
|
+
* `cause` is only added when `exposeCause: true` is also set. Enable explicitly
|
|
437
|
+
* for local development only, e.g.
|
|
438
|
+
* `new LogixiaExceptionFilter(logger, undefined, { exposeDebug: process.env.NODE_ENV === 'development' })`.
|
|
439
|
+
*
|
|
440
|
+
* **Trace ID headers:**
|
|
441
|
+
* `X-Trace-ID` and `X-Request-ID` are echoed back on every error response so
|
|
442
|
+
* clients can correlate with server logs.
|
|
443
|
+
*
|
|
444
|
+
* **Retry-After header:**
|
|
445
|
+
* Automatically added for `429` responses (`Retry-After: 60`).
|
|
446
|
+
*
|
|
447
|
+
* Register in `main.ts`:
|
|
448
|
+
* ```ts
|
|
449
|
+
* const logger = app.get(LogixiaLoggerService);
|
|
450
|
+
* app.useGlobalFilters(new LogixiaExceptionFilter(logger));
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
interface LogixiaExceptionFilterOptions {
|
|
454
|
+
/**
|
|
455
|
+
* Include the `debug` block (stack trace, service, duration) in error responses.
|
|
456
|
+
* @default false
|
|
457
|
+
*/
|
|
458
|
+
exposeDebug?: boolean;
|
|
459
|
+
/**
|
|
460
|
+
* Include `debug.cause` as well. Requires `exposeDebug`.
|
|
461
|
+
* @default false
|
|
462
|
+
*/
|
|
463
|
+
exposeCause?: boolean;
|
|
464
|
+
}
|
|
465
|
+
/** Injection token for {@link LogixiaExceptionFilterOptions}. */
|
|
466
|
+
declare const LOGIXIA_EXCEPTION_FILTER_OPTIONS = "LOGIXIA_EXCEPTION_FILTER_OPTIONS";
|
|
467
|
+
declare class LogixiaExceptionFilter implements ExceptionFilter {
|
|
468
|
+
private readonly _logger;
|
|
469
|
+
private readonly _traceConfig;
|
|
470
|
+
private readonly _options;
|
|
471
|
+
constructor(logger?: LogixiaLoggerService, loggerConfig?: Partial<LoggerConfig>, options?: LogixiaExceptionFilterOptions);
|
|
472
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
473
|
+
}
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/core/trace.middleware.d.ts
|
|
9
476
|
declare global {
|
|
10
477
|
namespace Express {
|
|
11
478
|
interface Request {
|
|
@@ -20,5 +487,13 @@ declare class TraceMiddleware implements NestMiddleware {
|
|
|
20
487
|
use(req: Request, res: Response, next: NextFunction): void;
|
|
21
488
|
}
|
|
22
489
|
//#endregion
|
|
23
|
-
|
|
490
|
+
//#region src/nest.d.ts
|
|
491
|
+
/**
|
|
492
|
+
* Create a new Logixia logger service for NestJS
|
|
493
|
+
* @param config - Logger configuration
|
|
494
|
+
* @returns LogixiaLoggerService instance
|
|
495
|
+
*/
|
|
496
|
+
declare function createLoggerService(config?: Partial<LoggerConfig>): LogixiaLoggerService;
|
|
497
|
+
//#endregion
|
|
498
|
+
export { InjectLogger, KafkaTraceInterceptor, LOGIXIA_EXCEPTION_FILTER_OPTIONS, LOGIXIA_LOGGER_CONFIG, LOGIXIA_LOGGER_PREFIX, LOGIXIA_TRACE_CONFIG, LogMethod, type LogMethodOptions, LogixiaAsyncOptions, LogixiaExceptionFilter, type LogixiaExceptionFilterOptions, LogixiaLoggerModule, LogixiaLoggerService, LogixiaOptionsFactory, LogixiaServiceWith, LogixiaServiceWithLevels, TraceMiddleware, WebSocketTraceInterceptor, createLoggerService };
|
|
24
499
|
//# sourceMappingURL=nest.d.mts.map
|
package/dist/nest.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nest.d.mts","names":[],"sources":["../src/core/trace.middleware.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"nest.d.mts","names":[],"sources":["../src/core/logitron-nestjs.service.ts","../src/core/kafka-trace.interceptor.ts","../src/core/websocket-trace.interceptor.ts","../src/core/logitron-logger.module.ts","../src/core/nestjs-extras.ts","../src/core/trace.middleware.ts","../src/nest.ts"],"sourcesContent":[],"mappings":"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"}
|