Redact JS objects
npm install @pinojs/redact> Smart object redaction for JavaScript applications - safe AND fast!
Redact JS objects with the same API as fast-redact, but uses innovative selective cloning instead of mutating the original. This provides immutability guarantees with performance competitive to fast-redact for real-world usage patterns.
``bash`
npm install @pinojs/redact
`js
const slowRedact = require('@pinojs/redact')
const redact = slowRedact({
paths: ['headers.cookie', 'headers.authorization', 'user.password']
})
const obj = {
headers: {
cookie: 'secret-session-token',
authorization: 'Bearer abc123',
'x-forwarded-for': '192.168.1.1'
},
user: {
name: 'john',
password: 'secret123'
}
}
console.log(redact(obj))
// Output: {"headers":{"cookie":"[REDACTED]","authorization":"[REDACTED]","x-forwarded-for":"192.168.1.1"},"user":{"name":"john","password":"[REDACTED]"}}
// Original object is completely unchanged:
console.log(obj.headers.cookie) // 'secret-session-token'
`
Creates a redaction function with the specified options.
#### Options
- paths string[] (required): An array of strings describing the nested location of a key in an objectany
- censor (optional, default: '[REDACTED]'): The value to replace sensitive data with. Can be a static value or function.Function|boolean
- serialize (optional, default: JSON.stringify): Serialization function. Set to false to return the redacted object.boolean
- remove (optional, default: false): Remove redacted keys from serialized outputboolean
- strict (optional, default: true): Throw on non-object values or pass through primitives
#### Path Syntax
Supports the same path syntax as fast-redact:
- Dot notation: 'user.name', 'headers.cookie''user["password"]'
- Bracket notation: , 'headers["X-Forwarded-For"]''users[0].password'
- Array indices: , 'items[1].secret''users.*.password'
- Wildcards:
- Terminal: (redacts password for all users)'*.password'
- Intermediate: (redacts password at any level)'items.*'
- Array wildcard: (redacts all array elements)
#### Examples
Custom censor value:
`js`
const redact = slowRedact({
paths: ['password'],
censor: 'HIDDEN'
})
Dynamic censor function:
`jsREDACTED:${path}
const redact = slowRedact({
paths: ['password'],
censor: (value, path) => `
})
Return object instead of JSON string:
`js
const redact = slowRedact({
paths: ['secret'],
serialize: false
})
const result = redact({ secret: 'hidden', public: 'data' })
console.log(result.secret) // '[REDACTED]'
console.log(result.public) // 'data'
// Restore original values
const restored = result.restore()
console.log(restored.secret) // 'hidden'
`
Custom serialization:
`js`
const redact = slowRedact({
paths: ['password'],
serialize: obj => JSON.stringify(obj, null, 2)
})
Remove keys instead of redacting:
`js
const redact = slowRedact({
paths: ['password', 'user.secret'],
remove: true
})
const obj = { username: 'john', password: 'secret123', user: { name: 'Jane', secret: 'hidden' } }
console.log(redact(obj))
// Output: {"username":"john","user":{"name":"Jane"}}
// Note: 'password' and 'user.secret' are completely absent, not redacted
`
Wildcard patterns:
`js
// Redact all properties in secrets object
const redact1 = slowRedact({ paths: ['secrets.*'] })
// Redact password for any user
const redact2 = slowRedact({ paths: ['users.*.password'] })
// Redact all items in an array
const redact3 = slowRedact({ paths: ['items.*'] })
// Remove all secrets instead of redacting them
const redact4 = slowRedact({ paths: ['secrets.*'], remove: true })
`
$3
- Remove option: Full compatibility with fast-redact's remove: true option to completely omit keys from output
- All path patterns: Supports same syntax including wildcards, bracket notation, and array indices
- Censor functions: Dynamic censoring with path information passed as arrays
- Serialization: Custom serializers and serialize: false mode$3
- Selective cloning: Analyzes redaction paths and only clones necessary object branches
- Reference sharing: Non-redacted properties maintain original object references
- Memory efficiency: Dramatically reduced memory usage for large objects with minimal redaction
- Setup-time optimization: Path analysis happens once during setup, not per redaction$3
- When immutability is critical
- When you need to preserve original objects
- When objects are shared across multiple contexts
- In functional programming environments
- When debugging and you need to compare before/after
- Large objects with selective redaction (now performance-competitive!)
- When memory efficiency with reference sharing is important$3
- When absolute maximum performance is critical
- In extremely high-throughput scenarios (>100,000 ops/sec)
- When you control the object lifecycle and mutation is acceptable
- Very small objects where setup overhead mattersPerformance Benchmarks
@pinojs/redact uses selective cloning that provides good performance while maintaining immutability guarantees:
$3
| Operation Type | @pinojs/redact | fast-redact | Performance Ratio |
|---------------|-------------|-------------|-------------------|
| Small objects | ~690ns | ~200ns | ~3.5x slower |
| Large objects (minimal redaction) | ~18μs | ~17μs | ~same performance |
| Large objects (wildcards) | ~48μs | ~37μs | ~1.3x slower |
| No redaction (large objects) | ~18μs | ~17μs | ~same performance |
$3
@pinojs/redact is performance-competitive with fast-redact for large objects.
1. Selective cloning approach: Only clones object paths that need redaction
2. Reference sharing: Non-redacted properties share original object references
3. Setup-time optimization: Path analysis happens once, not per redaction
4. Memory efficiency: Dramatically reduced memory usage for typical use cases
$3
Small Objects (~180 bytes):
- @pinojs/redact: 690ns per operation
- fast-redact: 200ns per operation
- Slight setup overhead for small objects
Large Objects (~18KB, minimal redaction):
- @pinojs/redact: 18μs per operation
- fast-redact: 17μs per operation
- Near-identical performance
Large Objects (~18KB, wildcard patterns):
- @pinojs/redact: 48μs per operation
- fast-redact: 37μs per operation
- Competitive performance for complex patterns
Memory Considerations:
- @pinojs/redact: Selective reference sharing (much lower memory usage than before)
- fast-redact: Mutates in-place (lowest memory usage)
- Large objects with few redacted paths now share most references
$3
Choose fast-redact when:
- Absolute maximum performance is critical (>100,000 ops/sec)
- Working with very small objects frequently
- Mutation is acceptable and controlled
- Every microsecond counts
Choose @pinojs/redact when:
- Immutability is required (with competitive performance)
- Objects are shared across contexts
- Large objects with selective redaction
- Memory efficiency through reference sharing is important
- Safety and functionality are priorities
- Most production applications (performance gap is minimal)
Run benchmarks yourself:
`bash
npm run bench
`How Selective Cloning Works
@pinojs/redact uses an innovative selective cloning approach that provides immutability guarantees while dramatically improving performance:
$3
`js
// Old approach: Deep clone entire object, then redact
const fullClone = deepClone(originalObject) // Clone everything
redact(fullClone, paths) // Then redact specific paths
`$3
`js
// New approach: Analyze paths, clone only what's needed
const pathStructure = buildPathStructure(paths) // One-time setup
const selectiveClone = cloneOnlyNeededPaths(obj, pathStructure) // Smart cloning
redact(selectiveClone, paths) // Redact pre-identified paths
`$3
1. Path Analysis: Pre-processes redaction paths into an efficient tree structure
2. Selective Cloning: Only creates new objects for branches that contain redaction targets
3. Reference Sharing: Non-redacted properties maintain exact same object references
4. Setup Optimization: Path parsing happens once during redactor creation, not per redaction
$3
`js
const largeConfig = {
database: { / large config object / },
api: { / another large config / },
secrets: { password: 'hidden', apiKey: 'secret' }
}const redact = slowRedact({ paths: ['secrets.password'] })
const result = redact(largeConfig)
// Only secrets object is cloned, database and api share original references
console.log(result.database === largeConfig.database) // true - shared reference!
console.log(result.api === largeConfig.api) // true - shared reference!
console.log(result.secrets === largeConfig.secrets) // false - cloned for redaction
`This approach provides immutability where it matters while sharing references where it's safe.
Remove Option
The
remove: true option provides full compatibility with fast-redact's key removal functionality:`js
const redact = slowRedact({
paths: ['password', 'secrets.', 'users..credentials'],
remove: true
})const data = {
username: 'john',
password: 'secret123',
secrets: { apiKey: 'abc', token: 'xyz' },
users: [
{ name: 'Alice', credentials: { password: 'pass1' } },
{ name: 'Bob', credentials: { password: 'pass2' } }
]
}
console.log(redact(data))
// Output: {"username":"john","secrets":{},"users":[{"name":"Alice"},{"name":"Bob"}]}
`$3
| Option | Behavior | Output Example |
|--------|----------|----------------|
| Default (redact) | Replaces values with censor |
{"password":"[REDACTED]"} |
| remove: true | Completely omits keys | {} |$3
- Same output as fast-redact: Identical JSON output when using
remove: true
- Wildcard support: Works with all wildcard patterns (, users., items.*.secret)
- Array handling: Array items are set to undefined (omitted in JSON output)
- Nested paths: Supports deep removal (users.*.credentials.password)
- Serialize compatibility: Only works with JSON.stringify serializer (like fast-redact)Testing
`bash
Run unit tests
npm testRun integration tests comparing with fast-redact
npm run test:integrationRun all tests (unit + integration)
npm run test:allRun benchmarks
npm run bench
``- 16 unit tests: Core functionality and edge cases
- 16 integration tests: Output compatibility with fast-redact
- All major features: Paths, wildcards, serialization, custom censors
- Performance benchmarks: Direct comparison with fast-redact
MIT
Pull requests welcome! Please ensure all tests pass and add tests for new features.