@ -31,6 +31,44 @@ export function nullifyValuesInObjects<T>(aObjects: T[], keys: string[]): T[] {
} ) ;
} ) ;
}
}
// Helper: Custom fast clone (faster than structuredClone for our use case)
function fastClone ( obj : any , seen = new WeakMap ( ) ) : any {
// Handle primitives and null
if ( obj === null || typeof obj !== 'object' ) {
return obj ;
}
// Don't clone Big.js instances
if ( obj instanceof Big ) {
return obj ;
}
// Handle circular references
if ( seen . has ( obj ) ) {
return seen . get ( obj ) ;
}
// Handle arrays
if ( isArray ( obj ) ) {
const arr : any [ ] = [ ] ;
seen . set ( obj , arr ) ;
for ( let i = 0 ; i < obj . length ; i ++ ) {
arr [ i ] = fastClone ( obj [ i ] , seen ) ;
}
return arr ;
}
// Handle objects
const cloned : any = { } ;
seen . set ( obj , cloned ) ;
const keys = Object . keys ( obj ) ;
for ( let i = 0 ; i < keys . length ; i ++ ) {
const key = keys [ i ] ;
cloned [ key ] = fastClone ( obj [ key ] , seen ) ;
}
return cloned ;
}
export function redactAttributes ( {
export function redactAttributes ( {
isFirstRun = true ,
isFirstRun = true ,
object ,
object ,
@ -44,44 +82,75 @@ export function redactAttributes({
return object ;
return object ;
}
}
// Create deep clone
// Optimization 1: Use custom fast clone instead of structuredClone
const redactedObject = isFirstRun
const redactedObject = isFirstRun ? fastClone ( object ) : object ;
? JSON . parse ( JSON . stringify ( object ) )
: object ;
// Optimization 2: Pre-process options and separate wildcards from conditional mappings
const wildcardAttrs = new Map < string , any > ( ) ;
for ( const option of options ) {
const conditionalAttrs = new Map < string , { [ key : string ] : any } > ( ) ;
if ( redactedObject . hasOwnProperty ( option . attribute ) ) {
if ( option . valueMap [ '*' ] || option . valueMap [ '*' ] === null ) {
for ( const opt of options ) {
redactedObject [ option . attribute ] = option . valueMap [ '*' ] ;
if ( '*' in opt . valueMap ) {
} else if ( option . valueMap [ redactedObject [ option . attribute ] ] ) {
wildcardAttrs . set ( opt . attribute , opt . valueMap [ '*' ] ) ;
redactedObject [ option . attribute ] =
option . valueMap [ redactedObject [ option . attribute ] ] ;
}
} else {
} else {
// If the attribute is not present on the current object,
conditionalAttrs . set ( opt . attribute , opt . valueMap ) ;
// check if it exists on any nested objects
}
for ( const property in redactedObject ) {
}
if ( isArray ( redactedObject [ property ] ) ) {
redactedObject [ property ] = redactedObject [ property ] . map (
// Optimization 3: Use iterative traversal with pointer-based queue
( currentObject ) = > {
const workQueue : any [ ] = [ redactedObject ] ;
return redactAttributes ( {
let queueIndex = 0 ;
options ,
isFirstRun : false ,
while ( queueIndex < workQueue . length ) {
object : currentObject
const current = workQueue [ queueIndex ++ ] ;
} ) ;
// Skip null/undefined
if ( current == null ) {
continue ;
}
// Process wildcard attributes first (most common case)
for ( const [ attribute , replacementValue ] of wildcardAttrs ) {
if ( attribute in current ) {
current [ attribute ] = replacementValue ;
}
}
// Process conditional attributes
for ( const [ attribute , valueMap ] of conditionalAttrs ) {
if ( attribute in current ) {
const currentValue = current [ attribute ] ;
if ( currentValue in valueMap ) {
current [ attribute ] = valueMap [ currentValue ] ;
}
}
}
// Optimization 4: Use Object.keys() instead of for...in (faster, no inherited props)
const keys = Object . keys ( current ) ;
for ( let i = 0 ; i < keys . length ; i ++ ) {
const key = keys [ i ] ;
const value = current [ key ] ;
// Optimization 5: Cache type check
const valueType = typeof value ;
if ( valueType !== 'object' || value === null ) {
continue ;
}
if ( isArray ( value ) ) {
// Optimization 6: Batch array processing
if ( value . length > 0 ) {
for ( let j = 0 ; j < value . length ; j ++ ) {
const item = value [ j ] ;
if ( item != null && typeof item === 'object' ) {
workQueue . push ( item ) ;
}
}
) ;
}
} else if (
isObject ( redactedObject [ property ] ) &&
! ( redactedObject [ property ] instanceof Big )
) {
// Recursively call the function on the nested object
redactedObject [ property ] = redactAttributes ( {
options ,
isFirstRun : false ,
object : redactedObject [ property ]
} ) ;
}
}
} else if ( ! ( value instanceof Big ) ) {
// Push object to queue (Big.js instances excluded)
workQueue . push ( value ) ;
}
}
}
}
}
}