/** * Performs robust deep equality comparison with advanced type support and circular reference handling. * * Enhanced version of deep equality comparison that handles complex data structures including * circular references, typed arrays, dates, regular expressions, and sparse arrays. Uses * optimized algorithms with visited object tracking to prevent infinite loops while maintaining * high performance for nested structures. * * **Key Differences from `equals`:** * - **Circular Reference Safe**: Uses Map-based tracking to handle self-referencing objects * - **Advanced Type Support**: Proper comparison for Date, RegExp, TypedArrays, ArrayBuffers * - **Sparse Array Handling**: Correctly distinguishes sparse arrays from dense ones * - **Symbol Property Support**: Compares symbol-keyed properties using Reflect.ownKeys * - **Memory Safety**: No risk of stack overflow with deeply nested circular structures * - **Performance**: ~2x slower than `equals` but handles much more complex scenarios * * **When to Use stableEquals vs equals:** * ```typescript * // Use equals() for: * - Simple objects without circular references * - Performance-critical code paths * - Basic comparison needs * - Objects with only string/number keys * * // Use stableEquals() for: * - Objects with potential circular references * - Complex data with Date, RegExp, TypedArrays * - Sparse arrays or arrays with holes * - Objects with symbol properties * - Maximum reliability over performance * ``` * * @param left - First value to compare * @param right - Second value to compare * @param omit - Array of property keys to exclude from comparison (optional) * @returns true if values are deeply equal considering all supported types, false otherwise * * @example * Basic equality comparison: * ```typescript * import { stableEquals } from '@winglet/common-utils'; * * // Primitive values with NaN handling * console.log(stableEquals(42, 42)); // true * console.log(stableEquals('hello', 'hello')); // true * console.log(stableEquals(NaN, NaN)); // true (unlike === operator) * console.log(stableEquals(null, undefined)); // false * * // Object comparison * const obj1 = { name: 'John', age: 30 }; * const obj2 = { name: 'John', age: 30 }; * console.log(stableEquals(obj1, obj2)); // true * console.log(obj1 === obj2); // false (different references) * ``` * * @example * Advanced type support: * ```typescript * // Date objects * const date1 = new Date('2023-01-01T12:00:00Z'); * const date2 = new Date('2023-01-01T12:00:00Z'); * const date3 = new Date('2023-01-01T13:00:00Z'); * console.log(stableEquals(date1, date2)); // true (same timestamp) * console.log(stableEquals(date1, date3)); // false (different timestamp) * * // RegExp objects * const regex1 = /test/gi; * const regex2 = /test/gi; * const regex3 = /test/g; // different flags * console.log(stableEquals(regex1, regex2)); // true (same pattern and flags) * console.log(stableEquals(regex1, regex3)); // false (different flags) * * // TypedArrays and ArrayBuffers * const uint8_1 = new Uint8Array([1, 2, 3, 4]); * const uint8_2 = new Uint8Array([1, 2, 3, 4]); * const uint8_3 = new Uint8Array([1, 2, 3, 5]); * console.log(stableEquals(uint8_1, uint8_2)); // true (same content) * console.log(stableEquals(uint8_1, uint8_3)); // false (different content) * ``` * * @example * Circular reference handling: * ```typescript * // Objects with circular references * const circular1: any = { name: 'parent' }; * circular1.self = circular1; * circular1.child = { parent: circular1, name: 'child' }; * * const circular2: any = { name: 'parent' }; * circular2.self = circular2; * circular2.child = { parent: circular2, name: 'child' }; * * // stableEquals can handle circular references * console.log(stableEquals(circular1, circular2)); // true * * // Different circular structures * const different: any = { name: 'different' }; * different.self = different; * console.log(stableEquals(circular1, different)); // false * ``` * * @example * Sparse array comparison: * ```typescript * // Sparse arrays (arrays with holes) * const sparse1 = new Array(5); * sparse1[0] = 'first'; * sparse1[4] = 'last'; * // sparse1[1], sparse1[2], sparse1[3] are holes * * const sparse2 = new Array(5); * sparse2[0] = 'first'; * sparse2[4] = 'last'; * * const dense = ['first', undefined, undefined, undefined, 'last']; * * console.log(stableEquals(sparse1, sparse2)); // true (same sparse structure) * console.log(stableEquals(sparse1, dense)); // false (sparse vs dense) * * // Regular arrays * const arr1 = [1, 2, { nested: true }, [4, 5]]; * const arr2 = [1, 2, { nested: true }, [4, 5]]; * console.log(stableEquals(arr1, arr2)); // true * ``` * * @example * Property exclusion: * ```typescript * // Exclude specific properties from comparison * const user1 = { * id: 1, * name: 'Alice', * lastLogin: '2023-01-01T10:00:00Z', * sessionId: 'abc123' * }; * * const user2 = { * id: 1, * name: 'Alice', * lastLogin: '2023-01-01T11:00:00Z', // Different * sessionId: 'xyz789' // Different * }; * * // Compare excluding volatile fields * console.log(stableEquals(user1, user2)); // false * console.log(stableEquals(user1, user2, ['lastLogin', 'sessionId'])); // true * ``` * * @example * Complex nested structures: * ```typescript * // Deeply nested objects with mixed types * const complex1 = { * metadata: { * created: new Date('2023-01-01'), * pattern: /\w+/g, * tags: ['important', 'user-data'] * }, * data: { * users: [ * { id: 1, profile: { active: true, scores: new Float32Array([95.5, 87.2]) } }, * { id: 2, profile: { active: false, scores: new Float32Array([78.1, 92.3]) } } * ], * summary: { * total: 2, * active: 1, * averageScore: 88.275 * } * } * }; * * const complex2 = { * metadata: { * created: new Date('2023-01-01'), * pattern: /\w+/g, * tags: ['important', 'user-data'] * }, * data: { * users: [ * { id: 1, profile: { active: true, scores: new Float32Array([95.5, 87.2]) } }, * { id: 2, profile: { active: false, scores: new Float32Array([78.1, 92.3]) } } * ], * summary: { * total: 2, * active: 1, * averageScore: 88.275 * } * } * }; * * console.log(stableEquals(complex1, complex2)); // true * ``` * * @example * Performance with large structures: * ```typescript * // Large nested structures * const createLargeStructure = (depth: number, breadth: number) => { * const obj: any = {}; * * for (let i = 0; i < breadth; i++) { * if (depth > 0) { * obj[`branch_${i}`] = createLargeStructure(depth - 1, breadth); * } else { * obj[`leaf_${i}`] = `value_${i}`; * } * } * * return obj; * }; * * const large1 = createLargeStructure(5, 10); // Deep structure * const large2 = createLargeStructure(5, 10); // Identical structure * * console.time('stableEquals-large'); * const isEqual = stableEquals(large1, large2); * console.timeEnd('stableEquals-large'); * console.log(isEqual); // true * ``` * * @example * Symbol properties and edge cases: * ```typescript * // Objects with symbol properties * const sym1 = Symbol('test'); * const sym2 = Symbol('test'); // Different symbol * * const obj1 = { regular: 'prop', [sym1]: 'symbol value' }; * const obj2 = { regular: 'prop', [sym1]: 'symbol value' }; * const obj3 = { regular: 'prop', [sym2]: 'symbol value' }; * * console.log(stableEquals(obj1, obj2)); // true (same symbol reference) * console.log(stableEquals(obj1, obj3)); // false (different symbol) * * // Mixed types comparison * console.log(stableEquals({}, [])); // false (object vs array) * console.log(stableEquals(new Date(), {})); // false (Date vs plain object) * console.log(stableEquals(/regex/, {})); // false (RegExp vs plain object) * ``` * * @remarks * **Enhanced Features over Basic Equals:** * - **Circular Reference Detection**: Prevents infinite loops with visited object tracking * - **Advanced Type Support**: Handles Date, RegExp, TypedArray, ArrayBuffer, DataView * - **Sparse Array Handling**: Correctly compares arrays with holes vs dense arrays * - **Symbol Property Support**: Compares symbol-keyed properties using Reflect.ownKeys * - **Memory Optimization**: Uses WeakMap for efficient visited object tracking * * **Comparison Strategy:** * - **Primitives**: Standard equality with special NaN handling (NaN === NaN) * - **Dates**: Compares internal timestamp values using getTime() * - **RegExp**: Compares source pattern and flags for complete equality * - **TypedArrays**: Byte-by-byte comparison using DataView for accuracy * - **Objects**: Deep recursive comparison with circular reference protection * - **Arrays**: Index-by-index comparison with sparse array hole detection * * **Performance Characteristics:** * - **Time Complexity**: O(n) where n is total number of properties/elements * - **Space Complexity**: O(d + c) where d is depth, c is number of circular references * - **Optimization**: Early termination, reference equality check, visited object caching * - **Memory Efficient**: WeakMap-based visited tracking with automatic cleanup * * **Circular Reference Handling:** * - **Detection**: Uses Map to track visited object pairs during traversal * - **Prevention**: Returns true if circular reference is detected (assumes structural equality) * - **Bidirectional**: Tracks both left->right and right->left object relationships * - **Memory Safe**: No memory leaks from circular reference tracking * * **Supported Data Types:** * - **Primitives**: string, number, boolean, null, undefined, symbol, bigint * - **Objects**: Plain objects, arrays (including sparse), functions * - **Built-ins**: Date, RegExp, Error, Map, Set (reference equality) * - **Binary Data**: ArrayBuffer, SharedArrayBuffer, TypedArrays, DataView * - **Custom Objects**: Objects with custom prototypes * * **Use Cases:** * - **Testing Frameworks**: Deep equality assertions for complex data structures * - **State Management**: Comparing application state with circular references * - **Caching Systems**: Determining if cached data matches current data * - **Data Validation**: Verifying API responses against expected structures * - **Change Detection**: Identifying modifications in reactive systems * - **Configuration Comparison**: Comparing complex configuration objects * * **Limitations:** * - **Set/Map Comparison**: Currently uses reference equality (could be enhanced) * - **Function Comparison**: Compares by reference only, not implementation * - **Custom Class Instances**: May not handle specialized comparison logic * - **Performance on Extremely Large Objects**: May be slower than simple reference checks * - **Prototype Chain**: Only compares own properties, ignores inherited properties */ export declare const stableEquals: (left: unknown, right: unknown, omit?: readonly PropertyKey[]) => boolean;