3
3
* and provides a way to retrieve those changes.
4
4
*/
5
5
6
+ import { deepEquals , isTemporal } from "./utils"
7
+
6
8
/**
7
9
* Simple debug utility that only logs when debug mode is enabled
8
10
* Set DEBUG to true in localStorage to enable debug logging
@@ -133,6 +135,13 @@ function deepClone<T extends unknown>(
133
135
return clone as unknown as T
134
136
}
135
137
138
+ // Handle Temporal objects
139
+ if ( isTemporal ( obj ) ) {
140
+ // Temporal objects are immutable, so we can return them directly
141
+ // This preserves all their internal state correctly
142
+ return obj
143
+ }
144
+
136
145
const clone = { } as Record < string | symbol , unknown >
137
146
visited . set ( obj as object , clone )
138
147
@@ -156,107 +165,6 @@ function deepClone<T extends unknown>(
156
165
return clone as T
157
166
}
158
167
159
- /**
160
- * Deep equality check that handles special types like Date, RegExp, Map, and Set
161
- */
162
- function deepEqual < T > ( a : T , b : T ) : boolean {
163
- // Handle primitive types
164
- if ( a === b ) return true
165
-
166
- // If either is null or not an object, they're not equal
167
- if (
168
- a === null ||
169
- b === null ||
170
- typeof a !== `object` ||
171
- typeof b !== `object`
172
- ) {
173
- return false
174
- }
175
-
176
- // Handle Date objects
177
- if ( a instanceof Date && b instanceof Date ) {
178
- return a . getTime ( ) === b . getTime ( )
179
- }
180
-
181
- // Handle RegExp objects
182
- if ( a instanceof RegExp && b instanceof RegExp ) {
183
- return a . source === b . source && a . flags === b . flags
184
- }
185
-
186
- // Handle Map objects
187
- if ( a instanceof Map && b instanceof Map ) {
188
- if ( a . size !== b . size ) return false
189
-
190
- const entries = Array . from ( a . entries ( ) )
191
- for ( const [ key , val ] of entries ) {
192
- if ( ! b . has ( key ) || ! deepEqual ( val , b . get ( key ) ) ) {
193
- return false
194
- }
195
- }
196
-
197
- return true
198
- }
199
-
200
- // Handle Set objects
201
- if ( a instanceof Set && b instanceof Set ) {
202
- if ( a . size !== b . size ) return false
203
-
204
- // Convert to arrays for comparison
205
- const aValues = Array . from ( a )
206
- const bValues = Array . from ( b )
207
-
208
- // Simple comparison for primitive values
209
- if ( aValues . every ( ( val ) => typeof val !== `object` ) ) {
210
- return aValues . every ( ( val ) => b . has ( val ) )
211
- }
212
-
213
- // For objects in sets, we need to do a more complex comparison
214
- // This is a simplified approach and may not work for all cases
215
- return aValues . length === bValues . length
216
- }
217
-
218
- // Handle arrays
219
- if ( Array . isArray ( a ) && Array . isArray ( b ) ) {
220
- if ( a . length !== b . length ) return false
221
-
222
- for ( let i = 0 ; i < a . length ; i ++ ) {
223
- if ( ! deepEqual ( a [ i ] , b [ i ] ) ) return false
224
- }
225
-
226
- return true
227
- }
228
-
229
- // Handle TypedArrays
230
- if (
231
- ArrayBuffer . isView ( a ) &&
232
- ArrayBuffer . isView ( b ) &&
233
- ! ( a instanceof DataView ) &&
234
- ! ( b instanceof DataView )
235
- ) {
236
- const typedA = a as unknown as TypedArray
237
- const typedB = b as unknown as TypedArray
238
- if ( typedA . length !== typedB . length ) return false
239
-
240
- for ( let i = 0 ; i < typedA . length ; i ++ ) {
241
- if ( typedA [ i ] !== typedB [ i ] ) return false
242
- }
243
-
244
- return true
245
- }
246
-
247
- // Handle plain objects
248
- const keysA = Object . keys ( a as object )
249
- const keysB = Object . keys ( b as object )
250
-
251
- if ( keysA . length !== keysB . length ) return false
252
-
253
- return keysA . every (
254
- ( key ) =>
255
- Object . prototype . hasOwnProperty . call ( b , key ) &&
256
- deepEqual ( ( a as any ) [ key ] , ( b as any ) [ key ] )
257
- )
258
- }
259
-
260
168
let count = 0
261
169
function getProxyCount ( ) {
262
170
count += 1
@@ -392,7 +300,7 @@ export function createChangeProxy<
392
300
)
393
301
394
302
// If the value is not equal to original, something is still changed
395
- if ( ! deepEqual ( currentValue , originalValue ) ) {
303
+ if ( ! deepEquals ( currentValue , originalValue ) ) {
396
304
debugLog ( `Property ${ String ( prop ) } is different, returning false` )
397
305
return false
398
306
}
@@ -411,7 +319,7 @@ export function createChangeProxy<
411
319
const originalValue = ( state . originalObject as any ) [ sym ]
412
320
413
321
// If the value is not equal to original, something is still changed
414
- if ( ! deepEqual ( currentValue , originalValue ) ) {
322
+ if ( ! deepEquals ( currentValue , originalValue ) ) {
415
323
debugLog ( `Symbol property is different, returning false` )
416
324
return false
417
325
}
@@ -741,12 +649,13 @@ export function createChangeProxy<
741
649
return value . bind ( ptarget )
742
650
}
743
651
744
- // If the value is an object, create a proxy for it
652
+ // If the value is an object (but not Date, RegExp, or Temporal) , create a proxy for it
745
653
if (
746
654
value &&
747
655
typeof value === `object` &&
748
656
! ( ( value as any ) instanceof Date ) &&
749
- ! ( ( value as any ) instanceof RegExp )
657
+ ! ( ( value as any ) instanceof RegExp ) &&
658
+ ! isTemporal ( value )
750
659
) {
751
660
// Create a parent reference for the nested object
752
661
const nestedParent = {
@@ -779,11 +688,11 @@ export function createChangeProxy<
779
688
)
780
689
781
690
// Only track the change if the value is actually different
782
- if ( ! deepEqual ( currentValue , value ) ) {
691
+ if ( ! deepEquals ( currentValue , value ) ) {
783
692
// Check if the new value is equal to the original value
784
693
// Important: Use the originalObject to get the true original value
785
694
const originalValue = changeTracker . originalObject [ prop as keyof T ]
786
- const isRevertToOriginal = deepEqual ( value , originalValue )
695
+ const isRevertToOriginal = deepEquals ( value , originalValue )
787
696
debugLog (
788
697
`value:` ,
789
698
value ,
0 commit comments