| 
						
						
							
								
							
						
						
					 | 
					@ -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); | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					      } | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					    } | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					  } | 
					 | 
					 | 
					  } | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					
  |