evlog 2.18.1 → 2.19.1
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 +14 -16
- package/dist/adapters/axiom.d.mts +3 -3
- package/dist/adapters/axiom.d.mts.map +1 -1
- package/dist/adapters/axiom.mjs +10 -14
- package/dist/adapters/axiom.mjs.map +1 -1
- package/dist/adapters/better-stack.d.mts +2 -2
- package/dist/adapters/better-stack.d.mts.map +1 -1
- package/dist/adapters/better-stack.mjs +9 -13
- package/dist/adapters/better-stack.mjs.map +1 -1
- package/dist/adapters/datadog.d.mts +3 -3
- package/dist/adapters/datadog.mjs +9 -5
- package/dist/adapters/datadog.mjs.map +1 -1
- package/dist/adapters/fs.d.mts +1 -1
- package/dist/adapters/fs.d.mts.map +1 -1
- package/dist/adapters/fs.mjs +15 -2
- package/dist/adapters/fs.mjs.map +1 -1
- package/dist/adapters/hyperdx.d.mts +2 -2
- package/dist/adapters/hyperdx.mjs +3 -3
- package/dist/adapters/hyperdx.mjs.map +1 -1
- package/dist/adapters/memory.d.mts +2 -3
- package/dist/adapters/memory.d.mts.map +1 -1
- package/dist/adapters/memory.mjs +2 -3
- package/dist/adapters/memory.mjs.map +1 -1
- package/dist/adapters/otlp.d.mts +4 -4
- package/dist/adapters/otlp.mjs +19 -11
- package/dist/adapters/otlp.mjs.map +1 -1
- package/dist/adapters/posthog.d.mts +2 -2
- package/dist/adapters/posthog.mjs +5 -5
- package/dist/adapters/posthog.mjs.map +1 -1
- package/dist/adapters/sentry.d.mts +3 -3
- package/dist/adapters/sentry.mjs +6 -6
- package/dist/adapters/sentry.mjs.map +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/{audit-BUI3af4w.mjs → audit-BQt8yAHo.mjs} +376 -116
- package/dist/audit-BQt8yAHo.mjs.map +1 -0
- package/dist/{audit-DVdkntSO.d.mts → audit-D7v6JHj0.d.mts} +113 -35
- package/dist/audit-D7v6JHj0.d.mts.map +1 -0
- package/dist/better-auth/index.d.mts +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/deferred-drain-jeajC8QF.mjs +36 -0
- package/dist/deferred-drain-jeajC8QF.mjs.map +1 -0
- 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-DTQpu4f6.d.mts} +8 -3
- package/dist/define-DTQpu4f6.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-fDb-eNwz.mjs} +35 -4
- package/dist/drain-fDb-eNwz.mjs.map +1 -0
- 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-error-stack-next.node-Dgm_rCf5.mjs +120 -0
- package/dist/enrich-error-stack-next.node-Dgm_rCf5.mjs.map +1 -0
- package/dist/{enricher-UW9npoB2.d.mts → enricher-CBRmQw6e.d.mts} +2 -2
- package/dist/{enricher-UW9npoB2.d.mts.map → enricher-CBRmQw6e.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-CpghjrkY.d.mts} +2 -2
- package/dist/{error-CVtn5U7b.d.mts.map → error-CpghjrkY.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/{errors-dEMNQCiL.d.mts → errors-BLU4Tfpe.d.mts} +2 -2
- package/dist/{errors-dEMNQCiL.d.mts.map → errors-BLU4Tfpe.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-CgGlAaHa.mjs} +39 -14
- package/dist/fork-CgGlAaHa.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-ChVS9GYc.mjs} +2 -2
- package/dist/{http-B6YgAhyN.mjs.map → http-ChVS9GYc.mjs.map} +1 -1
- package/dist/http.d.mts +1 -1
- package/dist/{index-ZSRQP_BI.d.mts → index-EvnrXvQM.d.mts} +15 -8
- package/dist/index-EvnrXvQM.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/instrumentation-create-BrjQtSKD.d.mts +115 -0
- package/dist/instrumentation-create-BrjQtSKD.d.mts.map +1 -0
- package/dist/{integration-Dhig7ae6.mjs → integration-DYp2uw8O.mjs} +3 -3
- package/dist/{integration-Dhig7ae6.mjs.map → integration-DYp2uw8O.mjs.map} +1 -1
- package/dist/{logger-CTcvd5Cc.d.mts → logger-mHIWxBhJ.d.mts} +12 -4
- package/dist/logger-mHIWxBhJ.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-B_k4Mrzg.d.mts} +9 -2
- package/dist/middleware-B_k4Mrzg.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 +49 -38
- package/dist/next/index.mjs.map +1 -1
- package/dist/next/instrumentation/create.d.mts +2 -0
- package/dist/next/instrumentation/create.mjs +155 -0
- package/dist/next/instrumentation/create.mjs.map +1 -0
- package/dist/next/instrumentation.d.mts +2 -77
- package/dist/next/instrumentation.mjs +32 -81
- package/dist/next/instrumentation.mjs.map +1 -1
- package/dist/next/stream.d.mts +1 -1
- package/dist/next/stream.mjs +2 -2
- package/dist/next/stream.mjs.map +1 -1
- package/dist/nitro/errorHandler.mjs +23 -18
- 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 +74 -18
- 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-_Hda8Deo.d.mts} +7 -2
- package/dist/nitro-_Hda8Deo.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-CNV_CXs8.mjs} +2 -2
- package/dist/package-CNV_CXs8.mjs.map +1 -0
- package/dist/{parseError-D4PIxEWo.d.mts → parseError-BeBXEd2V.d.mts} +2 -2
- package/dist/parseError-BeBXEd2V.d.mts.map +1 -0
- package/dist/pipeline.d.mts +0 -19
- package/dist/pipeline.d.mts.map +1 -1
- package/dist/pipeline.mjs +12 -0
- package/dist/pipeline.mjs.map +1 -1
- package/dist/pretty-error-THg0U0w9.mjs +288 -0
- package/dist/pretty-error-THg0U0w9.mjs.map +1 -0
- package/dist/pretty-error-snippet.node-itfCajBM.mjs +48 -0
- package/dist/pretty-error-snippet.node-itfCajBM.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.d.mts +7 -0
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs +39 -3
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs.map +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-CHOPF2nd.mjs} +2 -1
- package/dist/{source-location-Dco0cRTz.mjs.map → source-location-CHOPF2nd.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 +51 -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-Cfv8Ck8b.d.mts} +2 -2
- package/dist/{useLogger-CqvH6qOf.d.mts.map → useLogger-Cfv8Ck8b.d.mts.map} +1 -1
- package/dist/{utils-DxqvIOyR.d.mts → utils-CJJG0ZYW.d.mts} +11 -3
- package/dist/{utils-DxqvIOyR.d.mts.map → utils-CJJG0ZYW.d.mts.map} +1 -1
- package/dist/utils.d.mts +2 -2
- package/dist/utils.mjs +23 -9
- 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 +12 -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/drain-7n3K6kPe.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/next/instrumentation.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,117 @@
|
|
|
1
|
-
import { colors, cssColors, detectEnvironment, escapeFormatString, formatDuration, getConsoleMethod, getCssLevelColor, getLevelColor, isBrowser, isDev, isLevelEnabled, matchesPattern } from "./utils.mjs";
|
|
2
|
-
import { r as
|
|
1
|
+
import { colors, cssColors, detectEnvironment, escapeFormatString, formatDuration, getConsoleMethod, getCssLevelColor, getLevelColor, globToRegExp, isBrowser, isDev, isLevelEnabled, matchesPattern } from "./utils.mjs";
|
|
2
|
+
import { n as buildErrorEntries, o as registerPrettyErrorSnippetReader, r as compactStackForStorage } from "./pretty-error-THg0U0w9.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].toLowerCase());
|
|
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
|
+
if (matchers.caseInsensitiveLeaves.has(leafKey.toLowerCase())) return true;
|
|
59
|
+
for (const glob of matchers.pathGlobs) {
|
|
60
|
+
glob.lastIndex = 0;
|
|
61
|
+
if (glob.test(fullPath)) return true;
|
|
62
|
+
}
|
|
63
|
+
for (const glob of matchers.keyGlobs) {
|
|
64
|
+
glob.lastIndex = 0;
|
|
65
|
+
if (glob.test(leafKey)) return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Redact fields matching path globs recursively. Mutates `obj` in place (use on a clone).
|
|
71
|
+
*/
|
|
72
|
+
function redactPathsInTree(obj, matchers, replacement, prefix = "") {
|
|
73
|
+
if (obj === null || obj === void 0) return;
|
|
74
|
+
if (Array.isArray(obj)) {
|
|
75
|
+
for (let i = 0; i < obj.length; i++) {
|
|
76
|
+
const segment = String(i);
|
|
77
|
+
const fullPath = prefix ? `${prefix}.${segment}` : segment;
|
|
78
|
+
redactPathsInTree(obj[i], matchers, replacement, fullPath);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (typeof obj === "object") {
|
|
83
|
+
const record = obj;
|
|
84
|
+
for (const key in record) {
|
|
85
|
+
const fullPath = prefix ? `${prefix}.${key}` : key;
|
|
86
|
+
if (matchesRedactPath(fullPath, key, matchers)) record[key] = replacement;
|
|
87
|
+
else redactPathsInTree(record[key], matchers, replacement, fullPath);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Return a copy of `value` with path-pattern matches replaced by `replacement`.
|
|
93
|
+
* Used by audit diffs; does not mutate the input.
|
|
94
|
+
*
|
|
95
|
+
* `pointerPath` is a JSON Pointer (e.g. `/user/password`).
|
|
96
|
+
*/
|
|
97
|
+
function redactValueByPaths(value, matchers, replacement, pointerPath = "") {
|
|
98
|
+
const segments = pointerPath.split("/").filter(Boolean);
|
|
99
|
+
const dotPath = segments.join(".");
|
|
100
|
+
const leafKey = segments.at(-1) ?? "";
|
|
101
|
+
if (value === null || typeof value !== "object") {
|
|
102
|
+
if (dotPath && matchesRedactPath(dotPath, leafKey, matchers)) return replacement;
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
if (Array.isArray(value)) return value.map((v, i) => redactValueByPaths(v, matchers, replacement, `${pointerPath}/${i}`));
|
|
106
|
+
if (!isPlainRecord(value)) return value;
|
|
107
|
+
const out = {};
|
|
108
|
+
for (const [k, v] of Object.entries(value)) {
|
|
109
|
+
const childPointer = pointerPath ? `${pointerPath}/${k}` : `/${k}`;
|
|
110
|
+
out[k] = matchesRedactPath(dotPath ? `${dotPath}.${k}` : k, k, matchers) ? replacement : redactValueByPaths(v, matchers, replacement, childPointer);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
6
115
|
* Built-in PII detection patterns with smart masking.
|
|
7
116
|
* Each builtin preserves just enough signal for debugging while scrubbing PII.
|
|
8
117
|
*/
|
|
@@ -76,11 +185,15 @@ const builtinPatterns = {
|
|
|
76
185
|
function resolveRedactConfig(input) {
|
|
77
186
|
if (input === void 0 || input === false) return void 0;
|
|
78
187
|
if (input === true) return { _maskers: allBuiltinMaskers() };
|
|
79
|
-
if (input.builtins === false) return
|
|
188
|
+
if (input.builtins === false) return {
|
|
189
|
+
...input,
|
|
190
|
+
_pathMatchers: compileRedactPathMatchers(input.paths)
|
|
191
|
+
};
|
|
80
192
|
const maskers = Array.isArray(input.builtins) ? input.builtins.map((name) => builtinPatterns[name]).filter(Boolean).map((b) => [cloneRegex(b.pattern), b.mask]) : allBuiltinMaskers();
|
|
81
193
|
return {
|
|
82
194
|
...input,
|
|
83
|
-
_maskers: maskers
|
|
195
|
+
_maskers: maskers,
|
|
196
|
+
_pathMatchers: compileRedactPathMatchers(input.paths)
|
|
84
197
|
};
|
|
85
198
|
}
|
|
86
199
|
function allBuiltinMaskers() {
|
|
@@ -89,32 +202,56 @@ function allBuiltinMaskers() {
|
|
|
89
202
|
function cloneRegex(re) {
|
|
90
203
|
return new RegExp(re.source, re.flags);
|
|
91
204
|
}
|
|
205
|
+
/** @internal Set on wide events after initLogger redaction so middleware skips a second pass. */
|
|
206
|
+
const globallyRedacted = Symbol.for("evlog.globallyRedacted");
|
|
207
|
+
/** @internal Mark a wide event as already redacted by {@link initLogger}. */
|
|
208
|
+
function markGloballyRedacted(event) {
|
|
209
|
+
Object.defineProperty(event, globallyRedacted, {
|
|
210
|
+
value: true,
|
|
211
|
+
enumerable: false,
|
|
212
|
+
configurable: true
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/** @internal Whether global redaction already ran on this wide event. */
|
|
216
|
+
function isGloballyRedacted(event) {
|
|
217
|
+
return Reflect.has(event, globallyRedacted);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Clone before redaction. Wide events are JSON-shaped; fall back when
|
|
221
|
+
* `structuredClone` rejects non-cloneable values (functions, symbols, etc.).
|
|
222
|
+
*/
|
|
223
|
+
function cloneForRedaction(event) {
|
|
224
|
+
try {
|
|
225
|
+
return structuredClone(event);
|
|
226
|
+
} catch {
|
|
227
|
+
try {
|
|
228
|
+
return JSON.parse(JSON.stringify(event));
|
|
229
|
+
} catch {
|
|
230
|
+
console.warn("[cloneForRedaction] Shallow clone used — nested objects may be mutated by redactPath, redactPatterns, and applyMaskersToTree");
|
|
231
|
+
return { ...event };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
92
235
|
/**
|
|
93
|
-
* Redact sensitive data from a wide event
|
|
236
|
+
* Redact sensitive data from a wide event without mutating the input.
|
|
94
237
|
*
|
|
95
|
-
* Three strategies
|
|
96
|
-
* 1. **Path-based**: dot-notation paths
|
|
238
|
+
* Returns a deep clone with redaction applied. Three strategies run in order:
|
|
239
|
+
* 1. **Path-based**: dot-notation paths with optional globs (`password`, `**.password`, `*_token`, `user.*`) — full value replacement.
|
|
97
240
|
* 2. **Masker-based**: built-in patterns with smart partial masking (e.g. `****1111`).
|
|
98
|
-
* 3. **Pattern-based**: custom RegExp patterns replaced with `replacement`.
|
|
241
|
+
* 3. **Pattern-based**: custom RegExp patterns on string values replaced with `replacement`.
|
|
99
242
|
*
|
|
100
|
-
* @param event - The wide event object (mutated
|
|
243
|
+
* @param event - The wide event object (not mutated).
|
|
101
244
|
* @param config - Redaction configuration.
|
|
245
|
+
* @returns A redacted deep clone of `event`.
|
|
102
246
|
*/
|
|
103
247
|
function redactEvent(event, config) {
|
|
248
|
+
const clone = cloneForRedaction(event);
|
|
104
249
|
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;
|
|
250
|
+
const pathMatchers = config._pathMatchers ?? compileRedactPathMatchers(config.paths);
|
|
251
|
+
if (pathMatchers) redactPathsInTree(clone, pathMatchers, replacement);
|
|
252
|
+
if (config._maskers?.length) applyMaskersToTree(clone, config._maskers);
|
|
253
|
+
if (config.patterns?.length) redactPatterns(clone, config.patterns, replacement);
|
|
254
|
+
return clone;
|
|
118
255
|
}
|
|
119
256
|
function redactPatterns(obj, patterns, replacement) {
|
|
120
257
|
if (obj === null || obj === void 0) return;
|
|
@@ -134,10 +271,7 @@ function redactPatterns(obj, patterns, replacement) {
|
|
|
134
271
|
}
|
|
135
272
|
function applyPatterns(value, patterns, replacement) {
|
|
136
273
|
let result = value;
|
|
137
|
-
for (const pattern of patterns)
|
|
138
|
-
pattern.lastIndex = 0;
|
|
139
|
-
result = result.replace(pattern, replacement);
|
|
140
|
-
}
|
|
274
|
+
for (const pattern of patterns) result = result.replace(pattern, replacement);
|
|
141
275
|
return result;
|
|
142
276
|
}
|
|
143
277
|
function applyMaskersToTree(obj, maskers) {
|
|
@@ -158,10 +292,7 @@ function applyMaskersToTree(obj, maskers) {
|
|
|
158
292
|
}
|
|
159
293
|
function applyMaskers(value, maskers) {
|
|
160
294
|
let result = value;
|
|
161
|
-
for (const [pattern, mask] of maskers)
|
|
162
|
-
pattern.lastIndex = 0;
|
|
163
|
-
result = result.replace(pattern, mask);
|
|
164
|
-
}
|
|
295
|
+
for (const [pattern, mask] of maskers) result = result.replace(pattern, mask);
|
|
165
296
|
return result;
|
|
166
297
|
}
|
|
167
298
|
/**
|
|
@@ -177,17 +308,34 @@ function normalizeRedactConfig(raw) {
|
|
|
177
308
|
if (typeof raw.replacement === "string") config.replacement = raw.replacement;
|
|
178
309
|
if (raw.builtins === false) config.builtins = false;
|
|
179
310
|
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);
|
|
311
|
+
if (Array.isArray(raw.patterns)) config.patterns = deserializeRegexList(raw.patterns);
|
|
189
312
|
return resolveRedactConfig(config);
|
|
190
313
|
}
|
|
314
|
+
function isPlainRecord(value) {
|
|
315
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
316
|
+
const proto = Object.getPrototypeOf(value);
|
|
317
|
+
return proto === Object.prototype || proto === null;
|
|
318
|
+
}
|
|
319
|
+
function deserializeRegexList(raw) {
|
|
320
|
+
const patterns = [];
|
|
321
|
+
for (const p of raw) try {
|
|
322
|
+
if (p instanceof RegExp) {
|
|
323
|
+
patterns.push(cloneRegex(p));
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (typeof p === "string") {
|
|
327
|
+
patterns.push(new RegExp(p, "g"));
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (typeof p === "object" && p !== null && typeof p.source === "string") {
|
|
331
|
+
const flags = typeof p.flags === "string" ? p.flags : "g";
|
|
332
|
+
patterns.push(new RegExp(p.source, flags));
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
console.warn("[normalizeRedactConfig] Ignoring invalid redact regex entry");
|
|
336
|
+
}
|
|
337
|
+
return patterns;
|
|
338
|
+
}
|
|
191
339
|
//#endregion
|
|
192
340
|
//#region src/shared/plugin.ts
|
|
193
341
|
/** Identity helper preserving plugin type inference. */
|
|
@@ -314,6 +462,10 @@ function getEmptyPluginRunner() {
|
|
|
314
462
|
}
|
|
315
463
|
//#endregion
|
|
316
464
|
//#region src/logger.ts
|
|
465
|
+
const nativeStdoutWrite = typeof process !== "undefined" && typeof process.stdout?.write === "function" ? process.stdout.write.bind(process.stdout) : void 0;
|
|
466
|
+
function writePrettyStdout(text) {
|
|
467
|
+
(globalThis.__evlogNativeStdoutWrite ?? nativeStdoutWrite)?.(text);
|
|
468
|
+
}
|
|
317
469
|
function isPlainObject$1(val) {
|
|
318
470
|
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
319
471
|
}
|
|
@@ -337,11 +489,33 @@ function mergeInto(target, source) {
|
|
|
337
489
|
else target[key] = sourceVal;
|
|
338
490
|
}
|
|
339
491
|
}
|
|
492
|
+
const pendingDrainState = /* @__PURE__ */ new WeakMap();
|
|
493
|
+
function isAiOnlyFieldUpdate(data) {
|
|
494
|
+
const keys = Object.keys(data);
|
|
495
|
+
return keys.length === 1 && keys[0] === "ai";
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Mark a wide event as past the post-emit AI merge window so late `log.set({ ai })`
|
|
499
|
+
* calls warn again. Called by framework enrich/drain pipelines before drain runs.
|
|
500
|
+
*
|
|
501
|
+
* @internal Used by middleware and framework integrations.
|
|
502
|
+
*/
|
|
503
|
+
function markWideEventDrainStarted(event) {
|
|
504
|
+
if (!event) return;
|
|
505
|
+
const state = pendingDrainState.get(event);
|
|
506
|
+
if (state) state.drainStarted = true;
|
|
507
|
+
}
|
|
340
508
|
let globalEnv = {
|
|
341
509
|
service: "app",
|
|
342
510
|
environment: "development"
|
|
343
511
|
};
|
|
344
512
|
let globalPretty = isDev();
|
|
513
|
+
let globalPrettyError = {
|
|
514
|
+
snippet: isDev(),
|
|
515
|
+
stackDepth: 2,
|
|
516
|
+
compact: isDev(),
|
|
517
|
+
detail: "full"
|
|
518
|
+
};
|
|
345
519
|
let globalSampling = {};
|
|
346
520
|
let globalStringify = true;
|
|
347
521
|
let globalDrain;
|
|
@@ -367,6 +541,7 @@ function initLogger(config = {}) {
|
|
|
367
541
|
region: config.env?.region ?? detected.region
|
|
368
542
|
};
|
|
369
543
|
globalPretty = config.pretty ?? isDev();
|
|
544
|
+
globalPrettyError = resolveDevTerminal(config).prettyError;
|
|
370
545
|
globalSampling = config.sampling ?? {};
|
|
371
546
|
globalStringify = config.stringify ?? true;
|
|
372
547
|
globalDrain = config.drain;
|
|
@@ -375,6 +550,11 @@ function initLogger(config = {}) {
|
|
|
375
550
|
globalMinLevel = config.minLevel ?? "debug";
|
|
376
551
|
globalPluginRunner = config.plugins?.length ? createPluginRunner(config.plugins) : getEmptyPluginRunner();
|
|
377
552
|
if (globalPluginRunner.plugins.length > 0) globalPluginRunner.runSetup({ env: { ...globalEnv } });
|
|
553
|
+
if (!isBrowser() && typeof process !== "undefined" && process.versions?.node) import("./pretty-error-snippet.node-itfCajBM.mjs").then((n) => n.t).then((mod) => {
|
|
554
|
+
registerPrettyErrorSnippetReader(mod.readCodeSnippetFromDisk);
|
|
555
|
+
}).catch(() => {
|
|
556
|
+
registerPrettyErrorSnippetReader(null);
|
|
557
|
+
});
|
|
378
558
|
const hasAnyDrain = !!globalDrain || globalPluginRunner.hasDrain;
|
|
379
559
|
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
560
|
}
|
|
@@ -464,7 +644,10 @@ function emitWideEvent(level, event, options = {}) {
|
|
|
464
644
|
...event
|
|
465
645
|
};
|
|
466
646
|
finalizeAudit(formatted);
|
|
467
|
-
if (globalRedact)
|
|
647
|
+
if (globalRedact) {
|
|
648
|
+
formatted = redactEvent(formatted, globalRedact);
|
|
649
|
+
markGloballyRedacted(formatted);
|
|
650
|
+
}
|
|
468
651
|
if (!globalSilent) if (globalPretty) prettyPrintWideEvent(formatted);
|
|
469
652
|
else if (globalStringify) console[getConsoleMethod(level)](JSON.stringify(formatted));
|
|
470
653
|
else console[getConsoleMethod(level)](formatted);
|
|
@@ -698,11 +881,29 @@ function buildAIEntries(ai) {
|
|
|
698
881
|
});
|
|
699
882
|
return entries;
|
|
700
883
|
}
|
|
884
|
+
function flushPrettyLines(lines) {
|
|
885
|
+
if (lines.length === 0) return;
|
|
886
|
+
const text = `${lines.join("\n")}\n`;
|
|
887
|
+
if (nativeStdoutWrite && !isBrowser() && process.env.VITEST !== "true") {
|
|
888
|
+
writePrettyStdout(text);
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
console.log(lines.join("\n"));
|
|
892
|
+
}
|
|
701
893
|
function prettyPrintWideEvent(event) {
|
|
702
894
|
const { timestamp, level, service, environment, version, ...rest } = event;
|
|
703
895
|
const ts = typeof timestamp === "string" ? timestamp.slice(11, 23) : "";
|
|
704
896
|
const levelLabel = typeof level === "string" ? level : "info";
|
|
705
897
|
const browser = isBrowser();
|
|
898
|
+
const lines = [];
|
|
899
|
+
const writeLine = (...args) => {
|
|
900
|
+
if (browser) {
|
|
901
|
+
console.log(...args);
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
const [line] = args;
|
|
905
|
+
if (typeof line === "string") lines.push(line);
|
|
906
|
+
};
|
|
706
907
|
const parts = [];
|
|
707
908
|
const styles = [];
|
|
708
909
|
if (browser) {
|
|
@@ -711,7 +912,8 @@ function prettyPrintWideEvent(event) {
|
|
|
711
912
|
styles.push(cssColors.dim, cssColors.reset, lc, cssColors.reset, cssColors.cyan, cssColors.reset);
|
|
712
913
|
} else {
|
|
713
914
|
const lc = getLevelColor(levelLabel);
|
|
714
|
-
parts.push(`${
|
|
915
|
+
if (isDev()) parts.push(`${lc}${levelLabel.toUpperCase()}${colors.reset} ${colors.cyan}[${service}]${colors.reset}`);
|
|
916
|
+
else parts.push(`${colors.dim}${ts}${colors.reset} ${lc}${levelLabel.toUpperCase()}${colors.reset} ${colors.cyan}[${service}]${colors.reset}`);
|
|
715
917
|
}
|
|
716
918
|
if (rest.method && rest.path) {
|
|
717
919
|
parts.push(browser ? ` ${escapeFormatString(String(rest.method))} ${escapeFormatString(String(rest.path))}` : ` ${rest.method} ${rest.path}`);
|
|
@@ -734,15 +936,19 @@ function prettyPrintWideEvent(event) {
|
|
|
734
936
|
} else parts.push(` ${colors.dim}in ${rest.duration}${colors.reset}`);
|
|
735
937
|
delete rest.duration;
|
|
736
938
|
}
|
|
737
|
-
|
|
939
|
+
writeLine(parts.join(""), ...styles);
|
|
738
940
|
const aiData = isPlainObject$1(rest.ai) ? rest.ai : void 0;
|
|
739
941
|
if (aiData) delete rest.ai;
|
|
942
|
+
const errorData = rest.error;
|
|
943
|
+
if (errorData !== void 0) delete rest.error;
|
|
740
944
|
const restEntries = Object.entries(rest).filter(([_, v]) => v !== void 0);
|
|
741
945
|
const aiEntries = aiData ? buildAIEntries(aiData) : [];
|
|
742
|
-
const
|
|
946
|
+
const errorEntries = errorData !== void 0 ? buildErrorEntries(errorData, globalPrettyError) : [];
|
|
947
|
+
const contextEntries = [...restEntries.map(([key, value]) => ({
|
|
743
948
|
key,
|
|
744
949
|
value: formatValue(value)
|
|
745
950
|
})), ...aiEntries];
|
|
951
|
+
const allEntries = errorEntries.length > 0 ? [...errorEntries, ...contextEntries] : contextEntries;
|
|
746
952
|
for (let i = 0; i < allEntries.length; i++) {
|
|
747
953
|
const entry = allEntries[i];
|
|
748
954
|
if (!entry) continue;
|
|
@@ -751,22 +957,32 @@ function prettyPrintWideEvent(event) {
|
|
|
751
957
|
const prefix = i === allEntries.length - 1 && !hasChildren ? "└─" : "├─";
|
|
752
958
|
if (browser) {
|
|
753
959
|
const val = entry.value ? ` ${escapeFormatString(entry.value)}` : "";
|
|
754
|
-
|
|
960
|
+
writeLine(` %c${prefix}%c %c${escapeFormatString(entry.key)}:%c${val}`, cssColors.dim, cssColors.reset, cssColors.cyan, cssColors.reset);
|
|
755
961
|
} else {
|
|
756
962
|
const val = entry.value ? ` ${entry.value}` : "";
|
|
757
|
-
|
|
963
|
+
writeLine(` ${colors.dim}${prefix}${colors.reset} ${colors.cyan}${entry.key}:${colors.reset}${val}`);
|
|
758
964
|
}
|
|
759
965
|
if (hasChildren && children) {
|
|
760
966
|
const connector = i === allEntries.length - 1 ? " " : "│";
|
|
761
967
|
for (let j = 0; j < children.length; j++) {
|
|
762
968
|
const child = children[j];
|
|
763
969
|
if (child === void 0) continue;
|
|
970
|
+
if (child === "__EVLOG_TREE_SPACER__") {
|
|
971
|
+
writeLine(` ${colors.dim}${connector}${colors.reset}`);
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
764
974
|
const childPrefix = j === children.length - 1 ? "└─" : "├─";
|
|
765
|
-
if (
|
|
766
|
-
|
|
975
|
+
if (child === "") {
|
|
976
|
+
writeLine("");
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (browser) writeLine(` %c${connector} ${childPrefix}%c ${escapeFormatString(child)}`, cssColors.dim, cssColors.reset);
|
|
980
|
+
else if (child.startsWith(" ") || child.startsWith("\x1B")) writeLine(` ${colors.dim}${connector}${colors.reset}${child}`);
|
|
981
|
+
else writeLine(` ${colors.dim}${connector} ${childPrefix}${colors.reset} ${child}`);
|
|
767
982
|
}
|
|
768
983
|
}
|
|
769
984
|
}
|
|
985
|
+
if (!browser && lines.length > 0) flushPrettyLines(lines);
|
|
770
986
|
}
|
|
771
987
|
function createLogMethod(level) {
|
|
772
988
|
return function logMethod(tagOrEvent, message) {
|
|
@@ -829,6 +1045,7 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
829
1045
|
let hasWarn = false;
|
|
830
1046
|
let manualLevel;
|
|
831
1047
|
let emitted = false;
|
|
1048
|
+
let pendingWideEvent = null;
|
|
832
1049
|
function addLog(level, message) {
|
|
833
1050
|
if (!Array.isArray(context.requestLogs)) context.requestLogs = [];
|
|
834
1051
|
context.requestLogs.push({
|
|
@@ -858,7 +1075,13 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
858
1075
|
audit: auditMethod,
|
|
859
1076
|
set(data) {
|
|
860
1077
|
if (emitted) {
|
|
861
|
-
const
|
|
1078
|
+
const record = data;
|
|
1079
|
+
const pendingState = pendingWideEvent ? pendingDrainState.get(pendingWideEvent) : void 0;
|
|
1080
|
+
if (pendingWideEvent && pendingState && !pendingState.drainStarted && isAiOnlyFieldUpdate(record)) {
|
|
1081
|
+
mergeInto(pendingWideEvent, record);
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
const keys = Object.keys(record);
|
|
862
1085
|
warnPostEmit("log.set()", `Keys dropped: ${keys.length ? keys.join(", ") : "(empty)"}.`);
|
|
863
1086
|
return;
|
|
864
1087
|
}
|
|
@@ -882,7 +1105,7 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
882
1105
|
const errorObj = {
|
|
883
1106
|
name: err.name,
|
|
884
1107
|
message: err.message,
|
|
885
|
-
stack: err.stack
|
|
1108
|
+
stack: isDev() ? compactStackForStorage(err.stack) : err.stack
|
|
886
1109
|
};
|
|
887
1110
|
const errRecord = err;
|
|
888
1111
|
for (const k of [
|
|
@@ -895,6 +1118,13 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
895
1118
|
"cause",
|
|
896
1119
|
"internal"
|
|
897
1120
|
]) if (k in err) errorObj[k] = errRecord[k];
|
|
1121
|
+
if (err instanceof EvlogError) {
|
|
1122
|
+
if (err.code) errorObj.code = err.code;
|
|
1123
|
+
if (err.why) errorObj.why = err.why;
|
|
1124
|
+
if (err.fix) errorObj.fix = err.fix;
|
|
1125
|
+
if (err.link) errorObj.link = err.link;
|
|
1126
|
+
if (err.status) errorObj.status = err.status;
|
|
1127
|
+
}
|
|
898
1128
|
if (isPlainObject$1(context.error)) mergeInto(context.error, errorObj);
|
|
899
1129
|
else context.error = errorObj;
|
|
900
1130
|
},
|
|
@@ -940,6 +1170,7 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
940
1170
|
});
|
|
941
1171
|
if (!forceKeep && !shouldSample(level)) {
|
|
942
1172
|
emitted = true;
|
|
1173
|
+
pendingWideEvent = null;
|
|
943
1174
|
return null;
|
|
944
1175
|
}
|
|
945
1176
|
if (overrides) {
|
|
@@ -953,6 +1184,8 @@ function createLogger(initialContext = {}, internalOptions) {
|
|
|
953
1184
|
waitUntil
|
|
954
1185
|
});
|
|
955
1186
|
emitted = true;
|
|
1187
|
+
pendingWideEvent = wide;
|
|
1188
|
+
if (wide) pendingDrainState.set(wide, { drainStarted: !deferDrain });
|
|
956
1189
|
return wide;
|
|
957
1190
|
},
|
|
958
1191
|
getContext() {
|
|
@@ -1022,11 +1255,14 @@ function isPlainObject(value) {
|
|
|
1022
1255
|
if (Object.prototype.toString.call(value) !== "[object Object]") return false;
|
|
1023
1256
|
return Object.getPrototypeOf(value) === Object.prototype || value.constructor === Object;
|
|
1024
1257
|
}
|
|
1025
|
-
function stableStringify(value) {
|
|
1258
|
+
function stableStringify(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
1026
1259
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
1027
|
-
if (
|
|
1028
|
-
if (!isPlainObject(value)) return JSON.stringify(value);
|
|
1029
|
-
|
|
1260
|
+
if (ancestors.has(value)) return "\"[Circular]\"";
|
|
1261
|
+
if (!Array.isArray(value) && !isPlainObject(value)) return JSON.stringify(value);
|
|
1262
|
+
ancestors.add(value);
|
|
1263
|
+
const out = Array.isArray(value) ? `[${value.map((v) => stableStringify(v, ancestors)).join(",")}]` : `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(value[k], ancestors)}`).join(",")}}`;
|
|
1264
|
+
ancestors.delete(value);
|
|
1265
|
+
return out;
|
|
1030
1266
|
}
|
|
1031
1267
|
/**
|
|
1032
1268
|
* @internal Sync, isomorphic 32-bit FNV-1a. Used to derive the idempotency
|
|
@@ -1172,10 +1408,7 @@ function markForceKeep(logger) {
|
|
|
1172
1408
|
* ```
|
|
1173
1409
|
*/
|
|
1174
1410
|
function audit(input) {
|
|
1175
|
-
|
|
1176
|
-
const wide = createLogger({ audit: fields }).emit({ _forceKeep: true });
|
|
1177
|
-
_testCollector?.(fields, wide);
|
|
1178
|
-
return wide;
|
|
1411
|
+
return createLogger({ audit: buildAuditFields(input) }).emit({ _forceKeep: true });
|
|
1179
1412
|
}
|
|
1180
1413
|
/**
|
|
1181
1414
|
* Wrap a function so its outcome (success / failure / denied) is automatically
|
|
@@ -1248,8 +1481,8 @@ var AuditDeniedError = class extends Error {
|
|
|
1248
1481
|
* `changes` field. Output is a JSON Patch-style array (RFC 6902 subset:
|
|
1249
1482
|
* `add`, `remove`, `replace`) — small enough to ship over the wire.
|
|
1250
1483
|
*
|
|
1251
|
-
*
|
|
1252
|
-
* `'
|
|
1484
|
+
* Fields matching `redactPaths` glob patterns (e.g. `'password'`, `'**.password'`,
|
|
1485
|
+
* `'*_token'`, `'user.email'`) are replaced with `'[REDACTED]'` so PII
|
|
1253
1486
|
* never leaks through the diff.
|
|
1254
1487
|
*
|
|
1255
1488
|
* @example
|
|
@@ -1264,14 +1497,13 @@ var AuditDeniedError = class extends Error {
|
|
|
1264
1497
|
*/
|
|
1265
1498
|
function auditDiff(before, after, options = {}) {
|
|
1266
1499
|
const replacement = options.replacement ?? "[REDACTED]";
|
|
1267
|
-
const
|
|
1500
|
+
const pathMatchers = compileRedactPathMatchers(options.redactPaths) ?? {
|
|
1501
|
+
exactPaths: /* @__PURE__ */ new Set(),
|
|
1502
|
+
pathGlobs: [],
|
|
1503
|
+
keyGlobs: [],
|
|
1504
|
+
caseInsensitiveLeaves: /* @__PURE__ */ new Set()
|
|
1505
|
+
};
|
|
1268
1506
|
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
1507
|
function diff(a, b, path) {
|
|
1276
1508
|
if (a === b) return;
|
|
1277
1509
|
if (a === void 0 && b !== void 0) {
|
|
@@ -1301,16 +1533,7 @@ function auditDiff(before, after, options = {}) {
|
|
|
1301
1533
|
});
|
|
1302
1534
|
}
|
|
1303
1535
|
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;
|
|
1536
|
+
return redactValueByPaths(value, pathMatchers, replacement, path);
|
|
1314
1537
|
}
|
|
1315
1538
|
diff(before, after, "");
|
|
1316
1539
|
const result = { patch };
|
|
@@ -1319,15 +1542,21 @@ function auditDiff(before, after, options = {}) {
|
|
|
1319
1542
|
return result;
|
|
1320
1543
|
}
|
|
1321
1544
|
/**
|
|
1322
|
-
* Define a typed audit action with
|
|
1545
|
+
* Define a typed audit action with optional fixed target type and catalog metadata.
|
|
1323
1546
|
*
|
|
1324
1547
|
* Returns a curried helper that fills in the action name (and target shape
|
|
1325
1548
|
* if provided) so call sites stay terse and the action set is discoverable
|
|
1326
|
-
* in one place.
|
|
1549
|
+
* in one place. Metadata (`description`, `severity`, `requiresChanges`, …)
|
|
1550
|
+
* is exposed on the factory for introspection, docs, and review tooling.
|
|
1327
1551
|
*
|
|
1328
1552
|
* @example
|
|
1329
1553
|
* ```ts
|
|
1330
|
-
* const refund = defineAuditAction('invoice.refund', {
|
|
1554
|
+
* const refund = defineAuditAction('invoice.refund', {
|
|
1555
|
+
* target: 'invoice',
|
|
1556
|
+
* severity: 'high',
|
|
1557
|
+
* requiresChanges: true,
|
|
1558
|
+
* redactPaths: ['cardNumber'],
|
|
1559
|
+
* })
|
|
1331
1560
|
*
|
|
1332
1561
|
* log.audit(refund({
|
|
1333
1562
|
* actor: { type: 'user', id: user.id },
|
|
@@ -1338,7 +1567,7 @@ function auditDiff(before, after, options = {}) {
|
|
|
1338
1567
|
*/
|
|
1339
1568
|
function defineAuditAction(action, options) {
|
|
1340
1569
|
const targetType = options?.target;
|
|
1341
|
-
|
|
1570
|
+
const factory = ((input) => {
|
|
1342
1571
|
const merged = {
|
|
1343
1572
|
...input,
|
|
1344
1573
|
action
|
|
@@ -1348,21 +1577,50 @@ function defineAuditAction(action, options) {
|
|
|
1348
1577
|
type: targetType
|
|
1349
1578
|
};
|
|
1350
1579
|
return merged;
|
|
1351
|
-
};
|
|
1580
|
+
});
|
|
1581
|
+
Object.defineProperties(factory, {
|
|
1582
|
+
action: {
|
|
1583
|
+
value: action,
|
|
1584
|
+
enumerable: true
|
|
1585
|
+
},
|
|
1586
|
+
target: {
|
|
1587
|
+
value: options?.target,
|
|
1588
|
+
enumerable: true
|
|
1589
|
+
},
|
|
1590
|
+
description: {
|
|
1591
|
+
value: options?.description,
|
|
1592
|
+
enumerable: true
|
|
1593
|
+
},
|
|
1594
|
+
severity: {
|
|
1595
|
+
value: options?.severity,
|
|
1596
|
+
enumerable: true
|
|
1597
|
+
},
|
|
1598
|
+
requiresChanges: {
|
|
1599
|
+
value: options?.requiresChanges,
|
|
1600
|
+
enumerable: true
|
|
1601
|
+
},
|
|
1602
|
+
requiresReason: {
|
|
1603
|
+
value: options?.requiresReason,
|
|
1604
|
+
enumerable: true
|
|
1605
|
+
},
|
|
1606
|
+
redactPaths: {
|
|
1607
|
+
value: options?.redactPaths,
|
|
1608
|
+
enumerable: true
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
return factory;
|
|
1352
1612
|
}
|
|
1353
1613
|
/**
|
|
1354
1614
|
* Test helper that captures every audit event emitted while it is active.
|
|
1355
1615
|
*
|
|
1356
|
-
* Returns `{ events, restore,
|
|
1616
|
+
* Returns `{ events, restore, toIncludeAuditOf, assertAudit }`:
|
|
1357
1617
|
* - `events` — live array of captured `AuditFields`, populated as audits fire.
|
|
1358
1618
|
* - `restore()` — uninstall the collector. Call from `afterEach()`.
|
|
1359
|
-
* - `
|
|
1360
|
-
*
|
|
1619
|
+
* - `toIncludeAuditOf(matcher)` — returns `true` if at least one captured event matches.
|
|
1620
|
+
* - `assertAudit(matcher)` — returns the matched event or throws with a readable summary.
|
|
1361
1621
|
*
|
|
1362
|
-
*
|
|
1363
|
-
* `
|
|
1364
|
-
* collector by design — wrap them with `log.audit()` to make them visible to
|
|
1365
|
-
* tests.
|
|
1622
|
+
* Captures audits on emit from `log.audit()`, standalone `audit()`, and
|
|
1623
|
+
* `log.set({ audit })` — including auto-filled `idempotencyKey`.
|
|
1366
1624
|
*
|
|
1367
1625
|
* @example
|
|
1368
1626
|
* ```ts
|
|
@@ -1386,6 +1644,18 @@ function mockAudit() {
|
|
|
1386
1644
|
},
|
|
1387
1645
|
toIncludeAuditOf(matcher) {
|
|
1388
1646
|
return events.some((event) => matchesAudit(event, matcher));
|
|
1647
|
+
},
|
|
1648
|
+
assertAudit(matcher) {
|
|
1649
|
+
const match = events.find((event) => matchesAudit(event, matcher));
|
|
1650
|
+
if (!match) {
|
|
1651
|
+
const summary = events.map((e) => ({
|
|
1652
|
+
action: e.action,
|
|
1653
|
+
outcome: e.outcome
|
|
1654
|
+
}));
|
|
1655
|
+
const matcherStr = JSON.stringify(matcher, (_k, v) => v instanceof RegExp ? v.toString() : v);
|
|
1656
|
+
throw new Error(`No audit event matched ${matcherStr}. Captured ${events.length} event(s): ${JSON.stringify(summary)}`);
|
|
1657
|
+
}
|
|
1658
|
+
return match;
|
|
1389
1659
|
}
|
|
1390
1660
|
};
|
|
1391
1661
|
}
|
|
@@ -1426,7 +1696,9 @@ function consumeAuditForceKeep(context) {
|
|
|
1426
1696
|
function finalizeAudit(event) {
|
|
1427
1697
|
const a = event.audit;
|
|
1428
1698
|
if (!a) return;
|
|
1429
|
-
|
|
1699
|
+
const decorated = decorateAudit(a, String(event.timestamp));
|
|
1700
|
+
event.audit = decorated;
|
|
1701
|
+
_testCollector?.(decorated, event);
|
|
1430
1702
|
}
|
|
1431
1703
|
/**
|
|
1432
1704
|
* Enrich audit-bearing wide events with request, runtime, and tenant context.
|
|
@@ -1659,33 +1931,21 @@ function stripIntegrity(event) {
|
|
|
1659
1931
|
* enough signal to be useful.
|
|
1660
1932
|
*/
|
|
1661
1933
|
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"
|
|
1934
|
+
"password",
|
|
1935
|
+
"passwordHash",
|
|
1936
|
+
"token",
|
|
1937
|
+
"apiKey",
|
|
1938
|
+
"secret",
|
|
1939
|
+
"accessToken",
|
|
1940
|
+
"refreshToken",
|
|
1941
|
+
"cardNumber",
|
|
1942
|
+
"cvv",
|
|
1943
|
+
"ssn",
|
|
1944
|
+
"authorization",
|
|
1945
|
+
"cookie",
|
|
1946
|
+
"set-cookie"
|
|
1687
1947
|
] };
|
|
1688
1948
|
//#endregion
|
|
1689
|
-
export {
|
|
1949
|
+
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
1950
|
|
|
1691
|
-
//# sourceMappingURL=audit-
|
|
1951
|
+
//# sourceMappingURL=audit-BQt8yAHo.mjs.map
|