maskit-pii
v1.0.3
Published
Zero-dependency TypeScript library for masking and redacting sensitive data
Downloads
481
Maintainers
Readme
maskit-pii
Zero-dependency TypeScript library for masking and redacting sensitive data in logs, API payloads, chat messages, and support tickets.
GitHub: github.com/swissjake/maskit-pii
Features
- Zero runtime dependencies — small bundle, no supply-chain risk
- Tree-shakeable — import only what you need (
sideEffects: false) - Framework agnostic — works in Node, browsers, and edge runtimes
- TypeScript — full type definitions included
- ESM + CJS —
importorrequire
Install
npm install maskit-piiWhen to use what
| You have… | Use |
| --------- | --- |
| A log line, chat message, or ticket with embedded emails/phones | redactText() |
| A JSON object or API payload with known sensitive keys | redactObject() |
| A single value you already identified (email, card, API key) | maskEmail(), maskCard(), etc. |
| Any string with custom visibility rules | mask() |
Quick start
import {
redactText,
redactObject,
mask,
maskEmail,
maskPhone,
maskCard,
maskApiKey,
} from 'maskit-pii';
redactText('Contact [email protected] or +447123456789');
// → 'Contact ja**@gmail.com or +44********89'
redactObject({ user: 'alice', password: 'hunter2' });
// → { user: 'alice', password: '[REDACTED]' }
mask('1234567890'); // '12******90'
maskEmail('[email protected]'); // 'ja*****[email protected]'
maskPhone('+15551234567'); // '+1********67'
maskCard('4111111111111111'); // '************1111'Examples
Logging
import { redactText } from 'maskit-pii';
const message = 'User [email protected] failed login from +447123456789';
logger.info(redactText(message));
// User ja**@gmail.com failed login from +44********89API request logging
import { redactObject } from 'maskit-pii';
app.post('/login', (req, res) => {
logger.info('Login attempt', redactObject(req.body));
// password, token, etc. are replaced before hitting logs
});Masking individual fields
import { maskCard, maskApiKey } from 'maskit-pii';
const safeUser = {
name: user.name,
card: maskCard(user.cardNumber),
apiKey: maskApiKey(user.apiKey),
};API reference
mask(value, options?)
The core function. Keeps start code points at the beginning and end at the end, replaces the rest with a mask character.
mask('1234567890'); // '12******90'
mask('hello', { start: 1, end: 1, char: '#' }); // 'h###o'
mask('ab', { trim: false }); // '**' (short input)null and undefined are returned unchanged.
maskEmail(email, options?)
Masks the local part (before @). The domain is always preserved.
maskEmail('[email protected]'); // 'ja*****[email protected]'
maskEmail('[email protected]'); // '*@b.com'Falls back to mask() when no @ is found. Accepts MaskOptions overrides.
maskPhone(phone, options?)
Masks the full string. Defaults: start: 2, end: 2, trim: false.
maskPhone('+15551234567'); // '+1********67'
maskPhone('(555) 123-4567'); // '(5**********67'maskCard(card, options?)
Shows the last four characters. Defaults: start: 0, end: 4, trim: false.
maskCard('4111111111111111'); // '************1111'
maskCard('378282246310005'); // '***********0005'maskApiKey(key, options?)
Shows the first and last four characters. Defaults: start: 4, end: 4, trim: false.
maskApiKey('sk_live_AbCdEfGhIjKlMnOpQrStUv');
// 'sk_l**********************StUv'redactText(text, options?)
Scans free text and masks embedded emails and phone numbers in place. Best for unstructured content: logs, chat, support tickets, audit trails.
redactText('Email [email protected] or call +447123456789');
// → 'Email ja**@gmail.com or call +44********89'
redactText('callback (555) 123-4567');
// → 'callback (5**********67'How matches are masked
| Pattern | Behavior |
| ------- | -------- |
| Email | First 2 chars of local part + **, domain kept |
| + phone | Country prefix (3 chars) + mask + last 2 digits |
| US phone | Uses maskPhone defaults |
Options
| Option | Default | Description |
| ------ | ------- | ----------- |
| email | true | Mask email addresses |
| phone | true | Mask phone numbers |
redactText(text, { email: false }); // phones only
redactText(text, { phone: false }); // emails onlyredactObject(value, options?)
Returns a shallow-safe copy with sensitive keys replaced. Walks nested objects and arrays. Non-plain values (Date, functions, etc.) are left untouched.
redactObject({
user: 'alice',
password: 'hunter2',
meta: { token: 'secret', refreshToken: 'rt' },
});
// → {
// user: 'alice',
// password: '[REDACTED]',
// meta: { token: '[REDACTED]', refreshToken: '[REDACTED]' },
// }Options
| Option | Default | Description |
| ------ | ------- | ----------- |
| keys | see below | Keys to redact (case-insensitive) |
| replacement | '[REDACTED]' | Substituted value |
| maxDepth | 16 | Max nesting depth |
redactObject(payload, {
keys: ['internalId', 'note'],
replacement: '***',
maxDepth: 8,
});Default redacted keys
password, passwd, pwd, secret, token, authorization, apikey, api_key, accesstoken, refreshtoken, cookie, set-cookie, ssn, creditcard, cardnumber, cvv, pin
MaskOptions
Shared by mask() and all mask* presets.
| Option | Default | Description |
| ------ | ------- | ----------- |
| start | 2 | Code points to keep at the start |
| end | 2 | Code points to keep at the end |
| char | '*' | Mask character |
| trim | true | Return short inputs unchanged instead of masking |
Presets set their own defaults but any option can be overridden:
maskEmail('[email protected]', { start: 1, end: 0, trim: false });
maskCard('4111111111111111', { end: 2 }); // show last 2 onlyTree-shaking
Import only what you need — unused exports are dropped by your bundler:
import { redactText } from 'maskit-pii';License
MIT
