pii-redact 1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 neenakrishnan1501-bit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # PII Redact
2
+
3
+ A lightweight and extensible TypeScript library for identifying and redacting Personally Identifiable Information (PII) from both unstructured text and structured objects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install pii-redact
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **Built-in Matchers**: Email, Phone Number, Credit Card, SSN, IP Address, Physical Address, Zipcode, Location Coordinates, Passport, and Driver's License.
14
+ - **Built-in Strategies**: Replace, Mask, and Hash.
15
+ - **Structured Object Redaction**: Recursively redacts PII throughout deeply nested objects and arrays.
16
+ - **Extensible**: Easily provide your own custom regex matchers or complex redaction strategies.
17
+ - **TypeScript First**: Strict typings for a robust developer experience.
18
+
19
+ ## Basic Usage
20
+
21
+ ### Understanding DefaultMatchers
22
+
23
+ `DefaultMatchers` is a convenience array exported by the library that contains all the standard, built-in matchers. If you only want to redact specific types of PII (e.g., only emails and phone numbers), you can pass an array of just the matchers you want:
24
+
25
+ ```typescript
26
+ import { Redactor, EmailMatcher, PhoneMatcher } from 'pii-redact';
27
+
28
+ // This redactor will ONLY look for emails and phone numbers
29
+ const redactor = new Redactor({ matchers: [EmailMatcher, PhoneMatcher] });
30
+ ```
31
+
32
+ ### Unstructured Text Redaction
33
+
34
+ ```typescript
35
+ import { Redactor, DefaultMatchers } from 'pii-redact';
36
+
37
+ // Create a Redactor using the default built-in matchers
38
+ const redactor = new Redactor({ matchers: DefaultMatchers });
39
+
40
+ const originalText = "Hello, my email is admin@example.com and phone is (555) 123-4567.";
41
+ const safeText = redactor.redact(originalText);
42
+
43
+ console.log(safeText);
44
+ // Output: "Hello, my email is [EMAIL] and phone is [PHONE]."
45
+ ```
46
+
47
+ ### Structured Object Redaction
48
+
49
+ ```typescript
50
+ import { Redactor, DefaultMatchers } from 'pii-redact';
51
+
52
+ const redactor = new Redactor({ matchers: DefaultMatchers });
53
+
54
+ const payload = {
55
+ user: {
56
+ id: 12345,
57
+ name: "John Doe",
58
+ contactDetails: "Contact me at (555) 123-4567",
59
+ emails: ["primary@domain.com", "secondary@domain.com"]
60
+ }
61
+ };
62
+
63
+ const safePayload = redactor.redactObject(payload);
64
+
65
+ console.log(safePayload.user.contactDetails); // "Contact me at [PHONE]"
66
+ console.log(safePayload.user.emails[0]); // "[EMAIL]"
67
+ ```
68
+
69
+ ### HTML Redaction
70
+
71
+ If you have raw HTML and want to redact the visible text *without* breaking tags or attributes, use `.redactHtml()`:
72
+
73
+ ```typescript
74
+ import { Redactor, DefaultMatchers } from 'pii-redact';
75
+
76
+ const redactor = new Redactor({ matchers: DefaultMatchers });
77
+
78
+ const html = `
79
+ <div id="contact-info" data-email="admin@example.com">
80
+ <p>Please contact me at admin@example.com or call (555) 123-4567.</p>
81
+ <a href="mailto:admin@example.com">Email Admin</a>
82
+ </div>
83
+ `;
84
+
85
+ const safeHtml = redactor.redactHtml(html);
86
+
87
+ console.log(safeHtml);
88
+ /* Output:
89
+ <div id="contact-info" data-email="admin@example.com">
90
+ <p>Please contact me at [EMAIL] or call [PHONE].</p>
91
+ <a href="mailto:admin@example.com">Email Admin</a>
92
+ </div>
93
+ */
94
+ ```
95
+
96
+ ## Changing the Redaction Strategy
97
+
98
+ By default, the `Redactor` uses the `ReplaceStrategy` (which replaces matches with e.g. `[EMAIL]`). You can configure this globally:
99
+
100
+ ### Masking
101
+
102
+ Replaces the characters with asterisks, optionally leaving parts of the string visible for context.
103
+
104
+ ```typescript
105
+ import { Redactor, EmailMatcher, MaskStrategy } from 'pii-redact';
106
+
107
+ const redactor = new Redactor({
108
+ matchers: [EmailMatcher],
109
+ // Leaves 2 chars at the start, and 4 at the end visible:
110
+ defaultStrategy: new MaskStrategy({ maskChar: '*', unmaskedStart: 2, unmaskedEnd: 4 })
111
+ });
112
+
113
+ console.log(redactor.redact('john.doe@example.com'));
114
+ // Output: "jo**************.com"
115
+ ```
116
+
117
+ ### Hashing
118
+
119
+ Replaces the characters with a cryptographic hash (useful for maintaining uniqueness in analytics databases without retaining the actual PII).
120
+
121
+ ```typescript
122
+ import { Redactor, SSNMatcher, HashStrategy } from 'pii-redact';
123
+
124
+ const redactor = new Redactor({
125
+ matchers: [SSNMatcher],
126
+ defaultStrategy: new HashStrategy('sha256')
127
+ });
128
+
129
+ console.log(redactor.redact('My SSN is 123-45-6789'));
130
+ // Output: "My SSN is a3b8d... [sha256 hash]"
131
+ ```
132
+
133
+ ### Advanced: NLP Named Entity Recognition
134
+
135
+ By default, the library uses extremely fast RegEx matchers. However, RegEx is purely structural and cannot reliably detect things like People's names or specific Geographic locations without context.
136
+
137
+ To solve this, `pii-redact` ships with an optional Natural Language Processing (NLP) integration via `compromise`. It intelligently reads the context of a sentence to detect **People, Organizations, and Locations**.
138
+
139
+ Because NLP adds a slight performance overhead and dependency weight, it is *not* included in `DefaultMatchers`. You must explicitly import it if you want context-aware redaction.
140
+
141
+ ```typescript
142
+ import { Redactor, NlpMatcher, DefaultMatchers } from 'pii-redact';
143
+
144
+ // Combine the NLP Matcher with standard RegEx Matchers
145
+ const redactor = new Redactor({
146
+ matchers: [
147
+ new NlpMatcher(), // Detects People, Orgs, Locations
148
+ ...DefaultMatchers // Detects Phones, Emails, SSNs, etc.
149
+ ]
150
+ });
151
+
152
+ const text = "John Smith flew to Chicago to visit Microsoft on his (555) 123-4567 phone.";
153
+
154
+ console.log(redactor.redact(text));
155
+ // Output: "[PERSON] flew to [LOCATION] to visit [ORG] on his [PHONE] phone."
156
+ ```
157
+
158
+ You can optionally configure what the `NlpMatcher` specifically looks for:
159
+
160
+ ```typescript
161
+ new NlpMatcher({
162
+ detectPeople: true,
163
+ detectOrganizations: false,
164
+ detectLocations: false
165
+ })
166
+ ```
167
+
168
+ ## Custom Matchers & Strategies
169
+
170
+ You can easily extend the library by implementing the `Matcher` or `RedactionStrategy` interfaces.
171
+
172
+ ```typescript
173
+ import { Matcher, MatchResult, Redactor } from 'pii-redact';
174
+
175
+ class CustomSecretMatcher implements Matcher {
176
+ name = 'secret_code';
177
+
178
+ match(text: string): MatchResult[] {
179
+ const results: MatchResult[] = [];
180
+ const regex = /SECRET-[A-Z0-9]{5}/g;
181
+ let match;
182
+ while ((match = regex.exec(text)) !== null) {
183
+ results.push({ value: match[0], start: match.index, end: match.index + match[0].length });
184
+ }
185
+ return results;
186
+ }
187
+ }
188
+
189
+ const redactor = new Redactor({ matchers: [new CustomSecretMatcher()] });
190
+ ```
191
+
192
+ ## Running the Demo
193
+
194
+ A simple static HTML demo is included in the `demo/` folder to test the library purely in your browser.
195
+
196
+ 1. Navigate to the `demo` directory: `cd demo`
197
+ 2. Open `index.html` in any web browser (e.g. `open index.html` on Mac).
198
+ 3. Type any text and test the different redaction strategies!
199
+
200
+ ## License
201
+
202
+ MIT License.
@@ -0,0 +1,139 @@
1
+ interface Matcher {
2
+ /** Uniquely identifies the type of PII (e.g., 'email', 'phone') */
3
+ name: string;
4
+ /**
5
+ * Finds all instances of the PII in the given text.
6
+ * Returns an array of objects containing the matched string and its start/end indices.
7
+ */
8
+ match(text: string): MatchResult[];
9
+ }
10
+ interface MatchResult {
11
+ value: string;
12
+ start: number;
13
+ end: number;
14
+ /** Optional override for the strategy replacement name */
15
+ name?: string;
16
+ }
17
+ interface RedactionStrategy {
18
+ /** Uniquely identifies the strategy (e.g., 'replace', 'mask', 'hash') */
19
+ name: string;
20
+ /**
21
+ * Applies the strategy to a matched PII string.
22
+ * @param matchedValue The actual PII string found
23
+ * @param matcherName The type of PII (e.g., 'email')
24
+ */
25
+ apply(matchedValue: string, matcherName?: string): string;
26
+ }
27
+ interface RedactorConfig {
28
+ /** The built-in or custom matchers to enable */
29
+ matchers?: Matcher[];
30
+ /** The default fallback strategy if a specific matcher doesn't override it */
31
+ defaultStrategy?: RedactionStrategy;
32
+ }
33
+
34
+ interface NlpMatcherConfig {
35
+ detectPeople?: boolean;
36
+ detectOrganizations?: boolean;
37
+ detectLocations?: boolean;
38
+ }
39
+ /**
40
+ * A matcher that uses Natural Language Processing (via compromise)
41
+ * to detect named entities through sentence context rather than regex.
42
+ */
43
+ declare class NlpMatcher implements Matcher {
44
+ name: string;
45
+ private config;
46
+ constructor(config?: NlpMatcherConfig);
47
+ match(text: string): MatchResult[];
48
+ }
49
+
50
+ /**
51
+ * A base class for Regex-based matchers to avoid boilerplate.
52
+ */
53
+ declare class RegexMatcher implements Matcher {
54
+ name: string;
55
+ private regex;
56
+ constructor(name: string, regex: RegExp);
57
+ match(text: string): MatchResult[];
58
+ }
59
+ declare const EmailMatcher: RegexMatcher;
60
+ declare const PhoneMatcher: RegexMatcher;
61
+ declare const CreditCardMatcher: RegexMatcher;
62
+ declare const SSNMatcher: RegexMatcher;
63
+ declare const IPv4Matcher: RegexMatcher;
64
+ declare const ZipcodeMatcher: RegexMatcher;
65
+ declare const AddressMatcher: RegexMatcher;
66
+ declare const LocationCoordinatesMatcher: RegexMatcher;
67
+ declare const PassportMatcher: RegexMatcher;
68
+ declare const MentionMatcher: RegexMatcher;
69
+ declare const DriverLicenseMatcher: RegexMatcher;
70
+ declare const DateOfBirthMatcher: RegexMatcher;
71
+
72
+ declare class Redactor {
73
+ private matchers;
74
+ private defaultStrategy;
75
+ constructor(config?: RedactorConfig);
76
+ /**
77
+ * Redacts PII from an unstructured text string.
78
+ */
79
+ redact(text: string): string;
80
+ /**
81
+ * Recursively traverses an object or array and redacts any string values found.
82
+ */
83
+ redactObject<T>(obj: T): T;
84
+ /**
85
+ * Safely redacts PII from an HTML string without corrupting tags or attributes.
86
+ */
87
+ redactHtml(htmlString: string): string;
88
+ }
89
+
90
+ /**
91
+ * Replaces the matched PII with a static string.
92
+ */
93
+ declare class ReplaceStrategy implements RedactionStrategy {
94
+ name: string;
95
+ private replacement;
96
+ /**
97
+ * @param replacement The string to replace the PII with. If not provided, it defaults to using the matcher name (e.g., [EMAIL]).
98
+ */
99
+ constructor(replacement?: string);
100
+ apply(_matchedValue: string, matcherName?: string): string;
101
+ }
102
+ /**
103
+ * Masks the matched PII by replacing most characters with a masking character,
104
+ * while leaving a few visible to provide context.
105
+ */
106
+ declare class MaskStrategy implements RedactionStrategy {
107
+ name: string;
108
+ private maskChar;
109
+ private unmaskedStart;
110
+ private unmaskedEnd;
111
+ /**
112
+ * @param options.maskChar The character to use for masking (default: '*')
113
+ * @param options.unmaskedStart Number of characters to leave unmasked at the start (default: 1)
114
+ * @param options.unmaskedEnd Number of characters to leave unmasked at the end (default: 1)
115
+ */
116
+ constructor(options?: {
117
+ maskChar?: string;
118
+ unmaskedStart?: number;
119
+ unmaskedEnd?: number;
120
+ });
121
+ apply(matchedValue: string): string;
122
+ }
123
+ /**
124
+ * Replaces the matched PII with a cryptographic hash.
125
+ * Useful for preserving uniqueness without revealing the actual value.
126
+ */
127
+ declare class HashStrategy implements RedactionStrategy {
128
+ name: string;
129
+ private algorithm;
130
+ /**
131
+ * @param algorithm The cryptographic hash algorithm to use (default: 'sha256')
132
+ */
133
+ constructor(algorithm?: string);
134
+ apply(matchedValue: string): string;
135
+ }
136
+
137
+ declare const DefaultMatchers: RegexMatcher[];
138
+
139
+ export { AddressMatcher, CreditCardMatcher, DateOfBirthMatcher, DefaultMatchers, DriverLicenseMatcher, EmailMatcher, HashStrategy, IPv4Matcher, LocationCoordinatesMatcher, MaskStrategy, type MatchResult, type Matcher, MentionMatcher, NlpMatcher, type NlpMatcherConfig, PassportMatcher, PhoneMatcher, type RedactionStrategy, Redactor, type RedactorConfig, RegexMatcher, ReplaceStrategy, SSNMatcher, ZipcodeMatcher };
@@ -0,0 +1,139 @@
1
+ interface Matcher {
2
+ /** Uniquely identifies the type of PII (e.g., 'email', 'phone') */
3
+ name: string;
4
+ /**
5
+ * Finds all instances of the PII in the given text.
6
+ * Returns an array of objects containing the matched string and its start/end indices.
7
+ */
8
+ match(text: string): MatchResult[];
9
+ }
10
+ interface MatchResult {
11
+ value: string;
12
+ start: number;
13
+ end: number;
14
+ /** Optional override for the strategy replacement name */
15
+ name?: string;
16
+ }
17
+ interface RedactionStrategy {
18
+ /** Uniquely identifies the strategy (e.g., 'replace', 'mask', 'hash') */
19
+ name: string;
20
+ /**
21
+ * Applies the strategy to a matched PII string.
22
+ * @param matchedValue The actual PII string found
23
+ * @param matcherName The type of PII (e.g., 'email')
24
+ */
25
+ apply(matchedValue: string, matcherName?: string): string;
26
+ }
27
+ interface RedactorConfig {
28
+ /** The built-in or custom matchers to enable */
29
+ matchers?: Matcher[];
30
+ /** The default fallback strategy if a specific matcher doesn't override it */
31
+ defaultStrategy?: RedactionStrategy;
32
+ }
33
+
34
+ interface NlpMatcherConfig {
35
+ detectPeople?: boolean;
36
+ detectOrganizations?: boolean;
37
+ detectLocations?: boolean;
38
+ }
39
+ /**
40
+ * A matcher that uses Natural Language Processing (via compromise)
41
+ * to detect named entities through sentence context rather than regex.
42
+ */
43
+ declare class NlpMatcher implements Matcher {
44
+ name: string;
45
+ private config;
46
+ constructor(config?: NlpMatcherConfig);
47
+ match(text: string): MatchResult[];
48
+ }
49
+
50
+ /**
51
+ * A base class for Regex-based matchers to avoid boilerplate.
52
+ */
53
+ declare class RegexMatcher implements Matcher {
54
+ name: string;
55
+ private regex;
56
+ constructor(name: string, regex: RegExp);
57
+ match(text: string): MatchResult[];
58
+ }
59
+ declare const EmailMatcher: RegexMatcher;
60
+ declare const PhoneMatcher: RegexMatcher;
61
+ declare const CreditCardMatcher: RegexMatcher;
62
+ declare const SSNMatcher: RegexMatcher;
63
+ declare const IPv4Matcher: RegexMatcher;
64
+ declare const ZipcodeMatcher: RegexMatcher;
65
+ declare const AddressMatcher: RegexMatcher;
66
+ declare const LocationCoordinatesMatcher: RegexMatcher;
67
+ declare const PassportMatcher: RegexMatcher;
68
+ declare const MentionMatcher: RegexMatcher;
69
+ declare const DriverLicenseMatcher: RegexMatcher;
70
+ declare const DateOfBirthMatcher: RegexMatcher;
71
+
72
+ declare class Redactor {
73
+ private matchers;
74
+ private defaultStrategy;
75
+ constructor(config?: RedactorConfig);
76
+ /**
77
+ * Redacts PII from an unstructured text string.
78
+ */
79
+ redact(text: string): string;
80
+ /**
81
+ * Recursively traverses an object or array and redacts any string values found.
82
+ */
83
+ redactObject<T>(obj: T): T;
84
+ /**
85
+ * Safely redacts PII from an HTML string without corrupting tags or attributes.
86
+ */
87
+ redactHtml(htmlString: string): string;
88
+ }
89
+
90
+ /**
91
+ * Replaces the matched PII with a static string.
92
+ */
93
+ declare class ReplaceStrategy implements RedactionStrategy {
94
+ name: string;
95
+ private replacement;
96
+ /**
97
+ * @param replacement The string to replace the PII with. If not provided, it defaults to using the matcher name (e.g., [EMAIL]).
98
+ */
99
+ constructor(replacement?: string);
100
+ apply(_matchedValue: string, matcherName?: string): string;
101
+ }
102
+ /**
103
+ * Masks the matched PII by replacing most characters with a masking character,
104
+ * while leaving a few visible to provide context.
105
+ */
106
+ declare class MaskStrategy implements RedactionStrategy {
107
+ name: string;
108
+ private maskChar;
109
+ private unmaskedStart;
110
+ private unmaskedEnd;
111
+ /**
112
+ * @param options.maskChar The character to use for masking (default: '*')
113
+ * @param options.unmaskedStart Number of characters to leave unmasked at the start (default: 1)
114
+ * @param options.unmaskedEnd Number of characters to leave unmasked at the end (default: 1)
115
+ */
116
+ constructor(options?: {
117
+ maskChar?: string;
118
+ unmaskedStart?: number;
119
+ unmaskedEnd?: number;
120
+ });
121
+ apply(matchedValue: string): string;
122
+ }
123
+ /**
124
+ * Replaces the matched PII with a cryptographic hash.
125
+ * Useful for preserving uniqueness without revealing the actual value.
126
+ */
127
+ declare class HashStrategy implements RedactionStrategy {
128
+ name: string;
129
+ private algorithm;
130
+ /**
131
+ * @param algorithm The cryptographic hash algorithm to use (default: 'sha256')
132
+ */
133
+ constructor(algorithm?: string);
134
+ apply(matchedValue: string): string;
135
+ }
136
+
137
+ declare const DefaultMatchers: RegexMatcher[];
138
+
139
+ export { AddressMatcher, CreditCardMatcher, DateOfBirthMatcher, DefaultMatchers, DriverLicenseMatcher, EmailMatcher, HashStrategy, IPv4Matcher, LocationCoordinatesMatcher, MaskStrategy, type MatchResult, type Matcher, MentionMatcher, NlpMatcher, type NlpMatcherConfig, PassportMatcher, PhoneMatcher, type RedactionStrategy, Redactor, type RedactorConfig, RegexMatcher, ReplaceStrategy, SSNMatcher, ZipcodeMatcher };
package/dist/index.js ADDED
@@ -0,0 +1,374 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AddressMatcher: () => AddressMatcher,
34
+ CreditCardMatcher: () => CreditCardMatcher,
35
+ DateOfBirthMatcher: () => DateOfBirthMatcher,
36
+ DefaultMatchers: () => DefaultMatchers,
37
+ DriverLicenseMatcher: () => DriverLicenseMatcher,
38
+ EmailMatcher: () => EmailMatcher,
39
+ HashStrategy: () => HashStrategy,
40
+ IPv4Matcher: () => IPv4Matcher,
41
+ LocationCoordinatesMatcher: () => LocationCoordinatesMatcher,
42
+ MaskStrategy: () => MaskStrategy,
43
+ MentionMatcher: () => MentionMatcher,
44
+ NlpMatcher: () => NlpMatcher,
45
+ PassportMatcher: () => PassportMatcher,
46
+ PhoneMatcher: () => PhoneMatcher,
47
+ Redactor: () => Redactor,
48
+ RegexMatcher: () => RegexMatcher,
49
+ ReplaceStrategy: () => ReplaceStrategy,
50
+ SSNMatcher: () => SSNMatcher,
51
+ ZipcodeMatcher: () => ZipcodeMatcher
52
+ });
53
+ module.exports = __toCommonJS(index_exports);
54
+
55
+ // src/strategies/index.ts
56
+ var import_crypto = __toESM(require("crypto"));
57
+ var ReplaceStrategy = class {
58
+ name = "replace";
59
+ replacement;
60
+ /**
61
+ * @param replacement The string to replace the PII with. If not provided, it defaults to using the matcher name (e.g., [EMAIL]).
62
+ */
63
+ constructor(replacement) {
64
+ this.replacement = replacement || "";
65
+ }
66
+ apply(_matchedValue, matcherName) {
67
+ if (this.replacement) {
68
+ return this.replacement;
69
+ }
70
+ return `[${(matcherName || "REDACTED").toUpperCase()}]`;
71
+ }
72
+ };
73
+ var MaskStrategy = class {
74
+ name = "mask";
75
+ maskChar;
76
+ unmaskedStart;
77
+ unmaskedEnd;
78
+ /**
79
+ * @param options.maskChar The character to use for masking (default: '*')
80
+ * @param options.unmaskedStart Number of characters to leave unmasked at the start (default: 1)
81
+ * @param options.unmaskedEnd Number of characters to leave unmasked at the end (default: 1)
82
+ */
83
+ constructor(options) {
84
+ this.maskChar = options?.maskChar || "*";
85
+ this.unmaskedStart = options?.unmaskedStart ?? 1;
86
+ this.unmaskedEnd = options?.unmaskedEnd ?? 1;
87
+ }
88
+ apply(matchedValue) {
89
+ const length = matchedValue.length;
90
+ if (length <= this.unmaskedStart + this.unmaskedEnd) {
91
+ return this.maskChar.repeat(length);
92
+ }
93
+ const start = matchedValue.slice(0, this.unmaskedStart);
94
+ const end = matchedValue.slice(length - this.unmaskedEnd);
95
+ const middle = this.maskChar.repeat(length - this.unmaskedStart - this.unmaskedEnd);
96
+ return `${start}${middle}${end}`;
97
+ }
98
+ };
99
+ var HashStrategy = class {
100
+ name = "hash";
101
+ algorithm;
102
+ /**
103
+ * @param algorithm The cryptographic hash algorithm to use (default: 'sha256')
104
+ */
105
+ constructor(algorithm = "sha256") {
106
+ this.algorithm = algorithm;
107
+ }
108
+ apply(matchedValue) {
109
+ return import_crypto.default.createHash(this.algorithm).update(matchedValue).digest("hex");
110
+ }
111
+ };
112
+
113
+ // src/redactor.ts
114
+ var import_node_html_parser = require("node-html-parser");
115
+ var Redactor = class {
116
+ matchers;
117
+ defaultStrategy;
118
+ constructor(config) {
119
+ this.matchers = config?.matchers || [];
120
+ this.defaultStrategy = config?.defaultStrategy || new ReplaceStrategy();
121
+ }
122
+ /**
123
+ * Redacts PII from an unstructured text string.
124
+ */
125
+ redact(text) {
126
+ if (!text || typeof text !== "string") return text;
127
+ const allMatches = [];
128
+ for (const matcher of this.matchers) {
129
+ const results = matcher.match(text);
130
+ for (const result of results) {
131
+ const replacement = this.defaultStrategy.apply(result.value, result.name || matcher.name);
132
+ allMatches.push({ start: result.start, end: result.end, replacement });
133
+ }
134
+ }
135
+ if (allMatches.length === 0) return text;
136
+ allMatches.sort((a, b) => a.start - b.start);
137
+ let resultText = text;
138
+ const validMatches = [];
139
+ let lastEnd = -1;
140
+ for (const match of allMatches) {
141
+ if (match.start >= lastEnd) {
142
+ validMatches.push(match);
143
+ lastEnd = match.end;
144
+ }
145
+ }
146
+ for (let i = validMatches.length - 1; i >= 0; i--) {
147
+ const { start, end, replacement } = validMatches[i];
148
+ resultText = resultText.substring(0, start) + replacement + resultText.substring(end);
149
+ }
150
+ return resultText;
151
+ }
152
+ /**
153
+ * Recursively traverses an object or array and redacts any string values found.
154
+ */
155
+ redactObject(obj) {
156
+ if (obj === null || obj === void 0) return obj;
157
+ if (typeof obj === "string") {
158
+ return this.redact(obj);
159
+ }
160
+ if (Array.isArray(obj)) {
161
+ return obj.map((item) => this.redactObject(item));
162
+ }
163
+ if (typeof obj === "object") {
164
+ const result = {};
165
+ for (const [key, value] of Object.entries(obj)) {
166
+ result[key] = this.redactObject(value);
167
+ }
168
+ return result;
169
+ }
170
+ return obj;
171
+ }
172
+ /**
173
+ * Safely redacts PII from an HTML string without corrupting tags or attributes.
174
+ */
175
+ redactHtml(htmlString) {
176
+ if (!htmlString || typeof htmlString !== "string") return htmlString;
177
+ const root = (0, import_node_html_parser.parse)(htmlString);
178
+ const traverse = (node) => {
179
+ if (node.nodeType === 3) {
180
+ const textNode = node;
181
+ if (textNode.rawText && textNode.rawText.trim()) {
182
+ textNode.rawText = this.redact(textNode.rawText);
183
+ }
184
+ } else if (node.nodeType === 1) {
185
+ const element = node;
186
+ if (element.tagName && ["SCRIPT", "STYLE"].includes(element.tagName.toUpperCase())) {
187
+ return;
188
+ }
189
+ for (const child of element.childNodes) {
190
+ traverse(child);
191
+ }
192
+ }
193
+ };
194
+ traverse(root);
195
+ return root.toString();
196
+ }
197
+ };
198
+
199
+ // src/matchers/nlp.ts
200
+ var import_compromise = __toESM(require("compromise"));
201
+ var NlpMatcher = class {
202
+ name;
203
+ config;
204
+ constructor(config) {
205
+ this.name = "nlp";
206
+ this.config = {
207
+ detectPeople: true,
208
+ detectOrganizations: true,
209
+ detectLocations: true,
210
+ ...config
211
+ };
212
+ }
213
+ match(text) {
214
+ const results = [];
215
+ const doc = (0, import_compromise.default)(text);
216
+ if (this.config.detectPeople) {
217
+ const people = doc.people().out("offset");
218
+ people.forEach((p) => {
219
+ results.push({
220
+ value: p.text,
221
+ // e.g. "John Smith"
222
+ name: "person",
223
+ // Override name for specific replacement (e.g. [PERSON])
224
+ start: p.offset.start,
225
+ end: p.offset.start + p.offset.length
226
+ });
227
+ });
228
+ }
229
+ if (this.config.detectOrganizations) {
230
+ const orgs = doc.organizations().out("offset");
231
+ orgs.forEach((o) => {
232
+ results.push({
233
+ value: o.text,
234
+ // e.g. "Google"
235
+ name: "org",
236
+ // Override name for specific replacement (e.g. [ORG])
237
+ start: o.offset.start,
238
+ end: o.offset.start + o.offset.length
239
+ });
240
+ });
241
+ }
242
+ if (this.config.detectLocations) {
243
+ const places = doc.places().out("offset");
244
+ places.forEach((p) => {
245
+ results.push({
246
+ value: p.text,
247
+ // e.g. "New York"
248
+ name: "location",
249
+ // Override name for specific replacement (e.g. [LOCATION])
250
+ start: p.offset.start,
251
+ end: p.offset.start + p.offset.length
252
+ });
253
+ });
254
+ }
255
+ return results;
256
+ }
257
+ };
258
+
259
+ // src/matchers/index.ts
260
+ var RegexMatcher = class {
261
+ name;
262
+ regex;
263
+ constructor(name, regex) {
264
+ this.name = name;
265
+ if (!regex.flags.includes("g")) {
266
+ this.regex = new RegExp(regex.source, regex.flags + "g");
267
+ } else {
268
+ this.regex = regex;
269
+ }
270
+ }
271
+ match(text) {
272
+ const results = [];
273
+ let match;
274
+ this.regex.lastIndex = 0;
275
+ while ((match = this.regex.exec(text)) !== null) {
276
+ results.push({
277
+ value: match[0],
278
+ start: match.index,
279
+ end: match.index + match[0].length
280
+ });
281
+ }
282
+ return results;
283
+ }
284
+ };
285
+ var EmailMatcher = new RegexMatcher(
286
+ "email",
287
+ /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
288
+ );
289
+ var PhoneMatcher = new RegexMatcher(
290
+ "phone",
291
+ /(?<!\d)(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}(?!\d)/g
292
+ );
293
+ var CreditCardMatcher = new RegexMatcher(
294
+ "credit_card",
295
+ /\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\d{3})\d{11})\b/g
296
+ );
297
+ var SSNMatcher = new RegexMatcher(
298
+ "ssn",
299
+ /\b(?!(000|666|9))\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b/g
300
+ );
301
+ var IPv4Matcher = new RegexMatcher(
302
+ "ipv4",
303
+ /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g
304
+ );
305
+ var ZipcodeMatcher = new RegexMatcher(
306
+ "zipcode",
307
+ /\b\d{5}(?:[-\s]\d{4})?\b/g
308
+ );
309
+ var AddressMatcher = new RegexMatcher(
310
+ "address",
311
+ /\b\d{1,5}\s(?:[a-zA-Z0-9\s.,-]{1,50})\s(?:St|Street|Rd|Road|Ave|Avenue|Blvd|Boulevard|Ln|Lane|Dr|Drive|Ct|Court)\b/gi
312
+ );
313
+ var LocationCoordinatesMatcher = new RegexMatcher(
314
+ "location",
315
+ /[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)/g
316
+ );
317
+ var PassportMatcher = new RegexMatcher(
318
+ "passport",
319
+ /\b(?:[A-Z]{1,2}\d{6,8}|\d{9})\b/g
320
+ );
321
+ var MentionMatcher = new RegexMatcher(
322
+ "mention",
323
+ /(?<=^|\s)@[a-zA-Z0-9_\-]+/g
324
+ );
325
+ var DriverLicenseMatcher = new RegexMatcher(
326
+ "driver_license",
327
+ // Varies heavily by state. Covers alphanumeric formats of 7-14 chars commonly seen in the US.
328
+ // Using positive lookahead (?=.*[0-9]) to ensure it has at least one number, preventing normal 7-14 letter words from matching.
329
+ /\b(?=.*[0-9])[A-Za-z0-9]{7,14}\b/g
330
+ );
331
+ var DateOfBirthMatcher = new RegexMatcher(
332
+ "date_of_birth",
333
+ // Matches common DOB formats: MM/DD/YYYY, DD/MM/YYYY, YYYY-MM-DD, MM-DD-YYYY, etc.
334
+ /\b(?:(?:0?[1-9]|1[0-2])[-/](?:0?[1-9]|[12][0-9]|3[01])[-/](?:19|20)\d{2}|(?:19|20)\d{2}[-/](?:0?[1-9]|1[0-2])[-/](?:0?[1-9]|[12][0-9]|3[01]))\b/g
335
+ );
336
+
337
+ // src/index.ts
338
+ var DefaultMatchers = [
339
+ EmailMatcher,
340
+ PhoneMatcher,
341
+ CreditCardMatcher,
342
+ SSNMatcher,
343
+ IPv4Matcher,
344
+ ZipcodeMatcher,
345
+ LocationCoordinatesMatcher,
346
+ PassportMatcher,
347
+ DriverLicenseMatcher,
348
+ MentionMatcher,
349
+ DateOfBirthMatcher,
350
+ // Address matcher is typically noisy without NLP, so it's included but should be used with caution
351
+ AddressMatcher
352
+ ];
353
+ // Annotate the CommonJS export names for ESM import in node:
354
+ 0 && (module.exports = {
355
+ AddressMatcher,
356
+ CreditCardMatcher,
357
+ DateOfBirthMatcher,
358
+ DefaultMatchers,
359
+ DriverLicenseMatcher,
360
+ EmailMatcher,
361
+ HashStrategy,
362
+ IPv4Matcher,
363
+ LocationCoordinatesMatcher,
364
+ MaskStrategy,
365
+ MentionMatcher,
366
+ NlpMatcher,
367
+ PassportMatcher,
368
+ PhoneMatcher,
369
+ Redactor,
370
+ RegexMatcher,
371
+ ReplaceStrategy,
372
+ SSNMatcher,
373
+ ZipcodeMatcher
374
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,319 @@
1
+ // src/strategies/index.ts
2
+ import crypto from "crypto";
3
+ var ReplaceStrategy = class {
4
+ name = "replace";
5
+ replacement;
6
+ /**
7
+ * @param replacement The string to replace the PII with. If not provided, it defaults to using the matcher name (e.g., [EMAIL]).
8
+ */
9
+ constructor(replacement) {
10
+ this.replacement = replacement || "";
11
+ }
12
+ apply(_matchedValue, matcherName) {
13
+ if (this.replacement) {
14
+ return this.replacement;
15
+ }
16
+ return `[${(matcherName || "REDACTED").toUpperCase()}]`;
17
+ }
18
+ };
19
+ var MaskStrategy = class {
20
+ name = "mask";
21
+ maskChar;
22
+ unmaskedStart;
23
+ unmaskedEnd;
24
+ /**
25
+ * @param options.maskChar The character to use for masking (default: '*')
26
+ * @param options.unmaskedStart Number of characters to leave unmasked at the start (default: 1)
27
+ * @param options.unmaskedEnd Number of characters to leave unmasked at the end (default: 1)
28
+ */
29
+ constructor(options) {
30
+ this.maskChar = options?.maskChar || "*";
31
+ this.unmaskedStart = options?.unmaskedStart ?? 1;
32
+ this.unmaskedEnd = options?.unmaskedEnd ?? 1;
33
+ }
34
+ apply(matchedValue) {
35
+ const length = matchedValue.length;
36
+ if (length <= this.unmaskedStart + this.unmaskedEnd) {
37
+ return this.maskChar.repeat(length);
38
+ }
39
+ const start = matchedValue.slice(0, this.unmaskedStart);
40
+ const end = matchedValue.slice(length - this.unmaskedEnd);
41
+ const middle = this.maskChar.repeat(length - this.unmaskedStart - this.unmaskedEnd);
42
+ return `${start}${middle}${end}`;
43
+ }
44
+ };
45
+ var HashStrategy = class {
46
+ name = "hash";
47
+ algorithm;
48
+ /**
49
+ * @param algorithm The cryptographic hash algorithm to use (default: 'sha256')
50
+ */
51
+ constructor(algorithm = "sha256") {
52
+ this.algorithm = algorithm;
53
+ }
54
+ apply(matchedValue) {
55
+ return crypto.createHash(this.algorithm).update(matchedValue).digest("hex");
56
+ }
57
+ };
58
+
59
+ // src/redactor.ts
60
+ import { parse } from "node-html-parser";
61
+ var Redactor = class {
62
+ matchers;
63
+ defaultStrategy;
64
+ constructor(config) {
65
+ this.matchers = config?.matchers || [];
66
+ this.defaultStrategy = config?.defaultStrategy || new ReplaceStrategy();
67
+ }
68
+ /**
69
+ * Redacts PII from an unstructured text string.
70
+ */
71
+ redact(text) {
72
+ if (!text || typeof text !== "string") return text;
73
+ const allMatches = [];
74
+ for (const matcher of this.matchers) {
75
+ const results = matcher.match(text);
76
+ for (const result of results) {
77
+ const replacement = this.defaultStrategy.apply(result.value, result.name || matcher.name);
78
+ allMatches.push({ start: result.start, end: result.end, replacement });
79
+ }
80
+ }
81
+ if (allMatches.length === 0) return text;
82
+ allMatches.sort((a, b) => a.start - b.start);
83
+ let resultText = text;
84
+ const validMatches = [];
85
+ let lastEnd = -1;
86
+ for (const match of allMatches) {
87
+ if (match.start >= lastEnd) {
88
+ validMatches.push(match);
89
+ lastEnd = match.end;
90
+ }
91
+ }
92
+ for (let i = validMatches.length - 1; i >= 0; i--) {
93
+ const { start, end, replacement } = validMatches[i];
94
+ resultText = resultText.substring(0, start) + replacement + resultText.substring(end);
95
+ }
96
+ return resultText;
97
+ }
98
+ /**
99
+ * Recursively traverses an object or array and redacts any string values found.
100
+ */
101
+ redactObject(obj) {
102
+ if (obj === null || obj === void 0) return obj;
103
+ if (typeof obj === "string") {
104
+ return this.redact(obj);
105
+ }
106
+ if (Array.isArray(obj)) {
107
+ return obj.map((item) => this.redactObject(item));
108
+ }
109
+ if (typeof obj === "object") {
110
+ const result = {};
111
+ for (const [key, value] of Object.entries(obj)) {
112
+ result[key] = this.redactObject(value);
113
+ }
114
+ return result;
115
+ }
116
+ return obj;
117
+ }
118
+ /**
119
+ * Safely redacts PII from an HTML string without corrupting tags or attributes.
120
+ */
121
+ redactHtml(htmlString) {
122
+ if (!htmlString || typeof htmlString !== "string") return htmlString;
123
+ const root = parse(htmlString);
124
+ const traverse = (node) => {
125
+ if (node.nodeType === 3) {
126
+ const textNode = node;
127
+ if (textNode.rawText && textNode.rawText.trim()) {
128
+ textNode.rawText = this.redact(textNode.rawText);
129
+ }
130
+ } else if (node.nodeType === 1) {
131
+ const element = node;
132
+ if (element.tagName && ["SCRIPT", "STYLE"].includes(element.tagName.toUpperCase())) {
133
+ return;
134
+ }
135
+ for (const child of element.childNodes) {
136
+ traverse(child);
137
+ }
138
+ }
139
+ };
140
+ traverse(root);
141
+ return root.toString();
142
+ }
143
+ };
144
+
145
+ // src/matchers/nlp.ts
146
+ import nlp from "compromise";
147
+ var NlpMatcher = class {
148
+ name;
149
+ config;
150
+ constructor(config) {
151
+ this.name = "nlp";
152
+ this.config = {
153
+ detectPeople: true,
154
+ detectOrganizations: true,
155
+ detectLocations: true,
156
+ ...config
157
+ };
158
+ }
159
+ match(text) {
160
+ const results = [];
161
+ const doc = nlp(text);
162
+ if (this.config.detectPeople) {
163
+ const people = doc.people().out("offset");
164
+ people.forEach((p) => {
165
+ results.push({
166
+ value: p.text,
167
+ // e.g. "John Smith"
168
+ name: "person",
169
+ // Override name for specific replacement (e.g. [PERSON])
170
+ start: p.offset.start,
171
+ end: p.offset.start + p.offset.length
172
+ });
173
+ });
174
+ }
175
+ if (this.config.detectOrganizations) {
176
+ const orgs = doc.organizations().out("offset");
177
+ orgs.forEach((o) => {
178
+ results.push({
179
+ value: o.text,
180
+ // e.g. "Google"
181
+ name: "org",
182
+ // Override name for specific replacement (e.g. [ORG])
183
+ start: o.offset.start,
184
+ end: o.offset.start + o.offset.length
185
+ });
186
+ });
187
+ }
188
+ if (this.config.detectLocations) {
189
+ const places = doc.places().out("offset");
190
+ places.forEach((p) => {
191
+ results.push({
192
+ value: p.text,
193
+ // e.g. "New York"
194
+ name: "location",
195
+ // Override name for specific replacement (e.g. [LOCATION])
196
+ start: p.offset.start,
197
+ end: p.offset.start + p.offset.length
198
+ });
199
+ });
200
+ }
201
+ return results;
202
+ }
203
+ };
204
+
205
+ // src/matchers/index.ts
206
+ var RegexMatcher = class {
207
+ name;
208
+ regex;
209
+ constructor(name, regex) {
210
+ this.name = name;
211
+ if (!regex.flags.includes("g")) {
212
+ this.regex = new RegExp(regex.source, regex.flags + "g");
213
+ } else {
214
+ this.regex = regex;
215
+ }
216
+ }
217
+ match(text) {
218
+ const results = [];
219
+ let match;
220
+ this.regex.lastIndex = 0;
221
+ while ((match = this.regex.exec(text)) !== null) {
222
+ results.push({
223
+ value: match[0],
224
+ start: match.index,
225
+ end: match.index + match[0].length
226
+ });
227
+ }
228
+ return results;
229
+ }
230
+ };
231
+ var EmailMatcher = new RegexMatcher(
232
+ "email",
233
+ /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g
234
+ );
235
+ var PhoneMatcher = new RegexMatcher(
236
+ "phone",
237
+ /(?<!\d)(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}(?!\d)/g
238
+ );
239
+ var CreditCardMatcher = new RegexMatcher(
240
+ "credit_card",
241
+ /\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\d{3})\d{11})\b/g
242
+ );
243
+ var SSNMatcher = new RegexMatcher(
244
+ "ssn",
245
+ /\b(?!(000|666|9))\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b/g
246
+ );
247
+ var IPv4Matcher = new RegexMatcher(
248
+ "ipv4",
249
+ /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g
250
+ );
251
+ var ZipcodeMatcher = new RegexMatcher(
252
+ "zipcode",
253
+ /\b\d{5}(?:[-\s]\d{4})?\b/g
254
+ );
255
+ var AddressMatcher = new RegexMatcher(
256
+ "address",
257
+ /\b\d{1,5}\s(?:[a-zA-Z0-9\s.,-]{1,50})\s(?:St|Street|Rd|Road|Ave|Avenue|Blvd|Boulevard|Ln|Lane|Dr|Drive|Ct|Court)\b/gi
258
+ );
259
+ var LocationCoordinatesMatcher = new RegexMatcher(
260
+ "location",
261
+ /[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)/g
262
+ );
263
+ var PassportMatcher = new RegexMatcher(
264
+ "passport",
265
+ /\b(?:[A-Z]{1,2}\d{6,8}|\d{9})\b/g
266
+ );
267
+ var MentionMatcher = new RegexMatcher(
268
+ "mention",
269
+ /(?<=^|\s)@[a-zA-Z0-9_\-]+/g
270
+ );
271
+ var DriverLicenseMatcher = new RegexMatcher(
272
+ "driver_license",
273
+ // Varies heavily by state. Covers alphanumeric formats of 7-14 chars commonly seen in the US.
274
+ // Using positive lookahead (?=.*[0-9]) to ensure it has at least one number, preventing normal 7-14 letter words from matching.
275
+ /\b(?=.*[0-9])[A-Za-z0-9]{7,14}\b/g
276
+ );
277
+ var DateOfBirthMatcher = new RegexMatcher(
278
+ "date_of_birth",
279
+ // Matches common DOB formats: MM/DD/YYYY, DD/MM/YYYY, YYYY-MM-DD, MM-DD-YYYY, etc.
280
+ /\b(?:(?:0?[1-9]|1[0-2])[-/](?:0?[1-9]|[12][0-9]|3[01])[-/](?:19|20)\d{2}|(?:19|20)\d{2}[-/](?:0?[1-9]|1[0-2])[-/](?:0?[1-9]|[12][0-9]|3[01]))\b/g
281
+ );
282
+
283
+ // src/index.ts
284
+ var DefaultMatchers = [
285
+ EmailMatcher,
286
+ PhoneMatcher,
287
+ CreditCardMatcher,
288
+ SSNMatcher,
289
+ IPv4Matcher,
290
+ ZipcodeMatcher,
291
+ LocationCoordinatesMatcher,
292
+ PassportMatcher,
293
+ DriverLicenseMatcher,
294
+ MentionMatcher,
295
+ DateOfBirthMatcher,
296
+ // Address matcher is typically noisy without NLP, so it's included but should be used with caution
297
+ AddressMatcher
298
+ ];
299
+ export {
300
+ AddressMatcher,
301
+ CreditCardMatcher,
302
+ DateOfBirthMatcher,
303
+ DefaultMatchers,
304
+ DriverLicenseMatcher,
305
+ EmailMatcher,
306
+ HashStrategy,
307
+ IPv4Matcher,
308
+ LocationCoordinatesMatcher,
309
+ MaskStrategy,
310
+ MentionMatcher,
311
+ NlpMatcher,
312
+ PassportMatcher,
313
+ PhoneMatcher,
314
+ Redactor,
315
+ RegexMatcher,
316
+ ReplaceStrategy,
317
+ SSNMatcher,
318
+ ZipcodeMatcher
319
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "pii-redact",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight and extensible library for identifying and redacting Personally Identifiable Information (PII) from structured and unstructured text.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
20
+ "test": "jest",
21
+ "test:watch": "jest --watch",
22
+ "lint": "eslint src/ --ext .ts",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/neenakrishnan1501-bit/pii-redact.git"
28
+ },
29
+ "keywords": [],
30
+ "author": "",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/neenakrishnan1501-bit/pii-redact/issues"
34
+ },
35
+ "homepage": "https://github.com/neenakrishnan1501-bit/pii-redact#readme",
36
+ "devDependencies": {
37
+ "@types/jest": "^30.0.0",
38
+ "@types/node": "^25.3.5",
39
+ "jest": "^30.2.0",
40
+ "rimraf": "^6.1.3",
41
+ "ts-jest": "^29.4.6",
42
+ "tsup": "^8.5.1",
43
+ "typescript": "^5.9.3"
44
+ },
45
+ "dependencies": {
46
+ "compromise": "^14.15.0",
47
+ "node-html-parser": "^7.1.0"
48
+ }
49
+ }