evlog 2.18.1 → 2.19.0
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/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/axiom.mjs +2 -2
- package/dist/adapters/better-stack.d.mts +1 -1
- package/dist/adapters/better-stack.mjs +2 -2
- package/dist/adapters/datadog.d.mts +1 -1
- package/dist/adapters/datadog.mjs +2 -2
- package/dist/adapters/fs.d.mts +1 -1
- package/dist/adapters/fs.mjs +2 -2
- package/dist/adapters/hyperdx.d.mts +1 -1
- package/dist/adapters/hyperdx.mjs +1 -1
- package/dist/adapters/memory.d.mts +1 -1
- package/dist/adapters/memory.mjs +1 -1
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/otlp.mjs +4 -4
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/posthog.mjs +2 -2
- package/dist/adapters/sentry.d.mts +1 -1
- package/dist/adapters/sentry.mjs +3 -3
- package/dist/ai/index.d.mts +1 -1
- package/dist/{audit-BUI3af4w.mjs → audit-BFwTUxBJ.mjs} +357 -101
- package/dist/audit-BFwTUxBJ.mjs.map +1 -0
- package/dist/{audit-DVdkntSO.d.mts → audit-BUAajsPU.d.mts} +106 -35
- package/dist/audit-BUAajsPU.d.mts.map +1 -0
- package/dist/better-auth/index.d.mts +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/{define-D6OJdSUH.mjs → define-Bpaymi-h.mjs} +2 -1
- package/dist/define-Bpaymi-h.mjs.map +1 -0
- package/dist/{define-D-BVMf2l.d.mts → define-DGwZkZ7x.d.mts} +8 -3
- package/dist/define-DGwZkZ7x.d.mts.map +1 -0
- package/dist/dev-terminal-D4UaEm17.mjs +54 -0
- package/dist/dev-terminal-D4UaEm17.mjs.map +1 -0
- package/dist/{dist-H3GIh-KK.mjs → dist-DdQWiZn8.mjs} +1 -1
- package/dist/{dist-H3GIh-KK.mjs.map → dist-DdQWiZn8.mjs.map} +1 -1
- package/dist/{drain-7n3K6kPe.mjs → drain-D_fy7m0n.mjs} +3 -3
- package/dist/{drain-7n3K6kPe.mjs.map → drain-D_fy7m0n.mjs.map} +1 -1
- package/dist/elysia/index.d.mts +3 -3
- package/dist/elysia/index.d.mts.map +1 -1
- package/dist/elysia/index.mjs +8 -5
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/enrich-drain-CG_2Nix-.mjs +122 -0
- package/dist/enrich-drain-CG_2Nix-.mjs.map +1 -0
- package/dist/{enricher-UW9npoB2.d.mts → enricher-CuMbbdqp.d.mts} +2 -2
- package/dist/{enricher-UW9npoB2.d.mts.map → enricher-CuMbbdqp.d.mts.map} +1 -1
- package/dist/{enricher-N0erZS87.mjs → enricher-DAWf2-Fx.mjs} +2 -2
- package/dist/{enricher-N0erZS87.mjs.map → enricher-DAWf2-Fx.mjs.map} +1 -1
- package/dist/enrichers.d.mts +2 -2
- package/dist/enrichers.mjs +2 -2
- package/dist/{error-CVtn5U7b.d.mts → error-DwajXSKM.d.mts} +2 -2
- package/dist/{error-CVtn5U7b.d.mts.map → error-DwajXSKM.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/{errors-dEMNQCiL.d.mts → errors-CAq8pYpW.d.mts} +2 -2
- package/dist/{errors-dEMNQCiL.d.mts.map → errors-CAq8pYpW.d.mts.map} +1 -1
- package/dist/{errors-BQgyQ9xe.mjs → errors-DA0cyr8q.mjs} +1 -1
- package/dist/{errors-BQgyQ9xe.mjs.map → errors-DA0cyr8q.mjs.map} +1 -1
- package/dist/{event-1BMl7o0k.mjs → event-qwAv-7AZ.mjs} +1 -1
- package/dist/{event-1BMl7o0k.mjs.map → event-qwAv-7AZ.mjs.map} +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.mjs +3 -3
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.mjs +2 -2
- package/dist/{fork-Bga8x-X4.mjs → fork-CYm453dq.mjs} +39 -14
- package/dist/fork-CYm453dq.mjs.map +1 -0
- package/dist/{headers-CU-QqnYg.mjs → headers-VtmnWcfn.mjs} +1 -1
- package/dist/{headers-CU-QqnYg.mjs.map → headers-VtmnWcfn.mjs.map} +1 -1
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.d.mts.map +1 -1
- package/dist/hono/index.mjs +10 -2
- package/dist/hono/index.mjs.map +1 -1
- package/dist/{http-B6YgAhyN.mjs → http-Bept5EIC.mjs} +2 -2
- package/dist/{http-B6YgAhyN.mjs.map → http-Bept5EIC.mjs.map} +1 -1
- package/dist/http.d.mts +1 -1
- package/dist/{index-ZSRQP_BI.d.mts → index-CE7kH0II.d.mts} +15 -8
- package/dist/index-CE7kH0II.d.mts.map +1 -0
- package/dist/index.d.mts +9 -9
- package/dist/index.mjs +9 -15
- package/dist/index.mjs.map +1 -1
- package/dist/{integration-Dhig7ae6.mjs → integration-CR601uyW.mjs} +3 -3
- package/dist/{integration-Dhig7ae6.mjs.map → integration-CR601uyW.mjs.map} +1 -1
- package/dist/{logger-CTcvd5Cc.d.mts → logger-BccCJUyD.d.mts} +8 -4
- package/dist/logger-BccCJUyD.d.mts.map +1 -0
- package/dist/logger.d.mts +2 -2
- package/dist/logger.mjs +2 -2
- package/dist/{middleware-31KhtiEF.d.mts → middleware-DQ6-h8h0.d.mts} +9 -2
- package/dist/middleware-DQ6-h8h0.d.mts.map +1 -0
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.mjs +4 -4
- package/dist/next/client.d.mts +1 -1
- package/dist/next/index.d.mts +4 -4
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +35 -37
- package/dist/next/index.mjs.map +1 -1
- package/dist/next/instrumentation.d.mts +1 -1
- package/dist/next/instrumentation.mjs +1 -1
- package/dist/nitro/errorHandler.mjs +10 -16
- package/dist/nitro/errorHandler.mjs.map +1 -1
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/module.d.mts.map +1 -1
- package/dist/nitro/module.mjs +2 -1
- package/dist/nitro/module.mjs.map +1 -1
- package/dist/nitro/plugin.mjs +36 -65
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.d.mts +0 -7
- package/dist/nitro/v3/errorHandler.mjs +13 -15
- package/dist/nitro/v3/errorHandler.mjs.map +1 -1
- package/dist/nitro/v3/index.d.mts +2 -2
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/module.d.mts.map +1 -1
- package/dist/nitro/v3/module.mjs +3 -3
- package/dist/nitro/v3/module.mjs.map +1 -1
- package/dist/nitro/v3/plugin.mjs +76 -44
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/nitro-ClRZLD1g.mjs +96 -0
- package/dist/nitro-ClRZLD1g.mjs.map +1 -0
- package/dist/{nitro-BRddgqSb.d.mts → nitro-zCXTylj4.d.mts} +7 -2
- package/dist/nitro-zCXTylj4.d.mts.map +1 -0
- package/dist/{nitroConfigBridge-NbFn-sIK.mjs → nitroConfigBridge-BkVWnSV3.mjs} +9 -2
- package/dist/nitroConfigBridge-BkVWnSV3.mjs.map +1 -0
- package/dist/{nodeResponse-BkkionWl.mjs → nodeResponse-CIEEbrNE.mjs} +1 -1
- package/dist/{nodeResponse-BkkionWl.mjs.map → nodeResponse-CIEEbrNE.mjs.map} +1 -1
- package/dist/nuxt/module.d.mts +6 -1
- package/dist/nuxt/module.d.mts.map +1 -1
- package/dist/nuxt/module.mjs +11 -4
- package/dist/nuxt/module.mjs.map +1 -1
- package/dist/orpc/index.d.mts +2 -2
- package/dist/orpc/index.d.mts.map +1 -1
- package/dist/orpc/index.mjs +5 -4
- package/dist/orpc/index.mjs.map +1 -1
- package/dist/{package-B23bR3tK.mjs → package-CUhII9DA.mjs} +2 -2
- package/dist/package-CUhII9DA.mjs.map +1 -0
- package/dist/{parseError-D4PIxEWo.d.mts → parseError-Cagr-Ctc.d.mts} +2 -2
- package/dist/parseError-Cagr-Ctc.d.mts.map +1 -0
- package/dist/pretty-error-CVVgwlTn.mjs +278 -0
- package/dist/pretty-error-CVVgwlTn.mjs.map +1 -0
- package/dist/pretty-error-snippet.node-c_bzjg7g.mjs +47 -0
- package/dist/pretty-error-snippet.node-c_bzjg7g.mjs.map +1 -0
- package/dist/react-router/index.d.mts +2 -2
- package/dist/react-router/index.mjs +5 -6
- package/dist/react-router/index.mjs.map +1 -1
- package/dist/{routes-CnIgYWf8.mjs → routes-4rMzRyTk.mjs} +1 -1
- package/dist/{routes-CnIgYWf8.mjs.map → routes-4rMzRyTk.mjs.map} +1 -1
- package/dist/runtime/client/log.d.mts +1 -1
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs +1 -1
- package/dist/runtime/server/useLogger.d.mts +1 -1
- package/dist/runtime/utils/parseError.d.mts +2 -2
- package/dist/runtime/utils/parseError.mjs +1 -1
- package/dist/{severity-R5Egq3qz.mjs → severity-CwXUSHt3.mjs} +1 -1
- package/dist/{severity-R5Egq3qz.mjs.map → severity-CwXUSHt3.mjs.map} +1 -1
- package/dist/{source-location-Dco0cRTz.mjs → source-location-xkDGiERl.mjs} +1 -1
- package/dist/{source-location-Dco0cRTz.mjs.map → source-location-xkDGiERl.mjs.map} +1 -1
- package/dist/{storage-BNubsWwz.mjs → storage-7X37OToT.mjs} +1 -1
- package/dist/{storage-BNubsWwz.mjs.map → storage-7X37OToT.mjs.map} +1 -1
- package/dist/stream.d.mts +1 -1
- package/dist/stream.mjs +1 -1
- package/dist/streamResponse-CmQ3qUbF.mjs +94 -0
- package/dist/streamResponse-CmQ3qUbF.mjs.map +1 -0
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.d.mts.map +1 -1
- package/dist/sveltekit/index.mjs +7 -8
- package/dist/sveltekit/index.mjs.map +1 -1
- package/dist/toolkit.d.mts +37 -6
- package/dist/toolkit.d.mts.map +1 -1
- package/dist/toolkit.mjs +15 -14
- package/dist/types.d.mts +2 -2
- package/dist/{useLogger-CqvH6qOf.d.mts → useLogger-Dv52PDpH.d.mts} +2 -2
- package/dist/{useLogger-CqvH6qOf.d.mts.map → useLogger-Dv52PDpH.d.mts.map} +1 -1
- package/dist/{utils-DxqvIOyR.d.mts → utils-DmNbZwBZ.d.mts} +11 -3
- package/dist/{utils-DxqvIOyR.d.mts.map → utils-DmNbZwBZ.d.mts.map} +1 -1
- package/dist/utils.d.mts +2 -2
- package/dist/utils.mjs +21 -8
- package/dist/utils.mjs.map +1 -1
- package/dist/vite/index.d.mts +1 -1
- package/dist/vite/index.mjs +1 -1
- package/dist/workers.d.mts +1 -1
- package/dist/workers.mjs +1 -1
- package/package.json +1 -1
- package/dist/audit-BUI3af4w.mjs.map +0 -1
- package/dist/audit-DVdkntSO.d.mts.map +0 -1
- package/dist/define-D-BVMf2l.d.mts.map +0 -1
- package/dist/define-D6OJdSUH.mjs.map +0 -1
- package/dist/fork-Bga8x-X4.mjs.map +0 -1
- package/dist/index-ZSRQP_BI.d.mts.map +0 -1
- package/dist/logger-CTcvd5Cc.d.mts.map +0 -1
- package/dist/middleware-31KhtiEF.d.mts.map +0 -1
- package/dist/nitro-BRddgqSb.d.mts.map +0 -1
- package/dist/nitro-DErMq_Zj.mjs +0 -34
- package/dist/nitro-DErMq_Zj.mjs.map +0 -1
- package/dist/nitroConfigBridge-NbFn-sIK.mjs.map +0 -1
- package/dist/package-B23bR3tK.mjs.map +0 -1
- package/dist/parseError-D4PIxEWo.d.mts.map +0 -1
|
@@ -1,8 +1,118 @@
|
|
|
1
|
-
import { colors, cssColors, detectEnvironment, escapeFormatString, formatDuration, getConsoleMethod, getCssLevelColor, getLevelColor, isBrowser, isDev, isLevelEnabled, matchesPattern } from "./utils.mjs";
|
|
2
|
-
import {
|
|
1
|
+
import { colors, cssColors, detectEnvironment, escapeFormatString, formatDuration, getConsoleMethod, getCssLevelColor, getLevelColor, globToRegExp, isBrowser, isDev, isLevelEnabled, matchesPattern } from "./utils.mjs";
|
|
2
|
+
import { i as registerPrettyErrorSnippetReader, n as buildErrorEntries } from "./pretty-error-CVVgwlTn.mjs";
|
|
3
|
+
import { t as resolveDevTerminal } from "./dev-terminal-D4UaEm17.mjs";
|
|
4
|
+
import { EvlogError } from "./error.mjs";
|
|
5
|
+
import { r as getHeader$1 } from "./headers-VtmnWcfn.mjs";
|
|
3
6
|
//#region src/redact.ts
|
|
4
7
|
const DEFAULT_REPLACEMENT = "[REDACTED]";
|
|
5
8
|
/**
|
|
9
|
+
* Normalize a redact path pattern.
|
|
10
|
+
* Single segments without wildcards are shorthand for `**.<segment>`.
|
|
11
|
+
*/
|
|
12
|
+
function normalizeRedactPathPattern(pattern) {
|
|
13
|
+
if (!pattern.includes("*") && !pattern.includes(".")) return `**.${pattern}`;
|
|
14
|
+
return pattern;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compile `RedactConfig.paths` into exact paths, path globs, and key globs.
|
|
18
|
+
* Returns `undefined` when `patterns` is empty.
|
|
19
|
+
*/
|
|
20
|
+
function compileRedactPathMatchers(patterns) {
|
|
21
|
+
if (!patterns?.length) return void 0;
|
|
22
|
+
const exactPaths = /* @__PURE__ */ new Set();
|
|
23
|
+
const pathGlobs = [];
|
|
24
|
+
const keyGlobs = [];
|
|
25
|
+
const caseInsensitiveLeaves = /* @__PURE__ */ new Set();
|
|
26
|
+
for (const raw of patterns) {
|
|
27
|
+
if (!raw.includes("*")) {
|
|
28
|
+
if (raw.includes(".")) exactPaths.add(raw);
|
|
29
|
+
else addPathGlobPattern(normalizeRedactPathPattern(raw), exactPaths, pathGlobs, caseInsensitiveLeaves);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!raw.includes(".")) keyGlobs.push(globToRegExp(raw, "."));
|
|
33
|
+
else addPathGlobPattern(raw, exactPaths, pathGlobs, caseInsensitiveLeaves);
|
|
34
|
+
}
|
|
35
|
+
if (exactPaths.size === 0 && pathGlobs.length === 0 && keyGlobs.length === 0 && caseInsensitiveLeaves.size === 0) return;
|
|
36
|
+
return {
|
|
37
|
+
exactPaths,
|
|
38
|
+
pathGlobs,
|
|
39
|
+
keyGlobs,
|
|
40
|
+
caseInsensitiveLeaves
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** `**.segment` also matches a top-level `segment` field. */
|
|
44
|
+
function addPathGlobPattern(pattern, exactPaths, pathGlobs, caseInsensitiveLeaves) {
|
|
45
|
+
pathGlobs.push(globToRegExp(pattern, "."));
|
|
46
|
+
const leaf = pattern.match(/^\*\*\.([^.?*]+)$/);
|
|
47
|
+
if (leaf) {
|
|
48
|
+
exactPaths.add(leaf[1]);
|
|
49
|
+
caseInsensitiveLeaves.add(leaf[1]);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Whether a field at `fullPath` (dot-notation from root) with leaf key `leafKey`
|
|
54
|
+
* should be fully redacted.
|
|
55
|
+
*/
|
|
56
|
+
function matchesRedactPath(fullPath, leafKey, matchers) {
|
|
57
|
+
if (matchers.exactPaths.has(fullPath)) return true;
|
|
58
|
+
const leafLower = leafKey.toLowerCase();
|
|
59
|
+
for (const name of matchers.caseInsensitiveLeaves) if (leafLower === name.toLowerCase()) return true;
|
|
60
|
+
for (const glob of matchers.pathGlobs) {
|
|
61
|
+
glob.lastIndex = 0;
|
|
62
|
+
if (glob.test(fullPath)) return true;
|
|
63
|
+
}
|
|
64
|
+
for (const glob of matchers.keyGlobs) {
|
|
65
|
+
glob.lastIndex = 0;
|
|
66
|
+
if (glob.test(leafKey)) return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Redact fields matching path globs recursively. Mutates `obj` in place (use on a clone).
|
|
72
|
+
*/
|
|
73
|
+
function redactPathsInTree(obj, matchers, replacement, prefix = "") {
|
|
74
|
+
if (obj === null || obj === void 0) return;
|
|
75
|
+
if (Array.isArray(obj)) {
|
|
76
|
+
for (let i = 0; i < obj.length; i++) {
|
|
77
|
+
const segment = String(i);
|
|
78
|
+
const fullPath = prefix ? `${prefix}.${segment}` : segment;
|
|
79
|
+
redactPathsInTree(obj[i], matchers, replacement, fullPath);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (typeof obj === "object") {
|
|
84
|
+
const record = obj;
|
|
85
|
+
for (const key in record) {
|
|
86
|
+
const fullPath = prefix ? `${prefix}.${key}` : key;
|
|
87
|
+
if (matchesRedactPath(fullPath, key, matchers)) record[key] = replacement;
|
|
88
|
+
else redactPathsInTree(record[key], matchers, replacement, fullPath);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Return a copy of `value` with path-pattern matches replaced by `replacement`.
|
|
94
|
+
* Used by audit diffs; does not mutate the input.
|
|
95
|
+
*
|
|
96
|
+
* `pointerPath` is a JSON Pointer (e.g. `/user/password`).
|
|
97
|
+
*/
|
|
98
|
+
function redactValueByPaths(value, matchers, replacement, pointerPath = "") {
|
|
99
|
+
const segments = pointerPath.split("/").filter(Boolean);
|
|
100
|
+
const dotPath = segments.join(".");
|
|
101
|
+
const leafKey = segments.at(-1) ?? "";
|
|
102
|
+
if (value === null || typeof value !== "object") {
|
|
103
|
+
if (dotPath && matchesRedactPath(dotPath, leafKey, matchers)) return replacement;
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(value)) return value.map((v, i) => redactValueByPaths(v, matchers, replacement, `${pointerPath}/${i}`));
|
|
107
|
+
if (!isPlainRecord(value)) return value;
|
|
108
|
+
const out = {};
|
|
109
|
+
for (const [k, v] of Object.entries(value)) {
|
|
110
|
+
const childPointer = pointerPath ? `${pointerPath}/${k}` : `/${k}`;
|
|
111
|
+
out[k] = matchesRedactPath(dotPath ? `${dotPath}.${k}` : k, k, matchers) ? replacement : redactValueByPaths(v, matchers, replacement, childPointer);
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
6
116
|
* Built-in PII detection patterns with smart masking.
|
|
7
117
|
* Each builtin preserves just enough signal for debugging while scrubbing PII.
|
|
8
118
|
*/
|
|
@@ -89,32 +199,56 @@ function allBuiltinMaskers() {
|
|
|
89
199
|
function cloneRegex(re) {
|
|
90
200
|
return new RegExp(re.source, re.flags);
|
|
91
201
|
}
|
|
202
|
+
/** @internal Set on wide events after initLogger redaction so middleware skips a second pass. */
|
|
203
|
+
const globallyRedacted = Symbol.for("evlog.globallyRedacted");
|
|
204
|
+
/** @internal Mark a wide event as already redacted by {@link initLogger}. */
|
|
205
|
+
function markGloballyRedacted(event) {
|
|
206
|
+
Object.defineProperty(event, globallyRedacted, {
|
|
207
|
+
value: true,
|
|
208
|
+
enumerable: false,
|
|
209
|
+
configurable: true
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/** @internal Whether global redaction already ran on this wide event. */
|
|
213
|
+
function isGloballyRedacted(event) {
|
|
214
|
+
return Reflect.has(event, globallyRedacted);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Clone before redaction. Wide events are JSON-shaped; fall back when
|
|
218
|
+
* `structuredClone` rejects non-cloneable values (functions, symbols, etc.).
|
|
219
|
+
*/
|
|
220
|
+
function cloneForRedaction(event) {
|
|
221
|
+
try {
|
|
222
|
+
return structuredClone(event);
|
|
223
|
+
} catch {
|
|
224
|
+
try {
|
|
225
|
+
return JSON.parse(JSON.stringify(event));
|
|
226
|
+
} catch {
|
|
227
|
+
console.warn("[cloneForRedaction] Shallow clone used — nested objects may be mutated by redactPath, redactPatterns, and applyMaskersToTree");
|
|
228
|
+
return { ...event };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
92
232
|
/**
|
|
93
|
-
* Redact sensitive data from a wide event
|
|
233
|
+
* Redact sensitive data from a wide event without mutating the input.
|
|
94
234
|
*
|
|
95
|
-
* Three strategies
|
|
96
|
-
* 1. **Path-based**: dot-notation paths
|
|
235
|
+
* Returns a deep clone with redaction applied. Three strategies run in order:
|
|
236
|
+
* 1. **Path-based**: dot-notation paths with optional globs (`password`, `**.password`, `*_token`, `user.*`) — full value replacement.
|
|
97
237
|
* 2. **Masker-based**: built-in patterns with smart partial masking (e.g. `****1111`).
|
|
98
|
-
* 3. **Pattern-based**: custom RegExp patterns replaced with `replacement`.
|
|
238
|
+
* 3. **Pattern-based**: custom RegExp patterns on string values replaced with `replacement`.
|
|
99
239
|
*
|
|
100
|
-
* @param event - The wide event object (mutated
|
|
240
|
+
* @param event - The wide event object (not mutated).
|
|
101
241
|
* @param config - Redaction configuration.
|
|
242
|
+
* @returns A redacted deep clone of `event`.
|
|
102
243
|
*/
|
|
103
244
|
function redactEvent(event, config) {
|
|
245
|
+
const clone = cloneForRedaction(event);
|
|
104
246
|
const replacement = config.replacement ?? DEFAULT_REPLACEMENT;
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
if (config.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
let current = obj;
|
|
111
|
-
for (let i = 0; i < segments.length - 1; i++) {
|
|
112
|
-
if (current === null || current === void 0 || typeof current !== "object") return;
|
|
113
|
-
current = current[segments[i]];
|
|
114
|
-
}
|
|
115
|
-
if (current === null || current === void 0 || typeof current !== "object") return;
|
|
116
|
-
const leaf = segments[segments.length - 1];
|
|
117
|
-
if (leaf in current) current[leaf] = replacement;
|
|
247
|
+
const pathMatchers = compileRedactPathMatchers(config.paths);
|
|
248
|
+
if (pathMatchers) redactPathsInTree(clone, pathMatchers, replacement);
|
|
249
|
+
if (config._maskers?.length) applyMaskersToTree(clone, config._maskers);
|
|
250
|
+
if (config.patterns?.length) redactPatterns(clone, config.patterns, replacement);
|
|
251
|
+
return clone;
|
|
118
252
|
}
|
|
119
253
|
function redactPatterns(obj, patterns, replacement) {
|
|
120
254
|
if (obj === null || obj === void 0) return;
|
|
@@ -177,17 +311,34 @@ function normalizeRedactConfig(raw) {
|
|
|
177
311
|
if (typeof raw.replacement === "string") config.replacement = raw.replacement;
|
|
178
312
|
if (raw.builtins === false) config.builtins = false;
|
|
179
313
|
else if (Array.isArray(raw.builtins)) config.builtins = raw.builtins;
|
|
180
|
-
if (Array.isArray(raw.patterns)) config.patterns = raw.patterns
|
|
181
|
-
if (p instanceof RegExp) return p;
|
|
182
|
-
if (typeof p === "string") return new RegExp(p, "g");
|
|
183
|
-
if (typeof p === "object" && p !== null) {
|
|
184
|
-
const obj = p;
|
|
185
|
-
return new RegExp(obj.source, obj.flags ?? "g");
|
|
186
|
-
}
|
|
187
|
-
return null;
|
|
188
|
-
}).filter((p) => p !== null);
|
|
314
|
+
if (Array.isArray(raw.patterns)) config.patterns = deserializeRegexList(raw.patterns);
|
|
189
315
|
return resolveRedactConfig(config);
|
|
190
316
|
}
|
|
317
|
+
function isPlainRecord(value) {
|
|
318
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
319
|
+
const proto = Object.getPrototypeOf(value);
|
|
320
|
+
return proto === Object.prototype || proto === null;
|
|
321
|
+
}
|
|
322
|
+
function deserializeRegexList(raw) {
|
|
323
|
+
const patterns = [];
|
|
324
|
+
for (const p of raw) try {
|
|
325
|
+
if (p instanceof RegExp) {
|
|
326
|
+
patterns.push(cloneRegex(p));
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (typeof p === "string") {
|
|
330
|
+
patterns.push(new RegExp(p, "g"));
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (typeof p === "object" && p !== null && typeof p.source === "string") {
|
|
334
|
+
const flags = typeof p.flags === "string" ? p.flags : "g";
|
|
335
|
+
patterns.push(new RegExp(p.source, flags));
|
|
336
|
+
}
|
|
337
|
+
} catch {
|
|
338
|
+
console.warn("[normalizeRedactConfig] Ignoring invalid redact regex entry");
|
|
339
|
+
}
|
|
340
|
+
return patterns;
|
|
341
|
+
}
|
|
191
342
|
//#endregion
|
|
192
343
|
//#region src/shared/plugin.ts
|
|
193
344
|
/** Identity helper preserving plugin type inference. */
|
|
@@ -337,11 +488,33 @@ function mergeInto(target, source) {
|
|
|
337
488
|
else target[key] = sourceVal;
|
|
338
489
|
}
|
|
339
490
|
}
|
|
491
|
+
const pendingDrainState = /* @__PURE__ */ new WeakMap();
|
|
492
|
+
function isAiOnlyFieldUpdate(data) {
|
|
493
|
+
const keys = Object.keys(data);
|
|
494
|
+
return keys.length === 1 && keys[0] === "ai";
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Mark a wide event as past the post-emit AI merge window so late `log.set({ ai })`
|
|
498
|
+
* calls warn again. Called by framework enrich/drain pipelines before drain runs.
|
|
499
|
+
*
|
|
500
|
+
* @internal Used by middleware and framework integrations.
|
|
501
|
+
*/
|
|
502
|
+
function markWideEventDrainStarted(event) {
|
|
503
|
+
if (!event) return;
|
|
504
|
+
const state = pendingDrainState.get(event);
|
|
505
|
+
if (state) state.drainStarted = true;
|
|
506
|
+
}
|
|
340
507
|
let globalEnv = {
|
|
341
508
|
service: "app",
|
|
342
509
|
environment: "development"
|
|
343
510
|
};
|
|
344
511
|
let globalPretty = isDev();
|
|
512
|
+
let globalPrettyError = {
|
|
513
|
+
snippet: isDev(),
|
|
514
|
+
stackDepth: 2,
|
|
515
|
+
compact: isDev(),
|
|
516
|
+
detail: "full"
|
|
517
|
+
};
|
|
345
518
|
let globalSampling = {};
|
|
346
519
|
let globalStringify = true;
|
|
347
520
|
let globalDrain;
|
|
@@ -367,6 +540,7 @@ function initLogger(config = {}) {
|
|
|
367
540
|
region: config.env?.region ?? detected.region
|
|
368
541
|
};
|
|
369
542
|
globalPretty = config.pretty ?? isDev();
|
|
543
|
+
globalPrettyError = resolveDevTerminal(config).prettyError;
|
|
370
544
|
globalSampling = config.sampling ?? {};
|
|
371
545
|
globalStringify = config.stringify ?? true;
|
|
372
546
|
globalDrain = config.drain;
|
|
@@ -375,6 +549,11 @@ function initLogger(config = {}) {
|
|
|
375
549
|
globalMinLevel = config.minLevel ?? "debug";
|
|
376
550
|
globalPluginRunner = config.plugins?.length ? createPluginRunner(config.plugins) : getEmptyPluginRunner();
|
|
377
551
|
if (globalPluginRunner.plugins.length > 0) globalPluginRunner.runSetup({ env: { ...globalEnv } });
|
|
552
|
+
if (!isBrowser() && typeof process !== "undefined" && process.versions?.node) import("./pretty-error-snippet.node-c_bzjg7g.mjs").then((n) => n.t).then((mod) => {
|
|
553
|
+
registerPrettyErrorSnippetReader(mod.readCodeSnippetFromDisk);
|
|
554
|
+
}).catch(() => {
|
|
555
|
+
registerPrettyErrorSnippetReader(null);
|
|
556
|
+
});
|
|
378
557
|
const hasAnyDrain = !!globalDrain || globalPluginRunner.hasDrain;
|
|
379
558
|
if (globalSilent && !hasAnyDrain && !config._suppressDrainWarning) console.warn("[evlog] silent mode is enabled but no drain is configured. Events will be built and sampled but not output anywhere. Set a drain via initLogger({ drain }) or a framework hook (evlog:drain).");
|
|
380
559
|
}
|
|
@@ -464,7 +643,10 @@ function emitWideEvent(level, event, options = {}) {
|
|
|
464
643
|
...event
|
|
465
644
|
};
|
|
466
645
|
finalizeAudit(formatted);
|
|
467
|
-
if (globalRedact)
|
|
646
|
+
if (globalRedact) {
|
|
647
|
+
formatted = redactEvent(formatted, globalRedact);
|
|
648
|
+
markGloballyRedacted(formatted);
|
|
649
|
+
}
|
|
468
650
|
if (!globalSilent) if (globalPretty) prettyPrintWideEvent(formatted);
|
|
469
651
|
else if (globalStringify) console[getConsoleMethod(level)](JSON.stringify(formatted));
|
|
470
652
|
else console[getConsoleMethod(level)](formatted);
|
|
@@ -698,11 +880,29 @@ function buildAIEntries(ai) {
|
|
|
698
880
|
});
|
|
699
881
|
return entries;
|
|
700
882
|
}
|
|
883
|
+
function flushPrettyLines(lines) {
|
|
884
|
+
if (lines.length === 0) return;
|
|
885
|
+
const text = `${lines.join("\n")}\n`;
|
|
886
|
+
if (typeof process !== "undefined" && typeof process.stdout?.write === "function" && !isBrowser() && process.env.VITEST !== "true") {
|
|
887
|
+
process.stdout.write(text);
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
console.log(lines.join("\n"));
|
|
891
|
+
}
|
|
701
892
|
function prettyPrintWideEvent(event) {
|
|
702
893
|
const { timestamp, level, service, environment, version, ...rest } = event;
|
|
703
894
|
const ts = typeof timestamp === "string" ? timestamp.slice(11, 23) : "";
|
|
704
895
|
const levelLabel = typeof level === "string" ? level : "info";
|
|
705
896
|
const browser = isBrowser();
|
|
897
|
+
const lines = [];
|
|
898
|
+
const writeLine = (...args) => {
|
|
899
|
+
if (browser) {
|
|
900
|
+
console.log(...args);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const [line] = args;
|
|
904
|
+
if (typeof line === "string") lines.push(line);
|
|
905
|
+
};
|
|
706
906
|
const parts = [];
|
|
707
907
|
const styles = [];
|
|
708
908
|
if (browser) {
|
|
@@ -711,7 +911,8 @@ function prettyPrintWideEvent(event) {
|
|
|
711
911
|
styles.push(cssColors.dim, cssColors.reset, lc, cssColors.reset, cssColors.cyan, cssColors.reset);
|
|
712
912
|
} else {
|
|
713
913
|
const lc = getLevelColor(levelLabel);
|
|
714
|
-
parts.push(`${
|
|
914
|
+
if (isDev()) parts.push(`${lc}${levelLabel.toUpperCase()}${colors.reset} ${colors.cyan}[${service}]${colors.reset}`);
|
|
915
|
+
else parts.push(`${colors.dim}${ts}${colors.reset} ${lc}${levelLabel.toUpperCase()}${colors.reset} ${colors.cyan}[${service}]${colors.reset}`);
|
|
715
916
|
}
|
|
716
917
|
if (rest.method && rest.path) {
|
|
717
918
|
parts.push(browser ? ` ${escapeFormatString(String(rest.method))} ${escapeFormatString(String(rest.path))}` : ` ${rest.method} ${rest.path}`);
|
|
@@ -734,15 +935,19 @@ function prettyPrintWideEvent(event) {
|
|
|
734
935
|
} else parts.push(` ${colors.dim}in ${rest.duration}${colors.reset}`);
|
|
735
936
|
delete rest.duration;
|
|
736
937
|
}
|
|
737
|
-
|
|
938
|
+
writeLine(parts.join(""), ...styles);
|
|
738
939
|
const aiData = isPlainObject$1(rest.ai) ? rest.ai : void 0;
|
|
739
940
|
if (aiData) delete rest.ai;
|
|
941
|
+
const errorData = rest.error;
|
|
942
|
+
if (errorData !== void 0) delete rest.error;
|
|
740
943
|
const restEntries = Object.entries(rest).filter(([_, v]) => v !== void 0);
|
|
741
944
|
const aiEntries = aiData ? buildAIEntries(aiData) : [];
|
|
742
|
-
const
|
|
945
|
+
const errorEntries = errorData !== void 0 ? buildErrorEntries(errorData, globalPrettyError) : [];
|
|
946
|
+
const contextEntries = [...restEntries.map(([key, value]) => ({
|
|
743
947
|
key,
|
|
744
948
|
value: formatValue(value)
|
|
745
949
|
})), ...aiEntries];
|
|
950
|
+
const allEntries = errorEntries.length > 0 ? [...errorEntries, ...contextEntries] : contextEntries;
|
|
746
951
|
for (let i = 0; i < allEntries.length; i++) {
|
|
747
952
|
const entry = allEntries[i];
|
|
748
953
|
if (!entry) continue;
|
|
@@ -751,22 +956,32 @@ function prettyPrintWideEvent(event) {
|
|
|
751
956
|
const prefix = i === allEntries.length - 1 && !hasChildren ? "└─" : "├─";
|
|
752
957
|
if (browser) {
|
|
753
958
|
const val = entry.value ? ` ${escapeFormatString(entry.value)}` : "";
|
|
754
|
-
|
|
959
|
+
writeLine(` %c${prefix}%c %c${escapeFormatString(entry.key)}:%c${val}`, cssColors.dim, cssColors.reset, cssColors.cyan, cssColors.reset);
|
|
755
960
|
} else {
|
|
756
961
|
const val = entry.value ? ` ${entry.value}` : "";
|
|
757
|
-
|
|
962
|
+
writeLine(` ${colors.dim}${prefix}${colors.reset} ${colors.cyan}${entry.key}:${colors.reset}${val}`);
|
|
758
963
|
}
|
|
759
964
|
if (hasChildren && children) {
|
|
760
965
|
const connector = i === allEntries.length - 1 ? " " : "│";
|
|
761
966
|
for (let j = 0; j < children.length; j++) {
|
|
762
967
|
const child = children[j];
|
|
763
968
|
if (child === void 0) continue;
|
|
969
|
+
if (child === "__EVLOG_TREE_SPACER__") {
|
|
970
|
+
writeLine(` ${colors.dim}${connector}${colors.reset}`);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
764
973
|
const childPrefix = j === children.length - 1 ? "└─" : "├─";
|
|
765
|
-
if (
|
|
766
|
-
|
|
974
|
+
if (child === "") {
|
|
975
|
+
writeLine("");
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
if (browser) writeLine(` %c${connector} ${childPrefix}%c ${escapeFormatString(child)}`, cssColors.dim, cssColors.reset);
|
|
979
|
+
else if (child.startsWith(" ") || child.startsWith("\x1B")) writeLine(` ${colors.dim}${connector}${colors.reset}${child}`);
|
|
980
|
+
else writeLine(` ${colors.dim}${connector} ${childPrefix}${colors.reset} ${child}`);
|
|
767
981
|
}
|
|
768
982
|
}
|
|
769
983
|
}
|
|
984
|
+
if (!browser && lines.length > 0) flushPrettyLines(lines);
|
|
770
985
|
}
|
|
771
986
|
function createLogMethod(level) {
|
|
772
987
|
return function logMethod(tagOrEvent, message) {
|
|
@@ -829,6 +1044,7 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
829
1044
|
let hasWarn = false;
|
|
830
1045
|
let manualLevel;
|
|
831
1046
|
let emitted = false;
|
|
1047
|
+
let pendingWideEvent = null;
|
|
832
1048
|
function addLog(level, message) {
|
|
833
1049
|
if (!Array.isArray(context.requestLogs)) context.requestLogs = [];
|
|
834
1050
|
context.requestLogs.push({
|
|
@@ -858,7 +1074,13 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
858
1074
|
audit: auditMethod,
|
|
859
1075
|
set(data) {
|
|
860
1076
|
if (emitted) {
|
|
861
|
-
const
|
|
1077
|
+
const record = data;
|
|
1078
|
+
const pendingState = pendingWideEvent ? pendingDrainState.get(pendingWideEvent) : void 0;
|
|
1079
|
+
if (pendingWideEvent && pendingState && !pendingState.drainStarted && isAiOnlyFieldUpdate(record)) {
|
|
1080
|
+
mergeInto(pendingWideEvent, record);
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
const keys = Object.keys(record);
|
|
862
1084
|
warnPostEmit("log.set()", `Keys dropped: ${keys.length ? keys.join(", ") : "(empty)"}.`);
|
|
863
1085
|
return;
|
|
864
1086
|
}
|
|
@@ -895,6 +1117,13 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
895
1117
|
"cause",
|
|
896
1118
|
"internal"
|
|
897
1119
|
]) if (k in err) errorObj[k] = errRecord[k];
|
|
1120
|
+
if (err instanceof EvlogError) {
|
|
1121
|
+
if (err.code) errorObj.code = err.code;
|
|
1122
|
+
if (err.why) errorObj.why = err.why;
|
|
1123
|
+
if (err.fix) errorObj.fix = err.fix;
|
|
1124
|
+
if (err.link) errorObj.link = err.link;
|
|
1125
|
+
if (err.status) errorObj.status = err.status;
|
|
1126
|
+
}
|
|
898
1127
|
if (isPlainObject$1(context.error)) mergeInto(context.error, errorObj);
|
|
899
1128
|
else context.error = errorObj;
|
|
900
1129
|
},
|
|
@@ -940,6 +1169,7 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
940
1169
|
});
|
|
941
1170
|
if (!forceKeep && !shouldSample(level)) {
|
|
942
1171
|
emitted = true;
|
|
1172
|
+
pendingWideEvent = null;
|
|
943
1173
|
return null;
|
|
944
1174
|
}
|
|
945
1175
|
if (overrides) {
|
|
@@ -953,6 +1183,8 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
953
1183
|
waitUntil
|
|
954
1184
|
});
|
|
955
1185
|
emitted = true;
|
|
1186
|
+
pendingWideEvent = wide;
|
|
1187
|
+
if (wide) pendingDrainState.set(wide, { drainStarted: !deferDrain });
|
|
956
1188
|
return wide;
|
|
957
1189
|
},
|
|
958
1190
|
getContext() {
|
|
@@ -1172,10 +1404,7 @@ function markForceKeep(logger) {
|
|
|
1172
1404
|
* ```
|
|
1173
1405
|
*/
|
|
1174
1406
|
function audit(input) {
|
|
1175
|
-
|
|
1176
|
-
const wide = createLogger({ audit: fields }).emit({ _forceKeep: true });
|
|
1177
|
-
_testCollector?.(fields, wide);
|
|
1178
|
-
return wide;
|
|
1407
|
+
return createLogger({ audit: buildAuditFields(input) }).emit({ _forceKeep: true });
|
|
1179
1408
|
}
|
|
1180
1409
|
/**
|
|
1181
1410
|
* Wrap a function so its outcome (success / failure / denied) is automatically
|
|
@@ -1248,8 +1477,8 @@ var AuditDeniedError = class extends Error {
|
|
|
1248
1477
|
* `changes` field. Output is a JSON Patch-style array (RFC 6902 subset:
|
|
1249
1478
|
* `add`, `remove`, `replace`) — small enough to ship over the wire.
|
|
1250
1479
|
*
|
|
1251
|
-
*
|
|
1252
|
-
* `'
|
|
1480
|
+
* Fields matching `redactPaths` glob patterns (e.g. `'password'`, `'**.password'`,
|
|
1481
|
+
* `'*_token'`, `'user.email'`) are replaced with `'[REDACTED]'` so PII
|
|
1253
1482
|
* never leaks through the diff.
|
|
1254
1483
|
*
|
|
1255
1484
|
* @example
|
|
@@ -1264,14 +1493,13 @@ var AuditDeniedError = class extends Error {
|
|
|
1264
1493
|
*/
|
|
1265
1494
|
function auditDiff(before, after, options = {}) {
|
|
1266
1495
|
const replacement = options.replacement ?? "[REDACTED]";
|
|
1267
|
-
const
|
|
1496
|
+
const pathMatchers = compileRedactPathMatchers(options.redactPaths) ?? {
|
|
1497
|
+
exactPaths: /* @__PURE__ */ new Set(),
|
|
1498
|
+
pathGlobs: [],
|
|
1499
|
+
keyGlobs: [],
|
|
1500
|
+
caseInsensitiveLeaves: /* @__PURE__ */ new Set()
|
|
1501
|
+
};
|
|
1268
1502
|
const patch = [];
|
|
1269
|
-
function isRedacted(path) {
|
|
1270
|
-
if (redactSet.size === 0) return false;
|
|
1271
|
-
if (redactSet.has(path)) return true;
|
|
1272
|
-
for (const p of redactSet) if (path.endsWith(`.${p}`)) return true;
|
|
1273
|
-
return false;
|
|
1274
|
-
}
|
|
1275
1503
|
function diff(a, b, path) {
|
|
1276
1504
|
if (a === b) return;
|
|
1277
1505
|
if (a === void 0 && b !== void 0) {
|
|
@@ -1301,16 +1529,7 @@ function auditDiff(before, after, options = {}) {
|
|
|
1301
1529
|
});
|
|
1302
1530
|
}
|
|
1303
1531
|
function redactValue(value, path) {
|
|
1304
|
-
|
|
1305
|
-
const segs = path.split("/").filter(Boolean);
|
|
1306
|
-
const last = segs[segs.length - 1];
|
|
1307
|
-
if (last && isRedacted(last)) return replacement;
|
|
1308
|
-
return value;
|
|
1309
|
-
}
|
|
1310
|
-
if (Array.isArray(value)) return value.map((v, i) => redactValue(v, `${path}/${i}`));
|
|
1311
|
-
const out = {};
|
|
1312
|
-
for (const [k, v] of Object.entries(value)) out[k] = isRedacted(k) ? replacement : redactValue(v, `${path}/${k}`);
|
|
1313
|
-
return out;
|
|
1532
|
+
return redactValueByPaths(value, pathMatchers, replacement, path);
|
|
1314
1533
|
}
|
|
1315
1534
|
diff(before, after, "");
|
|
1316
1535
|
const result = { patch };
|
|
@@ -1319,15 +1538,21 @@ function auditDiff(before, after, options = {}) {
|
|
|
1319
1538
|
return result;
|
|
1320
1539
|
}
|
|
1321
1540
|
/**
|
|
1322
|
-
* Define a typed audit action with
|
|
1541
|
+
* Define a typed audit action with optional fixed target type and catalog metadata.
|
|
1323
1542
|
*
|
|
1324
1543
|
* Returns a curried helper that fills in the action name (and target shape
|
|
1325
1544
|
* if provided) so call sites stay terse and the action set is discoverable
|
|
1326
|
-
* in one place.
|
|
1545
|
+
* in one place. Metadata (`description`, `severity`, `requiresChanges`, …)
|
|
1546
|
+
* is exposed on the factory for introspection, docs, and review tooling.
|
|
1327
1547
|
*
|
|
1328
1548
|
* @example
|
|
1329
1549
|
* ```ts
|
|
1330
|
-
* const refund = defineAuditAction('invoice.refund', {
|
|
1550
|
+
* const refund = defineAuditAction('invoice.refund', {
|
|
1551
|
+
* target: 'invoice',
|
|
1552
|
+
* severity: 'high',
|
|
1553
|
+
* requiresChanges: true,
|
|
1554
|
+
* redactPaths: ['cardNumber'],
|
|
1555
|
+
* })
|
|
1331
1556
|
*
|
|
1332
1557
|
* log.audit(refund({
|
|
1333
1558
|
* actor: { type: 'user', id: user.id },
|
|
@@ -1338,7 +1563,7 @@ function auditDiff(before, after, options = {}) {
|
|
|
1338
1563
|
*/
|
|
1339
1564
|
function defineAuditAction(action, options) {
|
|
1340
1565
|
const targetType = options?.target;
|
|
1341
|
-
|
|
1566
|
+
const factory = ((input) => {
|
|
1342
1567
|
const merged = {
|
|
1343
1568
|
...input,
|
|
1344
1569
|
action
|
|
@@ -1348,21 +1573,50 @@ function defineAuditAction(action, options) {
|
|
|
1348
1573
|
type: targetType
|
|
1349
1574
|
};
|
|
1350
1575
|
return merged;
|
|
1351
|
-
};
|
|
1576
|
+
});
|
|
1577
|
+
Object.defineProperties(factory, {
|
|
1578
|
+
action: {
|
|
1579
|
+
value: action,
|
|
1580
|
+
enumerable: true
|
|
1581
|
+
},
|
|
1582
|
+
target: {
|
|
1583
|
+
value: options?.target,
|
|
1584
|
+
enumerable: true
|
|
1585
|
+
},
|
|
1586
|
+
description: {
|
|
1587
|
+
value: options?.description,
|
|
1588
|
+
enumerable: true
|
|
1589
|
+
},
|
|
1590
|
+
severity: {
|
|
1591
|
+
value: options?.severity,
|
|
1592
|
+
enumerable: true
|
|
1593
|
+
},
|
|
1594
|
+
requiresChanges: {
|
|
1595
|
+
value: options?.requiresChanges,
|
|
1596
|
+
enumerable: true
|
|
1597
|
+
},
|
|
1598
|
+
requiresReason: {
|
|
1599
|
+
value: options?.requiresReason,
|
|
1600
|
+
enumerable: true
|
|
1601
|
+
},
|
|
1602
|
+
redactPaths: {
|
|
1603
|
+
value: options?.redactPaths,
|
|
1604
|
+
enumerable: true
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
return factory;
|
|
1352
1608
|
}
|
|
1353
1609
|
/**
|
|
1354
1610
|
* Test helper that captures every audit event emitted while it is active.
|
|
1355
1611
|
*
|
|
1356
|
-
* Returns `{ events, restore,
|
|
1612
|
+
* Returns `{ events, restore, toIncludeAuditOf, assertAudit }`:
|
|
1357
1613
|
* - `events` — live array of captured `AuditFields`, populated as audits fire.
|
|
1358
1614
|
* - `restore()` — uninstall the collector. Call from `afterEach()`.
|
|
1359
|
-
* - `
|
|
1360
|
-
*
|
|
1615
|
+
* - `toIncludeAuditOf(matcher)` — returns `true` if at least one captured event matches.
|
|
1616
|
+
* - `assertAudit(matcher)` — returns the matched event or throws with a readable summary.
|
|
1361
1617
|
*
|
|
1362
|
-
*
|
|
1363
|
-
* `
|
|
1364
|
-
* collector by design — wrap them with `log.audit()` to make them visible to
|
|
1365
|
-
* tests.
|
|
1618
|
+
* Captures audits on emit from `log.audit()`, standalone `audit()`, and
|
|
1619
|
+
* `log.set({ audit })` — including auto-filled `idempotencyKey`.
|
|
1366
1620
|
*
|
|
1367
1621
|
* @example
|
|
1368
1622
|
* ```ts
|
|
@@ -1386,6 +1640,18 @@ function mockAudit() {
|
|
|
1386
1640
|
},
|
|
1387
1641
|
toIncludeAuditOf(matcher) {
|
|
1388
1642
|
return events.some((event) => matchesAudit(event, matcher));
|
|
1643
|
+
},
|
|
1644
|
+
assertAudit(matcher) {
|
|
1645
|
+
const match = events.find((event) => matchesAudit(event, matcher));
|
|
1646
|
+
if (!match) {
|
|
1647
|
+
const summary = events.map((e) => ({
|
|
1648
|
+
action: e.action,
|
|
1649
|
+
outcome: e.outcome
|
|
1650
|
+
}));
|
|
1651
|
+
const matcherStr = JSON.stringify(matcher, (_k, v) => v instanceof RegExp ? v.toString() : v);
|
|
1652
|
+
throw new Error(`No audit event matched ${matcherStr}. Captured ${events.length} event(s): ${JSON.stringify(summary)}`);
|
|
1653
|
+
}
|
|
1654
|
+
return match;
|
|
1389
1655
|
}
|
|
1390
1656
|
};
|
|
1391
1657
|
}
|
|
@@ -1426,7 +1692,9 @@ function consumeAuditForceKeep(context) {
|
|
|
1426
1692
|
function finalizeAudit(event) {
|
|
1427
1693
|
const a = event.audit;
|
|
1428
1694
|
if (!a) return;
|
|
1429
|
-
|
|
1695
|
+
const decorated = decorateAudit(a, String(event.timestamp));
|
|
1696
|
+
event.audit = decorated;
|
|
1697
|
+
_testCollector?.(decorated, event);
|
|
1430
1698
|
}
|
|
1431
1699
|
/**
|
|
1432
1700
|
* Enrich audit-bearing wide events with request, runtime, and tenant context.
|
|
@@ -1659,33 +1927,21 @@ function stripIntegrity(event) {
|
|
|
1659
1927
|
* enough signal to be useful.
|
|
1660
1928
|
*/
|
|
1661
1929
|
const auditRedactPreset = { paths: [
|
|
1662
|
-
"
|
|
1663
|
-
"
|
|
1664
|
-
"
|
|
1665
|
-
"
|
|
1666
|
-
"
|
|
1667
|
-
"
|
|
1668
|
-
"
|
|
1669
|
-
"
|
|
1670
|
-
"
|
|
1671
|
-
"
|
|
1672
|
-
"
|
|
1673
|
-
"
|
|
1674
|
-
"
|
|
1675
|
-
"audit.changes.after.apiKey",
|
|
1676
|
-
"audit.changes.after.secret",
|
|
1677
|
-
"audit.changes.after.accessToken",
|
|
1678
|
-
"audit.changes.after.refreshToken",
|
|
1679
|
-
"audit.changes.after.cardNumber",
|
|
1680
|
-
"audit.changes.after.cvv",
|
|
1681
|
-
"audit.changes.after.ssn",
|
|
1682
|
-
"headers.authorization",
|
|
1683
|
-
"headers.cookie",
|
|
1684
|
-
"headers.set-cookie",
|
|
1685
|
-
"audit.context.headers.authorization",
|
|
1686
|
-
"audit.context.headers.cookie"
|
|
1930
|
+
"password",
|
|
1931
|
+
"passwordHash",
|
|
1932
|
+
"token",
|
|
1933
|
+
"apiKey",
|
|
1934
|
+
"secret",
|
|
1935
|
+
"accessToken",
|
|
1936
|
+
"refreshToken",
|
|
1937
|
+
"cardNumber",
|
|
1938
|
+
"cvv",
|
|
1939
|
+
"ssn",
|
|
1940
|
+
"authorization",
|
|
1941
|
+
"cookie",
|
|
1942
|
+
"set-cookie"
|
|
1687
1943
|
] };
|
|
1688
1944
|
//#endregion
|
|
1689
|
-
export {
|
|
1945
|
+
export { enricherPlugin as A, lockLogger as C, createPluginRunner as D, shouldKeep as E, resolveRedactConfig as F, isGloballyRedacted as M, normalizeRedactConfig as N, definePlugin as O, redactEvent as P, isLoggerLocked as S, mergeInto as T, getEnvironment as _, auditEnricher as a, initLogger as b, buildAuditFields as c, signed as d, withAudit as f, createRequestLogger as g, createLogger as h, auditDiff as i, getEmptyPluginRunner as j, drainPlugin as k, defineAuditAction as l, _log as m, AuditDeniedError as n, auditOnly as o, withAuditMethods as p, audit as r, auditRedactPreset as s, AUDIT_SCHEMA_VERSION as t, mockAudit as u, getGlobalDrain as v, markWideEventDrainStarted as w, isEnabled as x, getGlobalPluginRunner as y };
|
|
1690
1946
|
|
|
1691
|
-
//# sourceMappingURL=audit-
|
|
1947
|
+
//# sourceMappingURL=audit-BFwTUxBJ.mjs.map
|