logixia 2.0.4 → 2.0.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 (218) hide show
  1. package/README.md +172 -2614
  2. package/dist/{async-context-B05ArHgM.mjs → async-context-2mbUKhcl.mjs} +4 -6
  3. package/dist/async-context-2mbUKhcl.mjs.map +1 -0
  4. package/dist/{async-context-DTQgi8f7.js → async-context-DUFMDYAr.js} +4 -6
  5. package/dist/async-context-DUFMDYAr.js.map +1 -0
  6. package/dist/{azure-monitor.transport-CwbOLZHB.js → azure-monitor.transport-Bu4S2I2q.js} +5 -5
  7. package/dist/azure-monitor.transport-Bu4S2I2q.js.map +1 -0
  8. package/dist/{azure-monitor.transport-DV03Z5P4.d.ts → azure-monitor.transport-Bvf7Xegt.d.ts} +2 -2
  9. package/dist/{azure-monitor.transport-DV03Z5P4.d.ts.map → azure-monitor.transport-Bvf7Xegt.d.ts.map} +1 -1
  10. package/dist/{azure-monitor.transport-BPbgjATu.mjs → azure-monitor.transport-C1RWAumT.mjs} +5 -5
  11. package/dist/azure-monitor.transport-C1RWAumT.mjs.map +1 -0
  12. package/dist/{azure-monitor.transport-C7kYyZUW.d.mts → azure-monitor.transport-CbSHOnNX.d.mts} +2 -2
  13. package/dist/{azure-monitor.transport-C7kYyZUW.d.mts.map → azure-monitor.transport-CbSHOnNX.d.mts.map} +1 -1
  14. package/dist/azure.d.mts +1 -1
  15. package/dist/azure.d.ts +1 -1
  16. package/dist/azure.js +1 -1
  17. package/dist/azure.mjs +1 -1
  18. package/dist/{batch-buffer-0fgLmIPn.mjs → batch-buffer-BvXLDPlV.mjs} +3 -3
  19. package/dist/{batch-buffer-0fgLmIPn.mjs.map → batch-buffer-BvXLDPlV.mjs.map} +1 -1
  20. package/dist/{batch-buffer-DT4gdXmf.js → batch-buffer-C7Am4Mw-.js} +3 -3
  21. package/dist/{batch-buffer-DT4gdXmf.js.map → batch-buffer-C7Am4Mw-.js.map} +1 -1
  22. package/dist/browser.d.mts +5 -5
  23. package/dist/browser.d.mts.map +1 -1
  24. package/dist/browser.d.ts +5 -5
  25. package/dist/browser.d.ts.map +1 -1
  26. package/dist/browser.js +47 -12
  27. package/dist/browser.js.map +1 -1
  28. package/dist/browser.mjs +47 -12
  29. package/dist/browser.mjs.map +1 -1
  30. package/dist/{builder-D70z6kjj.mjs → builder-DhNhvOkt.mjs} +106 -153
  31. package/dist/builder-DhNhvOkt.mjs.map +1 -0
  32. package/dist/{builder-M7NoaugM.js → builder-yFvJrz7o.js} +109 -179
  33. package/dist/builder-yFvJrz7o.js.map +1 -0
  34. package/dist/cli/index.js +157 -66
  35. package/dist/cloudwatch.d.mts +1 -1
  36. package/dist/cloudwatch.d.ts +1 -1
  37. package/dist/cloudwatch.js +1 -1
  38. package/dist/cloudwatch.mjs +1 -1
  39. package/dist/{cloudwatch.transport-Bf7L4I59.d.ts → cloudwatch.transport-CQyT6U91.d.ts} +2 -2
  40. package/dist/{cloudwatch.transport-Bf7L4I59.d.ts.map → cloudwatch.transport-CQyT6U91.d.ts.map} +1 -1
  41. package/dist/{cloudwatch.transport-B2wKQFF7.js → cloudwatch.transport-Ch1Uae-0.js} +6 -6
  42. package/dist/cloudwatch.transport-Ch1Uae-0.js.map +1 -0
  43. package/dist/{cloudwatch.transport-Rj4yTpRn.d.mts → cloudwatch.transport-CjVrI6S4.d.mts} +2 -2
  44. package/dist/{cloudwatch.transport-Rj4yTpRn.d.mts.map → cloudwatch.transport-CjVrI6S4.d.mts.map} +1 -1
  45. package/dist/{cloudwatch.transport-BTvYfLs9.mjs → cloudwatch.transport-DZqF2xZO.mjs} +6 -6
  46. package/dist/cloudwatch.transport-DZqF2xZO.mjs.map +1 -0
  47. package/dist/correlation.d.mts +4 -55
  48. package/dist/correlation.d.mts.map +1 -1
  49. package/dist/correlation.d.ts +4 -55
  50. package/dist/correlation.d.ts.map +1 -1
  51. package/dist/correlation.js +13 -13
  52. package/dist/correlation.js.map +1 -1
  53. package/dist/correlation.mjs +13 -13
  54. package/dist/correlation.mjs.map +1 -1
  55. package/dist/{entry-fields-RLd1jCoR.js → entry-fields-BSLkzw18.js} +8 -3
  56. package/dist/entry-fields-BSLkzw18.js.map +1 -0
  57. package/dist/{entry-fields-CGuvT2R0.mjs → entry-fields-J1OBi24o.mjs} +8 -3
  58. package/dist/entry-fields-J1OBi24o.mjs.map +1 -0
  59. package/dist/gcp.d.mts +1 -1
  60. package/dist/gcp.d.ts +1 -1
  61. package/dist/gcp.js +1 -1
  62. package/dist/gcp.mjs +1 -1
  63. package/dist/{gcp.transport-CFQfdAOK.d.ts → gcp.transport-BCIvdFV-.d.ts} +4 -4
  64. package/dist/{gcp.transport-CFQfdAOK.d.ts.map → gcp.transport-BCIvdFV-.d.ts.map} +1 -1
  65. package/dist/{gcp.transport-C5D72b1g.d.mts → gcp.transport-D9SdLPwP.d.mts} +4 -4
  66. package/dist/{gcp.transport-C5D72b1g.d.mts.map → gcp.transport-D9SdLPwP.d.mts.map} +1 -1
  67. package/dist/{gcp.transport-CGKopAhQ.mjs → gcp.transport-DVCmnA3b.mjs} +20 -16
  68. package/dist/gcp.transport-DVCmnA3b.mjs.map +1 -0
  69. package/dist/{gcp.transport-KZe9hCAR.js → gcp.transport-Dy2Lf8FI.js} +20 -16
  70. package/dist/gcp.transport-Dy2Lf8FI.js.map +1 -0
  71. package/dist/http-shapes-BnBkJcid.d.mts +32 -0
  72. package/dist/http-shapes-BnBkJcid.d.mts.map +1 -0
  73. package/dist/http-shapes-BnBkJcid.d.ts +32 -0
  74. package/dist/http-shapes-BnBkJcid.d.ts.map +1 -0
  75. package/dist/{index-xqaJtAZn.d.mts → index-BSTtFcQ6.d.mts} +142 -8
  76. package/dist/index-BSTtFcQ6.d.mts.map +1 -0
  77. package/dist/{index-DwozbmB4.d.ts → index-CFDGkI8n.d.ts} +142 -8
  78. package/dist/index-CFDGkI8n.d.ts.map +1 -0
  79. package/dist/index.d.mts +21 -65
  80. package/dist/index.d.mts.map +1 -1
  81. package/dist/index.d.ts +21 -65
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +26 -26
  84. package/dist/index.js.map +1 -1
  85. package/dist/index.mjs +21 -21
  86. package/dist/index.mjs.map +1 -1
  87. package/dist/{internal-log-uCSd1PMc.mjs → internal-log-Bg4V1cPg.mjs} +30 -2
  88. package/dist/internal-log-Bg4V1cPg.mjs.map +1 -0
  89. package/dist/{internal-log-b0Vp4qrB.js → internal-log-By4Govmu.js} +41 -1
  90. package/dist/internal-log-By4Govmu.js.map +1 -0
  91. package/dist/{logitron-logger-D2B75Oc7.d.ts → logitron-logger-D22Yb61K.d.mts} +13 -100
  92. package/dist/logitron-logger-D22Yb61K.d.mts.map +1 -0
  93. package/dist/{logitron-logger-oEd6wNhf.d.mts → logitron-logger-D_MYYrQt.d.ts} +13 -100
  94. package/dist/logitron-logger-D_MYYrQt.d.ts.map +1 -0
  95. package/dist/middleware.d.mts +4 -17
  96. package/dist/middleware.d.mts.map +1 -1
  97. package/dist/middleware.d.ts +4 -17
  98. package/dist/middleware.d.ts.map +1 -1
  99. package/dist/middleware.js +9 -39
  100. package/dist/middleware.js.map +1 -1
  101. package/dist/middleware.mjs +7 -37
  102. package/dist/middleware.mjs.map +1 -1
  103. package/dist/nest.d.mts +14 -7
  104. package/dist/nest.d.mts.map +1 -1
  105. package/dist/nest.d.ts +14 -7
  106. package/dist/nest.d.ts.map +1 -1
  107. package/dist/nest.js +15 -10
  108. package/dist/nest.js.map +1 -1
  109. package/dist/nest.mjs +15 -10
  110. package/dist/nest.mjs.map +1 -1
  111. package/dist/{transport.manager-eZ_EeC00.mjs → otel-Dt5riXuE.mjs} +173 -24
  112. package/dist/otel-Dt5riXuE.mjs.map +1 -0
  113. package/dist/{transport.manager-DkcEOlqV.js → otel-DyeH6DVR.js} +214 -23
  114. package/dist/otel-DyeH6DVR.js.map +1 -0
  115. package/dist/own-property-BWkibepN.js +40 -0
  116. package/dist/own-property-BWkibepN.js.map +1 -0
  117. package/dist/own-property-CjFw9qwZ.mjs +29 -0
  118. package/dist/own-property-CjFw9qwZ.mjs.map +1 -0
  119. package/dist/{request-trace-C_fvIDv4.mjs → request-trace-BgNykH9M.mjs} +1 -5
  120. package/dist/{request-trace-C_fvIDv4.mjs.map → request-trace-BgNykH9M.mjs.map} +1 -1
  121. package/dist/{request-trace-wTJdKp6-.js → request-trace-ByujsUZk.js} +1 -5
  122. package/dist/{request-trace-wTJdKp6-.js.map → request-trace-ByujsUZk.js.map} +1 -1
  123. package/dist/{safe-stringify-BMRl3EX3.mjs → safe-stringify-B5p5ktFe.mjs} +37 -12
  124. package/dist/safe-stringify-B5p5ktFe.mjs.map +1 -0
  125. package/dist/{safe-stringify-DLz1Kz3o.js → safe-stringify-J3sh-7YM.js} +37 -12
  126. package/dist/safe-stringify-J3sh-7YM.js.map +1 -0
  127. package/dist/{search-DUfWuzFP.js → search-CRjhHxe9.js} +269 -25
  128. package/dist/search-CRjhHxe9.js.map +1 -0
  129. package/dist/{search-CazwyX5Z.d.ts → search-DA3ft3X5.d.mts} +5 -2
  130. package/dist/search-DA3ft3X5.d.mts.map +1 -0
  131. package/dist/{search-CWMVYC-R.mjs → search-DAXKE-P9.mjs} +269 -25
  132. package/dist/search-DAXKE-P9.mjs.map +1 -0
  133. package/dist/{search-BH33XhuU.d.mts → search-DEl3Pe47.d.ts} +5 -2
  134. package/dist/search-DEl3Pe47.d.ts.map +1 -0
  135. package/dist/search.d.mts +1 -1
  136. package/dist/search.d.ts +1 -1
  137. package/dist/search.js +1 -1
  138. package/dist/search.mjs +1 -1
  139. package/dist/{secret-config-CBZBs0p1.js → secret-config-CmK2wVkr.js} +6 -5
  140. package/dist/secret-config-CmK2wVkr.js.map +1 -0
  141. package/dist/{secret-config-BegMLkYl.mjs → secret-config-Dhb6pqTU.mjs} +6 -5
  142. package/dist/secret-config-Dhb6pqTU.mjs.map +1 -0
  143. package/dist/{slack.transport-DxHOXlLu.d.mts → slack.transport-BDyENuR0.d.mts} +2 -2
  144. package/dist/{slack.transport-DxHOXlLu.d.mts.map → slack.transport-BDyENuR0.d.mts.map} +1 -1
  145. package/dist/{slack.transport-Ce96JBDk.mjs → slack.transport-BTEwsREY.mjs} +66 -15
  146. package/dist/slack.transport-BTEwsREY.mjs.map +1 -0
  147. package/dist/{slack.transport-DrirgDNy.d.ts → slack.transport-C6E0l4kw.d.ts} +2 -2
  148. package/dist/{slack.transport-DrirgDNy.d.ts.map → slack.transport-C6E0l4kw.d.ts.map} +1 -1
  149. package/dist/{slack.transport-Dlx3xvb7.js → slack.transport-UbYvOdKx.js} +67 -16
  150. package/dist/slack.transport-UbYvOdKx.js.map +1 -0
  151. package/dist/testing.d.mts +11 -4
  152. package/dist/testing.d.mts.map +1 -1
  153. package/dist/testing.d.ts +11 -4
  154. package/dist/testing.d.ts.map +1 -1
  155. package/dist/testing.js +38 -3
  156. package/dist/testing.js.map +1 -1
  157. package/dist/testing.mjs +38 -3
  158. package/dist/testing.mjs.map +1 -1
  159. package/dist/{transport.types-BJVL1QQl.d.ts → transport.types-CzJq4IrF.d.mts} +14 -3
  160. package/dist/transport.types-CzJq4IrF.d.mts.map +1 -0
  161. package/dist/{transport.types-BJVL1QQl.d.mts → transport.types-CzJq4IrF.d.ts} +14 -3
  162. package/dist/transport.types-CzJq4IrF.d.ts.map +1 -0
  163. package/dist/transports.d.mts +25 -13
  164. package/dist/transports.d.mts.map +1 -1
  165. package/dist/transports.d.ts +25 -13
  166. package/dist/transports.d.ts.map +1 -1
  167. package/dist/transports.js +15 -15
  168. package/dist/transports.mjs +6 -6
  169. package/dist/worker-entry.js +105 -24
  170. package/dist/worker.d.mts +1 -1
  171. package/dist/worker.d.ts +1 -1
  172. package/dist/worker.js +1 -1
  173. package/dist/worker.mjs +1 -1
  174. package/dist/{worker.transport-BuPwBi3d.d.ts → worker.transport-B9mCQSM1.d.ts} +7 -2
  175. package/dist/worker.transport-B9mCQSM1.d.ts.map +1 -0
  176. package/dist/{worker.transport-hJay1wxA.d.mts → worker.transport-Bl8lFRck.d.mts} +7 -2
  177. package/dist/worker.transport-Bl8lFRck.d.mts.map +1 -0
  178. package/dist/{worker.transport-DLVA4y2e.mjs → worker.transport-BmmOKb1-.mjs} +13 -10
  179. package/dist/worker.transport-BmmOKb1-.mjs.map +1 -0
  180. package/dist/{worker.transport-C6GNl_r2.js → worker.transport-DxxbqQCG.js} +13 -10
  181. package/dist/worker.transport-DxxbqQCG.js.map +1 -0
  182. package/package.json +33 -65
  183. package/dist/async-context-B05ArHgM.mjs.map +0 -1
  184. package/dist/async-context-DTQgi8f7.js.map +0 -1
  185. package/dist/azure-monitor.transport-BPbgjATu.mjs.map +0 -1
  186. package/dist/azure-monitor.transport-CwbOLZHB.js.map +0 -1
  187. package/dist/builder-D70z6kjj.mjs.map +0 -1
  188. package/dist/builder-M7NoaugM.js.map +0 -1
  189. package/dist/cloudwatch.transport-B2wKQFF7.js.map +0 -1
  190. package/dist/cloudwatch.transport-BTvYfLs9.mjs.map +0 -1
  191. package/dist/entry-fields-CGuvT2R0.mjs.map +0 -1
  192. package/dist/entry-fields-RLd1jCoR.js.map +0 -1
  193. package/dist/gcp.transport-CGKopAhQ.mjs.map +0 -1
  194. package/dist/gcp.transport-KZe9hCAR.js.map +0 -1
  195. package/dist/index-DwozbmB4.d.ts.map +0 -1
  196. package/dist/index-xqaJtAZn.d.mts.map +0 -1
  197. package/dist/internal-log-b0Vp4qrB.js.map +0 -1
  198. package/dist/internal-log-uCSd1PMc.mjs.map +0 -1
  199. package/dist/logitron-logger-D2B75Oc7.d.ts.map +0 -1
  200. package/dist/logitron-logger-oEd6wNhf.d.mts.map +0 -1
  201. package/dist/safe-stringify-BMRl3EX3.mjs.map +0 -1
  202. package/dist/safe-stringify-DLz1Kz3o.js.map +0 -1
  203. package/dist/search-BH33XhuU.d.mts.map +0 -1
  204. package/dist/search-CWMVYC-R.mjs.map +0 -1
  205. package/dist/search-CazwyX5Z.d.ts.map +0 -1
  206. package/dist/search-DUfWuzFP.js.map +0 -1
  207. package/dist/secret-config-BegMLkYl.mjs.map +0 -1
  208. package/dist/secret-config-CBZBs0p1.js.map +0 -1
  209. package/dist/slack.transport-Ce96JBDk.mjs.map +0 -1
  210. package/dist/slack.transport-Dlx3xvb7.js.map +0 -1
  211. package/dist/transport.manager-DkcEOlqV.js.map +0 -1
  212. package/dist/transport.manager-eZ_EeC00.mjs.map +0 -1
  213. package/dist/transport.types-BJVL1QQl.d.mts.map +0 -1
  214. package/dist/transport.types-BJVL1QQl.d.ts.map +0 -1
  215. package/dist/worker.transport-BuPwBi3d.d.ts.map +0 -1
  216. package/dist/worker.transport-C6GNl_r2.js.map +0 -1
  217. package/dist/worker.transport-DLVA4y2e.mjs.map +0 -1
  218. package/dist/worker.transport-hJay1wxA.d.mts.map +0 -1
@@ -1,100 +1,4 @@
1
- import { b as LoggerWithLevels, f as ILogger, h as LogEntry, p as ILoggerDefault, v as LogLevelString, x as NamespaceLevels, y as LoggerConfig } from "./index-DwozbmB4.js";
2
- interface LogixiaPlugin {
3
- /**
4
- * Unique plugin name.
5
- * logixia uses this to prevent duplicate registration of the same plugin
6
- * on the same logger instance.
7
- */
8
- name: string;
9
- /**
10
- * Called once immediately after `logger.use(plugin)` is called.
11
- * Use for one-time setup (open connections, allocate buffers, etc.).
12
- * Async `onInit` errors are silently swallowed to avoid blocking the logger.
13
- */
14
- onInit?(): void | Promise<void>;
15
- /**
16
- * Called for every log entry **after** PII redaction and **before** transport
17
- * dispatch. Plugins run in registration order; each receives the (possibly
18
- * modified) output of the previous plugin.
19
- *
20
- * - Return the entry (modified or unchanged) to continue processing.
21
- * - Return `null` to **drop** the entry — no transport will receive it.
22
- *
23
- * Errors thrown inside `onLog` are swallowed so a buggy plugin cannot crash
24
- * logging; the chain continues with the previous (un-transformed) entry.
25
- */
26
- onLog?(entry: LogEntry): LogEntry | null | Promise<LogEntry | null>;
27
- /**
28
- * Called when a transport write fails (after any configured retries).
29
- * Useful for routing transport errors to an alerting or metrics system.
30
- * Errors thrown inside `onError` are silently swallowed.
31
- */
32
- onError?(error: Error, entry?: LogEntry): void | Promise<void>;
33
- /**
34
- * Called when the logger is closed via `logger.close()` or the process
35
- * receives a shutdown signal (when `gracefulShutdown` is enabled).
36
- * Errors thrown inside `onShutdown` are silently swallowed.
37
- */
38
- onShutdown?(): void | Promise<void>;
39
- }
40
- /**
41
- * Holds an ordered list of `LogixiaPlugin` instances and dispatches lifecycle
42
- * events to them. One registry is created per logger instance; a global
43
- * singleton is also exported for process-wide registration.
44
- */
45
- declare class PluginRegistry {
46
- private readonly _plugins;
47
- /**
48
- * Register a plugin. Silently skips if a plugin with the same `name` is
49
- * already registered on this registry.
50
- */
51
- register(plugin: LogixiaPlugin): void;
52
- /** Remove a previously registered plugin by name. No-op if not found. */
53
- unregister(name: string): void;
54
- /** Returns `true` if a plugin with the given name is registered. */
55
- has(name: string): boolean;
56
- /** Number of currently registered plugins. */
57
- get size(): number;
58
- /**
59
- * Run all `onLog` hooks in order.
60
- * Returns `null` if any plugin cancels the entry; otherwise returns the
61
- * (possibly transformed) entry.
62
- */
63
- runOnLog(entry: LogEntry): Promise<LogEntry | null>;
64
- /**
65
- * Notify all `onError` hooks.
66
- * Errors thrown inside hooks are swallowed to prevent error cascades.
67
- */
68
- runOnError(error: Error, entry?: LogEntry): Promise<void>;
69
- /** Run all `onShutdown` hooks concurrently. Hook errors are swallowed. */
70
- runOnShutdown(): Promise<void>;
71
- }
72
- /**
73
- * Module-level singleton registry.
74
- *
75
- * Plugins registered here are automatically copied into every **new** logger
76
- * instance created after the registration. Already-created loggers are not
77
- * retroactively affected — use `logger.use(plugin)` for per-instance control.
78
- */
79
- declare const globalPluginRegistry: PluginRegistry;
80
- /**
81
- * Register a plugin in the global registry so it applies to every future
82
- * logger instance.
83
- *
84
- * @example
85
- * ```ts
86
- * import { usePlugin } from 'logixia';
87
- *
88
- * usePlugin({
89
- * name: 'audit-sink',
90
- * onLog(entry) {
91
- * if (entry.level === 'error') auditQueue.push(entry);
92
- * return entry;
93
- * },
94
- * });
95
- * ```
96
- */
97
- declare function usePlugin(plugin: LogixiaPlugin): void;
1
+ import { C as LogixiaLoggerInstance, L as LogixiaPlugin, b as LoggerConfig, p as ILoggerDefault, w as NamespaceLevels, x as LoggerWithLevels, y as LogLevelString } from "./index-BSTtFcQ6.mjs";
98
2
  /**
99
3
  * The logixia logger. Create instances with {@link createLogger} so that custom
100
4
  * levels from `levelOptions.levels` also become methods.
@@ -147,6 +51,10 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
147
51
  private readonly _pluginRegistry;
148
52
  /** Set on loggers created by child(); they borrow the root's transports, sampler and plugins. */
149
53
  private readonly _parent;
54
+ /** Namespace level map shared by a logger and all its children; `version` bumps on every change. */
55
+ private readonly _nsStore;
56
+ /** Store version this logger's threshold was last computed against. */
57
+ private _nsSeenVersion;
150
58
  constructor(config: TConfig, context?: string, parent?: LogixiaLogger<TConfig>);
151
59
  private setupGracefulShutdown;
152
60
  private createCustomLevelMethods;
@@ -225,6 +133,10 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
225
133
  * (flip a module's verbosity live to chase a bug without raising global
226
134
  * volume). The new map fully replaces the previous one.
227
135
  *
136
+ * The map is shared by a logger and all its `child()` loggers: calling this on
137
+ * a child changes the whole family, and existing children pick the change up on
138
+ * their next log call.
139
+ *
228
140
  * @example
229
141
  * ```ts
230
142
  * // Turn on debug for the DB layer only, live:
@@ -236,6 +148,7 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
236
148
  patchNamespaceLevels(levels: NamespaceLevels): void;
237
149
  /** Return a copy of the current per-namespace level map. */
238
150
  getNamespaceLevels(): NamespaceLevels;
151
+ private _syncNamespaceLevels;
239
152
  /** Set the context label (e.g. a class or module name) attached to every subsequent entry. */
240
153
  setContext(context: string): void;
241
154
  /** Return the current context label, or `undefined` if none is set. */
@@ -273,7 +186,7 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
273
186
  * await req.info('started'); // entry includes requestId
274
187
  * ```
275
188
  */
276
- child(context: string, data?: Record<string, unknown>): ILogger;
189
+ child(context: string, data?: Record<string, unknown>): LogixiaLoggerInstance;
277
190
  /**
278
191
  * Register a plugin on this logger instance.
279
192
  *
@@ -361,5 +274,5 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
361
274
  * ```
362
275
  */
363
276
  declare function createLogger<T extends LoggerConfig<Record<string, number>>>(config: T, context?: string): LoggerWithLevels<T>;
364
- export { globalPluginRegistry as a, PluginRegistry as i, createLogger as n, usePlugin as o, LogixiaPlugin as r, LogixiaLogger as t };
365
- //# sourceMappingURL=logitron-logger-D2B75Oc7.d.ts.map
277
+ export { createLogger as n, LogixiaLogger as t };
278
+ //# sourceMappingURL=logitron-logger-D22Yb61K.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logitron-logger-D22Yb61K.d.mts","names":[],"sources":["../src/core/logitron-logger.ts"],"mappings":";;;;;;;;;;;;;;cAyLa,cACX,gBAAgB,aAAa,0BAA0B,yBAC5C;GAEV;UAEO;UACA;UACA;UACA;UACA;UACA;;mBAGS;;UAIT;;UAEA;;UAEA;;mBAES;;UAET;;UAEA;;UAEA;;;;;UAKA;;UAEA;;UAEA;;UAEA;;mBAES;;mBAEA;;mBAEA;;UAET;EAEI,YAAA,QAAQ,SAAS,kBAAkB,SAAS,cAAc;UA4G9D;UAiBA;;;;;UAsBA;;;;;;EA2HF,MAAM,yBAAyB,OAAO,OAAO,0BAA0B;;EAYvE,KAAK,iBAAiB,OAAO,0BAA0B;;EAKvD,KAAK,iBAAiB,OAAO,0BAA0B;;EAKvD,MAAM,iBAAiB,OAAO,0BAA0B;;EAKxD,MAAM,iBAAiB,OAAO,0BAA0B;;EAKxD,QAAQ,iBAAiB,OAAO,0BAA0B;;;;;;EAS1D,SAAS,eAAe,iBAAiB,OAAO,0BAA0B;;EAOhF,KAAK;;;;;;;;;;;;;EAgBC,QAAQ,gBAAgB;EAmBxB,UAAU,GAAG,eAAe,UAAU,QAAQ,KAAK,QAAQ;;;;;;;;;;;;EAyBjE,SAAS,OAAO;;;;;;EAuBhB,YAAY;UAIJ;;;;;UAQA;;;;;;;;;;;;;;;;;;;EAkCR,mBAAmB,QAAQ;;EAS3B,qBAAqB,QAAQ;;EAO7B,sBAAsB;UAId;;EAMR,WAAW;;EAKX;;EAOA,YAAY;;EASZ,aAAa;;EASb,eAAe;;EASf,iBAAiB;;EAmBjB;;EAQA;;EAMA;;EAMA,mBAAmB,qBAAqB;;EAMxC,mBAAmB;EAMnB;;EAMA;;;;;;;;;;;;;EAkBA,MAAM,iBAAiB,OAAO,0BAA0B;;;;;;;;;;;;EA0BxD,IAAI,QAAQ;;;;;EASZ,MAAM;;EAQA,SAAS;;EAKT,eAAe;IAAU;IAAkB,SAAS;;;;;;;;;;;;;;;;EAqBpD,SAAS;UAsBD;;;;;UA8GN;;;;;;;;;;;UAgBA;UAwCA;UAgDA;;;;;;;;UAcA;;;UAuBM;;;;;;;;;;;;;;;;;;iBA+BA,aAAa,UAAU,aAAa,yBAClD,QAAQ,GACR,mBACC,iBAAiB"}
@@ -1,100 +1,4 @@
1
- import { b as LoggerWithLevels, f as ILogger, h as LogEntry, p as ILoggerDefault, v as LogLevelString, x as NamespaceLevels, y as LoggerConfig } from "./index-xqaJtAZn.mjs";
2
- interface LogixiaPlugin {
3
- /**
4
- * Unique plugin name.
5
- * logixia uses this to prevent duplicate registration of the same plugin
6
- * on the same logger instance.
7
- */
8
- name: string;
9
- /**
10
- * Called once immediately after `logger.use(plugin)` is called.
11
- * Use for one-time setup (open connections, allocate buffers, etc.).
12
- * Async `onInit` errors are silently swallowed to avoid blocking the logger.
13
- */
14
- onInit?(): void | Promise<void>;
15
- /**
16
- * Called for every log entry **after** PII redaction and **before** transport
17
- * dispatch. Plugins run in registration order; each receives the (possibly
18
- * modified) output of the previous plugin.
19
- *
20
- * - Return the entry (modified or unchanged) to continue processing.
21
- * - Return `null` to **drop** the entry — no transport will receive it.
22
- *
23
- * Errors thrown inside `onLog` are swallowed so a buggy plugin cannot crash
24
- * logging; the chain continues with the previous (un-transformed) entry.
25
- */
26
- onLog?(entry: LogEntry): LogEntry | null | Promise<LogEntry | null>;
27
- /**
28
- * Called when a transport write fails (after any configured retries).
29
- * Useful for routing transport errors to an alerting or metrics system.
30
- * Errors thrown inside `onError` are silently swallowed.
31
- */
32
- onError?(error: Error, entry?: LogEntry): void | Promise<void>;
33
- /**
34
- * Called when the logger is closed via `logger.close()` or the process
35
- * receives a shutdown signal (when `gracefulShutdown` is enabled).
36
- * Errors thrown inside `onShutdown` are silently swallowed.
37
- */
38
- onShutdown?(): void | Promise<void>;
39
- }
40
- /**
41
- * Holds an ordered list of `LogixiaPlugin` instances and dispatches lifecycle
42
- * events to them. One registry is created per logger instance; a global
43
- * singleton is also exported for process-wide registration.
44
- */
45
- declare class PluginRegistry {
46
- private readonly _plugins;
47
- /**
48
- * Register a plugin. Silently skips if a plugin with the same `name` is
49
- * already registered on this registry.
50
- */
51
- register(plugin: LogixiaPlugin): void;
52
- /** Remove a previously registered plugin by name. No-op if not found. */
53
- unregister(name: string): void;
54
- /** Returns `true` if a plugin with the given name is registered. */
55
- has(name: string): boolean;
56
- /** Number of currently registered plugins. */
57
- get size(): number;
58
- /**
59
- * Run all `onLog` hooks in order.
60
- * Returns `null` if any plugin cancels the entry; otherwise returns the
61
- * (possibly transformed) entry.
62
- */
63
- runOnLog(entry: LogEntry): Promise<LogEntry | null>;
64
- /**
65
- * Notify all `onError` hooks.
66
- * Errors thrown inside hooks are swallowed to prevent error cascades.
67
- */
68
- runOnError(error: Error, entry?: LogEntry): Promise<void>;
69
- /** Run all `onShutdown` hooks concurrently. Hook errors are swallowed. */
70
- runOnShutdown(): Promise<void>;
71
- }
72
- /**
73
- * Module-level singleton registry.
74
- *
75
- * Plugins registered here are automatically copied into every **new** logger
76
- * instance created after the registration. Already-created loggers are not
77
- * retroactively affected — use `logger.use(plugin)` for per-instance control.
78
- */
79
- declare const globalPluginRegistry: PluginRegistry;
80
- /**
81
- * Register a plugin in the global registry so it applies to every future
82
- * logger instance.
83
- *
84
- * @example
85
- * ```ts
86
- * import { usePlugin } from 'logixia';
87
- *
88
- * usePlugin({
89
- * name: 'audit-sink',
90
- * onLog(entry) {
91
- * if (entry.level === 'error') auditQueue.push(entry);
92
- * return entry;
93
- * },
94
- * });
95
- * ```
96
- */
97
- declare function usePlugin(plugin: LogixiaPlugin): void;
1
+ import { C as LogixiaLoggerInstance, L as LogixiaPlugin, b as LoggerConfig, p as ILoggerDefault, w as NamespaceLevels, x as LoggerWithLevels, y as LogLevelString } from "./index-CFDGkI8n.js";
98
2
  /**
99
3
  * The logixia logger. Create instances with {@link createLogger} so that custom
100
4
  * levels from `levelOptions.levels` also become methods.
@@ -147,6 +51,10 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
147
51
  private readonly _pluginRegistry;
148
52
  /** Set on loggers created by child(); they borrow the root's transports, sampler and plugins. */
149
53
  private readonly _parent;
54
+ /** Namespace level map shared by a logger and all its children; `version` bumps on every change. */
55
+ private readonly _nsStore;
56
+ /** Store version this logger's threshold was last computed against. */
57
+ private _nsSeenVersion;
150
58
  constructor(config: TConfig, context?: string, parent?: LogixiaLogger<TConfig>);
151
59
  private setupGracefulShutdown;
152
60
  private createCustomLevelMethods;
@@ -225,6 +133,10 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
225
133
  * (flip a module's verbosity live to chase a bug without raising global
226
134
  * volume). The new map fully replaces the previous one.
227
135
  *
136
+ * The map is shared by a logger and all its `child()` loggers: calling this on
137
+ * a child changes the whole family, and existing children pick the change up on
138
+ * their next log call.
139
+ *
228
140
  * @example
229
141
  * ```ts
230
142
  * // Turn on debug for the DB layer only, live:
@@ -236,6 +148,7 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
236
148
  patchNamespaceLevels(levels: NamespaceLevels): void;
237
149
  /** Return a copy of the current per-namespace level map. */
238
150
  getNamespaceLevels(): NamespaceLevels;
151
+ private _syncNamespaceLevels;
239
152
  /** Set the context label (e.g. a class or module name) attached to every subsequent entry. */
240
153
  setContext(context: string): void;
241
154
  /** Return the current context label, or `undefined` if none is set. */
@@ -273,7 +186,7 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
273
186
  * await req.info('started'); // entry includes requestId
274
187
  * ```
275
188
  */
276
- child(context: string, data?: Record<string, unknown>): ILogger;
189
+ child(context: string, data?: Record<string, unknown>): LogixiaLoggerInstance;
277
190
  /**
278
191
  * Register a plugin on this logger instance.
279
192
  *
@@ -361,5 +274,5 @@ declare class LogixiaLogger<TConfig extends LoggerConfig<Record<string, number>>
361
274
  * ```
362
275
  */
363
276
  declare function createLogger<T extends LoggerConfig<Record<string, number>>>(config: T, context?: string): LoggerWithLevels<T>;
364
- export { globalPluginRegistry as a, PluginRegistry as i, createLogger as n, usePlugin as o, LogixiaPlugin as r, LogixiaLogger as t };
365
- //# sourceMappingURL=logitron-logger-oEd6wNhf.d.mts.map
277
+ export { createLogger as n, LogixiaLogger as t };
278
+ //# sourceMappingURL=logitron-logger-D_MYYrQt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logitron-logger-D_MYYrQt.d.ts","names":[],"sources":["../src/core/logitron-logger.ts"],"mappings":";;;;;;;;;;;;;;cAyLa,cACX,gBAAgB,aAAa,0BAA0B,yBAC5C;GAEV;UAEO;UACA;UACA;UACA;UACA;UACA;;mBAGS;;UAIT;;UAEA;;UAEA;;mBAES;;UAET;;UAEA;;UAEA;;;;;UAKA;;UAEA;;UAEA;;UAEA;;mBAES;;mBAEA;;mBAEA;;UAET;EAEI,YAAA,QAAQ,SAAS,kBAAkB,SAAS,cAAc;UA4G9D;UAiBA;;;;;UAsBA;;;;;;EA2HF,MAAM,yBAAyB,OAAO,OAAO,0BAA0B;;EAYvE,KAAK,iBAAiB,OAAO,0BAA0B;;EAKvD,KAAK,iBAAiB,OAAO,0BAA0B;;EAKvD,MAAM,iBAAiB,OAAO,0BAA0B;;EAKxD,MAAM,iBAAiB,OAAO,0BAA0B;;EAKxD,QAAQ,iBAAiB,OAAO,0BAA0B;;;;;;EAS1D,SAAS,eAAe,iBAAiB,OAAO,0BAA0B;;EAOhF,KAAK;;;;;;;;;;;;;EAgBC,QAAQ,gBAAgB;EAmBxB,UAAU,GAAG,eAAe,UAAU,QAAQ,KAAK,QAAQ;;;;;;;;;;;;EAyBjE,SAAS,OAAO;;;;;;EAuBhB,YAAY;UAIJ;;;;;UAQA;;;;;;;;;;;;;;;;;;;EAkCR,mBAAmB,QAAQ;;EAS3B,qBAAqB,QAAQ;;EAO7B,sBAAsB;UAId;;EAMR,WAAW;;EAKX;;EAOA,YAAY;;EASZ,aAAa;;EASb,eAAe;;EASf,iBAAiB;;EAmBjB;;EAQA;;EAMA;;EAMA,mBAAmB,qBAAqB;;EAMxC,mBAAmB;EAMnB;;EAMA;;;;;;;;;;;;;EAkBA,MAAM,iBAAiB,OAAO,0BAA0B;;;;;;;;;;;;EA0BxD,IAAI,QAAQ;;;;;EASZ,MAAM;;EAQA,SAAS;;EAKT,eAAe;IAAU;IAAkB,SAAS;;;;;;;;;;;;;;;;EAqBpD,SAAS;UAsBD;;;;;UA8GN;;;;;;;;;;;UAgBA;UAwCA;UAgDA;;;;;;;;UAcA;;;UAuBM;;;;;;;;;;;;;;;;;;iBA+BA,aAAa,UAAU,aAAa,yBAClD,QAAQ,GACR,mBACC,iBAAiB"}
@@ -1,4 +1,5 @@
1
- import { u as IBaseLogger } from "./index-xqaJtAZn.mjs";
1
+ import { n as HttpResponseShape, t as HttpRequestShape } from "./http-shapes-BnBkJcid.mjs";
2
+ import { u as IBaseLogger } from "./index-BSTtFcQ6.mjs";
2
3
  interface HttpLoggerOptions {
3
4
  /**
4
5
  * Skip logging for a request. Called before any I/O.
@@ -53,22 +54,8 @@ interface HttpLoggerOptions {
53
54
  /** Log level for error responses (status ≥ 500). Default: 'error'. */
54
55
  errorLevel?: string;
55
56
  }
56
- interface IncomingRequest {
57
- method?: string;
58
- url?: string;
59
- headers?: Record<string, string | string[] | undefined>;
60
- body?: unknown;
61
- socket?: {
62
- remoteAddress?: string;
63
- };
64
- ip?: string;
65
- }
66
- interface OutgoingResponse {
67
- statusCode?: number;
68
- on?: (event: string, cb: () => void) => void;
69
- once?: (event: string, cb: () => void) => void;
70
- getHeader?: (name: string) => string | number | string[] | undefined;
71
- }
57
+ type IncomingRequest = HttpRequestShape;
58
+ type OutgoingResponse = HttpResponseShape;
72
59
  /**
73
60
  * Create an Express / Connect compatible middleware that replaces Morgan.
74
61
  */
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.mts","names":[],"sources":["../src/middleware/http-logger.ts"],"mappings":";UAgCiB;;;;;EAKf,QAAQ,KAAK;;;;;;EAMb;;EAEA;;;;;EAKA;;;;;EAKA,eAAe,KAAK,oBAAoB;;;;;;;;EAQxC;;;;;;EAMA;;;;;;EAMA;;;;;EAKA;;EAEA;;EAEA;;UAIe;EACf;EACA;EACA,UAAU;EACV;EACA;IAAW;;EACX;;UAGe;EACf;EACA,MAAM,eAAe;EACrB,QAAQ,eAAe;EACvB,aAAa;;;;;wBAkGC,wBACd,QAAQ,aACR,UAAS,qBACP,KAAK,iBAAiB,KAAK,kBAAkB;UAqFhC;EACf,UAAU,cAAc,KAAK,cAAc,gBAAgB;;;;;;;;;;wBAW7C,oBAAoB,QAAQ,aAAa,UAAS,sBAAsB,SAY/C,iBAAe,gBAAgB"}
1
+ {"version":3,"file":"middleware.d.mts","names":[],"sources":["../src/middleware/http-logger.ts"],"mappings":";;UAuCiB;;;;;EAKf,QAAQ,KAAK;;;;;;EAMb;;EAEA;;;;;EAKA;;;;;EAKA,eAAe,KAAK,oBAAoB;;;;;;;;EAQxC;;;;;;EAMA;;;;;;EAMA;;;;;EAKA;;EAEA;;EAEA;;KAIU,kBAAkB;KAClB,mBAAmB;;;;wBA+Df,wBACd,QAAQ,aACR,UAAS,qBACP,KAAK,iBAAiB,KAAK,kBAAkB;UAqFhC;EACf,UAAU,cAAc,KAAK,cAAc,gBAAgB;;;;;;;;;;wBAW7C,oBAAoB,QAAQ,aAAa,UAAS,sBAAsB,SAY/C,iBAAe,gBAAgB"}
@@ -1,4 +1,5 @@
1
- import { u as IBaseLogger } from "./index-DwozbmB4.js";
1
+ import { n as HttpResponseShape, t as HttpRequestShape } from "./http-shapes-BnBkJcid.js";
2
+ import { u as IBaseLogger } from "./index-CFDGkI8n.js";
2
3
  interface HttpLoggerOptions {
3
4
  /**
4
5
  * Skip logging for a request. Called before any I/O.
@@ -53,22 +54,8 @@ interface HttpLoggerOptions {
53
54
  /** Log level for error responses (status ≥ 500). Default: 'error'. */
54
55
  errorLevel?: string;
55
56
  }
56
- interface IncomingRequest {
57
- method?: string;
58
- url?: string;
59
- headers?: Record<string, string | string[] | undefined>;
60
- body?: unknown;
61
- socket?: {
62
- remoteAddress?: string;
63
- };
64
- ip?: string;
65
- }
66
- interface OutgoingResponse {
67
- statusCode?: number;
68
- on?: (event: string, cb: () => void) => void;
69
- once?: (event: string, cb: () => void) => void;
70
- getHeader?: (name: string) => string | number | string[] | undefined;
71
- }
57
+ type IncomingRequest = HttpRequestShape;
58
+ type OutgoingResponse = HttpResponseShape;
72
59
  /**
73
60
  * Create an Express / Connect compatible middleware that replaces Morgan.
74
61
  */
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","names":[],"sources":["../src/middleware/http-logger.ts"],"mappings":";UAgCiB;;;;;EAKf,QAAQ,KAAK;;;;;;EAMb;;EAEA;;;;;EAKA;;;;;EAKA,eAAe,KAAK,oBAAoB;;;;;;;;EAQxC;;;;;;EAMA;;;;;;EAMA;;;;;EAKA;;EAEA;;EAEA;;UAIe;EACf;EACA;EACA,UAAU;EACV;EACA;IAAW;;EACX;;UAGe;EACf;EACA,MAAM,eAAe;EACrB,QAAQ,eAAe;EACvB,aAAa;;;;;wBAkGC,wBACd,QAAQ,aACR,UAAS,qBACP,KAAK,iBAAiB,KAAK,kBAAkB;UAqFhC;EACf,UAAU,cAAc,KAAK,cAAc,gBAAgB;;;;;;;;;;wBAW7C,oBAAoB,QAAQ,aAAa,UAAS,sBAAsB,SAY/C,iBAAe,gBAAgB"}
1
+ {"version":3,"file":"middleware.d.ts","names":[],"sources":["../src/middleware/http-logger.ts"],"mappings":";;UAuCiB;;;;;EAKf,QAAQ,KAAK;;;;;;EAMb;;EAEA;;;;;EAKA;;;;;EAKA,eAAe,KAAK,oBAAoB;;;;;;;;EAQxC;;;;;;EAMA;;;;;;EAMA;;;;;EAKA;;EAEA;;EAEA;;KAIU,kBAAkB;KAClB,mBAAmB;;;;wBA+Df,wBACd,QAAQ,aACR,UAAS,qBACP,KAAK,iBAAiB,KAAK,kBAAkB;UAqFhC;EACf,UAAU,cAAc,KAAK,cAAc,gBAAgB;;;;;;;;;;wBAW7C,oBAAoB,QAAQ,aAAa,UAAS,sBAAsB,SAY/C,iBAAe,gBAAgB"}
@@ -1,42 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_request_trace = require("./request-trace-wTJdKp6-.js");
3
- const require_internal_log = require("./internal-log-b0Vp4qrB.js");
4
- const require_safe_stringify = require("./safe-stringify-DLz1Kz3o.js");
2
+ const require_own_property = require("./own-property-BWkibepN.js");
3
+ const require_request_trace = require("./request-trace-ByujsUZk.js");
4
+ const require_internal_log = require("./internal-log-By4Govmu.js");
5
+ const require_safe_stringify = require("./safe-stringify-J3sh-7YM.js");
5
6
  // src/middleware/http-logger.ts
6
- function buildNameSet(defaults, extra) {
7
- return new Set([...defaults, ...extra ?? []].map((n) => n.toLowerCase()));
8
- }
9
- function safeDecode(value) {
10
- try {
11
- return decodeURIComponent(value.replace(/\+/g, " "));
12
- } catch {
13
- return value;
14
- }
15
- }
16
- /** Replace the values of sensitive query parameters, leaving the rest verbatim. */
17
- function sanitizeUrl(url, redactParams) {
18
- const q = url.indexOf("?");
19
- if (q === -1) return url;
20
- const hashAt = url.indexOf("#", q);
21
- const query = url.slice(q + 1, hashAt === -1 ? void 0 : hashAt);
22
- const hash = hashAt === -1 ? "" : url.slice(hashAt);
23
- const cleaned = query.split("&").map((pair) => {
24
- const eq = pair.indexOf("=");
25
- const rawKey = eq === -1 ? pair : pair.slice(0, eq);
26
- const decoded = safeDecode(rawKey);
27
- const bracket = decoded.indexOf("[");
28
- const name = (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();
29
- return redactParams.has(name) && eq !== -1 ? `${rawKey}=[REDACTED]` : pair;
30
- }).join("&");
31
- return `${url.slice(0, q)}?${cleaned}${hash}`;
32
- }
33
7
  function sanitizeHeaders(headers, redactSet) {
34
8
  if (!headers) return {};
35
9
  const out = {};
36
- for (const [k, v] of Object.entries(headers)) {
37
- if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
38
- out[k] = redactSet.has(k.toLowerCase()) ? "[REDACTED]" : v;
39
- }
10
+ for (const [k, v] of Object.entries(headers)) require_own_property.assignOwn(out, k, redactSet.has(k.toLowerCase()) ? "[REDACTED]" : v);
40
11
  return out;
41
12
  }
42
13
  /** Fire-and-forget a logger call; a failing log must never become an unhandled rejection. */
@@ -45,8 +16,8 @@ function fire(result) {
45
16
  }
46
17
  function resolveNameSets(options) {
47
18
  return {
48
- headers: buildNameSet(require_internal_log.DEFAULT_REDACT_HEADERS, options.redactHeaders),
49
- queryParams: buildNameSet(require_internal_log.DEFAULT_REDACT_QUERY_PARAMS, options.redactQueryParams)
19
+ headers: require_internal_log.buildNameSet(require_internal_log.DEFAULT_REDACT_HEADERS, options.redactHeaders),
20
+ queryParams: require_internal_log.buildNameSet(require_internal_log.DEFAULT_REDACT_QUERY_PARAMS, options.redactQueryParams)
50
21
  };
51
22
  }
52
23
  function buildBaseFields(req, traceId, options, { headers: redactSet, queryParams: paramSet }) {
@@ -54,7 +25,7 @@ function buildBaseFields(req, traceId, options, { headers: redactSet, queryParam
54
25
  const fields = {
55
26
  traceId,
56
27
  method: ((_req$method = req.method) === null || _req$method === void 0 ? void 0 : _req$method.toUpperCase()) ?? "UNKNOWN",
57
- url: sanitizeUrl(req.url ?? "/", paramSet),
28
+ url: require_internal_log.redactUrlQuery(req.url ?? "/", paramSet),
58
29
  ip: req.ip ?? ((_req$socket = req.socket) === null || _req$socket === void 0 ? void 0 : _req$socket.remoteAddress) ?? "unknown",
59
30
  headers: sanitizeHeaders(req.headers, redactSet)
60
31
  };
@@ -217,13 +188,12 @@ function pruneValue(value, budget, depth, ancestors) {
217
188
  }
218
189
  const record = {};
219
190
  for (const [k, v] of Object.entries(value)) {
220
- if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
221
191
  if (budget.left <= 0) {
222
192
  record["…"] = TRUNCATED;
223
193
  break;
224
194
  }
225
195
  budget.left -= k.length;
226
- record[k] = pruneValue(v, budget, depth + 1, ancestors);
196
+ require_own_property.assignOwn(record, k, pruneValue(v, budget, depth + 1, ancestors));
227
197
  }
228
198
  return record;
229
199
  } catch {
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","names":["internalError","DEFAULT_REDACT_HEADERS","DEFAULT_REDACT_QUERY_PARAMS","resolveTraceHeaders","resolveRequestTraceId","stringifyForLog"],"sources":["../src/middleware/http-logger.ts"],"sourcesContent":["/**\n * HTTP request/response logging middleware — Morgan replacement.\n *\n * Fixes every documented Morgan bug:\n * - statusCode always captured correctly, even for requests > 20 s\n * - Logs request START (with traceId) and response FINISH (with duration)\n * - Captures errors before and after response\n * - Auto-redacts Authorization / Cookie / Set-Cookie headers\n * - Slow-request warnings\n * - Skip predicates for health-check routes / static assets\n *\n * @example Express\n * ```ts\n * import { createExpressMiddleware } from 'logixia/middleware';\n * app.use(createExpressMiddleware(logger));\n * ```\n *\n * @example Fastify\n * ```ts\n * import { createFastifyPlugin } from 'logixia/middleware';\n * await fastify.register(createFastifyPlugin(logger));\n * ```\n */\n\nimport type { IBaseLogger } from '../types';\nimport { internalError } from '../utils/internal-log';\nimport { resolveRequestTraceId, resolveTraceHeaders } from '../utils/request-trace';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport { DEFAULT_REDACT_HEADERS, DEFAULT_REDACT_QUERY_PARAMS } from '../utils/sensitive-names';\n\n// ── Shared types ─────────────────────────────────────────────────────────────\n\nexport interface HttpLoggerOptions {\n /**\n * Skip logging for a request. Called before any I/O.\n * @example `skip: (req) => req.url === '/health'`\n */\n skip?: (req: IncomingRequest) => boolean;\n /**\n * Log request body (POST/PUT/PATCH). Capped at `bodyMaxBytes` (default: 4096).\n * Redaction still applies to the captured body.\n * Default: false.\n */\n logBody?: boolean;\n /** Max bytes of body to capture. Default: 4096. */\n bodyMaxBytes?: number;\n /**\n * Emit a WARN log when a request duration exceeds this threshold (ms).\n * Default: 1000.\n */\n slowRequestThresholdMs?: number;\n /**\n * Additional fields to include in every log entry.\n * @example `extraFields: (req) => ({ tenantId: req.headers['x-tenant-id'] })`\n */\n extraFields?: (req: IncomingRequest) => Record<string, unknown>;\n /**\n * Header name, or list of names tried in order, to read the trace ID from.\n * Default: the library-wide list (`DEFAULT_TRACE_HEADERS`: traceparent,\n * x-trace-id, x-request-id, x-correlation-id, trace-id).\n * If none is present, `request.id` is used when it is not a framework counter,\n * otherwise a UUID is generated and shared with the logixia context helpers.\n */\n traceIdHeader?: string | readonly string[];\n /**\n * Additional headers to redact from logged output. Merged with the defaults\n * (authorization, cookie, set-cookie, x-api-key, proxy-authorization,\n * x-auth-token, x-access-token, x-csrf-token, x-amz-security-token).\n */\n redactHeaders?: string[];\n /**\n * Additional query-string parameter names whose values are redacted from the\n * logged URL. Merged with the defaults (token, access_token, refresh_token,\n * code, password, secret, api_key, apikey, key, signature, sig, auth).\n */\n redactQueryParams?: string[];\n /**\n * Log level for request-start entries. Default: 'debug'.\n * Set to 'silent' to suppress request-start logs entirely.\n */\n requestLevel?: string;\n /** Log level for successful response entries. Default: 'info'. */\n responseLevel?: string;\n /** Log level for error responses (status ≥ 500). Default: 'error'. */\n errorLevel?: string;\n}\n\n// Minimal structural types so we don't need @types/express / fastify in core\nexport interface IncomingRequest {\n method?: string;\n url?: string;\n headers?: Record<string, string | string[] | undefined>;\n body?: unknown;\n socket?: { remoteAddress?: string };\n ip?: string;\n}\n\nexport interface OutgoingResponse {\n statusCode?: number;\n on?: (event: string, cb: () => void) => void;\n once?: (event: string, cb: () => void) => void;\n getHeader?: (name: string) => string | number | string[] | undefined;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction buildNameSet(defaults: readonly string[], extra: readonly string[] | undefined) {\n return new Set([...defaults, ...(extra ?? [])].map((n) => n.toLowerCase()));\n}\n\nfunction safeDecode(value: string): string {\n try {\n return decodeURIComponent(value.replace(/\\+/g, ' '));\n } catch {\n return value;\n }\n}\n\n/** Replace the values of sensitive query parameters, leaving the rest verbatim. */\nfunction sanitizeUrl(url: string, redactParams: Set<string>): string {\n const q = url.indexOf('?');\n if (q === -1) return url;\n const hashAt = url.indexOf('#', q);\n const query = url.slice(q + 1, hashAt === -1 ? undefined : hashAt);\n const hash = hashAt === -1 ? '' : url.slice(hashAt);\n const cleaned = query\n .split('&')\n .map((pair) => {\n const eq = pair.indexOf('=');\n const rawKey = eq === -1 ? pair : pair.slice(0, eq);\n const decoded = safeDecode(rawKey);\n const bracket = decoded.indexOf('[');\n const name = (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();\n return redactParams.has(name) && eq !== -1 ? `${rawKey}=[REDACTED]` : pair;\n })\n .join('&');\n return `${url.slice(0, q)}?${cleaned}${hash}`;\n}\n\nfunction sanitizeHeaders(\n headers: Record<string, string | string[] | undefined> | undefined,\n redactSet: Set<string>\n): Record<string, unknown> {\n if (!headers) return {};\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(headers)) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n out[k] = redactSet.has(k.toLowerCase()) ? '[REDACTED]' : v;\n }\n return out;\n}\n\n/** Fire-and-forget a logger call; a failing log must never become an unhandled rejection. */\nfunction fire(result: unknown): void {\n if (result && typeof (result as Promise<unknown>).catch === 'function') {\n (result as Promise<unknown>).catch((error: unknown) =>\n internalError('http-logger: log call failed', error)\n );\n }\n}\n\ninterface NameSets {\n headers: Set<string>;\n queryParams: Set<string>;\n}\n\nfunction resolveNameSets(options: HttpLoggerOptions): NameSets {\n return {\n headers: buildNameSet(DEFAULT_REDACT_HEADERS, options.redactHeaders),\n queryParams: buildNameSet(DEFAULT_REDACT_QUERY_PARAMS, options.redactQueryParams),\n };\n}\n\nfunction buildBaseFields(\n req: IncomingRequest,\n traceId: string,\n options: HttpLoggerOptions,\n { headers: redactSet, queryParams: paramSet }: NameSets\n): Record<string, unknown> {\n const fields: Record<string, unknown> = {\n traceId,\n method: req.method?.toUpperCase() ?? 'UNKNOWN',\n url: sanitizeUrl(req.url ?? '/', paramSet),\n ip: req.ip ?? req.socket?.remoteAddress ?? 'unknown',\n headers: sanitizeHeaders(req.headers, redactSet),\n };\n\n if (options.extraFields) {\n Object.assign(fields, options.extraFields(req));\n }\n\n return fields;\n}\n\n// ── Express middleware ────────────────────────────────────────────────────────\n\n/**\n * Create an Express / Connect compatible middleware that replaces Morgan.\n */\nexport function createExpressMiddleware(\n logger: IBaseLogger,\n options: HttpLoggerOptions = {}\n): (req: IncomingRequest, res: OutgoingResponse, next: () => void) => void {\n const {\n skip,\n logBody,\n bodyMaxBytes,\n traceIdHeader,\n requestLevel = 'debug',\n responseLevel = 'info',\n errorLevel = 'error',\n slowRequestThresholdMs = 1000,\n } = options;\n const nameSets = resolveNameSets(options);\n const traceHeaders = resolveTraceHeaders(traceIdHeader);\n\n return function logixiaHttpMiddleware(\n req: IncomingRequest,\n res: OutgoingResponse,\n next: () => void\n ): void {\n if (skip?.(req)) {\n next();\n return;\n }\n\n const traceId = resolveRequestTraceId(req, traceHeaders);\n const startMs = Date.now();\n const baseFields = buildBaseFields(req, traceId, options, nameSets);\n\n // Log request start\n if (requestLevel !== 'silent') {\n fire(\n logger.logLevel(requestLevel, 'request started', {\n ...baseFields,\n ...(logBody && req.body ? { body: truncateBody(req.body, bodyMaxBytes) } : {}),\n })\n );\n }\n\n // Hook into the response 'finish' event — fires after headers + body are sent.\n // This is what Morgan gets wrong for slow requests (it uses 'close' which may\n // fire before the status code is set on some Node versions).\n //\n // BOTH 'finish' and 'close' fire on a normal response, so guard against\n // logging the completion twice (which would duplicate the completed entry and\n // the slow-request warning).\n let completed = false;\n const onFinish = (): void => {\n if (completed) return;\n completed = true;\n\n const duration = Date.now() - startMs;\n const status = res.statusCode ?? 0;\n const level = status >= 500 ? errorLevel : responseLevel;\n\n fire(\n logger.logLevel(level, 'request completed', {\n ...baseFields,\n statusCode: status,\n duration,\n })\n );\n\n if (duration > slowRequestThresholdMs) {\n fire(\n logger.warn('slow request detected', {\n ...baseFields,\n statusCode: status,\n duration,\n threshold: slowRequestThresholdMs,\n })\n );\n }\n };\n\n res.once?.('finish', onFinish);\n // Fallback: also covers a client that disconnects before 'finish' fires. The\n // `completed` guard ensures a normal finish+close pair logs only once.\n res.once?.('close', onFinish);\n\n next();\n };\n}\n\n// ── Fastify plugin ─────────────────────────────────────────────────────────────\n\nexport interface FastifyInstance {\n addHook: (name: string, fn: (req: unknown, reply: unknown, done: () => void) => void) => void;\n}\n\n/**\n * Create a Fastify plugin (a function you pass to `fastify.register()`).\n *\n * @example\n * ```ts\n * await fastify.register(createFastifyPlugin(logger, { slowRequestThresholdMs: 500 }));\n * ```\n */\nexport function createFastifyPlugin(logger: IBaseLogger, options: HttpLoggerOptions = {}) {\n const {\n skip,\n traceIdHeader,\n requestLevel = 'debug',\n responseLevel = 'info',\n errorLevel = 'error',\n slowRequestThresholdMs = 1000,\n } = options;\n const nameSets = resolveNameSets(options);\n const traceHeaders = resolveTraceHeaders(traceIdHeader);\n\n function logixiaFastifyPlugin(fastify: FastifyInstance, _opts: unknown, done: () => void): void {\n fastify.addHook('onRequest', (request: unknown, _reply: unknown, hookDone: () => void) => {\n const req = request as IncomingRequest & { _logixiaStart?: number };\n if (skip?.(req)) {\n hookDone();\n return;\n }\n\n const traceId = resolveRequestTraceId(req, traceHeaders);\n req._logixiaStart = Date.now();\n\n if (requestLevel !== 'silent') {\n fire(\n logger.logLevel(\n requestLevel,\n 'request started',\n buildBaseFields(req, traceId, options, nameSets)\n )\n );\n }\n hookDone();\n });\n\n fastify.addHook('onResponse', (request: unknown, reply: unknown, hookDone: () => void) => {\n const req = request as IncomingRequest & { _logixiaStart?: number };\n if (req._logixiaStart === undefined) {\n hookDone();\n return;\n }\n\n const rep = reply as { statusCode?: number };\n const duration = Date.now() - req._logixiaStart;\n const status = rep.statusCode ?? 0;\n const traceId = resolveRequestTraceId(req, traceHeaders);\n const level = status >= 500 ? errorLevel : responseLevel;\n const baseFields = buildBaseFields(req, traceId, options, nameSets);\n\n fire(\n logger.logLevel(level, 'request completed', {\n ...baseFields,\n statusCode: status,\n duration,\n })\n );\n\n if (duration > slowRequestThresholdMs) {\n fire(\n logger.warn('slow request detected', {\n traceId,\n url: baseFields.url,\n method: req.method,\n duration,\n threshold: slowRequestThresholdMs,\n })\n );\n }\n\n hookDone();\n });\n\n done();\n }\n\n return Object.assign(logixiaFastifyPlugin, {\n [Symbol.for('skip-override')]: true,\n [Symbol.for('fastify.display-name')]: 'logixia',\n });\n}\n\n// ── Internal helpers ──────────────────────────────────────────────────────────\n\n/**\n * Bound the captured body without turning it into a string, so the logger's\n * path-based redaction still sees the original keys. Oversized structures are\n * pruned (extra keys/items dropped, long strings cut) rather than serialized.\n */\nfunction truncateBody(body: unknown, maxBytes = 4096): unknown {\n if (typeof body === 'string') {\n return body.length > maxBytes ? body.slice(0, maxBytes) + '…[truncated]' : body;\n }\n if (body && typeof body === 'object') {\n const str = stringifyForLog(body);\n if (str.length <= maxBytes) return body;\n return pruneValue(body, { left: maxBytes }, 0, new Set());\n }\n return body;\n}\n\nconst TRUNCATED = '…[truncated]';\nconst MAX_PRUNE_DEPTH = 8;\n\nfunction pruneValue(\n value: unknown,\n budget: { left: number },\n depth: number,\n ancestors: Set<object>\n): unknown {\n if (typeof value === 'string') {\n const cut = value.slice(0, Math.max(0, budget.left));\n budget.left -= cut.length;\n return cut.length < value.length ? cut + TRUNCATED : cut;\n }\n if (value === null || typeof value !== 'object') {\n budget.left -= 8;\n return value;\n }\n if (value instanceof Date) {\n budget.left -= 24;\n return Number.isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString();\n }\n if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {\n budget.left -= 16;\n return `[Buffer ${value.byteLength} bytes]`;\n }\n if (value instanceof Error) return value;\n if (ancestors.has(value) || depth >= MAX_PRUNE_DEPTH) return TRUNCATED;\n ancestors.add(value);\n try {\n if (Array.isArray(value) || value instanceof Set || value instanceof Map) {\n const items: unknown[] = [];\n for (const item of value instanceof Map ? value.entries() : value) {\n if (budget.left <= 0) {\n items.push(TRUNCATED);\n break;\n }\n items.push(pruneValue(item, budget, depth + 1, ancestors));\n }\n return items;\n }\n const toJSON = (value as { toJSON?: unknown }).toJSON;\n if (typeof toJSON === 'function') {\n const json: unknown = toJSON.call(value);\n if (json !== value) return pruneValue(json, budget, depth + 1, ancestors);\n }\n const record: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n if (budget.left <= 0) {\n record['…'] = TRUNCATED;\n break;\n }\n budget.left -= k.length;\n record[k] = pruneValue(v, budget, depth + 1, ancestors);\n }\n return record;\n } catch {\n return TRUNCATED;\n } finally {\n ancestors.delete(value);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","names":["internalError","buildNameSet","DEFAULT_REDACT_HEADERS","DEFAULT_REDACT_QUERY_PARAMS","sanitizeUrl","resolveTraceHeaders","resolveRequestTraceId","stringifyForLog"],"sources":["../src/middleware/http-logger.ts"],"sourcesContent":["/**\n * HTTP request/response logging middleware — Morgan replacement.\n *\n * Fixes every documented Morgan bug:\n * - statusCode always captured correctly, even for requests > 20 s\n * - Logs request START (with traceId) and response FINISH (with duration)\n * - Captures errors before and after response\n * - Auto-redacts Authorization / Cookie / Set-Cookie headers\n * - Slow-request warnings\n * - Skip predicates for health-check routes / static assets\n *\n * @example Express\n * ```ts\n * import { createExpressMiddleware } from 'logixia/middleware';\n * app.use(createExpressMiddleware(logger));\n * ```\n *\n * @example Fastify\n * ```ts\n * import { createFastifyPlugin } from 'logixia/middleware';\n * await fastify.register(createFastifyPlugin(logger));\n * ```\n */\n\nimport type { IBaseLogger } from '../types';\nimport type { HttpRequestShape, HttpResponseShape } from '../types/http-shapes';\nimport { internalError } from '../utils/internal-log';\nimport { assignOwn } from '../utils/own-property';\nimport { resolveRequestTraceId, resolveTraceHeaders } from '../utils/request-trace';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n buildNameSet,\n DEFAULT_REDACT_HEADERS,\n DEFAULT_REDACT_QUERY_PARAMS,\n redactUrlQuery as sanitizeUrl,\n} from '../utils/sensitive-names';\n\n// ── Shared types ─────────────────────────────────────────────────────────────\n\nexport interface HttpLoggerOptions {\n /**\n * Skip logging for a request. Called before any I/O.\n * @example `skip: (req) => req.url === '/health'`\n */\n skip?: (req: IncomingRequest) => boolean;\n /**\n * Log request body (POST/PUT/PATCH). Capped at `bodyMaxBytes` (default: 4096).\n * Redaction still applies to the captured body.\n * Default: false.\n */\n logBody?: boolean;\n /** Max bytes of body to capture. Default: 4096. */\n bodyMaxBytes?: number;\n /**\n * Emit a WARN log when a request duration exceeds this threshold (ms).\n * Default: 1000.\n */\n slowRequestThresholdMs?: number;\n /**\n * Additional fields to include in every log entry.\n * @example `extraFields: (req) => ({ tenantId: req.headers['x-tenant-id'] })`\n */\n extraFields?: (req: IncomingRequest) => Record<string, unknown>;\n /**\n * Header name, or list of names tried in order, to read the trace ID from.\n * Default: the library-wide list (`DEFAULT_TRACE_HEADERS`: traceparent,\n * x-trace-id, x-request-id, x-correlation-id, trace-id).\n * If none is present, `request.id` is used when it is not a framework counter,\n * otherwise a UUID is generated and shared with the logixia context helpers.\n */\n traceIdHeader?: string | readonly string[];\n /**\n * Additional headers to redact from logged output. Merged with the defaults\n * (authorization, cookie, set-cookie, x-api-key, proxy-authorization,\n * x-auth-token, x-access-token, x-csrf-token, x-amz-security-token).\n */\n redactHeaders?: string[];\n /**\n * Additional query-string parameter names whose values are redacted from the\n * logged URL. Merged with the defaults (token, access_token, refresh_token,\n * code, password, secret, api_key, apikey, key, signature, sig, auth).\n */\n redactQueryParams?: string[];\n /**\n * Log level for request-start entries. Default: 'debug'.\n * Set to 'silent' to suppress request-start logs entirely.\n */\n requestLevel?: string;\n /** Log level for successful response entries. Default: 'info'. */\n responseLevel?: string;\n /** Log level for error responses (status ≥ 500). Default: 'error'. */\n errorLevel?: string;\n}\n\n// Minimal structural types so we don't need @types/express / fastify in core\nexport type IncomingRequest = HttpRequestShape;\nexport type OutgoingResponse = HttpResponseShape;\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction sanitizeHeaders(\n headers: Record<string, string | string[] | undefined> | undefined,\n redactSet: Set<string>\n): Record<string, unknown> {\n if (!headers) return {};\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(headers)) {\n assignOwn(out, k, redactSet.has(k.toLowerCase()) ? '[REDACTED]' : v);\n }\n return out;\n}\n\n/** Fire-and-forget a logger call; a failing log must never become an unhandled rejection. */\nfunction fire(result: unknown): void {\n if (result && typeof (result as Promise<unknown>).catch === 'function') {\n (result as Promise<unknown>).catch((error: unknown) =>\n internalError('http-logger: log call failed', error)\n );\n }\n}\n\ninterface NameSets {\n headers: Set<string>;\n queryParams: Set<string>;\n}\n\nfunction resolveNameSets(options: HttpLoggerOptions): NameSets {\n return {\n headers: buildNameSet(DEFAULT_REDACT_HEADERS, options.redactHeaders),\n queryParams: buildNameSet(DEFAULT_REDACT_QUERY_PARAMS, options.redactQueryParams),\n };\n}\n\nfunction buildBaseFields(\n req: IncomingRequest,\n traceId: string,\n options: HttpLoggerOptions,\n { headers: redactSet, queryParams: paramSet }: NameSets\n): Record<string, unknown> {\n const fields: Record<string, unknown> = {\n traceId,\n method: req.method?.toUpperCase() ?? 'UNKNOWN',\n url: sanitizeUrl(req.url ?? '/', paramSet),\n ip: req.ip ?? req.socket?.remoteAddress ?? 'unknown',\n headers: sanitizeHeaders(req.headers, redactSet),\n };\n\n if (options.extraFields) {\n Object.assign(fields, options.extraFields(req));\n }\n\n return fields;\n}\n\n// ── Express middleware ────────────────────────────────────────────────────────\n\n/**\n * Create an Express / Connect compatible middleware that replaces Morgan.\n */\nexport function createExpressMiddleware(\n logger: IBaseLogger,\n options: HttpLoggerOptions = {}\n): (req: IncomingRequest, res: OutgoingResponse, next: () => void) => void {\n const {\n skip,\n logBody,\n bodyMaxBytes,\n traceIdHeader,\n requestLevel = 'debug',\n responseLevel = 'info',\n errorLevel = 'error',\n slowRequestThresholdMs = 1000,\n } = options;\n const nameSets = resolveNameSets(options);\n const traceHeaders = resolveTraceHeaders(traceIdHeader);\n\n return function logixiaHttpMiddleware(\n req: IncomingRequest,\n res: OutgoingResponse,\n next: () => void\n ): void {\n if (skip?.(req)) {\n next();\n return;\n }\n\n const traceId = resolveRequestTraceId(req, traceHeaders);\n const startMs = Date.now();\n const baseFields = buildBaseFields(req, traceId, options, nameSets);\n\n // Log request start\n if (requestLevel !== 'silent') {\n fire(\n logger.logLevel(requestLevel, 'request started', {\n ...baseFields,\n ...(logBody && req.body ? { body: truncateBody(req.body, bodyMaxBytes) } : {}),\n })\n );\n }\n\n // Hook into the response 'finish' event — fires after headers + body are sent.\n // This is what Morgan gets wrong for slow requests (it uses 'close' which may\n // fire before the status code is set on some Node versions).\n //\n // BOTH 'finish' and 'close' fire on a normal response, so guard against\n // logging the completion twice (which would duplicate the completed entry and\n // the slow-request warning).\n let completed = false;\n const onFinish = (): void => {\n if (completed) return;\n completed = true;\n\n const duration = Date.now() - startMs;\n const status = res.statusCode ?? 0;\n const level = status >= 500 ? errorLevel : responseLevel;\n\n fire(\n logger.logLevel(level, 'request completed', {\n ...baseFields,\n statusCode: status,\n duration,\n })\n );\n\n if (duration > slowRequestThresholdMs) {\n fire(\n logger.warn('slow request detected', {\n ...baseFields,\n statusCode: status,\n duration,\n threshold: slowRequestThresholdMs,\n })\n );\n }\n };\n\n res.once?.('finish', onFinish);\n // Fallback: also covers a client that disconnects before 'finish' fires. The\n // `completed` guard ensures a normal finish+close pair logs only once.\n res.once?.('close', onFinish);\n\n next();\n };\n}\n\n// ── Fastify plugin ─────────────────────────────────────────────────────────────\n\nexport interface FastifyInstance {\n addHook: (name: string, fn: (req: unknown, reply: unknown, done: () => void) => void) => void;\n}\n\n/**\n * Create a Fastify plugin (a function you pass to `fastify.register()`).\n *\n * @example\n * ```ts\n * await fastify.register(createFastifyPlugin(logger, { slowRequestThresholdMs: 500 }));\n * ```\n */\nexport function createFastifyPlugin(logger: IBaseLogger, options: HttpLoggerOptions = {}) {\n const {\n skip,\n traceIdHeader,\n requestLevel = 'debug',\n responseLevel = 'info',\n errorLevel = 'error',\n slowRequestThresholdMs = 1000,\n } = options;\n const nameSets = resolveNameSets(options);\n const traceHeaders = resolveTraceHeaders(traceIdHeader);\n\n function logixiaFastifyPlugin(fastify: FastifyInstance, _opts: unknown, done: () => void): void {\n fastify.addHook('onRequest', (request: unknown, _reply: unknown, hookDone: () => void) => {\n const req = request as IncomingRequest & { _logixiaStart?: number };\n if (skip?.(req)) {\n hookDone();\n return;\n }\n\n const traceId = resolveRequestTraceId(req, traceHeaders);\n req._logixiaStart = Date.now();\n\n if (requestLevel !== 'silent') {\n fire(\n logger.logLevel(\n requestLevel,\n 'request started',\n buildBaseFields(req, traceId, options, nameSets)\n )\n );\n }\n hookDone();\n });\n\n fastify.addHook('onResponse', (request: unknown, reply: unknown, hookDone: () => void) => {\n const req = request as IncomingRequest & { _logixiaStart?: number };\n if (req._logixiaStart === undefined) {\n hookDone();\n return;\n }\n\n const rep = reply as { statusCode?: number };\n const duration = Date.now() - req._logixiaStart;\n const status = rep.statusCode ?? 0;\n const traceId = resolveRequestTraceId(req, traceHeaders);\n const level = status >= 500 ? errorLevel : responseLevel;\n const baseFields = buildBaseFields(req, traceId, options, nameSets);\n\n fire(\n logger.logLevel(level, 'request completed', {\n ...baseFields,\n statusCode: status,\n duration,\n })\n );\n\n if (duration > slowRequestThresholdMs) {\n fire(\n logger.warn('slow request detected', {\n traceId,\n url: baseFields.url,\n method: req.method,\n duration,\n threshold: slowRequestThresholdMs,\n })\n );\n }\n\n hookDone();\n });\n\n done();\n }\n\n return Object.assign(logixiaFastifyPlugin, {\n [Symbol.for('skip-override')]: true,\n [Symbol.for('fastify.display-name')]: 'logixia',\n });\n}\n\n// ── Internal helpers ──────────────────────────────────────────────────────────\n\n/**\n * Bound the captured body without turning it into a string, so the logger's\n * path-based redaction still sees the original keys. Oversized structures are\n * pruned (extra keys/items dropped, long strings cut) rather than serialized.\n */\nfunction truncateBody(body: unknown, maxBytes = 4096): unknown {\n if (typeof body === 'string') {\n return body.length > maxBytes ? body.slice(0, maxBytes) + '…[truncated]' : body;\n }\n if (body && typeof body === 'object') {\n const str = stringifyForLog(body);\n if (str.length <= maxBytes) return body;\n return pruneValue(body, { left: maxBytes }, 0, new Set());\n }\n return body;\n}\n\nconst TRUNCATED = '…[truncated]';\nconst MAX_PRUNE_DEPTH = 8;\n\nfunction pruneValue(\n value: unknown,\n budget: { left: number },\n depth: number,\n ancestors: Set<object>\n): unknown {\n if (typeof value === 'string') {\n const cut = value.slice(0, Math.max(0, budget.left));\n budget.left -= cut.length;\n return cut.length < value.length ? cut + TRUNCATED : cut;\n }\n if (value === null || typeof value !== 'object') {\n budget.left -= 8;\n return value;\n }\n if (value instanceof Date) {\n budget.left -= 24;\n return Number.isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString();\n }\n if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {\n budget.left -= 16;\n return `[Buffer ${value.byteLength} bytes]`;\n }\n if (value instanceof Error) return value;\n if (ancestors.has(value) || depth >= MAX_PRUNE_DEPTH) return TRUNCATED;\n ancestors.add(value);\n try {\n if (Array.isArray(value) || value instanceof Set || value instanceof Map) {\n const items: unknown[] = [];\n for (const item of value instanceof Map ? value.entries() : value) {\n if (budget.left <= 0) {\n items.push(TRUNCATED);\n break;\n }\n items.push(pruneValue(item, budget, depth + 1, ancestors));\n }\n return items;\n }\n const toJSON = (value as { toJSON?: unknown }).toJSON;\n if (typeof toJSON === 'function') {\n const json: unknown = toJSON.call(value);\n if (json !== value) return pruneValue(json, budget, depth + 1, ancestors);\n }\n const record: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n if (budget.left <= 0) {\n record['…'] = TRUNCATED;\n break;\n }\n budget.left -= k.length;\n assignOwn(record, k, pruneValue(v, budget, depth + 1, ancestors));\n }\n return record;\n } catch {\n return TRUNCATED;\n } finally {\n ancestors.delete(value);\n }\n}\n"],"mappings":"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"}
@@ -1,41 +1,12 @@
1
- import { n as resolveTraceHeaders, t as resolveRequestTraceId } from "./request-trace-C_fvIDv4.mjs";
2
- import { a as DEFAULT_REDACT_QUERY_PARAMS, i as DEFAULT_REDACT_HEADERS, t as internalError } from "./internal-log-uCSd1PMc.mjs";
3
- import { i as stringifyForLog } from "./safe-stringify-BMRl3EX3.mjs";
1
+ import { t as assignOwn } from "./own-property-CjFw9qwZ.mjs";
2
+ import { n as resolveTraceHeaders, t as resolveRequestTraceId } from "./request-trace-BgNykH9M.mjs";
3
+ import { a as DEFAULT_REDACT_QUERY_PARAMS, c as buildNameSet, i as DEFAULT_REDACT_HEADERS, l as redactUrlQuery, t as internalError } from "./internal-log-Bg4V1cPg.mjs";
4
+ import { i as stringifyForLog } from "./safe-stringify-B5p5ktFe.mjs";
4
5
  // src/middleware/http-logger.ts
5
- function buildNameSet(defaults, extra) {
6
- return new Set([...defaults, ...extra ?? []].map((n) => n.toLowerCase()));
7
- }
8
- function safeDecode(value) {
9
- try {
10
- return decodeURIComponent(value.replace(/\+/g, " "));
11
- } catch {
12
- return value;
13
- }
14
- }
15
- /** Replace the values of sensitive query parameters, leaving the rest verbatim. */
16
- function sanitizeUrl(url, redactParams) {
17
- const q = url.indexOf("?");
18
- if (q === -1) return url;
19
- const hashAt = url.indexOf("#", q);
20
- const query = url.slice(q + 1, hashAt === -1 ? void 0 : hashAt);
21
- const hash = hashAt === -1 ? "" : url.slice(hashAt);
22
- const cleaned = query.split("&").map((pair) => {
23
- const eq = pair.indexOf("=");
24
- const rawKey = eq === -1 ? pair : pair.slice(0, eq);
25
- const decoded = safeDecode(rawKey);
26
- const bracket = decoded.indexOf("[");
27
- const name = (bracket === -1 ? decoded : decoded.slice(0, bracket)).toLowerCase();
28
- return redactParams.has(name) && eq !== -1 ? `${rawKey}=[REDACTED]` : pair;
29
- }).join("&");
30
- return `${url.slice(0, q)}?${cleaned}${hash}`;
31
- }
32
6
  function sanitizeHeaders(headers, redactSet) {
33
7
  if (!headers) return {};
34
8
  const out = {};
35
- for (const [k, v] of Object.entries(headers)) {
36
- if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
37
- out[k] = redactSet.has(k.toLowerCase()) ? "[REDACTED]" : v;
38
- }
9
+ for (const [k, v] of Object.entries(headers)) assignOwn(out, k, redactSet.has(k.toLowerCase()) ? "[REDACTED]" : v);
39
10
  return out;
40
11
  }
41
12
  /** Fire-and-forget a logger call; a failing log must never become an unhandled rejection. */
@@ -53,7 +24,7 @@ function buildBaseFields(req, traceId, options, { headers: redactSet, queryParam
53
24
  const fields = {
54
25
  traceId,
55
26
  method: ((_req$method = req.method) === null || _req$method === void 0 ? void 0 : _req$method.toUpperCase()) ?? "UNKNOWN",
56
- url: sanitizeUrl(req.url ?? "/", paramSet),
27
+ url: redactUrlQuery(req.url ?? "/", paramSet),
57
28
  ip: req.ip ?? ((_req$socket = req.socket) === null || _req$socket === void 0 ? void 0 : _req$socket.remoteAddress) ?? "unknown",
58
29
  headers: sanitizeHeaders(req.headers, redactSet)
59
30
  };
@@ -216,13 +187,12 @@ function pruneValue(value, budget, depth, ancestors) {
216
187
  }
217
188
  const record = {};
218
189
  for (const [k, v] of Object.entries(value)) {
219
- if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
220
190
  if (budget.left <= 0) {
221
191
  record["…"] = TRUNCATED;
222
192
  break;
223
193
  }
224
194
  budget.left -= k.length;
225
- record[k] = pruneValue(v, budget, depth + 1, ancestors);
195
+ assignOwn(record, k, pruneValue(v, budget, depth + 1, ancestors));
226
196
  }
227
197
  return record;
228
198
  } catch {