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.
Files changed (176) hide show
  1. package/README.md +81 -16
  2. package/dist/{async-context-CwBNMm2E.mjs → async-context-BzJrbfQs.mjs} +8 -4
  3. package/dist/async-context-BzJrbfQs.mjs.map +1 -0
  4. package/dist/{async-context-CSHWoHMB.js → async-context-DyQQYq3I.js} +8 -4
  5. package/dist/async-context-DyQQYq3I.js.map +1 -0
  6. package/dist/{azure-monitor.transport-CG7ClkYj.d.ts → azure-monitor.transport-Cn_3Usvu.d.ts} +2 -2
  7. package/dist/{azure-monitor.transport-CG7ClkYj.d.ts.map → azure-monitor.transport-Cn_3Usvu.d.ts.map} +1 -1
  8. package/dist/{azure-monitor.transport-Cm74kXZS.d.mts → azure-monitor.transport-D7Da4RAb.d.mts} +2 -2
  9. package/dist/{azure-monitor.transport-Cm74kXZS.d.mts.map → azure-monitor.transport-D7Da4RAb.d.mts.map} +1 -1
  10. package/dist/azure.d.mts +2 -2
  11. package/dist/azure.d.ts +2 -2
  12. package/dist/browser.d.mts +9 -0
  13. package/dist/browser.d.mts.map +1 -1
  14. package/dist/browser.d.ts +9 -0
  15. package/dist/browser.d.ts.map +1 -1
  16. package/dist/browser.js +43 -12
  17. package/dist/browser.js.map +1 -1
  18. package/dist/browser.mjs +43 -12
  19. package/dist/browser.mjs.map +1 -1
  20. package/dist/{logitron-logger.module-Bs3XYZgF.mjs → builder-0gEzRopq.mjs} +383 -1021
  21. package/dist/builder-0gEzRopq.mjs.map +1 -0
  22. package/dist/{logitron-logger.module-BsDZR5BH.js → builder-D44QktIo.js} +411 -1155
  23. package/dist/builder-D44QktIo.js.map +1 -0
  24. package/dist/cli/index.js +130 -65
  25. package/dist/cloudwatch.d.mts +2 -2
  26. package/dist/cloudwatch.d.ts +2 -2
  27. package/dist/cloudwatch.js +2 -1
  28. package/dist/cloudwatch.mjs +2 -1
  29. package/dist/{cloudwatch.transport-pNBNWJLt.js → cloudwatch.transport-BO1ahxw-.js} +26 -19
  30. package/dist/cloudwatch.transport-BO1ahxw-.js.map +1 -0
  31. package/dist/{cloudwatch.transport-yBwm4OeO.mjs → cloudwatch.transport-DZ6ies7g.mjs} +26 -19
  32. package/dist/cloudwatch.transport-DZ6ies7g.mjs.map +1 -0
  33. package/dist/{cloudwatch.transport-CGHY4buE.d.mts → cloudwatch.transport-DsEq633o.d.ts} +9 -2
  34. package/dist/cloudwatch.transport-DsEq633o.d.ts.map +1 -0
  35. package/dist/{cloudwatch.transport-DK_8cKd3.d.ts → cloudwatch.transport-UlyZFWMn.d.mts} +9 -2
  36. package/dist/cloudwatch.transport-UlyZFWMn.d.mts.map +1 -0
  37. package/dist/{coerce.utils-BN980Y9V.js → coerce.utils-B-LilukN.js} +1 -1
  38. package/dist/{coerce.utils-BN980Y9V.js.map → coerce.utils-B-LilukN.js.map} +1 -1
  39. package/dist/{coerce.utils-Dxv4_ikC.mjs → coerce.utils-Cu6IUTgX.mjs} +1 -1
  40. package/dist/{coerce.utils-Dxv4_ikC.mjs.map → coerce.utils-Cu6IUTgX.mjs.map} +1 -1
  41. package/dist/correlation.d.mts +24 -6
  42. package/dist/correlation.d.mts.map +1 -1
  43. package/dist/correlation.d.ts +24 -6
  44. package/dist/correlation.d.ts.map +1 -1
  45. package/dist/correlation.js +41 -16
  46. package/dist/correlation.js.map +1 -1
  47. package/dist/correlation.mjs +41 -16
  48. package/dist/correlation.mjs.map +1 -1
  49. package/dist/entry-fields-BJkuPnIg.mjs +30 -0
  50. package/dist/entry-fields-BJkuPnIg.mjs.map +1 -0
  51. package/dist/entry-fields-BvOLFlJ4.js +36 -0
  52. package/dist/entry-fields-BvOLFlJ4.js.map +1 -0
  53. package/dist/gcp.d.mts +2 -2
  54. package/dist/gcp.d.ts +2 -2
  55. package/dist/gcp.js +2 -1
  56. package/dist/gcp.mjs +2 -1
  57. package/dist/{gcp.transport-BOhkVKlI.d.ts → gcp.transport-B7oc97sG.d.ts} +2 -2
  58. package/dist/gcp.transport-B7oc97sG.d.ts.map +1 -0
  59. package/dist/{gcp.transport-SHdtc1n0.js → gcp.transport-CHV8MGgz.js} +5 -5
  60. package/dist/gcp.transport-CHV8MGgz.js.map +1 -0
  61. package/dist/{gcp.transport-BJuNRFe_.mjs → gcp.transport-DBX5TPIy.mjs} +5 -5
  62. package/dist/gcp.transport-DBX5TPIy.mjs.map +1 -0
  63. package/dist/{gcp.transport-Bi6CUqZR.d.mts → gcp.transport-GrJurKo8.d.mts} +2 -2
  64. package/dist/gcp.transport-GrJurKo8.d.mts.map +1 -0
  65. package/dist/{index-BPKwwERo.d.mts → index-3mw5gaCw.d.mts} +2 -2
  66. package/dist/{index-BPKwwERo.d.mts.map → index-3mw5gaCw.d.mts.map} +1 -1
  67. package/dist/{index-C1X-TE5I.d.ts → index-DhIoploR.d.ts} +2 -2
  68. package/dist/{index-C1X-TE5I.d.ts.map → index-DhIoploR.d.ts.map} +1 -1
  69. package/dist/index.d.mts +99 -137
  70. package/dist/index.d.mts.map +1 -1
  71. package/dist/index.d.ts +99 -137
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +146 -510
  74. package/dist/index.js.map +1 -1
  75. package/dist/index.mjs +99 -431
  76. package/dist/index.mjs.map +1 -1
  77. package/dist/logitron-logger-BFDSO8Io.d.mts +257 -0
  78. package/dist/logitron-logger-BFDSO8Io.d.mts.map +1 -0
  79. package/dist/logitron-logger-UVeN-mQm.d.ts +257 -0
  80. package/dist/logitron-logger-UVeN-mQm.d.ts.map +1 -0
  81. package/dist/middleware.d.mts +11 -4
  82. package/dist/middleware.d.mts.map +1 -1
  83. package/dist/middleware.d.ts +11 -4
  84. package/dist/middleware.d.ts.map +1 -1
  85. package/dist/middleware.js +148 -24
  86. package/dist/middleware.js.map +1 -1
  87. package/dist/middleware.mjs +148 -24
  88. package/dist/middleware.mjs.map +1 -1
  89. package/dist/nest.d.mts +481 -6
  90. package/dist/nest.d.mts.map +1 -1
  91. package/dist/nest.d.ts +481 -6
  92. package/dist/nest.d.ts.map +1 -1
  93. package/dist/nest.js +884 -13
  94. package/dist/nest.js.map +1 -0
  95. package/dist/nest.mjs +863 -6
  96. package/dist/nest.mjs.map +1 -0
  97. package/dist/{search-CFQRISUv.mjs → search-BFbu_oVn.mjs} +35 -12
  98. package/dist/search-BFbu_oVn.mjs.map +1 -0
  99. package/dist/{search-njTI2hdo.js → search-C4V0OMEn.js} +35 -12
  100. package/dist/search-C4V0OMEn.js.map +1 -0
  101. package/dist/{search-DujhR94O.d.ts → search-CNh9uVwR.d.ts} +2 -3
  102. package/dist/search-CNh9uVwR.d.ts.map +1 -0
  103. package/dist/{search-CQOo-Iq6.d.mts → search-DXANsQ2n.d.mts} +2 -3
  104. package/dist/search-DXANsQ2n.d.mts.map +1 -0
  105. package/dist/search.d.mts +3 -3
  106. package/dist/search.d.ts +3 -3
  107. package/dist/search.js +2 -2
  108. package/dist/search.mjs +2 -2
  109. package/dist/slack.transport-BZzoXDPn.d.mts +112 -0
  110. package/dist/slack.transport-BZzoXDPn.d.mts.map +1 -0
  111. package/dist/slack.transport-BfJRouB2.js +654 -0
  112. package/dist/slack.transport-BfJRouB2.js.map +1 -0
  113. package/dist/slack.transport-Bzw7sucI.mjs +643 -0
  114. package/dist/slack.transport-Bzw7sucI.mjs.map +1 -0
  115. package/dist/slack.transport-CXjQYOes.d.ts +112 -0
  116. package/dist/slack.transport-CXjQYOes.d.ts.map +1 -0
  117. package/dist/testing.d.mts +2 -2
  118. package/dist/testing.d.ts +2 -2
  119. package/dist/trace.utils-CvXgaiQT.mjs +278 -0
  120. package/dist/trace.utils-CvXgaiQT.mjs.map +1 -0
  121. package/dist/trace.utils-DpBOLDtq.js +358 -0
  122. package/dist/trace.utils-DpBOLDtq.js.map +1 -0
  123. package/dist/{transport.manager-D6KL3t9V.mjs → transport.manager-BiuCAJd2.mjs} +526 -160
  124. package/dist/transport.manager-BiuCAJd2.mjs.map +1 -0
  125. package/dist/{transport.manager-BivyUTtU.js → transport.manager-CXbKKdVE.js} +537 -159
  126. package/dist/transport.manager-CXbKKdVE.js.map +1 -0
  127. package/dist/{transport.types-B5vcyv2k.d.mts → transport.types-BMnurPcc.d.mts} +23 -3
  128. package/dist/transport.types-BMnurPcc.d.mts.map +1 -0
  129. package/dist/{transport.types-BuY84TwO.d.ts → transport.types-CGoHpWQR.d.ts} +23 -3
  130. package/dist/transport.types-CGoHpWQR.d.ts.map +1 -0
  131. package/dist/transports.d.mts +50 -20
  132. package/dist/transports.d.mts.map +1 -1
  133. package/dist/transports.d.ts +50 -20
  134. package/dist/transports.d.ts.map +1 -1
  135. package/dist/transports.js +9 -5
  136. package/dist/transports.mjs +8 -6
  137. package/dist/worker-entry.js +307 -55
  138. package/dist/worker.d.mts +2 -2
  139. package/dist/worker.d.ts +2 -2
  140. package/dist/worker.js +1 -1
  141. package/dist/worker.mjs +1 -1
  142. package/dist/{worker.transport-CfH6z37Z.js → worker.transport-BdPxu3PN.js} +1 -1
  143. package/dist/{worker.transport-CfH6z37Z.js.map → worker.transport-BdPxu3PN.js.map} +1 -1
  144. package/dist/{worker.transport-mKXgNK0L.d.ts → worker.transport-CdzSZJmw.d.ts} +2 -2
  145. package/dist/{worker.transport-mKXgNK0L.d.ts.map → worker.transport-CdzSZJmw.d.ts.map} +1 -1
  146. package/dist/{worker.transport-BP8Q28Z2.d.mts → worker.transport-JvGPog3C.d.mts} +2 -2
  147. package/dist/{worker.transport-BP8Q28Z2.d.mts.map → worker.transport-JvGPog3C.d.mts.map} +1 -1
  148. package/dist/{worker.transport-BhMyvqQ2.mjs → worker.transport-lPxO2MVL.mjs} +1 -1
  149. package/dist/{worker.transport-BhMyvqQ2.mjs.map → worker.transport-lPxO2MVL.mjs.map} +1 -1
  150. package/package.json +27 -14
  151. package/dist/.tsbuildinfo +0 -1
  152. package/dist/async-context-CSHWoHMB.js.map +0 -1
  153. package/dist/async-context-CwBNMm2E.mjs.map +0 -1
  154. package/dist/cloudwatch.transport-CGHY4buE.d.mts.map +0 -1
  155. package/dist/cloudwatch.transport-DK_8cKd3.d.ts.map +0 -1
  156. package/dist/cloudwatch.transport-pNBNWJLt.js.map +0 -1
  157. package/dist/cloudwatch.transport-yBwm4OeO.mjs.map +0 -1
  158. package/dist/gcp.transport-BJuNRFe_.mjs.map +0 -1
  159. package/dist/gcp.transport-BOhkVKlI.d.ts.map +0 -1
  160. package/dist/gcp.transport-Bi6CUqZR.d.mts.map +0 -1
  161. package/dist/gcp.transport-SHdtc1n0.js.map +0 -1
  162. package/dist/logitron-logger.module-BdG1Jnpk.d.mts +0 -618
  163. package/dist/logitron-logger.module-BdG1Jnpk.d.mts.map +0 -1
  164. package/dist/logitron-logger.module-Bs3XYZgF.mjs.map +0 -1
  165. package/dist/logitron-logger.module-BsDZR5BH.js.map +0 -1
  166. package/dist/logitron-logger.module-CbXy2gfW.d.ts +0 -618
  167. package/dist/logitron-logger.module-CbXy2gfW.d.ts.map +0 -1
  168. package/dist/search-CFQRISUv.mjs.map +0 -1
  169. package/dist/search-CQOo-Iq6.d.mts.map +0 -1
  170. package/dist/search-DujhR94O.d.ts.map +0 -1
  171. package/dist/search-njTI2hdo.js.map +0 -1
  172. package/dist/transport.manager-BivyUTtU.js.map +0 -1
  173. package/dist/transport.manager-D6KL3t9V.mjs.map +0 -1
  174. package/dist/transport.types-B5vcyv2k.d.mts.map +0 -1
  175. package/dist/transport.types-BuY84TwO.d.ts.map +0 -1
  176. package/scripts/postinstall.js +0 -61
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.manager-BiuCAJd2.mjs","names":["VISIBLE_ESCAPES: Record<string, string>","config: ConsoleTransportConfig","parts: string[]","DROP_WARN_INTERVAL_MS","name: string","current: unknown","remaining: TransportLogEntry[]","chunks: T[][]","config: DatabaseTransportConfig","state: IsolationState","config: any","sqlite3: any","list: unknown[]","unsent: TransportLogEntry[]","core: Record<string, unknown>","STATUS_MAP: Record<string, string>","chunk","chunks: { entry: TransportLogEntry; json: string }[][]","current: { entry: TransportLogEntry; json: string }[]","kept: Record<string, unknown>","SIZE_UNITS: Record<string, number>","path","existing: fs.Stats | undefined","failure: unknown","waiting: FlushWaiter[]","chunk","stats: FileStats","archived: string | undefined","flattened: Record<string, unknown>","chunk","chunks: T[][]","chunk","chunks: T[][]","transportEntry: TransportLogEntry","pending: Promise<void>[] | undefined","syncFailure: { error: unknown } | undefined","result: void | Promise<void>","lastError: Error","wait: number","readyPromises: Promise<boolean>[]","flushPromises: Promise<void>[]","closePromises: Promise<void>[]","details: Record<string, unknown>"],"sources":["../src/utils/sanitize.utils.ts","../src/transports/console.transport.ts","../src/transports/buffer-cap.ts","../src/transports/database.transport.ts","../src/transports/analytics.transport.ts","../src/transports/datadog.transport.ts","../src/transports/file.transport.ts","../src/transports/google-analytics.transport.ts","../src/transports/mixpanel.transport.ts","../src/version.ts","../src/transports/segment.transport.ts","../src/transports/transport.manager.ts"],"sourcesContent":["/**\n * Control-character stripping for text-mode log output.\n *\n * Line breaks become the visible escapes `\\\\n` / `\\\\r`; other C0 characters\n * (except tab), DEL and C1 characters are removed, so user-supplied text cannot\n * inject ANSI escapes, overwrite lines, or forge extra log entries.\n */\n\n// eslint-disable-next-line no-control-regex\nconst CONTROL_CHARS_RE = /[\\x00-\\x08\\x0A-\\x1F\\x7F-\\x9F]/g;\n\nconst VISIBLE_ESCAPES: Record<string, string> = { '\\n': '\\\\n', '\\r': '\\\\r' };\n\nexport function stripControlChars(value: string): string {\n return value.replace(CONTROL_CHARS_RE, (ch) => VISIBLE_ESCAPES[ch] ?? '');\n}\n","import type {\n ConsoleTransportConfig,\n ITransport,\n TransportLogEntry,\n} from '../types/transport.types';\nimport { safeToString } from '../utils/coerce.utils';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { stripControlChars } from '../utils/sanitize.utils';\nimport { mergeCoreFields } from './entry-fields';\n\n/**\n * Writes log entries to stdout (info/debug/verbose) and stderr (error/warn).\n *\n * Supports pretty-printed colorized output and compact JSON mode.\n *\n * @example\n * transports: { console: { format: 'json' } }\n */\nexport class ConsoleTransport implements ITransport {\n public readonly name = 'console';\n public readonly filter?: (entry: TransportLogEntry) => boolean;\n\n // Pre-built ANSI color map — avoids object recreation inside formatEntry()\n private static readonly COLORS: ReadonlyMap<string, string> = new Map([\n ['black', '\\x1b[30m'],\n ['red', '\\x1b[31m'],\n ['green', '\\x1b[32m'],\n ['yellow', '\\x1b[33m'],\n ['blue', '\\x1b[34m'],\n ['magenta', '\\x1b[35m'],\n ['cyan', '\\x1b[36m'],\n ['white', '\\x1b[37m'],\n ['gray', '\\x1b[90m'],\n ['grey', '\\x1b[90m'],\n ['brightred', '\\x1b[91m'],\n ['brightgreen', '\\x1b[92m'],\n ['brightyellow', '\\x1b[93m'],\n ['brightblue', '\\x1b[94m'],\n ['brightmagenta', '\\x1b[95m'],\n ['brightcyan', '\\x1b[96m'],\n ['brightwhite', '\\x1b[97m'],\n ['reset', '\\x1b[0m'],\n ]);\n\n private static sanitize(value: unknown): string {\n // Coerce to string first: upstream code (NestJS internals, user payloads)\n // can pass objects / Errors here, and .replace() would throw otherwise.\n return stripControlChars(safeToString(value));\n }\n\n constructor(private config: ConsoleTransportConfig = {}) {\n if (config.filter) this.filter = config.filter;\n }\n\n // Synchronous write — returns void, NOT Promise.resolve(). Allocating a\n // resolved Promise on every log is a measurable hot-path tax; the manager\n // only awaits transports that actually return a thenable. The ITransport\n // contract permits `void | Promise<void>`.\n write(entry: TransportLogEntry): void {\n const formatted = this.formatEntry(entry) + '\\n';\n // Use process.stderr for errors, stdout for everything else —\n // avoids the extra indirection that console.log/error add.\n const lvl = entry.level.toLowerCase();\n // Per the class contract, error AND warn go to stderr; everything else stdout.\n const out = lvl === 'error' || lvl === 'warn' ? process.stderr : process.stdout;\n out.write(formatted);\n }\n\n private formatEntry(entry: TransportLogEntry): string {\n if (this.config.format === 'json') {\n // Compact (single-line) JSON so line-based log collectors can parse one\n // entry per line. JSON.stringify already escapes control chars.\n return stringifyForLog(\n mergeCoreFields(\n {\n timestamp: this.config.timestamp !== false ? entry.timestamp.toISOString() : undefined,\n level: entry.level,\n message: entry.message,\n context: entry.context,\n traceId: entry.traceId,\n appName: entry.appName,\n environment: entry.environment,\n },\n entry.data\n )\n );\n }\n\n // Text format\n const parts: string[] = [];\n\n // Timestamp\n if (this.config.timestamp !== false) {\n const timestamp = entry.timestamp.toISOString();\n parts.push(this.colorize(timestamp, 'gray'));\n }\n\n // Level — sanitized too: custom levels are user-controlled and could carry\n // ANSI escapes / control chars (CWE-117).\n const level = ConsoleTransport.sanitize(entry.level).toUpperCase().padEnd(5);\n const coloredLevel = this.colorize(level, this.getLevelColor(entry.level));\n parts.push(coloredLevel);\n\n // Context\n if (entry.context) {\n const context = `[${ConsoleTransport.sanitize(entry.context)}]`;\n parts.push(this.colorize(context, 'cyan'));\n }\n\n // Trace ID\n if (entry.traceId) {\n const traceId = `(${ConsoleTransport.sanitize(entry.traceId)})`;\n parts.push(this.colorize(traceId, 'magenta'));\n }\n\n // Message\n parts.push(ConsoleTransport.sanitize(entry.message));\n\n // Data — JSON.stringify already escapes control chars as \\u001b, so the\n // JSON representation is safe to print without further sanitization.\n if (entry.data && Object.keys(entry.data).length > 0) {\n const data = stringifyForLog(entry.data);\n parts.push(this.colorize(data, 'blue'));\n }\n\n return parts.join(' ');\n }\n\n private getLevelColor(level: string): string {\n const lower = level.toLowerCase();\n\n // User-configured colors take priority (covers custom levels like kafka, mongo, etc.)\n if (this.config.levelColors?.[lower]) {\n return this.config.levelColors[lower]!;\n }\n\n // Built-in defaults\n const defaults: Record<string, string> = {\n error: 'red',\n warn: 'yellow',\n warning: 'yellow',\n info: 'green',\n debug: 'blue',\n trace: 'magenta',\n verbose: 'cyan',\n };\n\n return defaults[lower] || 'gray';\n }\n\n private colorize(text: string, color: string): string {\n if (this.config.colorize === false) return text;\n // Use the static pre-built map — no per-call object allocation\n const reset = ConsoleTransport.COLORS.get('reset')!;\n const code = ConsoleTransport.COLORS.get(color) ?? ConsoleTransport.COLORS.get('white')!;\n return `${code}${text}${reset}`;\n }\n\n close(): Promise<void> {\n return Promise.resolve();\n }\n}\n","import { internalWarn } from '../utils/internal-log';\n\nexport const DEFAULT_BUFFER_CAP = 10_000;\nconst DROP_WARN_INTERVAL_MS = 5000;\n\n/**\n * Keeps an in-memory retry buffer bounded. Past `max` the oldest items are\n * dropped, counted, and reported through internalWarn at most every few seconds.\n *\n * `requested` must be an integer of at least 1, otherwise the default is used.\n * A cap below `batchSize` is raised to `batchSize` so one batch always fits.\n */\nexport class BufferCap {\n public readonly max: number;\n private dropped = 0;\n private lastWarning = 0;\n\n constructor(\n private readonly name: string,\n requested: number | undefined,\n batchSize: number,\n fallback = DEFAULT_BUFFER_CAP\n ) {\n let max = fallback;\n if (requested !== undefined) {\n if (Number.isInteger(requested) && requested >= 1) {\n max = requested;\n } else {\n internalWarn(\n `${name}: maxBufferSize ${String(requested)} is not a positive integer; using ${fallback}`\n );\n }\n }\n this.max = Math.max(max, batchSize);\n }\n\n /** Drops the oldest items beyond the cap, in place. */\n enforce(items: unknown[]): void {\n const excess = items.length - this.max;\n if (excess <= 0) return;\n items.splice(0, excess);\n this.note(excess);\n }\n\n note(count: number): void {\n this.dropped += count;\n this.report(DROP_WARN_INTERVAL_MS);\n }\n\n report(minGapMs: number): void {\n const now = Date.now();\n if (this.dropped === 0 || now - this.lastWarning < minGapMs) return;\n internalWarn(\n `${this.name}: buffer full (${this.max}), dropped ${this.dropped} oldest entr${this.dropped === 1 ? 'y' : 'ies'}`\n );\n this.dropped = 0;\n this.lastWarning = now;\n }\n}\n","import type {\n DatabaseTransportConfig,\n IAsyncTransport,\n IBatchTransport,\n TransportLogEntry,\n} from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { BufferCap } from './buffer-cap';\n\n/**\n * Validates that a SQL identifier (table/index name) contains only safe characters.\n * Prevents SQL injection via user-supplied table names.\n */\nconst POSTGRES_MAX_PARAMS = 65535;\nconst POSTGRES_COLUMNS = 8;\nconst POSTGRES_ROWS_PER_INSERT = Math.floor(POSTGRES_MAX_PARAMS / POSTGRES_COLUMNS);\nconst MYSQL_ROWS_PER_INSERT = 5000;\n\n/** Postgres rejects NUL in TEXT and JSONB. */\nfunction stripNul(value: unknown): unknown {\n return typeof value === 'string' ? value.split('\\u0000').join('') : value;\n}\n\n/** JSONB also rejects the \\u0000 escape, which JSON.stringify writes for a NUL. */\nfunction stripJsonNulEscape(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n return value.replace(/(?<!\\\\)((?:\\\\\\\\)*)\\\\u0000/g, '$1');\n}\n\nconst PG_DATA_CLASSES = ['22', '23', '54'];\nconst MYSQL_DATA_CODES = new Set([\n 'ER_DATA_TOO_LONG',\n 'ER_TRUNCATED_WRONG_VALUE',\n 'ER_TRUNCATED_WRONG_VALUE_FOR_FIELD',\n 'ER_BAD_NULL_ERROR',\n 'ER_WARN_DATA_OUT_OF_RANGE',\n 'ER_DUP_ENTRY',\n 'ER_INVALID_JSON_TEXT',\n 'ER_INVALID_CHARACTER_STRING',\n]);\nconst MYSQL_DATA_ERRNOS = new Set([1048, 1062, 1264, 1292, 1366, 1406, 3140]);\nconst SQLITE_DATA_CODES = new Set(['SQLITE_CONSTRAINT', 'SQLITE_TOOBIG', 'SQLITE_MISMATCH']);\nconst MONGO_DATA_CODES = new Set([2, 121, 10334, 11000]);\n\ninterface DriverError {\n code?: unknown;\n errno?: unknown;\n name?: unknown;\n cause?: unknown;\n}\n\nfunction isDataErrorLayer(type: string, e: DriverError): boolean {\n const code = e.code;\n switch (type) {\n case 'postgresql':\n return typeof code === 'string' && PG_DATA_CLASSES.includes(code.slice(0, 2));\n case 'mysql':\n return (\n (typeof code === 'string' && MYSQL_DATA_CODES.has(code)) ||\n (typeof e.errno === 'number' && MYSQL_DATA_ERRNOS.has(e.errno))\n );\n case 'sqlite':\n return (\n typeof code === 'string' &&\n (SQLITE_DATA_CODES.has(code) || code.startsWith('SQLITE_CONSTRAINT_'))\n );\n case 'mongodb':\n return (\n e.name === 'BSONObjectTooLarge' || (typeof code === 'number' && MONGO_DATA_CODES.has(code))\n );\n default:\n return false;\n }\n}\n\n/**\n * True when the database refused the data itself (bad value, constraint, size),\n * which retrying cannot fix. Connection, timeout, auth and unknown errors are\n * not data errors and must be retried, never dropped.\n */\nexport function isDataError(type: string, error: unknown): boolean {\n let current: unknown = error;\n for (let depth = 0; depth < 3 && current && typeof current === 'object'; depth += 1) {\n if (isDataErrorLayer(type, current as DriverError)) return true;\n current = (current as DriverError).cause;\n }\n return false;\n}\n\ninterface IsolationState {\n budget: number;\n rejected: number;\n requeue: TransportLogEntry[];\n stopped: boolean;\n error?: unknown;\n}\n\n/** A write that stored part of a batch: only `remaining` still needs to be written. */\nclass PartialWriteError extends Error {\n constructor(\n message: string,\n public readonly remaining: TransportLogEntry[],\n cause: unknown\n ) {\n super(message, { cause });\n }\n}\n\nfunction chunk<T>(items: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < items.length; i += size) {\n chunks.push(items.slice(i, i + size));\n }\n return chunks;\n}\n\nfunction validateSqlIdentifier(name: string, label = 'table'): void {\n if (!/^[a-zA-Z_]\\w*$/.test(name)) {\n throw new Error(\n `Invalid ${label} name \"${name}\". Only letters, digits, and underscores are allowed, and the name must start with a letter or underscore.`\n );\n }\n}\n\n/**\n * Persists log entries to a database in batches.\n *\n * Supports MongoDB, PostgreSQL, MySQL, and SQLite. Entries are buffered and\n * flushed on a configurable interval or batch size threshold to minimize\n * round-trips.\n *\n * @example\n * transports: {\n * database: { type: 'postgresql', host: 'localhost', database: 'appdb', table: 'logs' }\n * }\n */\nexport class DatabaseTransport implements IAsyncTransport, IBatchTransport {\n public readonly name = 'database';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly filter?: (entry: TransportLogEntry) => boolean;\n\n private batch: TransportLogEntry[] = [];\n private flushTimer?: NodeJS.Timeout | undefined;\n /**\n * The in-flight drain promise, or undefined when idle. Concurrent flush()\n * calls (the un-awaited threshold flush write() fires, plus the interval\n * timer) all await this single promise instead of starting their own drain,\n * so a batch is never snapshotted and written twice. Mirrors the FileTransport\n * fix for the overlapping-batch-flush duplication bug.\n */\n private flushPromise?: Promise<void> | undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic database driver connection object\n private connection: any;\n private isConnected = false;\n private connectionPromise?: Promise<void> | undefined;\n private readonly cap: BufferCap;\n\n constructor(private config: DatabaseTransportConfig) {\n this.batchSize = config.batchSize || 100;\n this.flushInterval = config.flushInterval || 5000; // 5 seconds\n this.cap = new BufferCap('DatabaseTransport', config.maxBufferSize, this.batchSize);\n if (config.filter) this.filter = config.filter;\n // Validate table name early so misconfiguration fails at construction, not at first write\n if (config.table) {\n validateSqlIdentifier(config.table, 'table');\n }\n this.setupFlushTimer();\n }\n\n async write(entry: TransportLogEntry): Promise<void> {\n if (!this.isConnected) {\n await this.connect();\n }\n\n this.addToBatch(entry);\n\n if (this.batch.length >= this.batchSize) {\n await this.flush();\n }\n }\n\n addToBatch(entry: TransportLogEntry): void {\n this.batch.push(entry);\n this.cap.enforce(this.batch);\n }\n\n async flush(): Promise<void> {\n // Serialize concurrent flushes. write() fires flush() un-awaited once the\n // batch crosses batchSize, and the interval timer fires it too, so a burst\n // of writes can trigger many overlapping flushes. Every caller joins the\n // SAME in-flight drain, so a batch is never snapshotted and written twice —\n // the same overlapping-flush duplication that turned N file logs into N²\n // lines (see FileTransport fix). Awaiters still see the batch fully drained\n // because they await the shared drain promise.\n if (!this.flushPromise) {\n this.flushPromise = this.drain().finally(() => {\n this.flushPromise = undefined;\n });\n }\n\n await this.flushPromise;\n }\n\n /**\n * Drains the buffer to the database one batch at a time. A batch is detached\n * SYNCHRONOUSLY before awaiting the write, so entries appended by concurrent\n * write() calls land in a fresh array and are never written twice.\n *\n * A failure that is not a data error (connection, timeout, auth, unknown) puts\n * the batch back at the front of the buffer, bounded by `maxBufferSize`, and\n * stops the loop so a down database is not hammered. A data error means the\n * database refused some entry itself, so that batch alone is split to find and\n * drop the refused entries; see isolate().\n */\n private async drain(): Promise<void> {\n while (this.batch.length > 0) {\n const entries = this.batch.splice(0, this.batchSize);\n\n try {\n await this.writeEntries(entries);\n } catch (error) {\n internalError('Database flush error', error);\n if (error instanceof PartialWriteError) {\n this.requeue(error.remaining);\n throw error.cause;\n }\n if (!isDataError(this.config.type, error)) {\n this.requeue(entries);\n throw error;\n }\n const state: IsolationState = {\n budget: 2 * this.batchSize,\n rejected: 0,\n requeue: [],\n stopped: false,\n };\n await this.isolate(entries, state);\n if (state.rejected > 0) {\n internalWarn(\n `DatabaseTransport: dropped ${state.rejected} log entr${state.rejected === 1 ? 'y' : 'ies'} the database rejected`\n );\n }\n this.requeue(state.requeue);\n if (state.stopped) throw state.error;\n }\n }\n }\n\n private requeue(entries: TransportLogEntry[]): void {\n if (entries.length === 0) return;\n this.batch = entries.concat(this.batch);\n this.cap.enforce(this.batch);\n }\n\n private async writeEntries(entries: TransportLogEntry[]): Promise<void> {\n switch (this.config.type) {\n case 'mongodb':\n await this.flushToMongoDB(entries);\n break;\n case 'postgresql':\n await this.flushToPostgreSQL(entries);\n break;\n case 'mysql':\n await this.flushToMySQL(entries);\n break;\n case 'sqlite':\n await this.flushToSQLite(entries);\n break;\n default:\n throw new Error(`Unsupported database type: ${this.config.type}`);\n }\n }\n\n /**\n * `entries` failed with a data error. Splits them in half until the refused\n * entries are alone and drops those. A write that fails with anything but a\n * data error, or running out of the per-flush write budget, stops the search:\n * the entries not yet written go back to the buffer.\n */\n private async isolate(entries: TransportLogEntry[], state: IsolationState): Promise<void> {\n if (entries.length === 1) {\n state.rejected += 1;\n return;\n }\n const mid = Math.ceil(entries.length / 2);\n for (const half of [entries.slice(0, mid), entries.slice(mid)]) {\n if (state.stopped || state.budget <= 0) {\n state.requeue.push(...half);\n continue;\n }\n state.budget -= 1;\n try {\n await this.writeEntries(half);\n } catch (error) {\n if (error instanceof PartialWriteError) {\n state.requeue.push(...error.remaining);\n state.stopped = true;\n state.error = error.cause;\n } else if (isDataError(this.config.type, error)) {\n await this.isolate(half, state);\n } else {\n state.requeue.push(...half);\n state.stopped = true;\n state.error = error;\n }\n }\n }\n }\n\n async isReady(): Promise<boolean> {\n try {\n if (!this.isConnected) {\n await this.connect();\n }\n return this.isConnected;\n } catch {\n return false;\n }\n }\n\n private async connect(): Promise<void> {\n if (this.connectionPromise) {\n return this.connectionPromise;\n }\n\n // Clear the memoized promise on failure so a transient startup error does\n // not permanently wedge the transport — the next write() retries the connect\n // instead of re-awaiting a forever-rejected promise.\n this.connectionPromise = this.establishConnection().catch((error: unknown) => {\n this.connectionPromise = undefined;\n throw error;\n });\n return this.connectionPromise;\n }\n\n private async establishConnection(): Promise<void> {\n try {\n switch (this.config.type) {\n case 'mongodb':\n await this.connectMongoDB();\n break;\n case 'postgresql':\n await this.connectPostgreSQL();\n break;\n case 'mysql':\n await this.connectMySQL();\n break;\n case 'sqlite':\n await this.connectSQLite();\n break;\n }\n this.isConnected = true;\n } catch (error) {\n this.isConnected = false;\n throw new Error(`Database connection failed: ${error}`, { cause: error });\n }\n }\n\n private async connectMongoDB(): Promise<void> {\n try {\n const { MongoClient } = await import('mongodb').catch(() => {\n throw new Error('MongoDB driver not installed. Run: npm install mongodb');\n });\n const connectionString =\n this.config.connectionString ||\n `mongodb://${this.config.host}:${this.config.port}/${this.config.database}`;\n\n this.connection = new MongoClient(connectionString);\n await this.connection.connect();\n\n // Test connection\n await this.connection.db(this.config.database).admin().ping();\n } catch (error) {\n throw new Error(`MongoDB connection failed: ${error}`, { cause: error });\n }\n }\n\n private async connectPostgreSQL(): Promise<void> {\n try {\n const { Client } = await import('pg').catch(() => {\n throw new Error('PostgreSQL driver not installed. Run: npm install pg @types/pg');\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- pg.Client accepts any config shape\n const config: any = {\n connectionString: this.config.connectionString,\n host: this.config.host,\n port: this.config.port,\n database: this.config.database,\n };\n\n if (this.config.username) config.user = this.config.username;\n if (this.config.password) config.password = this.config.password;\n if (this.config.ssl) config.ssl = typeof this.config.ssl === 'boolean' ? {} : this.config.ssl;\n\n this.connection = new Client(config);\n\n await this.connection.connect();\n await this.createPostgreSQLTable();\n } catch (error) {\n throw new Error(`PostgreSQL connection failed: ${error}`, { cause: error });\n }\n }\n\n private async connectMySQL(): Promise<void> {\n try {\n const mysql = await import('mysql2/promise').catch(() => {\n throw new Error('MySQL driver not installed. Run: npm install mysql2');\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- mysql2 accepts any config shape\n const config: any = {\n host: this.config.host,\n port: this.config.port,\n database: this.config.database,\n };\n\n if (this.config.username) config.user = this.config.username;\n if (this.config.password) config.password = this.config.password;\n if (this.config.ssl) config.ssl = typeof this.config.ssl === 'boolean' ? {} : this.config.ssl;\n\n this.connection = await mysql.createConnection(config);\n\n await this.createMySQLTable();\n } catch (error) {\n throw new Error(`MySQL connection failed: ${error}`, { cause: error });\n }\n }\n\n private async connectSQLite(): Promise<void> {\n try {\n // Optional peer dependencies resolved at runtime only.\n // Variable module specifiers bypass TS static resolution so logixia can\n // ship without declaring `sqlite`/`sqlite3` as devDependencies.\n const sqlite3ModuleName = 'sqlite3';\n const sqliteModuleName = 'sqlite';\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- optional runtime dependency without bundled types\n const sqlite3: any = await import(sqlite3ModuleName).catch(() => {\n throw new Error('SQLite driver not installed. Run: npm install sqlite3 sqlite');\n });\n const { open } = (await import(sqliteModuleName).catch(() => {\n throw new Error('SQLite driver not installed. Run: npm install sqlite3 sqlite');\n })) as { open: (opts: { filename: string; driver: unknown }) => Promise<unknown> };\n\n this.connection = await open({\n filename: this.config.database,\n driver: sqlite3.Database,\n });\n\n await this.createSQLiteTable();\n } catch (error) {\n throw new Error(`SQLite connection failed: ${error}`, { cause: error });\n }\n }\n\n private async flushToMongoDB(entries: TransportLogEntry[]): Promise<void> {\n const db = this.connection.db(this.config.database);\n const collection = db.collection(this.config.collection || 'logs');\n\n const documents = entries.map((entry) => ({\n timestamp: entry.timestamp,\n level: entry.level,\n message: entry.message,\n payload: entry.data,\n context: entry.context,\n traceId: entry.traceId,\n appName: entry.appName,\n environment: entry.environment,\n }));\n\n try {\n await collection.insertMany(documents, { ordered: false });\n } catch (error) {\n const raw = (error as { writeErrors?: unknown }).writeErrors;\n let list: unknown[] = [];\n if (Array.isArray(raw)) list = raw;\n else if (raw) list = [raw];\n const writeErrors = list as Array<{\n index?: number;\n code?: number;\n }>;\n if (writeErrors.length === 0) throw error;\n // Unordered inserts store every document that was not refused, so only\n // refused documents may be retried or dropped.\n const remaining: TransportLogEntry[] = [];\n let rejected = 0;\n for (const writeError of writeErrors) {\n const entry = typeof writeError.index === 'number' ? entries[writeError.index] : undefined;\n if (!entry) throw error;\n if (isDataError('mongodb', writeError)) rejected += 1;\n else remaining.push(entry);\n }\n if (rejected > 0) {\n internalWarn(\n `DatabaseTransport: dropped ${rejected} log entr${rejected === 1 ? 'y' : 'ies'} the database rejected`\n );\n }\n if (remaining.length > 0) {\n throw new PartialWriteError('MongoDB insert partially failed', remaining, error);\n }\n }\n }\n\n private async flushToPostgreSQL(entries: TransportLogEntry[]): Promise<void> {\n const tableName = this.config.table || 'logs';\n const values = entries.map((entry) =>\n [\n entry.timestamp,\n entry.level,\n entry.message,\n stringifyForLog(entry.data || {}),\n entry.context,\n entry.traceId,\n entry.appName,\n entry.environment,\n ].map((value, column) => stripNul(column === 3 ? stripJsonNulEscape(value) : value))\n );\n\n const chunks = chunk(values, POSTGRES_ROWS_PER_INSERT);\n const insertChunk = async (rows: unknown[][]): Promise<void> => {\n const placeholders = rows\n .map((_, i) => {\n const params = Array.from(\n { length: POSTGRES_COLUMNS },\n (_unused, c) => `$${i * POSTGRES_COLUMNS + c + 1}`\n );\n return `(${params.join(', ')})`;\n })\n .join(', ');\n\n const query = `\n INSERT INTO ${tableName} \n (timestamp, level, message, payload, context, trace_id, app_name, environment)\n VALUES ${placeholders}\n `;\n\n await this.connection.query(query, rows.flat());\n };\n\n if (chunks.length === 1) {\n await insertChunk(chunks[0] as unknown[][]);\n return;\n }\n\n await this.connection.query('BEGIN');\n try {\n for (const rows of chunks) {\n await insertChunk(rows);\n }\n await this.connection.query('COMMIT');\n } catch (error) {\n await this.connection.query('ROLLBACK').catch(() => {});\n throw error;\n }\n }\n\n private async flushToMySQL(entries: TransportLogEntry[]): Promise<void> {\n const tableName = this.config.table || 'logs';\n const values = entries.map((entry) => [\n entry.timestamp,\n entry.level,\n entry.message,\n stringifyForLog(entry.data || {}),\n entry.context,\n entry.traceId,\n entry.appName,\n entry.environment,\n ]);\n\n const query = `\n INSERT INTO ${tableName} \n (timestamp, level, message, payload, context, trace_id, app_name, environment)\n VALUES ?\n `;\n\n const chunks = chunk(values, MYSQL_ROWS_PER_INSERT);\n if (chunks.length === 1) {\n await this.connection.query(query, [values]);\n return;\n }\n\n await this.connection.beginTransaction();\n try {\n for (const rows of chunks) {\n await this.connection.query(query, [rows]);\n }\n await this.connection.commit();\n } catch (error) {\n await this.connection.rollback().catch(() => {});\n throw error;\n }\n }\n\n private async flushToSQLite(entries: TransportLogEntry[]): Promise<void> {\n const tableName = this.config.table || 'logs';\n\n const stmt = await this.connection.prepare(`\n INSERT INTO ${tableName} \n (timestamp, level, message, payload, context, trace_id, app_name, environment)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n await this.connection.exec('BEGIN');\n try {\n for (const entry of entries) {\n await stmt.run(\n entry.timestamp instanceof Date ? entry.timestamp.toISOString() : entry.timestamp,\n entry.level,\n entry.message,\n stringifyForLog(entry.data || {}),\n entry.context,\n entry.traceId,\n entry.appName,\n entry.environment\n );\n }\n await this.connection.exec('COMMIT');\n } catch (error) {\n await this.connection.exec('ROLLBACK').catch(() => {});\n throw error;\n } finally {\n await stmt.finalize().catch(() => {});\n }\n }\n\n private async createPostgreSQLTable(): Promise<void> {\n const tableName = this.config.table || 'logs';\n const query = `\n CREATE TABLE IF NOT EXISTS ${tableName} (\n id SERIAL PRIMARY KEY,\n timestamp TIMESTAMP WITH TIME ZONE NOT NULL,\n level VARCHAR(20) NOT NULL,\n message TEXT NOT NULL,\n payload JSONB,\n context VARCHAR(255),\n trace_id VARCHAR(255),\n app_name VARCHAR(255),\n environment VARCHAR(50),\n time_taken INTEGER,\n created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP\n )\n `;\n\n await this.connection.query(query);\n\n // Create indexes for better performance\n await this.connection.query(\n `CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName} (timestamp)`\n );\n await this.connection.query(\n `CREATE INDEX IF NOT EXISTS idx_${tableName}_level ON ${tableName} (level)`\n );\n await this.connection.query(\n `CREATE INDEX IF NOT EXISTS idx_${tableName}_trace_id ON ${tableName} (trace_id)`\n );\n }\n\n private async createMySQLTable(): Promise<void> {\n const tableName = this.config.table || 'logs';\n const query = `\n CREATE TABLE IF NOT EXISTS ${tableName} (\n id INT AUTO_INCREMENT PRIMARY KEY,\n timestamp DATETIME NOT NULL,\n level VARCHAR(20) NOT NULL,\n message TEXT NOT NULL,\n payload JSON,\n context VARCHAR(255),\n trace_id VARCHAR(255),\n app_name VARCHAR(255),\n environment VARCHAR(50),\n time_taken INT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n INDEX idx_timestamp (timestamp),\n INDEX idx_level (level),\n INDEX idx_trace_id (trace_id)\n )\n `;\n\n await this.connection.execute(query);\n }\n\n private async createSQLiteTable(): Promise<void> {\n const tableName = this.config.table || 'logs';\n const query = `\n CREATE TABLE IF NOT EXISTS ${tableName} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp TEXT NOT NULL,\n level TEXT NOT NULL,\n message TEXT NOT NULL,\n payload TEXT,\n context TEXT,\n trace_id TEXT,\n app_name TEXT,\n environment TEXT,\n time_taken INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n\n await this.connection.exec(query);\n\n // Create indexes\n await this.connection.exec(\n `CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName} (timestamp)`\n );\n await this.connection.exec(\n `CREATE INDEX IF NOT EXISTS idx_${tableName}_level ON ${tableName} (level)`\n );\n await this.connection.exec(\n `CREATE INDEX IF NOT EXISTS idx_${tableName}_trace_id ON ${tableName} (trace_id)`\n );\n }\n\n private setupFlushTimer(): void {\n this.flushTimer = setInterval(() => {\n this.flush().catch((error: unknown) => {\n internalError('Scheduled database flush error', error);\n });\n }, this.flushInterval);\n this.flushTimer.unref?.();\n }\n\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n\n // Drain remaining entries before closing the connection. This is the\n // shutdown path — losing buffered logs here is exactly the \"last N seconds\n // of logs lost on deploy\" problem the library promises to avoid, so we must\n // NOT let a single failed flush abort with entries still buffered.\n //\n // Retry a bounded number of times: each attempt flushes the current batch,\n // and a failed flush re-buffers its entries (see drain()) so the next\n // attempt picks them up. We stop early once the batch is empty, and cap the\n // attempts so a permanently-dead DB can't hang shutdown forever (the\n // flushOnExit force-exit timer is the outer backstop).\n const MAX_CLOSE_FLUSH_ATTEMPTS = 3;\n // A drain already running has detached its snapshot, so batch.length can be\n // 0 while rows are still being written. Wait for it before touching the\n // connection; a failure re-buffers its entries and the loop below retries.\n if (this.flushPromise) {\n await this.flushPromise.catch(() => {});\n }\n for (\n let attempt = 0;\n attempt < MAX_CLOSE_FLUSH_ATTEMPTS && this.batch.length > 0;\n attempt += 1\n ) {\n try {\n await this.flush();\n } catch (error) {\n internalError('Database flush during close failed; entries remain buffered', error);\n }\n }\n if (this.batch.length > 0) {\n internalError(\n `Database transport closing with ${this.batch.length} unflushed log entr${this.batch.length === 1 ? 'y' : 'ies'} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`\n );\n }\n\n this.cap.report(0);\n\n // Close connection\n if (this.connection) {\n switch (this.config.type) {\n case 'mongodb':\n await this.connection.close();\n break;\n case 'postgresql':\n await this.connection.end();\n break;\n case 'mysql':\n await this.connection.end();\n break;\n case 'sqlite':\n await this.connection.close();\n break;\n }\n }\n\n this.isConnected = false;\n }\n\n // Utility methods\n getBatchInfo() {\n return {\n batchSize: this.batchSize,\n currentBatchLength: this.batch.length,\n flushInterval: this.flushInterval,\n isConnected: this.isConnected,\n };\n }\n\n async getLogCount(): Promise<number> {\n if (!this.isConnected) return 0;\n\n try {\n switch (this.config.type) {\n case 'mongodb': {\n const collection = this.connection\n .db(this.config.database)\n .collection(this.config.collection || 'logs');\n return await collection.countDocuments();\n }\n\n case 'postgresql': {\n const tableName = this.config.table || 'logs';\n const result = await this.connection.query(`SELECT COUNT(*) as count FROM ${tableName}`);\n return Number(result.rows?.[0]?.count) || 0;\n }\n\n case 'mysql': {\n const tableName = this.config.table || 'logs';\n const [rows] = await this.connection.query(`SELECT COUNT(*) as count FROM ${tableName}`);\n return Number(rows?.[0]?.count) || 0;\n }\n\n case 'sqlite': {\n const tableName = this.config.table || 'logs';\n const row = await this.connection.get(`SELECT COUNT(*) as count FROM ${tableName}`);\n return Number(row?.count) || 0;\n }\n\n default:\n return 0;\n }\n } catch {\n return 0;\n }\n }\n}\n","import type {\n AnalyticsTransportConfig,\n IBatchTransport,\n ITransport,\n TransportLogEntry,\n} from '../types/transport.types';\nimport { internalError } from '../utils/internal-log';\nimport { BufferCap } from './buffer-cap';\nimport { mergeCoreFields } from './entry-fields';\n\n/**\n * Thrown by sendBatch() when part of a batch was already delivered. drain()\n * requeues only `unsent`, so delivered entries are not sent twice.\n */\nexport class PartialBatchError extends Error {\n constructor(\n message: string,\n public readonly unsent: TransportLogEntry[],\n options?: ErrorOptions\n ) {\n super(message, options);\n this.name = 'PartialBatchError';\n }\n}\n\nexport abstract class AnalyticsTransport implements ITransport, IBatchTransport {\n public readonly name: string;\n public readonly level?: string | undefined;\n public readonly batchSize?: number;\n public readonly flushInterval?: number;\n\n protected config: AnalyticsTransportConfig;\n protected batch: TransportLogEntry[] = [];\n protected batchTimer?: NodeJS.Timeout | undefined;\n protected isReady: boolean = false;\n /**\n * The in-flight drain promise, or undefined when idle. addToBatch() fires\n * flush() un-awaited on every Nth entry, so a synchronous burst can trigger\n * many overlapping flushes. Every caller joins this single promise instead of\n * starting its own drain, so a batch is never snapshotted and sent twice —\n * the overlapping-flush duplication that turns N logs into N² delivered events\n * (same class of bug fixed earlier in FileTransport / DatabaseTransport).\n */\n private flushPromise?: Promise<void> | undefined;\n /** Set on the first write. Subclass fields don't exist yet while the base constructor runs. */\n private initPromise?: Promise<void> | undefined;\n private readonly cap: BufferCap;\n\n constructor(name: string, config: AnalyticsTransportConfig) {\n this.name = name;\n this.config = {\n batchSize: 50,\n flushInterval: 10000, // 10 seconds\n enableUserTracking: true,\n enableEventTracking: true,\n ...config,\n };\n this.level = config.level;\n this.batchSize = this.config.batchSize || 50;\n this.flushInterval = this.config.flushInterval || 10000;\n this.cap = new BufferCap(name, config.maxBufferSize, this.batchSize);\n }\n\n async write(entry: TransportLogEntry): Promise<void> {\n if (!this.isReady) await this.ensureReady();\n\n if (this.shouldSkipEntry(entry)) {\n return;\n }\n\n if (this.config.batchSize && this.config.batchSize > 1) {\n this.addToBatch(entry);\n } else {\n await this.sendEntry(entry);\n }\n }\n\n addToBatch(entry: TransportLogEntry): void {\n this.batch.push(entry);\n this.cap.enforce(this.batch);\n\n if (this.batch.length >= (this.config.batchSize || 50)) {\n this.flush().catch((err: unknown) => internalError(`${this.name} batch flush failed`, err));\n } else if (!this.batchTimer && this.config.flushInterval) {\n this.batchTimer = setTimeout(() => {\n this.flush().catch((err: unknown) =>\n internalError(`${this.name} interval flush failed`, err)\n );\n }, this.config.flushInterval);\n this.batchTimer.unref?.();\n }\n }\n\n async flush(): Promise<void> {\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = undefined;\n }\n\n // Serialize concurrent flushes so overlapping un-awaited flushes (fired by\n // addToBatch on every Nth entry) all join the SAME drain instead of each\n // snapshotting the not-yet-cleared batch and sending it again.\n if (!this.flushPromise) {\n this.flushPromise = this.drain().finally(() => {\n this.flushPromise = undefined;\n });\n }\n\n await this.flushPromise;\n }\n\n /**\n * Drains the batch to the provider one snapshot at a time. The batch is\n * detached SYNCHRONOUSLY before awaiting sendBatch(), so entries appended by\n * concurrent writes land in a fresh array and are never sent twice. On failure\n * the snapshot is restored to the front of the batch for the next flush and\n * the loop stops, so a failing provider does not hot-spin.\n */\n private async drain(): Promise<void> {\n while (this.batch.length > 0) {\n const entriesToSend = this.batch;\n this.batch = [];\n\n try {\n await this.sendBatch(entriesToSend);\n } catch (error) {\n internalError(`Analytics transport ${this.name} flush failed`, error);\n // Re-add failed entries to the front of the batch for retry, then stop.\n this.batch.unshift(...(error instanceof PartialBatchError ? error.unsent : entriesToSend));\n this.cap.enforce(this.batch);\n return;\n }\n }\n }\n\n async close(): Promise<void> {\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = undefined;\n }\n\n // Drain remaining entries on shutdown. A failed flush re-buffers its entries\n // (see drain()), so retry a bounded number of times before giving up — this\n // prevents the \"last N seconds of logs lost on deploy\" problem without\n // hanging shutdown on a permanently-failing provider.\n const MAX_CLOSE_FLUSH_ATTEMPTS = 3;\n for (\n let attempt = 0;\n attempt < MAX_CLOSE_FLUSH_ATTEMPTS && this.batch.length > 0;\n attempt += 1\n ) {\n await this.flush();\n }\n if (this.batch.length > 0) {\n internalError(\n `Analytics transport ${this.name} closing with ${this.batch.length} unflushed entr${this.batch.length === 1 ? 'y' : 'ies'} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`\n );\n }\n\n this.cap.report(0);\n await this.cleanup();\n }\n\n protected shouldSkipEntry(entry: TransportLogEntry): boolean {\n // Skip debug/trace logs for analytics by default\n const skipLevels = ['debug', 'trace'];\n return skipLevels.includes(entry.level.toLowerCase());\n }\n\n protected transformEntry(entry: TransportLogEntry): Record<string, unknown> {\n const core: Record<string, unknown> = {\n timestamp: entry.timestamp.toISOString(),\n level: entry.level,\n message: entry.message,\n context: entry.context,\n traceId: entry.traceId,\n appName: entry.appName,\n environment: entry.environment,\n };\n const transformed = mergeCoreFields(core, entry.data, this.reservedKeys());\n for (const key of ['context', 'traceId', 'appName', 'environment']) {\n if (!core[key]) delete transformed[key];\n }\n\n // Add custom properties\n if (this.config.customProperties) {\n Object.assign(transformed, this.config.customProperties);\n }\n\n return transformed;\n }\n\n /** Provider-specific field names a payload key must not replace. */\n protected reservedKeys(): readonly string[] {\n return [];\n }\n\n /**\n * Runs initialize() once. A bad config (say, a missing API key) fails every\n * write with the same error instead of an unhandled rejection at startup.\n */\n protected ensureReady(): Promise<void> {\n this.initPromise ??= (async () => {\n await this.initialize();\n this.isReady = true;\n })();\n return this.initPromise;\n }\n\n /** @deprecated Use ensureReady(); kept for subclasses that call it. */\n protected async waitForReady(): Promise<void> {\n await this.ensureReady();\n }\n\n // Abstract methods to be implemented by specific analytics providers\n protected abstract initialize(): Promise<void> | void;\n protected abstract sendEntry(entry: TransportLogEntry): Promise<void>;\n protected abstract sendBatch(entries: TransportLogEntry[]): Promise<void>;\n protected abstract cleanup(): Promise<void> | void;\n}\n\n// Analytics Event Types\nexport interface AnalyticsEvent {\n name: string;\n properties?: Record<string, unknown>;\n userId?: string;\n sessionId?: string;\n timestamp?: Date;\n}\n\nexport interface AnalyticsUser {\n id: string;\n properties?: Record<string, unknown>;\n traits?: Record<string, unknown>;\n}\n\nexport interface AnalyticsMetric {\n name: string;\n value: number;\n unit?: string;\n tags?: Record<string, string>;\n timestamp?: Date;\n}\n","import { promisify } from 'node:util';\nimport { gzip } from 'node:zlib';\n\nimport type { DataDogTransportConfig, TransportLogEntry } from '../types/transport.types';\nimport { internalError } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport type { AnalyticsMetric } from './analytics.transport';\nimport { AnalyticsTransport, PartialBatchError } from './analytics.transport';\n\nconst gzipAsync = promisify(gzip);\n\nconst MAX_LOGS_PER_REQUEST = 1000;\nconst MAX_LOGS_PAYLOAD_BYTES = 5_000_000;\nconst MAX_LOG_BYTES = 1_000_000;\nconst MAX_TRUNCATED_MESSAGE_CHARS = 150_000;\n\nconst TRUNCATED_LOG_KEYS = [\n 'ddsource',\n 'ddtags',\n 'hostname',\n 'service',\n 'status',\n 'timestamp',\n 'level',\n 'context',\n 'traceId',\n 'appName',\n 'environment',\n 'env',\n 'version',\n 'logger',\n 'dd.trace_id',\n 'dd.span_id',\n];\n\nconst STATUS_MAP: Record<string, string> = {\n fatal: 'critical',\n critical: 'critical',\n error: 'error',\n warn: 'warn',\n warning: 'warn',\n info: 'info',\n debug: 'debug',\n trace: 'debug',\n verbose: 'debug',\n};\n\nconst MAX_UINT64 = (1n << 64n) - 1n;\nconst DASHED_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst HEX = /^[0-9a-f]+$/i;\nconst DIGITS = /^\\d+$/;\n\n/**\n * Datadog correlates on a decimal 64-bit id. Accepts:\n * - a dashed UUID (logixia's default trace id): dashes stripped, then 32-hex;\n * - 32 hex chars (W3C trace id) or 16 hex chars containing a-f: low 64 bits as decimal;\n * - an all-digit string: already decimal (dd-trace's native format), passed through.\n * An all-digit string is ambiguous when it is 16 or 32 long: 32 is read as hex (a\n * decimal id has at most 20 digits), 16 is read as decimal.\n * Anything else yields the raw string for trace ids (`passthrough`) and nothing for span ids.\n */\nfunction toDatadogId(id: unknown, passthrough: boolean): string | undefined {\n if (typeof id !== 'string' || id === '') return undefined;\n const fallback = passthrough ? id : undefined;\n const compact = DASHED_UUID.test(id) ? id.replaceAll('-', '') : id;\n const isHexId = HEX.test(compact) && (compact.length === 32 || compact.length === 16);\n if (DIGITS.test(compact) && compact.length !== 32) {\n const value = BigInt(compact);\n return value <= MAX_UINT64 ? value.toString() : fallback;\n }\n if (isHexId) return BigInt(`0x${compact.slice(-16)}`).toString();\n return fallback;\n}\n\n/** Datadog tags: no commas or whitespace, lowercase, at most 200 chars. */\nfunction tag(key: string, value: string): string {\n return `${key}:${value}`\n .replaceAll(/[\\s,]+/g, '_')\n .toLowerCase()\n .slice(0, 200);\n}\n\n/**\n * Sends log entries and metrics to Datadog via the Logs (v2) and Metrics HTTP APIs.\n *\n * The API key is transmitted via the `DD-API-KEY` request header — never in the URL.\n *\n * @example\n * transports: { datadog: { apiKey: process.env.DD_API_KEY, service: 'api', env: 'production' } }\n */\nexport class DataDogTransport extends AnalyticsTransport {\n private datadogConfig: DataDogTransportConfig;\n private baseUrl: string;\n private logsUrl: string;\n private metricsUrl: string;\n\n constructor(config: DataDogTransportConfig) {\n super('datadog', config);\n this.datadogConfig = config;\n\n // Set URLs based on site configuration\n const site = config.site || 'datadoghq.com';\n this.baseUrl = `https://api.${site}`;\n this.logsUrl = `https://http-intake.logs.${site}`;\n this.metricsUrl = `https://api.${site}`;\n }\n\n protected async initialize(): Promise<void> {\n try {\n // Validate required configuration\n if (!this.datadogConfig.apiKey) {\n throw new Error('DataDog API key is required');\n }\n this.isReady = true;\n } catch (error) {\n internalError('DataDog transport initialization failed', error);\n throw error;\n }\n }\n\n protected async sendEntry(entry: TransportLogEntry): Promise<void> {\n await this.sendBatch([entry]);\n }\n\n /**\n * Logs go first, chunk by chunk; metrics follow once every log chunk is out.\n * A failure part-way throws PartialBatchError naming only the entries that were\n * not delivered, so a retry never re-posts a chunk Datadog already accepted.\n */\n protected async sendBatch(entries: TransportLogEntry[]): Promise<void> {\n if (this.datadogConfig.enableLogs !== false) {\n await this.sendLogBatch(entries);\n }\n\n if (this.datadogConfig.enableMetrics) {\n try {\n await this.sendMetricBatch(entries);\n } catch (error) {\n if (this.datadogConfig.enableLogs === false) throw error;\n // Logs are already delivered; retrying would duplicate them.\n throw new PartialBatchError((error as Error).message, [], { cause: error });\n }\n }\n }\n\n protected async cleanup(): Promise<void> {\n // DataDog doesn't require explicit cleanup\n }\n\n private async sendLogBatch(entries: TransportLogEntry[]): Promise<void> {\n const items = entries.map((entry) => ({ entry, json: this.serializeLog(entry) }));\n\n // API key is sent via DD-API-KEY header inside makeLogsRequest — never put it in the URL\n // so it cannot be leaked through error messages, access logs, or network traces.\n const chunks = this.chunkLogs(items);\n for (let i = 0; i < chunks.length; i += 1) {\n const chunk = chunks[i]!;\n try {\n await this.makeLogsRequest('/api/v2/logs', `[${chunk.map((item) => item.json).join(',')}]`);\n } catch (error) {\n if (i === 0) throw error;\n const unsent = chunks.slice(i).flatMap((c) => c.map((item) => item.entry));\n throw new PartialBatchError((error as Error).message, unsent, { cause: error });\n }\n }\n }\n\n /** Splits into requests of at most 1000 entries and about 5MB of JSON. */\n private chunkLogs(items: { entry: TransportLogEntry; json: string }[]) {\n const chunks: { entry: TransportLogEntry; json: string }[][] = [];\n let current: { entry: TransportLogEntry; json: string }[] = [];\n let currentBytes = 2;\n for (const item of items) {\n const bytes = Buffer.byteLength(item.json) + 1;\n if (\n current.length > 0 &&\n (current.length >= MAX_LOGS_PER_REQUEST || currentBytes + bytes > MAX_LOGS_PAYLOAD_BYTES)\n ) {\n chunks.push(current);\n current = [];\n currentBytes = 2;\n }\n current.push(item);\n currentBytes += bytes;\n }\n if (current.length > 0) chunks.push(current);\n return chunks;\n }\n\n /** Serializes once. A log over 1MB keeps its identity and correlation fields but loses its attributes and most of its message. */\n private serializeLog(entry: TransportLogEntry): string {\n const log = this.transformToDataDogLog(entry);\n const json = stringifyForLog(log);\n if (Buffer.byteLength(json) <= MAX_LOG_BYTES) return json;\n const kept: Record<string, unknown> = {};\n for (const key of TRUNCATED_LOG_KEYS) {\n if (log[key] !== undefined) kept[key] = log[key];\n }\n kept['message'] = String(log['message']).slice(0, MAX_TRUNCATED_MESSAGE_CHARS);\n kept['truncated'] = true;\n return stringifyForLog(kept);\n }\n\n private async sendMetric(entry: TransportLogEntry): Promise<void> {\n await this.sendMetricBatch([entry]);\n }\n\n private async sendMetricBatch(entries: TransportLogEntry[]): Promise<void> {\n const metrics = entries.map((entry) => this.transformToDataDogMetric(entry));\n\n const payload = {\n series: metrics,\n };\n\n await this.makeMetricsRequest('/api/v1/series', payload);\n }\n\n private transformToDataDogLog(entry: TransportLogEntry): Record<string, unknown> {\n const transformed = this.transformEntry(entry);\n const service = this.datadogConfig.service || 'logixia';\n const env = this.datadogConfig.env || 'production';\n const version = this.datadogConfig.version;\n const tags = [\n tag('env', env),\n tag('service', service),\n ...(version ? [tag('version', version)] : []),\n ];\n const spanId = toDatadogId(entry.data?.['spanId'] ?? entry.data?.['span_id'], false);\n const traceId = toDatadogId(entry.traceId, true);\n\n return {\n ...transformed,\n ddsource: 'nodejs',\n ddtags: tags.join(','),\n hostname: this.getHostname(),\n service,\n status: this.mapLogLevel(entry.level),\n message: entry.message,\n env,\n version,\n logger: { name: 'logixia', thread_name: 'main' },\n ...(traceId && { 'dd.trace_id': traceId }),\n ...(traceId && spanId && { 'dd.span_id': spanId }),\n };\n }\n\n protected override reservedKeys(): readonly string[] {\n return [\n 'status',\n 'ddtags',\n 'ddsource',\n 'hostname',\n 'service',\n 'version',\n 'env',\n 'logger',\n // Datadog reserved attributes (host is the hostname alias, source the ddsource alias).\n 'host',\n 'source',\n 'dd.trace_id',\n 'dd.span_id',\n ];\n }\n\n private transformToDataDogMetric(entry: TransportLogEntry): Record<string, unknown> {\n return {\n // TODO(v2): rename to logixia.logs.* — kept as logitron.logs.* so existing\n // Datadog dashboards and monitors keep resolving. Maintainers decide when to break them.\n metric: `logitron.logs.${entry.level}`,\n points: [[Math.floor(entry.timestamp.getTime() / 1000), 1]],\n type: 'count',\n host: this.getHostname(),\n tags: [\n tag('level', entry.level),\n tag('service', this.datadogConfig.service || 'logixia'),\n tag('env', this.datadogConfig.env || 'production'),\n ...(entry.context ? [tag('context', entry.context)] : []),\n ...(entry.appName ? [tag('app', entry.appName)] : []),\n ],\n };\n }\n\n private mapLogLevel(level: string): string {\n return STATUS_MAP[level.toLowerCase()] || 'info';\n }\n\n private async makeLogsRequest(endpoint: string, json: string): Promise<void> {\n const url = `${this.logsUrl}${endpoint}`;\n\n try {\n const compress = this.datadogConfig.compress === true;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'DD-API-KEY': this.datadogConfig.apiKey,\n ...(compress && { 'Content-Encoding': 'gzip' }),\n },\n body: compress ? await gzipAsync(json) : json,\n });\n\n if (!response.ok) {\n throw new Error(`DataDog Logs API error: ${response.status} ${response.statusText}`);\n }\n } catch (error) {\n throw new Error(`Failed to send logs to DataDog: ${(error as Error).message}`, {\n cause: error,\n });\n }\n }\n\n private async makeMetricsRequest(endpoint: string, data: unknown): Promise<void> {\n const url = `${this.metricsUrl}${endpoint}`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'DD-API-KEY': this.datadogConfig.apiKey,\n },\n body: stringifyForLog(data),\n });\n\n if (!response.ok) {\n throw new Error(`DataDog Metrics API error: ${response.status} ${response.statusText}`);\n }\n } catch (error) {\n throw new Error(`Failed to send metrics to DataDog: ${(error as Error).message}`, {\n cause: error,\n });\n }\n }\n\n private getHostname(): string {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n return require('node:os').hostname() as string;\n } catch {\n return 'unknown';\n }\n }\n\n // Public methods for additional DataDog functionality\n public async sendCustomMetric(metric: AnalyticsMetric): Promise<void> {\n const payload = {\n series: [\n {\n metric: metric.name,\n points: [[Math.floor((metric.timestamp || new Date()).getTime() / 1000), metric.value]],\n type: 'gauge',\n host: this.getHostname(),\n tags: Object.entries(metric.tags || {}).map(([key, value]) => `${key}:${value}`),\n },\n ],\n };\n\n await this.makeMetricsRequest('/api/v1/series', payload);\n }\n\n public async sendEvent(title: string, text: string, tags: string[] = []): Promise<void> {\n const payload = {\n title,\n text,\n date_happened: Math.floor(Date.now() / 1000),\n priority: 'normal',\n tags: [\n `service:${this.datadogConfig.service || 'logixia'}`,\n `env:${this.datadogConfig.env || 'production'}`,\n ...tags,\n ],\n source_type_name: 'logixia',\n };\n\n const url = `${this.baseUrl}/api/v1/events`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'DD-API-KEY': this.datadogConfig.apiKey,\n },\n body: stringifyForLog(payload),\n });\n\n if (!response.ok) {\n throw new Error(`DataDog Events API error: ${response.status} ${response.statusText}`);\n }\n } catch (error) {\n throw new Error(`Failed to send event to DataDog: ${(error as Error).message}`, {\n cause: error,\n });\n }\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { pipeline } from 'node:stream/promises';\nimport { promisify } from 'node:util';\nimport { createGzip } from 'node:zlib';\n\nimport type {\n FileStats,\n FileTransportConfig,\n IBatchTransport,\n ITransport,\n TransportLogEntry,\n} from '../types/transport.types';\nimport { safeToString } from '../utils/coerce.utils';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { stripControlChars } from '../utils/sanitize.utils';\nimport { mergeCoreFields } from './entry-fields';\n\nconst appendFile = promisify(fs.appendFile);\nconst rename = promisify(fs.rename);\nconst unlink = promisify(fs.unlink);\nconst readdir = promisify(fs.readdir);\nconst stat = promisify(fs.stat);\n\ninterface FlushWaiter {\n upTo: number;\n resolve: () => void;\n reject: (error: unknown) => void;\n error?: unknown;\n}\n\nconst DROP_WARN_INTERVAL_MS = 5000;\nconst DEFAULT_MAX_QUEUE = 10_000;\nconst DEFAULT_FILE_MODE = 0o600;\nconst DEFAULT_DIR_MODE = 0o700;\n\nfunction csvField(value: unknown): string {\n return `\"${stripControlChars(safeToString(value)).replace(/\"/g, '\"\"')}\"`;\n}\n\nconst SIZE_UNITS: Record<string, number> = {\n '': 1,\n k: 1024,\n m: 1024 ** 2,\n g: 1024 ** 3,\n t: 1024 ** 4,\n};\n\n/**\n * Parses a size like `50MB`, `1.5g`, `512 KiB` or a plain byte count.\n * Units are binary (1KB = 1024 bytes). Returns undefined for anything else.\n */\nexport function parseSize(value: string | number | undefined): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value === 'number') {\n return Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;\n }\n const match = /^(\\d+(?:\\.\\d+)?) ?([kmgt]?)(?:i?b)?$/i.exec(value.trim());\n if (!match) return undefined;\n const bytes = Math.floor(Number(match[1]) * (SIZE_UNITS[(match[2] ?? '').toLowerCase()] ?? 1));\n return bytes > 0 ? bytes : undefined;\n}\n\n/** Formats a date with the YYYY, MM, DD, HH, mm and ss tokens, in local time. */\nexport function formatDatePattern(date: Date, pattern: string): string {\n const pad = (n: number): string => String(n).padStart(2, '0');\n return pattern\n .replace(/YYYY/g, String(date.getFullYear()))\n .replace(/MM/g, pad(date.getMonth() + 1))\n .replace(/DD/g, pad(date.getDate()))\n .replace(/HH/g, pad(date.getHours()))\n .replace(/mm/g, pad(date.getMinutes()))\n .replace(/ss/g, pad(date.getSeconds()));\n}\n\n/**\n * Appends log entries to a local file with automatic rotation.\n *\n * Rotates by size (`maxSize`), by date (`datePattern`), by interval\n * (`rotation.interval`) or by a custom `rotation.shouldRotate` check. Rotated\n * files can be gzipped (`zippedArchive`) and pruned to the newest `maxFiles`.\n *\n * Without a `rotation` block the live file keeps its name (`app.log`) and\n * rotated files are renamed beside it (`app-2026-10-22T09-30-00-000.log`).\n * With a `rotation` block every file, the live one included, is timestamped.\n *\n * Files are created with mode 0o600 and directories with 0o700; override with\n * `fileMode` and `dirMode`. At most `maxQueue` (default 10000) entries wait in\n * memory; the rest are dropped per `queuePolicy` and counted in a warning.\n *\n * @example\n * transports: { file: { filename: 'app.log', dirname: './logs', maxSize: '50MB' } }\n */\nexport class FileTransport implements ITransport, IBatchTransport {\n public readonly name = 'file';\n public readonly level?: string | undefined;\n public readonly batchSize?: number;\n public readonly flushInterval?: number;\n public readonly filter?: (entry: TransportLogEntry) => boolean;\n\n private readonly config: FileTransportConfig;\n private readonly maxBytes: number | undefined;\n private readonly maxFiles: number | undefined;\n private readonly compress: boolean;\n private readonly intervalMs: number | undefined;\n private readonly timestampedActive: boolean;\n private readonly dir: string;\n private readonly base: string;\n private readonly ext: string;\n\n private batch: TransportLogEntry[] = [];\n private batchTimer?: NodeJS.Timeout | undefined;\n private lastRotation: Date = new Date();\n private currentFilePath: string;\n private bytesWritten = 0;\n private period: string | undefined;\n private rotateRequested = false;\n private rotationWaiters: Array<() => void> = [];\n /**\n * The one drain loop, or undefined when idle. Only one ever runs, so the batch\n * is never snapshotted and written twice, and rotation (which runs inside it)\n * never races a write.\n */\n private draining: Promise<void> | undefined;\n /** Entries queued and entries written (or failed) so far; flush() waits on these. */\n private enqueued = 0;\n private settled = 0;\n private flushWaiters: FlushWaiter[] = [];\n private readonly maxQueue: number;\n private readonly fileMode: number;\n private readonly dirMode: number;\n private readonly queuePolicy: 'drop-oldest' | 'drop-newest' | 'block';\n private spaceWaiters: Array<() => void> = [];\n private inFlight = 0;\n private dropped = 0;\n private lastDropWarning = 0;\n /** Gzip and pruning of rotated files run off the write path, one at a time. */\n private housekeeping: Promise<void> = Promise.resolve();\n\n constructor(config: FileTransportConfig) {\n this.config = {\n format: 'json',\n batchSize: 100,\n flushInterval: 5000,\n ...config,\n };\n this.level = config.level;\n this.batchSize = this.config.batchSize || 100;\n this.flushInterval = this.config.flushInterval || 5000;\n if (config.filter) this.filter = config.filter;\n\n const rotation = config.rotation;\n const rawMaxSize = rotation?.maxSize ?? config.maxSize;\n this.maxBytes = parseSize(rawMaxSize);\n if (rawMaxSize !== undefined && this.maxBytes === undefined) {\n internalWarn(\n `FileTransport: maxSize ${JSON.stringify(rawMaxSize)} is not a valid size (try '50MB'); size rotation is off`\n );\n }\n const maxQueue = config.maxQueue;\n if (maxQueue !== undefined && !(Number.isInteger(maxQueue) && maxQueue > 0)) {\n internalWarn(\n `FileTransport: maxQueue ${String(maxQueue)} is not a positive integer; using ${DEFAULT_MAX_QUEUE}`\n );\n }\n this.maxQueue =\n maxQueue !== undefined && Number.isInteger(maxQueue) && maxQueue > 0\n ? maxQueue\n : DEFAULT_MAX_QUEUE;\n this.fileMode = config.fileMode ?? DEFAULT_FILE_MODE;\n this.dirMode = config.dirMode ?? DEFAULT_DIR_MODE;\n this.queuePolicy = config.queuePolicy ?? 'drop-oldest';\n this.maxFiles = rotation?.maxFiles ?? config.maxFiles;\n this.compress = rotation?.compress ?? config.zippedArchive ?? false;\n this.intervalMs = rotation?.interval ? this.parseInterval(rotation.interval) : undefined;\n this.timestampedActive = rotation !== undefined;\n\n this.dir = this.resolveDir();\n this.ext = path.extname(config.filename);\n this.base = path.basename(config.filename, this.ext);\n try {\n fs.mkdirSync(this.dir, { recursive: true, mode: this.dirMode });\n } catch (error) {\n internalError(`FileTransport could not create ${this.dir}`, error);\n }\n\n this.currentFilePath = this.timestampedActive\n ? this.uniquePath(this.timestamp(new Date()))\n : path.join(this.dir, `${this.base}${this.ext}`);\n\n // Pick up where a previous process left off, so a restart next to a 49MB\n // app.log still rotates at 50MB and yesterday's file still rolls over today.\n let existing: fs.Stats | undefined;\n try {\n existing = fs.statSync(this.currentFilePath);\n } catch {\n existing = undefined;\n }\n this.bytesWritten = existing?.size ?? 0;\n if (config.datePattern) {\n this.period = formatDatePattern(existing?.mtime ?? new Date(), config.datePattern);\n }\n }\n\n async write(entry: TransportLogEntry): Promise<void> {\n try {\n while (this.queuePolicy === 'block' && this.queueFull()) await this.roomAvailable();\n if (this.config.batchSize && this.config.batchSize > 1) {\n this.addToBatch(entry);\n } else {\n // Unbatched writes still go through the drain so they stay in order and\n // share the rotation checks with batched ones.\n this.enqueue(entry);\n await this.flush();\n }\n } catch (error) {\n throw new Error(`File transport write failed: ${(error as Error).message}`, { cause: error });\n }\n }\n\n /**\n * Resolves once every entry queued before the call is on disk. It does not\n * wait for entries queued afterwards, so under steady traffic it still returns\n * instead of waiting for a quiet moment that never comes.\n */\n async flush(): Promise<void> {\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = undefined;\n }\n if (this.settled >= this.enqueued) return;\n\n const done = new Promise<void>((resolve, reject) => {\n this.flushWaiters.push({ upTo: this.enqueued, resolve, reject });\n });\n this.startDrain();\n await done;\n }\n\n /** Writes out anything pending, then starts a new file. */\n async rotate(): Promise<void> {\n await this.flush();\n const done = new Promise<void>((resolve) => this.rotationWaiters.push(resolve));\n this.rotateRequested = true;\n this.startDrain();\n await done;\n }\n\n private enqueue(entry: TransportLogEntry): void {\n if (this.queueFull()) {\n // Block waits in write(); a caller that got here anyway (addToBatch) has\n // nowhere to wait, so it sheds the new entry. The batch being written\n // can't be taken back, so with nothing waiting the new entry goes too.\n if (this.queuePolicy === 'drop-oldest' && this.batch.length > 0) {\n // Replacing in place keeps enqueued == settled + waiting, so flush()\n // waiters never resolve before the entries they cover are written.\n this.batch.shift();\n this.batch.push(entry);\n }\n this.noteDropped();\n return;\n }\n this.batch.push(entry);\n this.enqueued += 1;\n }\n\n private queueFull(): boolean {\n return this.batch.length + this.inFlight >= this.maxQueue;\n }\n\n private roomAvailable(): Promise<void> {\n const room = new Promise<void>((resolve) => this.spaceWaiters.push(resolve));\n this.startDrain();\n return room;\n }\n\n private releaseWaiters(): void {\n const waiting = this.spaceWaiters;\n this.spaceWaiters = [];\n for (const resolve of waiting) resolve();\n }\n\n private noteDropped(): void {\n this.dropped += 1;\n this.reportDropped(DROP_WARN_INTERVAL_MS);\n }\n\n private reportDropped(minGapMs: number): void {\n const now = Date.now();\n if (this.dropped === 0 || now - this.lastDropWarning < minGapMs) return;\n internalWarn(\n `FileTransport: dropped ${this.dropped} log line${this.dropped === 1 ? '' : 's'} (queue full at maxQueue ${this.maxQueue}, policy ${this.queuePolicy})`\n );\n this.dropped = 0;\n this.lastDropWarning = now;\n }\n\n private startDrain(): void {\n if (this.draining) return;\n this.draining = this.drain().finally(() => {\n this.draining = undefined;\n });\n }\n\n /**\n * Writes the batch one snapshot at a time. Entries queued while a write is in\n * flight are picked up by the next iteration, so one drain empties the batch\n * however much is logged meanwhile.\n */\n private async drain(): Promise<void> {\n while (this.batch.length > 0 || this.rotateRequested) {\n if (this.rotateRequested) {\n this.rotateRequested = false;\n const waiting = this.rotationWaiters;\n this.rotationWaiters = [];\n try {\n await this.rotateFile();\n } catch (error) {\n internalError('FileTransport rotation failed', error);\n }\n for (const resolve of waiting) resolve();\n continue;\n }\n // Detach the current batch SYNCHRONOUSLY before awaiting, so entries pushed\n // during the write land in a fresh array and are never re-written.\n const pending = this.batch;\n this.batch = [];\n this.inFlight = pending.length;\n let failure: unknown;\n try {\n await this.writeBatch(pending);\n } catch (error) {\n failure = error;\n }\n this.inFlight = 0;\n this.settled += pending.length;\n this.settle(failure);\n this.releaseWaiters();\n }\n }\n\n /**\n * Resolves the flush() calls whose entries are now written. A failed batch\n * fails every flush still waiting, since each one covers that batch.\n */\n private settle(failure: unknown): void {\n if (failure !== undefined) {\n if (this.flushWaiters.length === 0) internalError('FileTransport write failed', failure);\n for (const waiter of this.flushWaiters) waiter.error ??= failure;\n }\n const waiting: FlushWaiter[] = [];\n for (const waiter of this.flushWaiters) {\n if (waiter.upTo > this.settled) waiting.push(waiter);\n else if (waiter.error === undefined) waiter.resolve();\n else waiter.reject(waiter.error);\n }\n this.flushWaiters = waiting;\n }\n\n private formatEntry(entry: TransportLogEntry): string {\n switch (this.config.format) {\n case 'json':\n return stringifyForLog(\n mergeCoreFields(\n {\n timestamp: entry.timestamp.toISOString(),\n level: entry.level,\n message: entry.message,\n },\n entry.data\n )\n );\n\n case 'csv': {\n const fields = [\n entry.timestamp.toISOString(),\n csvField(entry.level),\n csvField(entry.message),\n csvField(entry.context),\n csvField(entry.traceId),\n csvField(stringifyForLog(entry.data || {})),\n ];\n return fields.join(',');\n }\n\n case 'text':\n default:\n return `${entry.timestamp.toISOString()} [${stripControlChars(safeToString(entry.level)).toUpperCase()}] ${stripControlChars(safeToString(entry.message))}${entry.data ? ' ' + stringifyForLog(entry.data) : ''}`;\n }\n }\n\n addToBatch(entry: TransportLogEntry): void {\n this.enqueue(entry);\n\n // While a drain is running it picks this entry up by itself. Starting a flush\n // per entry here used to park one pending promise per log call until traffic\n // paused, which under steady load grew the heap by ~600 bytes a line.\n const threshold = Math.min(this.config.batchSize || 100, this.maxQueue);\n if (this.batch.length >= threshold && !this.draining) {\n // Cancel any pending interval flush — the threshold flush supersedes it, and\n // a leftover timer would fire a redundant flush after the batch is already gone.\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = undefined;\n }\n this.startDrain();\n } else if (!this.batchTimer && this.config.flushInterval) {\n this.batchTimer = setTimeout(() => {\n this.flush().catch((err: unknown) =>\n internalError('FileTransport interval flush failed', err)\n );\n }, this.config.flushInterval);\n }\n }\n\n /** Only ever called from drain(), so writes and rotations never overlap. */\n private async writeBatch(entries: TransportLogEntry[]): Promise<void> {\n if (entries.length === 0) return;\n\n if (this.timeToRotate()) await this.rotateFile();\n\n if (this.maxBytes === undefined) {\n const content = entries.map((entry) => this.formatEntry(entry)).join('\\n') + '\\n';\n await this.append(content);\n return;\n }\n\n // Split on line boundaries so no file grows past maxSize, except when a\n // single line is bigger than maxSize on its own.\n let chunk = '';\n let chunkBytes = 0;\n for (const entry of entries) {\n const line = this.formatEntry(entry) + '\\n';\n const lineBytes = Buffer.byteLength(line);\n const used = this.bytesWritten + chunkBytes;\n if (used > 0 && used + lineBytes > this.maxBytes) {\n if (chunk) await this.append(chunk, chunkBytes);\n chunk = '';\n chunkBytes = 0;\n await this.rotateFile();\n }\n chunk += line;\n chunkBytes += lineBytes;\n }\n if (chunk) await this.append(chunk, chunkBytes);\n }\n\n private async append(content: string, bytes = Buffer.byteLength(content)): Promise<void> {\n await appendFile(this.currentFilePath, content, { mode: this.fileMode });\n this.bytesWritten += bytes;\n }\n\n private timeToRotate(): boolean {\n if (\n this.intervalMs !== undefined &&\n Date.now() - this.lastRotation.getTime() >= this.intervalMs\n ) {\n return true;\n }\n if (this.config.datePattern && this.period !== undefined) {\n if (formatDatePattern(new Date(), this.config.datePattern) !== this.period) return true;\n }\n const shouldRotate = this.config.rotation?.shouldRotate;\n if (shouldRotate) {\n const stats: FileStats = {\n size: this.bytesWritten,\n created: this.lastRotation,\n modified: new Date(),\n };\n try {\n return shouldRotate(this.currentFilePath, stats);\n } catch (error) {\n internalError('FileTransport rotation.shouldRotate threw', error);\n }\n }\n return false;\n }\n\n private parseInterval(interval: string): number {\n const match = /^(\\d{1,6})([hdwmy])$/i.exec(interval.trim());\n if (!match) return 24 * 60 * 60 * 1000; // Default 1 day\n\n const value = Number.parseInt(match[1] || '1', 10);\n const unit = (match[2] || 'h').toLowerCase();\n\n switch (unit) {\n case 'h':\n return value * 60 * 60 * 1000;\n case 'd':\n return value * 24 * 60 * 60 * 1000;\n case 'w':\n return value * 7 * 24 * 60 * 60 * 1000;\n case 'm':\n return value * 30 * 24 * 60 * 60 * 1000;\n case 'y':\n return value * 365 * 24 * 60 * 60 * 1000;\n default:\n return value * 60 * 60 * 1000;\n }\n }\n\n private async rotateFile(): Promise<void> {\n const now = new Date();\n let archived: string | undefined;\n\n if (this.timestampedActive) {\n archived = this.currentFilePath;\n this.currentFilePath = this.uniquePath(this.timestamp(now));\n } else {\n archived = this.uniquePath(this.period ?? this.timestamp(now));\n try {\n await rename(this.currentFilePath, archived);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n internalError(`FileTransport could not rotate ${this.currentFilePath}`, error);\n // Keep writing to the same file and try again after another maxSize,\n // rather than retrying (and failing) on every line.\n this.bytesWritten = 0;\n return;\n }\n archived = undefined;\n }\n }\n\n this.bytesWritten = 0;\n this.lastRotation = now;\n if (this.config.datePattern) this.period = formatDatePattern(now, this.config.datePattern);\n\n if (archived === undefined || !fs.existsSync(archived)) return;\n const file = archived;\n this.housekeeping = this.housekeeping.then(async () => {\n if (this.compress) await this.compressFile(file);\n if (this.maxFiles) await this.cleanupOldFiles();\n });\n }\n\n private timestamp(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-').slice(0, 23);\n }\n\n /** `<base>-<stamp><ext>`, with `.1`, `.2`… added if that name (or its .gz) is taken. */\n private uniquePath(stamp: string): string {\n const taken = (p: string): boolean => fs.existsSync(p) || fs.existsSync(`${p}.gz`);\n let candidate = path.join(this.dir, `${this.base}-${stamp}${this.ext}`);\n let n = 0;\n while (taken(candidate) || candidate === this.currentFilePath) {\n n += 1;\n candidate = path.join(this.dir, `${this.base}-${stamp}.${n}${this.ext}`);\n }\n return candidate;\n }\n\n private resolveDir(): string {\n // If dirname is provided, use it; otherwise fall back to the directory part of filename\n if (this.config.dirname) {\n return this.config.dirname;\n }\n const fromFilename = path.dirname(this.config.filename);\n // path.dirname returns '.' for bare filenames like 'app.log' — use CWD in that case\n return fromFilename === '.' ? process.cwd() : fromFilename;\n }\n\n /**\n * Gzip the rotated file to `<file>.gz` and remove the original. Streams the\n * data so large log files don't have to be buffered in memory. Best-effort:\n * any failure is logged and leaves the original file intact rather than\n * throwing out of the rotation path.\n */\n private async compressFile(filePath: string): Promise<void> {\n const gzPath = `${filePath}.gz`;\n try {\n await pipeline(\n fs.createReadStream(filePath),\n createGzip(),\n fs.createWriteStream(gzPath, { mode: this.fileMode })\n );\n // Only delete the source once the compressed copy is fully written.\n await unlink(filePath);\n } catch (error) {\n internalError('Failed to compress rotated log file', error);\n // Clean up a partial .gz so a later cleanup pass doesn't keep a corrupt file.\n try {\n await unlink(gzPath);\n } catch {\n /* nothing to clean up */\n }\n }\n }\n\n /**\n * True for this transport's rotated files only: `<base>-<stamp><ext>` where the\n * stamp is digits and separators, optionally `.gz`. Another transport's\n * `app-error.log` must not count as one of `app.log`'s rotations.\n */\n private isOwnRotatedFile(file: string): boolean {\n const name = file.endsWith('.gz') ? file.slice(0, -3) : file;\n if (!name.startsWith(`${this.base}-`) || !name.endsWith(this.ext)) return false;\n const stamp = name.slice(this.base.length + 1, name.length - this.ext.length);\n return /^\\d[\\d\\-_.T]*$/.test(stamp);\n }\n\n private async cleanupOldFiles(): Promise<void> {\n if (!this.maxFiles) return;\n\n try {\n const files = await readdir(this.dir);\n const active = path.basename(this.currentFilePath);\n\n const fileStats = await Promise.all(\n files\n .filter((file) => file !== active && this.isOwnRotatedFile(file))\n .map(async (file) => {\n const filePath = path.join(this.dir, file);\n const stats = await stat(filePath);\n return { path: filePath, mtime: stats.mtimeMs };\n })\n );\n const newestFirst = fileStats.sort(\n (a, b) => b.mtime - a.mtime || b.path.localeCompare(a.path)\n );\n\n for (const file of newestFirst.slice(this.maxFiles)) {\n await unlink(file.path);\n }\n } catch (error) {\n internalError('Failed to cleanup old log files', error);\n }\n }\n\n async close(): Promise<void> {\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = undefined;\n }\n await this.flush();\n await this.housekeeping;\n this.reportDropped(0);\n }\n}\n","import type { GoogleAnalyticsTransportConfig, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalLog } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { AnalyticsTransport } from './analytics.transport';\n\n/**\n * Sends log entries as Google Analytics 4 Measurement Protocol events.\n *\n * @example\n * transports: {\n * googleAnalytics: { measurementId: 'G-XXXXXXX', apiSecret: process.env.GA_API_SECRET }\n * }\n */\nexport class GoogleAnalyticsTransport extends AnalyticsTransport {\n private gaConfig: GoogleAnalyticsTransportConfig;\n private baseUrl: string = 'https://www.google-analytics.com/mp/collect';\n private debugUrl: string = 'https://www.google-analytics.com/debug/mp/collect';\n\n constructor(config: GoogleAnalyticsTransportConfig) {\n super('google-analytics', config);\n this.gaConfig = config;\n }\n\n protected async initialize(): Promise<void> {\n try {\n // Validate required configuration\n if (!this.gaConfig.measurementId) {\n throw new Error('Google Analytics Measurement ID is required');\n }\n if (!this.gaConfig.apiSecret) {\n throw new Error('Google Analytics API Secret is required');\n }\n this.isReady = true;\n } catch (error) {\n internalError('Google Analytics transport initialization failed', error);\n throw error;\n }\n }\n\n protected async sendEntry(entry: TransportLogEntry): Promise<void> {\n const event = this.transformToGAEvent(entry);\n await this.sendEvent(event);\n }\n\n protected async sendBatch(entries: TransportLogEntry[]): Promise<void> {\n const events = entries.map((entry) => this.transformToGAEvent(entry));\n await this.sendEvents(events);\n }\n\n protected async cleanup(): Promise<void> {\n // Google Analytics doesn't require explicit cleanup\n }\n\n protected override reservedKeys(): readonly string[] {\n return [\n 'event_category',\n 'event_label',\n 'log_level',\n 'log_message',\n 'trace_id',\n 'app_name',\n 'custom_dimension_1',\n 'custom_dimension_2',\n 'custom_dimension_3',\n ];\n }\n\n private transformToGAEvent(entry: TransportLogEntry): Record<string, unknown> {\n const transformed = this.transformEntry(entry);\n\n // Map log levels to GA event categories\n const eventCategory = this.mapLogLevelToCategory(entry.level);\n\n return {\n name: 'log_event',\n params: {\n ...this.flattenData(transformed),\n event_category: eventCategory,\n event_label: entry.message,\n log_level: entry.level,\n log_message: entry.message,\n timestamp: entry.timestamp.toISOString(),\n ...(entry.context && { context: entry.context }),\n ...(entry.traceId && { trace_id: entry.traceId }),\n ...(entry.appName && { app_name: entry.appName }),\n ...(entry.environment && { environment: entry.environment }),\n // Add custom dimensions\n custom_dimension_1: entry.level,\n custom_dimension_2: entry.context || 'default',\n custom_dimension_3: entry.environment || 'production',\n },\n };\n }\n\n private mapLogLevelToCategory(level: string): string {\n const categoryMap: Record<string, string> = {\n error: 'Error',\n warn: 'Warning',\n warning: 'Warning',\n info: 'Information',\n debug: 'Debug',\n trace: 'Trace',\n verbose: 'Verbose',\n };\n\n return categoryMap[level.toLowerCase()] || 'Information';\n }\n\n private flattenData(data: Record<string, unknown>, prefix: string = ''): Record<string, unknown> {\n const flattened: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data)) {\n const newKey = prefix ? `${prefix}_${key}` : key;\n\n if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {\n Object.assign(flattened, this.flattenData(value as Record<string, unknown>, newKey));\n } else {\n // GA4 has parameter value limits\n const stringValue = String(value);\n flattened[newKey] = stringValue.length > 100 ? stringValue.slice(0, 100) : stringValue;\n }\n }\n\n return flattened;\n }\n\n private async sendEvent(event: unknown): Promise<void> {\n const payload = {\n client_id: this.gaConfig.clientId || this.generateClientId(),\n events: [event],\n };\n\n await this.makeRequest(payload);\n }\n\n private async sendEvents(events: unknown[]): Promise<void> {\n // GA4 allows up to 25 events per request\n const chunks = this.chunkArray(events, 25);\n\n for (const chunk of chunks) {\n const payload = {\n client_id: this.gaConfig.clientId || this.generateClientId(),\n events: chunk,\n };\n\n await this.makeRequest(payload);\n }\n }\n\n private async makeRequest(payload: unknown, debug: boolean = false): Promise<void> {\n const url = debug ? this.debugUrl : this.baseUrl;\n const params = new URLSearchParams({\n measurement_id: this.gaConfig.measurementId,\n api_secret: this.gaConfig.apiSecret,\n });\n\n try {\n const response = await fetch(`${url}?${params}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: stringifyForLog(payload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Google Analytics API error: ${response.status} ${response.statusText} - ${errorText}`\n );\n }\n\n if (debug) {\n const result = await response.json();\n internalLog(`GA4 Debug Response: ${JSON.stringify(result)}`);\n }\n } catch (error) {\n throw new Error(`Failed to send data to Google Analytics: ${(error as Error).message}`, {\n cause: error,\n });\n }\n }\n\n private generateClientId(): string {\n // Generate a UUID-like client ID (non-security use)\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n // eslint-disable-next-line sonarjs/pseudo-random -- non-security UUID generation\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private chunkArray<T>(array: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n return chunks;\n }\n\n // Public methods for additional GA4 functionality\n public async trackPageView(pagePath: string, pageTitle?: string): Promise<void> {\n const event = {\n name: 'page_view',\n params: {\n page_location: pagePath,\n page_title: pageTitle || pagePath,\n engagement_time_msec: 1,\n },\n };\n\n await this.sendEvent(event);\n }\n\n public async trackCustomEvent(\n eventName: string,\n parameters: Record<string, unknown> = {}\n ): Promise<void> {\n // Ensure event name follows GA4 conventions\n const sanitizedEventName = eventName.toLowerCase().replace(/[^a-z0-9_]/g, '_');\n\n const event = {\n name: sanitizedEventName,\n params: {\n ...this.flattenData(parameters),\n timestamp: new Date().toISOString(),\n },\n };\n\n await this.sendEvent(event);\n }\n\n public async trackConversion(\n conversionName: string,\n value?: number,\n currency?: string\n ): Promise<void> {\n const event = {\n name: conversionName,\n params: {\n ...(value !== undefined && { value }),\n ...(currency && { currency }),\n timestamp: new Date().toISOString(),\n },\n };\n\n await this.sendEvent(event);\n }\n\n public async trackError(\n errorMessage: string,\n errorCode?: string,\n fatal: boolean = false\n ): Promise<void> {\n const event = {\n name: 'exception',\n params: {\n description: errorMessage,\n fatal,\n ...(errorCode && { error_code: errorCode }),\n timestamp: new Date().toISOString(),\n },\n };\n\n await this.sendEvent(event);\n }\n\n public async setUserProperties(properties: Record<string, unknown>): Promise<void> {\n // GA4 user properties are set with events\n const event = {\n name: 'user_properties_update',\n params: {\n ...this.flattenData(properties),\n timestamp: new Date().toISOString(),\n },\n };\n\n await this.sendEvent(event);\n }\n\n // Enhanced measurement events\n public async trackFileDownload(fileName: string, fileExtension: string): Promise<void> {\n if (!this.gaConfig.enableEnhancedMeasurement) {\n return;\n }\n\n const event = {\n name: 'file_download',\n params: {\n file_name: fileName,\n file_extension: fileExtension,\n link_url: fileName,\n timestamp: new Date().toISOString(),\n },\n };\n\n await this.sendEvent(event);\n }\n\n public async trackVideoEngagement(\n videoTitle: string,\n videoUrl: string,\n progress: number\n ): Promise<void> {\n if (!this.gaConfig.enableEnhancedMeasurement) {\n return;\n }\n\n const event = {\n name: 'video_progress',\n params: {\n video_title: videoTitle,\n video_url: videoUrl,\n video_progress: progress,\n timestamp: new Date().toISOString(),\n },\n };\n\n await this.sendEvent(event);\n }\n}\n","import type { MixpanelTransportConfig, TransportLogEntry } from '../types/transport.types';\nimport { internalError } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport type { AnalyticsEvent, AnalyticsUser } from './analytics.transport';\nimport { AnalyticsTransport } from './analytics.transport';\n\n/**\n * Sends log entries as Mixpanel events via the Track and Engage HTTP APIs.\n *\n * @example\n * transports: { mixpanel: { token: process.env.MIXPANEL_TOKEN } }\n */\nexport class MixpanelTransport extends AnalyticsTransport {\n private mixpanelConfig: MixpanelTransportConfig;\n private baseUrl: string = 'https://api.mixpanel.com';\n\n constructor(config: MixpanelTransportConfig) {\n super('mixpanel', config);\n this.mixpanelConfig = config;\n }\n\n protected async initialize(): Promise<void> {\n try {\n // Validate required configuration\n if (!this.mixpanelConfig.token) {\n throw new Error('Mixpanel token is required');\n }\n this.isReady = true;\n } catch (error) {\n internalError('Mixpanel transport initialization failed', error);\n throw error;\n }\n }\n\n protected async sendEntry(entry: TransportLogEntry): Promise<void> {\n const event = this.transformToMixpanelEvent(entry);\n await this.trackEvent(event);\n }\n\n protected async sendBatch(entries: TransportLogEntry[]): Promise<void> {\n const events = entries.map((entry) => this.transformToMixpanelEvent(entry));\n await this.trackEvents(events);\n }\n\n protected async cleanup(): Promise<void> {\n // Mixpanel doesn't require explicit cleanup\n }\n\n protected override reservedKeys(): readonly string[] {\n return ['token', 'distinct_id', 'time', '$insert_id'];\n }\n\n private transformToMixpanelEvent(entry: TransportLogEntry): AnalyticsEvent {\n const transformed = this.transformEntry(entry);\n\n return {\n name: `log_${entry.level}`,\n properties: {\n ...transformed,\n token: this.mixpanelConfig.token,\n distinct_id: this.mixpanelConfig.distinct_id || 'anonymous',\n time: entry.timestamp.getTime(),\n $insert_id: this.generateInsertId(entry),\n // Add super properties if enabled\n ...(this.mixpanelConfig.enableSuperProperties ? this.mixpanelConfig.superProperties : {}),\n },\n timestamp: entry.timestamp,\n };\n }\n\n private async trackEvent(event: AnalyticsEvent): Promise<void> {\n const payload = {\n event: event.name,\n properties: {\n ...event.properties,\n token: this.mixpanelConfig.token,\n },\n };\n\n await this.makeRequest('/track', [payload]);\n }\n\n private async trackEvents(events: AnalyticsEvent[]): Promise<void> {\n const payload = events.map((event) => ({\n event: event.name,\n properties: {\n ...event.properties,\n token: this.mixpanelConfig.token,\n },\n }));\n\n await this.makeRequest('/track', payload);\n }\n\n private async makeRequest(endpoint: string, data: unknown): Promise<void> {\n const url = `${this.mixpanelConfig.endpoint || this.baseUrl}${endpoint}`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/plain',\n },\n body: stringifyForLog(data),\n });\n\n if (!response.ok) {\n throw new Error(`Mixpanel API error: ${response.status} ${response.statusText}`);\n }\n\n const result = await response.text();\n if (result !== '1') {\n throw new Error(`Mixpanel API returned error: ${result}`);\n }\n } catch (error) {\n throw new Error(`Failed to send data to Mixpanel: ${(error as Error).message}`, {\n cause: error,\n });\n }\n }\n\n private generateInsertId(entry: TransportLogEntry): string {\n // Generate a unique insert ID to prevent duplicate events\n const timestamp = entry.timestamp.getTime();\n const hash = this.simpleHash(entry.message + (entry.traceId || ''));\n return `${timestamp}_${hash}`;\n }\n\n private simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(36);\n }\n\n // Public methods for additional Mixpanel functionality\n public async identifyUser(user: AnalyticsUser): Promise<void> {\n if (!this.mixpanelConfig.enableUserTracking) {\n return;\n }\n\n const payload = {\n $token: this.mixpanelConfig.token,\n $distinct_id: user.id,\n $set: user.properties || {},\n };\n\n await this.makeRequest('/engage', [payload]);\n }\n\n public async setUserProperties(\n userId: string,\n properties: Record<string, unknown>\n ): Promise<void> {\n if (!this.mixpanelConfig.enableUserTracking) {\n return;\n }\n\n const payload = {\n $token: this.mixpanelConfig.token,\n $distinct_id: userId,\n $set: properties,\n };\n\n await this.makeRequest('/engage', [payload]);\n }\n\n public async trackCustomEvent(\n eventName: string,\n properties: Record<string, unknown> = {}\n ): Promise<void> {\n if (!this.mixpanelConfig.enableEventTracking) {\n return;\n }\n\n const payload = {\n event: eventName,\n properties: {\n ...properties,\n ...(this.mixpanelConfig.enableSuperProperties ? this.mixpanelConfig.superProperties : {}),\n token: this.mixpanelConfig.token,\n distinct_id: this.mixpanelConfig.distinct_id || 'anonymous',\n time: Date.now(),\n },\n };\n\n await this.makeRequest('/track', [payload]);\n }\n}\n","/**\n * The published version of this package.\n *\n * Kept in sync with `version` in package.json by the release process. Importing\n * from here (instead of reading package.json at runtime) keeps the built `dist`\n * free of a JSON import that bundlers and consumers would have to resolve.\n */\nexport const LOGIXIA_VERSION = '1.12.0';\n","import type { SegmentTransportConfig, TransportLogEntry } from '../types/transport.types';\nimport { internalError } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { LOGIXIA_VERSION } from '../version';\nimport type { AnalyticsUser } from './analytics.transport';\nimport { AnalyticsTransport } from './analytics.transport';\n\n/**\n * Sends log entries as Segment track/identify events via the HTTP Tracking API.\n *\n * @example\n * transports: { segment: { writeKey: process.env.SEGMENT_WRITE_KEY } }\n */\nexport class SegmentTransport extends AnalyticsTransport {\n private segmentConfig: SegmentTransportConfig;\n private baseUrl: string;\n\n constructor(config: SegmentTransportConfig) {\n super('segment', config);\n this.segmentConfig = {\n enableBatching: true,\n maxBatchSize: 100,\n flushAt: 20,\n flushInterval: 10000,\n ...config,\n };\n\n this.baseUrl = config.dataPlaneUrl || 'https://api.segment.io';\n }\n\n protected async initialize(): Promise<void> {\n try {\n // Validate required configuration\n if (!this.segmentConfig.writeKey) {\n throw new Error('Segment Write Key is required');\n }\n this.isReady = true;\n } catch (error) {\n internalError('Segment transport initialization failed', error);\n throw error;\n }\n }\n\n protected async sendEntry(entry: TransportLogEntry): Promise<void> {\n const segmentEvent = this.transformToSegmentEvent(entry);\n await this.track(segmentEvent);\n }\n\n protected async sendBatch(entries: TransportLogEntry[]): Promise<void> {\n if (!this.segmentConfig.enableBatching) {\n // Send individually if batching is disabled\n for (const entry of entries) {\n await this.sendEntry(entry);\n }\n return;\n }\n\n const segmentEvents = entries.map((entry) => this.transformToSegmentEvent(entry));\n await this.batchTrack(segmentEvents);\n }\n\n protected async cleanup(): Promise<void> {\n // Segment doesn't require explicit cleanup\n }\n\n protected override reservedKeys(): readonly string[] {\n return ['logger'];\n }\n\n private transformToSegmentEvent(entry: TransportLogEntry): Record<string, unknown> {\n const transformed = this.transformEntry(entry);\n\n return {\n type: 'track',\n event: `Log ${entry.level.charAt(0).toUpperCase() + entry.level.slice(1)}`,\n userId: this.extractUserId(entry),\n anonymousId: this.generateAnonymousId(),\n timestamp: entry.timestamp.toISOString(),\n properties: {\n ...transformed,\n level: entry.level,\n message: entry.message,\n logger: 'logixia',\n ...(entry.context && { context: entry.context }),\n ...(entry.traceId && { traceId: entry.traceId }),\n ...(entry.appName && { appName: entry.appName }),\n ...(entry.environment && { environment: entry.environment }),\n },\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n app: {\n name: entry.appName || 'unknown',\n version: '1.0.0',\n environment: entry.environment || 'production',\n },\n ...(entry.traceId && {\n trace: {\n trace_id: entry.traceId,\n },\n }),\n },\n integrations: {\n // Disable integrations that might interfere\n All: false,\n 'Segment.io': true,\n },\n };\n }\n\n private async track(event: unknown): Promise<void> {\n await this.makeRequest('/v1/track', event);\n }\n\n private async batchTrack(events: unknown[]): Promise<void> {\n // Split into chunks based on maxBatchSize\n const chunks = this.chunkArray(events, this.segmentConfig.maxBatchSize || 100);\n\n for (const chunk of chunks) {\n const payload = {\n batch: chunk,\n };\n\n await this.makeRequest('/v1/batch', payload);\n }\n }\n\n private async makeRequest(endpoint: string, data: unknown): Promise<void> {\n const url = `${this.baseUrl}${endpoint}`;\n\n // Encode write key for basic auth\n const auth = Buffer.from(`${this.segmentConfig.writeKey}:`).toString('base64');\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Basic ${auth}`,\n 'User-Agent': `logixia/${LOGIXIA_VERSION}`,\n },\n body: stringifyForLog(data),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Segment API error: ${response.status} ${response.statusText} - ${errorText}`\n );\n }\n } catch (error) {\n throw new Error(`Failed to send data to Segment: ${(error as Error).message}`, {\n cause: error,\n });\n }\n }\n\n private extractUserId(entry: TransportLogEntry): string | undefined {\n const field = this.segmentConfig.userIdField ?? 'userId';\n const id = entry.data?.[field];\n return typeof id === 'string' ? id : undefined;\n }\n\n private generateAnonymousId(): string {\n // Generate a UUID-like anonymous ID (non-security use)\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n // eslint-disable-next-line sonarjs/pseudo-random -- non-security UUID generation\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private chunkArray<T>(array: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n return chunks;\n }\n\n // Public methods for Segment-specific functionality\n public async identify(user: AnalyticsUser): Promise<void> {\n const payload = {\n type: 'identify',\n userId: user.id,\n anonymousId: this.generateAnonymousId(),\n timestamp: new Date().toISOString(),\n traits: {\n ...user.properties,\n ...user.traits,\n },\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n },\n };\n\n await this.makeRequest('/v1/identify', payload);\n }\n\n public async page(name: string, properties: Record<string, unknown> = {}): Promise<void> {\n const payload = {\n type: 'page',\n name,\n anonymousId: this.generateAnonymousId(),\n timestamp: new Date().toISOString(),\n properties: {\n ...properties,\n url: properties.url || `/${name.toLowerCase().replace(/\\s+/g, '-')}`,\n title: properties.title || name,\n },\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n page: {\n url: properties.url || `/${name.toLowerCase().replace(/\\s+/g, '-')}`,\n title: properties.title || name,\n },\n },\n };\n\n await this.makeRequest('/v1/page', payload);\n }\n\n public async screen(name: string, properties: Record<string, unknown> = {}): Promise<void> {\n const payload = {\n type: 'screen',\n name,\n anonymousId: this.generateAnonymousId(),\n timestamp: new Date().toISOString(),\n properties: {\n ...properties,\n },\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n screen: {\n name,\n },\n },\n };\n\n await this.makeRequest('/v1/screen', payload);\n }\n\n public async group(\n groupId: string,\n traits: Record<string, unknown> = {},\n userId?: string\n ): Promise<void> {\n const payload = {\n type: 'group',\n groupId,\n userId,\n anonymousId: this.generateAnonymousId(),\n timestamp: new Date().toISOString(),\n traits,\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n },\n };\n\n await this.makeRequest('/v1/group', payload);\n }\n\n public async alias(userId: string, previousId: string): Promise<void> {\n const payload = {\n type: 'alias',\n userId,\n previousId,\n timestamp: new Date().toISOString(),\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n },\n };\n\n await this.makeRequest('/v1/alias', payload);\n }\n\n public async trackCustomEvent(\n eventName: string,\n properties: Record<string, unknown> = {},\n userId?: string\n ): Promise<void> {\n const payload = {\n type: 'track',\n event: eventName,\n userId,\n anonymousId: this.generateAnonymousId(),\n timestamp: new Date().toISOString(),\n properties,\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n },\n };\n\n await this.makeRequest('/v1/track', payload);\n }\n\n // E-commerce tracking methods\n public async trackPurchase(\n orderId: string,\n products: unknown[],\n revenue: number,\n currency: string = 'USD'\n ): Promise<void> {\n const payload = {\n type: 'track',\n event: 'Order Completed',\n anonymousId: this.generateAnonymousId(),\n timestamp: new Date().toISOString(),\n properties: {\n orderId,\n revenue,\n currency,\n products,\n },\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n },\n };\n\n await this.makeRequest('/v1/track', payload);\n }\n\n public async trackProductViewed(product: unknown): Promise<void> {\n const payload = {\n type: 'track',\n event: 'Product Viewed',\n anonymousId: this.generateAnonymousId(),\n timestamp: new Date().toISOString(),\n properties: product,\n context: {\n library: {\n name: 'logixia',\n version: LOGIXIA_VERSION,\n },\n },\n };\n\n await this.makeRequest('/v1/track', payload);\n }\n}\n","import { EventEmitter } from 'node:events';\n\nimport type { LogEntry } from '../types';\nimport type {\n IAsyncTransport,\n IBatchTransport,\n ITransport,\n TransportConfig,\n TransportLogEntry,\n TransportMetrics,\n TransportType,\n} from '../types/transport.types';\nimport { internalLog, internalWarn } from '../utils/internal-log';\nimport { ConsoleTransport } from './console.transport';\nimport { DatabaseTransport } from './database.transport';\nimport { DataDogTransport } from './datadog.transport';\nimport { FileTransport } from './file.transport';\nimport { GoogleAnalyticsTransport } from './google-analytics.transport';\nimport { MixpanelTransport } from './mixpanel.transport';\nimport { SegmentTransport } from './segment.transport';\n\n// Tiny sleep helper — avoids setTimeout wrapper object allocation on every call\nfunction _sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class TransportManager extends EventEmitter {\n private transports: Map<string, ITransport> = new Map();\n private metrics: Map<string, TransportMetrics> = new Map();\n private isShuttingDown = false;\n private transportLevelPreferences: Map<string, string[]> = new Map(); // Store user preferences for transport levels\n private warnedPromptDeprecated = false;\n\n constructor(config: TransportConfig = {}) {\n super();\n this.setupTransports(config);\n }\n\n private setupTransports(config: TransportConfig): void {\n if (config.console) {\n const consoleTransport = new ConsoleTransport(\n typeof config.console === 'object' ? config.console : {}\n );\n this.addTransport(consoleTransport, 'console');\n }\n\n // Setup file transports\n if (config.file) {\n const fileConfigs = Array.isArray(config.file) ? config.file : [config.file];\n for (const [index, fileConfig] of fileConfigs.entries()) {\n const fileTransport = new FileTransport(fileConfig);\n this.addTransport(fileTransport, `file-${index}`);\n }\n }\n\n // Setup database transports\n if (config.database) {\n const dbConfigs = Array.isArray(config.database) ? config.database : [config.database];\n for (const [index, dbConfig] of dbConfigs.entries()) {\n const dbTransport = new DatabaseTransport(dbConfig);\n this.addTransport(dbTransport, `database-${index}`);\n }\n }\n\n // Setup analytics transports\n if (config.analytics) {\n if (config.analytics.mixpanel) {\n const mixpanelConfigs = Array.isArray(config.analytics.mixpanel)\n ? config.analytics.mixpanel\n : [config.analytics.mixpanel];\n for (const [index, mixpanelConfig] of mixpanelConfigs.entries()) {\n const mixpanelTransport = new MixpanelTransport(mixpanelConfig);\n this.addTransport(mixpanelTransport, `mixpanel-${index}`);\n }\n }\n\n if (config.analytics.datadog) {\n const datadogConfigs = Array.isArray(config.analytics.datadog)\n ? config.analytics.datadog\n : [config.analytics.datadog];\n for (const [index, datadogConfig] of datadogConfigs.entries()) {\n const datadogTransport = new DataDogTransport(datadogConfig);\n this.addTransport(datadogTransport, `datadog-${index}`);\n }\n }\n\n if (config.analytics.googleAnalytics) {\n const gaConfigs = Array.isArray(config.analytics.googleAnalytics)\n ? config.analytics.googleAnalytics\n : [config.analytics.googleAnalytics];\n for (const [index, gaConfig] of gaConfigs.entries()) {\n const gaTransport = new GoogleAnalyticsTransport(gaConfig);\n this.addTransport(gaTransport, `google-analytics-${index}`);\n }\n }\n\n if (config.analytics.segment) {\n const segmentConfigs = Array.isArray(config.analytics.segment)\n ? config.analytics.segment\n : [config.analytics.segment];\n for (const [index, segmentConfig] of segmentConfigs.entries()) {\n const segmentTransport = new SegmentTransport(segmentConfig);\n this.addTransport(segmentTransport, `segment-${index}`);\n }\n }\n }\n\n // Setup custom transports\n if (config.custom) {\n for (const [index, transport] of config.custom.entries()) {\n this.addTransport(transport, `custom-${index}`);\n }\n }\n }\n\n addTransport(transport: ITransport, id?: string): void {\n const transportId = id || `${transport.name}-${Date.now()}`;\n this.transports.set(transportId, transport);\n\n // Initialize metrics\n this.metrics.set(transportId, {\n name: transportId,\n type: this.getTransportType(transport),\n logsWritten: 0,\n errors: 0,\n lastWrite: new Date(),\n averageWriteTime: 0,\n });\n\n this.emit('transport:added', transportId, transport);\n }\n\n removeTransport(id: string): boolean {\n const transport = this.transports.get(id);\n if (!transport) return false;\n\n this.transports.delete(id);\n this.metrics.delete(id);\n\n // Close transport if it has a close method\n if (transport.close) {\n transport.close().catch((error) => {\n this.emit('error', error, id);\n });\n }\n\n this.emit('transport:removed', id);\n return true;\n }\n\n /**\n * Write an entry to all eligible transports.\n *\n * Returns `void` when every transport completed synchronously (the common\n * console-only hot path — no Promise allocated, no microtask scheduled), and a\n * `Promise<void>` only when at least one transport's write is genuinely async.\n * The logger awaits the result either way; awaiting a non-thenable is a no-op,\n * so the public `Promise<void>` API is preserved without paying for it on the\n * sync path.\n */\n write(entry: LogEntry): void | Promise<void> {\n if (this.isShuttingDown) return;\n\n // Build the transport entry — exactOptionalPropertyTypes requires we only set\n // optional properties when they have a real value (not undefined).\n const transportEntry: TransportLogEntry = {\n timestamp: typeof entry.timestamp === 'string' ? new Date(entry.timestamp) : entry.timestamp,\n level: entry.level,\n message: entry.message,\n appName: entry.appName,\n };\n if (entry.payload !== undefined) transportEntry.data = entry.payload;\n if (entry.context !== undefined) transportEntry.context = entry.context;\n if (entry.traceId !== undefined) transportEntry.traceId = entry.traceId;\n if (entry.environment !== undefined) transportEntry.environment = entry.environment;\n\n let pending: Promise<void>[] | undefined;\n let syncFailure: { error: unknown } | undefined;\n\n for (const [id, transport] of this.transports) {\n if (!this.shouldTransportHandle(transport, transportEntry.level, id)) continue;\n if (transport.filter && !transport.filter(transportEntry)) continue;\n\n let result: void | Promise<void>;\n try {\n result = this.writeToTransport(id, transport, transportEntry);\n } catch (error) {\n // Keep going: one transport throwing must not stop the entry reaching the rest.\n syncFailure ??= { error };\n continue;\n }\n // Only collect genuinely-pending results. A synchronous transport returns\n // undefined → nothing to await.\n if (result && typeof (result as Promise<void>).then === 'function') {\n if (pending === undefined) pending = [];\n pending.push(result as Promise<void>);\n }\n }\n\n if (syncFailure) throw syncFailure.error;\n if (pending === undefined) return; // all transports were synchronous\n\n return Promise.allSettled(pending).then((results) => {\n const failures = results.filter((r) => r.status === 'rejected') as PromiseRejectedResult[];\n if (failures.length > 0) {\n const errors = failures.map((f) => f.reason);\n this.emit('error', new Error(`Transport write failures: ${errors.join(', ')}`), 'multiple');\n }\n });\n }\n\n private writeToTransport(\n id: string,\n transport: ITransport,\n entry: TransportLogEntry\n ): void | Promise<void> {\n const startTime = Date.now();\n const metrics = this.metrics.get(id)!;\n\n const onSuccess = (): void => {\n const writeTime = Date.now() - startTime;\n metrics.logsWritten++;\n metrics.lastWrite = new Date(startTime);\n // True cumulative mean: avg += (sample - avg) / n.\n metrics.averageWriteTime += (writeTime - metrics.averageWriteTime) / metrics.logsWritten;\n this.emit('log', entry);\n };\n const onError = (error: unknown): never => {\n metrics.errors++;\n this.emit('error', error, id);\n throw error;\n };\n\n // Fast path: no retry config AND the transport write is synchronous → do the\n // success bookkeeping inline with zero Promise allocation. Only fall back to\n // the async path when retry is configured or the write returns a thenable.\n if (!transport.retry || !transport.retry.maxRetries) {\n let result: void | Promise<void>;\n try {\n result = transport.write(entry);\n } catch (error) {\n return onError(error);\n }\n if (result && typeof (result as Promise<void>).then === 'function') {\n return (result as Promise<void>).then(onSuccess, onError);\n }\n onSuccess();\n return;\n }\n\n return this._writeWithRetry(transport, entry).then(onSuccess, onError);\n }\n\n /**\n * Feature 9: Multi-transport retry + failover.\n *\n * Attempts `transport.write()` up to `retry.maxRetries + 1` times with the\n * configured backoff strategy. If all attempts fail and a fallback transport\n * is configured, routes the entry there instead.\n */\n private async _writeWithRetry(transport: ITransport, entry: TransportLogEntry): Promise<void> {\n const retryCfg = transport.retry;\n\n // Fast path: no retry config → single attempt\n if (!retryCfg || !retryCfg.maxRetries) {\n await transport.write(entry);\n return;\n }\n\n const {\n maxRetries,\n backoff = 'exponential',\n delay: baseDelay = 500,\n maxDelay = 30_000,\n fallback,\n onExhausted,\n } = retryCfg;\n\n let lastError: Error = new Error('Unknown transport error');\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n await transport.write(entry);\n return; // success\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (attempt < maxRetries) {\n // Compute next delay\n let wait: number;\n switch (backoff) {\n case 'fixed':\n wait = baseDelay;\n break;\n case 'linear':\n wait = baseDelay * (attempt + 1);\n break;\n case 'exponential':\n default:\n wait = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);\n }\n\n this.emit('transport:retry', transport.name, attempt + 1, maxRetries, wait);\n await _sleep(wait);\n }\n }\n }\n\n // All retries exhausted — try fallback\n if (fallback) {\n this.emit('transport:fallback', transport.name, fallback.name);\n try {\n await fallback.write(entry);\n return;\n } catch {\n // fallback also failed — fall through to onExhausted\n }\n }\n\n onExhausted?.(lastError, entry);\n throw lastError;\n }\n\n private shouldTransportHandle(\n transport: ITransport,\n level: string,\n transportId?: string\n ): boolean {\n // Check user preferences first if transport ID is provided\n if (transportId && this.transportLevelPreferences.has(transportId)) {\n const allowedLevels = this.transportLevelPreferences.get(transportId)!;\n return allowedLevels.includes(level.toLowerCase());\n }\n\n if (!transport.level) return true;\n\n // Simple level comparison - you might want to implement proper level hierarchy\n const levels = ['error', 'warn', 'info', 'debug', 'trace', 'verbose'];\n const transportLevelIndex = levels.indexOf(transport.level.toLowerCase());\n const entryLevelIndex = levels.indexOf(level.toLowerCase());\n\n if (transportLevelIndex === -1 || entryLevelIndex === -1) return true;\n\n return entryLevelIndex <= transportLevelIndex;\n }\n\n private getTransportType(transport: ITransport): TransportType {\n if (transport.name === 'console') return 'console';\n if (transport.name === 'file') return 'file';\n if (transport.name === 'database') return 'database';\n if (\n transport.name === 'mixpanel' ||\n transport.name === 'datadog' ||\n transport.name === 'google-analytics' ||\n transport.name === 'segment'\n ) {\n return 'analytics' as TransportType;\n }\n return 'custom';\n }\n\n // Async transport management\n async waitForReady(): Promise<void> {\n const readyPromises: Promise<boolean>[] = [];\n\n for (const transport of this.transports.values()) {\n if (this.isAsyncTransport(transport)) {\n readyPromises.push(transport.isReady!());\n }\n }\n\n if (readyPromises.length > 0) {\n await Promise.all(readyPromises);\n }\n }\n\n async flush(): Promise<void> {\n const flushPromises: Promise<void>[] = [];\n\n for (const [id, transport] of this.transports) {\n if (this.isBatchTransport(transport)) {\n const flushPromise = transport.flush().catch((error) => {\n this.emit('error', error, id);\n });\n flushPromises.push(flushPromise);\n }\n }\n\n await Promise.allSettled(flushPromises);\n this.emit('flush', 'all', this.transports.size);\n }\n\n async close(): Promise<void> {\n // Flush first so any in-flight log calls that race in during shutdown still\n // get written — only block new writes once we move to the close phase.\n await this.flush();\n\n this.isShuttingDown = true;\n\n // Close all transports\n const closePromises: Promise<void>[] = [];\n\n for (const [id, transport] of this.transports) {\n if (transport.close) {\n const closePromise = transport.close().catch((error) => {\n this.emit('error', error, id);\n });\n closePromises.push(closePromise);\n }\n }\n\n await Promise.allSettled(closePromises);\n\n this.transports.clear();\n this.metrics.clear();\n this.removeAllListeners();\n }\n\n // Utility methods\n getTransports(): string[] {\n return Array.from(this.transports.keys());\n }\n\n getTransport(id: string): ITransport | undefined {\n return this.transports.get(id);\n }\n\n getMetrics(): TransportMetrics[] {\n return Array.from(this.metrics.values());\n }\n\n getMetricsForTransport(id: string): TransportMetrics | undefined {\n return this.metrics.get(id);\n }\n\n isTransportReady(id: string): Promise<boolean> {\n const transport = this.transports.get(id);\n if (!transport) return Promise.resolve(false);\n\n if (this.isAsyncTransport(transport)) {\n return transport.isReady!();\n }\n\n return Promise.resolve(true);\n }\n\n // Type guards\n private isAsyncTransport(transport: ITransport): transport is IAsyncTransport {\n return (\n 'isReady' in transport && typeof (transport as Record<string, unknown>).isReady === 'function'\n );\n }\n\n private isBatchTransport(transport: ITransport): transport is IBatchTransport {\n return (\n 'flush' in transport && typeof (transport as Record<string, unknown>).flush === 'function'\n );\n }\n\n // Event handling helpers\n onLog(callback: (entry: TransportLogEntry) => void): void {\n this.on('log', callback);\n }\n\n onError(callback: (error: Error, transport: string) => void): void {\n this.on('error', callback);\n }\n\n onFlush(callback: (transport: string, count: number) => void): void {\n this.on('flush', callback);\n }\n\n onTransportAdded(callback: (id: string, transport: ITransport) => void): void {\n this.on('transport:added', callback);\n }\n\n onTransportRemoved(callback: (id: string) => void): void {\n this.on('transport:removed', callback);\n }\n\n // Health check\n async healthCheck(): Promise<{\n healthy: boolean;\n details: Record<string, unknown>;\n }> {\n const details: Record<string, unknown> = {};\n let healthy = true;\n\n for (const [id] of this.transports) {\n try {\n const isReady = await this.isTransportReady(id);\n details[id] = { ready: isReady, metrics: this.metrics.get(id) };\n if (!isReady) healthy = false;\n } catch (error) {\n details[id] = {\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n healthy = false;\n }\n }\n\n return { healthy, details };\n }\n\n // Transport Level Configuration Methods\n\n /** @deprecated Library code never prompts on stdin. Use setTransportLevels(). No-op. */\n enableLevelPrompting(): void {\n this.warnPromptingRemoved();\n }\n\n /** @deprecated Library code never prompts on stdin. No-op. */\n disableLevelPrompting(): void {\n this.warnPromptingRemoved();\n }\n\n private warnPromptingRemoved(): void {\n if (this.warnedPromptDeprecated) return;\n this.warnedPromptDeprecated = true;\n internalWarn(\n 'Transport level prompting was removed: logixia never reads stdin. Use setTransportLevels(id, levels) instead.'\n );\n }\n\n setTransportLevels(transportId: string, levels: string[]): void {\n this.transportLevelPreferences.set(transportId, levels);\n internalLog(`Transport '${transportId}' configured for levels: ${levels.join(', ')}`);\n }\n\n getTransportLevels(transportId: string): string[] | undefined {\n return this.transportLevelPreferences.get(transportId);\n }\n\n clearTransportLevelPreferences(): void {\n this.transportLevelPreferences.clear();\n internalLog('Transport level preferences cleared');\n }\n}\n"],"mappings":"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"}