logixia 1.12.6 → 1.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +19 -4
  2. package/dist/{async-context-CwBNMm2E.mjs → async-context-BzJrbfQs.mjs} +8 -4
  3. package/dist/async-context-BzJrbfQs.mjs.map +1 -0
  4. package/dist/{async-context-CSHWoHMB.js → async-context-DyQQYq3I.js} +8 -4
  5. package/dist/async-context-DyQQYq3I.js.map +1 -0
  6. package/dist/{azure-monitor.transport-CG7ClkYj.d.ts → azure-monitor.transport-BDFO4F-i.d.ts} +2 -2
  7. package/dist/{azure-monitor.transport-CG7ClkYj.d.ts.map → azure-monitor.transport-BDFO4F-i.d.ts.map} +1 -1
  8. package/dist/{azure-monitor.transport-Cm74kXZS.d.mts → azure-monitor.transport-D5wQEH04.d.mts} +2 -2
  9. package/dist/{azure-monitor.transport-Cm74kXZS.d.mts.map → azure-monitor.transport-D5wQEH04.d.mts.map} +1 -1
  10. package/dist/azure.d.mts +2 -2
  11. package/dist/azure.d.ts +2 -2
  12. package/dist/cli/index.js +98 -52
  13. package/dist/cloudwatch.d.mts +2 -2
  14. package/dist/cloudwatch.d.ts +2 -2
  15. package/dist/cloudwatch.js +2 -1
  16. package/dist/cloudwatch.mjs +2 -1
  17. package/dist/{cloudwatch.transport-CGHY4buE.d.mts → cloudwatch.transport-B00st6Xv.d.ts} +9 -2
  18. package/dist/cloudwatch.transport-B00st6Xv.d.ts.map +1 -0
  19. package/dist/{cloudwatch.transport-pNBNWJLt.js → cloudwatch.transport-BO1ahxw-.js} +26 -19
  20. package/dist/cloudwatch.transport-BO1ahxw-.js.map +1 -0
  21. package/dist/{cloudwatch.transport-DK_8cKd3.d.ts → cloudwatch.transport-Blc7Gtxt.d.mts} +9 -2
  22. package/dist/cloudwatch.transport-Blc7Gtxt.d.mts.map +1 -0
  23. package/dist/{cloudwatch.transport-yBwm4OeO.mjs → cloudwatch.transport-DZ6ies7g.mjs} +26 -19
  24. package/dist/cloudwatch.transport-DZ6ies7g.mjs.map +1 -0
  25. package/dist/{coerce.utils-BN980Y9V.js → coerce.utils-B-LilukN.js} +1 -1
  26. package/dist/{coerce.utils-BN980Y9V.js.map → coerce.utils-B-LilukN.js.map} +1 -1
  27. package/dist/{coerce.utils-Dxv4_ikC.mjs → coerce.utils-Cu6IUTgX.mjs} +1 -1
  28. package/dist/{coerce.utils-Dxv4_ikC.mjs.map → coerce.utils-Cu6IUTgX.mjs.map} +1 -1
  29. package/dist/correlation.d.mts +31 -6
  30. package/dist/correlation.d.mts.map +1 -1
  31. package/dist/correlation.d.ts +31 -6
  32. package/dist/correlation.d.ts.map +1 -1
  33. package/dist/correlation.js +45 -20
  34. package/dist/correlation.js.map +1 -1
  35. package/dist/correlation.mjs +45 -20
  36. package/dist/correlation.mjs.map +1 -1
  37. package/dist/entry-fields-BJkuPnIg.mjs +30 -0
  38. package/dist/entry-fields-BJkuPnIg.mjs.map +1 -0
  39. package/dist/entry-fields-BvOLFlJ4.js +36 -0
  40. package/dist/entry-fields-BvOLFlJ4.js.map +1 -0
  41. package/dist/gcp.d.mts +2 -2
  42. package/dist/gcp.d.ts +2 -2
  43. package/dist/gcp.js +2 -1
  44. package/dist/gcp.mjs +2 -1
  45. package/dist/{gcp.transport-SHdtc1n0.js → gcp.transport-CHV8MGgz.js} +5 -5
  46. package/dist/gcp.transport-CHV8MGgz.js.map +1 -0
  47. package/dist/{gcp.transport-BJuNRFe_.mjs → gcp.transport-DBX5TPIy.mjs} +5 -5
  48. package/dist/gcp.transport-DBX5TPIy.mjs.map +1 -0
  49. package/dist/{gcp.transport-BOhkVKlI.d.ts → gcp.transport-m4fVw0b1.d.ts} +2 -2
  50. package/dist/gcp.transport-m4fVw0b1.d.ts.map +1 -0
  51. package/dist/{gcp.transport-Bi6CUqZR.d.mts → gcp.transport-zzNyIoih.d.mts} +2 -2
  52. package/dist/gcp.transport-zzNyIoih.d.mts.map +1 -0
  53. package/dist/{index-BPKwwERo.d.mts → index-BPwEisy4.d.mts} +2 -2
  54. package/dist/{index-BPKwwERo.d.mts.map → index-BPwEisy4.d.mts.map} +1 -1
  55. package/dist/{index-C1X-TE5I.d.ts → index-nZEeCr3X.d.ts} +2 -2
  56. package/dist/{index-C1X-TE5I.d.ts.map → index-nZEeCr3X.d.ts.map} +1 -1
  57. package/dist/index.d.mts +63 -98
  58. package/dist/index.d.mts.map +1 -1
  59. package/dist/index.d.ts +63 -98
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +167 -443
  62. package/dist/index.js.map +1 -1
  63. package/dist/index.mjs +98 -377
  64. package/dist/index.mjs.map +1 -1
  65. package/dist/middleware.d.mts +11 -4
  66. package/dist/middleware.d.mts.map +1 -1
  67. package/dist/middleware.d.ts +11 -4
  68. package/dist/middleware.d.ts.map +1 -1
  69. package/dist/middleware.js +148 -24
  70. package/dist/middleware.js.map +1 -1
  71. package/dist/middleware.mjs +148 -24
  72. package/dist/middleware.mjs.map +1 -1
  73. package/dist/nest.d.mts +4 -4
  74. package/dist/nest.d.ts +4 -4
  75. package/dist/nest.js +20 -11
  76. package/dist/nest.mjs +7 -5
  77. package/dist/{logitron-logger.module-BsDZR5BH.js → nestjs-extras-DnJ6vrIC.js} +562 -444
  78. package/dist/nestjs-extras-DnJ6vrIC.js.map +1 -0
  79. package/dist/{logitron-logger.module-Bs3XYZgF.mjs → nestjs-extras-ImbuDhs1.mjs} +505 -349
  80. package/dist/nestjs-extras-ImbuDhs1.mjs.map +1 -0
  81. package/dist/{logitron-logger.module-zDKVd_o0.d.mts → nestjs-extras-OkC796py.d.mts} +113 -6
  82. package/dist/nestjs-extras-OkC796py.d.mts.map +1 -0
  83. package/dist/{logitron-logger.module-CxT1F6Cm.d.ts → nestjs-extras-mKrHE9S6.d.ts} +113 -6
  84. package/dist/nestjs-extras-mKrHE9S6.d.ts.map +1 -0
  85. package/dist/{search-njTI2hdo.js → search-B0MH1SJJ.js} +23 -10
  86. package/dist/search-B0MH1SJJ.js.map +1 -0
  87. package/dist/{search-DujhR94O.d.ts → search-BfBzDIT9.d.mts} +2 -3
  88. package/dist/search-BfBzDIT9.d.mts.map +1 -0
  89. package/dist/{search-CQOo-Iq6.d.mts → search-CHUZPl0h.d.ts} +2 -3
  90. package/dist/search-CHUZPl0h.d.ts.map +1 -0
  91. package/dist/{search-CFQRISUv.mjs → search-DjXSn6v5.mjs} +23 -10
  92. package/dist/search-DjXSn6v5.mjs.map +1 -0
  93. package/dist/search.d.mts +3 -3
  94. package/dist/search.d.ts +3 -3
  95. package/dist/search.js +2 -2
  96. package/dist/search.mjs +2 -2
  97. package/dist/testing.d.mts +2 -2
  98. package/dist/testing.d.ts +2 -2
  99. package/dist/trace.utils-CvXgaiQT.mjs +278 -0
  100. package/dist/trace.utils-CvXgaiQT.mjs.map +1 -0
  101. package/dist/trace.utils-DpBOLDtq.js +358 -0
  102. package/dist/trace.utils-DpBOLDtq.js.map +1 -0
  103. package/dist/{transport.manager-D6KL3t9V.mjs → transport.manager-CpdVHEGP.mjs} +365 -86
  104. package/dist/transport.manager-CpdVHEGP.mjs.map +1 -0
  105. package/dist/{transport.manager-BivyUTtU.js → transport.manager-Crau3Rle.js} +370 -85
  106. package/dist/transport.manager-Crau3Rle.js.map +1 -0
  107. package/dist/{transport.types-B5vcyv2k.d.mts → transport.types-CtNr3D7S.d.mts} +19 -2
  108. package/dist/transport.types-CtNr3D7S.d.mts.map +1 -0
  109. package/dist/{transport.types-BuY84TwO.d.ts → transport.types-DqVLZTyf.d.ts} +19 -2
  110. package/dist/transport.types-DqVLZTyf.d.ts.map +1 -0
  111. package/dist/transports.d.mts +38 -13
  112. package/dist/transports.d.mts.map +1 -1
  113. package/dist/transports.d.ts +38 -13
  114. package/dist/transports.d.ts.map +1 -1
  115. package/dist/transports.js +6 -5
  116. package/dist/transports.mjs +6 -5
  117. package/dist/worker-entry.js +307 -55
  118. package/dist/worker.d.mts +2 -2
  119. package/dist/worker.d.ts +2 -2
  120. package/dist/worker.js +1 -1
  121. package/dist/worker.mjs +1 -1
  122. package/dist/{worker.transport-BP8Q28Z2.d.mts → worker.transport-Bgv9heDI.d.mts} +2 -2
  123. package/dist/{worker.transport-BP8Q28Z2.d.mts.map → worker.transport-Bgv9heDI.d.mts.map} +1 -1
  124. package/dist/{worker.transport-mKXgNK0L.d.ts → worker.transport-Bj-i0P9V.d.ts} +2 -2
  125. package/dist/{worker.transport-mKXgNK0L.d.ts.map → worker.transport-Bj-i0P9V.d.ts.map} +1 -1
  126. package/dist/{worker.transport-CfH6z37Z.js → worker.transport-JP8YHBuU.js} +1 -1
  127. package/dist/{worker.transport-CfH6z37Z.js.map → worker.transport-JP8YHBuU.js.map} +1 -1
  128. package/dist/{worker.transport-BhMyvqQ2.mjs → worker.transport-otwhJ_Et.mjs} +1 -1
  129. package/dist/{worker.transport-BhMyvqQ2.mjs.map → worker.transport-otwhJ_Et.mjs.map} +1 -1
  130. package/package.json +10 -6
  131. package/dist/.tsbuildinfo +0 -1
  132. package/dist/async-context-CSHWoHMB.js.map +0 -1
  133. package/dist/async-context-CwBNMm2E.mjs.map +0 -1
  134. package/dist/cloudwatch.transport-CGHY4buE.d.mts.map +0 -1
  135. package/dist/cloudwatch.transport-DK_8cKd3.d.ts.map +0 -1
  136. package/dist/cloudwatch.transport-pNBNWJLt.js.map +0 -1
  137. package/dist/cloudwatch.transport-yBwm4OeO.mjs.map +0 -1
  138. package/dist/gcp.transport-BJuNRFe_.mjs.map +0 -1
  139. package/dist/gcp.transport-BOhkVKlI.d.ts.map +0 -1
  140. package/dist/gcp.transport-Bi6CUqZR.d.mts.map +0 -1
  141. package/dist/gcp.transport-SHdtc1n0.js.map +0 -1
  142. package/dist/logitron-logger.module-Bs3XYZgF.mjs.map +0 -1
  143. package/dist/logitron-logger.module-BsDZR5BH.js.map +0 -1
  144. package/dist/logitron-logger.module-CxT1F6Cm.d.ts.map +0 -1
  145. package/dist/logitron-logger.module-zDKVd_o0.d.mts.map +0 -1
  146. package/dist/search-CFQRISUv.mjs.map +0 -1
  147. package/dist/search-CQOo-Iq6.d.mts.map +0 -1
  148. package/dist/search-DujhR94O.d.ts.map +0 -1
  149. package/dist/search-njTI2hdo.js.map +0 -1
  150. package/dist/transport.manager-BivyUTtU.js.map +0 -1
  151. package/dist/transport.manager-D6KL3t9V.mjs.map +0 -1
  152. package/dist/transport.types-B5vcyv2k.d.mts.map +0 -1
  153. package/dist/transport.types-BuY84TwO.d.ts.map +0 -1
  154. package/scripts/postinstall.js +0 -61
@@ -1,14 +1,13 @@
1
1
  import { t as __require } from "./chunk-DLSkYcPk.mjs";
2
2
  import { n as internalLog, r as internalWarn, t as internalError } from "./internal-log-Cm8SOWSV.mjs";
3
3
  import { i as stringifyForLog } from "./safe-stringify-DJ66tW5I.mjs";
4
- import { t as LogixiaContext } from "./async-context-CwBNMm2E.mjs";
5
- import { t as safeToString } from "./coerce.utils-Dxv4_ikC.mjs";
6
- import { t as TransportManager } from "./transport.manager-D6KL3t9V.mjs";
4
+ import { d as sanitizeIncomingId, i as TraceContext, l as getTraceContextKey, s as extractTraceId, t as DEFAULT_TRACE_HEADERS } from "./trace.utils-CvXgaiQT.mjs";
5
+ import { t as LogixiaContext } from "./async-context-BzJrbfQs.mjs";
6
+ import { t as safeToString } from "./coerce.utils-Cu6IUTgX.mjs";
7
+ import { t as TransportManager, u as stripControlChars } from "./transport.manager-CpdVHEGP.mjs";
7
8
  import { constants } from "node:os";
8
- import { AsyncLocalStorage } from "node:async_hooks";
9
9
  import buildFastStringify from "fast-json-stringify";
10
- import crypto from "crypto";
11
- import { Injectable, Module, Optional, RequestMethod, Scope } from "@nestjs/common";
10
+ import { Catch, Inject, Injectable, Module, Optional, RequestMethod, Scope } from "@nestjs/common";
12
11
  import { EMPTY, Observable } from "rxjs";
13
12
 
14
13
  //#region src/plugin.ts
@@ -386,16 +385,29 @@ function disableOtelBridge() {
386
385
  //#endregion
387
386
  //#region src/utils/redact.utils.ts
388
387
  const DEFAULT_CENSOR = "[REDACTED]";
388
+ /** Maximum nesting depth walked; deeper values are replaced with a marker. */
389
+ const MAX_DEPTH = 32;
390
+ /** Maximum number of objects/arrays visited per redaction pass. */
391
+ const MAX_NODES = 1e4;
392
+ /**
393
+ * Strings longer than this are truncated before pattern scanning. The tail is
394
+ * dropped rather than emitted unscanned, so oversized input can never carry an
395
+ * unredacted secret past the patterns.
396
+ */
397
+ const MAX_SCAN_LENGTH = 16384;
398
+ const MAX_DEPTH_MARKER = "[MaxDepth]";
399
+ const MAX_NODES_MARKER = "[MaxNodes]";
400
+ const REDACTION_FAILED = "[RedactionFailed]";
389
401
  /**
390
402
  * Conservative patterns: tokens and secrets that should NEVER appear in logs.
391
403
  * Applied when `autoDetect: true` or `autoDetect: 'conservative'`.
392
404
  */
393
405
  const PII_CONSERVATIVE_PATTERNS = [
394
- /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*/g,
406
+ /eyJ[A-Za-z0-9_-]{1,1024}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{0,1024}/g,
395
407
  /Bearer\s+[A-Za-z0-9._~+/-]+=*/gi,
396
408
  /\b(?:sk|pk|api|key|secret|token)-[A-Za-z0-9_-]{16,}/gi,
397
409
  /\bAKIA[0-9A-Z]{16}\b/g,
398
- /\b[A-Za-z0-9/+]{40}\b(?=.*aws)/gi
410
+ /secret[_-]?(?:access[_-]?)?key["'\s:=]{1,6}[A-Za-z0-9/+]{40}(?![A-Za-z0-9/+])/gi
399
411
  ];
400
412
  /**
401
413
  * Conservative field-name paths auto-redacted by name regardless of value.
@@ -416,14 +428,24 @@ const PII_CONSERVATIVE_PATHS = [
416
428
  "**.privateKey",
417
429
  "**.private_key",
418
430
  "**.clientSecret",
419
- "**.client_secret"
431
+ "**.client_secret",
432
+ "**.cookie",
433
+ "**.set-cookie",
434
+ "**.x-api-key",
435
+ "**.session",
436
+ "**.proxy-authorization",
437
+ "**.x-auth-token",
438
+ "**.x-access-token",
439
+ "**.aws_secret_access_key",
440
+ "**.awsSecretAccessKey",
441
+ "**.secretAccessKey"
420
442
  ];
421
443
  /**
422
444
  * Aggressive patterns: also catch personal data that could identify a person.
423
445
  * Applied when `autoDetect: 'aggressive'`.
424
446
  */
425
447
  const PII_AGGRESSIVE_PATTERNS = [
426
- /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
448
+ /\b[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]{1,255}\.[A-Za-z]{2,24}\b/g,
427
449
  /\b\d{3}-?\d{2}-?\d{4}\b/g,
428
450
  /\b(?:\d[ -]?){13,19}\b/g,
429
451
  /\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
@@ -520,32 +542,54 @@ function matchesPath(fullPath, pattern) {
520
542
  }
521
543
  return re.test(fullPath);
522
544
  }
545
+ function isTokenChar(code) {
546
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 46 || code === 95 || code === 126 || code === 43 || code === 47 || code === 61 || code === 45;
547
+ }
523
548
  function applyPatterns(value, patterns, censor) {
524
549
  let redacted = value;
550
+ let suffix = "";
551
+ if (redacted.length > MAX_SCAN_LENGTH) {
552
+ let end = MAX_SCAN_LENGTH;
553
+ while (end > 0 && isTokenChar(redacted.charCodeAt(end - 1))) end--;
554
+ suffix = `…[truncated ${value.length - end} unscanned chars]`;
555
+ redacted = redacted.slice(0, end);
556
+ }
525
557
  for (const pattern of patterns) redacted = redacted.replace(pattern, censor);
526
- return redacted;
558
+ return redacted + suffix;
527
559
  }
528
560
  const CIRCULAR = "[Circular]";
529
561
  function joinPath(parent, key) {
530
562
  return parent ? `${parent}.${key}` : key;
531
563
  }
532
- function walkValue(value, state, fullPath, bare) {
564
+ function walkValue(value, state, fullPath, bare, depth) {
533
565
  const { paths = [], excludePaths = [], patterns = [], censor = DEFAULT_CENSOR } = state.config;
534
566
  if (paths.some((p) => matchesPath(fullPath, p) || matchesPath(bare, p))) return censor;
567
+ if (typeof value === "object" && value !== null) {
568
+ if (depth >= MAX_DEPTH) return MAX_DEPTH_MARKER;
569
+ if (++state.nodes > MAX_NODES) return MAX_NODES_MARKER;
570
+ }
535
571
  if (typeof value === "string") {
536
572
  if (patterns.length === 0) return value;
537
573
  if (excludePaths.some((p) => matchesPath(fullPath, p) || matchesPath(bare, p))) return value;
538
574
  return applyPatterns(value, patterns, censor);
539
575
  }
540
- if (Array.isArray(value)) return walkContainer(value, state, fullPath, bare, walkArray);
541
- if (isPlainObject(value)) return walkContainer(value, state, fullPath, bare, walkRecord);
576
+ if (Array.isArray(value)) return walkContainer(value, state, fullPath, bare, depth, walkArray);
577
+ if (value instanceof Error) return walkContainer(value, state, fullPath, bare, depth, walkError);
578
+ if (value instanceof URL) {
579
+ const url = new URL(value.href);
580
+ url.username = "";
581
+ url.password = "";
582
+ const redacted = walkValue(url.href, state, fullPath, bare, depth);
583
+ return redacted === value.href ? value : redacted;
584
+ }
585
+ if (isPlainObject(value)) return walkContainer(value, state, fullPath, bare, depth, typeof value.toJSON === "function" ? walkToJSON : walkRecord);
542
586
  return value;
543
587
  }
544
- function walkContainer(value, state, fullPath, bare, walk) {
588
+ function walkContainer(value, state, fullPath, bare, depth, walk) {
545
589
  if (state.ancestors.has(value)) return CIRCULAR;
546
590
  state.ancestors.add(value);
547
591
  try {
548
- return walk(value, state, fullPath, bare);
592
+ return walk(value, state, fullPath, bare, depth + 1);
549
593
  } finally {
550
594
  state.ancestors.delete(value);
551
595
  }
@@ -554,26 +598,51 @@ function walkContainer(value, state, fullPath, bare, walk) {
554
598
  * Array elements are matched by their indexed path (`users.0.password`) and by
555
599
  * the index-free path (`users.password`) that earlier versions used.
556
600
  */
557
- function walkArray(arr, state, fullPath, bare) {
601
+ function walkArray(arr, state, fullPath, bare, depth) {
558
602
  let result;
559
603
  for (const [i, item] of arr.entries()) {
560
- const next = walkValue(item, state, joinPath(fullPath, String(i)), bare);
604
+ const next = walkValue(item, state, joinPath(fullPath, String(i)), bare, depth);
561
605
  if (next !== item && !result) result = arr.slice(0, i);
562
606
  if (result) result.push(next);
563
607
  }
564
608
  return result ?? arr;
565
609
  }
566
- function walkRecord(obj, state, fullPath, bare) {
610
+ function walkRecord(obj, state, fullPath, bare, depth) {
567
611
  let result;
568
612
  const entries = Object.entries(obj);
569
613
  for (const [index, [key, value]] of entries.entries()) {
570
614
  const forbidden = key === "__proto__" || key === "constructor" || key === "prototype";
571
- const next = forbidden ? void 0 : walkValue(value, state, joinPath(fullPath, key), joinPath(bare, key));
615
+ const next = forbidden ? void 0 : walkValue(value, state, joinPath(fullPath, key), joinPath(bare, key), depth);
572
616
  if ((forbidden || next !== value) && !result) result = Object.fromEntries(entries.slice(0, index));
573
617
  if (result && !forbidden) result[key] = next;
574
618
  }
575
619
  return result ?? obj;
576
620
  }
621
+ /** Errors are serialized (own props + cause chain) and the result is walked. */
622
+ function walkError(err, state, fullPath, bare, depth) {
623
+ let serialized;
624
+ try {
625
+ serialized = serializeError(err);
626
+ } catch {
627
+ return {
628
+ name: err.name,
629
+ message: REDACTION_FAILED
630
+ };
631
+ }
632
+ const result = walkRecord(serialized, state, fullPath, bare, depth);
633
+ return result === serialized ? { ...serialized } : result;
634
+ }
635
+ /** Objects with `toJSON()` are redacted via the value they would serialize to. */
636
+ function walkToJSON(obj, state, fullPath, bare, depth) {
637
+ let json;
638
+ try {
639
+ json = obj.toJSON();
640
+ } catch {
641
+ return REDACTION_FAILED;
642
+ }
643
+ if (json === obj) return walkRecord(obj, state, fullPath, bare, depth);
644
+ return walkValue(json, state, fullPath, bare, depth);
645
+ }
577
646
  /**
578
647
  * Redact an object according to the given RedactConfig and return a new
579
648
  * top-level object. Subtrees with nothing to redact are shared with the input,
@@ -582,8 +651,9 @@ function walkRecord(obj, state, fullPath, bare) {
582
651
  function redactObject(obj, config, _currentPath = "") {
583
652
  const result = walkRecord(obj, {
584
653
  config,
585
- ancestors: new Set([obj])
586
- }, _currentPath, _currentPath);
654
+ ancestors: new Set([obj]),
655
+ nodes: 0
656
+ }, _currentPath, _currentPath, 0);
587
657
  return result === obj ? { ...obj } : result;
588
658
  }
589
659
  /**
@@ -594,12 +664,11 @@ function redactObject(obj, config, _currentPath = "") {
594
664
  * stays allocation-free for the common no-redact case.
595
665
  */
596
666
  function applyRedactionToString(value, config) {
597
- if (!config) return value;
667
+ const text = typeof value === "string" ? value : safeToString(value);
668
+ if (!config) return text;
598
669
  const resolved = resolveConfig(config);
599
- if (!resolved.patterns || resolved.patterns.length === 0) return value;
600
- let redacted = value;
601
- for (const pattern of resolved.patterns) redacted = redacted.replace(pattern, resolved.censor ?? DEFAULT_CENSOR);
602
- return redacted;
670
+ if (!resolved.patterns || resolved.patterns.length === 0) return text;
671
+ return applyPatterns(text, resolved.patterns, resolved.censor ?? DEFAULT_CENSOR);
603
672
  }
604
673
  /**
605
674
  * Apply redaction to a log payload (top-level call convenience wrapper).
@@ -639,11 +708,21 @@ const ALWAYS_EMIT_LEVELS = new Set([
639
708
  "warn",
640
709
  "fatal"
641
710
  ]);
711
+ const MAX_CACHED_TRACE_ID_LENGTH = 256;
712
+ function isCacheableTraceId(id) {
713
+ if (typeof id !== "string" || id.length === 0 || id.length > MAX_CACHED_TRACE_ID_LENGTH) return false;
714
+ for (let i = 0; i < id.length; i++) {
715
+ const c = id.charCodeAt(i);
716
+ if (c < 32 || c >= 127 && c <= 159) return false;
717
+ }
718
+ return true;
719
+ }
642
720
  var Sampler = class {
643
721
  constructor(config, onStats) {
644
722
  this.traceDecisions = /* @__PURE__ */ new Map();
645
723
  this.maxTrackedTraces = 1e5;
646
724
  this.traceTtlMs = 5 * 6e4;
725
+ this.traceMaxLifetimeMs = 60 * 6e4;
647
726
  this._tokenBucket = 0;
648
727
  this._lastRefillMs = Date.now();
649
728
  this._adaptiveWindow = [];
@@ -763,13 +842,15 @@ var Sampler = class {
763
842
  }
764
843
  /**
765
844
  * Return the cached decision for a trace, or make and cache a new one. Entries
766
- * idle past the TTL are re-decided; the least recently used entry is evicted
767
- * once the cap is reached.
845
+ * idle past the TTL, or older than the hard lifetime, are re-decided; the least
846
+ * recently used entry is evicted once the cap is reached. IDs that are empty,
847
+ * too long or contain control characters are sampled independently and never cached.
768
848
  */
769
849
  _decideForTrace(traceId, level) {
850
+ if (!isCacheableTraceId(traceId)) return this._sampleByRate(level);
770
851
  const now = Date.now();
771
852
  const known = this.traceDecisions.get(traceId);
772
- if (known && now - known.seenAt <= this.traceTtlMs) {
853
+ if (known && now - known.seenAt <= this.traceTtlMs && now - known.firstAt <= this.traceMaxLifetimeMs) {
773
854
  this.traceDecisions.delete(traceId);
774
855
  known.seenAt = now;
775
856
  this.traceDecisions.set(traceId, known);
@@ -779,6 +860,7 @@ var Sampler = class {
779
860
  this.traceDecisions.delete(traceId);
780
861
  this.traceDecisions.set(traceId, {
781
862
  emit,
863
+ firstAt: now,
782
864
  seenAt: now
783
865
  });
784
866
  if (this.traceDecisions.size > this.maxTrackedTraces) {
@@ -942,318 +1024,6 @@ function resetShutdownHandlers() {
942
1024
  shutdownInProgress = false;
943
1025
  }
944
1026
 
945
- //#endregion
946
- //#region node_modules/uuid/dist/esm-node/rng.js
947
- const rnds8Pool = new Uint8Array(256);
948
- let poolPtr = rnds8Pool.length;
949
- function rng() {
950
- if (poolPtr > rnds8Pool.length - 16) {
951
- crypto.randomFillSync(rnds8Pool);
952
- poolPtr = 0;
953
- }
954
- return rnds8Pool.slice(poolPtr, poolPtr += 16);
955
- }
956
-
957
- //#endregion
958
- //#region node_modules/uuid/dist/esm-node/stringify.js
959
- /**
960
- * Convert array of 16 byte values to UUID string format of the form:
961
- * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
962
- */
963
- const byteToHex = [];
964
- for (let i = 0; i < 256; ++i) byteToHex.push((i + 256).toString(16).slice(1));
965
- function unsafeStringify(arr, offset = 0) {
966
- return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
967
- }
968
-
969
- //#endregion
970
- //#region node_modules/uuid/dist/esm-node/native.js
971
- var native_default = { randomUUID: crypto.randomUUID };
972
-
973
- //#endregion
974
- //#region node_modules/uuid/dist/esm-node/v4.js
975
- function v4(options, buf, offset) {
976
- if (native_default.randomUUID && !buf && !options) return native_default.randomUUID();
977
- options = options || {};
978
- const rnds = options.random || (options.rng || rng)();
979
- rnds[6] = rnds[6] & 15 | 64;
980
- rnds[8] = rnds[8] & 63 | 128;
981
- if (buf) {
982
- offset = offset || 0;
983
- for (let i = 0; i < 16; ++i) buf[offset + i] = rnds[i];
984
- return buf;
985
- }
986
- return unsafeStringify(rnds);
987
- }
988
- var v4_default = v4;
989
-
990
- //#endregion
991
- //#region src/utils/trace.utils.ts
992
- /** Default key used to store the trace ID in AsyncLocalStorage. */
993
- const TRACE_CONTEXT_KEY = "traceId";
994
- /**
995
- * Singleton that owns the AsyncLocalStorage and the active contextKey.
996
- *
997
- * Why a class instead of bare module-level variables?
998
- * - The contextKey is user-configurable; encapsulating it here means there is
999
- * exactly one place that reads and writes it.
1000
- * - All helpers (getCurrentTraceId, runWithTraceId, etc.) delegate to this
1001
- * instance, so the key is always in sync without hidden global state.
1002
- * - Easier to test: you can reset the singleton between test cases.
1003
- *
1004
- * Usage (advanced):
1005
- * import { TraceContext } from 'logixia';
1006
- * TraceContext.instance.contextKey // → 'traceId' (or custom)
1007
- * TraceContext.instance.getCurrentTraceId() // same as standalone fn
1008
- */
1009
- var TraceContext = class TraceContext {
1010
- static #_ = this._instance = null;
1011
- constructor() {
1012
- this.storage = new AsyncLocalStorage();
1013
- this._contextKey = TRACE_CONTEXT_KEY;
1014
- }
1015
- /** The process-wide singleton. */
1016
- static get instance() {
1017
- if (!TraceContext._instance) TraceContext._instance = new TraceContext();
1018
- return TraceContext._instance;
1019
- }
1020
- /** @internal Reset the singleton (useful in tests). */
1021
- static _reset() {
1022
- TraceContext._instance = null;
1023
- }
1024
- /** The AsyncLocalStorage key that holds the trace ID value. */
1025
- get contextKey() {
1026
- return this._contextKey;
1027
- }
1028
- /** @internal Called by TraceMiddleware when it boots to register the user's key. */
1029
- setContextKey(key) {
1030
- this._contextKey = key;
1031
- }
1032
- /** UUID v4 generator (default). */
1033
- generate() {
1034
- return v4_default();
1035
- }
1036
- /** Read the trace ID from the current async context. */
1037
- getCurrentTraceId() {
1038
- var _this$storage$getStor;
1039
- return (_this$storage$getStor = this.storage.getStore()) === null || _this$storage$getStor === void 0 ? void 0 : _this$storage$getStor[this._contextKey];
1040
- }
1041
- /**
1042
- * Mutate the CURRENT async context in-place.
1043
- *
1044
- * ⚠️ DEPRECATED — unsafe for concurrent requests.
1045
- *
1046
- * Uses `AsyncLocalStorage.enterWith()`, which mutates the current async
1047
- * execution context and every Promise chain spawned from it. In a server
1048
- * processing overlapping requests this can cause a trace ID set for one
1049
- * request to bleed into *other* in-flight requests that share the same
1050
- * async parent (e.g. a module-level setup function or a cached handler).
1051
- *
1052
- * Use {@link run} instead — it scopes the context to a callback so there
1053
- * is no risk of cross-request leakage.
1054
- *
1055
- * @deprecated Use `TraceContext.instance.run(traceId, fn)` — do not call
1056
- * `setTraceId` from request-handling code.
1057
- */
1058
- setTraceId(traceId, data) {
1059
- const current = this.storage.getStore() ?? {};
1060
- this.storage.enterWith({
1061
- ...current,
1062
- [this._contextKey]: traceId,
1063
- ...data
1064
- });
1065
- }
1066
- /** Run `fn` inside a new async context that carries `traceId`. */
1067
- run(traceId, fn, data) {
1068
- return this.storage.run({
1069
- [this._contextKey]: traceId,
1070
- ...data
1071
- }, fn);
1072
- }
1073
- };
1074
- /**
1075
- * The shared AsyncLocalStorage instance.
1076
- * @deprecated Prefer `TraceContext.instance.storage` for new code.
1077
- */
1078
- const traceStorage = TraceContext.instance.storage;
1079
- /** Returns the key currently used to store the trace ID in AsyncLocalStorage. */
1080
- function getTraceContextKey() {
1081
- return TraceContext.instance.contextKey;
1082
- }
1083
- /** @internal Called by TraceMiddleware / traceMiddleware() when they boot. */
1084
- function _setActiveContextKey(key) {
1085
- TraceContext.instance.setContextKey(key);
1086
- }
1087
- /** Generate a UUID v4 trace ID. */
1088
- function generateTraceId() {
1089
- return TraceContext.instance.generate();
1090
- }
1091
- /**
1092
- * Get the current trace ID from async context.
1093
- * Returns `undefined` when called outside a traced request.
1094
- */
1095
- function getCurrentTraceId() {
1096
- return TraceContext.instance.getCurrentTraceId();
1097
- }
1098
- /**
1099
- * Set trace ID in the CURRENT async context without starting a new one.
1100
- *
1101
- * ⚠️ DEPRECATED — unsafe for concurrent requests.
1102
- *
1103
- * Uses `AsyncLocalStorage.enterWith()`, which mutates the current async
1104
- * execution context and every Promise chain spawned from it. Under load this
1105
- * can cause a trace ID from one request to bleed into others sharing the same
1106
- * async parent.
1107
- *
1108
- * Use {@link runWithTraceId} instead. Migration patterns:
1109
- *
1110
- * **Express / Fastify middleware — before:**
1111
- * ```ts
1112
- * app.use((req, _res, next) => {
1113
- * setTraceId(req.headers['x-trace-id'] ?? generateTraceId());
1114
- * next();
1115
- * });
1116
- * ```
1117
- * **after:**
1118
- * ```ts
1119
- * app.use((req, _res, next) => {
1120
- * runWithTraceId(req.headers['x-trace-id'] ?? generateTraceId(), () => next());
1121
- * });
1122
- * ```
1123
- *
1124
- * **Kafka / queue consumer — before:**
1125
- * ```ts
1126
- * async function handle(msg) {
1127
- * setTraceId(msg.headers['x-trace-id']);
1128
- * await processMessage(msg);
1129
- * }
1130
- * ```
1131
- * **after:**
1132
- * ```ts
1133
- * async function handle(msg) {
1134
- * await runWithTraceId(msg.headers['x-trace-id'], () => processMessage(msg));
1135
- * }
1136
- * ```
1137
- *
1138
- * **Background job — before:**
1139
- * ```ts
1140
- * setTraceId(generateTraceId(), { jobId: job.id });
1141
- * await job.run();
1142
- * ```
1143
- * **after:**
1144
- * ```ts
1145
- * await runWithTraceId(generateTraceId(), () => job.run(), { jobId: job.id });
1146
- * ```
1147
- *
1148
- * @deprecated Use `runWithTraceId(traceId, fn)` — do not call `setTraceId`
1149
- * from request-handling code.
1150
- */
1151
- function setTraceId(traceId, data) {
1152
- TraceContext.instance.setTraceId(traceId, data);
1153
- }
1154
- /** Run `fn` inside a new async context carrying `traceId`. */
1155
- function runWithTraceId(traceId, fn, data) {
1156
- return TraceContext.instance.run(traceId, fn, data);
1157
- }
1158
- /**
1159
- * Coerce an arbitrary value to a non-empty trace ID string, or `undefined`.
1160
- * Rejects empty/whitespace-only strings, non-strings, and non-first array elements.
1161
- */
1162
- function toValidTraceId(value) {
1163
- const first = Array.isArray(value) ? value[0] : value;
1164
- if (typeof first !== "string") return void 0;
1165
- const trimmed = first.trim();
1166
- return trimmed.length > 0 ? trimmed : void 0;
1167
- }
1168
- /** W3C trace-context: `version-traceId(32hex)-spanId(16hex)-flags(2hex)`. */
1169
- const W3C_TRACEPARENT_RE = /^[\da-f]{2}-([\da-f]{32})-[\da-f]{16}-[\da-f]{2}$/i;
1170
- /**
1171
- * Extract the 32-hex trace-id segment from a W3C `traceparent` header value.
1172
- * Returns the parsed trace ID, or the original value unchanged when it is not a
1173
- * well-formed traceparent (so non-W3C propagation still works).
1174
- */
1175
- function parseTraceparent(value) {
1176
- const m = W3C_TRACEPARENT_RE.exec(value);
1177
- return m ? m[1] : value;
1178
- }
1179
- /** Extract trace ID from request using configuration (header → query → body → params). */
1180
- function extractTraceId(request, config) {
1181
- const req = request;
1182
- if (config.header) {
1183
- const headers = Array.isArray(config.header) ? config.header : [config.header];
1184
- for (const header of headers) {
1185
- var _req$headers;
1186
- const lower = header.toLowerCase();
1187
- const value = toValidTraceId((_req$headers = req.headers) === null || _req$headers === void 0 ? void 0 : _req$headers[lower]);
1188
- if (value) return lower === "traceparent" ? parseTraceparent(value) : value;
1189
- }
1190
- }
1191
- if (config.query) {
1192
- const queries = Array.isArray(config.query) ? config.query : [config.query];
1193
- for (const query of queries) {
1194
- var _req$query;
1195
- const value = toValidTraceId((_req$query = req.query) === null || _req$query === void 0 ? void 0 : _req$query[query]);
1196
- if (value) return value;
1197
- }
1198
- }
1199
- if (config.body) {
1200
- const bodyFields = Array.isArray(config.body) ? config.body : [config.body];
1201
- for (const field of bodyFields) {
1202
- var _req$body;
1203
- const value = toValidTraceId((_req$body = req.body) === null || _req$body === void 0 ? void 0 : _req$body[field]);
1204
- if (value) return value;
1205
- }
1206
- }
1207
- if (config.params) {
1208
- const paramFields = Array.isArray(config.params) ? config.params : [config.params];
1209
- for (const param of paramFields) {
1210
- var _req$params;
1211
- const value = toValidTraceId((_req$params = req.params) === null || _req$params === void 0 ? void 0 : _req$params[param]);
1212
- if (value) return value;
1213
- }
1214
- }
1215
- }
1216
- /**
1217
- * Default headers checked for incoming trace ID propagation, in priority order:
1218
- * traceparent (W3C/OTel) → x-trace-id → x-request-id → x-correlation-id → trace-id
1219
- */
1220
- const DEFAULT_TRACE_HEADERS = [
1221
- "traceparent",
1222
- "x-trace-id",
1223
- "x-request-id",
1224
- "x-correlation-id",
1225
- "trace-id"
1226
- ];
1227
- /**
1228
- * Create trace ID middleware for Express/NestJS
1229
- */
1230
- function createTraceMiddleware(config) {
1231
- const defaultExtractor = {
1232
- header: DEFAULT_TRACE_HEADERS,
1233
- query: ["traceId", "trace_id"]
1234
- };
1235
- const resolvedConfig = {
1236
- ...config,
1237
- extractor: (config === null || config === void 0 ? void 0 : config.extractor) ? {
1238
- ...defaultExtractor,
1239
- ...config.extractor
1240
- } : defaultExtractor
1241
- };
1242
- _setActiveContextKey(resolvedConfig.contextKey ?? TRACE_CONTEXT_KEY);
1243
- return (req, res, next) => {
1244
- let traceId;
1245
- if (resolvedConfig.extractor) traceId = extractTraceId(req, resolvedConfig.extractor);
1246
- if (!traceId) traceId = resolvedConfig.generator ? resolvedConfig.generator() : generateTraceId();
1247
- req.traceId = traceId;
1248
- const resObj = res;
1249
- if (!resObj.headersSent) try {
1250
- if (typeof resObj.setHeader === "function") resObj.setHeader("X-Trace-Id", traceId);
1251
- else if (typeof resObj.header === "function") resObj.header("X-Trace-Id", traceId);
1252
- } catch {}
1253
- runWithTraceId(traceId, () => next());
1254
- };
1255
- }
1256
-
1257
1027
  //#endregion
1258
1028
  //#region src/core/logitron-logger.ts
1259
1029
  const _fastStringifyEntry = buildFastStringify({
@@ -1816,9 +1586,21 @@ var LogixiaLogger = class LogixiaLogger {
1816
1586
  ...mergedData
1817
1587
  } : mergedData;
1818
1588
  let payload;
1819
- if (rawPayload !== void 0 && rawPayload !== null) payload = this._hasRedact ? applyRedaction(rawPayload, this.config.redact) ?? rawPayload : rawPayload;
1589
+ if (rawPayload !== void 0 && rawPayload !== null) if (this._hasRedact) try {
1590
+ payload = applyRedaction(rawPayload, this.config.redact) ?? rawPayload;
1591
+ } catch (error) {
1592
+ internalError("Redaction failed", error);
1593
+ payload = { redaction: REDACTION_FAILED };
1594
+ }
1595
+ else payload = rawPayload;
1820
1596
  const traceId = this.config.traceId ? TraceContext.instance.getCurrentTraceId() ?? this.fallbackTraceId : void 0;
1821
- const safeMessage = this._hasRedact ? applyRedactionToString(message, this.config.redact) : message;
1597
+ let safeMessage = message;
1598
+ if (this._hasRedact) try {
1599
+ safeMessage = applyRedactionToString(message, this.config.redact);
1600
+ } catch (error) {
1601
+ internalError("Redaction failed", error);
1602
+ safeMessage = REDACTION_FAILED;
1603
+ }
1822
1604
  const entry = {
1823
1605
  timestamp: nowIso(),
1824
1606
  level,
@@ -1889,9 +1671,9 @@ var LogixiaLogger = class LogixiaLogger {
1889
1671
  if (((_this$config$format4 = this.config.format) === null || _this$config$format4 === void 0 ? void 0 : _this$config$format4.timestamp) !== false && this._fieldCache.get("timestamp") !== false) formatted += `${gray}[${entry.timestamp}]${reset} `;
1890
1672
  if (this._fieldCache.get("level") !== false) formatted += this._formattedLevels.get(entry.level) ?? `[${entry.level.toUpperCase()}] `;
1891
1673
  formatted += this._formattedAppName;
1892
- if (entry.traceId && this._fieldCache.get("traceId") !== false) formatted += `${cyan}[${entry.traceId}]${reset} `;
1893
- if (entry.context && this._fieldCache.get("context") !== false) formatted += `${yellow}[${entry.context}]${reset} `;
1894
- if (this._fieldCache.get("message") !== false) formatted += entry.message;
1674
+ if (entry.traceId && this._fieldCache.get("traceId") !== false) formatted += `${cyan}[${stripControlChars(String(entry.traceId))}]${reset} `;
1675
+ if (entry.context && this._fieldCache.get("context") !== false) formatted += `${yellow}[${stripControlChars(String(entry.context))}]${reset} `;
1676
+ if (this._fieldCache.get("message") !== false) formatted += stripControlChars(safeToString(entry.message));
1895
1677
  if (entry.payload !== void 0 && this._fieldCache.get("payload") !== false) formatted += ` ${stringifyForLog(entry.payload)}`;
1896
1678
  return formatted;
1897
1679
  }
@@ -2618,5 +2400,379 @@ let LogixiaLoggerModule = class LogixiaLoggerModule$1 {
2618
2400
  LogixiaLoggerModule = _LogixiaLoggerModule = __decorate([Module({})], LogixiaLoggerModule);
2619
2401
 
2620
2402
  //#endregion
2621
- export { resetShutdownHandlers as A, DEFAULT_LOG_COLORS as B, runWithTraceId as C, deregisterFromShutdown as D, _shutdownRegistrySize as E, getOtelMetaFields as F, usePlugin as G, LogLevel as H, initOtelBridge as I, isError as L, redactObject as M, disableOtelBridge as N, flushOnExit as O, getActiveOtelContext as P, normalizeError as R, getTraceContextKey as S, traceStorage as T, PluginRegistry as U, DEFAULT_LOG_LEVELS as V, globalPluginRegistry as W, TraceContext as _, WebSocketTraceInterceptor as a, extractTraceId as b, __decorateParam as c, __decorate as d, __decorateMetadata as f, TRACE_CONTEXT_KEY as g, DEFAULT_TRACE_HEADERS as h, LogixiaLoggerModule as i, applyRedaction as j, registerForShutdown as k, KafkaTraceInterceptor as l, createLogger as m, LOGIXIA_LOGGER_PREFIX as n, TraceMiddleware as o, LogixiaLogger as p, LOGIXIA_TRACE_CONFIG as r, resolveResponseHeader as s, LOGIXIA_LOGGER_CONFIG as t, LogixiaLoggerService as u, _setActiveContextKey as v, setTraceId as w, getCurrentTraceId as x, createTraceMiddleware as y, serializeError as z };
2622
- //# sourceMappingURL=logitron-logger.module-Bs3XYZgF.mjs.map
2403
+ //#region src/exceptions/exception.ts
2404
+ var LogixiaException = class extends Error {
2405
+ constructor(options) {
2406
+ super(options.message, { cause: options.cause });
2407
+ this.name = "LogixiaException";
2408
+ this.errorCode = options.code;
2409
+ this.errorType = options.type;
2410
+ this.httpStatus = options.httpStatus;
2411
+ this.param = options.param;
2412
+ this.details = options.details;
2413
+ this.docUrl = options.docUrl;
2414
+ this.metadata = options.metadata;
2415
+ Object.setPrototypeOf(this, new.target.prototype);
2416
+ }
2417
+ };
2418
+ /**
2419
+ * Returns `true` when `value` is a `LogixiaException` instance.
2420
+ *
2421
+ * Useful in exception filters or middleware that need to distinguish a
2422
+ * `LogixiaException` from a plain `Error` or a framework `HttpException`.
2423
+ *
2424
+ * @example
2425
+ * ```ts
2426
+ * if (isLogixiaException(err)) {
2427
+ * console.log(err.errorCode, err.httpStatus);
2428
+ * }
2429
+ * ```
2430
+ */
2431
+ function isLogixiaException(value) {
2432
+ return value instanceof LogixiaException;
2433
+ }
2434
+
2435
+ //#endregion
2436
+ //#region src/exceptions/builder.ts
2437
+ /**
2438
+ * Generates a trace ID via the shared {@link TraceContext} generator.
2439
+ *
2440
+ * Delegates to `TraceContext.instance.generate()` so any custom generator
2441
+ * configured on `TraceIdConfig.generator` is respected — avoids the silent
2442
+ * "builder has its own UUID source" divergence this module had previously.
2443
+ *
2444
+ * @example `'550e8400-e29b-41d4-a716-446655440000'`
2445
+ */
2446
+ function generateTraceId() {
2447
+ return TraceContext.instance.generate();
2448
+ }
2449
+ /** @deprecated Use `generateTraceId` instead. */
2450
+ const generateRequestId = generateTraceId;
2451
+ /**
2452
+ * Maps an HTTP status code to a human-friendly error type string.
2453
+ * Used as a fallback when the exception is a generic `HttpException`.
2454
+ */
2455
+ function httpStatusToType(status) {
2456
+ if (status === 400) return "api_error";
2457
+ if (status === 401) return "authentication_error";
2458
+ if (status === 402) return "payment_error";
2459
+ if (status === 403) return "authorization_error";
2460
+ if (status === 404) return "not_found_error";
2461
+ if (status === 408) return "timeout_error";
2462
+ if (status === 409) return "conflict_error";
2463
+ if (status === 422) return "validation_error";
2464
+ if (status === 429) return "rate_limit_error";
2465
+ if (status >= 500) return "server_error";
2466
+ if (status >= 400) return "api_error";
2467
+ return "server_error";
2468
+ }
2469
+ /**
2470
+ * Duck-type check for NestJS `HttpException`.
2471
+ * Avoids a hard import on `@nestjs/common` so the builder is usable outside NestJS.
2472
+ */
2473
+ function isHttpException(value) {
2474
+ return typeof value === "object" && value !== null && typeof value["getStatus"] === "function" && typeof value["getResponse"] === "function";
2475
+ }
2476
+ /**
2477
+ * Builds the optional `debug` block from an error.
2478
+ * Returns `undefined` when there is nothing useful to include.
2479
+ */
2480
+ function buildDebug(error, service, durationMs, expose) {
2481
+ if (expose.debug !== true) return void 0;
2482
+ const stack = error instanceof Error ? error.stack : void 0;
2483
+ const causeRaw = error instanceof Error ? error.cause : void 0;
2484
+ let cause;
2485
+ if (expose.cause !== true) cause = void 0;
2486
+ else if (causeRaw instanceof Error) cause = causeRaw.message;
2487
+ else if (causeRaw !== void 0 && causeRaw !== null) cause = String(causeRaw);
2488
+ if (!stack && !cause && !service && durationMs === void 0) return void 0;
2489
+ return {
2490
+ ...stack !== void 0 ? { stack } : {},
2491
+ ...cause !== void 0 ? { cause } : {},
2492
+ ...service !== void 0 ? { service } : {},
2493
+ ...durationMs !== void 0 ? { duration_ms: durationMs } : {}
2494
+ };
2495
+ }
2496
+ var ErrorResponseBuilder = class {
2497
+ /**
2498
+ * Build a `LogixiaErrorResponse` from any thrown value.
2499
+ *
2500
+ * @param params - See `BuildParams`.
2501
+ * @returns The unified error response and the HTTP status code to send.
2502
+ *
2503
+ * @remarks
2504
+ * `traceId` does not need to be supplied by the caller in most cases — the
2505
+ * builder reads `TraceContext.instance.getCurrentTraceId()` internally and
2506
+ * falls back to the explicit `traceId` argument only when AsyncLocalStorage
2507
+ * is empty. If neither source yields a value, `meta.trace_id` is omitted.
2508
+ *
2509
+ * @example In a NestJS exception filter
2510
+ * ```ts
2511
+ * const { response, httpStatus } = ErrorResponseBuilder.build<AppCode, AppType>({
2512
+ * exception,
2513
+ * // Omit traceId — it will be picked up from ALS automatically.
2514
+ * path: request.url,
2515
+ * service: process.env.SERVICE_NAME,
2516
+ * startTime: request.startTime,
2517
+ * });
2518
+ *
2519
+ * if (process.env.NODE_ENV === 'production') delete response.debug;
2520
+ * // Echo the resolved traceId back on whatever header your TraceIdConfig says —
2521
+ * // do NOT hardcode 'X-Trace-Id', use `resolveResponseHeader(config)` instead.
2522
+ * if (response.meta.trace_id) {
2523
+ * response.setHeader(resolveResponseHeader(config) ?? 'X-Trace-Id', response.meta.trace_id);
2524
+ * }
2525
+ * response.status(httpStatus).json(response);
2526
+ * ```
2527
+ */
2528
+ static build(params) {
2529
+ const { exception, path, service, startTime, traceId: rawTraceId, exposeDebug, exposeCause } = params;
2530
+ const expose = {
2531
+ debug: exposeDebug,
2532
+ cause: exposeCause
2533
+ };
2534
+ const traceId = TraceContext.instance.getCurrentTraceId() ?? rawTraceId;
2535
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2536
+ const durationMs = startTime !== void 0 ? Date.now() - startTime : void 0;
2537
+ if (isLogixiaException(exception)) {
2538
+ const debug$1 = buildDebug(exception, service, durationMs, expose);
2539
+ const errorBlock = {
2540
+ type: exception.errorType,
2541
+ code: exception.errorCode,
2542
+ message: exception.message
2543
+ };
2544
+ if (exception.param !== void 0) errorBlock.param = exception.param;
2545
+ if (exception.details !== void 0 && exception.details.length > 0) errorBlock.details = exception.details;
2546
+ if (exception.docUrl !== void 0) errorBlock.doc_url = exception.docUrl;
2547
+ return {
2548
+ response: {
2549
+ success: false,
2550
+ error: errorBlock,
2551
+ meta: {
2552
+ ...traceId !== void 0 ? { trace_id: traceId } : {},
2553
+ timestamp,
2554
+ path,
2555
+ status: exception.httpStatus
2556
+ },
2557
+ ...debug$1 !== void 0 ? { debug: debug$1 } : {}
2558
+ },
2559
+ httpStatus: exception.httpStatus
2560
+ };
2561
+ }
2562
+ if (isHttpException(exception)) {
2563
+ const status = exception.getStatus();
2564
+ const exResponse = exception.getResponse();
2565
+ let message;
2566
+ if (typeof exResponse === "string") message = exResponse;
2567
+ else if (typeof exResponse === "object" && exResponse !== null && "message" in exResponse) {
2568
+ const raw = exResponse.message;
2569
+ message = Array.isArray(raw) ? raw.join(", ") : String(raw);
2570
+ } else message = exception.message;
2571
+ const type = httpStatusToType(status);
2572
+ const code = `HTTP_${status}`;
2573
+ const debug$1 = buildDebug(exception, service, durationMs, expose);
2574
+ return {
2575
+ response: {
2576
+ success: false,
2577
+ error: {
2578
+ type,
2579
+ code,
2580
+ message
2581
+ },
2582
+ meta: {
2583
+ ...traceId !== void 0 ? { trace_id: traceId } : {},
2584
+ timestamp,
2585
+ path,
2586
+ status
2587
+ },
2588
+ ...debug$1 !== void 0 ? { debug: debug$1 } : {}
2589
+ },
2590
+ httpStatus: status
2591
+ };
2592
+ }
2593
+ const debug = buildDebug(exception instanceof Error ? exception : new Error(String(exception)), service, durationMs, expose);
2594
+ return {
2595
+ response: {
2596
+ success: false,
2597
+ error: {
2598
+ type: "server_error",
2599
+ code: "INTERNAL_SERVER_ERROR",
2600
+ message: "An unexpected error occurred."
2601
+ },
2602
+ meta: {
2603
+ ...traceId !== void 0 ? { trace_id: traceId } : {},
2604
+ timestamp,
2605
+ path,
2606
+ status: 500
2607
+ },
2608
+ ...debug !== void 0 ? { debug } : {}
2609
+ },
2610
+ httpStatus: 500
2611
+ };
2612
+ }
2613
+ };
2614
+
2615
+ //#endregion
2616
+ //#region src/core/nestjs-extras.ts
2617
+ var _ref;
2618
+ /**
2619
+ * Inject the Logixia logger registered in the current NestJS DI container.
2620
+ *
2621
+ * Equivalent to `@Inject(LOGIXIA_LOGGER_TOKEN)` but without needing to import
2622
+ * the internal token constant yourself.
2623
+ *
2624
+ * @example
2625
+ * ```ts
2626
+ * constructor(@InjectLogger() private readonly logger: LogixiaLoggerService) {}
2627
+ * ```
2628
+ */
2629
+ const InjectLogger = () => Inject(`${LOGIXIA_LOGGER_PREFIX}SERVICE`);
2630
+ /**
2631
+ * Method decorator that auto-logs entry, exit, duration, and errors.
2632
+ *
2633
+ * Preserves the original method's sync/async contract: a synchronous method
2634
+ * stays synchronous (returns its value directly, with logs emitted
2635
+ * fire-and-forget), and an async method is awaited so exit/error logs reflect
2636
+ * the resolved result. Attaches to the logger found on the class instance via a
2637
+ * `logger` property (the conventional NestJS name).
2638
+ *
2639
+ * @example
2640
+ * ```ts
2641
+ * @LogMethod({ level: 'info', logArgs: true })
2642
+ * async processPayment(orderId: string): Promise<void> { … }
2643
+ * ```
2644
+ */
2645
+ function LogMethod(options = {}) {
2646
+ const { level = "debug", logArgs = true, logResult = false, logErrors = true } = options;
2647
+ return function(target, propertyKey, descriptor) {
2648
+ var _constructor;
2649
+ const originalMethod = descriptor.value;
2650
+ if (typeof originalMethod !== "function") return descriptor;
2651
+ const methodName = String(propertyKey);
2652
+ const className = ((_constructor = target.constructor) === null || _constructor === void 0 ? void 0 : _constructor.name) ?? "Unknown";
2653
+ const label = options.label ?? `${className}.${methodName}`;
2654
+ let _warnedNoLogger = false;
2655
+ const reportLogFailure = (phase, err) => {
2656
+ internalError(`@LogMethod(${label}) ${phase} log failed: ${String(err)}`);
2657
+ };
2658
+ const emit = (logger, phase, message, data) => {
2659
+ const logFnRaw = logger[level];
2660
+ const p = (typeof logFnRaw === "function" ? logFnRaw : logger.debug).bind(logger)(message, data);
2661
+ if (p && typeof p.catch === "function") p.catch((e) => reportLogFailure(phase, e));
2662
+ };
2663
+ const emitError = (logger, error, start) => {
2664
+ const err = error instanceof Error ? error : new Error(String(error));
2665
+ const errLog = logger.error(err, {
2666
+ method: label,
2667
+ durationMs: Date.now() - start
2668
+ });
2669
+ if (errLog && typeof errLog.catch === "function") errLog.catch((e) => reportLogFailure("error", e));
2670
+ };
2671
+ descriptor.value = function(...args) {
2672
+ const logger = this.logger ?? LogixiaLoggerModule._globalLogger ?? void 0;
2673
+ if (!logger && !_warnedNoLogger) {
2674
+ _warnedNoLogger = true;
2675
+ internalWarn(`@LogMethod on ${label}: no logger available. Either inject LogixiaLoggerService as this.logger or ensure LogixiaLoggerModule is initialised.`);
2676
+ }
2677
+ const start = Date.now();
2678
+ const entry = { method: label };
2679
+ if (logArgs && args.length > 0) entry["args"] = args;
2680
+ if (logger) emit(logger, "entry", `→ ${label}`, entry);
2681
+ const buildExit = (result$1) => {
2682
+ const exit = {
2683
+ method: label,
2684
+ durationMs: Date.now() - start
2685
+ };
2686
+ if (logResult) exit["result"] = result$1;
2687
+ return exit;
2688
+ };
2689
+ let result;
2690
+ try {
2691
+ result = originalMethod.apply(this, args);
2692
+ } catch (error) {
2693
+ if (logger && logErrors) emitError(logger, error, start);
2694
+ throw error;
2695
+ }
2696
+ if (result && typeof result.then === "function") return result.then((resolved) => {
2697
+ if (logger) emit(logger, "exit", `← ${label}`, buildExit(resolved));
2698
+ return resolved;
2699
+ }, (error) => {
2700
+ if (logger && logErrors) emitError(logger, error, start);
2701
+ throw error;
2702
+ });
2703
+ if (logger) emit(logger, "exit", `← ${label}`, buildExit(result));
2704
+ return result;
2705
+ };
2706
+ return descriptor;
2707
+ };
2708
+ }
2709
+ /** Injection token for {@link LogixiaExceptionFilterOptions}. */
2710
+ const LOGIXIA_EXCEPTION_FILTER_OPTIONS = "LOGIXIA_EXCEPTION_FILTER_OPTIONS";
2711
+ let LogixiaExceptionFilter = class LogixiaExceptionFilter$1 {
2712
+ constructor(logger, loggerConfig, options) {
2713
+ this._options = options ?? {};
2714
+ this._logger = logger ?? LogixiaLoggerModule._globalLogger ?? void 0;
2715
+ this._traceConfig = typeof (loggerConfig === null || loggerConfig === void 0 ? void 0 : loggerConfig.traceId) === "object" ? loggerConfig.traceId : void 0;
2716
+ }
2717
+ catch(exception, host) {
2718
+ var _request$headers;
2719
+ const ctx = host.switchToHttp();
2720
+ const request = ctx.getRequest();
2721
+ const response = ctx.getResponse();
2722
+ const traceId = TraceContext.instance.getCurrentTraceId() ?? sanitizeIncomingId((_request$headers = request.headers) === null || _request$headers === void 0 ? void 0 : _request$headers["x-trace-id"]) ?? sanitizeIncomingId(request.id) ?? void 0;
2723
+ const { response: errorResponse, httpStatus } = ErrorResponseBuilder.build({
2724
+ exception,
2725
+ traceId,
2726
+ path: request.url ?? "/",
2727
+ startTime: request.startTime,
2728
+ exposeDebug: this._options.exposeDebug === true,
2729
+ exposeCause: this._options.exposeCause === true
2730
+ });
2731
+ if (this._logger) {
2732
+ const requestMeta = {
2733
+ method: request.method ?? "",
2734
+ url: request.url ?? "",
2735
+ status: httpStatus,
2736
+ ...errorResponse.meta.trace_id !== void 0 ? { trace_id: errorResponse.meta.trace_id } : {}
2737
+ };
2738
+ const onLogFailure = (err) => {
2739
+ internalError(`ExceptionFilter failed to write log entry: ${String(err)}`);
2740
+ };
2741
+ let logPromise;
2742
+ if (httpStatus >= 500) {
2743
+ const err = exception instanceof Error ? exception : new Error(String(exception));
2744
+ logPromise = this._logger.error(err, requestMeta);
2745
+ } else if (isLogixiaException(exception)) logPromise = this._logger.warn(`[${errorResponse.error.code}] ${errorResponse.error.message}`, requestMeta);
2746
+ else logPromise = this._logger.warn(`[${httpStatus}] ${errorResponse.error.message}`, requestMeta);
2747
+ if (logPromise && typeof logPromise.catch === "function") logPromise.catch(onLogFailure);
2748
+ }
2749
+ if (errorResponse.meta.trace_id) {
2750
+ const traceHeader = resolveResponseHeader(this._traceConfig);
2751
+ if (traceHeader) try {
2752
+ response.setHeader(traceHeader, errorResponse.meta.trace_id);
2753
+ } catch {}
2754
+ }
2755
+ if (httpStatus === 429) try {
2756
+ response.setHeader("Retry-After", "60");
2757
+ } catch {}
2758
+ response.status(httpStatus).json(errorResponse);
2759
+ }
2760
+ };
2761
+ LogixiaExceptionFilter = __decorate([
2762
+ Catch(),
2763
+ __decorateParam(0, Optional()),
2764
+ __decorateParam(0, Inject(`${LOGIXIA_LOGGER_PREFIX}SERVICE`)),
2765
+ __decorateParam(1, Optional()),
2766
+ __decorateParam(1, Inject(LOGIXIA_LOGGER_CONFIG)),
2767
+ __decorateParam(2, Optional()),
2768
+ __decorateParam(2, Inject(LOGIXIA_EXCEPTION_FILTER_OPTIONS)),
2769
+ __decorateMetadata("design:paramtypes", [
2770
+ Object,
2771
+ typeof (_ref = typeof Partial !== "undefined" && Partial) === "function" ? _ref : Object,
2772
+ Object
2773
+ ])
2774
+ ], LogixiaExceptionFilter);
2775
+
2776
+ //#endregion
2777
+ export { initOtelBridge as A, registerForShutdown as C, disableOtelBridge as D, redactObject as E, DEFAULT_LOG_LEVELS as F, LogLevel as I, PluginRegistry as L, normalizeError as M, serializeError as N, getActiveOtelContext as O, DEFAULT_LOG_COLORS as P, globalPluginRegistry as R, flushOnExit as S, applyRedaction as T, LogixiaLoggerService as _, ErrorResponseBuilder as a, _shutdownRegistrySize as b, LogixiaException as c, LOGIXIA_LOGGER_PREFIX as d, LOGIXIA_TRACE_CONFIG as f, KafkaTraceInterceptor as g, TraceMiddleware as h, LogixiaExceptionFilter as i, isError as j, getOtelMetaFields as k, isLogixiaException as l, WebSocketTraceInterceptor as m, LOGIXIA_EXCEPTION_FILTER_OPTIONS as n, generateRequestId as o, LogixiaLoggerModule as p, LogMethod as r, generateTraceId as s, InjectLogger as t, LOGIXIA_LOGGER_CONFIG as u, LogixiaLogger as v, resetShutdownHandlers as w, deregisterFromShutdown as x, createLogger as y, usePlugin as z };
2778
+ //# sourceMappingURL=nestjs-extras-ImbuDhs1.mjs.map