@hackylabs/deep-redact 2.2.0 → 3.0.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 (37) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +36 -56
  3. package/dist/cjs/index.js +10 -131
  4. package/dist/cjs/utils/index.js +429 -0
  5. package/dist/cjs/utils/standardTransformers/bigint.js +10 -0
  6. package/dist/cjs/utils/standardTransformers/date.js +9 -0
  7. package/dist/cjs/utils/standardTransformers/error.js +16 -0
  8. package/dist/cjs/utils/standardTransformers/index.js +19 -0
  9. package/dist/cjs/utils/standardTransformers/map.js +9 -0
  10. package/dist/cjs/utils/standardTransformers/regex.js +15 -0
  11. package/dist/cjs/utils/standardTransformers/set.js +9 -0
  12. package/dist/cjs/utils/standardTransformers/url.js +9 -0
  13. package/dist/esm/index.mjs +9 -128
  14. package/dist/esm/utils/index.mjs +423 -0
  15. package/dist/esm/utils/standardTransformers/bigint.js +6 -0
  16. package/dist/esm/utils/standardTransformers/date.js +5 -0
  17. package/dist/esm/utils/standardTransformers/error.js +12 -0
  18. package/dist/esm/utils/standardTransformers/index.js +16 -0
  19. package/dist/esm/utils/standardTransformers/map.js +5 -0
  20. package/dist/esm/utils/standardTransformers/regex.js +11 -0
  21. package/dist/esm/utils/standardTransformers/set.js +5 -0
  22. package/dist/esm/utils/standardTransformers/url.js +5 -0
  23. package/dist/types/index.d.ts +3 -38
  24. package/dist/types/types.d.ts +48 -17
  25. package/dist/types/utils/index.d.ts +130 -0
  26. package/dist/types/utils/standardTransformers/bigint.d.ts +2 -0
  27. package/dist/types/utils/standardTransformers/date.d.ts +2 -0
  28. package/dist/types/utils/standardTransformers/error.d.ts +2 -0
  29. package/dist/types/utils/standardTransformers/index.d.ts +2 -0
  30. package/dist/types/utils/standardTransformers/map.d.ts +2 -0
  31. package/dist/types/utils/standardTransformers/regex.d.ts +2 -0
  32. package/dist/types/utils/standardTransformers/set.d.ts +2 -0
  33. package/dist/types/utils/standardTransformers/url.d.ts +2 -0
  34. package/package.json +66 -13
  35. package/dist/cjs/utils/redactorUtils.js +0 -263
  36. package/dist/esm/utils/redactorUtils.mjs +0 -264
  37. package/dist/types/utils/redactorUtils.d.ts +0 -91
@@ -1,15 +1,10 @@
1
- import { DeepRedactConfig } from './types';
1
+ import type { DeepRedactConfig, RedactorUtilsConfig, BlacklistKeyConfig, Types, Transformer, ComplexStringTest, BaseDeepRedactConfig } from './types';
2
2
  declare class DeepRedact {
3
3
  /**
4
4
  * The redactorUtils instance to handle the redaction.
5
5
  * @private
6
6
  */
7
7
  private redactorUtils;
8
- /**
9
- * A WeakSet to store circular references during redaction. Reset to null after redaction is complete.
10
- * @private
11
- */
12
- private circularReference;
13
8
  /**
14
9
  * The configuration for the redaction.
15
10
  * @private
@@ -22,42 +17,12 @@ declare class DeepRedact {
22
17
  * @param {DeepRedactConfig} config. The configuration for the redaction.
23
18
  */
24
19
  constructor(config: DeepRedactConfig);
25
- /**
26
- * A transformer for unsupported data types. If `serialise` is false, the value will be returned as is,
27
- * otherwise it will transform the value into a format that is supported by JSON.stringify.
28
- *
29
- * Error, RegExp, and Date instances are technically supported by JSON.stringify,
30
- * but they returned as empty objects, therefore they are also transformed here.
31
- * @protected
32
- * @param {unknown} value The value that is not supported by JSON.stringify.
33
- * @returns {unknown} The value in a format that is supported by JSON.stringify.
34
- */
35
- protected unsupportedTransformer: (value: unknown) => unknown;
36
- /**
37
- * Calls `unsupportedTransformer` on the provided value and rewrites any circular references.
38
- *
39
- * Circular references will always be removed to avoid infinite recursion.
40
- * When a circular reference is found, the value will be replaced with `[[CIRCULAR_REFERENCE: path.to.original.value]]`.
41
- * @protected
42
- * @param {unknown} value The value to rewrite.
43
- * @param {string | undefined} path The path to the value in the object.
44
- * @returns {unknown} The rewritten value.
45
- */
46
- protected rewriteUnsupported: (value: unknown, path?: string) => unknown;
47
- /**
48
- * Depending on the value of `serialise`, return the value as a JSON string or as the provided value.
49
- *
50
- * Also resets the `circularReference` property to null after redaction is complete.
51
- * This is to ensure that the WeakSet doesn't cause memory leaks.
52
- * @private
53
- * @param value
54
- */
55
- private maybeSerialise;
56
20
  /**
57
21
  * Redact the provided value. The value will be stripped of any circular references and other unsupported data types, before being redacted according to the configuration and finally serialised if required.
58
22
  * @param {unknown} value The value to redact.
59
23
  * @returns {unknown} The redacted value.
24
+ * @throws {Error} If the value cannot be serialised to JSON and serialise is true.
60
25
  */
61
26
  redact: (value: unknown) => unknown;
62
27
  }
63
- export { DeepRedact as default, DeepRedact };
28
+ export { DeepRedact, DeepRedact as default, type BaseDeepRedactConfig, type RedactorUtilsConfig, type BlacklistKeyConfig, type ComplexStringTest, type Transformer, type Types, };
@@ -1,5 +1,5 @@
1
1
  export type Types = 'string' | 'number' | 'bigint' | 'boolean' | 'object' | 'function' | 'symbol' | 'undefined';
2
- export type Transformer = (value: unknown) => unknown;
2
+ export type Transformer = (value: unknown, key?: string, reference?: WeakMap<object, unknown>) => unknown;
3
3
  export interface BlacklistKeyConfig {
4
4
  /**
5
5
  * Perform a fuzzy match on the key. This will match any key that contains the string, rather than a case-sensitive match.
@@ -28,13 +28,23 @@ export interface BlacklistKeyConfig {
28
28
  * @example true // remove the redacted data.
29
29
  */
30
30
  remove?: boolean;
31
+ /**
32
+ * The replacement value for redacted data. Can be a string, or a function that takes the original value and returns any value.
33
+ * @default '[REDACTED]'
34
+ * @example '*' // if `replacement` equals `*` then `joe.bloggs@example.com` becomes `**********************`
35
+ * @example (value) => `REDACTED: ${typeof value}` // redact the value with a prefix of 'REDACTED: ' and the type of the value.
36
+ * @example (value) => return typeof value === 'string' ? '*'.repeat(value.length) : '[REDACTED]' // redact the value with a string of the same length.
37
+ * @param value The original value that is being redacted.
38
+ * @returns The redacted value or undefined to remove the value.
39
+ */
40
+ replacement?: string | ((value: unknown) => unknown);
31
41
  /**
32
42
  * Replace string values with a redacted string of the same length, using the `replacement` option. Ignored if `remove` is true, `replacement` is a function, or the value is not a string.
33
43
  * @default false
34
44
  * @example true // if `replacement` equals `*` then `joe.bloggs@example.com` becomes `**********************`
35
45
  * @example false // if `replacement` equals `*` then `joe.bloggs@example.com` becomes `*`
36
46
  */
37
- replacement?: string | ((value: unknown) => unknown);
47
+ replaceStringByLength?: boolean;
38
48
  /**
39
49
  * The key to redact. Can be a string or a RegExp.
40
50
  * @example 'address' // redact any key that is 'address'.
@@ -54,7 +64,6 @@ export interface BaseDeepRedactConfig {
54
64
  * @example [{ key: 'address', fuzzyKeyMatch: true, caseSensitiveKeyMatch: false }] // redact any key that contains 'address' regardless of case.
55
65
  */
56
66
  blacklistedKeys?: Array<string | RegExp | BlacklistKeyConfig>;
57
- blacklistedKeysTransformed: Array<Required<BlacklistKeyConfig>>;
58
67
  /**
59
68
  * Redact a string value that matches a test pattern.
60
69
  * @default []
@@ -63,7 +72,6 @@ export interface BaseDeepRedactConfig {
63
72
  * ]
64
73
  */
65
74
  stringTests?: Array<RegExp | ComplexStringTest>;
66
- partialStringTests?: Array<ComplexStringTest>;
67
75
  /**
68
76
  * Perform a fuzzy match on the key. This will match any key that contains the string, rather than a case-sensitive match.
69
77
  * @default false
@@ -122,25 +130,48 @@ export interface BaseDeepRedactConfig {
122
130
  * Alias of `serialise` for International-English users.
123
131
  */
124
132
  serialize?: boolean;
133
+ /**
134
+ * A list of transformers to apply when transforming unsupported values.
135
+ * Each transformer should conditionally transform the value, or return the value unchanged to be passed to the next transformer.
136
+ * Transformers will be run in the order they are provided recursively over the entire object.
137
+ * @default []
138
+ * @example [
139
+ * // redact a key by name.
140
+ * standardTransformers.redactByKey,
141
+ * // convert a Date to an ISO string.
142
+ * (value: unknown) => {
143
+ * if (!(value instanceof Date)) return value
144
+ * return value.toISOString()
145
+ * },
146
+ * // convert a BigInt to a string.
147
+ * (value: unknown) => {
148
+ * if (typeof value !== 'bigint') return value
149
+ * return value.toString(10)
150
+ * }
151
+ */
152
+ transformers?: Array<Transformer>;
125
153
  }
126
- export type DeepRedactConfig = Partial<Omit<BaseDeepRedactConfig, 'blacklistedKeysTransformed' | 'blacklistedKeys' | 'stringTests'>> & ({
127
- partialStringTests: BaseDeepRedactConfig['partialStringTests'];
154
+ export type DeepRedactConfig = Partial<Omit<BaseDeepRedactConfig, '_blacklistedKeysTransformed' | 'blacklistedKeys' | 'stringTests'>> & ({
128
155
  blacklistedKeys: BaseDeepRedactConfig['blacklistedKeys'];
129
156
  stringTests: BaseDeepRedactConfig['stringTests'];
130
- } | {
131
- partialStringTests: BaseDeepRedactConfig['partialStringTests'];
132
- blacklistedKeys: BaseDeepRedactConfig['blacklistedKeys'];
133
- } | {
134
- blacklistedKeys: BaseDeepRedactConfig['blacklistedKeys'];
135
- stringTests: BaseDeepRedactConfig['stringTests'];
136
- } | {
137
- partialStringTests: BaseDeepRedactConfig['partialStringTests'];
138
- stringTests: BaseDeepRedactConfig['stringTests'];
139
- } | {
140
- partialStringTests: BaseDeepRedactConfig['partialStringTests'];
141
157
  } | {
142
158
  blacklistedKeys: BaseDeepRedactConfig['blacklistedKeys'];
143
159
  } | {
144
160
  stringTests: BaseDeepRedactConfig['stringTests'];
145
161
  });
146
162
  export type RedactorUtilsConfig = Omit<BaseDeepRedactConfig, 'serialise' | 'serialize'>;
163
+ export type StackReference = WeakMap<object, unknown>;
164
+ export type Stack = Array<{
165
+ parent: any;
166
+ key: string;
167
+ value: unknown;
168
+ path: Array<string | number>;
169
+ redactingParent: boolean;
170
+ keyConfig: BlacklistKeyConfig | undefined;
171
+ }>;
172
+ export type Logs = Array<{
173
+ path: string;
174
+ message: string;
175
+ raw: unknown;
176
+ transformed: unknown;
177
+ }> | null;
@@ -0,0 +1,130 @@
1
+ import type { RedactorUtilsConfig } from '../types';
2
+ declare class RedactorUtils {
3
+ /**
4
+ * The configuration for the redaction.
5
+ * @private
6
+ */
7
+ private readonly config;
8
+ /**
9
+ * The computed regex pattern generated from sanitised blacklist keys of flat strings
10
+ * @private
11
+ */
12
+ private readonly computedRegex;
13
+ /**
14
+ * Regex to sanitise strings for the computed regex
15
+ * @private
16
+ */
17
+ private readonly sanitiseRegex;
18
+ /**
19
+ * The transformed blacklist keys of flat regex patterns and complex config objects
20
+ * @private
21
+ */
22
+ private readonly blacklistedKeysTransformed;
23
+ constructor(customConfig: RedactorUtilsConfig);
24
+ private createTransformedBlacklistedKey;
25
+ /**
26
+ * Applies transformers to a value
27
+ * @param value - The value to transform
28
+ * @param key - The key to check
29
+ * @returns The transformed value
30
+ * @private
31
+ */
32
+ private applyTransformers;
33
+ /**
34
+ * Sanitises a string for the computed regex
35
+ * @param key - The string to sanitise
36
+ * @returns The sanitised string
37
+ * @private
38
+ */
39
+ private sanitiseStringForRegex;
40
+ /**
41
+ * Checks if a key should be redacted
42
+ * @param key - The key to check
43
+ * @returns Whether the key should be redacted
44
+ * @private
45
+ */
46
+ private shouldRedactKey;
47
+ /**
48
+ * Checks if a value should be redacted
49
+ * @param value - The value to check
50
+ * @param key - The key to check
51
+ * @returns Whether the value should be redacted
52
+ * @private
53
+ */
54
+ private shouldRedactValue;
55
+ /**
56
+ * Redacts a value based on the key-specific config
57
+ * @param value - The value to redact
58
+ * @param key - The key to check
59
+ * @param redactingParent - Whether the parent is being redacted
60
+ * @returns The redacted value
61
+ * @private
62
+ */
63
+ private redactValue;
64
+ /**
65
+ * Applies string transformations
66
+ * @param value - The value to transform
67
+ * @param key - The key to check
68
+ * @returns The transformed value
69
+ * @private
70
+ */
71
+ private applyStringTransformations;
72
+ /**
73
+ * Handles primitive values
74
+ * @param value - The value to handle
75
+ * @param key - The key to check
76
+ * @param redactingParent - Whether the parent is being redacted
77
+ * @param keyConfig - The key config
78
+ * @returns The transformed value
79
+ * @private
80
+ */
81
+ private handlePrimitiveValue;
82
+ /**
83
+ * Handles object values
84
+ * @param value - The value to handle
85
+ * @param key - The key to check
86
+ * @param path - The path to the value
87
+ * @param redactingParent - Whether the parent is being redacted
88
+ * @param referenceMap - The reference map
89
+ * @returns The transformed value and stack
90
+ * @private
91
+ */
92
+ private handleObjectValue;
93
+ /**
94
+ * Handles object values
95
+ * @param value - The value to handle
96
+ * @param path - The path to the value
97
+ * @param redactingParent - Whether the parent is being redacted
98
+ * @returns The transformed value and stack
99
+ * @private
100
+ */
101
+ private handleRetainStructure;
102
+ /**
103
+ * Finds the matching key config
104
+ * @param key - The key to find
105
+ * @returns The matching key config
106
+ * @private
107
+ */
108
+ private findMatchingKeyConfig;
109
+ /**
110
+ * Initialises the traversal
111
+ * @param raw - The raw value to traverse
112
+ * @returns The output and stack
113
+ * @private
114
+ */
115
+ private initialiseTraversal;
116
+ /**
117
+ * Pre-processes the input to replace circular references with transformer objects
118
+ * @param raw - The raw value to process
119
+ * @returns The processed value with circular references replaced
120
+ * @private
121
+ */
122
+ private replaceCircularReferences;
123
+ /**
124
+ * Traverses the raw value
125
+ * @param raw - The raw value to traverse
126
+ * @returns The transformed value
127
+ */
128
+ traverse: (raw: unknown) => unknown;
129
+ }
130
+ export default RedactorUtils;
@@ -0,0 +1,2 @@
1
+ import type { Transformer } from '../../types';
2
+ export declare const _bigint: Transformer;
@@ -0,0 +1,2 @@
1
+ import type { Transformer } from "../../types";
2
+ export declare const _date: Transformer;
@@ -0,0 +1,2 @@
1
+ import type { Transformer } from "../../types";
2
+ export declare const _error: Transformer;
@@ -0,0 +1,2 @@
1
+ import type { Transformer } from "src/types";
2
+ export declare const standardTransformers: Transformer[];
@@ -0,0 +1,2 @@
1
+ import type { Transformer } from "../../types";
2
+ export declare const _map: Transformer;
@@ -0,0 +1,2 @@
1
+ import type { Transformer } from "../../types";
2
+ export declare const _regex: Transformer;
@@ -0,0 +1,2 @@
1
+ import type { Transformer } from "../../types";
2
+ export declare const _set: Transformer;
@@ -0,0 +1,2 @@
1
+ import type { Transformer } from "../../types";
2
+ export declare const _url: Transformer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hackylabs/deep-redact",
3
- "version": "2.2.0",
3
+ "version": "3.0.0",
4
4
  "description": "A fast, safe and configurable zero-dependency library for redacting strings or deeply redacting arrays and objects.",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -11,14 +11,59 @@
11
11
  "module": "./dist/esm/index.mjs",
12
12
  "exports": {
13
13
  ".": {
14
- "import": "./dist/esm/index.mjs",
15
- "require": "./dist/cjs/index.js",
14
+ "import": "./dist/index",
15
+ "require": "./dist/index",
16
16
  "types": "./dist/types/index.d.ts"
17
17
  },
18
- "./utils/redactorUtils": {
19
- "import": "./dist/esm/utils/redactorUtils.mjs",
20
- "require": "./dist/cjs/utils/redactorUtils.js",
21
- "types": "./dist/types/utils/redactorUtils.d.ts"
18
+ "./types": {
19
+ "import": "./dist/types",
20
+ "require": "./dist/types",
21
+ "types": "./dist/types/types.d.ts"
22
+ },
23
+ "./utils": {
24
+ "import": "./dist/utils",
25
+ "require": "./dist/utils",
26
+ "types": "./dist/types/utils.d.ts"
27
+ },
28
+ "./utils/standardTransformers/bigint": {
29
+ "import": "./dist/utils/standardTransformers/bigint",
30
+ "require": "./dist/utils/standardTransformers/bigint",
31
+ "types": "./dist/types/utils/standardTransformers/bigint.d.ts"
32
+ },
33
+ "./utils/standardTransformers/date": {
34
+ "import": "./dist/utils/standardTransformers/date",
35
+ "require": "./dist/utils/standardTransformers/date",
36
+ "types": "./dist/types/utils/standardTransformers/date.d.ts"
37
+ },
38
+ "./utils/standardTransformers/error": {
39
+ "import": "./dist/utils/standardTransformers/error",
40
+ "require": "./dist/utils/standardTransformers/error",
41
+ "types": "./dist/types/utils/standardTransformers/error.d.ts"
42
+ },
43
+ "./utils/standardTransformers": {
44
+ "import": "./dist/utils/standardTransformers",
45
+ "require": "./dist/utils/standardTransformers",
46
+ "types": "./dist/types/utils/standardTransformers.d.ts"
47
+ },
48
+ "./utils/standardTransformers/map": {
49
+ "import": "./dist/utils/standardTransformers/map",
50
+ "require": "./dist/utils/standardTransformers/map",
51
+ "types": "./dist/types/utils/standardTransformers/map.d.ts"
52
+ },
53
+ "./utils/standardTransformers/regex": {
54
+ "import": "./dist/utils/standardTransformers/regex",
55
+ "require": "./dist/utils/standardTransformers/regex",
56
+ "types": "./dist/types/utils/standardTransformers/regex.d.ts"
57
+ },
58
+ "./utils/standardTransformers/set": {
59
+ "import": "./dist/utils/standardTransformers/set",
60
+ "require": "./dist/utils/standardTransformers/set",
61
+ "types": "./dist/types/utils/standardTransformers/set.d.ts"
62
+ },
63
+ "./utils/standardTransformers/url": {
64
+ "import": "./dist/utils/standardTransformers/url",
65
+ "require": "./dist/utils/standardTransformers/url",
66
+ "types": "./dist/types/utils/standardTransformers/url.d.ts"
22
67
  }
23
68
  },
24
69
  "files": [
@@ -51,28 +96,35 @@
51
96
  },
52
97
  "scripts": {
53
98
  "lint": "eslint",
54
- "build": "npm run lint && npm run test && npm run bench && npm run build:esm && npm run build:cjs && npm run update-readme && npm run update-license",
99
+ "build": "npm run lint && npm run test && npm run bench && npm run build:esm && npm run build:cjs && npm run update-exports && npm run update-readme && npm run update-license",
55
100
  "build:esm": "tsc --project tsconfig.esm.json && ./scripts/js-to-mjs.sh",
56
101
  "build:cjs": "tsc --project tsconfig.cjs.json",
57
102
  "bench": "npx vitest bench --watch=false",
58
103
  "bench:dev": "npx vitest bench",
59
104
  "test:dev": "npx vitest",
60
105
  "test": "npx vitest run",
106
+ "load": "npx vitest run test/load/redact.test.ts --reporter=basic --coverage=false",
107
+ "load:dev": "npx vitest test/load/redact.test.ts --coverage=false --reporter=basic",
108
+ "unit": "npx vitest run test/unit --reporter=verbose",
109
+ "unit:dev": "npx vitest test/unit --coverage=false --reporter=verbose",
61
110
  "update-readme": "npx ts-node ./scripts/update-readme.ts",
62
- "update-license": "npx ts-node ./scripts/update-license.ts"
111
+ "update-license": "npx ts-node ./scripts/update-license.ts",
112
+ "update-exports": "npx ts-node ./scripts/update-exports.ts"
63
113
  },
64
114
  "//": [
65
- "fast-redact and obglob are installed only as benchmark comparisons and are not used in the library",
115
+ "autocannon, fast-redact and obglob are installed only as internal benchmark comparisons and are not used in the library",
66
116
  "all dependencies are for development purposes only"
67
117
  ],
68
118
  "devDependencies": {
69
119
  "@hackylabs/obglob": "1.1.2",
70
120
  "@memlab/core": "1.1.34",
121
+ "@types/autocannon": "7.12.6",
71
122
  "@types/fast-redact": "3.0.4",
72
123
  "@types/node": "20.14.12",
73
124
  "@typescript-eslint/eslint-plugin": "7.14.1",
74
125
  "@typescript-eslint/parser": "7.14.1",
75
126
  "@vitest/coverage-v8": "2.0.4",
127
+ "autocannon": "8.0.0",
76
128
  "eslint": "8.57.0",
77
129
  "eslint-config-airbnb-base": "15.0.0",
78
130
  "eslint-config-airbnb-typescript": "18.0.0",
@@ -80,9 +132,10 @@
80
132
  "eslint-plugin-n": "17.9.0",
81
133
  "eslint-plugin-promise": "7.0.0",
82
134
  "fast-redact": "3.5.0",
83
- "image-charts": "6.1.19",
84
- "superjson": "2.2.1",
85
135
  "typescript": "5.6.2",
86
136
  "vitest": "2.0.4"
87
- }
137
+ },
138
+ "editor.formatOnSave": true,
139
+ "editor.formatOnPaste": true,
140
+ "editor.defaultFormatter": "EditorConfig.editorconfig"
88
141
  }