pw-sanitizer 0.1.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.
Files changed (86) hide show
  1. package/README.md +415 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +113 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/config/loader.d.ts +30 -0
  7. package/dist/config/loader.d.ts.map +1 -0
  8. package/dist/config/loader.js +173 -0
  9. package/dist/config/loader.js.map +1 -0
  10. package/dist/config/types.d.ts +432 -0
  11. package/dist/config/types.d.ts.map +1 -0
  12. package/dist/config/types.js +6 -0
  13. package/dist/config/types.js.map +1 -0
  14. package/dist/config/validator.d.ts +34 -0
  15. package/dist/config/validator.d.ts.map +1 -0
  16. package/dist/config/validator.js +95 -0
  17. package/dist/config/validator.js.map +1 -0
  18. package/dist/index.d.ts +74 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +190 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/logger.d.ts +82 -0
  23. package/dist/logger.d.ts.map +1 -0
  24. package/dist/logger.js +122 -0
  25. package/dist/logger.js.map +1 -0
  26. package/dist/processors/html-report.d.ts +27 -0
  27. package/dist/processors/html-report.d.ts.map +1 -0
  28. package/dist/processors/html-report.js +220 -0
  29. package/dist/processors/html-report.js.map +1 -0
  30. package/dist/processors/screenshot.d.ts +37 -0
  31. package/dist/processors/screenshot.d.ts.map +1 -0
  32. package/dist/processors/screenshot.js +52 -0
  33. package/dist/processors/screenshot.js.map +1 -0
  34. package/dist/processors/trace-file.d.ts +29 -0
  35. package/dist/processors/trace-file.d.ts.map +1 -0
  36. package/dist/processors/trace-file.js +250 -0
  37. package/dist/processors/trace-file.js.map +1 -0
  38. package/dist/redact/json-walker.d.ts +28 -0
  39. package/dist/redact/json-walker.d.ts.map +1 -0
  40. package/dist/redact/json-walker.js +164 -0
  41. package/dist/redact/json-walker.js.map +1 -0
  42. package/dist/redact/matcher.d.ts +25 -0
  43. package/dist/redact/matcher.d.ts.map +1 -0
  44. package/dist/redact/matcher.js +130 -0
  45. package/dist/redact/matcher.js.map +1 -0
  46. package/dist/redact/pattern-loader.d.ts +25 -0
  47. package/dist/redact/pattern-loader.d.ts.map +1 -0
  48. package/dist/redact/pattern-loader.js +111 -0
  49. package/dist/redact/pattern-loader.js.map +1 -0
  50. package/dist/redact/pattern-registry.d.ts +17 -0
  51. package/dist/redact/pattern-registry.d.ts.map +1 -0
  52. package/dist/redact/pattern-registry.js +58 -0
  53. package/dist/redact/pattern-registry.js.map +1 -0
  54. package/dist/remove/detector.d.ts +22 -0
  55. package/dist/remove/detector.d.ts.map +1 -0
  56. package/dist/remove/detector.js +152 -0
  57. package/dist/remove/detector.js.map +1 -0
  58. package/dist/remove/remover.d.ts +18 -0
  59. package/dist/remove/remover.d.ts.map +1 -0
  60. package/dist/remove/remover.js +72 -0
  61. package/dist/remove/remover.js.map +1 -0
  62. package/dist/remove/rule-loader.d.ts +25 -0
  63. package/dist/remove/rule-loader.d.ts.map +1 -0
  64. package/dist/remove/rule-loader.js +110 -0
  65. package/dist/remove/rule-loader.js.map +1 -0
  66. package/dist/remove/rule-registry.d.ts +17 -0
  67. package/dist/remove/rule-registry.d.ts.map +1 -0
  68. package/dist/remove/rule-registry.js +58 -0
  69. package/dist/remove/rule-registry.js.map +1 -0
  70. package/dist/remove/timestamp-repair.d.ts +28 -0
  71. package/dist/remove/timestamp-repair.d.ts.map +1 -0
  72. package/dist/remove/timestamp-repair.js +157 -0
  73. package/dist/remove/timestamp-repair.js.map +1 -0
  74. package/dist/reporter.d.ts +44 -0
  75. package/dist/reporter.d.ts.map +1 -0
  76. package/dist/reporter.js +180 -0
  77. package/dist/reporter.js.map +1 -0
  78. package/dist/teardown.d.ts +27 -0
  79. package/dist/teardown.d.ts.map +1 -0
  80. package/dist/teardown.js +42 -0
  81. package/dist/teardown.js.map +1 -0
  82. package/dist/utils.d.ts +36 -0
  83. package/dist/utils.d.ts.map +1 -0
  84. package/dist/utils.js +112 -0
  85. package/dist/utils.js.map +1 -0
  86. package/package.json +71 -0
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.walkAndRedact = walkAndRedact;
4
+ const matcher_js_1 = require("./matcher.js");
5
+ /**
6
+ * Recursively traverses a JSON-like structure and redacts any string values
7
+ * that match the provided patterns.
8
+ *
9
+ * Traversal rules:
10
+ * - **Arrays**: each element is walked with an indexed path (e.g. `items[0].token`).
11
+ * - **Objects**: each property is visited; the key is tested against patterns.
12
+ * - **Strings**: tested via {@link redactValue}. If not redacted, the value is also
13
+ * tested as embedded JSON (a string that is itself a JSON object/array) and, if
14
+ * parseable, the inner structure is walked recursively and re-serialised.
15
+ * - **Base64 JSON bodies**: if an object has a `Content-Type: application/json`
16
+ * sibling and a body-like field containing base64 data, the decoded JSON is
17
+ * walked and the field is re-encoded after redaction.
18
+ * - **Numbers / booleans**: copied through as-is (never redacted).
19
+ * - **`null` / `undefined`**: returned unchanged.
20
+ *
21
+ * The input is **never mutated** — a new tree is produced for each call.
22
+ *
23
+ * @param obj - The JSON value to walk. Typically a parsed `trace.json` array
24
+ * or the `window.__pw_report_data__` object extracted from an HTML report.
25
+ * @param patterns - Ordered list of {@link RedactPattern}s to apply.
26
+ * @param config - The redact config, forwarded to {@link redactValue} for placeholder resolution.
27
+ * @returns A {@link WalkResult} containing the transformed tree, total redaction count,
28
+ * and the list of individual {@link RedactionMatch}es.
29
+ */
30
+ function walkAndRedact(obj, patterns, config) {
31
+ const matches = [];
32
+ let count = 0;
33
+ function walk(node, keyPath) {
34
+ if (node === null || node === undefined) {
35
+ return node;
36
+ }
37
+ if (Array.isArray(node)) {
38
+ // Mutate array in place to save memory
39
+ for (let i = 0; i < node.length; i++) {
40
+ node[i] = walk(node[i], `${keyPath}[${i}]`);
41
+ }
42
+ return node;
43
+ }
44
+ if (typeof node === 'object') {
45
+ const objNode = node;
46
+ let hasJsonContentType = false;
47
+ const bodyKeys = [];
48
+ for (const [key, value] of Object.entries(objNode)) {
49
+ const currentPath = keyPath ? `${keyPath}.${key}` : key;
50
+ const keyLower = key.toLowerCase();
51
+ // Check for content-type indicating JSON
52
+ if ((keyLower === 'content-type' || keyLower === 'contenttype') &&
53
+ typeof value === 'string' &&
54
+ value.toLowerCase().includes('application/json')) {
55
+ hasJsonContentType = true;
56
+ }
57
+ else if (['body', 'content', 'data', 'payload'].includes(keyLower) &&
58
+ typeof value === 'string' &&
59
+ isLikelyBase64(value)) {
60
+ bodyKeys.push(key);
61
+ }
62
+ if (typeof value === 'string') {
63
+ // Try to redact the string value
64
+ const redactionResult = (0, matcher_js_1.redactValue)(key, value, patterns, config);
65
+ if (redactionResult.redacted) {
66
+ objNode[key] = redactionResult.value;
67
+ count++;
68
+ matches.push({
69
+ keyPath: currentPath,
70
+ patternId: redactionResult.matchedPatternId,
71
+ });
72
+ }
73
+ else {
74
+ // Check if the string is embedded JSON
75
+ const parsed = tryParseJson(value);
76
+ if (parsed !== undefined) {
77
+ const innerResult = walk(parsed, currentPath);
78
+ objNode[key] = JSON.stringify(innerResult);
79
+ }
80
+ // else string remains unchanged
81
+ }
82
+ }
83
+ else if (typeof value === 'object' && value !== null) {
84
+ // Recurse into nested objects/arrays
85
+ objNode[key] = walk(value, currentPath);
86
+ }
87
+ // Booleans and numbers remain unchanged
88
+ }
89
+ // Handle base64-encoded JSON bodies
90
+ if (hasJsonContentType && bodyKeys.length > 0) {
91
+ for (const bodyKey of bodyKeys) {
92
+ const bodyValue = objNode[bodyKey];
93
+ if (typeof bodyValue === 'string') {
94
+ const decoded = tryDecodeBase64Json(bodyValue);
95
+ if (decoded !== undefined) {
96
+ const innerPath = keyPath ? `${keyPath}.${bodyKey}` : bodyKey;
97
+ const redacted = walk(decoded, innerPath);
98
+ objNode[bodyKey] = Buffer.from(JSON.stringify(redacted)).toString('base64');
99
+ }
100
+ }
101
+ }
102
+ }
103
+ return objNode;
104
+ }
105
+ return node;
106
+ }
107
+ const result = walk(obj, '');
108
+ return { result, count, matches };
109
+ }
110
+ /**
111
+ * Attempts to parse a string as JSON.
112
+ *
113
+ * Uses a fast heuristic to skip non-JSON strings: the trimmed value must start
114
+ * with `{` and end with `}`, or start with `[` and end with `]`.
115
+ *
116
+ * @param value - The string to attempt to parse.
117
+ * @returns The parsed value, or `undefined` if parsing fails or the value
118
+ * does not look like JSON.
119
+ */
120
+ function tryParseJson(value) {
121
+ // Quick heuristic: only try if it starts with { or [
122
+ const trimmed = value.trim();
123
+ if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
124
+ (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
125
+ try {
126
+ return JSON.parse(value);
127
+ }
128
+ catch {
129
+ return undefined;
130
+ }
131
+ }
132
+ return undefined;
133
+ }
134
+ /**
135
+ * Heuristic check for whether a string looks like base64-encoded data.
136
+ *
137
+ * Considers a string base64 if it is at least 4 characters long and consists
138
+ * entirely of the base64 alphabet (`A-Za-z0-9+/`) with optional `=` padding.
139
+ *
140
+ * @param value - The string to test.
141
+ * @returns `true` if the value matches the base64 character set.
142
+ */
143
+ function isLikelyBase64(value) {
144
+ if (value.length < 4)
145
+ return false;
146
+ // Base64 uses A-Z, a-z, 0-9, +, /, = (padding)
147
+ return /^[A-Za-z0-9+/]+=*$/.test(value);
148
+ }
149
+ /**
150
+ * Attempts to decode a base64 string and parse the result as JSON.
151
+ *
152
+ * @param value - A base64-encoded string (UTF-8 JSON content expected).
153
+ * @returns The parsed JSON value, or `undefined` if decoding or parsing fails.
154
+ */
155
+ function tryDecodeBase64Json(value) {
156
+ try {
157
+ const decoded = Buffer.from(value, 'base64').toString('utf-8');
158
+ return JSON.parse(decoded);
159
+ }
160
+ catch {
161
+ return undefined;
162
+ }
163
+ }
164
+ //# sourceMappingURL=json-walker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-walker.js","sourceRoot":"","sources":["../../src/redact/json-walker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { RedactConfig, RedactPattern, RedactionResult } from '../config/types.js';
2
+ /**
3
+ * Evaluates a key/value pair against the full list of patterns and returns
4
+ * a {@link RedactionResult} describing whether — and how — it was redacted.
5
+ *
6
+ * Matching rules:
7
+ * - Pattern with only `key`: key must match.
8
+ * - Pattern with only `valuePattern`: value must match.
9
+ * - Pattern with both: **both** must match (AND logic).
10
+ * - Patterns are tested in order; the **first** match wins.
11
+ *
12
+ * @remarks
13
+ * This function intentionally **never logs the matched value** to prevent
14
+ * secrets from appearing in log output. Only the key name and pattern ID
15
+ * are logged at verbose level.
16
+ *
17
+ * @param key - The field name (e.g. header name, JSON property key).
18
+ * @param value - The current string value of the field.
19
+ * @param patterns - Ordered list of {@link RedactPattern}s to test against.
20
+ * @param config - The redact config, used to resolve placeholder / partial-redaction settings.
21
+ * @returns A {@link RedactionResult} with `redacted: false` if no pattern matched,
22
+ * or `redacted: true` with the replacement value and matched pattern ID.
23
+ */
24
+ export declare function redactValue(key: string, value: string, patterns: RedactPattern[], config: RedactConfig): RedactionResult;
25
+ //# sourceMappingURL=matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../../src/redact/matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAgEvF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,aAAa,EAAE,EACzB,MAAM,EAAE,YAAY,GACnB,eAAe,CAoDjB"}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.redactValue = redactValue;
4
+ const logger_js_1 = require("../logger.js");
5
+ /**
6
+ * Tests whether an event key matches a pattern's `key` field.
7
+ *
8
+ * - `string` → exact case-insensitive comparison
9
+ * - `RegExp` → tested against the raw key string
10
+ *
11
+ * @param key - The key to test (e.g. a header name or JSON property name).
12
+ * @param patternKey - The matcher from {@link RedactPattern.key}.
13
+ * @returns `true` if the key satisfies the matcher.
14
+ */
15
+ function matchesKey(key, patternKey) {
16
+ if (typeof patternKey === 'string') {
17
+ return key.toLowerCase() === patternKey.toLowerCase();
18
+ }
19
+ return patternKey.test(key);
20
+ }
21
+ /**
22
+ * Tests whether a string value satisfies a pattern's `valuePattern` regexp.
23
+ *
24
+ * @param value - The string value to test.
25
+ * @param valuePattern - The regexp from {@link RedactPattern.valuePattern}.
26
+ * @returns `true` if the regexp matches.
27
+ */
28
+ function matchesValue(value, valuePattern) {
29
+ return valuePattern.test(value);
30
+ }
31
+ /**
32
+ * Applies partial redaction to a string value.
33
+ *
34
+ * Keeps the first `prefix` characters and last `suffix` characters visible,
35
+ * replacing everything in between with `'***'`.
36
+ * If the value is too short for partial redaction (`length <= prefix + suffix`),
37
+ * the entire value is replaced with `'***'`.
38
+ *
39
+ * @param value - The original string value to partially redact.
40
+ * @param prefix - Number of leading characters to keep visible.
41
+ * @param suffix - Number of trailing characters to keep visible.
42
+ * @returns The partially redacted string.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * applyPartialRedaction('Bearer eyJhbGci', 4, 4); // 'Bear***lci'
47
+ * applyPartialRedaction('short', 4, 4); // '***' (too short)
48
+ * ```
49
+ */
50
+ function applyPartialRedaction(value, prefix, suffix) {
51
+ if (value.length <= prefix + suffix) {
52
+ // Value too short for partial redaction — redact entirely
53
+ return '***';
54
+ }
55
+ const start = value.substring(0, prefix);
56
+ const end = value.substring(value.length - suffix);
57
+ return `${start}***${end}`;
58
+ }
59
+ /**
60
+ * Evaluates a key/value pair against the full list of patterns and returns
61
+ * a {@link RedactionResult} describing whether — and how — it was redacted.
62
+ *
63
+ * Matching rules:
64
+ * - Pattern with only `key`: key must match.
65
+ * - Pattern with only `valuePattern`: value must match.
66
+ * - Pattern with both: **both** must match (AND logic).
67
+ * - Patterns are tested in order; the **first** match wins.
68
+ *
69
+ * @remarks
70
+ * This function intentionally **never logs the matched value** to prevent
71
+ * secrets from appearing in log output. Only the key name and pattern ID
72
+ * are logged at verbose level.
73
+ *
74
+ * @param key - The field name (e.g. header name, JSON property key).
75
+ * @param value - The current string value of the field.
76
+ * @param patterns - Ordered list of {@link RedactPattern}s to test against.
77
+ * @param config - The redact config, used to resolve placeholder / partial-redaction settings.
78
+ * @returns A {@link RedactionResult} with `redacted: false` if no pattern matched,
79
+ * or `redacted: true` with the replacement value and matched pattern ID.
80
+ */
81
+ function redactValue(key, value, patterns, config) {
82
+ for (const pattern of patterns) {
83
+ let keyMatches = true;
84
+ let valueMatches = true;
85
+ // Check key match (if pattern specifies a key)
86
+ if (pattern.key !== undefined) {
87
+ keyMatches = matchesKey(key, pattern.key);
88
+ }
89
+ // Check value match (if pattern specifies a valuePattern)
90
+ if (pattern.valuePattern !== undefined) {
91
+ valueMatches = matchesValue(value, pattern.valuePattern);
92
+ }
93
+ // If pattern has only key: key must match
94
+ // If pattern has only valuePattern: value must match
95
+ // If pattern has both: both must match (AND logic)
96
+ if (pattern.key !== undefined && pattern.valuePattern !== undefined) {
97
+ // AND logic: both must match
98
+ if (!keyMatches || !valueMatches)
99
+ continue;
100
+ }
101
+ else if (pattern.key !== undefined) {
102
+ if (!keyMatches)
103
+ continue;
104
+ }
105
+ else if (pattern.valuePattern !== undefined) {
106
+ if (!valueMatches)
107
+ continue;
108
+ }
109
+ else {
110
+ // No matchers (should have been caught by validation)
111
+ continue;
112
+ }
113
+ // Match found — apply redaction
114
+ logger_js_1.logger.verbose(`Redacting key "${key}" matched by pattern "${pattern.id}"`);
115
+ let redactedValue;
116
+ if (config.partialRedaction) {
117
+ redactedValue = applyPartialRedaction(value, config.partialRedaction.prefix, config.partialRedaction.suffix);
118
+ }
119
+ else {
120
+ redactedValue = config.placeholder ?? '[REDACTED]';
121
+ }
122
+ return {
123
+ redacted: true,
124
+ value: redactedValue,
125
+ matchedPatternId: pattern.id,
126
+ };
127
+ }
128
+ return { redacted: false, value };
129
+ }
130
+ //# sourceMappingURL=matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../src/redact/matcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { RedactPattern } from '../config/types.js';
2
+ /**
3
+ * Thrown when a pattern file path listed in `redact.patternFiles` cannot be
4
+ * resolved or loaded.
5
+ */
6
+ export declare class PatternFileNotFoundError extends Error {
7
+ constructor(filePath: string);
8
+ }
9
+ /**
10
+ * Loads an array of {@link RedactPattern}s from an external file.
11
+ *
12
+ * Supported formats:
13
+ * - **`.json`** — parsed with `JSON.parse`; RegExp values are not supported
14
+ * (use string patterns instead, which are matched case-insensitively).
15
+ * - **`.ts` / `.js`** — loaded via dynamic `import()`; the file must export a
16
+ * default array of {@link RedactPattern} objects (supports `RegExp` fields).
17
+ * If a `.ts` file fails to import (e.g. no `tsx` / `ts-node` available),
18
+ * a compiled `.js` sibling at the same path is tried automatically.
19
+ *
20
+ * @param filePath - Absolute or relative path to the pattern file.
21
+ * @returns Array of {@link RedactPattern}s defined in the file.
22
+ * @throws {@link PatternFileNotFoundError} if the file does not exist or cannot be loaded.
23
+ */
24
+ export declare function loadPatternFile(filePath: string): Promise<RedactPattern[]>;
25
+ //# sourceMappingURL=pattern-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-loader.d.ts","sourceRoot":"","sources":["../../src/redact/pattern-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,QAAQ,EAAE,MAAM;CAI7B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAkDhF"}
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PatternFileNotFoundError = void 0;
37
+ exports.loadPatternFile = loadPatternFile;
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ const logger_js_1 = require("../logger.js");
41
+ /**
42
+ * Thrown when a pattern file path listed in `redact.patternFiles` cannot be
43
+ * resolved or loaded.
44
+ */
45
+ class PatternFileNotFoundError extends Error {
46
+ constructor(filePath) {
47
+ super(`redact.patternFiles: file not found: ${filePath}`);
48
+ this.name = 'PatternFileNotFoundError';
49
+ }
50
+ }
51
+ exports.PatternFileNotFoundError = PatternFileNotFoundError;
52
+ /**
53
+ * Loads an array of {@link RedactPattern}s from an external file.
54
+ *
55
+ * Supported formats:
56
+ * - **`.json`** — parsed with `JSON.parse`; RegExp values are not supported
57
+ * (use string patterns instead, which are matched case-insensitively).
58
+ * - **`.ts` / `.js`** — loaded via dynamic `import()`; the file must export a
59
+ * default array of {@link RedactPattern} objects (supports `RegExp` fields).
60
+ * If a `.ts` file fails to import (e.g. no `tsx` / `ts-node` available),
61
+ * a compiled `.js` sibling at the same path is tried automatically.
62
+ *
63
+ * @param filePath - Absolute or relative path to the pattern file.
64
+ * @returns Array of {@link RedactPattern}s defined in the file.
65
+ * @throws {@link PatternFileNotFoundError} if the file does not exist or cannot be loaded.
66
+ */
67
+ async function loadPatternFile(filePath) {
68
+ const absolutePath = path.resolve(filePath);
69
+ if (!fs.existsSync(absolutePath)) {
70
+ throw new PatternFileNotFoundError(filePath);
71
+ }
72
+ const ext = path.extname(absolutePath).toLowerCase();
73
+ if (ext === '.json') {
74
+ const content = fs.readFileSync(absolutePath, 'utf-8');
75
+ const parsed = JSON.parse(content);
76
+ if (!Array.isArray(parsed)) {
77
+ logger_js_1.logger.fatal(`Pattern file ${filePath} must export an array of RedactPattern objects.`);
78
+ }
79
+ return parsed;
80
+ }
81
+ // .ts or .js — dynamic import
82
+ try {
83
+ const module = await import(absolutePath);
84
+ const patterns = module.default ?? module;
85
+ if (!Array.isArray(patterns)) {
86
+ logger_js_1.logger.fatal(`Pattern file ${filePath} must export a default array of RedactPattern objects.`);
87
+ }
88
+ return patterns;
89
+ }
90
+ catch (err) {
91
+ // If .ts file failed, try .js sibling
92
+ if (ext === '.ts') {
93
+ const jsSibling = absolutePath.replace(/\.ts$/, '.js');
94
+ if (fs.existsSync(jsSibling)) {
95
+ try {
96
+ const module = await import(jsSibling);
97
+ const patterns = module.default ?? module;
98
+ if (!Array.isArray(patterns)) {
99
+ logger_js_1.logger.fatal(`Pattern file ${jsSibling} must export a default array of RedactPattern objects.`);
100
+ }
101
+ return patterns;
102
+ }
103
+ catch {
104
+ throw new PatternFileNotFoundError(`Failed to load pattern file from both ${filePath} and ${jsSibling}`);
105
+ }
106
+ }
107
+ }
108
+ throw new PatternFileNotFoundError(`Failed to load pattern file ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
109
+ }
110
+ }
111
+ //# sourceMappingURL=pattern-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-loader.js","sourceRoot":"","sources":["../../src/redact/pattern-loader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,0CAkDC;AAjFD,4CAA8B;AAC9B,gDAAkC;AAElC,4CAAsC;AAEtC;;;GAGG;AACH,MAAa,wBAAyB,SAAQ,KAAK;IACjD,YAAY,QAAgB;QAC1B,KAAK,CAAC,wCAAwC,QAAQ,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AALD,4DAKC;AAED;;;;;;;;;;;;;;GAcG;AACI,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IAErD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC;QACtD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,kBAAM,CAAC,KAAK,CAAC,gBAAgB,QAAQ,iDAAiD,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,8BAA8B;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;QAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,kBAAM,CAAC,KAAK,CAAC,gBAAgB,QAAQ,wDAAwD,CAAC,CAAC;QACjG,CAAC;QACD,OAAO,QAA2B,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,sCAAsC;QACtC,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACvD,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC7B,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;oBACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;oBAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC7B,kBAAM,CAAC,KAAK,CAAC,gBAAgB,SAAS,wDAAwD,CAAC,CAAC;oBAClG,CAAC;oBACD,OAAO,QAA2B,CAAC;gBACrC,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,IAAI,wBAAwB,CAChC,yCAAyC,QAAQ,QAAQ,SAAS,EAAE,CACrE,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,IAAI,wBAAwB,CAChC,+BAA+B,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC/F,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { RedactConfig, RedactPattern } from '../config/types.js';
2
+ /**
3
+ * Builds the complete, deduplicated list of {@link RedactPattern}s for a run.
4
+ *
5
+ * Merge order:
6
+ * 1. All patterns loaded from `config.patternFiles` (in declaration order)
7
+ * 2. Inline patterns from `config.patterns`
8
+ *
9
+ * No built-in patterns are ever injected — only what the user explicitly declared.
10
+ * If the same `id` appears more than once, a warning is emitted and the **last**
11
+ * definition wins (last-write-wins semantics).
12
+ *
13
+ * @param config - The `redact` section of the sanitizer config.
14
+ * @returns Deduplicated array of validated {@link RedactPattern}s, ready for use.
15
+ */
16
+ export declare function buildPatternRegistry(config: RedactConfig): Promise<RedactPattern[]>;
17
+ //# sourceMappingURL=pattern-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-registry.d.ts","sourceRoot":"","sources":["../../src/redact/pattern-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKtE;;;;;;;;;;;;;GAaG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,aAAa,EAAE,CAAC,CA6C1B"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildPatternRegistry = buildPatternRegistry;
4
+ const pattern_loader_js_1 = require("./pattern-loader.js");
5
+ const validator_js_1 = require("../config/validator.js");
6
+ const logger_js_1 = require("../logger.js");
7
+ /**
8
+ * Builds the complete, deduplicated list of {@link RedactPattern}s for a run.
9
+ *
10
+ * Merge order:
11
+ * 1. All patterns loaded from `config.patternFiles` (in declaration order)
12
+ * 2. Inline patterns from `config.patterns`
13
+ *
14
+ * No built-in patterns are ever injected — only what the user explicitly declared.
15
+ * If the same `id` appears more than once, a warning is emitted and the **last**
16
+ * definition wins (last-write-wins semantics).
17
+ *
18
+ * @param config - The `redact` section of the sanitizer config.
19
+ * @returns Deduplicated array of validated {@link RedactPattern}s, ready for use.
20
+ */
21
+ async function buildPatternRegistry(config) {
22
+ const allPatterns = [];
23
+ // Load from pattern files first
24
+ if (config.patternFiles) {
25
+ const files = Array.isArray(config.patternFiles)
26
+ ? config.patternFiles
27
+ : [config.patternFiles];
28
+ for (const filePath of files) {
29
+ const patterns = await (0, pattern_loader_js_1.loadPatternFile)(filePath);
30
+ allPatterns.push(...patterns);
31
+ }
32
+ }
33
+ // Then inline patterns
34
+ if (config.patterns) {
35
+ allPatterns.push(...config.patterns);
36
+ }
37
+ // Validate each pattern
38
+ for (const pattern of allPatterns) {
39
+ (0, validator_js_1.validatePattern)(pattern);
40
+ }
41
+ // Check for duplicate IDs and warn
42
+ const seenIds = new Map();
43
+ for (let i = 0; i < allPatterns.length; i++) {
44
+ const pattern = allPatterns[i];
45
+ const previousIndex = seenIds.get(pattern.id);
46
+ if (previousIndex !== undefined) {
47
+ logger_js_1.logger.warn(`Duplicate pattern id "${pattern.id}" — last definition wins (index ${i} replaces ${previousIndex}).`);
48
+ }
49
+ seenIds.set(pattern.id, i);
50
+ }
51
+ // Deduplicate: last-write-wins
52
+ const deduped = new Map();
53
+ for (const pattern of allPatterns) {
54
+ deduped.set(pattern.id, pattern);
55
+ }
56
+ return Array.from(deduped.values());
57
+ }
58
+ //# sourceMappingURL=pattern-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-registry.js","sourceRoot":"","sources":["../../src/redact/pattern-registry.ts"],"names":[],"mappings":";;AAmBA,oDA+CC;AAjED,2DAAsD;AACtD,yDAAyD;AACzD,4CAAsC;AAEtC;;;;;;;;;;;;;GAaG;AACI,KAAK,UAAU,oBAAoB,CACxC,MAAoB;IAEpB,MAAM,WAAW,GAAoB,EAAE,CAAC;IAExC,gCAAgC;IAChC,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;YAC9C,CAAC,CAAC,MAAM,CAAC,YAAY;YACrB,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAE1B,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,MAAM,IAAA,mCAAe,EAAC,QAAQ,CAAC,CAAC;YACjD,WAAW,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,WAAW,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,wBAAwB;IACxB,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QAClC,IAAA,8BAAe,EAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED,mCAAmC;IACnC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAE,CAAC;QAChC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9C,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YAChC,kBAAM,CAAC,IAAI,CACT,yBAAyB,OAAO,CAAC,EAAE,mCAAmC,CAAC,aAAa,aAAa,IAAI,CACtG,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,+BAA+B;IAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IACjD,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { TraceEvent, RemoveRule, RemovalSet } from '../config/types.js';
2
+ /**
3
+ * Identifies all trace events that should be removed based on user-declared rules.
4
+ *
5
+ * Matching semantics:
6
+ * - **Within a rule**: all provided matchers must match (AND logic).
7
+ * - **Across rules**: any matching rule makes an event a removal candidate (OR logic).
8
+ * - **No automatic detection**: zero built-in heuristics; only explicitly declared rules apply.
9
+ *
10
+ * `minConsecutiveOccurrences` safety guard:
11
+ * If a rule declares this threshold, each consecutive run of matching events is
12
+ * evaluated against it. Runs that are *shorter* than the threshold are **skipped**
13
+ * (not removed) and a warning is emitted. This prevents accidentally deleting
14
+ * one-off occurrences of a step that also appears in noisy repeating sequences.
15
+ *
16
+ * @param events - The flat ordered list of trace events to scan.
17
+ * @param rules - The user-declared removal rules to apply.
18
+ * @returns A {@link RemovalSet} containing the indices and match details of every
19
+ * event selected for removal.
20
+ */
21
+ export declare function findStepsToRemove(events: TraceEvent[], rules: RemoveRule[]): RemovalSet;
22
+ //# sourceMappingURL=detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/remove/detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAa,MAAM,oBAAoB,CAAC;AAoGxF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAAE,EACpB,KAAK,EAAE,UAAU,EAAE,GAClB,UAAU,CA8CZ"}