* Creates handlers for Map/Set iterator methods (entries, keys, values, forEach). * Returns proxied values for iteration to enable change tracking.
(
methodName: string,
prop: string | symbol,
methodFn: (...args: Array<unknown>) => unknown,
target: Map<unknown, unknown> | Set<unknown>,
changeTracker: ChangeTracker<T>,
memoizedCreateChangeProxy: (
obj: Record<string | symbol, unknown>,
parent?: {
tracker: ChangeTracker<Record<string | symbol, unknown>>
prop: string | symbol
},
) => { proxy: Record<string | symbol, unknown> },
markChanged: (tracker: ChangeTracker<T>) => void,
)
| 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> |