Skip to main content
Glama
README.md11.8 kB
# @pinojs/redact > Smart object redaction for JavaScript applications - safe AND fast! Redact JS objects with the same API as [fast-redact](https://github.com/davidmarkclements/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 ```bash npm install @pinojs/redact ``` ## Usage ```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' ``` ## 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 to `false` to 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) #### Examples **Custom censor value:** ```js const redact = slowRedact({ paths: ['password'], censor: '***HIDDEN***' }) ``` **Dynamic censor function:** ```js const redact = slowRedact({ paths: ['password'], censor: (value, path) => `REDACTED:${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 }) ``` ## 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: 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 ### 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. 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 ### 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: ```bash npm run bench ``` ## How Selective Cloning Works @pinojs/redact uses an innovative **selective cloning** approach that provides immutability guarantees while dramatically improving performance: ### Traditional Approach (before optimization) ```js // Old approach: Deep clone entire object, then redact const fullClone = deepClone(originalObject) // Clone everything redact(fullClone, paths) // Then redact specific paths ``` ### Selective Cloning Approach (current) ```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 ``` ### Key Innovations 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 ### Example: Reference Sharing in Action ```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"}]} ``` ### 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.stringify` serializer (like fast-redact) ## Testing ```bash # 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 bench ``` ### Test 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.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/krtw00/search-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server