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.
- package/README.md +172 -2614
- package/dist/{async-context-B05ArHgM.mjs → async-context-2mbUKhcl.mjs} +4 -6
- package/dist/async-context-2mbUKhcl.mjs.map +1 -0
- package/dist/{async-context-DTQgi8f7.js → async-context-DUFMDYAr.js} +4 -6
- package/dist/async-context-DUFMDYAr.js.map +1 -0
- package/dist/{azure-monitor.transport-CwbOLZHB.js → azure-monitor.transport-Bu4S2I2q.js} +5 -5
- package/dist/azure-monitor.transport-Bu4S2I2q.js.map +1 -0
- package/dist/{azure-monitor.transport-DV03Z5P4.d.ts → azure-monitor.transport-Bvf7Xegt.d.ts} +2 -2
- package/dist/{azure-monitor.transport-DV03Z5P4.d.ts.map → azure-monitor.transport-Bvf7Xegt.d.ts.map} +1 -1
- package/dist/{azure-monitor.transport-BPbgjATu.mjs → azure-monitor.transport-C1RWAumT.mjs} +5 -5
- package/dist/azure-monitor.transport-C1RWAumT.mjs.map +1 -0
- package/dist/{azure-monitor.transport-C7kYyZUW.d.mts → azure-monitor.transport-CbSHOnNX.d.mts} +2 -2
- package/dist/{azure-monitor.transport-C7kYyZUW.d.mts.map → azure-monitor.transport-CbSHOnNX.d.mts.map} +1 -1
- package/dist/azure.d.mts +1 -1
- package/dist/azure.d.ts +1 -1
- package/dist/azure.js +1 -1
- package/dist/azure.mjs +1 -1
- package/dist/{batch-buffer-0fgLmIPn.mjs → batch-buffer-BvXLDPlV.mjs} +3 -3
- package/dist/{batch-buffer-0fgLmIPn.mjs.map → batch-buffer-BvXLDPlV.mjs.map} +1 -1
- package/dist/{batch-buffer-DT4gdXmf.js → batch-buffer-C7Am4Mw-.js} +3 -3
- package/dist/{batch-buffer-DT4gdXmf.js.map → batch-buffer-C7Am4Mw-.js.map} +1 -1
- package/dist/browser.d.mts +5 -5
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.d.ts +5 -5
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +47 -12
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +47 -12
- package/dist/browser.mjs.map +1 -1
- package/dist/{builder-D70z6kjj.mjs → builder-DhNhvOkt.mjs} +106 -153
- package/dist/builder-DhNhvOkt.mjs.map +1 -0
- package/dist/{builder-M7NoaugM.js → builder-yFvJrz7o.js} +109 -179
- package/dist/builder-yFvJrz7o.js.map +1 -0
- package/dist/cli/index.js +157 -66
- package/dist/cloudwatch.d.mts +1 -1
- package/dist/cloudwatch.d.ts +1 -1
- package/dist/cloudwatch.js +1 -1
- package/dist/cloudwatch.mjs +1 -1
- package/dist/{cloudwatch.transport-Bf7L4I59.d.ts → cloudwatch.transport-CQyT6U91.d.ts} +2 -2
- package/dist/{cloudwatch.transport-Bf7L4I59.d.ts.map → cloudwatch.transport-CQyT6U91.d.ts.map} +1 -1
- package/dist/{cloudwatch.transport-B2wKQFF7.js → cloudwatch.transport-Ch1Uae-0.js} +6 -6
- package/dist/cloudwatch.transport-Ch1Uae-0.js.map +1 -0
- package/dist/{cloudwatch.transport-Rj4yTpRn.d.mts → cloudwatch.transport-CjVrI6S4.d.mts} +2 -2
- package/dist/{cloudwatch.transport-Rj4yTpRn.d.mts.map → cloudwatch.transport-CjVrI6S4.d.mts.map} +1 -1
- package/dist/{cloudwatch.transport-BTvYfLs9.mjs → cloudwatch.transport-DZqF2xZO.mjs} +6 -6
- package/dist/cloudwatch.transport-DZqF2xZO.mjs.map +1 -0
- package/dist/correlation.d.mts +4 -55
- package/dist/correlation.d.mts.map +1 -1
- package/dist/correlation.d.ts +4 -55
- package/dist/correlation.d.ts.map +1 -1
- package/dist/correlation.js +13 -13
- package/dist/correlation.js.map +1 -1
- package/dist/correlation.mjs +13 -13
- package/dist/correlation.mjs.map +1 -1
- package/dist/{entry-fields-RLd1jCoR.js → entry-fields-BSLkzw18.js} +8 -3
- package/dist/entry-fields-BSLkzw18.js.map +1 -0
- package/dist/{entry-fields-CGuvT2R0.mjs → entry-fields-J1OBi24o.mjs} +8 -3
- package/dist/entry-fields-J1OBi24o.mjs.map +1 -0
- package/dist/gcp.d.mts +1 -1
- package/dist/gcp.d.ts +1 -1
- package/dist/gcp.js +1 -1
- package/dist/gcp.mjs +1 -1
- package/dist/{gcp.transport-CFQfdAOK.d.ts → gcp.transport-BCIvdFV-.d.ts} +4 -4
- package/dist/{gcp.transport-CFQfdAOK.d.ts.map → gcp.transport-BCIvdFV-.d.ts.map} +1 -1
- package/dist/{gcp.transport-C5D72b1g.d.mts → gcp.transport-D9SdLPwP.d.mts} +4 -4
- package/dist/{gcp.transport-C5D72b1g.d.mts.map → gcp.transport-D9SdLPwP.d.mts.map} +1 -1
- package/dist/{gcp.transport-CGKopAhQ.mjs → gcp.transport-DVCmnA3b.mjs} +20 -16
- package/dist/gcp.transport-DVCmnA3b.mjs.map +1 -0
- package/dist/{gcp.transport-KZe9hCAR.js → gcp.transport-Dy2Lf8FI.js} +20 -16
- package/dist/gcp.transport-Dy2Lf8FI.js.map +1 -0
- package/dist/http-shapes-BnBkJcid.d.mts +32 -0
- package/dist/http-shapes-BnBkJcid.d.mts.map +1 -0
- package/dist/http-shapes-BnBkJcid.d.ts +32 -0
- package/dist/http-shapes-BnBkJcid.d.ts.map +1 -0
- package/dist/{index-xqaJtAZn.d.mts → index-BSTtFcQ6.d.mts} +142 -8
- package/dist/index-BSTtFcQ6.d.mts.map +1 -0
- package/dist/{index-DwozbmB4.d.ts → index-CFDGkI8n.d.ts} +142 -8
- package/dist/index-CFDGkI8n.d.ts.map +1 -0
- package/dist/index.d.mts +21 -65
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +21 -65
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -21
- package/dist/index.mjs.map +1 -1
- package/dist/{internal-log-uCSd1PMc.mjs → internal-log-Bg4V1cPg.mjs} +30 -2
- package/dist/internal-log-Bg4V1cPg.mjs.map +1 -0
- package/dist/{internal-log-b0Vp4qrB.js → internal-log-By4Govmu.js} +41 -1
- package/dist/internal-log-By4Govmu.js.map +1 -0
- package/dist/{logitron-logger-D2B75Oc7.d.ts → logitron-logger-D22Yb61K.d.mts} +13 -100
- package/dist/logitron-logger-D22Yb61K.d.mts.map +1 -0
- package/dist/{logitron-logger-oEd6wNhf.d.mts → logitron-logger-D_MYYrQt.d.ts} +13 -100
- package/dist/logitron-logger-D_MYYrQt.d.ts.map +1 -0
- package/dist/middleware.d.mts +4 -17
- package/dist/middleware.d.mts.map +1 -1
- package/dist/middleware.d.ts +4 -17
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +9 -39
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +7 -37
- package/dist/middleware.mjs.map +1 -1
- package/dist/nest.d.mts +14 -7
- package/dist/nest.d.mts.map +1 -1
- package/dist/nest.d.ts +14 -7
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +15 -10
- package/dist/nest.js.map +1 -1
- package/dist/nest.mjs +15 -10
- package/dist/nest.mjs.map +1 -1
- package/dist/{transport.manager-eZ_EeC00.mjs → otel-Dt5riXuE.mjs} +173 -24
- package/dist/otel-Dt5riXuE.mjs.map +1 -0
- package/dist/{transport.manager-DkcEOlqV.js → otel-DyeH6DVR.js} +214 -23
- package/dist/otel-DyeH6DVR.js.map +1 -0
- package/dist/own-property-BWkibepN.js +40 -0
- package/dist/own-property-BWkibepN.js.map +1 -0
- package/dist/own-property-CjFw9qwZ.mjs +29 -0
- package/dist/own-property-CjFw9qwZ.mjs.map +1 -0
- package/dist/{request-trace-C_fvIDv4.mjs → request-trace-BgNykH9M.mjs} +1 -5
- package/dist/{request-trace-C_fvIDv4.mjs.map → request-trace-BgNykH9M.mjs.map} +1 -1
- package/dist/{request-trace-wTJdKp6-.js → request-trace-ByujsUZk.js} +1 -5
- package/dist/{request-trace-wTJdKp6-.js.map → request-trace-ByujsUZk.js.map} +1 -1
- package/dist/{safe-stringify-BMRl3EX3.mjs → safe-stringify-B5p5ktFe.mjs} +37 -12
- package/dist/safe-stringify-B5p5ktFe.mjs.map +1 -0
- package/dist/{safe-stringify-DLz1Kz3o.js → safe-stringify-J3sh-7YM.js} +37 -12
- package/dist/safe-stringify-J3sh-7YM.js.map +1 -0
- package/dist/{search-DUfWuzFP.js → search-CRjhHxe9.js} +269 -25
- package/dist/search-CRjhHxe9.js.map +1 -0
- package/dist/{search-CazwyX5Z.d.ts → search-DA3ft3X5.d.mts} +5 -2
- package/dist/search-DA3ft3X5.d.mts.map +1 -0
- package/dist/{search-CWMVYC-R.mjs → search-DAXKE-P9.mjs} +269 -25
- package/dist/search-DAXKE-P9.mjs.map +1 -0
- package/dist/{search-BH33XhuU.d.mts → search-DEl3Pe47.d.ts} +5 -2
- package/dist/search-DEl3Pe47.d.ts.map +1 -0
- package/dist/search.d.mts +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +1 -1
- package/dist/search.mjs +1 -1
- package/dist/{secret-config-CBZBs0p1.js → secret-config-CmK2wVkr.js} +6 -5
- package/dist/secret-config-CmK2wVkr.js.map +1 -0
- package/dist/{secret-config-BegMLkYl.mjs → secret-config-Dhb6pqTU.mjs} +6 -5
- package/dist/secret-config-Dhb6pqTU.mjs.map +1 -0
- package/dist/{slack.transport-DxHOXlLu.d.mts → slack.transport-BDyENuR0.d.mts} +2 -2
- package/dist/{slack.transport-DxHOXlLu.d.mts.map → slack.transport-BDyENuR0.d.mts.map} +1 -1
- package/dist/{slack.transport-Ce96JBDk.mjs → slack.transport-BTEwsREY.mjs} +66 -15
- package/dist/slack.transport-BTEwsREY.mjs.map +1 -0
- package/dist/{slack.transport-DrirgDNy.d.ts → slack.transport-C6E0l4kw.d.ts} +2 -2
- package/dist/{slack.transport-DrirgDNy.d.ts.map → slack.transport-C6E0l4kw.d.ts.map} +1 -1
- package/dist/{slack.transport-Dlx3xvb7.js → slack.transport-UbYvOdKx.js} +67 -16
- package/dist/slack.transport-UbYvOdKx.js.map +1 -0
- package/dist/testing.d.mts +11 -4
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.d.ts +11 -4
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +38 -3
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +38 -3
- package/dist/testing.mjs.map +1 -1
- package/dist/{transport.types-BJVL1QQl.d.ts → transport.types-CzJq4IrF.d.mts} +14 -3
- package/dist/transport.types-CzJq4IrF.d.mts.map +1 -0
- package/dist/{transport.types-BJVL1QQl.d.mts → transport.types-CzJq4IrF.d.ts} +14 -3
- package/dist/transport.types-CzJq4IrF.d.ts.map +1 -0
- package/dist/transports.d.mts +25 -13
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +25 -13
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +15 -15
- package/dist/transports.mjs +6 -6
- package/dist/worker-entry.js +105 -24
- package/dist/worker.d.mts +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.mjs +1 -1
- package/dist/{worker.transport-BuPwBi3d.d.ts → worker.transport-B9mCQSM1.d.ts} +7 -2
- package/dist/worker.transport-B9mCQSM1.d.ts.map +1 -0
- package/dist/{worker.transport-hJay1wxA.d.mts → worker.transport-Bl8lFRck.d.mts} +7 -2
- package/dist/worker.transport-Bl8lFRck.d.mts.map +1 -0
- package/dist/{worker.transport-DLVA4y2e.mjs → worker.transport-BmmOKb1-.mjs} +13 -10
- package/dist/worker.transport-BmmOKb1-.mjs.map +1 -0
- package/dist/{worker.transport-C6GNl_r2.js → worker.transport-DxxbqQCG.js} +13 -10
- package/dist/worker.transport-DxxbqQCG.js.map +1 -0
- package/package.json +33 -65
- package/dist/async-context-B05ArHgM.mjs.map +0 -1
- package/dist/async-context-DTQgi8f7.js.map +0 -1
- package/dist/azure-monitor.transport-BPbgjATu.mjs.map +0 -1
- package/dist/azure-monitor.transport-CwbOLZHB.js.map +0 -1
- package/dist/builder-D70z6kjj.mjs.map +0 -1
- package/dist/builder-M7NoaugM.js.map +0 -1
- package/dist/cloudwatch.transport-B2wKQFF7.js.map +0 -1
- package/dist/cloudwatch.transport-BTvYfLs9.mjs.map +0 -1
- package/dist/entry-fields-CGuvT2R0.mjs.map +0 -1
- package/dist/entry-fields-RLd1jCoR.js.map +0 -1
- package/dist/gcp.transport-CGKopAhQ.mjs.map +0 -1
- package/dist/gcp.transport-KZe9hCAR.js.map +0 -1
- package/dist/index-DwozbmB4.d.ts.map +0 -1
- package/dist/index-xqaJtAZn.d.mts.map +0 -1
- package/dist/internal-log-b0Vp4qrB.js.map +0 -1
- package/dist/internal-log-uCSd1PMc.mjs.map +0 -1
- package/dist/logitron-logger-D2B75Oc7.d.ts.map +0 -1
- package/dist/logitron-logger-oEd6wNhf.d.mts.map +0 -1
- package/dist/safe-stringify-BMRl3EX3.mjs.map +0 -1
- package/dist/safe-stringify-DLz1Kz3o.js.map +0 -1
- package/dist/search-BH33XhuU.d.mts.map +0 -1
- package/dist/search-CWMVYC-R.mjs.map +0 -1
- package/dist/search-CazwyX5Z.d.ts.map +0 -1
- package/dist/search-DUfWuzFP.js.map +0 -1
- package/dist/secret-config-BegMLkYl.mjs.map +0 -1
- package/dist/secret-config-CBZBs0p1.js.map +0 -1
- package/dist/slack.transport-Ce96JBDk.mjs.map +0 -1
- package/dist/slack.transport-Dlx3xvb7.js.map +0 -1
- package/dist/transport.manager-DkcEOlqV.js.map +0 -1
- package/dist/transport.manager-eZ_EeC00.mjs.map +0 -1
- package/dist/transport.types-BJVL1QQl.d.mts.map +0 -1
- package/dist/transport.types-BJVL1QQl.d.ts.map +0 -1
- package/dist/worker.transport-BuPwBi3d.d.ts.map +0 -1
- package/dist/worker.transport-C6GNl_r2.js.map +0 -1
- package/dist/worker.transport-DLVA4y2e.mjs.map +0 -1
- package/dist/worker.transport-hJay1wxA.d.mts.map +0 -1
|
@@ -1,100 +1,4 @@
|
|
|
1
|
-
import {
|
|
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>):
|
|
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 {
|
|
365
|
-
//# sourceMappingURL=logitron-logger-
|
|
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 {
|
|
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>):
|
|
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 {
|
|
365
|
-
//# sourceMappingURL=logitron-logger-
|
|
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"}
|
package/dist/middleware.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
57
|
-
|
|
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":"
|
|
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"}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
57
|
-
|
|
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
|
*/
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","names":[],"sources":["../src/middleware/http-logger.ts"],"mappings":"
|
|
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"}
|
package/dist/middleware.js
CHANGED
|
@@ -1,42 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
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:
|
|
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
|
|
196
|
+
require_own_property.assignOwn(record, k, pruneValue(v, budget, depth + 1, ancestors));
|
|
227
197
|
}
|
|
228
198
|
return record;
|
|
229
199
|
} catch {
|
package/dist/middleware.js.map
CHANGED
|
@@ -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":";;;;;;AAoGA,SAAS,gBACP,SACA,WACyB;CACzB,IAAI,CAAC,SAAS,OAAO,CAAC;CACtB,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,GACzC,qBAAA,UAAU,KAAK,GAAG,UAAU,IAAI,EAAE,YAAY,CAAC,IAAI,eAAe,CAAC;CAErE,OAAO;AACT;;AAGA,SAAS,KAAK,QAAuB;CACnC,IAAI,UAAU,OAAQ,OAA4B,UAAU,YAC1D,OAA6B,OAAO,UAClCA,qBAAAA,cAAc,gCAAgC,KAAK,CACrD;AAEJ;AAOA,SAAS,gBAAgB,SAAsC;CAC7D,OAAO;EACL,SAASC,qBAAAA,aAAaC,qBAAAA,wBAAwB,QAAQ,aAAa;EACnE,aAAaD,qBAAAA,aAAaE,qBAAAA,6BAA6B,QAAQ,iBAAiB;CAClF;AACF;AAEA,SAAS,gBACP,KACA,SACA,SACA,EAAE,SAAS,WAAW,aAAa,YACV;;CACzB,MAAM,SAAkC;EACtC;EACA,UAAA,cAAQ,IAAI,YAAA,QAAA,gBAAA,KAAA,IAAA,KAAA,IAAA,YAAQ,YAAY,MAAK;EACrC,KAAKC,qBAAAA,eAAY,IAAI,OAAO,KAAK,QAAQ;EACzC,IAAI,IAAI,QAAA,cAAM,IAAI,YAAA,QAAA,gBAAA,KAAA,IAAA,KAAA,IAAA,YAAQ,kBAAiB;EAC3C,SAAS,gBAAgB,IAAI,SAAS,SAAS;CACjD;CAEA,IAAI,QAAQ,aACV,OAAO,OAAO,QAAQ,QAAQ,YAAY,GAAG,CAAC;CAGhD,OAAO;AACT;;;;AAOA,SAAgB,wBACd,QACA,UAA6B,CAAC,GAC2C;CACzE,MAAM,EACJ,MACA,SACA,cACA,eACA,eAAe,SACf,gBAAgB,QAChB,aAAa,SACb,yBAAyB,QACvB;CACJ,MAAM,WAAW,gBAAgB,OAAO;CACxC,MAAM,eAAeC,sBAAAA,oBAAoB,aAAa;CAEtD,OAAO,SAAS,sBACd,KACA,KACA,MACM;;EACN,IAAA,SAAA,QAAA,SAAA,KAAA,IAAA,KAAA,IAAI,KAAO,GAAG,GAAG;GACf,KAAK;GACL;EACF;EAEA,MAAM,UAAUC,sBAAAA,sBAAsB,KAAK,YAAY;EACvD,MAAM,UAAU,KAAK,IAAI;EACzB,MAAM,aAAa,gBAAgB,KAAK,SAAS,SAAS,QAAQ;EAGlE,IAAI,iBAAiB,UACnB,KACE,OAAO,SAAS,cAAc,mBAAmB;GAC/C,GAAG;GACH,GAAI,WAAW,IAAI,OAAO,EAAE,MAAM,aAAa,IAAI,MAAM,YAAY,EAAE,IAAI,CAAC;EAC9E,CAAC,CACH;EAUF,IAAI,YAAY;EAChB,MAAM,iBAAuB;GAC3B,IAAI,WAAW;GACf,YAAY;GAEZ,MAAM,WAAW,KAAK,IAAI,IAAI;GAC9B,MAAM,SAAS,IAAI,cAAc;GACjC,MAAM,QAAQ,UAAU,MAAM,aAAa;GAE3C,KACE,OAAO,SAAS,OAAO,qBAAqB;IAC1C,GAAG;IACH,YAAY;IACZ;GACF,CAAC,CACH;GAEA,IAAI,WAAW,wBACb,KACE,OAAO,KAAK,yBAAyB;IACnC,GAAG;IACH,YAAY;IACZ;IACA,WAAW;GACb,CAAC,CACH;EAEJ;EAEA,CAAA,YAAA,IAAI,UAAA,QAAA,cAAA,KAAA,KAAA,UAAA,KAAA,KAAO,UAAU,QAAQ;EAG7B,CAAA,aAAA,IAAI,UAAA,QAAA,eAAA,KAAA,KAAA,WAAA,KAAA,KAAO,SAAS,QAAQ;EAE5B,KAAK;CACP;AACF;;;;;;;;;AAgBA,SAAgB,oBAAoB,QAAqB,UAA6B,CAAC,GAAG;CACxF,MAAM,EACJ,MACA,eACA,eAAe,SACf,gBAAgB,QAChB,aAAa,SACb,yBAAyB,QACvB;CACJ,MAAM,WAAW,gBAAgB,OAAO;CACxC,MAAM,eAAeD,sBAAAA,oBAAoB,aAAa;CAEtD,SAAS,qBAAqB,SAA0B,OAAgB,MAAwB;EAC9F,QAAQ,QAAQ,cAAc,SAAkB,QAAiB,aAAyB;GACxF,MAAM,MAAM;GACZ,IAAA,SAAA,QAAA,SAAA,KAAA,IAAA,KAAA,IAAI,KAAO,GAAG,GAAG;IACf,SAAS;IACT;GACF;GAEA,MAAM,UAAUC,sBAAAA,sBAAsB,KAAK,YAAY;GACvD,IAAI,gBAAgB,KAAK,IAAI;GAE7B,IAAI,iBAAiB,UACnB,KACE,OAAO,SACL,cACA,mBACA,gBAAgB,KAAK,SAAS,SAAS,QAAQ,CACjD,CACF;GAEF,SAAS;EACX,CAAC;EAED,QAAQ,QAAQ,eAAe,SAAkB,OAAgB,aAAyB;GACxF,MAAM,MAAM;GACZ,IAAI,IAAI,kBAAkB,KAAA,GAAW;IACnC,SAAS;IACT;GACF;GAEA,MAAM,MAAM;GACZ,MAAM,WAAW,KAAK,IAAI,IAAI,IAAI;GAClC,MAAM,SAAS,IAAI,cAAc;GACjC,MAAM,UAAUA,sBAAAA,sBAAsB,KAAK,YAAY;GACvD,MAAM,QAAQ,UAAU,MAAM,aAAa;GAC3C,MAAM,aAAa,gBAAgB,KAAK,SAAS,SAAS,QAAQ;GAElE,KACE,OAAO,SAAS,OAAO,qBAAqB;IAC1C,GAAG;IACH,YAAY;IACZ;GACF,CAAC,CACH;GAEA,IAAI,WAAW,wBACb,KACE,OAAO,KAAK,yBAAyB;IACnC;IACA,KAAK,WAAW;IAChB,QAAQ,IAAI;IACZ;IACA,WAAW;GACb,CAAC,CACH;GAGF,SAAS;EACX,CAAC;EAED,KAAK;CACP;CAEA,OAAO,OAAO,OAAO,sBAAsB;GACxC,OAAO,IAAI,eAAe,IAAI;GAC9B,OAAO,IAAI,sBAAsB,IAAI;CACxC,CAAC;AACH;;;;;;AASA,SAAS,aAAa,MAAe,WAAW,MAAe;CAC7D,IAAI,OAAO,SAAS,UAClB,OAAO,KAAK,SAAS,WAAW,KAAK,MAAM,GAAG,QAAQ,IAAI,iBAAiB;CAE7E,IAAI,QAAQ,OAAO,SAAS,UAAU;EAEpC,IADYC,uBAAAA,gBAAgB,IACtB,CAAC,CAAC,UAAU,UAAU,OAAO;EACnC,OAAO,WAAW,MAAM,EAAE,MAAM,SAAS,GAAG,mBAAG,IAAI,IAAI,CAAC;CAC1D;CACA,OAAO;AACT;AAEA,MAAM,YAAY;AAClB,MAAM,kBAAkB;AAExB,SAAS,WACP,OACA,QACA,OACA,WACS;CACT,IAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,MAAM,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC;EACnD,OAAO,QAAQ,IAAI;EACnB,OAAO,IAAI,SAAS,MAAM,SAAS,MAAM,YAAY;CACvD;CACA,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;EAC/C,OAAO,QAAQ;EACf,OAAO;CACT;CACA,IAAI,iBAAiB,MAAM;EACzB,OAAO,QAAQ;EACf,OAAO,OAAO,MAAM,MAAM,QAAQ,CAAC,IAAI,iBAAiB,MAAM,YAAY;CAC5E;CACA,IAAI,iBAAiB,eAAe,YAAY,OAAO,KAAK,GAAG;EAC7D,OAAO,QAAQ;EACf,OAAO,WAAW,MAAM,WAAW;CACrC;CACA,IAAI,iBAAiB,OAAO,OAAO;CACnC,IAAI,UAAU,IAAI,KAAK,KAAK,SAAS,iBAAiB,OAAO;CAC7D,UAAU,IAAI,KAAK;CACnB,IAAI;EACF,IAAI,MAAM,QAAQ,KAAK,KAAK,iBAAiB,OAAO,iBAAiB,KAAK;GACxE,MAAM,QAAmB,CAAC;GAC1B,KAAK,MAAM,QAAQ,iBAAiB,MAAM,MAAM,QAAQ,IAAI,OAAO;IACjE,IAAI,OAAO,QAAQ,GAAG;KACpB,MAAM,KAAK,SAAS;KACpB;IACF;IACA,MAAM,KAAK,WAAW,MAAM,QAAQ,QAAQ,GAAG,SAAS,CAAC;GAC3D;GACA,OAAO;EACT;EACA,MAAM,SAAU,MAA+B;EAC/C,IAAI,OAAO,WAAW,YAAY;GAChC,MAAM,OAAgB,OAAO,KAAK,KAAK;GACvC,IAAI,SAAS,OAAO,OAAO,WAAW,MAAM,QAAQ,QAAQ,GAAG,SAAS;EAC1E;EACA,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,GAAG;GAC1C,IAAI,OAAO,QAAQ,GAAG;IACpB,OAAO,OAAO;IACd;GACF;GACA,OAAO,QAAQ,EAAE;GACjB,qBAAA,UAAU,QAAQ,GAAG,WAAW,GAAG,QAAQ,QAAQ,GAAG,SAAS,CAAC;EAClE;EACA,OAAO;CACT,QAAQ;EACN,OAAO;CACT,UAAU;EACR,UAAU,OAAO,KAAK;CACxB;AACF"}
|
package/dist/middleware.mjs
CHANGED
|
@@ -1,41 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { i as
|
|
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:
|
|
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
|
|
195
|
+
assignOwn(record, k, pruneValue(v, budget, depth + 1, ancestors));
|
|
226
196
|
}
|
|
227
197
|
return record;
|
|
228
198
|
} catch {
|