Package Exports
- @pinojs/redact
- @pinojs/redact/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@pinojs/redact) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@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.
Install
npm install @pinojs/redactUsage
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'API
slowRedact(options) → Function
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 object - censor
any(optional, default:'[REDACTED]'): The value to replace sensitive data with. Can be a static value or function. - serialize
Function|boolean(optional, default:JSON.stringify): Serialization function. Set tofalseto return the redacted object. - remove
boolean(optional, default:false): Remove redacted keys from serialized output - strict
boolean(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' - Bracket notation:
'user["password"]','headers["X-Forwarded-For"]' - Array indices:
'users[0].password','items[1].secret' - Wildcards:
- Terminal:
'users.*.password'(redacts password for all users) - Intermediate:
'*.password'(redacts password at any level) - Array wildcard:
'items.*'(redacts all array elements)
- Terminal:
Examples
Custom censor value:
const redact = slowRedact({
paths: ['password'],
censor: '***HIDDEN***'
})Dynamic censor function:
const redact = slowRedact({
paths: ['password'],
censor: (value, path) => `REDACTED:${path}`
})Return object instead of JSON string:
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:
const redact = slowRedact({
paths: ['password'],
serialize: obj => JSON.stringify(obj, null, 2)
})Remove keys instead of redacting:
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 redactedWildcard patterns:
// 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 })Key Differences from fast-redact
Safety First
- No mutation: Original objects are never modified
- Selective cloning: Only clones paths that need redaction, shares references for everything else
- Restore capability: Can restore original values when
serialize: false
Feature Compatibility
- Remove option: Full compatibility with fast-redact's
remove: trueoption 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: falsemode
Smart Performance Approach
- 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
When to Use @pinojs/redact
- 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
When to Use fast-redact
- 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 matters
Performance Benchmarks
@pinojs/redact uses selective cloning that provides good performance while maintaining immutability guarantees:
Performance Results
| 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 |
Performance Improvements
@pinojs/redact is performance-competitive with fast-redact for large objects.
- Selective cloning approach: Only clones object paths that need redaction
- Reference sharing: Non-redacted properties share original object references
- Setup-time optimization: Path analysis happens once, not per redaction
- Memory efficiency: Dramatically reduced memory usage for typical use cases
Benchmark Details
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
When Performance Matters
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:
npm run benchHow Selective Cloning Works
@pinojs/redact uses an innovative selective cloning approach that provides immutability guarantees while dramatically improving performance:
Traditional Approach (before optimization)
// Old approach: Deep clone entire object, then redact
const fullClone = deepClone(originalObject) // Clone everything
redact(fullClone, paths) // Then redact specific pathsSelective Cloning Approach (current)
// 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 pathsKey Innovations
- Path Analysis: Pre-processes redaction paths into an efficient tree structure
- Selective Cloning: Only creates new objects for branches that contain redaction targets
- Reference Sharing: Non-redacted properties maintain exact same object references
- Setup Optimization: Path parsing happens once during redactor creation, not per redaction
Example: Reference Sharing in Action
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 redactionThis 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:
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"}]}Remove vs Redact Behavior
| Option | Behavior | Output Example |
|---|---|---|
| Default (redact) | Replaces values with censor | {"password":"[REDACTED]"} |
remove: true |
Completely omits keys | {} |
Compatibility Notes
- 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.stringifyserializer (like fast-redact)
Testing
# Run unit tests
npm test
# Run integration tests comparing with fast-redact
npm run test:integration
# Run all tests (unit + integration)
npm run test:all
# Run benchmarks
npm run benchTest Coverage
- 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
License
MIT
Contributing
Pull requests welcome! Please ensure all tests pass and add tests for new features.