mmpSearch/node_modules/@pinojs/redact
JotaChina 07562989cf
Deploy / Deploy (push) Successful in 1m16s Details
sync mode on
2025-10-26 16:43:01 -03:00
..
.github sync mode on 2025-10-26 16:43:01 -03:00
benchmarks sync mode on 2025-10-26 16:43:01 -03:00
scripts sync mode on 2025-10-26 16:43:01 -03:00
test sync mode on 2025-10-26 16:43:01 -03:00
LICENSE sync mode on 2025-10-26 16:43:01 -03:00
README.md sync mode on 2025-10-26 16:43:01 -03:00
eslint.config.js sync mode on 2025-10-26 16:43:01 -03:00
index.d.ts sync mode on 2025-10-26 16:43:01 -03:00
index.js sync mode on 2025-10-26 16:43:01 -03:00
index.test-d.ts sync mode on 2025-10-26 16:43:01 -03:00
package.json sync mode on 2025-10-26 16:43:01 -03:00
tsconfig.json sync mode on 2025-10-26 16:43:01 -03:00

README.md

@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/redact

Usage

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:

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 redacted

Wildcard 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: 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:

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)

// 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)

// 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

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:

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

# 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.