logixia 1.12.4 → 1.12.6

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 (166) hide show
  1. package/README.md +80 -27
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/async-context-CSHWoHMB.js +88 -0
  4. package/dist/async-context-CSHWoHMB.js.map +1 -0
  5. package/dist/async-context-CwBNMm2E.mjs +68 -0
  6. package/dist/async-context-CwBNMm2E.mjs.map +1 -0
  7. package/dist/azure-monitor.transport-CG7ClkYj.d.ts +62 -0
  8. package/dist/azure-monitor.transport-CG7ClkYj.d.ts.map +1 -0
  9. package/dist/azure-monitor.transport-CHnjVRF-.mjs +152 -0
  10. package/dist/azure-monitor.transport-CHnjVRF-.mjs.map +1 -0
  11. package/dist/azure-monitor.transport-Cm74kXZS.d.mts +62 -0
  12. package/dist/azure-monitor.transport-Cm74kXZS.d.mts.map +1 -0
  13. package/dist/azure-monitor.transport-CrvfHd9C.js +157 -0
  14. package/dist/azure-monitor.transport-CrvfHd9C.js.map +1 -0
  15. package/dist/azure.d.mts +3 -0
  16. package/dist/azure.d.ts +3 -0
  17. package/dist/azure.js +6 -0
  18. package/dist/azure.mjs +6 -0
  19. package/dist/batch-buffer-BY-mRWIK.js +170 -0
  20. package/dist/batch-buffer-BY-mRWIK.js.map +1 -0
  21. package/dist/batch-buffer-PGvSgbmd.mjs +132 -0
  22. package/dist/batch-buffer-PGvSgbmd.mjs.map +1 -0
  23. package/dist/browser.d.mts +427 -0
  24. package/dist/browser.d.mts.map +1 -0
  25. package/dist/browser.d.ts +427 -0
  26. package/dist/browser.d.ts.map +1 -0
  27. package/dist/browser.js +590 -0
  28. package/dist/browser.js.map +1 -0
  29. package/dist/browser.mjs +586 -0
  30. package/dist/browser.mjs.map +1 -0
  31. package/dist/chunk-DLSkYcPk.mjs +7 -0
  32. package/dist/chunk-nOFOJqeH.js +30 -0
  33. package/dist/cloudwatch.d.mts +3 -0
  34. package/dist/cloudwatch.d.ts +3 -0
  35. package/dist/cloudwatch.js +6 -0
  36. package/dist/cloudwatch.mjs +6 -0
  37. package/dist/cloudwatch.transport-CGHY4buE.d.mts +81 -0
  38. package/dist/cloudwatch.transport-CGHY4buE.d.mts.map +1 -0
  39. package/dist/cloudwatch.transport-DK_8cKd3.d.ts +81 -0
  40. package/dist/cloudwatch.transport-DK_8cKd3.d.ts.map +1 -0
  41. package/dist/cloudwatch.transport-pNBNWJLt.js +370 -0
  42. package/dist/cloudwatch.transport-pNBNWJLt.js.map +1 -0
  43. package/dist/cloudwatch.transport-yBwm4OeO.mjs +360 -0
  44. package/dist/cloudwatch.transport-yBwm4OeO.mjs.map +1 -0
  45. package/dist/coerce.utils-BN980Y9V.js +44 -0
  46. package/dist/coerce.utils-BN980Y9V.js.map +1 -0
  47. package/dist/coerce.utils-Dxv4_ikC.mjs +38 -0
  48. package/dist/coerce.utils-Dxv4_ikC.mjs.map +1 -0
  49. package/dist/correlation.d.mts +238 -0
  50. package/dist/correlation.d.mts.map +1 -0
  51. package/dist/correlation.d.ts +238 -0
  52. package/dist/correlation.d.ts.map +1 -0
  53. package/dist/correlation.js +272 -0
  54. package/dist/correlation.js.map +1 -0
  55. package/dist/correlation.mjs +261 -0
  56. package/dist/correlation.mjs.map +1 -0
  57. package/dist/gcp.d.mts +3 -0
  58. package/dist/gcp.d.ts +3 -0
  59. package/dist/gcp.js +6 -0
  60. package/dist/gcp.mjs +6 -0
  61. package/dist/gcp.transport-BJuNRFe_.mjs +192 -0
  62. package/dist/gcp.transport-BJuNRFe_.mjs.map +1 -0
  63. package/dist/gcp.transport-BOhkVKlI.d.ts +71 -0
  64. package/dist/gcp.transport-BOhkVKlI.d.ts.map +1 -0
  65. package/dist/gcp.transport-Bi6CUqZR.d.mts +71 -0
  66. package/dist/gcp.transport-Bi6CUqZR.d.mts.map +1 -0
  67. package/dist/gcp.transport-SHdtc1n0.js +198 -0
  68. package/dist/gcp.transport-SHdtc1n0.js.map +1 -0
  69. package/dist/{index-CwFww7VC.d.ts → index-BPKwwERo.d.mts} +18 -174
  70. package/dist/index-BPKwwERo.d.mts.map +1 -0
  71. package/dist/{index-DnyeaoZU.d.mts → index-C1X-TE5I.d.ts} +18 -174
  72. package/dist/index-C1X-TE5I.d.ts.map +1 -0
  73. package/dist/index.d.mts +41 -11
  74. package/dist/index.d.mts.map +1 -1
  75. package/dist/index.d.ts +40 -10
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +87 -147
  78. package/dist/index.js.map +1 -1
  79. package/dist/index.mjs +62 -132
  80. package/dist/index.mjs.map +1 -1
  81. package/dist/internal-log-BUJYMZbE.js +70 -0
  82. package/dist/internal-log-BUJYMZbE.js.map +1 -0
  83. package/dist/internal-log-Cm8SOWSV.mjs +52 -0
  84. package/dist/internal-log-Cm8SOWSV.mjs.map +1 -0
  85. package/dist/{logitron-logger.module-BIGTm2V6.mjs → logitron-logger.module-Bs3XYZgF.mjs} +266 -166
  86. package/dist/logitron-logger.module-Bs3XYZgF.mjs.map +1 -0
  87. package/dist/{logitron-logger.module-BTxDpK6u.js → logitron-logger.module-BsDZR5BH.js} +297 -203
  88. package/dist/logitron-logger.module-BsDZR5BH.js.map +1 -0
  89. package/dist/{logitron-logger.module-BmT-ewin.d.ts → logitron-logger.module-CxT1F6Cm.d.ts} +23 -10
  90. package/dist/logitron-logger.module-CxT1F6Cm.d.ts.map +1 -0
  91. package/dist/{logitron-logger.module-BCq3sF27.d.mts → logitron-logger.module-zDKVd_o0.d.mts} +23 -10
  92. package/dist/logitron-logger.module-zDKVd_o0.d.mts.map +1 -0
  93. package/dist/middleware.d.mts +5 -2
  94. package/dist/middleware.d.mts.map +1 -1
  95. package/dist/middleware.d.ts +5 -2
  96. package/dist/middleware.d.ts.map +1 -1
  97. package/dist/middleware.js +13 -4
  98. package/dist/middleware.js.map +1 -1
  99. package/dist/middleware.mjs +14 -4
  100. package/dist/middleware.mjs.map +1 -1
  101. package/dist/nest.d.mts +3 -2
  102. package/dist/nest.d.mts.map +1 -1
  103. package/dist/nest.d.ts +3 -2
  104. package/dist/nest.d.ts.map +1 -1
  105. package/dist/nest.js +6 -2
  106. package/dist/nest.mjs +6 -2
  107. package/dist/safe-stringify-DJ66tW5I.mjs +118 -0
  108. package/dist/safe-stringify-DJ66tW5I.mjs.map +1 -0
  109. package/dist/safe-stringify-DWo3aZp_.js +142 -0
  110. package/dist/safe-stringify-DWo3aZp_.js.map +1 -0
  111. package/dist/search-CFQRISUv.mjs +1596 -0
  112. package/dist/search-CFQRISUv.mjs.map +1 -0
  113. package/dist/search-CQOo-Iq6.d.mts +816 -0
  114. package/dist/search-CQOo-Iq6.d.mts.map +1 -0
  115. package/dist/search-DujhR94O.d.ts +816 -0
  116. package/dist/search-DujhR94O.d.ts.map +1 -0
  117. package/dist/search-njTI2hdo.js +1631 -0
  118. package/dist/search-njTI2hdo.js.map +1 -0
  119. package/dist/search.d.mts +4 -1
  120. package/dist/search.d.ts +4 -1
  121. package/dist/search.js +9 -1
  122. package/dist/search.mjs +3 -2
  123. package/dist/testing.d.mts +2 -1
  124. package/dist/testing.d.mts.map +1 -1
  125. package/dist/testing.d.ts +2 -1
  126. package/dist/testing.d.ts.map +1 -1
  127. package/dist/{transport.manager-ciG4r5uz.js → transport.manager-BivyUTtU.js} +497 -437
  128. package/dist/transport.manager-BivyUTtU.js.map +1 -0
  129. package/dist/{transport.manager-DETpXuaY.mjs → transport.manager-D6KL3t9V.mjs} +473 -364
  130. package/dist/transport.manager-D6KL3t9V.mjs.map +1 -0
  131. package/dist/transport.types-B5vcyv2k.d.mts +187 -0
  132. package/dist/transport.types-B5vcyv2k.d.mts.map +1 -0
  133. package/dist/transport.types-BuY84TwO.d.ts +187 -0
  134. package/dist/transport.types-BuY84TwO.d.ts.map +1 -0
  135. package/dist/transports.d.mts +90 -29
  136. package/dist/transports.d.mts.map +1 -1
  137. package/dist/transports.d.ts +90 -29
  138. package/dist/transports.d.ts.map +1 -1
  139. package/dist/transports.js +14 -2
  140. package/dist/transports.mjs +10 -2
  141. package/dist/worker-entry.js +1241 -0
  142. package/dist/worker.d.mts +3 -0
  143. package/dist/worker.d.ts +3 -0
  144. package/dist/worker.js +4 -0
  145. package/dist/worker.mjs +4 -0
  146. package/dist/worker.transport-BP8Q28Z2.d.mts +76 -0
  147. package/dist/worker.transport-BP8Q28Z2.d.mts.map +1 -0
  148. package/dist/worker.transport-BhMyvqQ2.mjs +167 -0
  149. package/dist/worker.transport-BhMyvqQ2.mjs.map +1 -0
  150. package/dist/worker.transport-CfH6z37Z.js +168 -0
  151. package/dist/worker.transport-CfH6z37Z.js.map +1 -0
  152. package/dist/worker.transport-mKXgNK0L.d.ts +76 -0
  153. package/dist/worker.transport-mKXgNK0L.d.ts.map +1 -0
  154. package/package.json +23 -18
  155. package/dist/index-CwFww7VC.d.ts.map +0 -1
  156. package/dist/index-DnyeaoZU.d.mts.map +0 -1
  157. package/dist/logitron-logger.module-BCq3sF27.d.mts.map +0 -1
  158. package/dist/logitron-logger.module-BIGTm2V6.mjs.map +0 -1
  159. package/dist/logitron-logger.module-BTxDpK6u.js.map +0 -1
  160. package/dist/logitron-logger.module-BmT-ewin.d.ts.map +0 -1
  161. package/dist/search-Cg_OasF-.d.ts +0 -1
  162. package/dist/search-DN676Dnz.d.mts +0 -1
  163. package/dist/search-DOvSI-mb.mjs +0 -1
  164. package/dist/search-DoZF3RZj.js +0 -0
  165. package/dist/transport.manager-DETpXuaY.mjs.map +0 -1
  166. package/dist/transport.manager-ciG4r5uz.js.map +0 -1
@@ -1,127 +1,20 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
-
23
- //#endregion
24
- let node_events = require("node:events");
25
- node_events = __toESM(node_events);
26
- let node_readline = require("node:readline");
27
- node_readline = __toESM(node_readline);
1
+ const require_chunk = require('./chunk-nOFOJqeH.js');
2
+ const require_internal_log = require('./internal-log-BUJYMZbE.js');
3
+ const require_safe_stringify = require('./safe-stringify-DWo3aZp_.js');
4
+ const require_coerce_utils = require('./coerce.utils-BN980Y9V.js');
28
5
  let node_fs = require("node:fs");
29
- node_fs = __toESM(node_fs);
6
+ node_fs = require_chunk.__toESM(node_fs);
30
7
  let node_path = require("node:path");
31
- node_path = __toESM(node_path);
8
+ node_path = require_chunk.__toESM(node_path);
9
+ let node_events = require("node:events");
10
+ node_events = require_chunk.__toESM(node_events);
32
11
  let node_stream_promises = require("node:stream/promises");
33
- node_stream_promises = __toESM(node_stream_promises);
12
+ node_stream_promises = require_chunk.__toESM(node_stream_promises);
34
13
  let node_util = require("node:util");
35
- node_util = __toESM(node_util);
14
+ node_util = require_chunk.__toESM(node_util);
36
15
  let node_zlib = require("node:zlib");
37
- node_zlib = __toESM(node_zlib);
38
-
39
- //#region src/utils/internal-log.ts
40
- /**
41
- * Internal logging helpers for use within the logixia library itself.
42
- *
43
- * These functions write directly to stderr/stdout without going through
44
- * LogixiaLogger — this avoids circular dependencies and ensures internal
45
- * diagnostics still surface even when the logger is misconfigured.
46
- *
47
- * Library code must NEVER call console.log/warn/error directly. Use these
48
- * helpers instead so that internal output can be silenced in tests by setting
49
- * the LOGIXIA_SILENT_INTERNAL=1 environment variable.
50
- */
51
- /**
52
- * Read the silence flag on each call rather than caching it at import time, so
53
- * setting LOGIXIA_SILENT_INTERNAL=1 AFTER the module is first imported (e.g. in
54
- * a test setup file) still takes effect — the documented test-silencing
55
- * behavior was previously unreliable because the value was frozen at load.
56
- */
57
- function isSilent() {
58
- return process.env.LOGIXIA_SILENT_INTERNAL === "1";
59
- }
60
- /**
61
- * Emit an internal debug/info message to stderr.
62
- * Use for field-enable/disable notifications that a developer might want to see
63
- * during development but should not appear in production log streams.
64
- */
65
- function internalLog(message) {
66
- if (!isSilent()) process.stderr.write(`[logixia] ${message}\n`);
67
- }
68
- /**
69
- * Emit an internal warning to stderr.
70
- * Use when something is misconfigured but logixia can continue operating.
71
- */
72
- function internalWarn(message) {
73
- if (!isSilent()) process.stderr.write(`[logixia:warn] ${message}\n`);
74
- }
75
- /**
76
- * Emit an internal error to stderr.
77
- * Use when a transport write fails or a serious internal error occurs.
78
- */
79
- function internalError(message, error) {
80
- if (!isSilent()) {
81
- let errStr = "";
82
- if (error instanceof Error) errStr = ` — ${error.message}`;
83
- else if (error != null) errStr = ` — ${String(error)}`;
84
- process.stderr.write(`[logixia:error] ${message}${errStr}\n`);
85
- }
86
- }
16
+ node_zlib = require_chunk.__toESM(node_zlib);
87
17
 
88
- //#endregion
89
- //#region src/utils/coerce.utils.ts
90
- /**
91
- * Defensive value coercion helpers for Logixia.
92
- *
93
- * Logger inputs come from third-party code (NestJS framework internals,
94
- * user payloads, error objects) where the runtime type is not always what
95
- * the TypeScript signature promises. The formatter / transport hot paths
96
- * call `.replace()` on string fields and crash if the field is anything
97
- * else. Use these helpers anywhere a downstream consumer assumes a string.
98
- */
99
- /**
100
- * Coerce any value to a string without throwing.
101
- *
102
- * - `undefined` / `null` → `''` (so the field is rendered as empty rather
103
- * than the literal string "undefined")
104
- * - strings → returned as-is (zero allocation on the hot path)
105
- * - Errors → `error.message` (matches what users would expect to see)
106
- * - everything else → `JSON.stringify` if possible, falling back to
107
- * `String(value)` for circular refs / values without a stringifier.
108
- */
109
- function safeToString(value) {
110
- var _constructor;
111
- if (typeof value === "string") return value;
112
- if (value === void 0 || value === null) return "";
113
- if (value instanceof Error) return value.message;
114
- if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") return String(value);
115
- if (typeof value === "symbol") return value.toString();
116
- if (typeof value === "function") return `[Function: ${value.name || "anonymous"}]`;
117
- try {
118
- const json = JSON.stringify(value);
119
- if (json !== void 0) return json;
120
- } catch {}
121
- return `[${((_constructor = value.constructor) === null || _constructor === void 0 ? void 0 : _constructor.name) ?? "object"}]`;
122
- }
123
-
124
- //#endregion
125
18
  //#region src/transports/console.transport.ts
126
19
  /**
127
20
  * Writes log entries to stdout (info/debug/verbose) and stderr (error/warn).
@@ -154,7 +47,7 @@ var ConsoleTransport = class ConsoleTransport {
154
47
  ]);
155
48
  static #_2 = this.CONTROL_CHARS_RE = /[\x00-\x08\x0B-\x1F\x7F-\x9F]/g;
156
49
  static sanitize(value) {
157
- return safeToString(value).replace(ConsoleTransport.CONTROL_CHARS_RE, "");
50
+ return require_coerce_utils.safeToString(value).replace(ConsoleTransport.CONTROL_CHARS_RE, "");
158
51
  }
159
52
  constructor(config = {}) {
160
53
  this.config = config;
@@ -167,7 +60,7 @@ var ConsoleTransport = class ConsoleTransport {
167
60
  (lvl === "error" || lvl === "warn" ? process.stderr : process.stdout).write(formatted);
168
61
  }
169
62
  formatEntry(entry) {
170
- if (this.config.format === "json") return JSON.stringify({
63
+ if (this.config.format === "json") return require_safe_stringify.stringifyForLog({
171
64
  timestamp: this.config.timestamp !== false ? entry.timestamp.toISOString() : void 0,
172
65
  level: entry.level,
173
66
  message: entry.message,
@@ -195,7 +88,7 @@ var ConsoleTransport = class ConsoleTransport {
195
88
  }
196
89
  parts.push(ConsoleTransport.sanitize(entry.message));
197
90
  if (entry.data && Object.keys(entry.data).length > 0) {
198
- const data = JSON.stringify(entry.data);
91
+ const data = require_safe_stringify.stringifyForLog(entry.data);
199
92
  parts.push(this.colorize(data, "blue"));
200
93
  }
201
94
  return parts.join(" ");
@@ -230,6 +123,15 @@ var ConsoleTransport = class ConsoleTransport {
230
123
  * Validates that a SQL identifier (table/index name) contains only safe characters.
231
124
  * Prevents SQL injection via user-supplied table names.
232
125
  */
126
+ const POSTGRES_MAX_PARAMS = 65535;
127
+ const POSTGRES_COLUMNS = 8;
128
+ const POSTGRES_ROWS_PER_INSERT = Math.floor(POSTGRES_MAX_PARAMS / POSTGRES_COLUMNS);
129
+ const MYSQL_ROWS_PER_INSERT = 5e3;
130
+ function chunk(items, size) {
131
+ const chunks = [];
132
+ for (let i = 0; i < items.length; i += size) chunks.push(items.slice(i, i + size));
133
+ return chunks;
134
+ }
233
135
  function validateSqlIdentifier(name, label = "table") {
234
136
  if (!/^[a-zA-Z_]\w*$/.test(name)) throw new Error(`Invalid ${label} name "${name}". Only letters, digits, and underscores are allowed, and the name must start with a letter or underscore.`);
235
137
  }
@@ -300,7 +202,7 @@ var DatabaseTransport = class {
300
202
  default: throw new Error(`Unsupported database type: ${this.config.type}`);
301
203
  }
302
204
  } catch (error) {
303
- internalError("Database flush error", error);
205
+ require_internal_log.internalError("Database flush error", error);
304
206
  this.batch.unshift(...entriesToFlush);
305
207
  throw error;
306
208
  }
@@ -431,22 +333,38 @@ var DatabaseTransport = class {
431
333
  }
432
334
  async flushToPostgreSQL(entries) {
433
335
  const tableName = this.config.table || "logs";
434
- const values = entries.map((entry) => [
336
+ const chunks = chunk(entries.map((entry) => [
435
337
  entry.timestamp,
436
338
  entry.level,
437
339
  entry.message,
438
- JSON.stringify(entry.data || {}),
340
+ require_safe_stringify.stringifyForLog(entry.data || {}),
439
341
  entry.context,
440
342
  entry.traceId,
441
343
  entry.appName,
442
344
  entry.environment
443
- ]);
444
- const query = `
345
+ ]), POSTGRES_ROWS_PER_INSERT);
346
+ const insertChunk = async (rows) => {
347
+ const query = `
445
348
  INSERT INTO ${tableName}
446
349
  (timestamp, level, message, payload, context, trace_id, app_name, environment)
447
- VALUES ${values.map((_, i) => `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8})`).join(", ")}
350
+ VALUES ${rows.map((_, i) => {
351
+ return `(${Array.from({ length: POSTGRES_COLUMNS }, (_unused, c) => `$${i * POSTGRES_COLUMNS + c + 1}`).join(", ")})`;
352
+ }).join(", ")}
448
353
  `;
449
- await this.connection.query(query, values.flat());
354
+ await this.connection.query(query, rows.flat());
355
+ };
356
+ if (chunks.length === 1) {
357
+ await insertChunk(chunks[0]);
358
+ return;
359
+ }
360
+ await this.connection.query("BEGIN");
361
+ try {
362
+ for (const rows of chunks) await insertChunk(rows);
363
+ await this.connection.query("COMMIT");
364
+ } catch (error) {
365
+ await this.connection.query("ROLLBACK").catch(() => {});
366
+ throw error;
367
+ }
450
368
  }
451
369
  async flushToMySQL(entries) {
452
370
  const tableName = this.config.table || "logs";
@@ -454,7 +372,7 @@ var DatabaseTransport = class {
454
372
  entry.timestamp,
455
373
  entry.level,
456
374
  entry.message,
457
- JSON.stringify(entry.data || {}),
375
+ require_safe_stringify.stringifyForLog(entry.data || {}),
458
376
  entry.context,
459
377
  entry.traceId,
460
378
  entry.appName,
@@ -465,7 +383,19 @@ var DatabaseTransport = class {
465
383
  (timestamp, level, message, payload, context, trace_id, app_name, environment)
466
384
  VALUES ?
467
385
  `;
468
- await this.connection.query(query, [values]);
386
+ const chunks = chunk(values, MYSQL_ROWS_PER_INSERT);
387
+ if (chunks.length === 1) {
388
+ await this.connection.query(query, [values]);
389
+ return;
390
+ }
391
+ await this.connection.beginTransaction();
392
+ try {
393
+ for (const rows of chunks) await this.connection.query(query, [rows]);
394
+ await this.connection.commit();
395
+ } catch (error) {
396
+ await this.connection.rollback().catch(() => {});
397
+ throw error;
398
+ }
469
399
  }
470
400
  async flushToSQLite(entries) {
471
401
  const tableName = this.config.table || "logs";
@@ -474,8 +404,16 @@ var DatabaseTransport = class {
474
404
  (timestamp, level, message, payload, context, trace_id, app_name, environment)
475
405
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
476
406
  `);
477
- for (const entry of entries) await stmt.run(entry.timestamp instanceof Date ? entry.timestamp.toISOString() : entry.timestamp, entry.level, entry.message, JSON.stringify(entry.data || {}), entry.context, entry.traceId, entry.appName, entry.environment);
478
- await stmt.finalize();
407
+ await this.connection.exec("BEGIN");
408
+ try {
409
+ for (const entry of entries) await stmt.run(entry.timestamp instanceof Date ? entry.timestamp.toISOString() : entry.timestamp, entry.level, entry.message, require_safe_stringify.stringifyForLog(entry.data || {}), entry.context, entry.traceId, entry.appName, entry.environment);
410
+ await this.connection.exec("COMMIT");
411
+ } catch (error) {
412
+ await this.connection.exec("ROLLBACK").catch(() => {});
413
+ throw error;
414
+ } finally {
415
+ await stmt.finalize().catch(() => {});
416
+ }
479
417
  }
480
418
  async createPostgreSQLTable() {
481
419
  const tableName = this.config.table || "logs";
@@ -543,11 +481,13 @@ var DatabaseTransport = class {
543
481
  await this.connection.exec(`CREATE INDEX IF NOT EXISTS idx_${tableName}_trace_id ON ${tableName} (trace_id)`);
544
482
  }
545
483
  setupFlushTimer() {
484
+ var _this$flushTimer$unre, _this$flushTimer;
546
485
  this.flushTimer = setInterval(() => {
547
486
  this.flush().catch((error) => {
548
- internalError("Scheduled database flush error", error);
487
+ require_internal_log.internalError("Scheduled database flush error", error);
549
488
  });
550
489
  }, this.flushInterval);
490
+ (_this$flushTimer$unre = (_this$flushTimer = this.flushTimer).unref) === null || _this$flushTimer$unre === void 0 || _this$flushTimer$unre.call(_this$flushTimer);
551
491
  }
552
492
  async close() {
553
493
  if (this.flushTimer) {
@@ -555,12 +495,13 @@ var DatabaseTransport = class {
555
495
  this.flushTimer = void 0;
556
496
  }
557
497
  const MAX_CLOSE_FLUSH_ATTEMPTS = 3;
498
+ if (this.flushPromise) await this.flushPromise.catch(() => {});
558
499
  for (let attempt = 0; attempt < MAX_CLOSE_FLUSH_ATTEMPTS && this.batch.length > 0; attempt += 1) try {
559
500
  await this.flush();
560
501
  } catch (error) {
561
- internalError("Database flush during close failed; entries remain buffered", error);
502
+ require_internal_log.internalError("Database flush during close failed; entries remain buffered", error);
562
503
  }
563
- if (this.batch.length > 0) internalError(`Database transport closing with ${this.batch.length} unflushed log entr${this.batch.length === 1 ? "y" : "ies"} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`);
504
+ if (this.batch.length > 0) require_internal_log.internalError(`Database transport closing with ${this.batch.length} unflushed log entr${this.batch.length === 1 ? "y" : "ies"} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`);
564
505
  if (this.connection) switch (this.config.type) {
565
506
  case "mongodb":
566
507
  await this.connection.close();
@@ -590,14 +531,22 @@ var DatabaseTransport = class {
590
531
  try {
591
532
  switch (this.config.type) {
592
533
  case "mongodb": return await this.connection.db(this.config.database).collection(this.config.collection || "logs").countDocuments();
593
- case "postgresql":
594
- case "mysql":
595
- case "sqlite": {
596
- var _result$rows, _result$;
534
+ case "postgresql": {
535
+ var _result$rows;
597
536
  const tableName = this.config.table || "logs";
598
537
  const result = await this.connection.query(`SELECT COUNT(*) as count FROM ${tableName}`);
599
- const rawCount = ((_result$rows = result.rows) === null || _result$rows === void 0 || (_result$rows = _result$rows[0]) === null || _result$rows === void 0 ? void 0 : _result$rows.count) ?? ((_result$ = result[0]) === null || _result$ === void 0 ? void 0 : _result$.count) ?? 0;
600
- return Number(rawCount) || 0;
538
+ return Number((_result$rows = result.rows) === null || _result$rows === void 0 || (_result$rows = _result$rows[0]) === null || _result$rows === void 0 ? void 0 : _result$rows.count) || 0;
539
+ }
540
+ case "mysql": {
541
+ var _rows$;
542
+ const tableName = this.config.table || "logs";
543
+ const [rows] = await this.connection.query(`SELECT COUNT(*) as count FROM ${tableName}`);
544
+ return Number(rows === null || rows === void 0 || (_rows$ = rows[0]) === null || _rows$ === void 0 ? void 0 : _rows$.count) || 0;
545
+ }
546
+ case "sqlite": {
547
+ const tableName = this.config.table || "logs";
548
+ const row = await this.connection.get(`SELECT COUNT(*) as count FROM ${tableName}`);
549
+ return Number(row === null || row === void 0 ? void 0 : row.count) || 0;
601
550
  }
602
551
  default: return 0;
603
552
  }
@@ -624,20 +573,23 @@ var AnalyticsTransport = class {
624
573
  this.level = config.level;
625
574
  this.batchSize = this.config.batchSize || 50;
626
575
  this.flushInterval = this.config.flushInterval || 1e4;
627
- this.initialize();
628
576
  }
629
577
  async write(entry) {
630
- if (!this.isReady) await this.waitForReady();
578
+ if (!this.isReady) await this.ensureReady();
631
579
  if (this.shouldSkipEntry(entry)) return;
632
580
  if (this.config.batchSize && this.config.batchSize > 1) this.addToBatch(entry);
633
581
  else await this.sendEntry(entry);
634
582
  }
635
583
  addToBatch(entry) {
636
584
  this.batch.push(entry);
637
- if (this.batch.length >= (this.config.batchSize || 50)) this.flush().catch((err) => internalError(`${this.name} batch flush failed`, err));
638
- else if (!this.batchTimer && this.config.flushInterval) this.batchTimer = setTimeout(() => {
639
- this.flush().catch((err) => internalError(`${this.name} interval flush failed`, err));
640
- }, this.config.flushInterval);
585
+ if (this.batch.length >= (this.config.batchSize || 50)) this.flush().catch((err) => require_internal_log.internalError(`${this.name} batch flush failed`, err));
586
+ else if (!this.batchTimer && this.config.flushInterval) {
587
+ var _this$batchTimer$unre, _this$batchTimer;
588
+ this.batchTimer = setTimeout(() => {
589
+ this.flush().catch((err) => require_internal_log.internalError(`${this.name} interval flush failed`, err));
590
+ }, this.config.flushInterval);
591
+ (_this$batchTimer$unre = (_this$batchTimer = this.batchTimer).unref) === null || _this$batchTimer$unre === void 0 || _this$batchTimer$unre.call(_this$batchTimer);
592
+ }
641
593
  }
642
594
  async flush() {
643
595
  if (this.batchTimer) {
@@ -663,7 +615,7 @@ var AnalyticsTransport = class {
663
615
  try {
664
616
  await this.sendBatch(entriesToSend);
665
617
  } catch (error) {
666
- internalError(`Analytics transport ${this.name} flush failed`, error);
618
+ require_internal_log.internalError(`Analytics transport ${this.name} flush failed`, error);
667
619
  this.batch.unshift(...entriesToSend);
668
620
  return;
669
621
  }
@@ -676,7 +628,7 @@ var AnalyticsTransport = class {
676
628
  }
677
629
  const MAX_CLOSE_FLUSH_ATTEMPTS = 3;
678
630
  for (let attempt = 0; attempt < MAX_CLOSE_FLUSH_ATTEMPTS && this.batch.length > 0; attempt += 1) await this.flush();
679
- if (this.batch.length > 0) internalError(`Analytics transport ${this.name} closing with ${this.batch.length} unflushed entr${this.batch.length === 1 ? "y" : "ies"} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`);
631
+ if (this.batch.length > 0) require_internal_log.internalError(`Analytics transport ${this.name} closing with ${this.batch.length} unflushed entr${this.batch.length === 1 ? "y" : "ies"} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`);
680
632
  await this.cleanup();
681
633
  }
682
634
  shouldSkipEntry(entry) {
@@ -696,10 +648,20 @@ var AnalyticsTransport = class {
696
648
  if (this.config.customProperties) Object.assign(transformed, this.config.customProperties);
697
649
  return transformed;
698
650
  }
699
- async waitForReady(timeout = 5e3) {
700
- const startTime = Date.now();
701
- while (!this.isReady && Date.now() - startTime < timeout) await new Promise((resolve) => setTimeout(resolve, 100));
702
- if (!this.isReady) throw new Error(`Analytics transport ${this.name} failed to initialize within ${timeout}ms`);
651
+ /**
652
+ * Runs initialize() once. A bad config (say, a missing API key) fails every
653
+ * write with the same error instead of an unhandled rejection at startup.
654
+ */
655
+ ensureReady() {
656
+ this.initPromise ??= (async () => {
657
+ await this.initialize();
658
+ this.isReady = true;
659
+ })();
660
+ return this.initPromise;
661
+ }
662
+ /** @deprecated Use ensureReady(); kept for subclasses that call it. */
663
+ async waitForReady() {
664
+ await this.ensureReady();
703
665
  }
704
666
  };
705
667
 
@@ -725,10 +687,9 @@ var DataDogTransport = class extends AnalyticsTransport {
725
687
  async initialize() {
726
688
  try {
727
689
  if (!this.datadogConfig.apiKey) throw new Error("DataDog API key is required");
728
- await this.testConnection();
729
690
  this.isReady = true;
730
691
  } catch (error) {
731
- internalError("DataDog transport initialization failed", error);
692
+ require_internal_log.internalError("DataDog transport initialization failed", error);
732
693
  throw error;
733
694
  }
734
695
  }
@@ -776,11 +737,11 @@ var DataDogTransport = class extends AnalyticsTransport {
776
737
  timestamp: entry.timestamp.toISOString(),
777
738
  level: this.mapLogLevel(entry.level),
778
739
  message: entry.message,
779
- service: this.datadogConfig.service || "logitron",
740
+ service: this.datadogConfig.service || "logixia",
780
741
  version: this.datadogConfig.version,
781
742
  env: this.datadogConfig.env || "production",
782
743
  logger: {
783
- name: "logitron",
744
+ name: "logixia",
784
745
  thread_name: "main"
785
746
  },
786
747
  ...transformed,
@@ -798,7 +759,7 @@ var DataDogTransport = class extends AnalyticsTransport {
798
759
  host: this.getHostname(),
799
760
  tags: [
800
761
  `level:${entry.level}`,
801
- `service:${this.datadogConfig.service || "logitron"}`,
762
+ `service:${this.datadogConfig.service || "logixia"}`,
802
763
  `env:${this.datadogConfig.env || "production"}`,
803
764
  ...entry.context ? [`context:${entry.context}`] : [],
804
765
  ...entry.appName ? [`app:${entry.appName}`] : []
@@ -811,7 +772,7 @@ var DataDogTransport = class extends AnalyticsTransport {
811
772
  span_id: this.generateSpanId(),
812
773
  parent_id: null,
813
774
  name: "log.entry",
814
- service: this.datadogConfig.service || "logitron",
775
+ service: this.datadogConfig.service || "logixia",
815
776
  resource: entry.message,
816
777
  type: "log",
817
778
  start: entry.timestamp.getTime() * 1e6,
@@ -848,7 +809,7 @@ var DataDogTransport = class extends AnalyticsTransport {
848
809
  "Content-Type": "application/json",
849
810
  "DD-API-KEY": this.datadogConfig.apiKey
850
811
  },
851
- body: JSON.stringify(data)
812
+ body: require_safe_stringify.stringifyForLog(data)
852
813
  });
853
814
  if (!response.ok) throw new Error(`DataDog Logs API error: ${response.status} ${response.statusText}`);
854
815
  } catch (error) {
@@ -864,23 +825,13 @@ var DataDogTransport = class extends AnalyticsTransport {
864
825
  "Content-Type": "application/json",
865
826
  "DD-API-KEY": this.datadogConfig.apiKey
866
827
  },
867
- body: JSON.stringify(data)
828
+ body: require_safe_stringify.stringifyForLog(data)
868
829
  });
869
830
  if (!response.ok) throw new Error(`DataDog Metrics API error: ${response.status} ${response.statusText}`);
870
831
  } catch (error) {
871
832
  throw new Error(`Failed to send metrics to DataDog: ${error.message}`, { cause: error });
872
833
  }
873
834
  }
874
- async testConnection() {
875
- const testMetric = { series: [{
876
- metric: "logitron.test",
877
- points: [[Math.floor(Date.now() / 1e3), 1]],
878
- type: "count",
879
- host: this.getHostname(),
880
- tags: ["test:true"]
881
- }] };
882
- await this.makeMetricsRequest("/api/v1/series", testMetric);
883
- }
884
835
  generateSpanId() {
885
836
  return Math.floor(Math.random() * 0xffffffffffff).toString(16);
886
837
  }
@@ -908,11 +859,11 @@ var DataDogTransport = class extends AnalyticsTransport {
908
859
  date_happened: Math.floor(Date.now() / 1e3),
909
860
  priority: "normal",
910
861
  tags: [
911
- `service:${this.datadogConfig.service || "logitron"}`,
862
+ `service:${this.datadogConfig.service || "logixia"}`,
912
863
  `env:${this.datadogConfig.env || "production"}`,
913
864
  ...tags
914
865
  ],
915
- source_type_name: "logitron"
866
+ source_type_name: "logixia"
916
867
  };
917
868
  const url = `${this.baseUrl}/api/v1/events`;
918
869
  try {
@@ -922,7 +873,7 @@ var DataDogTransport = class extends AnalyticsTransport {
922
873
  "Content-Type": "application/json",
923
874
  "DD-API-KEY": this.datadogConfig.apiKey
924
875
  },
925
- body: JSON.stringify(payload)
876
+ body: require_safe_stringify.stringifyForLog(payload)
926
877
  });
927
878
  if (!response.ok) throw new Error(`DataDog Events API error: ${response.status} ${response.statusText}`);
928
879
  } catch (error) {
@@ -933,16 +884,46 @@ var DataDogTransport = class extends AnalyticsTransport {
933
884
 
934
885
  //#endregion
935
886
  //#region src/transports/file.transport.ts
936
- const writeFile = (0, node_util.promisify)(node_fs.writeFile);
937
- const mkdir = (0, node_util.promisify)(node_fs.mkdir);
887
+ const appendFile = (0, node_util.promisify)(node_fs.appendFile);
888
+ const rename = (0, node_util.promisify)(node_fs.rename);
938
889
  const unlink = (0, node_util.promisify)(node_fs.unlink);
939
890
  const readdir = (0, node_util.promisify)(node_fs.readdir);
940
891
  const stat = (0, node_util.promisify)(node_fs.stat);
892
+ const DROP_WARN_INTERVAL_MS = 5e3;
893
+ const SIZE_UNITS = {
894
+ "": 1,
895
+ k: 1024,
896
+ m: 1024 ** 2,
897
+ g: 1024 ** 3,
898
+ t: 1024 ** 4
899
+ };
900
+ /**
901
+ * Parses a size like `50MB`, `1.5g`, `512 KiB` or a plain byte count.
902
+ * Units are binary (1KB = 1024 bytes). Returns undefined for anything else.
903
+ */
904
+ function parseSize(value) {
905
+ if (value === void 0) return void 0;
906
+ if (typeof value === "number") return Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
907
+ const match = /^(\d+(?:\.\d+)?) ?([kmgt]?)(?:i?b)?$/i.exec(value.trim());
908
+ if (!match) return void 0;
909
+ const bytes = Math.floor(Number(match[1]) * (SIZE_UNITS[(match[2] ?? "").toLowerCase()] ?? 1));
910
+ return bytes > 0 ? bytes : void 0;
911
+ }
912
+ /** Formats a date with the YYYY, MM, DD, HH, mm and ss tokens, in local time. */
913
+ function formatDatePattern(date, pattern) {
914
+ const pad = (n) => String(n).padStart(2, "0");
915
+ return pattern.replace(/YYYY/g, String(date.getFullYear())).replace(/MM/g, pad(date.getMonth() + 1)).replace(/DD/g, pad(date.getDate())).replace(/HH/g, pad(date.getHours())).replace(/mm/g, pad(date.getMinutes())).replace(/ss/g, pad(date.getSeconds()));
916
+ }
941
917
  /**
942
918
  * Appends log entries to a local file with automatic rotation.
943
919
  *
944
- * Supports batched writes, size-based and date-based rotation, and optional
945
- * gzip compression of rotated files.
920
+ * Rotates by size (`maxSize`), by date (`datePattern`), by interval
921
+ * (`rotation.interval`) or by a custom `rotation.shouldRotate` check. Rotated
922
+ * files can be gzipped (`zippedArchive`) and pruned to the newest `maxFiles`.
923
+ *
924
+ * Without a `rotation` block the live file keeps its name (`app.log`) and
925
+ * rotated files are renamed beside it (`app-2026-10-22T09-30-00-000.log`).
926
+ * With a `rotation` block every file, the live one included, is timestamped.
946
927
  *
947
928
  * @example
948
929
  * transports: { file: { filename: 'app.log', dirname: './logs', maxSize: '50MB' } }
@@ -952,7 +933,17 @@ var FileTransport = class {
952
933
  this.name = "file";
953
934
  this.batch = [];
954
935
  this.lastRotation = /* @__PURE__ */ new Date();
955
- this.isRotating = false;
936
+ this.bytesWritten = 0;
937
+ this.rotateRequested = false;
938
+ this.rotationWaiters = [];
939
+ this.enqueued = 0;
940
+ this.settled = 0;
941
+ this.flushWaiters = [];
942
+ this.spaceWaiters = [];
943
+ this.inFlight = 0;
944
+ this.dropped = 0;
945
+ this.lastDropWarning = 0;
946
+ this.housekeeping = Promise.resolve();
956
947
  this.config = {
957
948
  format: "json",
958
949
  batchSize: 100,
@@ -963,100 +954,257 @@ var FileTransport = class {
963
954
  this.batchSize = this.config.batchSize || 100;
964
955
  this.flushInterval = this.config.flushInterval || 5e3;
965
956
  if (config.filter) this.filter = config.filter;
966
- this.currentFilePath = this.generateFilePath();
967
- this.ensureDirectoryExists();
957
+ const rotation = config.rotation;
958
+ const rawMaxSize = (rotation === null || rotation === void 0 ? void 0 : rotation.maxSize) ?? config.maxSize;
959
+ this.maxBytes = parseSize(rawMaxSize);
960
+ if (rawMaxSize !== void 0 && this.maxBytes === void 0) require_internal_log.internalWarn(`FileTransport: maxSize ${JSON.stringify(rawMaxSize)} is not a valid size (try '50MB'); size rotation is off`);
961
+ const maxQueue = config.maxQueue;
962
+ if (maxQueue !== void 0 && !(Number.isInteger(maxQueue) && maxQueue > 0)) require_internal_log.internalWarn(`FileTransport: maxQueue ${String(maxQueue)} is not a positive integer; ignored`);
963
+ else this.maxQueue = maxQueue;
964
+ this.queuePolicy = config.queuePolicy ?? "drop-oldest";
965
+ this.maxFiles = (rotation === null || rotation === void 0 ? void 0 : rotation.maxFiles) ?? config.maxFiles;
966
+ this.compress = (rotation === null || rotation === void 0 ? void 0 : rotation.compress) ?? config.zippedArchive ?? false;
967
+ this.intervalMs = (rotation === null || rotation === void 0 ? void 0 : rotation.interval) ? this.parseInterval(rotation.interval) : void 0;
968
+ this.timestampedActive = rotation !== void 0;
969
+ this.dir = this.resolveDir();
970
+ this.ext = node_path.extname(config.filename);
971
+ this.base = node_path.basename(config.filename, this.ext);
972
+ try {
973
+ node_fs.mkdirSync(this.dir, { recursive: true });
974
+ } catch (error) {
975
+ require_internal_log.internalError(`FileTransport could not create ${this.dir}`, error);
976
+ }
977
+ this.currentFilePath = this.timestampedActive ? this.uniquePath(this.timestamp(/* @__PURE__ */ new Date())) : node_path.join(this.dir, `${this.base}${this.ext}`);
978
+ let existing;
979
+ try {
980
+ existing = node_fs.statSync(this.currentFilePath);
981
+ } catch {
982
+ existing = void 0;
983
+ }
984
+ this.bytesWritten = (existing === null || existing === void 0 ? void 0 : existing.size) ?? 0;
985
+ if (config.datePattern) this.period = formatDatePattern((existing === null || existing === void 0 ? void 0 : existing.mtime) ?? /* @__PURE__ */ new Date(), config.datePattern);
968
986
  }
969
987
  async write(entry) {
970
988
  try {
971
- if (this.shouldRotateNow() && !this.isRotating) {
972
- this.isRotating = true;
973
- try {
974
- await this.rotate();
975
- } finally {
976
- this.isRotating = false;
977
- }
978
- }
989
+ while (this.queuePolicy === "block" && this.queueFull()) await this.roomAvailable();
979
990
  if (this.config.batchSize && this.config.batchSize > 1) this.addToBatch(entry);
980
- else await this.writeBatch([entry]);
991
+ else {
992
+ this.enqueue(entry);
993
+ await this.flush();
994
+ }
981
995
  } catch (error) {
982
996
  throw new Error(`File transport write failed: ${error.message}`, { cause: error });
983
997
  }
984
998
  }
999
+ /**
1000
+ * Resolves once every entry queued before the call is on disk. It does not
1001
+ * wait for entries queued afterwards, so under steady traffic it still returns
1002
+ * instead of waiting for a quiet moment that never comes.
1003
+ */
985
1004
  async flush() {
986
1005
  if (this.batchTimer) {
987
1006
  clearTimeout(this.batchTimer);
988
1007
  this.batchTimer = void 0;
989
1008
  }
990
- if (!this.flushPromise) this.flushPromise = this.drain().finally(() => {
991
- this.flushPromise = void 0;
1009
+ if (this.settled >= this.enqueued) return;
1010
+ const done = new Promise((resolve, reject) => {
1011
+ this.flushWaiters.push({
1012
+ upTo: this.enqueued,
1013
+ resolve,
1014
+ reject
1015
+ });
1016
+ });
1017
+ this.startDrain();
1018
+ await done;
1019
+ }
1020
+ /** Writes out anything pending, then starts a new file. */
1021
+ async rotate() {
1022
+ await this.flush();
1023
+ const done = new Promise((resolve) => this.rotationWaiters.push(resolve));
1024
+ this.rotateRequested = true;
1025
+ this.startDrain();
1026
+ await done;
1027
+ }
1028
+ enqueue(entry) {
1029
+ if (this.queueFull()) {
1030
+ if (this.queuePolicy === "drop-oldest" && this.batch.length > 0) {
1031
+ this.batch.shift();
1032
+ this.batch.push(entry);
1033
+ }
1034
+ this.noteDropped();
1035
+ return;
1036
+ }
1037
+ this.batch.push(entry);
1038
+ this.enqueued += 1;
1039
+ }
1040
+ queueFull() {
1041
+ return this.maxQueue !== void 0 && this.batch.length + this.inFlight >= this.maxQueue;
1042
+ }
1043
+ roomAvailable() {
1044
+ const room = new Promise((resolve) => this.spaceWaiters.push(resolve));
1045
+ this.startDrain();
1046
+ return room;
1047
+ }
1048
+ releaseWaiters() {
1049
+ const waiting = this.spaceWaiters;
1050
+ this.spaceWaiters = [];
1051
+ for (const resolve of waiting) resolve();
1052
+ }
1053
+ noteDropped() {
1054
+ this.dropped += 1;
1055
+ this.reportDropped(DROP_WARN_INTERVAL_MS);
1056
+ }
1057
+ reportDropped(minGapMs) {
1058
+ const now = Date.now();
1059
+ if (this.dropped === 0 || now - this.lastDropWarning < minGapMs) return;
1060
+ require_internal_log.internalWarn(`FileTransport: dropped ${this.dropped} log line${this.dropped === 1 ? "" : "s"} (queue full at maxQueue ${this.maxQueue}, policy ${this.queuePolicy})`);
1061
+ this.dropped = 0;
1062
+ this.lastDropWarning = now;
1063
+ }
1064
+ startDrain() {
1065
+ if (this.draining) return;
1066
+ this.draining = this.drain().finally(() => {
1067
+ this.draining = void 0;
992
1068
  });
993
- await this.flushPromise;
994
1069
  }
995
1070
  /**
996
- * Drains the batch to disk one snapshot at a time. Entries appended while a
997
- * write is in flight are picked up by the next loop iteration, so a single
998
- * drain() empties the batch completely regardless of concurrent writes.
1071
+ * Writes the batch one snapshot at a time. Entries queued while a write is in
1072
+ * flight are picked up by the next iteration, so one drain empties the batch
1073
+ * however much is logged meanwhile.
999
1074
  */
1000
1075
  async drain() {
1001
- while (this.batch.length > 0) {
1076
+ while (this.batch.length > 0 || this.rotateRequested) {
1077
+ if (this.rotateRequested) {
1078
+ this.rotateRequested = false;
1079
+ const waiting = this.rotationWaiters;
1080
+ this.rotationWaiters = [];
1081
+ try {
1082
+ await this.rotateFile();
1083
+ } catch (error) {
1084
+ require_internal_log.internalError("FileTransport rotation failed", error);
1085
+ }
1086
+ for (const resolve of waiting) resolve();
1087
+ continue;
1088
+ }
1002
1089
  const pending = this.batch;
1003
1090
  this.batch = [];
1004
- await this.writeBatch(pending);
1091
+ this.inFlight = pending.length;
1092
+ let failure;
1093
+ try {
1094
+ await this.writeBatch(pending);
1095
+ } catch (error) {
1096
+ failure = error;
1097
+ }
1098
+ this.inFlight = 0;
1099
+ this.settled += pending.length;
1100
+ this.settle(failure);
1101
+ this.releaseWaiters();
1005
1102
  }
1006
1103
  }
1104
+ /**
1105
+ * Resolves the flush() calls whose entries are now written. A failed batch
1106
+ * fails every flush still waiting, since each one covers that batch.
1107
+ */
1108
+ settle(failure) {
1109
+ if (failure !== void 0) {
1110
+ if (this.flushWaiters.length === 0) require_internal_log.internalError("FileTransport write failed", failure);
1111
+ for (const waiter of this.flushWaiters) waiter.error ??= failure;
1112
+ }
1113
+ const waiting = [];
1114
+ for (const waiter of this.flushWaiters) if (waiter.upTo > this.settled) waiting.push(waiter);
1115
+ else if (waiter.error === void 0) waiter.resolve();
1116
+ else waiter.reject(waiter.error);
1117
+ this.flushWaiters = waiting;
1118
+ }
1007
1119
  formatEntry(entry) {
1008
1120
  switch (this.config.format) {
1009
- case "json": return JSON.stringify({
1121
+ case "json": return require_safe_stringify.stringifyForLog({
1010
1122
  timestamp: entry.timestamp.toISOString(),
1011
1123
  level: entry.level,
1012
1124
  message: entry.message,
1013
1125
  ...entry.data || {}
1014
1126
  });
1015
1127
  case "csv": {
1016
- const safeMessage = safeToString(entry.message);
1128
+ const safeMessage = require_coerce_utils.safeToString(entry.message);
1017
1129
  return [
1018
1130
  entry.timestamp.toISOString(),
1019
1131
  entry.level,
1020
1132
  `"${safeMessage.replace(/"/g, "\"\"")}"`,
1021
- safeToString(entry.context),
1022
- safeToString(entry.traceId),
1023
- JSON.stringify(entry.data || {})
1133
+ require_coerce_utils.safeToString(entry.context),
1134
+ require_coerce_utils.safeToString(entry.traceId),
1135
+ require_safe_stringify.stringifyForLog(entry.data || {})
1024
1136
  ].join(",");
1025
1137
  }
1026
1138
  case "text":
1027
- default: return `${entry.timestamp.toISOString()} [${entry.level.toUpperCase()}] ${safeToString(entry.message)}${entry.data ? " " + JSON.stringify(entry.data) : ""}`;
1139
+ default: return `${entry.timestamp.toISOString()} [${entry.level.toUpperCase()}] ${require_coerce_utils.safeToString(entry.message)}${entry.data ? " " + require_safe_stringify.stringifyForLog(entry.data) : ""}`;
1028
1140
  }
1029
1141
  }
1030
1142
  addToBatch(entry) {
1031
- this.batch.push(entry);
1032
- if (this.batch.length >= (this.config.batchSize || 100)) {
1143
+ this.enqueue(entry);
1144
+ const threshold = Math.min(this.config.batchSize || 100, this.maxQueue ?? Infinity);
1145
+ if (this.batch.length >= threshold && !this.draining) {
1033
1146
  if (this.batchTimer) {
1034
1147
  clearTimeout(this.batchTimer);
1035
1148
  this.batchTimer = void 0;
1036
1149
  }
1037
- this.flush().catch((err) => internalError("FileTransport batch flush failed", err));
1150
+ this.startDrain();
1038
1151
  } else if (!this.batchTimer && this.config.flushInterval) this.batchTimer = setTimeout(() => {
1039
- this.flush().catch((err) => internalError("FileTransport interval flush failed", err));
1152
+ this.flush().catch((err) => require_internal_log.internalError("FileTransport interval flush failed", err));
1040
1153
  }, this.config.flushInterval);
1041
1154
  }
1155
+ /** Only ever called from drain(), so writes and rotations never overlap. */
1042
1156
  async writeBatch(entries) {
1043
1157
  if (entries.length === 0) return;
1044
- const content = entries.map((entry) => this.formatEntry(entry)).join("\n") + "\n";
1045
- if (this.writeStream) return new Promise((resolve, reject) => {
1046
- this.writeStream.write(content, (error) => {
1047
- if (error) reject(error);
1048
- else resolve();
1049
- });
1050
- });
1051
- else await writeFile(this.currentFilePath, content, { flag: "a" });
1158
+ if (this.timeToRotate()) await this.rotateFile();
1159
+ if (this.maxBytes === void 0) {
1160
+ const content = entries.map((entry) => this.formatEntry(entry)).join("\n") + "\n";
1161
+ await this.append(content);
1162
+ return;
1163
+ }
1164
+ let chunk$1 = "";
1165
+ let chunkBytes = 0;
1166
+ for (const entry of entries) {
1167
+ const line = this.formatEntry(entry) + "\n";
1168
+ const lineBytes = Buffer.byteLength(line);
1169
+ const used = this.bytesWritten + chunkBytes;
1170
+ if (used > 0 && used + lineBytes > this.maxBytes) {
1171
+ if (chunk$1) await this.append(chunk$1, chunkBytes);
1172
+ chunk$1 = "";
1173
+ chunkBytes = 0;
1174
+ await this.rotateFile();
1175
+ }
1176
+ chunk$1 += line;
1177
+ chunkBytes += lineBytes;
1178
+ }
1179
+ if (chunk$1) await this.append(chunk$1, chunkBytes);
1052
1180
  }
1053
- shouldRotateNow() {
1181
+ async append(content, bytes = Buffer.byteLength(content)) {
1182
+ await appendFile(this.currentFilePath, content);
1183
+ this.bytesWritten += bytes;
1184
+ }
1185
+ timeToRotate() {
1054
1186
  var _this$config$rotation;
1055
- if (!((_this$config$rotation = this.config.rotation) === null || _this$config$rotation === void 0 ? void 0 : _this$config$rotation.interval)) return false;
1056
- return (/* @__PURE__ */ new Date()).getTime() - this.lastRotation.getTime() >= this.parseInterval(this.config.rotation.interval);
1187
+ if (this.intervalMs !== void 0 && Date.now() - this.lastRotation.getTime() >= this.intervalMs) return true;
1188
+ if (this.config.datePattern && this.period !== void 0) {
1189
+ if (formatDatePattern(/* @__PURE__ */ new Date(), this.config.datePattern) !== this.period) return true;
1190
+ }
1191
+ const shouldRotate = (_this$config$rotation = this.config.rotation) === null || _this$config$rotation === void 0 ? void 0 : _this$config$rotation.shouldRotate;
1192
+ if (shouldRotate) {
1193
+ const stats = {
1194
+ size: this.bytesWritten,
1195
+ created: this.lastRotation,
1196
+ modified: /* @__PURE__ */ new Date()
1197
+ };
1198
+ try {
1199
+ return shouldRotate(this.currentFilePath, stats);
1200
+ } catch (error) {
1201
+ require_internal_log.internalError("FileTransport rotation.shouldRotate threw", error);
1202
+ }
1203
+ }
1204
+ return false;
1057
1205
  }
1058
1206
  parseInterval(interval) {
1059
- const match = (/* @__PURE__ */ new RegExp(/(\d+)([hdwmy])/i)).exec(interval);
1207
+ const match = /^(\d{1,6})([hdwmy])$/i.exec(interval.trim());
1060
1208
  if (!match) return 1440 * 60 * 1e3;
1061
1209
  const value = Number.parseInt(match[1] || "1", 10);
1062
1210
  switch ((match[2] || "h").toLowerCase()) {
@@ -1068,40 +1216,54 @@ var FileTransport = class {
1068
1216
  default: return value * 60 * 60 * 1e3;
1069
1217
  }
1070
1218
  }
1071
- async rotate() {
1072
- var _this$config$rotation2, _this$config$rotation3;
1073
- await this.flush();
1074
- if (this.writeStream) {
1075
- await new Promise((resolve) => {
1076
- this.writeStream.end(() => resolve());
1077
- });
1078
- this.writeStream = void 0;
1219
+ async rotateFile() {
1220
+ const now = /* @__PURE__ */ new Date();
1221
+ let archived;
1222
+ if (this.timestampedActive) {
1223
+ archived = this.currentFilePath;
1224
+ this.currentFilePath = this.uniquePath(this.timestamp(now));
1225
+ } else {
1226
+ archived = this.uniquePath(this.period ?? this.timestamp(now));
1227
+ try {
1228
+ await rename(this.currentFilePath, archived);
1229
+ } catch (error) {
1230
+ if (error.code !== "ENOENT") {
1231
+ require_internal_log.internalError(`FileTransport could not rotate ${this.currentFilePath}`, error);
1232
+ this.bytesWritten = 0;
1233
+ return;
1234
+ }
1235
+ archived = void 0;
1236
+ }
1079
1237
  }
1080
- const oldPath = this.currentFilePath;
1081
- this.currentFilePath = this.generateFilePath();
1082
- this.lastRotation = /* @__PURE__ */ new Date();
1083
- if ((_this$config$rotation2 = this.config.rotation) === null || _this$config$rotation2 === void 0 ? void 0 : _this$config$rotation2.compress) await this.compressFile(oldPath);
1084
- if ((_this$config$rotation3 = this.config.rotation) === null || _this$config$rotation3 === void 0 ? void 0 : _this$config$rotation3.maxFiles) await this.cleanupOldFiles();
1238
+ this.bytesWritten = 0;
1239
+ this.lastRotation = now;
1240
+ if (this.config.datePattern) this.period = formatDatePattern(now, this.config.datePattern);
1241
+ if (archived === void 0 || !node_fs.existsSync(archived)) return;
1242
+ const file = archived;
1243
+ this.housekeeping = this.housekeeping.then(async () => {
1244
+ if (this.compress) await this.compressFile(file);
1245
+ if (this.maxFiles) await this.cleanupOldFiles();
1246
+ });
1247
+ }
1248
+ timestamp(date) {
1249
+ return date.toISOString().replace(/[:.]/g, "-").slice(0, 23);
1250
+ }
1251
+ /** `<base>-<stamp><ext>`, with `.1`, `.2`… added if that name (or its .gz) is taken. */
1252
+ uniquePath(stamp) {
1253
+ const taken = (p) => node_fs.existsSync(p) || node_fs.existsSync(`${p}.gz`);
1254
+ let candidate = node_path.join(this.dir, `${this.base}-${stamp}${this.ext}`);
1255
+ let n = 0;
1256
+ while (taken(candidate) || candidate === this.currentFilePath) {
1257
+ n += 1;
1258
+ candidate = node_path.join(this.dir, `${this.base}-${stamp}.${n}${this.ext}`);
1259
+ }
1260
+ return candidate;
1085
1261
  }
1086
1262
  resolveDir() {
1087
1263
  if (this.config.dirname) return this.config.dirname;
1088
1264
  const fromFilename = node_path.dirname(this.config.filename);
1089
1265
  return fromFilename === "." ? process.cwd() : fromFilename;
1090
1266
  }
1091
- generateFilePath() {
1092
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
1093
- const dir = this.resolveDir();
1094
- const ext = node_path.extname(this.config.filename);
1095
- const base = node_path.basename(this.config.filename, ext);
1096
- if (this.config.rotation) return node_path.join(dir, `${base}-${timestamp}${ext}`);
1097
- return node_path.join(dir, `${base}${ext}`);
1098
- }
1099
- async ensureDirectoryExists() {
1100
- const dir = this.resolveDir();
1101
- try {
1102
- await mkdir(dir, { recursive: true });
1103
- } catch {}
1104
- }
1105
1267
  /**
1106
1268
  * Gzip the rotated file to `<file>.gz` and remove the original. Streams the
1107
1269
  * data so large log files don't have to be buffered in memory. Best-effort:
@@ -1114,39 +1276,48 @@ var FileTransport = class {
1114
1276
  await (0, node_stream_promises.pipeline)(node_fs.createReadStream(filePath), (0, node_zlib.createGzip)(), node_fs.createWriteStream(gzPath));
1115
1277
  await unlink(filePath);
1116
1278
  } catch (error) {
1117
- internalError("Failed to compress rotated log file", error);
1279
+ require_internal_log.internalError("Failed to compress rotated log file", error);
1118
1280
  try {
1119
1281
  await unlink(gzPath);
1120
1282
  } catch {}
1121
1283
  }
1122
1284
  }
1285
+ /**
1286
+ * True for this transport's rotated files only: `<base>-<stamp><ext>` where the
1287
+ * stamp is digits and separators, optionally `.gz`. Another transport's
1288
+ * `app-error.log` must not count as one of `app.log`'s rotations.
1289
+ */
1290
+ isOwnRotatedFile(file) {
1291
+ const name = file.endsWith(".gz") ? file.slice(0, -3) : file;
1292
+ if (!name.startsWith(`${this.base}-`) || !name.endsWith(this.ext)) return false;
1293
+ const stamp = name.slice(this.base.length + 1, name.length - this.ext.length);
1294
+ return /^\d[\d\-_.T]*$/.test(stamp);
1295
+ }
1123
1296
  async cleanupOldFiles() {
1124
- var _this$config$rotation4;
1125
- if (!((_this$config$rotation4 = this.config.rotation) === null || _this$config$rotation4 === void 0 ? void 0 : _this$config$rotation4.maxFiles)) return;
1297
+ if (!this.maxFiles) return;
1126
1298
  try {
1127
- const dir = this.resolveDir();
1128
- const files = await readdir(dir);
1129
- const ext = node_path.extname(this.config.filename);
1130
- const base = node_path.basename(this.config.filename, ext);
1131
- const isOwnRotatedFile = (file) => file.startsWith(`${base}-`) && (file.endsWith(ext) || file.endsWith(`${ext}.gz`));
1132
- const logFiles = files.filter(isOwnRotatedFile).map(async (file) => {
1133
- const filePath = node_path.join(dir, file);
1299
+ const files = await readdir(this.dir);
1300
+ const active = node_path.basename(this.currentFilePath);
1301
+ const newestFirst = (await Promise.all(files.filter((file) => file !== active && this.isOwnRotatedFile(file)).map(async (file) => {
1302
+ const filePath = node_path.join(this.dir, file);
1134
1303
  return {
1135
1304
  path: filePath,
1136
- mtime: (await stat(filePath)).mtime
1305
+ mtime: (await stat(filePath)).mtimeMs
1137
1306
  };
1138
- });
1139
- const filesToDelete = (await Promise.all(logFiles)).sort((a, b) => b.mtime.getTime() - a.mtime.getTime()).slice(this.config.rotation.maxFiles);
1140
- for (const file of filesToDelete) await unlink(file.path);
1307
+ }))).sort((a, b) => b.mtime - a.mtime || b.path.localeCompare(a.path));
1308
+ for (const file of newestFirst.slice(this.maxFiles)) await unlink(file.path);
1141
1309
  } catch (error) {
1142
- internalError("Failed to cleanup old log files", error);
1310
+ require_internal_log.internalError("Failed to cleanup old log files", error);
1143
1311
  }
1144
1312
  }
1145
1313
  async close() {
1314
+ if (this.batchTimer) {
1315
+ clearTimeout(this.batchTimer);
1316
+ this.batchTimer = void 0;
1317
+ }
1146
1318
  await this.flush();
1147
- if (this.writeStream) return new Promise((resolve) => {
1148
- this.writeStream.end(() => resolve());
1149
- });
1319
+ await this.housekeeping;
1320
+ this.reportDropped(0);
1150
1321
  }
1151
1322
  };
1152
1323
 
@@ -1171,10 +1342,9 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
1171
1342
  try {
1172
1343
  if (!this.gaConfig.measurementId) throw new Error("Google Analytics Measurement ID is required");
1173
1344
  if (!this.gaConfig.apiSecret) throw new Error("Google Analytics API Secret is required");
1174
- await this.testConnection();
1175
1345
  this.isReady = true;
1176
1346
  } catch (error) {
1177
- internalError("Google Analytics transport initialization failed", error);
1347
+ require_internal_log.internalError("Google Analytics transport initialization failed", error);
1178
1348
  throw error;
1179
1349
  }
1180
1350
  }
@@ -1240,10 +1410,10 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
1240
1410
  }
1241
1411
  async sendEvents(events) {
1242
1412
  const chunks = this.chunkArray(events, 25);
1243
- for (const chunk of chunks) {
1413
+ for (const chunk$1 of chunks) {
1244
1414
  const payload = {
1245
1415
  client_id: this.gaConfig.clientId || this.generateClientId(),
1246
- events: chunk
1416
+ events: chunk$1
1247
1417
  };
1248
1418
  await this.makeRequest(payload);
1249
1419
  }
@@ -1258,7 +1428,7 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
1258
1428
  const response = await fetch(`${url}?${params}`, {
1259
1429
  method: "POST",
1260
1430
  headers: { "Content-Type": "application/json" },
1261
- body: JSON.stringify(payload)
1431
+ body: require_safe_stringify.stringifyForLog(payload)
1262
1432
  });
1263
1433
  if (!response.ok) {
1264
1434
  const errorText = await response.text();
@@ -1266,28 +1436,12 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
1266
1436
  }
1267
1437
  if (debug) {
1268
1438
  const result = await response.json();
1269
- internalLog(`GA4 Debug Response: ${JSON.stringify(result)}`);
1439
+ require_internal_log.internalLog(`GA4 Debug Response: ${JSON.stringify(result)}`);
1270
1440
  }
1271
1441
  } catch (error) {
1272
1442
  throw new Error(`Failed to send data to Google Analytics: ${error.message}`, { cause: error });
1273
1443
  }
1274
1444
  }
1275
- async testConnection() {
1276
- const testEvent = {
1277
- name: "logitron_test",
1278
- params: {
1279
- event_category: "Test",
1280
- event_label: "Connection Test",
1281
- test: true,
1282
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1283
- }
1284
- };
1285
- const payload = {
1286
- client_id: this.gaConfig.clientId || this.generateClientId(),
1287
- events: [testEvent]
1288
- };
1289
- await this.makeRequest(payload, true);
1290
- }
1291
1445
  generateClientId() {
1292
1446
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
1293
1447
  const r = Math.random() * 16 | 0;
@@ -1398,10 +1552,9 @@ var MixpanelTransport = class extends AnalyticsTransport {
1398
1552
  async initialize() {
1399
1553
  try {
1400
1554
  if (!this.mixpanelConfig.token) throw new Error("Mixpanel token is required");
1401
- await this.testConnection();
1402
1555
  this.isReady = true;
1403
1556
  } catch (error) {
1404
- internalError("Mixpanel transport initialization failed", error);
1557
+ require_internal_log.internalError("Mixpanel transport initialization failed", error);
1405
1558
  throw error;
1406
1559
  }
1407
1560
  }
@@ -1457,7 +1610,7 @@ var MixpanelTransport = class extends AnalyticsTransport {
1457
1610
  "Content-Type": "application/json",
1458
1611
  Accept: "text/plain"
1459
1612
  },
1460
- body: JSON.stringify(data)
1613
+ body: require_safe_stringify.stringifyForLog(data)
1461
1614
  });
1462
1615
  if (!response.ok) throw new Error(`Mixpanel API error: ${response.status} ${response.statusText}`);
1463
1616
  const result = await response.text();
@@ -1466,18 +1619,6 @@ var MixpanelTransport = class extends AnalyticsTransport {
1466
1619
  throw new Error(`Failed to send data to Mixpanel: ${error.message}`, { cause: error });
1467
1620
  }
1468
1621
  }
1469
- async testConnection() {
1470
- const testEvent = {
1471
- event: "logitron_test",
1472
- properties: {
1473
- token: this.mixpanelConfig.token,
1474
- distinct_id: "test_user",
1475
- test: true,
1476
- time: Date.now()
1477
- }
1478
- };
1479
- await this.makeRequest("/track", [testEvent]);
1480
- }
1481
1622
  generateInsertId(entry) {
1482
1623
  return `${entry.timestamp.getTime()}_${this.simpleHash(entry.message + (entry.traceId || ""))}`;
1483
1624
  }
@@ -1524,6 +1665,17 @@ var MixpanelTransport = class extends AnalyticsTransport {
1524
1665
  }
1525
1666
  };
1526
1667
 
1668
+ //#endregion
1669
+ //#region src/version.ts
1670
+ /**
1671
+ * The published version of this package.
1672
+ *
1673
+ * Kept in sync with `version` in package.json by the release process. Importing
1674
+ * from here (instead of reading package.json at runtime) keeps the built `dist`
1675
+ * free of a JSON import that bundlers and consumers would have to resolve.
1676
+ */
1677
+ const LOGIXIA_VERSION = "1.12.0";
1678
+
1527
1679
  //#endregion
1528
1680
  //#region src/transports/segment.transport.ts
1529
1681
  /**
@@ -1547,10 +1699,9 @@ var SegmentTransport = class extends AnalyticsTransport {
1547
1699
  async initialize() {
1548
1700
  try {
1549
1701
  if (!this.segmentConfig.writeKey) throw new Error("Segment Write Key is required");
1550
- await this.testConnection();
1551
1702
  this.isReady = true;
1552
1703
  } catch (error) {
1553
- internalError("Segment transport initialization failed", error);
1704
+ require_internal_log.internalError("Segment transport initialization failed", error);
1554
1705
  throw error;
1555
1706
  }
1556
1707
  }
@@ -1588,7 +1739,7 @@ var SegmentTransport = class extends AnalyticsTransport {
1588
1739
  context: {
1589
1740
  library: {
1590
1741
  name: "logixia",
1591
- version: "1.0.0"
1742
+ version: LOGIXIA_VERSION
1592
1743
  },
1593
1744
  app: {
1594
1745
  name: entry.appName || "unknown",
@@ -1608,8 +1759,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1608
1759
  }
1609
1760
  async batchTrack(events) {
1610
1761
  const chunks = this.chunkArray(events, this.segmentConfig.maxBatchSize || 100);
1611
- for (const chunk of chunks) {
1612
- const payload = { batch: chunk };
1762
+ for (const chunk$1 of chunks) {
1763
+ const payload = { batch: chunk$1 };
1613
1764
  await this.makeRequest("/v1/batch", payload);
1614
1765
  }
1615
1766
  }
@@ -1622,9 +1773,9 @@ var SegmentTransport = class extends AnalyticsTransport {
1622
1773
  headers: {
1623
1774
  "Content-Type": "application/json",
1624
1775
  Authorization: `Basic ${auth}`,
1625
- "User-Agent": "logixia/1.0.0"
1776
+ "User-Agent": `logixia/${LOGIXIA_VERSION}`
1626
1777
  },
1627
- body: JSON.stringify(data)
1778
+ body: require_safe_stringify.stringifyForLog(data)
1628
1779
  });
1629
1780
  if (!response.ok) {
1630
1781
  const errorText = await response.text();
@@ -1634,24 +1785,6 @@ var SegmentTransport = class extends AnalyticsTransport {
1634
1785
  throw new Error(`Failed to send data to Segment: ${error.message}`, { cause: error });
1635
1786
  }
1636
1787
  }
1637
- async testConnection() {
1638
- const testEvent = {
1639
- type: "track",
1640
- event: "Logixia Test",
1641
- userId: "test-user",
1642
- anonymousId: this.generateAnonymousId(),
1643
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1644
- properties: {
1645
- test: true,
1646
- source: "logixia"
1647
- },
1648
- context: { library: {
1649
- name: "logixia",
1650
- version: "1.0.0"
1651
- } }
1652
- };
1653
- await this.makeRequest("/v1/track", testEvent);
1654
- }
1655
1788
  extractUserId(entry) {
1656
1789
  if (entry.data) {
1657
1790
  const d = entry.data;
@@ -1681,8 +1814,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1681
1814
  ...user.traits
1682
1815
  },
1683
1816
  context: { library: {
1684
- name: "logitron",
1685
- version: "1.0.0"
1817
+ name: "logixia",
1818
+ version: LOGIXIA_VERSION
1686
1819
  } }
1687
1820
  };
1688
1821
  await this.makeRequest("/v1/identify", payload);
@@ -1700,8 +1833,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1700
1833
  },
1701
1834
  context: {
1702
1835
  library: {
1703
- name: "logitron",
1704
- version: "1.0.0"
1836
+ name: "logixia",
1837
+ version: LOGIXIA_VERSION
1705
1838
  },
1706
1839
  page: {
1707
1840
  url: properties.url || `/${name.toLowerCase().replace(/\s+/g, "-")}`,
@@ -1720,8 +1853,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1720
1853
  properties: { ...properties },
1721
1854
  context: {
1722
1855
  library: {
1723
- name: "logitron",
1724
- version: "1.0.0"
1856
+ name: "logixia",
1857
+ version: LOGIXIA_VERSION
1725
1858
  },
1726
1859
  screen: { name }
1727
1860
  }
@@ -1737,8 +1870,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1737
1870
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1738
1871
  traits,
1739
1872
  context: { library: {
1740
- name: "logitron",
1741
- version: "1.0.0"
1873
+ name: "logixia",
1874
+ version: LOGIXIA_VERSION
1742
1875
  } }
1743
1876
  };
1744
1877
  await this.makeRequest("/v1/group", payload);
@@ -1750,8 +1883,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1750
1883
  previousId,
1751
1884
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1752
1885
  context: { library: {
1753
- name: "logitron",
1754
- version: "1.0.0"
1886
+ name: "logixia",
1887
+ version: LOGIXIA_VERSION
1755
1888
  } }
1756
1889
  };
1757
1890
  await this.makeRequest("/v1/alias", payload);
@@ -1765,8 +1898,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1765
1898
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1766
1899
  properties,
1767
1900
  context: { library: {
1768
- name: "logitron",
1769
- version: "1.0.0"
1901
+ name: "logixia",
1902
+ version: LOGIXIA_VERSION
1770
1903
  } }
1771
1904
  };
1772
1905
  await this.makeRequest("/v1/track", payload);
@@ -1784,8 +1917,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1784
1917
  products
1785
1918
  },
1786
1919
  context: { library: {
1787
- name: "logitron",
1788
- version: "1.0.0"
1920
+ name: "logixia",
1921
+ version: LOGIXIA_VERSION
1789
1922
  } }
1790
1923
  };
1791
1924
  await this.makeRequest("/v1/track", payload);
@@ -1798,8 +1931,8 @@ var SegmentTransport = class extends AnalyticsTransport {
1798
1931
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1799
1932
  properties: product,
1800
1933
  context: { library: {
1801
- name: "logitron",
1802
- version: "1.0.0"
1934
+ name: "logixia",
1935
+ version: LOGIXIA_VERSION
1803
1936
  } }
1804
1937
  };
1805
1938
  await this.makeRequest("/v1/track", payload);
@@ -1818,7 +1951,7 @@ var TransportManager = class extends node_events.EventEmitter {
1818
1951
  this.metrics = /* @__PURE__ */ new Map();
1819
1952
  this.isShuttingDown = false;
1820
1953
  this.transportLevelPreferences = /* @__PURE__ */ new Map();
1821
- this.promptForLevels = false;
1954
+ this.warnedPromptDeprecated = false;
1822
1955
  this.setupTransports(config);
1823
1956
  }
1824
1957
  setupTransports(config) {
@@ -1918,17 +2051,24 @@ var TransportManager = class extends node_events.EventEmitter {
1918
2051
  if (entry.context !== void 0) transportEntry.context = entry.context;
1919
2052
  if (entry.traceId !== void 0) transportEntry.traceId = entry.traceId;
1920
2053
  if (entry.environment !== void 0) transportEntry.environment = entry.environment;
1921
- if (this.promptForLevels) return this._writeAsyncWithPrompt(transportEntry);
1922
2054
  let pending;
2055
+ let syncFailure;
1923
2056
  for (const [id, transport] of this.transports) {
1924
2057
  if (!this.shouldTransportHandle(transport, transportEntry.level, id)) continue;
1925
2058
  if (transport.filter && !transport.filter(transportEntry)) continue;
1926
- const result = this.writeToTransport(id, transport, transportEntry);
2059
+ let result;
2060
+ try {
2061
+ result = this.writeToTransport(id, transport, transportEntry);
2062
+ } catch (error) {
2063
+ syncFailure ??= { error };
2064
+ continue;
2065
+ }
1927
2066
  if (result && typeof result.then === "function") {
1928
2067
  if (pending === void 0) pending = [];
1929
2068
  pending.push(result);
1930
2069
  }
1931
2070
  }
2071
+ if (syncFailure) throw syncFailure.error;
1932
2072
  if (pending === void 0) return;
1933
2073
  return Promise.allSettled(pending).then((results) => {
1934
2074
  const failures = results.filter((r) => r.status === "rejected");
@@ -1938,22 +2078,6 @@ var TransportManager = class extends node_events.EventEmitter {
1938
2078
  }
1939
2079
  });
1940
2080
  }
1941
- /** Slow path used only when interactive level-prompting is enabled. */
1942
- async _writeAsyncWithPrompt(transportEntry) {
1943
- const writePromises = [];
1944
- for (const [id, transport] of this.transports) {
1945
- if (!this.transportLevelPreferences.has(id)) await this.configureTransportLevels(id);
1946
- if (!this.shouldTransportHandle(transport, transportEntry.level, id)) continue;
1947
- if (transport.filter && !transport.filter(transportEntry)) continue;
1948
- const result = this.writeToTransport(id, transport, transportEntry);
1949
- if (result && typeof result.then === "function") writePromises.push(result);
1950
- }
1951
- const failures = (await Promise.allSettled(writePromises)).filter((r) => r.status === "rejected");
1952
- if (failures.length > 0) {
1953
- const errors = failures.map((f) => f.reason);
1954
- this.emit("error", /* @__PURE__ */ new Error(`Transport write failures: ${errors.join(", ")}`), "multiple");
1955
- }
1956
- }
1957
2081
  writeToTransport(id, transport, entry) {
1958
2082
  const startTime = Date.now();
1959
2083
  const metrics = this.metrics.get(id);
@@ -2140,63 +2264,29 @@ var TransportManager = class extends node_events.EventEmitter {
2140
2264
  details
2141
2265
  };
2142
2266
  }
2267
+ /** @deprecated Library code never prompts on stdin. Use setTransportLevels(). No-op. */
2143
2268
  enableLevelPrompting() {
2144
- this.promptForLevels = true;
2145
- internalLog("Transport level prompting enabled");
2269
+ this.warnPromptingRemoved();
2146
2270
  }
2271
+ /** @deprecated Library code never prompts on stdin. No-op. */
2147
2272
  disableLevelPrompting() {
2148
- this.promptForLevels = false;
2149
- internalLog("Transport level prompting disabled");
2150
- }
2151
- async promptUserForTransportLevels(transportId) {
2152
- const availableLevels = [
2153
- "error",
2154
- "warn",
2155
- "info",
2156
- "debug",
2157
- "trace",
2158
- "verbose"
2159
- ];
2160
- return new Promise((resolve) => {
2161
- const rl = node_readline.createInterface({
2162
- input: process.stdin,
2163
- output: process.stdout
2164
- });
2165
- process.stdout.write(`\nConfigure log levels for transport '${transportId}'\n`);
2166
- process.stdout.write(`Available levels: ${availableLevels.join(", ")}\n`);
2167
- process.stdout.write("Enter levels separated by commas (e.g., error,warn,info) or \"all\" for all levels:\n");
2168
- rl.question("> ", (answer) => {
2169
- rl.close();
2170
- if (answer.toLowerCase().trim() === "all") resolve(availableLevels);
2171
- else {
2172
- const selectedLevels = answer.split(",").map((level) => level.trim().toLowerCase()).filter((level) => availableLevels.includes(level));
2173
- if (selectedLevels.length === 0) {
2174
- internalWarn("No valid levels selected, using all levels");
2175
- resolve(availableLevels);
2176
- } else {
2177
- internalLog(`Selected levels for ${transportId}: ${selectedLevels.join(", ")}`);
2178
- resolve(selectedLevels);
2179
- }
2180
- }
2181
- });
2182
- });
2273
+ this.warnPromptingRemoved();
2183
2274
  }
2184
- async configureTransportLevels(transportId) {
2185
- if (this.promptForLevels && !this.transportLevelPreferences.has(transportId)) {
2186
- const selectedLevels = await this.promptUserForTransportLevels(transportId);
2187
- this.transportLevelPreferences.set(transportId, selectedLevels);
2188
- }
2275
+ warnPromptingRemoved() {
2276
+ if (this.warnedPromptDeprecated) return;
2277
+ this.warnedPromptDeprecated = true;
2278
+ require_internal_log.internalWarn("Transport level prompting was removed: logixia never reads stdin. Use setTransportLevels(id, levels) instead.");
2189
2279
  }
2190
2280
  setTransportLevels(transportId, levels) {
2191
2281
  this.transportLevelPreferences.set(transportId, levels);
2192
- internalLog(`Transport '${transportId}' configured for levels: ${levels.join(", ")}`);
2282
+ require_internal_log.internalLog(`Transport '${transportId}' configured for levels: ${levels.join(", ")}`);
2193
2283
  }
2194
2284
  getTransportLevels(transportId) {
2195
2285
  return this.transportLevelPreferences.get(transportId);
2196
2286
  }
2197
2287
  clearTransportLevelPreferences() {
2198
2288
  this.transportLevelPreferences.clear();
2199
- internalLog("Transport level preferences cleared");
2289
+ require_internal_log.internalLog("Transport level preferences cleared");
2200
2290
  }
2201
2291
  };
2202
2292
 
@@ -2255,34 +2345,4 @@ Object.defineProperty(exports, 'TransportManager', {
2255
2345
  return TransportManager;
2256
2346
  }
2257
2347
  });
2258
- Object.defineProperty(exports, '__toESM', {
2259
- enumerable: true,
2260
- get: function () {
2261
- return __toESM;
2262
- }
2263
- });
2264
- Object.defineProperty(exports, 'internalError', {
2265
- enumerable: true,
2266
- get: function () {
2267
- return internalError;
2268
- }
2269
- });
2270
- Object.defineProperty(exports, 'internalLog', {
2271
- enumerable: true,
2272
- get: function () {
2273
- return internalLog;
2274
- }
2275
- });
2276
- Object.defineProperty(exports, 'internalWarn', {
2277
- enumerable: true,
2278
- get: function () {
2279
- return internalWarn;
2280
- }
2281
- });
2282
- Object.defineProperty(exports, 'safeToString', {
2283
- enumerable: true,
2284
- get: function () {
2285
- return safeToString;
2286
- }
2287
- });
2288
- //# sourceMappingURL=transport.manager-ciG4r5uz.js.map
2348
+ //# sourceMappingURL=transport.manager-BivyUTtU.js.map