@tanstack/db 0.5.6 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/proxy.ts CHANGED
@@ -5,6 +5,453 @@
5
5
 
6
6
  import { deepEquals, isTemporal } from "./utils"
7
7
 
8
+ /**
9
+ * Set of array methods that iterate with callbacks and may return elements.
10
+ * Hoisted to module scope to avoid creating a new Set on every property access.
11
+ */
12
+ const CALLBACK_ITERATION_METHODS = new Set([
13
+ `find`,
14
+ `findLast`,
15
+ `findIndex`,
16
+ `findLastIndex`,
17
+ `filter`,
18
+ `map`,
19
+ `flatMap`,
20
+ `forEach`,
21
+ `some`,
22
+ `every`,
23
+ `reduce`,
24
+ `reduceRight`,
25
+ ])
26
+
27
+ /**
28
+ * Set of array methods that modify the array in place.
29
+ */
30
+ const ARRAY_MODIFYING_METHODS = new Set([
31
+ `pop`,
32
+ `push`,
33
+ `shift`,
34
+ `unshift`,
35
+ `splice`,
36
+ `sort`,
37
+ `reverse`,
38
+ `fill`,
39
+ `copyWithin`,
40
+ ])
41
+
42
+ /**
43
+ * Set of Map/Set methods that modify the collection in place.
44
+ */
45
+ const MAP_SET_MODIFYING_METHODS = new Set([`set`, `delete`, `clear`, `add`])
46
+
47
+ /**
48
+ * Set of Map/Set iterator methods.
49
+ */
50
+ const MAP_SET_ITERATOR_METHODS = new Set([
51
+ `entries`,
52
+ `keys`,
53
+ `values`,
54
+ `forEach`,
55
+ ])
56
+
57
+ /**
58
+ * Check if a value is a proxiable object (not Date, RegExp, or Temporal)
59
+ */
60
+ function isProxiableObject(
61
+ value: unknown
62
+ ): value is Record<string | symbol, unknown> {
63
+ return (
64
+ value !== null &&
65
+ typeof value === `object` &&
66
+ !((value as any) instanceof Date) &&
67
+ !((value as any) instanceof RegExp) &&
68
+ !isTemporal(value)
69
+ )
70
+ }
71
+
72
+ /**
73
+ * Creates a handler for array iteration methods that ensures proxied elements
74
+ * are passed to callbacks and returned from methods like find/filter.
75
+ */
76
+ function createArrayIterationHandler<T extends object>(
77
+ methodName: string,
78
+ methodFn: (...args: Array<unknown>) => unknown,
79
+ changeTracker: ChangeTracker<T>,
80
+ memoizedCreateChangeProxy: (
81
+ obj: Record<string | symbol, unknown>,
82
+ parent?: {
83
+ tracker: ChangeTracker<Record<string | symbol, unknown>>
84
+ prop: string | symbol
85
+ }
86
+ ) => { proxy: Record<string | symbol, unknown> }
87
+ ): ((...args: Array<unknown>) => unknown) | undefined {
88
+ if (!CALLBACK_ITERATION_METHODS.has(methodName)) {
89
+ return undefined
90
+ }
91
+
92
+ return function (...args: Array<unknown>) {
93
+ const callback = args[0]
94
+ if (typeof callback !== `function`) {
95
+ return methodFn.apply(changeTracker.copy_, args)
96
+ }
97
+
98
+ // Create a helper to get proxied version of an array element
99
+ const getProxiedElement = (element: unknown, index: number): unknown => {
100
+ if (isProxiableObject(element)) {
101
+ const nestedParent = {
102
+ tracker: changeTracker as unknown as ChangeTracker<
103
+ Record<string | symbol, unknown>
104
+ >,
105
+ prop: String(index),
106
+ }
107
+ const { proxy: elementProxy } = memoizedCreateChangeProxy(
108
+ element,
109
+ nestedParent
110
+ )
111
+ return elementProxy
112
+ }
113
+ return element
114
+ }
115
+
116
+ // Wrap the callback to pass proxied elements
117
+ const wrappedCallback = function (
118
+ this: unknown,
119
+ element: unknown,
120
+ index: number,
121
+ array: unknown
122
+ ) {
123
+ const proxiedElement = getProxiedElement(element, index)
124
+ return callback.call(this, proxiedElement, index, array)
125
+ }
126
+
127
+ // For reduce/reduceRight, the callback signature is different
128
+ if (methodName === `reduce` || methodName === `reduceRight`) {
129
+ const reduceCallback = function (
130
+ this: unknown,
131
+ accumulator: unknown,
132
+ element: unknown,
133
+ index: number,
134
+ array: unknown
135
+ ) {
136
+ const proxiedElement = getProxiedElement(element, index)
137
+ return callback.call(this, accumulator, proxiedElement, index, array)
138
+ }
139
+ return methodFn.apply(changeTracker.copy_, [
140
+ reduceCallback,
141
+ ...args.slice(1),
142
+ ])
143
+ }
144
+
145
+ const result = methodFn.apply(changeTracker.copy_, [
146
+ wrappedCallback,
147
+ ...args.slice(1),
148
+ ])
149
+
150
+ // For find/findLast, proxy the returned element if it's an object
151
+ if (
152
+ (methodName === `find` || methodName === `findLast`) &&
153
+ result &&
154
+ typeof result === `object`
155
+ ) {
156
+ const foundIndex = (
157
+ changeTracker.copy_ as unknown as Array<unknown>
158
+ ).indexOf(result)
159
+ if (foundIndex !== -1) {
160
+ return getProxiedElement(result, foundIndex)
161
+ }
162
+ }
163
+
164
+ // For filter, proxy each element in the result array
165
+ if (methodName === `filter` && Array.isArray(result)) {
166
+ return result.map((element) => {
167
+ const originalIndex = (
168
+ changeTracker.copy_ as unknown as Array<unknown>
169
+ ).indexOf(element)
170
+ if (originalIndex !== -1) {
171
+ return getProxiedElement(element, originalIndex)
172
+ }
173
+ return element
174
+ })
175
+ }
176
+
177
+ return result
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Creates a Symbol.iterator handler for arrays that yields proxied elements.
183
+ */
184
+ function createArrayIteratorHandler<T extends object>(
185
+ changeTracker: ChangeTracker<T>,
186
+ memoizedCreateChangeProxy: (
187
+ obj: Record<string | symbol, unknown>,
188
+ parent?: {
189
+ tracker: ChangeTracker<Record<string | symbol, unknown>>
190
+ prop: string | symbol
191
+ }
192
+ ) => { proxy: Record<string | symbol, unknown> }
193
+ ): () => Iterator<unknown> {
194
+ return function () {
195
+ const array = changeTracker.copy_ as unknown as Array<unknown>
196
+ let index = 0
197
+
198
+ return {
199
+ next() {
200
+ if (index >= array.length) {
201
+ return { done: true, value: undefined }
202
+ }
203
+
204
+ const element = array[index]
205
+ let proxiedElement = element
206
+
207
+ if (isProxiableObject(element)) {
208
+ const nestedParent = {
209
+ tracker: changeTracker as unknown as ChangeTracker<
210
+ Record<string | symbol, unknown>
211
+ >,
212
+ prop: String(index),
213
+ }
214
+ const { proxy: elementProxy } = memoizedCreateChangeProxy(
215
+ element,
216
+ nestedParent
217
+ )
218
+ proxiedElement = elementProxy
219
+ }
220
+
221
+ index++
222
+ return { done: false, value: proxiedElement }
223
+ },
224
+ [Symbol.iterator]() {
225
+ return this
226
+ },
227
+ }
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Creates a wrapper for methods that modify a collection (array, Map, Set).
233
+ * The wrapper calls the method and marks the change tracker as modified.
234
+ */
235
+ function createModifyingMethodHandler<T extends object>(
236
+ methodFn: (...args: Array<unknown>) => unknown,
237
+ changeTracker: ChangeTracker<T>,
238
+ markChanged: (tracker: ChangeTracker<T>) => void
239
+ ): (...args: Array<unknown>) => unknown {
240
+ return function (...args: Array<unknown>) {
241
+ const result = methodFn.apply(changeTracker.copy_, args)
242
+ markChanged(changeTracker)
243
+ return result
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Creates handlers for Map/Set iterator methods (entries, keys, values, forEach).
249
+ * Returns proxied values for iteration to enable change tracking.
250
+ */
251
+ function createMapSetIteratorHandler<T extends object>(
252
+ methodName: string,
253
+ prop: string | symbol,
254
+ methodFn: (...args: Array<unknown>) => unknown,
255
+ target: Map<unknown, unknown> | Set<unknown>,
256
+ changeTracker: ChangeTracker<T>,
257
+ memoizedCreateChangeProxy: (
258
+ obj: Record<string | symbol, unknown>,
259
+ parent?: {
260
+ tracker: ChangeTracker<Record<string | symbol, unknown>>
261
+ prop: string | symbol
262
+ }
263
+ ) => { proxy: Record<string | symbol, unknown> },
264
+ markChanged: (tracker: ChangeTracker<T>) => void
265
+ ): ((...args: Array<unknown>) => unknown) | undefined {
266
+ const isIteratorMethod =
267
+ MAP_SET_ITERATOR_METHODS.has(methodName) || prop === Symbol.iterator
268
+
269
+ if (!isIteratorMethod) {
270
+ return undefined
271
+ }
272
+
273
+ return function (this: unknown, ...args: Array<unknown>) {
274
+ const result = methodFn.apply(changeTracker.copy_, args)
275
+
276
+ // For forEach, wrap the callback to track changes
277
+ if (methodName === `forEach`) {
278
+ const callback = args[0]
279
+ if (typeof callback === `function`) {
280
+ const wrappedCallback = function (
281
+ this: unknown,
282
+ value: unknown,
283
+ key: unknown,
284
+ collection: unknown
285
+ ) {
286
+ const cbresult = callback.call(this, value, key, collection)
287
+ markChanged(changeTracker)
288
+ return cbresult
289
+ }
290
+ return methodFn.apply(target, [wrappedCallback, ...args.slice(1)])
291
+ }
292
+ }
293
+
294
+ // For iterators (entries, keys, values, Symbol.iterator)
295
+ const isValueIterator =
296
+ methodName === `entries` ||
297
+ methodName === `values` ||
298
+ methodName === Symbol.iterator.toString() ||
299
+ prop === Symbol.iterator
300
+
301
+ if (isValueIterator) {
302
+ const originalIterator = result as Iterator<unknown>
303
+
304
+ // For values() iterator on Maps, create a value-to-key mapping
305
+ const valueToKeyMap = new Map()
306
+ if (methodName === `values` && target instanceof Map) {
307
+ for (const [key, mapValue] of (
308
+ changeTracker.copy_ as unknown as Map<unknown, unknown>
309
+ ).entries()) {
310
+ valueToKeyMap.set(mapValue, key)
311
+ }
312
+ }
313
+
314
+ // For Set iterators, create an original-to-modified mapping
315
+ const originalToModifiedMap = new Map()
316
+ if (target instanceof Set) {
317
+ for (const setValue of (
318
+ changeTracker.copy_ as unknown as Set<unknown>
319
+ ).values()) {
320
+ originalToModifiedMap.set(setValue, setValue)
321
+ }
322
+ }
323
+
324
+ // Return a wrapped iterator that proxies values
325
+ return {
326
+ next() {
327
+ const nextResult = originalIterator.next()
328
+
329
+ if (
330
+ !nextResult.done &&
331
+ nextResult.value &&
332
+ typeof nextResult.value === `object`
333
+ ) {
334
+ // For entries, the value is a [key, value] pair
335
+ if (
336
+ methodName === `entries` &&
337
+ Array.isArray(nextResult.value) &&
338
+ nextResult.value.length === 2
339
+ ) {
340
+ if (
341
+ nextResult.value[1] &&
342
+ typeof nextResult.value[1] === `object`
343
+ ) {
344
+ const mapKey = nextResult.value[0]
345
+ const mapParent = {
346
+ tracker: changeTracker as unknown as ChangeTracker<
347
+ Record<string | symbol, unknown>
348
+ >,
349
+ prop: mapKey as string | symbol,
350
+ updateMap: (newValue: unknown) => {
351
+ if (changeTracker.copy_ instanceof Map) {
352
+ ;(changeTracker.copy_ as Map<unknown, unknown>).set(
353
+ mapKey,
354
+ newValue
355
+ )
356
+ }
357
+ },
358
+ }
359
+ const { proxy: valueProxy } = memoizedCreateChangeProxy(
360
+ nextResult.value[1] as Record<string | symbol, unknown>,
361
+ mapParent as unknown as {
362
+ tracker: ChangeTracker<Record<string | symbol, unknown>>
363
+ prop: string | symbol
364
+ }
365
+ )
366
+ nextResult.value[1] = valueProxy
367
+ }
368
+ } else if (
369
+ methodName === `values` ||
370
+ methodName === Symbol.iterator.toString() ||
371
+ prop === Symbol.iterator
372
+ ) {
373
+ // For Map values(), use the key mapping
374
+ if (methodName === `values` && target instanceof Map) {
375
+ const mapKey = valueToKeyMap.get(nextResult.value)
376
+ if (mapKey !== undefined) {
377
+ const mapParent = {
378
+ tracker: changeTracker as unknown as ChangeTracker<
379
+ Record<string | symbol, unknown>
380
+ >,
381
+ prop: mapKey as string | symbol,
382
+ updateMap: (newValue: unknown) => {
383
+ if (changeTracker.copy_ instanceof Map) {
384
+ ;(changeTracker.copy_ as Map<unknown, unknown>).set(
385
+ mapKey,
386
+ newValue
387
+ )
388
+ }
389
+ },
390
+ }
391
+ const { proxy: valueProxy } = memoizedCreateChangeProxy(
392
+ nextResult.value as Record<string | symbol, unknown>,
393
+ mapParent as unknown as {
394
+ tracker: ChangeTracker<Record<string | symbol, unknown>>
395
+ prop: string | symbol
396
+ }
397
+ )
398
+ nextResult.value = valueProxy
399
+ }
400
+ } else if (target instanceof Set) {
401
+ // For Set, track modifications
402
+ const setOriginalValue = nextResult.value
403
+ const setParent = {
404
+ tracker: changeTracker as unknown as ChangeTracker<
405
+ Record<string | symbol, unknown>
406
+ >,
407
+ prop: setOriginalValue as unknown as string | symbol,
408
+ updateSet: (newValue: unknown) => {
409
+ if (changeTracker.copy_ instanceof Set) {
410
+ ;(changeTracker.copy_ as Set<unknown>).delete(
411
+ setOriginalValue
412
+ )
413
+ ;(changeTracker.copy_ as Set<unknown>).add(newValue)
414
+ originalToModifiedMap.set(setOriginalValue, newValue)
415
+ }
416
+ },
417
+ }
418
+ const { proxy: valueProxy } = memoizedCreateChangeProxy(
419
+ nextResult.value as Record<string | symbol, unknown>,
420
+ setParent as unknown as {
421
+ tracker: ChangeTracker<Record<string | symbol, unknown>>
422
+ prop: string | symbol
423
+ }
424
+ )
425
+ nextResult.value = valueProxy
426
+ } else {
427
+ // For other cases, use a symbol placeholder
428
+ const tempKey = Symbol(`iterator-value`)
429
+ const { proxy: valueProxy } = memoizedCreateChangeProxy(
430
+ nextResult.value as Record<string | symbol, unknown>,
431
+ {
432
+ tracker: changeTracker as unknown as ChangeTracker<
433
+ Record<string | symbol, unknown>
434
+ >,
435
+ prop: tempKey,
436
+ }
437
+ )
438
+ nextResult.value = valueProxy
439
+ }
440
+ }
441
+ }
442
+
443
+ return nextResult
444
+ },
445
+ [Symbol.iterator]() {
446
+ return this
447
+ },
448
+ }
449
+ }
450
+
451
+ return result
452
+ }
453
+ }
454
+
8
455
  /**
9
456
  * Simple debug utility that only logs when debug mode is enabled
10
457
  * Set DEBUG to true in localStorage to enable debug logging
@@ -392,271 +839,66 @@ export function createChangeProxy<
392
839
  // For Array methods that modify the array
393
840
  if (Array.isArray(ptarget)) {
394
841
  const methodName = prop.toString()
395
- const modifyingMethods = new Set([
396
- `pop`,
397
- `push`,
398
- `shift`,
399
- `unshift`,
400
- `splice`,
401
- `sort`,
402
- `reverse`,
403
- `fill`,
404
- `copyWithin`,
405
- ])
406
-
407
- if (modifyingMethods.has(methodName)) {
408
- return function (...args: Array<unknown>) {
409
- const result = value.apply(changeTracker.copy_, args)
410
- markChanged(changeTracker)
411
- return result
412
- }
842
+
843
+ if (ARRAY_MODIFYING_METHODS.has(methodName)) {
844
+ return createModifyingMethodHandler(
845
+ value,
846
+ changeTracker,
847
+ markChanged
848
+ )
849
+ }
850
+
851
+ // Handle array iteration methods (find, filter, forEach, etc.)
852
+ const iterationHandler = createArrayIterationHandler(
853
+ methodName,
854
+ value,
855
+ changeTracker,
856
+ memoizedCreateChangeProxy
857
+ )
858
+ if (iterationHandler) {
859
+ return iterationHandler
860
+ }
861
+
862
+ // Handle array Symbol.iterator for for...of loops
863
+ if (prop === Symbol.iterator) {
864
+ return createArrayIteratorHandler(
865
+ changeTracker,
866
+ memoizedCreateChangeProxy
867
+ )
413
868
  }
414
869
  }
415
870
 
416
871
  // For Map and Set methods that modify the collection
417
872
  if (ptarget instanceof Map || ptarget instanceof Set) {
418
873
  const methodName = prop.toString()
419
- const modifyingMethods = new Set([
420
- `set`,
421
- `delete`,
422
- `clear`,
423
- `add`,
424
- `pop`,
425
- `push`,
426
- `shift`,
427
- `unshift`,
428
- `splice`,
429
- `sort`,
430
- `reverse`,
431
- ])
432
-
433
- if (modifyingMethods.has(methodName)) {
434
- return function (...args: Array<unknown>) {
435
- const result = value.apply(changeTracker.copy_, args)
436
- markChanged(changeTracker)
437
- return result
438
- }
874
+
875
+ if (MAP_SET_MODIFYING_METHODS.has(methodName)) {
876
+ return createModifyingMethodHandler(
877
+ value,
878
+ changeTracker,
879
+ markChanged
880
+ )
439
881
  }
440
882
 
441
883
  // Handle iterator methods for Map and Set
442
- const iteratorMethods = new Set([
443
- `entries`,
444
- `keys`,
445
- `values`,
446
- `forEach`,
447
- Symbol.iterator,
448
- ])
449
-
450
- if (iteratorMethods.has(methodName) || prop === Symbol.iterator) {
451
- return function (this: unknown, ...args: Array<unknown>) {
452
- const result = value.apply(changeTracker.copy_, args)
453
-
454
- // For forEach, we need to wrap the callback to track changes
455
- if (methodName === `forEach`) {
456
- const callback = args[0]
457
- if (typeof callback === `function`) {
458
- // Replace the original callback with our wrapped version
459
- const wrappedCallback = function (
460
- this: unknown,
461
- // eslint-disable-next-line
462
- value: unknown,
463
- key: unknown,
464
- collection: unknown
465
- ) {
466
- // Call the original callback
467
- const cbresult = callback.call(
468
- this,
469
- value,
470
- key,
471
- collection
472
- )
473
- // Mark as changed since the callback might have modified the value
474
- markChanged(changeTracker)
475
- return cbresult
476
- }
477
- // Call forEach with our wrapped callback
478
- return value.apply(ptarget, [
479
- wrappedCallback,
480
- ...args.slice(1),
481
- ])
482
- }
483
- }
484
-
485
- // For iterators (entries, keys, values, Symbol.iterator)
486
- if (
487
- methodName === `entries` ||
488
- methodName === `values` ||
489
- methodName === Symbol.iterator.toString() ||
490
- prop === Symbol.iterator
491
- ) {
492
- // If it's an iterator, we need to wrap the returned iterator
493
- // to track changes when the values are accessed and potentially modified
494
- const originalIterator = result
495
-
496
- // For values() iterator on Maps, we need to create a value-to-key mapping
497
- const valueToKeyMap = new Map()
498
- if (methodName === `values` && ptarget instanceof Map) {
499
- // Build a mapping from value to key for reverse lookup
500
- // Use the copy_ (which is the current state) to build the mapping
501
- for (const [
502
- key,
503
- mapValue,
504
- ] of changeTracker.copy_.entries()) {
505
- valueToKeyMap.set(mapValue, key)
506
- }
507
- }
508
-
509
- // For Set iterators, we need to create an original-to-modified mapping
510
- const originalToModifiedMap = new Map()
511
- if (ptarget instanceof Set) {
512
- // Initialize with original values
513
- for (const setValue of changeTracker.copy_.values()) {
514
- originalToModifiedMap.set(setValue, setValue)
515
- }
516
- }
517
-
518
- // Create a proxy for the iterator that will mark changes when next() is called
519
- return {
520
- next() {
521
- const nextResult = originalIterator.next()
522
-
523
- // If we have a value and it's an object, we need to track it
524
- if (
525
- !nextResult.done &&
526
- nextResult.value &&
527
- typeof nextResult.value === `object`
528
- ) {
529
- // For entries, the value is a [key, value] pair
530
- if (
531
- methodName === `entries` &&
532
- Array.isArray(nextResult.value) &&
533
- nextResult.value.length === 2
534
- ) {
535
- // The value is at index 1 in the [key, value] pair
536
- if (
537
- nextResult.value[1] &&
538
- typeof nextResult.value[1] === `object`
539
- ) {
540
- const mapKey = nextResult.value[0]
541
- // Create a special parent tracker that knows how to update the Map
542
- const mapParent = {
543
- tracker: changeTracker,
544
- prop: mapKey,
545
- updateMap: (newValue: unknown) => {
546
- // Update the Map in the copy
547
- if (changeTracker.copy_ instanceof Map) {
548
- changeTracker.copy_.set(mapKey, newValue)
549
- }
550
- },
551
- }
552
-
553
- // Create a proxy for the value and replace it in the result
554
- const { proxy: valueProxy } =
555
- memoizedCreateChangeProxy(
556
- nextResult.value[1],
557
- mapParent
558
- )
559
- nextResult.value[1] = valueProxy
560
- }
561
- } else if (
562
- methodName === `values` ||
563
- methodName === Symbol.iterator.toString() ||
564
- prop === Symbol.iterator
565
- ) {
566
- // If the value is an object, create a proxy for it
567
- if (
568
- typeof nextResult.value === `object` &&
569
- nextResult.value !== null
570
- ) {
571
- // For Map values(), try to find the key using our mapping
572
- if (
573
- methodName === `values` &&
574
- ptarget instanceof Map
575
- ) {
576
- const mapKey = valueToKeyMap.get(nextResult.value)
577
- if (mapKey !== undefined) {
578
- // Create a special parent tracker for this Map value
579
- const mapParent = {
580
- tracker: changeTracker,
581
- prop: mapKey,
582
- updateMap: (newValue: unknown) => {
583
- // Update the Map in the copy
584
- if (changeTracker.copy_ instanceof Map) {
585
- changeTracker.copy_.set(mapKey, newValue)
586
- }
587
- },
588
- }
589
-
590
- const { proxy: valueProxy } =
591
- memoizedCreateChangeProxy(
592
- nextResult.value,
593
- mapParent
594
- )
595
- nextResult.value = valueProxy
596
- }
597
- } else if (ptarget instanceof Set) {
598
- // For Set, we need to track modifications and update the Set accordingly
599
- const setOriginalValue = nextResult.value
600
- const setParent = {
601
- tracker: changeTracker,
602
- prop: setOriginalValue, // Use the original value as the prop
603
- updateSet: (newValue: unknown) => {
604
- // Update the Set in the copy by removing old value and adding new one
605
- if (changeTracker.copy_ instanceof Set) {
606
- changeTracker.copy_.delete(setOriginalValue)
607
- changeTracker.copy_.add(newValue)
608
- // Update our mapping for future iterations
609
- originalToModifiedMap.set(
610
- setOriginalValue,
611
- newValue
612
- )
613
- }
614
- },
615
- }
616
-
617
- const { proxy: valueProxy } =
618
- memoizedCreateChangeProxy(
619
- nextResult.value,
620
- setParent
621
- )
622
- nextResult.value = valueProxy
623
- } else {
624
- // For other cases, use a symbol as a placeholder
625
- const tempKey = Symbol(`iterator-value`)
626
- const { proxy: valueProxy } =
627
- memoizedCreateChangeProxy(nextResult.value, {
628
- tracker: changeTracker,
629
- prop: tempKey,
630
- })
631
- nextResult.value = valueProxy
632
- }
633
- }
634
- }
635
- }
636
-
637
- return nextResult
638
- },
639
- [Symbol.iterator]() {
640
- return this
641
- },
642
- }
643
- }
644
-
645
- return result
646
- }
884
+ const iteratorHandler = createMapSetIteratorHandler(
885
+ methodName,
886
+ prop,
887
+ value,
888
+ ptarget,
889
+ changeTracker,
890
+ memoizedCreateChangeProxy,
891
+ markChanged
892
+ )
893
+ if (iteratorHandler) {
894
+ return iteratorHandler
647
895
  }
648
896
  }
649
897
  return value.bind(ptarget)
650
898
  }
651
899
 
652
900
  // If the value is an object (but not Date, RegExp, or Temporal), create a proxy for it
653
- if (
654
- value &&
655
- typeof value === `object` &&
656
- !((value as any) instanceof Date) &&
657
- !((value as any) instanceof RegExp) &&
658
- !isTemporal(value)
659
- ) {
901
+ if (isProxiableObject(value)) {
660
902
  // Create a parent reference for the nested object
661
903
  const nestedParent = {
662
904
  tracker: changeTracker,