@yiin/reactive-proxy-state 1.0.1 → 1.0.3

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.
@@ -0,0 +1,237 @@
1
+ import { deepEqual, getPathConcat, setPathConcat, wrapperCache } from './utils';
2
+ import { reactive } from './reactive';
3
+ import { wrapMap } from './wrapMap';
4
+ import { wrapSet } from './wrapSet';
5
+ import { track, trigger } from './watchEffect';
6
+ // Pre-allocate type check function
7
+ function isObject(v) {
8
+ return v && typeof v === 'object';
9
+ }
10
+ export function wrapArray(arr, emit, path) {
11
+ // Check wrapper cache first
12
+ const cachedProxy = wrapperCache.get(arr);
13
+ if (cachedProxy)
14
+ return cachedProxy;
15
+ const proxy = new Proxy(arr, {
16
+ get(target, prop, receiver) {
17
+ // Original track call - might be redundant if handled below but keep for now
18
+ track(target, prop);
19
+ // Handle specific array mutation methods first
20
+ switch (prop) {
21
+ case 'push':
22
+ track(target, 'length');
23
+ return function (...items) {
24
+ const oldLength = target.length;
25
+ const result = target.push(...items);
26
+ const newLength = target.length;
27
+ if (items.length > 0) {
28
+ const event = {
29
+ action: 'array-push',
30
+ path: path,
31
+ key: oldLength, // Start index was the old length
32
+ items: items
33
+ };
34
+ emit(event);
35
+ trigger(target, Symbol.iterator);
36
+ if (oldLength !== newLength) {
37
+ trigger(target, 'length');
38
+ }
39
+ }
40
+ return result;
41
+ };
42
+ case 'pop':
43
+ track(target, 'length');
44
+ return function () {
45
+ if (target.length === 0)
46
+ return undefined;
47
+ const oldLength = target.length;
48
+ const poppedIndex = oldLength - 1;
49
+ const oldValue = target[poppedIndex];
50
+ const result = target.pop();
51
+ const newLength = target.length;
52
+ const event = {
53
+ action: 'array-pop',
54
+ path: path,
55
+ key: poppedIndex,
56
+ oldValue: oldValue
57
+ };
58
+ emit(event);
59
+ trigger(target, Symbol.iterator);
60
+ if (oldLength !== newLength) {
61
+ trigger(target, 'length');
62
+ }
63
+ return result;
64
+ };
65
+ case 'shift':
66
+ track(target, 'length');
67
+ return function () {
68
+ if (target.length === 0)
69
+ return undefined;
70
+ const oldLength = target.length;
71
+ const oldValue = target[0];
72
+ const result = target.shift();
73
+ const newLength = target.length;
74
+ const event = {
75
+ action: 'array-shift',
76
+ path: path,
77
+ key: 0,
78
+ oldValue: oldValue
79
+ };
80
+ emit(event);
81
+ trigger(target, Symbol.iterator);
82
+ if (oldLength !== newLength) {
83
+ trigger(target, 'length');
84
+ }
85
+ return result;
86
+ };
87
+ case 'unshift':
88
+ track(target, 'length');
89
+ return function (...items) {
90
+ const oldLength = target.length;
91
+ const result = target.unshift(...items);
92
+ const newLength = target.length;
93
+ if (items.length > 0) {
94
+ const event = {
95
+ action: 'array-unshift',
96
+ path: path,
97
+ key: 0,
98
+ items: items
99
+ };
100
+ emit(event);
101
+ trigger(target, Symbol.iterator);
102
+ if (oldLength !== newLength) {
103
+ trigger(target, 'length');
104
+ }
105
+ }
106
+ return result;
107
+ };
108
+ case 'splice':
109
+ track(target, 'length');
110
+ return function (start, deleteCount, ...items) {
111
+ const oldLength = target.length;
112
+ const actualStart = start < 0 ? Math.max(target.length + start, 0) : Math.min(start, target.length);
113
+ const deleteCountNum = deleteCount === undefined ? target.length - actualStart : Number(deleteCount);
114
+ const actualDeleteCount = Math.min(deleteCountNum, target.length - actualStart);
115
+ const deletedItems = target.slice(actualStart, actualStart + actualDeleteCount);
116
+ const result = target.splice(start, deleteCountNum, ...items);
117
+ const newLength = target.length;
118
+ if (actualDeleteCount > 0 || items.length > 0) {
119
+ const event = {
120
+ action: 'array-splice',
121
+ path: path,
122
+ key: actualStart,
123
+ deleteCount: actualDeleteCount,
124
+ items: items.length > 0 ? items : undefined,
125
+ oldValues: deletedItems.length > 0 ? deletedItems : undefined
126
+ };
127
+ emit(event);
128
+ trigger(target, Symbol.iterator);
129
+ if (oldLength !== newLength) {
130
+ trigger(target, 'length');
131
+ }
132
+ }
133
+ return result;
134
+ };
135
+ // Handle iteration methods
136
+ case Symbol.iterator:
137
+ case 'values': // values() returns an iterator
138
+ case 'keys': // keys() returns an iterator
139
+ case 'entries': // entries() returns an iterator
140
+ // Track dependency on iteration
141
+ track(target, Symbol.iterator);
142
+ // Fall through to Reflect.get and bind below
143
+ break;
144
+ case 'forEach':
145
+ case 'map':
146
+ case 'filter':
147
+ case 'reduce':
148
+ case 'reduceRight':
149
+ case 'find':
150
+ case 'findIndex':
151
+ case 'every':
152
+ case 'some':
153
+ case 'join': // join depends on iteration
154
+ // These methods depend on iteration
155
+ track(target, Symbol.iterator);
156
+ // Fall through to Reflect.get and bind below
157
+ break;
158
+ case 'length':
159
+ // Explicitly track length access
160
+ track(target, 'length');
161
+ return Reflect.get(target, prop, receiver);
162
+ }
163
+ // Fallback for index access and other properties
164
+ const value = Reflect.get(target, prop, receiver);
165
+ // Handle index access: wrap retrieved element if it's an object
166
+ const isNumericIndex = typeof prop === 'number' || (typeof prop === 'string' && !isNaN(parseInt(prop, 10)));
167
+ if (isNumericIndex) {
168
+ // Track access to specific index
169
+ track(target, String(prop));
170
+ if (!isObject(value))
171
+ return value;
172
+ // Check wrapper cache for the element
173
+ const cachedValueProxy = wrapperCache.get(value);
174
+ if (cachedValueProxy)
175
+ return cachedValueProxy;
176
+ // Calculate path for the element
177
+ const propKey = String(prop);
178
+ const pathKey = path.length > 0 ? `${path.join('.')}.${propKey}` : propKey; // Fix pathKey generation for index 0
179
+ let newPath = getPathConcat(pathKey);
180
+ if (newPath === undefined) {
181
+ newPath = path.concat(propKey);
182
+ setPathConcat(pathKey, newPath);
183
+ }
184
+ // Wrap based on type (no longer passing seen)
185
+ if (Array.isArray(value))
186
+ return wrapArray(value, emit, newPath);
187
+ if (value instanceof Map)
188
+ return wrapMap(value, emit, newPath);
189
+ if (value instanceof Set)
190
+ return wrapSet(value, emit, newPath);
191
+ if (value instanceof Date)
192
+ return new Date(value.getTime()); // Dates are not proxied
193
+ // Default to reactive for plain objects
194
+ return reactive(value, emit, newPath);
195
+ }
196
+ // For non-numeric properties or properties that aren't objects, return value directly
197
+ // Also handle functions bound to the target
198
+ if (typeof value === 'function') {
199
+ return value.bind(target);
200
+ }
201
+ return value;
202
+ },
203
+ set(target, prop, value, receiver) {
204
+ const oldValue = target[prop];
205
+ // Fast path for primitive equality
206
+ if (oldValue === value)
207
+ return true;
208
+ // Deep equality check with new WeakMap
209
+ if (isObject(oldValue) && isObject(value) && deepEqual(oldValue, value, new WeakMap()))
210
+ return true;
211
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
212
+ const result = Reflect.set(target, prop, value, receiver);
213
+ const isNumericIndex = typeof prop === 'number' || (typeof prop === 'string' && !isNaN(parseInt(String(prop))));
214
+ if (result && (!descriptor || !descriptor.set || isNumericIndex)) {
215
+ const propKey = String(prop);
216
+ const pathKey = path.length > 0 ? `${path.join('.')}.${propKey}` : propKey; // Fix pathKey generation for index 0
217
+ let newPath = getPathConcat(pathKey);
218
+ if (newPath === undefined) {
219
+ newPath = path.concat(propKey);
220
+ setPathConcat(pathKey, newPath);
221
+ }
222
+ const event = {
223
+ action: 'set',
224
+ path: newPath,
225
+ oldValue,
226
+ newValue: value
227
+ };
228
+ emit(event);
229
+ trigger(target, prop);
230
+ }
231
+ return result;
232
+ }
233
+ });
234
+ // Cache the newly created proxy before returning
235
+ wrapperCache.set(arr, proxy);
236
+ return proxy;
237
+ }
@@ -0,0 +1,2 @@
1
+ import { EmitFunction, Path } from './types';
2
+ export declare function wrapMap<K, V>(map: Map<K, V>, emit: EmitFunction, path: Path): Map<K, V>;
@@ -0,0 +1,241 @@
1
+ import { deepEqual, getPathConcat, setPathConcat, wrapperCache } from './utils';
2
+ import { reactive } from './reactive';
3
+ import { wrapArray } from './wrapArray';
4
+ import { wrapSet } from './wrapSet';
5
+ import { track, trigger } from './watchEffect';
6
+ export function wrapMap(map, emit, path) {
7
+ // Check wrapper cache first
8
+ const cachedProxy = wrapperCache.get(map);
9
+ if (cachedProxy)
10
+ return cachedProxy;
11
+ const proxy = new Proxy(map, {
12
+ get(target, prop, receiver) {
13
+ // Original track call - Keep for properties not explicitly handled
14
+ track(target, prop);
15
+ // --- Specific Method Handlers ---
16
+ if (prop === 'set') {
17
+ return function (key, value) {
18
+ const existed = target.has(key);
19
+ const oldValue = target.get(key);
20
+ const oldSize = target.size;
21
+ if (oldValue === value)
22
+ return receiver;
23
+ if (oldValue && typeof oldValue === 'object' && value && typeof value === 'object' && deepEqual(oldValue, value, new WeakMap()))
24
+ return receiver;
25
+ target.set(key, value);
26
+ const newSize = target.size;
27
+ const pathKey = path.join('.');
28
+ let cachedPath = getPathConcat(pathKey);
29
+ if (cachedPath === undefined) {
30
+ cachedPath = path;
31
+ setPathConcat(pathKey, cachedPath);
32
+ }
33
+ const event = {
34
+ action: 'map-set',
35
+ path: cachedPath,
36
+ key,
37
+ oldValue,
38
+ newValue: value
39
+ };
40
+ emit(event);
41
+ if (!existed) {
42
+ trigger(target, Symbol.iterator);
43
+ if (oldSize !== newSize) {
44
+ trigger(target, 'size');
45
+ }
46
+ }
47
+ else {
48
+ trigger(target, String(key));
49
+ }
50
+ return receiver;
51
+ };
52
+ }
53
+ if (prop === 'delete') {
54
+ return function (key) {
55
+ const existed = target.has(key);
56
+ if (!existed)
57
+ return false;
58
+ const oldValue = target.get(key);
59
+ const oldSize = target.size;
60
+ const result = target.delete(key);
61
+ const newSize = target.size;
62
+ if (result) {
63
+ const pathKey = path.join('.');
64
+ let cachedPath = getPathConcat(pathKey);
65
+ if (cachedPath === undefined) {
66
+ cachedPath = path;
67
+ setPathConcat(pathKey, cachedPath);
68
+ }
69
+ const event = {
70
+ action: 'map-delete',
71
+ path: cachedPath,
72
+ key,
73
+ oldValue
74
+ };
75
+ emit(event);
76
+ trigger(target, Symbol.iterator);
77
+ if (oldSize !== newSize) {
78
+ trigger(target, 'size');
79
+ }
80
+ }
81
+ return result;
82
+ };
83
+ }
84
+ if (prop === 'clear') {
85
+ return function () {
86
+ const oldSize = target.size;
87
+ if (oldSize === 0)
88
+ return;
89
+ target.clear();
90
+ const newSize = target.size;
91
+ const event = {
92
+ action: 'map-clear',
93
+ path: path,
94
+ key: null
95
+ };
96
+ emit(event);
97
+ // Clear triggers both iterator and size
98
+ trigger(target, Symbol.iterator);
99
+ if (oldSize !== newSize) {
100
+ trigger(target, 'size');
101
+ }
102
+ };
103
+ }
104
+ if (prop === 'get') {
105
+ // Return function that tracks the specific key upon execution
106
+ return function (key) {
107
+ // Track access to this specific key when 'get' is called
108
+ track(target, String(key));
109
+ const value = target.get(key);
110
+ if (!value || typeof value !== 'object')
111
+ return value; // Fast path for non-objects
112
+ // Check wrapper cache for the value
113
+ const cachedValueProxy = wrapperCache.get(value);
114
+ if (cachedValueProxy)
115
+ return cachedValueProxy;
116
+ // Calculate path for the value
117
+ const keyString = String(key);
118
+ const pathKey = path.length > 0 ? `${path.join('.')}.${keyString}` : keyString;
119
+ let newPath = getPathConcat(pathKey);
120
+ if (newPath === undefined) {
121
+ newPath = path.concat(keyString);
122
+ setPathConcat(pathKey, newPath);
123
+ }
124
+ // Wrap based on type (no longer passing seen)
125
+ if (value instanceof Map)
126
+ return wrapMap(value, emit, newPath);
127
+ if (value instanceof Set)
128
+ return wrapSet(value, emit, newPath);
129
+ if (Array.isArray(value))
130
+ return wrapArray(value, emit, newPath);
131
+ if (value instanceof Date)
132
+ return new Date(value.getTime()); // Dates are not proxied
133
+ // Default to reactive for plain objects
134
+ return reactive(value, emit, newPath);
135
+ };
136
+ }
137
+ if (prop === 'has') {
138
+ // Track dependency on iteration/structure when 'has' is accessed
139
+ track(target, Symbol.iterator);
140
+ return function (key) {
141
+ // Track specific key when 'has' is called
142
+ track(target, String(key));
143
+ return target.has(key);
144
+ }.bind(target); // Bind to original target for correct 'this'
145
+ }
146
+ // --- Iteration Methods ---
147
+ if (prop === Symbol.iterator || prop === 'entries' || prop === 'values' || prop === 'keys' || prop === 'forEach') {
148
+ // Track dependency on iteration when these methods are accessed
149
+ track(target, Symbol.iterator);
150
+ const originalMethod = Reflect.get(target, prop, receiver);
151
+ // Return custom iterators/forEach that wrap values
152
+ if (prop === 'forEach') {
153
+ return (callbackfn, thisArg) => {
154
+ // Use proxied .entries() to get wrapped values + tracking
155
+ const entriesIterator = proxy.entries();
156
+ for (const [key, value] of entriesIterator) {
157
+ callbackfn.call(thisArg, value, key, proxy);
158
+ }
159
+ };
160
+ }
161
+ // Handle Symbol.iterator, entries, values, keys
162
+ return function* (...args) {
163
+ const iterator = originalMethod.apply(target, args);
164
+ for (const entry of iterator) {
165
+ let keyToWrap = entry; // Default for keys()
166
+ let valueToWrap = entry; // Default for values()
167
+ let isEntry = false;
168
+ if (prop === 'entries' || prop === Symbol.iterator) {
169
+ keyToWrap = entry[0];
170
+ valueToWrap = entry[1];
171
+ isEntry = true;
172
+ }
173
+ // Wrap key if object
174
+ let wrappedKey = keyToWrap;
175
+ if (isEntry && keyToWrap && typeof keyToWrap === 'object') {
176
+ const pathKey = path.length > 0 ? `${path.join('.')}.${String(keyToWrap)}` : String(keyToWrap); // Or find better key path?
177
+ let keyPath = getPathConcat(pathKey); // Reuse paths where possible
178
+ if (keyPath === undefined) {
179
+ keyPath = path.concat(String(keyToWrap)); // Simplification for key path
180
+ setPathConcat(pathKey, keyPath);
181
+ }
182
+ // TODO: Decide if Map keys should be deeply reactive
183
+ wrappedKey = reactive(keyToWrap, emit, keyPath);
184
+ }
185
+ // Wrap value if object
186
+ let wrappedValue = valueToWrap;
187
+ if (valueToWrap && typeof valueToWrap === 'object') {
188
+ const cachedValueProxy = wrapperCache.get(valueToWrap);
189
+ if (cachedValueProxy) {
190
+ wrappedValue = cachedValueProxy;
191
+ }
192
+ else {
193
+ const keyString = String(keyToWrap);
194
+ const pathKey = path.length > 0 ? `${path.join('.')}.${keyString}` : keyString;
195
+ let newPath = getPathConcat(pathKey);
196
+ if (newPath === undefined) {
197
+ newPath = path.concat(keyString);
198
+ setPathConcat(pathKey, newPath);
199
+ }
200
+ if (valueToWrap instanceof Map)
201
+ wrappedValue = wrapMap(valueToWrap, emit, newPath);
202
+ else if (valueToWrap instanceof Set)
203
+ wrappedValue = wrapSet(valueToWrap, emit, newPath);
204
+ else if (Array.isArray(valueToWrap))
205
+ wrappedValue = wrapArray(valueToWrap, emit, newPath);
206
+ else if (valueToWrap instanceof Date)
207
+ wrappedValue = new Date(valueToWrap.getTime());
208
+ else
209
+ wrappedValue = reactive(valueToWrap, emit, newPath);
210
+ }
211
+ }
212
+ // Yield based on iterator type
213
+ if (prop === 'entries' || prop === Symbol.iterator) {
214
+ yield [wrappedKey, wrappedValue];
215
+ }
216
+ else if (prop === 'values') {
217
+ yield wrappedValue;
218
+ }
219
+ else { // keys
220
+ yield wrappedKey;
221
+ }
222
+ }
223
+ };
224
+ }
225
+ // --- Size Property ---
226
+ if (prop === 'size') {
227
+ // Explicitly track size access
228
+ track(target, 'size');
229
+ // Return the size directly from the target to avoid potential 'this' issues with Reflect.get
230
+ return target.size;
231
+ }
232
+ // --- Fallback for other properties/methods ---
233
+ const value = Reflect.get(target, prop, receiver);
234
+ // For non-function properties or bound functions, return the value as is.
235
+ return value;
236
+ }
237
+ });
238
+ // Cache the newly created proxy before returning
239
+ wrapperCache.set(map, proxy);
240
+ return proxy;
241
+ }
@@ -0,0 +1,2 @@
1
+ import { EmitFunction, Path } from './types';
2
+ export declare function wrapSet<T>(set: Set<T>, emit: EmitFunction, path: Path): Set<T>;
@@ -0,0 +1,184 @@
1
+ import { getPathConcat, setPathConcat, wrapperCache } from './utils';
2
+ import { reactive } from './reactive';
3
+ import { wrapArray } from './wrapArray';
4
+ import { wrapMap } from './wrapMap';
5
+ import { track, trigger } from './watchEffect';
6
+ export function wrapSet(set, emit, path) {
7
+ // Check wrapper cache first
8
+ const cachedProxy = wrapperCache.get(set);
9
+ if (cachedProxy)
10
+ return cachedProxy;
11
+ const proxy = new Proxy(set, {
12
+ get(target, prop, receiver) {
13
+ // Original track call
14
+ track(target, prop);
15
+ if (prop === 'add') {
16
+ return function (value) {
17
+ const existed = target.has(value);
18
+ const oldSize = target.size;
19
+ if (!existed) {
20
+ target.add(value);
21
+ const newSize = target.size;
22
+ const event = {
23
+ action: 'set-add',
24
+ path: path,
25
+ value: value
26
+ };
27
+ emit(event);
28
+ trigger(target, Symbol.iterator);
29
+ if (oldSize !== newSize) {
30
+ trigger(target, 'size');
31
+ }
32
+ }
33
+ return receiver;
34
+ };
35
+ }
36
+ if (prop === 'delete') {
37
+ return function (value) {
38
+ const existed = target.has(value);
39
+ const oldSize = target.size;
40
+ if (existed) {
41
+ const oldValue = value; // The value being deleted is the oldValue
42
+ const result = target.delete(value);
43
+ const newSize = target.size;
44
+ if (result) {
45
+ const event = {
46
+ action: 'set-delete',
47
+ path: path,
48
+ value: value, // value being deleted
49
+ oldValue: oldValue // Add oldValue to the event
50
+ };
51
+ emit(event);
52
+ trigger(target, Symbol.iterator);
53
+ if (oldSize !== newSize) {
54
+ trigger(target, 'size');
55
+ }
56
+ }
57
+ return result;
58
+ }
59
+ return false;
60
+ };
61
+ }
62
+ if (prop === 'clear') {
63
+ return function () {
64
+ const oldSize = target.size;
65
+ if (oldSize === 0)
66
+ return;
67
+ target.clear();
68
+ const newSize = target.size;
69
+ const event = {
70
+ action: 'set-clear', // Create 'set-clear' action
71
+ path: path,
72
+ value: null
73
+ };
74
+ emit(event);
75
+ // Clear triggers both iterator and size
76
+ trigger(target, Symbol.iterator);
77
+ if (oldSize !== newSize) {
78
+ trigger(target, 'size');
79
+ }
80
+ };
81
+ }
82
+ if (prop === 'has') {
83
+ // 'has' depends on the specific value and iteration/size
84
+ track(target, Symbol.iterator); // Track iteration implicitly
85
+ return function (value) {
86
+ // Also track the specific value when 'has' is called
87
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'symbol') {
88
+ track(target, String(value));
89
+ }
90
+ // How to track non-primitive values?
91
+ // We might need a different strategy for object keys in Sets/Maps if deep reactivity on keys is needed
92
+ return target.has(value);
93
+ }.bind(target); // Bind to original target
94
+ }
95
+ // Handle iteration methods - custom implementation already tracks
96
+ if (prop === 'values' || prop === Symbol.iterator || prop === 'entries' || prop === 'keys' || prop === 'forEach') {
97
+ // Track dependency on iteration
98
+ track(target, Symbol.iterator);
99
+ const originalMethod = Reflect.get(target, prop, receiver);
100
+ // Return a function that yields wrapped values (or calls forEach)
101
+ if (prop === 'forEach') {
102
+ return (callbackfn, thisArg) => {
103
+ // Simplified forEach based on values iterator
104
+ const valuesIterator = proxy.values(); // Use the proxied values() to get wrapped values + tracking
105
+ for (const value of valuesIterator) {
106
+ callbackfn.call(thisArg, value, value, proxy);
107
+ }
108
+ };
109
+ }
110
+ // Handle Symbol.iterator, values, keys, entries
111
+ return function* (...args) {
112
+ let index = 0; // Index for path generation
113
+ const iterator = originalMethod.apply(target, args);
114
+ for (const entry of iterator) {
115
+ let valueToWrap = entry; // Default for values(), keys()
116
+ let mapKey = undefined; // For entries()
117
+ // For entries(), the entry is [key, value]
118
+ if (prop === 'entries') {
119
+ mapKey = entry[0];
120
+ valueToWrap = entry[1];
121
+ }
122
+ // Track access to each element during iteration
123
+ track(target, String(index));
124
+ let wrappedValue = valueToWrap;
125
+ if (valueToWrap && typeof valueToWrap === 'object') {
126
+ // Check wrapper cache first
127
+ const cachedValueProxy = wrapperCache.get(valueToWrap);
128
+ if (cachedValueProxy) {
129
+ wrappedValue = cachedValueProxy;
130
+ }
131
+ else {
132
+ // Calculate path using index (or mapKey if available and primitive)
133
+ const keyForPath = (mapKey !== undefined && (typeof mapKey !== 'object') ? String(mapKey) : String(index));
134
+ const pathKey = path.length > 0 ? `${path.join('.')}.${keyForPath}` : keyForPath;
135
+ let newPath = getPathConcat(pathKey);
136
+ if (newPath === undefined) {
137
+ newPath = path.concat(keyForPath);
138
+ setPathConcat(pathKey, newPath);
139
+ }
140
+ // Wrap based on type
141
+ if (valueToWrap instanceof Map)
142
+ wrappedValue = wrapMap(valueToWrap, emit, newPath);
143
+ else if (valueToWrap instanceof Set)
144
+ wrappedValue = wrapSet(valueToWrap, emit, newPath);
145
+ else if (Array.isArray(valueToWrap))
146
+ wrappedValue = wrapArray(valueToWrap, emit, newPath);
147
+ else if (valueToWrap instanceof Date)
148
+ wrappedValue = new Date(valueToWrap.getTime());
149
+ else
150
+ wrappedValue = reactive(valueToWrap, emit, newPath);
151
+ // Note: We don't cache the *wrapped* value here, wrap functions handle caching
152
+ }
153
+ }
154
+ // Yield the original or wrapped value/entry
155
+ if (prop === 'entries') {
156
+ yield [mapKey, wrappedValue];
157
+ }
158
+ else {
159
+ yield wrappedValue;
160
+ }
161
+ index++;
162
+ }
163
+ };
164
+ }
165
+ if (prop === 'size') {
166
+ // Explicitly track size access
167
+ track(target, 'size');
168
+ // Return the size directly from the target to avoid potential 'this' issues with Reflect.get
169
+ return target.size;
170
+ }
171
+ // Fallback for other properties (should be minimal for Set)
172
+ const value = Reflect.get(target, prop, receiver);
173
+ // REMOVED - Tracking handled above
174
+ // if (prop === 'size') { ... }
175
+ // If the property is a function (and not handled above), return it directly.
176
+ // Reflect.get preserves the correct 'this' binding (receiver).
177
+ // REMOVED: return value.bind(target);
178
+ return value;
179
+ }
180
+ });
181
+ // Cache the newly created proxy before returning
182
+ wrapperCache.set(set, proxy);
183
+ return proxy;
184
+ }