logixia 1.10.1 → 1.10.2

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 (36) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/index.d.mts +1 -1
  3. package/dist/index.d.mts.map +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -2
  7. package/dist/index.mjs +2 -2
  8. package/dist/{logitron-logger.module-NLCrDkXA.d.mts → logitron-logger.module-B8NklSC4.d.mts} +6 -3
  9. package/dist/logitron-logger.module-B8NklSC4.d.mts.map +1 -0
  10. package/dist/{logitron-logger.module-209uTmVY.d.ts → logitron-logger.module-BLT1y5Iq.d.ts} +6 -3
  11. package/dist/logitron-logger.module-BLT1y5Iq.d.ts.map +1 -0
  12. package/dist/{logitron-logger.module-BFNsUfx7.mjs → logitron-logger.module-Bt_Jei1V.mjs} +73 -21
  13. package/dist/logitron-logger.module-Bt_Jei1V.mjs.map +1 -0
  14. package/dist/{logitron-logger.module-DC0qwI3N.js → logitron-logger.module-bJ1hGhaL.js} +73 -21
  15. package/dist/logitron-logger.module-bJ1hGhaL.js.map +1 -0
  16. package/dist/nest.d.mts +1 -1
  17. package/dist/nest.d.ts +1 -1
  18. package/dist/nest.js +2 -2
  19. package/dist/nest.mjs +2 -2
  20. package/dist/{transport.manager-DsvWfJ_b.mjs → transport.manager-CaL4XuLD.mjs} +36 -17
  21. package/dist/transport.manager-CaL4XuLD.mjs.map +1 -0
  22. package/dist/{transport.manager-CgeSNG_8.js → transport.manager-zgEZCJhR.js} +36 -17
  23. package/dist/transport.manager-zgEZCJhR.js.map +1 -0
  24. package/dist/transports.d.mts +14 -1
  25. package/dist/transports.d.mts.map +1 -1
  26. package/dist/transports.d.ts +14 -1
  27. package/dist/transports.d.ts.map +1 -1
  28. package/dist/transports.js +1 -1
  29. package/dist/transports.mjs +1 -1
  30. package/package.json +1 -1
  31. package/dist/logitron-logger.module-209uTmVY.d.ts.map +0 -1
  32. package/dist/logitron-logger.module-BFNsUfx7.mjs.map +0 -1
  33. package/dist/logitron-logger.module-DC0qwI3N.js.map +0 -1
  34. package/dist/logitron-logger.module-NLCrDkXA.d.mts.map +0 -1
  35. package/dist/transport.manager-CgeSNG_8.js.map +0 -1
  36. package/dist/transport.manager-DsvWfJ_b.mjs.map +0 -1
@@ -128,7 +128,8 @@ var ConsoleTransport = class ConsoleTransport {
128
128
  }
129
129
  write(entry) {
130
130
  const formatted = this.formatEntry(entry) + "\n";
131
- (entry.level.toLowerCase() === "error" ? process.stderr : process.stdout).write(formatted);
131
+ const lvl = entry.level.toLowerCase();
132
+ (lvl === "error" || lvl === "warn" ? process.stderr : process.stdout).write(formatted);
132
133
  return Promise.resolve();
133
134
  }
134
135
  formatEntry(entry) {
@@ -141,13 +142,13 @@ var ConsoleTransport = class ConsoleTransport {
141
142
  traceId: entry.traceId,
142
143
  appName: entry.appName,
143
144
  environment: entry.environment
144
- }, null, 2);
145
+ });
145
146
  const parts = [];
146
147
  if (this.config.timestamp !== false) {
147
148
  const timestamp = entry.timestamp.toISOString();
148
149
  parts.push(this.colorize(timestamp, "gray"));
149
150
  }
150
- const level = entry.level.toUpperCase().padEnd(5);
151
+ const level = ConsoleTransport.sanitize(entry.level).toUpperCase().padEnd(5);
151
152
  const coloredLevel = this.colorize(level, this.getLevelColor(entry.level));
152
153
  parts.push(coloredLevel);
153
154
  if (entry.context) {
@@ -905,14 +906,26 @@ var FileTransport = class {
905
906
  }
906
907
  }
907
908
  async flush() {
908
- if (this.batch.length > 0) {
909
- await this.writeBatch([...this.batch]);
910
- this.batch = [];
911
- }
912
909
  if (this.batchTimer) {
913
910
  clearTimeout(this.batchTimer);
914
911
  this.batchTimer = void 0;
915
912
  }
913
+ if (!this.flushPromise) this.flushPromise = this.drain().finally(() => {
914
+ this.flushPromise = void 0;
915
+ });
916
+ await this.flushPromise;
917
+ }
918
+ /**
919
+ * Drains the batch to disk one snapshot at a time. Entries appended while a
920
+ * write is in flight are picked up by the next loop iteration, so a single
921
+ * drain() empties the batch completely regardless of concurrent writes.
922
+ */
923
+ async drain() {
924
+ while (this.batch.length > 0) {
925
+ const pending = this.batch;
926
+ this.batch = [];
927
+ await this.writeBatch(pending);
928
+ }
916
929
  }
917
930
  formatEntry(entry) {
918
931
  switch (this.config.format) {
@@ -939,8 +952,13 @@ var FileTransport = class {
939
952
  }
940
953
  addToBatch(entry) {
941
954
  this.batch.push(entry);
942
- if (this.batch.length >= (this.config.batchSize || 100)) this.flush().catch((err) => internalError("FileTransport batch flush failed", err));
943
- else if (!this.batchTimer && this.config.flushInterval) this.batchTimer = setTimeout(() => {
955
+ if (this.batch.length >= (this.config.batchSize || 100)) {
956
+ if (this.batchTimer) {
957
+ clearTimeout(this.batchTimer);
958
+ this.batchTimer = void 0;
959
+ }
960
+ this.flush().catch((err) => internalError("FileTransport batch flush failed", err));
961
+ } else if (!this.batchTimer && this.config.flushInterval) this.batchTimer = setTimeout(() => {
944
962
  this.flush().catch((err) => internalError("FileTransport interval flush failed", err));
945
963
  }, this.config.flushInterval);
946
964
  }
@@ -956,11 +974,12 @@ var FileTransport = class {
956
974
  else await writeFile(this.currentFilePath, content, { flag: "a" });
957
975
  }
958
976
  shouldRotateNow() {
959
- if (!this.config.rotation) return false;
977
+ var _this$config$rotation;
978
+ if (!((_this$config$rotation = this.config.rotation) === null || _this$config$rotation === void 0 ? void 0 : _this$config$rotation.interval)) return false;
960
979
  return (/* @__PURE__ */ new Date()).getTime() - this.lastRotation.getTime() >= this.parseInterval(this.config.rotation.interval);
961
980
  }
962
981
  parseInterval(interval) {
963
- const match = interval.match(/(\d+)([hdwmy])/i);
982
+ const match = (/* @__PURE__ */ new RegExp(/(\d+)([hdwmy])/i)).exec(interval);
964
983
  if (!match) return 1440 * 60 * 1e3;
965
984
  const value = Number.parseInt(match[1] || "1", 10);
966
985
  switch ((match[2] || "h").toLowerCase()) {
@@ -973,7 +992,7 @@ var FileTransport = class {
973
992
  }
974
993
  }
975
994
  async rotate() {
976
- var _this$config$rotation, _this$config$rotation2;
995
+ var _this$config$rotation2, _this$config$rotation3;
977
996
  await this.flush();
978
997
  if (this.writeStream) {
979
998
  await new Promise((resolve) => {
@@ -984,8 +1003,8 @@ var FileTransport = class {
984
1003
  const oldPath = this.currentFilePath;
985
1004
  this.currentFilePath = this.generateFilePath();
986
1005
  this.lastRotation = /* @__PURE__ */ new Date();
987
- if ((_this$config$rotation = this.config.rotation) === null || _this$config$rotation === void 0 ? void 0 : _this$config$rotation.compress) await this.compressFile(oldPath);
988
- if ((_this$config$rotation2 = this.config.rotation) === null || _this$config$rotation2 === void 0 ? void 0 : _this$config$rotation2.maxFiles) await this.cleanupOldFiles();
1006
+ if ((_this$config$rotation2 = this.config.rotation) === null || _this$config$rotation2 === void 0 ? void 0 : _this$config$rotation2.compress) await this.compressFile(oldPath);
1007
+ if ((_this$config$rotation3 = this.config.rotation) === null || _this$config$rotation3 === void 0 ? void 0 : _this$config$rotation3.maxFiles) await this.cleanupOldFiles();
989
1008
  }
990
1009
  resolveDir() {
991
1010
  if (this.config.dirname) return this.config.dirname;
@@ -1008,8 +1027,8 @@ var FileTransport = class {
1008
1027
  }
1009
1028
  async compressFile(_filePath) {}
1010
1029
  async cleanupOldFiles() {
1011
- var _this$config$rotation3;
1012
- if (!((_this$config$rotation3 = this.config.rotation) === null || _this$config$rotation3 === void 0 ? void 0 : _this$config$rotation3.maxFiles)) return;
1030
+ var _this$config$rotation4;
1031
+ if (!((_this$config$rotation4 = this.config.rotation) === null || _this$config$rotation4 === void 0 ? void 0 : _this$config$rotation4.maxFiles)) return;
1013
1032
  try {
1014
1033
  const dir = this.resolveDir();
1015
1034
  const files = await readdir(dir);
@@ -2043,4 +2062,4 @@ var TransportManager = class extends EventEmitter {
2043
2062
 
2044
2063
  //#endregion
2045
2064
  export { FileTransport as a, DatabaseTransport as c, internalError as d, internalLog as f, GoogleAnalyticsTransport as i, ConsoleTransport as l, __require as m, SegmentTransport as n, DataDogTransport as o, internalWarn as p, MixpanelTransport as r, AnalyticsTransport as s, TransportManager as t, safeToString as u };
2046
- //# sourceMappingURL=transport.manager-DsvWfJ_b.mjs.map
2065
+ //# sourceMappingURL=transport.manager-CaL4XuLD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.manager-CaL4XuLD.mjs","names":["config: ConsoleTransportConfig","parts: string[]","config: DatabaseTransportConfig","config: any","sqlite3: any","transformed: Record<string, unknown>","promises: Promise<void>[]","flattened: Record<string, unknown>","chunks: T[][]","chunks: T[][]","transportEntry: TransportLogEntry","writePromises: Promise<void>[]","lastError: Error","wait: number","readyPromises: Promise<boolean>[]","flushPromises: Promise<void>[]","closePromises: Promise<void>[]","details: Record<string, unknown>"],"sources":["../src/utils/internal-log.ts","../src/utils/coerce.utils.ts","../src/transports/console.transport.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/transports/segment.transport.ts","../src/transports/transport.manager.ts"],"sourcesContent":["/**\n * Internal logging helpers for use within the logixia library itself.\n *\n * These functions write directly to stderr/stdout without going through\n * LogixiaLogger — this avoids circular dependencies and ensures internal\n * diagnostics still surface even when the logger is misconfigured.\n *\n * Library code must NEVER call console.log/warn/error directly. Use these\n * helpers instead so that internal output can be silenced in tests by setting\n * the LOGIXIA_SILENT_INTERNAL=1 environment variable.\n */\n\nconst silent = process.env.LOGIXIA_SILENT_INTERNAL === '1';\n\n/**\n * Emit an internal debug/info message to stderr.\n * Use for field-enable/disable notifications that a developer might want to see\n * during development but should not appear in production log streams.\n */\nexport function internalLog(message: string): void {\n if (!silent) {\n process.stderr.write(`[logixia] ${message}\\n`);\n }\n}\n\n/**\n * Emit an internal warning to stderr.\n * Use when something is misconfigured but logixia can continue operating.\n */\nexport function internalWarn(message: string): void {\n if (!silent) {\n process.stderr.write(`[logixia:warn] ${message}\\n`);\n }\n}\n\n/**\n * Emit an internal error to stderr.\n * Use when a transport write fails or a serious internal error occurs.\n */\nexport function internalError(message: string, error?: unknown): void {\n if (!silent) {\n let errStr = '';\n if (error instanceof Error) {\n errStr = ` — ${error.message}`;\n } else if (error != null) {\n errStr = ` — ${String(error)}`;\n }\n process.stderr.write(`[logixia:error] ${message}${errStr}\\n`);\n }\n}\n","/**\n * Defensive value coercion helpers for Logixia.\n *\n * Logger inputs come from third-party code (NestJS framework internals,\n * user payloads, error objects) where the runtime type is not always what\n * the TypeScript signature promises. The formatter / transport hot paths\n * call `.replace()` on string fields and crash if the field is anything\n * else. Use these helpers anywhere a downstream consumer assumes a string.\n */\n\n/**\n * Coerce any value to a string without throwing.\n *\n * - `undefined` / `null` → `''` (so the field is rendered as empty rather\n * than the literal string \"undefined\")\n * - strings → returned as-is (zero allocation on the hot path)\n * - Errors → `error.message` (matches what users would expect to see)\n * - everything else → `JSON.stringify` if possible, falling back to\n * `String(value)` for circular refs / values without a stringifier.\n */\nexport function safeToString(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value === undefined || value === null) return '';\n if (value instanceof Error) return value.message;\n if (typeof value === 'number' || typeof value === 'bigint' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'symbol') return value.toString();\n if (typeof value === 'function') {\n return `[Function: ${(value as { name?: string }).name || 'anonymous'}]`;\n }\n // value is now narrowed to object — JSON-stringify, falling back to a\n // constructor-name tag for circular refs / throwing toJSON.\n try {\n return JSON.stringify(value);\n } catch {\n const ctor = (value as { constructor?: { name?: string } }).constructor?.name;\n return `[${ctor ?? 'object'}]`;\n }\n}\n\n/**\n * String-replace on a value that may not be a string.\n * Coerces non-strings before delegating to `String.prototype.replace`.\n */\nexport function safeReplace(value: unknown, pattern: string | RegExp, replacement: string): string {\n return safeToString(value).replace(pattern, replacement);\n}\n","import type {\n ConsoleTransportConfig,\n ITransport,\n TransportLogEntry,\n} from '../types/transport.types';\nimport { safeToString } from '../utils/coerce.utils';\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 // CWE-117 guard: strip ASCII control characters from user-supplied text so\n // attacker-controlled log payloads cannot inject ANSI escapes, overwrite\n // lines, or forge log entries on the operator's terminal. Keeps \\t (0x09)\n // since tabs are harmless, and strips everything else in the C0 + DEL + C1\n // ranges. Applied only to text mode — JSON mode already escapes via\n // JSON.stringify.\n // eslint-disable-next-line no-control-regex\n private static readonly CONTROL_CHARS_RE = /[\\x00-\\x08\\x0B-\\x1F\\x7F-\\x9F]/g;\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 safeToString(value).replace(ConsoleTransport.CONTROL_CHARS_RE, '');\n }\n\n constructor(private config: ConsoleTransportConfig = {}) {\n if (config.filter) this.filter = config.filter;\n }\n\n write(entry: TransportLogEntry): Promise<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 return Promise.resolve();\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 JSON.stringify({\n timestamp: this.config.timestamp !== false ? entry.timestamp.toISOString() : undefined,\n level: entry.level,\n message: entry.message,\n ...(entry.data || {}),\n context: entry.context,\n traceId: entry.traceId,\n appName: entry.appName,\n environment: entry.environment,\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 = JSON.stringify(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 type {\n DatabaseTransportConfig,\n IAsyncTransport,\n IBatchTransport,\n TransportLogEntry,\n} from '../types/transport.types';\nimport { internalError } from '../utils/internal-log';\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 */\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;\n private isFlushing = false;\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>;\n\n constructor(private config: DatabaseTransportConfig) {\n this.batchSize = config.batchSize || 100;\n this.flushInterval = config.flushInterval || 5000; // 5 seconds\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 }\n\n async flush(): Promise<void> {\n if (this.batch.length === 0) return;\n // Guard: skip if a flush is already in flight — entries will be picked up\n // by the next flush cycle triggered by the timer or the next write.\n if (this.isFlushing) return;\n\n this.isFlushing = true;\n const entriesToFlush = [...this.batch];\n this.batch = [];\n\n try {\n switch (this.config.type) {\n case 'mongodb':\n await this.flushToMongoDB(entriesToFlush);\n break;\n case 'postgresql':\n await this.flushToPostgreSQL(entriesToFlush);\n break;\n case 'mysql':\n await this.flushToMySQL(entriesToFlush);\n break;\n case 'sqlite':\n await this.flushToSQLite(entriesToFlush);\n break;\n default:\n throw new Error(`Unsupported database type: ${this.config.type}`);\n }\n } catch (error) {\n internalError('Database flush error', error);\n // Restore failed entries to the front of the batch for retry\n this.batch.unshift(...entriesToFlush);\n throw error;\n } finally {\n this.isFlushing = false;\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 this.connectionPromise = this.establishConnection();\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 await collection.insertMany(documents);\n }\n\n private async flushToPostgreSQL(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 JSON.stringify(entry.data || {}),\n entry.context,\n entry.traceId,\n entry.appName,\n entry.environment,\n ]);\n\n const placeholders = values\n .map(\n (_, i) =>\n `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8})`\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, values.flat());\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 JSON.stringify(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 await this.connection.query(query, [values]);\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 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 JSON.stringify(entry.data || {}),\n entry.context,\n entry.traceId,\n entry.appName,\n entry.environment\n );\n }\n\n await stmt.finalize();\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 }\n\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n\n // Flush remaining entries\n await this.flush();\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 case 'mysql':\n case 'sqlite': {\n const tableName = this.config.table || 'logs';\n const result = await this.connection.query(`SELECT COUNT(*) as count FROM ${tableName}`);\n return result.rows?.[0]?.count || result[0]?.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';\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;\n protected isReady: boolean = false;\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\n this.initialize();\n }\n\n async write(entry: TransportLogEntry): Promise<void> {\n if (!this.isReady) {\n await this.waitForReady();\n }\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\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 }\n }\n\n async flush(): Promise<void> {\n if (this.batch.length === 0) return;\n\n const entriesToSend = [...this.batch];\n this.batch = [];\n\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n delete this.batchTimer;\n }\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 batch for retry\n this.batch.unshift(...entriesToSend);\n }\n }\n\n async close(): Promise<void> {\n await this.flush();\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n }\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 transformed: Record<string, unknown> = {\n timestamp: entry.timestamp.toISOString(),\n level: entry.level,\n message: entry.message,\n ...entry.data,\n };\n\n if (entry.context) {\n transformed.context = entry.context;\n }\n\n if (entry.traceId) {\n transformed.traceId = entry.traceId;\n }\n\n if (entry.appName) {\n transformed.appName = entry.appName;\n }\n\n if (entry.environment) {\n transformed.environment = entry.environment;\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 protected async waitForReady(timeout: number = 5000): Promise<void> {\n const startTime = Date.now();\n while (!this.isReady && Date.now() - startTime < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n if (!this.isReady) {\n throw new Error(`Analytics transport ${this.name} failed to initialize within ${timeout}ms`);\n }\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 type { DataDogTransportConfig, TransportLogEntry } from '../types/transport.types';\nimport { internalError } from '../utils/internal-log';\nimport type { AnalyticsMetric } from './analytics.transport';\nimport { AnalyticsTransport } from './analytics.transport';\n\n/**\n * Sends log entries and metrics to Datadog via the Logs 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\n // Test connection\n await this.testConnection();\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 const promises: Promise<void>[] = [];\n\n // Send log if enabled\n if (this.datadogConfig.enableLogs !== false) {\n promises.push(this.sendLog(entry));\n }\n\n // Send metric if enabled\n if (this.datadogConfig.enableMetrics) {\n promises.push(this.sendMetric(entry));\n }\n\n // Send trace if enabled and trace ID exists\n if (this.datadogConfig.enableTraces && entry.traceId) {\n promises.push(this.sendTrace(entry));\n }\n\n await Promise.all(promises);\n }\n\n protected async sendBatch(entries: TransportLogEntry[]): Promise<void> {\n const promises: Promise<void>[] = [];\n\n // Batch logs\n if (this.datadogConfig.enableLogs !== false) {\n promises.push(this.sendLogBatch(entries));\n }\n\n // Batch metrics\n if (this.datadogConfig.enableMetrics) {\n promises.push(this.sendMetricBatch(entries));\n }\n\n // Batch traces\n if (this.datadogConfig.enableTraces) {\n const entriesWithTraces = entries.filter((e) => e.traceId);\n if (entriesWithTraces.length > 0) {\n promises.push(this.sendTraceBatch(entriesWithTraces));\n }\n }\n\n await Promise.all(promises);\n }\n\n protected async cleanup(): Promise<void> {\n // DataDog doesn't require explicit cleanup\n }\n\n private async sendLog(entry: TransportLogEntry): Promise<void> {\n await this.sendLogBatch([entry]);\n }\n\n private async sendLogBatch(entries: TransportLogEntry[]): Promise<void> {\n const logs = entries.map((entry) => this.transformToDataDogLog(entry));\n\n const payload = {\n logs: logs,\n };\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 await this.makeLogsRequest('/v1/input', payload);\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 async sendTrace(entry: TransportLogEntry): Promise<void> {\n // DataDog traces are typically sent via the trace agent\n // For simplicity, we'll log this as a structured log with trace information\n await this.sendLog(entry);\n }\n\n private async sendTraceBatch(entries: TransportLogEntry[]): Promise<void> {\n // For batch traces, send as structured logs with trace information\n await this.sendLogBatch(entries);\n }\n\n private transformToDataDogLog(entry: TransportLogEntry): Record<string, unknown> {\n const transformed = this.transformEntry(entry);\n\n return {\n timestamp: entry.timestamp.toISOString(),\n level: this.mapLogLevel(entry.level),\n message: entry.message,\n service: this.datadogConfig.service || 'logitron',\n version: this.datadogConfig.version,\n env: this.datadogConfig.env || 'production',\n logger: {\n name: 'logitron',\n thread_name: 'main',\n },\n ...transformed,\n // Add DataDog specific fields\n 'dd.trace_id': entry.traceId,\n 'dd.span_id': this.generateSpanId(),\n host: this.getHostname(),\n source: 'nodejs',\n };\n }\n\n private transformToDataDogMetric(entry: TransportLogEntry): Record<string, unknown> {\n return {\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 `level:${entry.level}`,\n `service:${this.datadogConfig.service || 'logitron'}`,\n `env:${this.datadogConfig.env || 'production'}`,\n ...(entry.context ? [`context:${entry.context}`] : []),\n ...(entry.appName ? [`app:${entry.appName}`] : []),\n ],\n };\n }\n\n private transformToDataDogTrace(entry: TransportLogEntry): Record<string, unknown> {\n return {\n trace_id: entry.traceId,\n span_id: this.generateSpanId(),\n parent_id: null,\n name: 'log.entry',\n service: this.datadogConfig.service || 'logitron',\n resource: entry.message,\n type: 'log',\n start: entry.timestamp.getTime() * 1000000, // nanoseconds\n duration: 1000000, // 1ms in nanoseconds\n meta: {\n 'log.level': entry.level,\n 'log.message': entry.message,\n ...(entry.context && { 'log.context': entry.context }),\n ...(entry.data &&\n Object.keys(entry.data).reduce(\n (acc, key) => {\n acc[`log.${key}`] = String(entry.data![key]);\n return acc;\n },\n {} as Record<string, string>\n )),\n },\n metrics: {\n _sampling_priority_v1: 1,\n },\n };\n }\n\n private mapLogLevel(level: string): string {\n const levelMap: Record<string, string> = {\n error: 'error',\n warn: 'warn',\n warning: 'warn',\n info: 'info',\n debug: 'debug',\n trace: 'trace',\n verbose: 'debug',\n };\n\n return levelMap[level.toLowerCase()] || 'info';\n }\n\n private async makeLogsRequest(endpoint: string, data: unknown): Promise<void> {\n const url = `${this.logsUrl}${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: JSON.stringify(data),\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: JSON.stringify(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 async testConnection(): Promise<void> {\n // Test with a simple metric\n const testMetric = {\n series: [\n {\n metric: 'logitron.test',\n points: [[Math.floor(Date.now() / 1000), 1]],\n type: 'count',\n host: this.getHostname(),\n tags: ['test:true'],\n },\n ],\n };\n\n await this.makeMetricsRequest('/api/v1/series', testMetric);\n }\n\n private generateSpanId(): string {\n // eslint-disable-next-line sonarjs/pseudo-random -- non-security span ID generation\n return Math.floor(Math.random() * 0xffffffffffff).toString(16);\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 || 'logitron'}`,\n `env:${this.datadogConfig.env || 'production'}`,\n ...tags,\n ],\n source_type_name: 'logitron',\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: JSON.stringify(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 type { WriteStream } from 'node:fs';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { promisify } from 'node:util';\n\nimport type {\n FileTransportConfig,\n IBatchTransport,\n ITransport,\n TransportLogEntry,\n} from '../types/transport.types';\nimport { safeToString } from '../utils/coerce.utils';\nimport { internalError } from '../utils/internal-log';\n\nconst writeFile = promisify(fs.writeFile);\nconst mkdir = promisify(fs.mkdir);\nconst unlink = promisify(fs.unlink);\nconst readdir = promisify(fs.readdir);\nconst stat = promisify(fs.stat);\n\n/**\n * Appends log entries to a local file with automatic rotation.\n *\n * Supports batched writes, size-based and date-based rotation, and optional\n * gzip compression of rotated files.\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 writeStream: WriteStream | undefined;\n private batch: TransportLogEntry[] = [];\n private batchTimer?: NodeJS.Timeout | undefined;\n private lastRotation: Date = new Date();\n private currentFilePath: string;\n /** Guards against concurrent rotation — two simultaneous writes both seeing shouldRotateNow(). */\n private isRotating = false;\n /**\n * The in-flight drain promise, or undefined when idle. Concurrent flush() calls\n * (e.g. the un-awaited flush addToBatch fires on every Nth entry) all await this\n * single promise instead of starting their own drain, so the batch is never\n * snapshotted and written twice.\n */\n private flushPromise: Promise<void> | undefined;\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 this.currentFilePath = this.generateFilePath();\n this.ensureDirectoryExists();\n }\n\n async write(entry: TransportLogEntry): Promise<void> {\n try {\n if (this.shouldRotateNow() && !this.isRotating) {\n this.isRotating = true;\n try {\n await this.rotate();\n } finally {\n this.isRotating = false;\n }\n }\n\n if (this.config.batchSize && this.config.batchSize > 1) {\n this.addToBatch(entry);\n } else {\n await this.writeBatch([entry]);\n }\n } catch (error) {\n throw new Error(`File transport write failed: ${(error as Error).message}`, { cause: error });\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. addToBatch() calls flush() un-awaited on every\n // Nth entry, so a synchronous burst of writes can fire many overlapping flushes.\n // Every caller (awaited or not) joins the SAME in-flight drain, so the batch is\n // never snapshotted twice — which previously turned N log calls into N² file\n // lines (the batch-flush duplication bug). Callers that await flush() still see\n // the batch fully drained 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 batch to disk one snapshot at a time. Entries appended while a\n * write is in flight are picked up by the next loop iteration, so a single\n * drain() empties the batch completely regardless of concurrent writes.\n */\n private async drain(): Promise<void> {\n while (this.batch.length > 0) {\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 await this.writeBatch(pending);\n }\n }\n\n private formatEntry(entry: TransportLogEntry): string {\n switch (this.config.format) {\n case 'json':\n return JSON.stringify({\n timestamp: entry.timestamp.toISOString(),\n level: entry.level,\n message: entry.message,\n ...(entry.data || {}),\n });\n\n case 'csv': {\n const safeMessage = safeToString(entry.message);\n const fields = [\n entry.timestamp.toISOString(),\n entry.level,\n `\"${safeMessage.replace(/\"/g, '\"\"')}\"`,\n safeToString(entry.context),\n safeToString(entry.traceId),\n JSON.stringify(entry.data || {}),\n ];\n return fields.join(',');\n }\n\n case 'text':\n default:\n return `${entry.timestamp.toISOString()} [${entry.level.toUpperCase()}] ${safeToString(entry.message)}${entry.data ? ' ' + JSON.stringify(entry.data) : ''}`;\n }\n }\n\n addToBatch(entry: TransportLogEntry): void {\n this.batch.push(entry);\n\n if (this.batch.length >= (this.config.batchSize || 100)) {\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.flush().catch((err: unknown) => internalError('FileTransport batch flush failed', err));\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 private async writeBatch(entries: TransportLogEntry[]): Promise<void> {\n if (entries.length === 0) return;\n\n const content = entries.map((entry) => this.formatEntry(entry)).join('\\n') + '\\n';\n\n if (this.writeStream) {\n return new Promise((resolve, reject) => {\n this.writeStream!.write(content, (error) => {\n if (error) reject(error);\n else resolve();\n });\n });\n } else {\n await writeFile(this.currentFilePath, content, { flag: 'a' });\n }\n }\n\n private shouldRotateNow(): boolean {\n if (!this.config.rotation?.interval) return false;\n\n const now = new Date();\n const timeDiff = now.getTime() - this.lastRotation.getTime();\n const rotationInterval = this.parseInterval(this.config.rotation.interval);\n\n return timeDiff >= rotationInterval;\n }\n\n private parseInterval(interval: string): number {\n // eslint-disable-next-line sonarjs/slow-regex -- simple bounded pattern for interval parsing\n const match = new RegExp(/(\\d+)([hdwmy])/i).exec(interval);\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 rotate(): Promise<void> {\n await this.flush();\n\n if (this.writeStream) {\n await new Promise<void>((resolve) => {\n this.writeStream!.end(() => resolve());\n });\n this.writeStream = undefined;\n }\n\n const oldPath = this.currentFilePath;\n this.currentFilePath = this.generateFilePath();\n this.lastRotation = new Date();\n\n // Compress old file if configured\n if (this.config.rotation?.compress) {\n await this.compressFile(oldPath);\n }\n\n // Clean up old files\n if (this.config.rotation?.maxFiles) {\n await this.cleanupOldFiles();\n }\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 private generateFilePath(): string {\n const now = new Date();\n const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, 19);\n const dir = this.resolveDir();\n const ext = path.extname(this.config.filename);\n const base = path.basename(this.config.filename, ext);\n\n if (this.config.rotation) {\n return path.join(dir, `${base}-${timestamp}${ext}`);\n }\n\n return path.join(dir, `${base}${ext}`);\n }\n\n private async ensureDirectoryExists(): Promise<void> {\n const dir = this.resolveDir();\n try {\n await mkdir(dir, { recursive: true });\n } catch {\n // Directory might already exist\n }\n }\n\n private async compressFile(_filePath: string): Promise<void> {\n // Simple gzip compression implementation would go here\n // For now, just rename with .gz extension\n // In a real implementation, you'd use zlib\n }\n\n private async cleanupOldFiles(): Promise<void> {\n if (!this.config.rotation?.maxFiles) return;\n\n try {\n const dir = this.resolveDir();\n const files = await readdir(dir);\n const base = path.basename(this.config.filename, path.extname(this.config.filename));\n\n const logFiles = files\n .filter((file) => file.startsWith(base))\n .map(async (file) => {\n const filePath = path.join(dir, file);\n const stats = await stat(filePath);\n return { path: filePath, mtime: stats.mtime };\n });\n\n const fileStats = await Promise.all(logFiles);\n const sortedFiles = fileStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n\n const filesToDelete = sortedFiles.slice(this.config.rotation.maxFiles);\n\n for (const file of filesToDelete) {\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 await this.flush();\n\n if (this.writeStream) {\n return new Promise((resolve) => {\n this.writeStream!.end(() => resolve());\n });\n }\n }\n}\n","import type { GoogleAnalyticsTransportConfig, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalLog } from '../utils/internal-log';\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\n // Test connection\n await this.testConnection();\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 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 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 ...this.flattenData(transformed),\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: JSON.stringify(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 async testConnection(): Promise<void> {\n // Send a test event to validate the connection\n const testEvent = {\n name: 'logitron_test',\n params: {\n event_category: 'Test',\n event_label: 'Connection Test',\n test: true,\n timestamp: new Date().toISOString(),\n },\n };\n\n const payload = {\n client_id: this.gaConfig.clientId || this.generateClientId(),\n events: [testEvent],\n };\n\n // Use debug endpoint for testing\n await this.makeRequest(payload, true);\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 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\n // Test connection with a simple request\n await this.testConnection();\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 private transformToMixpanelEvent(entry: TransportLogEntry): AnalyticsEvent {\n const transformed = this.transformEntry(entry);\n\n return {\n name: `log_${entry.level}`,\n properties: {\n ...transformed,\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 token: this.mixpanelConfig.token,\n ...event.properties,\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 token: this.mixpanelConfig.token,\n ...event.properties,\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: JSON.stringify(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 async testConnection(): Promise<void> {\n // Send a test event to validate the connection\n const testEvent = {\n event: 'logitron_test',\n properties: {\n token: this.mixpanelConfig.token,\n distinct_id: 'test_user',\n test: true,\n time: Date.now(),\n },\n };\n\n await this.makeRequest('/track', [testEvent]);\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 token: this.mixpanelConfig.token,\n distinct_id: this.mixpanelConfig.distinct_id || 'anonymous',\n time: Date.now(),\n ...properties,\n ...(this.mixpanelConfig.enableSuperProperties ? this.mixpanelConfig.superProperties : {}),\n },\n };\n\n await this.makeRequest('/track', [payload]);\n }\n}\n","import type { SegmentTransportConfig, TransportLogEntry } from '../types/transport.types';\nimport { internalError } from '../utils/internal-log';\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\n // Test connection\n await this.testConnection();\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 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 level: entry.level,\n message: entry.message,\n logger: 'logixia',\n ...transformed,\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: '1.0.0',\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/1.0.0',\n },\n body: JSON.stringify(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 async testConnection(): Promise<void> {\n // Send a test track event\n const testEvent = {\n type: 'track',\n event: 'Logixia Test',\n userId: 'test-user',\n anonymousId: this.generateAnonymousId(),\n timestamp: new Date().toISOString(),\n properties: {\n test: true,\n source: 'logixia',\n },\n context: {\n library: {\n name: 'logixia',\n version: '1.0.0',\n },\n },\n };\n\n await this.makeRequest('/v1/track', testEvent);\n }\n\n private extractUserId(entry: TransportLogEntry): string | undefined {\n // Try to extract user ID from entry data\n if (entry.data) {\n const d = entry.data as Record<string, unknown>;\n const id = d['userId'] ?? d['user_id'] ?? d['id'];\n return typeof id === 'string' ? id : undefined;\n }\n return 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: 'logitron',\n version: '1.0.0',\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: 'logitron',\n version: '1.0.0',\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: 'logitron',\n version: '1.0.0',\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: 'logitron',\n version: '1.0.0',\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: 'logitron',\n version: '1.0.0',\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: 'logitron',\n version: '1.0.0',\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: 'logitron',\n version: '1.0.0',\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: 'logitron',\n version: '1.0.0',\n },\n },\n };\n\n await this.makeRequest('/v1/track', payload);\n }\n}\n","import { EventEmitter } from 'node:events';\nimport * as readline from 'node:readline';\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 promptForLevels: boolean = false; // Flag to enable/disable prompting\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 async write(entry: LogEntry): 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: new Date(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 const writePromises: Promise<void>[] = [];\n\n for (const [id, transport] of this.transports) {\n // Configure transport levels if prompting is enabled\n if (this.promptForLevels && !this.transportLevelPreferences.has(id)) {\n await this.configureTransportLevels(id);\n }\n\n // Check if transport should handle this log level\n if (!this.shouldTransportHandle(transport, transportEntry.level, id)) {\n continue;\n }\n\n // Run the transport's custom filter predicate if one is set\n if (transport.filter && !transport.filter(transportEntry)) {\n continue;\n }\n\n const writePromise = this.writeToTransport(id, transport, transportEntry);\n writePromises.push(writePromise);\n }\n\n // Wait for all transports to complete (or fail)\n const results = await Promise.allSettled(writePromises);\n\n // Check for any failures\n const failures = results.filter(\n (result) => result.status === 'rejected'\n ) as PromiseRejectedResult[];\n if (failures.length > 0) {\n const errors = failures.map((failure) => failure.reason);\n this.emit('error', new Error(`Transport write failures: ${errors.join(', ')}`), 'multiple');\n }\n }\n\n private async writeToTransport(\n id: string,\n transport: ITransport,\n entry: TransportLogEntry\n ): Promise<void> {\n const startTime = Date.now();\n const metrics = this.metrics.get(id)!;\n\n try {\n await this._writeWithRetry(transport, entry);\n\n // Update metrics (reuse startTime, avoid new Date() allocation)\n const writeTime = Date.now() - startTime;\n metrics.logsWritten++;\n metrics.lastWrite = new Date(startTime);\n metrics.averageWriteTime = (metrics.averageWriteTime + writeTime) / 2;\n\n this.emit('log', entry);\n } catch (error) {\n metrics.errors++;\n this.emit('error', error, id);\n throw error;\n }\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 enableLevelPrompting(): void {\n this.promptForLevels = true;\n internalLog('Transport level prompting enabled');\n }\n\n disableLevelPrompting(): void {\n this.promptForLevels = false;\n internalLog('Transport level prompting disabled');\n }\n\n async promptUserForTransportLevels(transportId: string): Promise<string[]> {\n const availableLevels = ['error', 'warn', 'info', 'debug', 'trace', 'verbose'];\n\n return new Promise((resolve) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n process.stdout.write(`\\nConfigure log levels for transport '${transportId}'\\n`);\n process.stdout.write(`Available levels: ${availableLevels.join(', ')}\\n`);\n process.stdout.write(\n 'Enter levels separated by commas (e.g., error,warn,info) or \"all\" for all levels:\\n'\n );\n\n rl.question('> ', (answer) => {\n rl.close();\n\n if (answer.toLowerCase().trim() === 'all') {\n resolve(availableLevels);\n } else {\n const selectedLevels = answer\n .split(',')\n .map((level) => level.trim().toLowerCase())\n .filter((level) => availableLevels.includes(level));\n\n if (selectedLevels.length === 0) {\n internalWarn('No valid levels selected, using all levels');\n resolve(availableLevels);\n } else {\n internalLog(`Selected levels for ${transportId}: ${selectedLevels.join(', ')}`);\n resolve(selectedLevels);\n }\n }\n });\n });\n }\n\n async configureTransportLevels(transportId: string): Promise<void> {\n if (this.promptForLevels && !this.transportLevelPreferences.has(transportId)) {\n const selectedLevels = await this.promptUserForTransportLevels(transportId);\n this.transportLevelPreferences.set(transportId, selectedLevels);\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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"}
@@ -151,7 +151,8 @@ var ConsoleTransport = class ConsoleTransport {
151
151
  }
152
152
  write(entry) {
153
153
  const formatted = this.formatEntry(entry) + "\n";
154
- (entry.level.toLowerCase() === "error" ? process.stderr : process.stdout).write(formatted);
154
+ const lvl = entry.level.toLowerCase();
155
+ (lvl === "error" || lvl === "warn" ? process.stderr : process.stdout).write(formatted);
155
156
  return Promise.resolve();
156
157
  }
157
158
  formatEntry(entry) {
@@ -164,13 +165,13 @@ var ConsoleTransport = class ConsoleTransport {
164
165
  traceId: entry.traceId,
165
166
  appName: entry.appName,
166
167
  environment: entry.environment
167
- }, null, 2);
168
+ });
168
169
  const parts = [];
169
170
  if (this.config.timestamp !== false) {
170
171
  const timestamp = entry.timestamp.toISOString();
171
172
  parts.push(this.colorize(timestamp, "gray"));
172
173
  }
173
- const level = entry.level.toUpperCase().padEnd(5);
174
+ const level = ConsoleTransport.sanitize(entry.level).toUpperCase().padEnd(5);
174
175
  const coloredLevel = this.colorize(level, this.getLevelColor(entry.level));
175
176
  parts.push(coloredLevel);
176
177
  if (entry.context) {
@@ -928,14 +929,26 @@ var FileTransport = class {
928
929
  }
929
930
  }
930
931
  async flush() {
931
- if (this.batch.length > 0) {
932
- await this.writeBatch([...this.batch]);
933
- this.batch = [];
934
- }
935
932
  if (this.batchTimer) {
936
933
  clearTimeout(this.batchTimer);
937
934
  this.batchTimer = void 0;
938
935
  }
936
+ if (!this.flushPromise) this.flushPromise = this.drain().finally(() => {
937
+ this.flushPromise = void 0;
938
+ });
939
+ await this.flushPromise;
940
+ }
941
+ /**
942
+ * Drains the batch to disk one snapshot at a time. Entries appended while a
943
+ * write is in flight are picked up by the next loop iteration, so a single
944
+ * drain() empties the batch completely regardless of concurrent writes.
945
+ */
946
+ async drain() {
947
+ while (this.batch.length > 0) {
948
+ const pending = this.batch;
949
+ this.batch = [];
950
+ await this.writeBatch(pending);
951
+ }
939
952
  }
940
953
  formatEntry(entry) {
941
954
  switch (this.config.format) {
@@ -962,8 +975,13 @@ var FileTransport = class {
962
975
  }
963
976
  addToBatch(entry) {
964
977
  this.batch.push(entry);
965
- if (this.batch.length >= (this.config.batchSize || 100)) this.flush().catch((err) => internalError("FileTransport batch flush failed", err));
966
- else if (!this.batchTimer && this.config.flushInterval) this.batchTimer = setTimeout(() => {
978
+ if (this.batch.length >= (this.config.batchSize || 100)) {
979
+ if (this.batchTimer) {
980
+ clearTimeout(this.batchTimer);
981
+ this.batchTimer = void 0;
982
+ }
983
+ this.flush().catch((err) => internalError("FileTransport batch flush failed", err));
984
+ } else if (!this.batchTimer && this.config.flushInterval) this.batchTimer = setTimeout(() => {
967
985
  this.flush().catch((err) => internalError("FileTransport interval flush failed", err));
968
986
  }, this.config.flushInterval);
969
987
  }
@@ -979,11 +997,12 @@ var FileTransport = class {
979
997
  else await writeFile(this.currentFilePath, content, { flag: "a" });
980
998
  }
981
999
  shouldRotateNow() {
982
- if (!this.config.rotation) return false;
1000
+ var _this$config$rotation;
1001
+ if (!((_this$config$rotation = this.config.rotation) === null || _this$config$rotation === void 0 ? void 0 : _this$config$rotation.interval)) return false;
983
1002
  return (/* @__PURE__ */ new Date()).getTime() - this.lastRotation.getTime() >= this.parseInterval(this.config.rotation.interval);
984
1003
  }
985
1004
  parseInterval(interval) {
986
- const match = interval.match(/(\d+)([hdwmy])/i);
1005
+ const match = (/* @__PURE__ */ new RegExp(/(\d+)([hdwmy])/i)).exec(interval);
987
1006
  if (!match) return 1440 * 60 * 1e3;
988
1007
  const value = Number.parseInt(match[1] || "1", 10);
989
1008
  switch ((match[2] || "h").toLowerCase()) {
@@ -996,7 +1015,7 @@ var FileTransport = class {
996
1015
  }
997
1016
  }
998
1017
  async rotate() {
999
- var _this$config$rotation, _this$config$rotation2;
1018
+ var _this$config$rotation2, _this$config$rotation3;
1000
1019
  await this.flush();
1001
1020
  if (this.writeStream) {
1002
1021
  await new Promise((resolve) => {
@@ -1007,8 +1026,8 @@ var FileTransport = class {
1007
1026
  const oldPath = this.currentFilePath;
1008
1027
  this.currentFilePath = this.generateFilePath();
1009
1028
  this.lastRotation = /* @__PURE__ */ new Date();
1010
- if ((_this$config$rotation = this.config.rotation) === null || _this$config$rotation === void 0 ? void 0 : _this$config$rotation.compress) await this.compressFile(oldPath);
1011
- if ((_this$config$rotation2 = this.config.rotation) === null || _this$config$rotation2 === void 0 ? void 0 : _this$config$rotation2.maxFiles) await this.cleanupOldFiles();
1029
+ if ((_this$config$rotation2 = this.config.rotation) === null || _this$config$rotation2 === void 0 ? void 0 : _this$config$rotation2.compress) await this.compressFile(oldPath);
1030
+ if ((_this$config$rotation3 = this.config.rotation) === null || _this$config$rotation3 === void 0 ? void 0 : _this$config$rotation3.maxFiles) await this.cleanupOldFiles();
1012
1031
  }
1013
1032
  resolveDir() {
1014
1033
  if (this.config.dirname) return this.config.dirname;
@@ -1031,8 +1050,8 @@ var FileTransport = class {
1031
1050
  }
1032
1051
  async compressFile(_filePath) {}
1033
1052
  async cleanupOldFiles() {
1034
- var _this$config$rotation3;
1035
- if (!((_this$config$rotation3 = this.config.rotation) === null || _this$config$rotation3 === void 0 ? void 0 : _this$config$rotation3.maxFiles)) return;
1053
+ var _this$config$rotation4;
1054
+ if (!((_this$config$rotation4 = this.config.rotation) === null || _this$config$rotation4 === void 0 ? void 0 : _this$config$rotation4.maxFiles)) return;
1036
1055
  try {
1037
1056
  const dir = this.resolveDir();
1038
1057
  const files = await readdir(dir);
@@ -2149,4 +2168,4 @@ Object.defineProperty(exports, 'safeToString', {
2149
2168
  return safeToString;
2150
2169
  }
2151
2170
  });
2152
- //# sourceMappingURL=transport.manager-CgeSNG_8.js.map
2171
+ //# sourceMappingURL=transport.manager-zgEZCJhR.js.map