mutts 1.0.2 → 1.0.4
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/README.md +14 -6
- package/dist/chunks/{_tslib-C-cuVLvZ.js → _tslib-BgjropY9.js} +9 -1
- package/dist/chunks/_tslib-BgjropY9.js.map +1 -0
- package/dist/chunks/{_tslib-CMEnd0VE.esm.js → _tslib-Mzh1rNsX.esm.js} +9 -2
- package/dist/chunks/_tslib-Mzh1rNsX.esm.js.map +1 -0
- package/dist/chunks/{decorator-D4DU97Zg.js → decorator-DLvrD0UF.js} +42 -19
- package/dist/chunks/decorator-DLvrD0UF.js.map +1 -0
- package/dist/chunks/{decorator-GnHw1Az7.esm.js → decorator-DqiszP7i.esm.js} +42 -19
- package/dist/chunks/decorator-DqiszP7i.esm.js.map +1 -0
- package/dist/chunks/index-79Kk8D6e.esm.js +4857 -0
- package/dist/chunks/index-79Kk8D6e.esm.js.map +1 -0
- package/dist/chunks/index-GRBSx0mB.js +4908 -0
- package/dist/chunks/index-GRBSx0mB.js.map +1 -0
- package/dist/decorator.esm.js +1 -1
- package/dist/decorator.js +1 -1
- package/dist/destroyable.d.ts +1 -1
- package/dist/destroyable.esm.js +1 -1
- package/dist/destroyable.esm.js.map +1 -1
- package/dist/destroyable.js +1 -1
- package/dist/destroyable.js.map +1 -1
- package/dist/devtools/devtools.html +9 -0
- package/dist/devtools/devtools.js +5 -0
- package/dist/devtools/devtools.js.map +1 -0
- package/dist/devtools/manifest.json +8 -0
- package/dist/devtools/panel.css +72 -0
- package/dist/devtools/panel.html +31 -0
- package/dist/devtools/panel.js +13048 -0
- package/dist/devtools/panel.js.map +1 -0
- package/dist/eventful.esm.js +1 -1
- package/dist/eventful.js +1 -1
- package/dist/index.d.ts +18 -63
- package/dist/index.esm.js +4 -4
- package/dist/index.js +37 -11
- package/dist/index.js.map +1 -1
- package/dist/indexable.d.ts +187 -1
- package/dist/indexable.esm.js +197 -3
- package/dist/indexable.esm.js.map +1 -1
- package/dist/indexable.js +198 -2
- package/dist/indexable.js.map +1 -1
- package/dist/mutts.umd.js +1 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/promiseChain.esm.js.map +1 -1
- package/dist/promiseChain.js.map +1 -1
- package/dist/reactive.d.ts +602 -97
- package/dist/reactive.esm.js +3 -3
- package/dist/reactive.js +32 -10
- package/dist/reactive.js.map +1 -1
- package/dist/std-decorators.esm.js +1 -1
- package/dist/std-decorators.js +1 -1
- package/docs/ai/api-reference.md +133 -0
- package/docs/ai/manual.md +105 -0
- package/docs/iterableWeak.md +646 -0
- package/docs/reactive/advanced.md +1280 -0
- package/docs/reactive/collections.md +767 -0
- package/docs/reactive/core.md +973 -0
- package/docs/reactive.md +21 -9545
- package/package.json +18 -5
- package/src/decorator.ts +266 -0
- package/src/destroyable.ts +199 -0
- package/src/eventful.ts +77 -0
- package/src/index.d.ts +9 -0
- package/src/index.ts +9 -0
- package/src/indexable.ts +484 -0
- package/src/introspection.ts +59 -0
- package/src/iterableWeak.ts +233 -0
- package/src/mixins.ts +123 -0
- package/src/promiseChain.ts +110 -0
- package/src/reactive/array.ts +414 -0
- package/src/reactive/change.ts +134 -0
- package/src/reactive/debug.ts +517 -0
- package/src/reactive/deep-touch.ts +268 -0
- package/src/reactive/deep-watch-state.ts +82 -0
- package/src/reactive/deep-watch.ts +168 -0
- package/src/reactive/effect-context.ts +94 -0
- package/src/reactive/effects.ts +1345 -0
- package/src/reactive/index.ts +76 -0
- package/src/reactive/interface.ts +223 -0
- package/src/reactive/map.ts +171 -0
- package/src/reactive/mapped.ts +130 -0
- package/src/reactive/memoize.ts +107 -0
- package/src/reactive/non-reactive-state.ts +49 -0
- package/src/reactive/non-reactive.ts +43 -0
- package/src/reactive/project.project.md +93 -0
- package/src/reactive/project.ts +335 -0
- package/src/reactive/proxy-state.ts +27 -0
- package/src/reactive/proxy.ts +289 -0
- package/src/reactive/record.ts +196 -0
- package/src/reactive/register.ts +421 -0
- package/src/reactive/set.ts +144 -0
- package/src/reactive/tracking.ts +101 -0
- package/src/reactive/types.ts +358 -0
- package/src/reactive/zone.ts +208 -0
- package/src/std-decorators.ts +217 -0
- package/src/utils.ts +117 -0
- package/dist/chunks/_tslib-C-cuVLvZ.js.map +0 -1
- package/dist/chunks/_tslib-CMEnd0VE.esm.js.map +0 -1
- package/dist/chunks/decorator-D4DU97Zg.js.map +0 -1
- package/dist/chunks/decorator-GnHw1Az7.esm.js.map +0 -1
- package/dist/chunks/index-DBScoeCX.esm.js +0 -1960
- package/dist/chunks/index-DBScoeCX.esm.js.map +0 -1
- package/dist/chunks/index-DOTmXL89.js +0 -1983
- package/dist/chunks/index-DOTmXL89.js.map +0 -1
|
@@ -1,1960 +0,0 @@
|
|
|
1
|
-
import { i as isConstructor, R as ReflectGet, d as decorator, c as isObject, a as ReflectSet, b as isOwnAccessor, r as renamed } from './decorator-GnHw1Az7.esm.js';
|
|
2
|
-
import { a as __setFunctionName, b as __esDecorate, c as __runInitializers } from './_tslib-CMEnd0VE.esm.js';
|
|
3
|
-
import { Indexable } from '../indexable.esm.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Creates a mixin that can be used both as a class (extends) and as a function (mixin)
|
|
7
|
-
*
|
|
8
|
-
* This function supports:
|
|
9
|
-
* - Using mixins as base classes: `class MyClass extends MyMixin`
|
|
10
|
-
* - Using mixins as functions: `class MyClass extends MyMixin(SomeBase)`
|
|
11
|
-
* - Composing mixins: `const Composed = MixinA(MixinB)`
|
|
12
|
-
* - Type-safe property inference for all patterns
|
|
13
|
-
*
|
|
14
|
-
* @param mixinFunction - The function that creates the mixin
|
|
15
|
-
* @param unwrapFunction - Optional function to unwrap reactive objects for method calls
|
|
16
|
-
* @returns A mixin that can be used both as a class and as a function
|
|
17
|
-
*/
|
|
18
|
-
function mixin(mixinFunction, unwrapFunction) {
|
|
19
|
-
/**
|
|
20
|
-
* Cache for mixin results to ensure the same base class always returns the same mixed class
|
|
21
|
-
*/
|
|
22
|
-
const mixinCache = new WeakMap();
|
|
23
|
-
// Apply the mixin to Object as the base class
|
|
24
|
-
const MixedBase = mixinFunction(Object);
|
|
25
|
-
mixinCache.set(Object, MixedBase);
|
|
26
|
-
// Create the proxy that handles both constructor and function calls
|
|
27
|
-
return new Proxy(MixedBase, {
|
|
28
|
-
// Handle `MixinClass(SomeBase)` - use as mixin function
|
|
29
|
-
apply(_target, _thisArg, args) {
|
|
30
|
-
if (args.length === 0) {
|
|
31
|
-
throw new Error('Mixin requires a base class');
|
|
32
|
-
}
|
|
33
|
-
const baseClass = args[0];
|
|
34
|
-
if (typeof baseClass !== 'function') {
|
|
35
|
-
throw new Error('Mixin requires a constructor function');
|
|
36
|
-
}
|
|
37
|
-
// Check if it's a valid constructor or a mixin
|
|
38
|
-
if (!isConstructor(baseClass) &&
|
|
39
|
-
!(baseClass && typeof baseClass === 'function' && baseClass.prototype)) {
|
|
40
|
-
throw new Error('Mixin requires a valid constructor');
|
|
41
|
-
}
|
|
42
|
-
// Check cache first
|
|
43
|
-
const cached = mixinCache.get(baseClass);
|
|
44
|
-
if (cached) {
|
|
45
|
-
return cached;
|
|
46
|
-
}
|
|
47
|
-
let usedBase = baseClass;
|
|
48
|
-
if (unwrapFunction) {
|
|
49
|
-
// Create a proxied base class that handles method unwrapping
|
|
50
|
-
const ProxiedBaseClass = class extends baseClass {
|
|
51
|
-
};
|
|
52
|
-
// Proxy the prototype methods to handle unwrapping
|
|
53
|
-
const originalPrototype = baseClass.prototype;
|
|
54
|
-
const proxiedPrototype = new Proxy(originalPrototype, {
|
|
55
|
-
get(target, prop, receiver) {
|
|
56
|
-
const value = ReflectGet(target, prop, receiver);
|
|
57
|
-
// Only wrap methods that are likely to access private fields
|
|
58
|
-
// Skip symbols and special properties that the reactive system needs
|
|
59
|
-
if (typeof value === 'function' &&
|
|
60
|
-
typeof prop === 'string' &&
|
|
61
|
-
!['constructor', 'toString', 'valueOf'].includes(prop)) {
|
|
62
|
-
// Return a wrapped version that uses unwrapped context
|
|
63
|
-
return function (...args) {
|
|
64
|
-
// Use the unwrapping function if provided, otherwise use this
|
|
65
|
-
const context = unwrapFunction(this);
|
|
66
|
-
return value.apply(context, args);
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
return value;
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
// Set the proxied prototype
|
|
73
|
-
Object.setPrototypeOf(ProxiedBaseClass.prototype, proxiedPrototype);
|
|
74
|
-
usedBase = ProxiedBaseClass;
|
|
75
|
-
}
|
|
76
|
-
// Create the mixed class using the proxied base class
|
|
77
|
-
const mixedClass = mixinFunction(usedBase);
|
|
78
|
-
// Cache the result
|
|
79
|
-
mixinCache.set(baseClass, mixedClass);
|
|
80
|
-
return mixedClass;
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// biome-ignore-all lint/suspicious/noConfusingVoidType: Type 'void' is not assignable to type 'ScopedCallback | undefined'.
|
|
86
|
-
// Argument of type '() => void' is not assignable to parameter of type '(dep: DependencyFunction) => ScopedCallback | undefined'.
|
|
87
|
-
// Track which effects are watching which reactive objects for cleanup
|
|
88
|
-
const effectToReactiveObjects = new WeakMap();
|
|
89
|
-
// Track object -> proxy and proxy -> object relationships
|
|
90
|
-
const objectToProxy = new WeakMap();
|
|
91
|
-
const proxyToObject = new WeakMap();
|
|
92
|
-
// Deep watching data structures
|
|
93
|
-
// Track which objects contain which other objects (back-references)
|
|
94
|
-
const objectParents = new WeakMap();
|
|
95
|
-
// Track which objects have deep watchers
|
|
96
|
-
const objectsWithDeepWatchers = new WeakSet();
|
|
97
|
-
// Track deep watchers per object
|
|
98
|
-
const deepWatchers = new WeakMap();
|
|
99
|
-
// Track which effects are doing deep watching
|
|
100
|
-
const effectToDeepWatchedObjects = new WeakMap();
|
|
101
|
-
// Track objects that should never be reactive and cannot be modified
|
|
102
|
-
/**
|
|
103
|
-
* WeakSet containing objects that should never be made reactive
|
|
104
|
-
*/
|
|
105
|
-
const nonReactiveObjects = new WeakSet();
|
|
106
|
-
const absent = Symbol('absent');
|
|
107
|
-
/**
|
|
108
|
-
* Converts an iterator to a generator that yields reactive values
|
|
109
|
-
*/
|
|
110
|
-
function* makeReactiveIterator(iterator) {
|
|
111
|
-
let result = iterator.next();
|
|
112
|
-
while (!result.done) {
|
|
113
|
-
yield reactive(result.value);
|
|
114
|
-
result = iterator.next();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Converts an iterator of key-value pairs to a generator that yields reactive key-value pairs
|
|
119
|
-
*/
|
|
120
|
-
function* makeReactiveEntriesIterator(iterator) {
|
|
121
|
-
let result = iterator.next();
|
|
122
|
-
while (!result.done) {
|
|
123
|
-
const [key, value] = result.value;
|
|
124
|
-
yield [reactive(key), reactive(value)];
|
|
125
|
-
result = iterator.next();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
// Track effects per reactive object and property
|
|
129
|
-
const watchers = new WeakMap();
|
|
130
|
-
/**
|
|
131
|
-
* Object containing internal reactive system state for debugging and profiling
|
|
132
|
-
*/
|
|
133
|
-
const profileInfo = {
|
|
134
|
-
objectToProxy,
|
|
135
|
-
proxyToObject,
|
|
136
|
-
effectToReactiveObjects,
|
|
137
|
-
watchers,
|
|
138
|
-
objectParents,
|
|
139
|
-
objectsWithDeepWatchers,
|
|
140
|
-
deepWatchers,
|
|
141
|
-
effectToDeepWatchedObjects,
|
|
142
|
-
nonReactiveObjects,
|
|
143
|
-
};
|
|
144
|
-
// Track native reactivity
|
|
145
|
-
const nativeReactive = Symbol('native-reactive');
|
|
146
|
-
/**
|
|
147
|
-
* Symbol to mark individual objects as non-reactive
|
|
148
|
-
*/
|
|
149
|
-
const nonReactiveMark = Symbol('non-reactive');
|
|
150
|
-
/**
|
|
151
|
-
* Symbol to mark class properties as non-reactive
|
|
152
|
-
*/
|
|
153
|
-
const unreactiveProperties = Symbol('unreactive-properties');
|
|
154
|
-
/**
|
|
155
|
-
* Symbol for prototype forwarding in reactive objects
|
|
156
|
-
*/
|
|
157
|
-
const prototypeForwarding = Symbol('prototype-forwarding');
|
|
158
|
-
/**
|
|
159
|
-
* Symbol representing all properties in reactive tracking
|
|
160
|
-
*/
|
|
161
|
-
const allProps = Symbol('all-props');
|
|
162
|
-
// Symbol to mark functions with their root function
|
|
163
|
-
const rootFunction = Symbol('root-function');
|
|
164
|
-
/**
|
|
165
|
-
* Marks a function with its root function for effect tracking
|
|
166
|
-
* @param fn - The function to mark
|
|
167
|
-
* @param root - The root function
|
|
168
|
-
* @returns The marked function
|
|
169
|
-
*/
|
|
170
|
-
function markWithRoot(fn, root) {
|
|
171
|
-
// Mark fn with the new root
|
|
172
|
-
return Object.defineProperty(fn, rootFunction, {
|
|
173
|
-
value: getRoot(root),
|
|
174
|
-
writable: false,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Gets the root function of a function for effect tracking
|
|
179
|
-
* @param fn - The function to get the root of
|
|
180
|
-
* @returns The root function
|
|
181
|
-
*/
|
|
182
|
-
function getRoot(fn) {
|
|
183
|
-
return fn?.[rootFunction] || fn;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Error class for reactive system errors
|
|
187
|
-
*/
|
|
188
|
-
class ReactiveError extends Error {
|
|
189
|
-
constructor(message) {
|
|
190
|
-
super(message);
|
|
191
|
-
this.name = 'ReactiveError';
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
// biome-ignore-start lint/correctness/noUnusedFunctionParameters: Interface declaration with empty defaults
|
|
195
|
-
/**
|
|
196
|
-
* Global options for the reactive system
|
|
197
|
-
*/
|
|
198
|
-
const options = {
|
|
199
|
-
/**
|
|
200
|
-
* Debug purpose: called when an effect is entered
|
|
201
|
-
* @param effect - The effect that is entered
|
|
202
|
-
*/
|
|
203
|
-
enter: (effect) => { },
|
|
204
|
-
/**
|
|
205
|
-
* Debug purpose: called when an effect is left
|
|
206
|
-
* @param effect - The effect that is left
|
|
207
|
-
*/
|
|
208
|
-
leave: (effect) => { },
|
|
209
|
-
/**
|
|
210
|
-
* Debug purpose: called when an effect is chained
|
|
211
|
-
* @param target - The effect that is being triggered
|
|
212
|
-
* @param caller - The effect that is calling the target
|
|
213
|
-
*/
|
|
214
|
-
chain: (targets, caller) => { },
|
|
215
|
-
/**
|
|
216
|
-
* Debug purpose: called when an effect chain is started
|
|
217
|
-
* @param target - The effect that is being triggered
|
|
218
|
-
*/
|
|
219
|
-
beginChain: (targets) => { },
|
|
220
|
-
/**
|
|
221
|
-
* Debug purpose: called when an effect chain is ended
|
|
222
|
-
*/
|
|
223
|
-
endChain: () => { },
|
|
224
|
-
/**
|
|
225
|
-
* Debug purpose: called when an object is touched
|
|
226
|
-
* @param obj - The object that is touched
|
|
227
|
-
* @param evolution - The type of change
|
|
228
|
-
* @param props - The properties that changed
|
|
229
|
-
* @param deps - The dependencies that changed
|
|
230
|
-
*/
|
|
231
|
-
touched: (obj, evolution, props, deps) => { },
|
|
232
|
-
/**
|
|
233
|
-
* Debug purpose: maximum effect chain (like call stack max depth)
|
|
234
|
-
* Used to prevent infinite loops
|
|
235
|
-
* @default 100
|
|
236
|
-
*/
|
|
237
|
-
maxEffectChain: 100,
|
|
238
|
-
/**
|
|
239
|
-
* Debug purpose: maximum effect reaction (like call stack max depth)
|
|
240
|
-
* Used to prevent infinite loops
|
|
241
|
-
* @default 'throw'
|
|
242
|
-
*/
|
|
243
|
-
maxEffectReaction: 'throw',
|
|
244
|
-
/**
|
|
245
|
-
* Maximum depth for deep watching traversal
|
|
246
|
-
* Used to prevent infinite recursion in circular references
|
|
247
|
-
* @default 100
|
|
248
|
-
*/
|
|
249
|
-
maxDeepWatchDepth: 100,
|
|
250
|
-
/**
|
|
251
|
-
* Only react on instance members modification (not inherited properties)
|
|
252
|
-
* For instance, do not track class methods
|
|
253
|
-
* @default true
|
|
254
|
-
*/
|
|
255
|
-
instanceMembers: true,
|
|
256
|
-
/**
|
|
257
|
-
* Ignore accessors (getters and setters) and only track direct properties
|
|
258
|
-
* @default true
|
|
259
|
-
*/
|
|
260
|
-
ignoreAccessors: true,
|
|
261
|
-
// biome-ignore lint/suspicious/noConsole: This is the whole point here
|
|
262
|
-
warn: (...args) => console.warn(...args),
|
|
263
|
-
};
|
|
264
|
-
/**
|
|
265
|
-
* Registers a debug callback that is called when the current effect is triggered by a dependency change
|
|
266
|
-
*
|
|
267
|
-
* This function is useful for debugging purposes as it pin-points exactly which reactive property
|
|
268
|
-
* change triggered the effect. The callback receives information about:
|
|
269
|
-
* - The object that changed
|
|
270
|
-
* - The type of change (evolution)
|
|
271
|
-
* - The specific property that changed
|
|
272
|
-
*
|
|
273
|
-
* **Note:** The tracker callback is automatically removed after being called once. If you need
|
|
274
|
-
* to track multiple triggers, call `trackEffect` again within the effect.
|
|
275
|
-
*
|
|
276
|
-
* @param onTouch - Callback function that receives (obj, evolution, prop) when the effect is triggered
|
|
277
|
-
* @throws {Error} If called outside of an effect context
|
|
278
|
-
*
|
|
279
|
-
* @example
|
|
280
|
-
* ```typescript
|
|
281
|
-
* const state = reactive({ count: 0, name: 'John' })
|
|
282
|
-
*
|
|
283
|
-
* effect(() => {
|
|
284
|
-
* // Register a tracker to see what triggers this effect
|
|
285
|
-
* trackEffect((obj, evolution, prop) => {
|
|
286
|
-
* console.log(`Effect triggered by:`, {
|
|
287
|
-
* object: obj,
|
|
288
|
-
* change: evolution.type,
|
|
289
|
-
* property: prop
|
|
290
|
-
* })
|
|
291
|
-
* })
|
|
292
|
-
*
|
|
293
|
-
* // Access reactive properties
|
|
294
|
-
* console.log(state.count, state.name)
|
|
295
|
-
* })
|
|
296
|
-
*
|
|
297
|
-
* state.count = 5
|
|
298
|
-
* // Logs: Effect triggered by: { object: state, change: 'set', property: 'count' }
|
|
299
|
-
* ```
|
|
300
|
-
*/
|
|
301
|
-
function trackEffect(onTouch) {
|
|
302
|
-
if (!activeEffect)
|
|
303
|
-
throw new Error('Not in an effect');
|
|
304
|
-
if (!effectTrackers.has(activeEffect))
|
|
305
|
-
effectTrackers.set(activeEffect, new Set([onTouch]));
|
|
306
|
-
else
|
|
307
|
-
effectTrackers.get(activeEffect).add(onTouch);
|
|
308
|
-
}
|
|
309
|
-
const effectTrackers = new WeakMap();
|
|
310
|
-
function collectEffects(obj, evolution, effects, objectWatchers, ...keyChains) {
|
|
311
|
-
for (const keys of keyChains)
|
|
312
|
-
for (const key of keys) {
|
|
313
|
-
const deps = objectWatchers.get(key);
|
|
314
|
-
if (deps)
|
|
315
|
-
for (const effect of Array.from(deps)) {
|
|
316
|
-
effects.add(effect);
|
|
317
|
-
const trackers = effectTrackers.get(effect);
|
|
318
|
-
if (trackers) {
|
|
319
|
-
for (const tracker of trackers)
|
|
320
|
-
tracker(obj, evolution, key);
|
|
321
|
-
trackers.delete(effect);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Triggers effects for a single property change
|
|
328
|
-
* @param obj - The object that changed
|
|
329
|
-
* @param evolution - The type of change
|
|
330
|
-
* @param prop - The property that changed
|
|
331
|
-
*/
|
|
332
|
-
function touched1(obj, evolution, prop) {
|
|
333
|
-
touched(obj, evolution, [prop]);
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Triggers effects for property changes
|
|
337
|
-
* @param obj - The object that changed
|
|
338
|
-
* @param evolution - The type of change
|
|
339
|
-
* @param props - The properties that changed
|
|
340
|
-
*/
|
|
341
|
-
function touched(obj, evolution, props) {
|
|
342
|
-
obj = unwrap(obj);
|
|
343
|
-
addState(obj, evolution);
|
|
344
|
-
const objectWatchers = watchers.get(obj);
|
|
345
|
-
if (objectWatchers) {
|
|
346
|
-
// Note: we have to collect effects to remove duplicates in the specific case when no batch is running
|
|
347
|
-
const effects = new Set();
|
|
348
|
-
if (props) {
|
|
349
|
-
props = Array.from(props); // For debug purposes only
|
|
350
|
-
collectEffects(obj, evolution, effects, objectWatchers, [allProps], props);
|
|
351
|
-
}
|
|
352
|
-
else
|
|
353
|
-
collectEffects(obj, evolution, effects, objectWatchers, objectWatchers.keys());
|
|
354
|
-
options.touched(obj, evolution, props, effects);
|
|
355
|
-
batch(Array.from(effects));
|
|
356
|
-
}
|
|
357
|
-
// Bubble up changes if this object has deep watchers
|
|
358
|
-
if (objectsWithDeepWatchers.has(obj)) {
|
|
359
|
-
bubbleUpChange(obj);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
const states = new WeakMap();
|
|
363
|
-
function addState(obj, evolution) {
|
|
364
|
-
obj = unwrap(obj);
|
|
365
|
-
const next = {};
|
|
366
|
-
const state = getState(obj);
|
|
367
|
-
if (state)
|
|
368
|
-
Object.assign(state, { evolution, next });
|
|
369
|
-
states.set(obj, next);
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Gets the current state of a reactive object for evolution tracking
|
|
373
|
-
* @param obj - The reactive object
|
|
374
|
-
* @returns The current state object
|
|
375
|
-
*/
|
|
376
|
-
function getState(obj) {
|
|
377
|
-
obj = unwrap(obj);
|
|
378
|
-
let state = states.get(obj);
|
|
379
|
-
if (!state) {
|
|
380
|
-
state = {};
|
|
381
|
-
states.set(obj, state);
|
|
382
|
-
}
|
|
383
|
-
return state;
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Marks a property as a dependency of the current effect
|
|
387
|
-
* @param obj - The object containing the property
|
|
388
|
-
* @param prop - The property name (defaults to allProps)
|
|
389
|
-
*/
|
|
390
|
-
function dependant(obj, prop = allProps) {
|
|
391
|
-
obj = unwrap(obj);
|
|
392
|
-
if (activeEffect && (typeof prop !== 'symbol' || prop === allProps)) {
|
|
393
|
-
let objectWatchers = watchers.get(obj);
|
|
394
|
-
if (!objectWatchers) {
|
|
395
|
-
objectWatchers = new Map();
|
|
396
|
-
watchers.set(obj, objectWatchers);
|
|
397
|
-
}
|
|
398
|
-
let deps = objectWatchers.get(prop);
|
|
399
|
-
if (!deps) {
|
|
400
|
-
deps = new Set();
|
|
401
|
-
objectWatchers.set(prop, deps);
|
|
402
|
-
}
|
|
403
|
-
deps.add(activeEffect);
|
|
404
|
-
// Track which reactive objects this effect is watching
|
|
405
|
-
let effectObjects = effectToReactiveObjects.get(activeEffect);
|
|
406
|
-
if (!effectObjects) {
|
|
407
|
-
effectObjects = new Set();
|
|
408
|
-
effectToReactiveObjects.set(activeEffect, effectObjects);
|
|
409
|
-
}
|
|
410
|
-
effectObjects.add(obj);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
// Active effects to handle nested effects
|
|
414
|
-
let activeEffect;
|
|
415
|
-
// Parent effect used for lifecycle/cleanup relationships (can diverge later)
|
|
416
|
-
let parentEffect;
|
|
417
|
-
// Track currently executing effects to prevent re-execution
|
|
418
|
-
// These are all the effects triggered under `activeEffect`
|
|
419
|
-
let batchedEffects;
|
|
420
|
-
const batchCleanups = new Set();
|
|
421
|
-
/**
|
|
422
|
-
* Adds a cleanup function to be called when the current batch of effects completes
|
|
423
|
-
* @param cleanup - The cleanup function to add
|
|
424
|
-
*/
|
|
425
|
-
function addBatchCleanup(cleanup) {
|
|
426
|
-
if (!batchedEffects)
|
|
427
|
-
cleanup();
|
|
428
|
-
else
|
|
429
|
-
batchCleanups.add(cleanup);
|
|
430
|
-
}
|
|
431
|
-
// Track which sub-effects have been executed to prevent infinite loops
|
|
432
|
-
// These are all the effects triggered under `activeEffect` and all their sub-effects
|
|
433
|
-
function batch(effect, immediate) {
|
|
434
|
-
if (!Array.isArray(effect))
|
|
435
|
-
effect = [effect];
|
|
436
|
-
const roots = effect.map(getRoot);
|
|
437
|
-
if (batchedEffects) {
|
|
438
|
-
options?.chain(roots, getRoot(activeEffect));
|
|
439
|
-
for (let i = 0; i < effect.length; i++)
|
|
440
|
-
batchedEffects.set(roots[i], effect[i]);
|
|
441
|
-
if (immediate)
|
|
442
|
-
for (let i = 0; i < effect.length; i++)
|
|
443
|
-
try {
|
|
444
|
-
return effect[i]();
|
|
445
|
-
}
|
|
446
|
-
finally {
|
|
447
|
-
batchedEffects.delete(roots[i]);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
options.beginChain(roots);
|
|
452
|
-
const runEffects = [];
|
|
453
|
-
batchedEffects = new Map(roots.map((root, i) => [root, effect[i]]));
|
|
454
|
-
const firstReturn = {};
|
|
455
|
-
try {
|
|
456
|
-
while (batchedEffects.size) {
|
|
457
|
-
if (runEffects.length > options.maxEffectChain) {
|
|
458
|
-
switch (options.maxEffectReaction) {
|
|
459
|
-
case 'throw':
|
|
460
|
-
throw new ReactiveError('[reactive] Max effect chain reached');
|
|
461
|
-
case 'debug':
|
|
462
|
-
// biome-ignore lint/suspicious/noDebugger: This is the whole point here
|
|
463
|
-
debugger;
|
|
464
|
-
break;
|
|
465
|
-
case 'warn':
|
|
466
|
-
options.warn('[reactive] Max effect chain reached');
|
|
467
|
-
break;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
const [root, effect] = batchedEffects.entries().next().value;
|
|
471
|
-
runEffects.push(root);
|
|
472
|
-
const rv = effect();
|
|
473
|
-
if (!('value' in firstReturn))
|
|
474
|
-
firstReturn.value = rv;
|
|
475
|
-
batchedEffects.delete(root);
|
|
476
|
-
}
|
|
477
|
-
const cleanups = Array.from(batchCleanups);
|
|
478
|
-
batchCleanups.clear();
|
|
479
|
-
for (const cleanup of cleanups)
|
|
480
|
-
cleanup();
|
|
481
|
-
return firstReturn.value;
|
|
482
|
-
}
|
|
483
|
-
finally {
|
|
484
|
-
batchedEffects = undefined;
|
|
485
|
-
options.endChain();
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Decorator that makes methods atomic - batches all effects triggered within the method
|
|
491
|
-
*/
|
|
492
|
-
const atomic = decorator({
|
|
493
|
-
method(original) {
|
|
494
|
-
return function (...args) {
|
|
495
|
-
return batch(markWithRoot(() => original.apply(this, args), original), 'immediate');
|
|
496
|
-
};
|
|
497
|
-
},
|
|
498
|
-
default(original) {
|
|
499
|
-
return function (...args) {
|
|
500
|
-
return batch(markWithRoot(() => original.apply(this, args), original), 'immediate');
|
|
501
|
-
};
|
|
502
|
-
},
|
|
503
|
-
});
|
|
504
|
-
/**
|
|
505
|
-
* Executes a function with a specific effect context
|
|
506
|
-
* @param effect - The effect to use as context
|
|
507
|
-
* @param fn - The function to execute
|
|
508
|
-
* @param keepParent - Whether to keep the parent effect context
|
|
509
|
-
* @returns The result of the function
|
|
510
|
-
*/
|
|
511
|
-
function withEffect(effect, fn, keepParent) {
|
|
512
|
-
if (getRoot(effect) === getRoot(activeEffect))
|
|
513
|
-
return fn();
|
|
514
|
-
const oldActiveEffect = activeEffect;
|
|
515
|
-
const oldParentEffect = parentEffect;
|
|
516
|
-
activeEffect = effect;
|
|
517
|
-
parentEffect = effect;
|
|
518
|
-
try {
|
|
519
|
-
return fn();
|
|
520
|
-
}
|
|
521
|
-
finally {
|
|
522
|
-
activeEffect = oldActiveEffect;
|
|
523
|
-
parentEffect = oldParentEffect;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
//#endregion
|
|
527
|
-
//#region deep watching
|
|
528
|
-
/**
|
|
529
|
-
* Add a back-reference from child to parent
|
|
530
|
-
*/
|
|
531
|
-
function addBackReference(child, parent, prop) {
|
|
532
|
-
let parents = objectParents.get(child);
|
|
533
|
-
if (!parents) {
|
|
534
|
-
parents = new Set();
|
|
535
|
-
objectParents.set(child, parents);
|
|
536
|
-
}
|
|
537
|
-
parents.add({ parent, prop });
|
|
538
|
-
}
|
|
539
|
-
/**
|
|
540
|
-
* Remove a back-reference from child to parent
|
|
541
|
-
*/
|
|
542
|
-
function removeBackReference(child, parent, prop) {
|
|
543
|
-
const parents = objectParents.get(child);
|
|
544
|
-
if (parents) {
|
|
545
|
-
parents.delete({ parent, prop });
|
|
546
|
-
if (parents.size === 0) {
|
|
547
|
-
objectParents.delete(child);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* Check if an object needs back-references (has deep watchers or parents with deep watchers)
|
|
553
|
-
*/
|
|
554
|
-
function needsBackReferences(obj) {
|
|
555
|
-
return objectsWithDeepWatchers.has(obj) || hasParentWithDeepWatchers(obj);
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* Check if an object has any parent with deep watchers
|
|
559
|
-
*/
|
|
560
|
-
function hasParentWithDeepWatchers(obj) {
|
|
561
|
-
const parents = objectParents.get(obj);
|
|
562
|
-
if (!parents)
|
|
563
|
-
return false;
|
|
564
|
-
for (const { parent } of parents) {
|
|
565
|
-
if (objectsWithDeepWatchers.has(parent))
|
|
566
|
-
return true;
|
|
567
|
-
if (hasParentWithDeepWatchers(parent))
|
|
568
|
-
return true;
|
|
569
|
-
}
|
|
570
|
-
return false;
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Bubble up changes through the back-reference chain
|
|
574
|
-
*/
|
|
575
|
-
function bubbleUpChange(changedObject, evolution) {
|
|
576
|
-
const parents = objectParents.get(changedObject);
|
|
577
|
-
if (!parents)
|
|
578
|
-
return;
|
|
579
|
-
for (const { parent } of parents) {
|
|
580
|
-
// Trigger deep watchers on parent
|
|
581
|
-
const parentDeepWatchers = deepWatchers.get(parent);
|
|
582
|
-
if (parentDeepWatchers)
|
|
583
|
-
for (const watcher of parentDeepWatchers)
|
|
584
|
-
batch(watcher);
|
|
585
|
-
// Continue bubbling up
|
|
586
|
-
bubbleUpChange(parent);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Tracks property changes and manages back-references for deep watching
|
|
591
|
-
* @param obj - The object that changed
|
|
592
|
-
* @param prop - The property that changed
|
|
593
|
-
* @param oldVal - The old value
|
|
594
|
-
* @param newValue - The new value
|
|
595
|
-
*/
|
|
596
|
-
function track1(obj, prop, oldVal, newValue) {
|
|
597
|
-
// Manage back-references if this object has deep watchers
|
|
598
|
-
if (objectsWithDeepWatchers.has(obj)) {
|
|
599
|
-
// Remove old back-references
|
|
600
|
-
if (typeof oldVal === 'object' && oldVal !== null) {
|
|
601
|
-
removeBackReference(oldVal, obj, prop);
|
|
602
|
-
}
|
|
603
|
-
// Add new back-references
|
|
604
|
-
if (typeof newValue === 'object' && newValue !== null) {
|
|
605
|
-
const reactiveValue = reactive(newValue);
|
|
606
|
-
addBackReference(reactiveValue, obj, prop);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
return newValue;
|
|
610
|
-
}
|
|
611
|
-
//#endregion
|
|
612
|
-
const reactiveHandlers = {
|
|
613
|
-
[Symbol.toStringTag]: 'MutTs Reactive',
|
|
614
|
-
get(obj, prop, receiver) {
|
|
615
|
-
if (prop === nonReactiveMark)
|
|
616
|
-
return false;
|
|
617
|
-
// Check if this property is marked as unreactive
|
|
618
|
-
if (unwrap(obj)[unreactiveProperties]?.has(prop) || typeof prop === 'symbol')
|
|
619
|
-
return ReflectGet(obj, prop, receiver);
|
|
620
|
-
// Depend if...
|
|
621
|
-
if (!Reflect.has(receiver, prop) ||
|
|
622
|
-
(!(options.instanceMembers && !Object.hasOwn(receiver, prop)) &&
|
|
623
|
-
!(options.ignoreAccessors && isOwnAccessor(receiver, prop))))
|
|
624
|
-
dependant(obj, prop);
|
|
625
|
-
const value = ReflectGet(Object.hasOwn(obj, prop) ? obj : Object.getPrototypeOf(obj), prop, receiver);
|
|
626
|
-
if (typeof value === 'object' && value !== null) {
|
|
627
|
-
const reactiveValue = reactive(value);
|
|
628
|
-
// Only create back-references if this object needs them
|
|
629
|
-
if (needsBackReferences(obj)) {
|
|
630
|
-
addBackReference(reactiveValue, obj, prop);
|
|
631
|
-
}
|
|
632
|
-
return reactiveValue;
|
|
633
|
-
}
|
|
634
|
-
return value;
|
|
635
|
-
},
|
|
636
|
-
set(obj, prop, value, receiver) {
|
|
637
|
-
// Check if this property is marked as unreactive
|
|
638
|
-
if (unwrap(obj)[unreactiveProperties]?.has(prop))
|
|
639
|
-
return ReflectSet(obj, prop, value, receiver);
|
|
640
|
-
// Really specific case for when Array is forwarder, in order to let it manage the reactivity
|
|
641
|
-
const isArrayCase = prototypeForwarding in obj &&
|
|
642
|
-
// biome-ignore lint/suspicious/useIsArray: This is the whole point here
|
|
643
|
-
obj[prototypeForwarding] instanceof Array &&
|
|
644
|
-
(!Number.isNaN(Number(prop)) || prop === 'length');
|
|
645
|
-
const newValue = unwrap(value);
|
|
646
|
-
if (isArrayCase) {
|
|
647
|
-
obj[prop] = newValue;
|
|
648
|
-
return true;
|
|
649
|
-
}
|
|
650
|
-
const oldVal = Reflect.has(receiver, prop) ? unwrap(ReflectGet(obj, prop, receiver)) : absent;
|
|
651
|
-
track1(obj, prop, oldVal, newValue);
|
|
652
|
-
if (oldVal !== newValue) {
|
|
653
|
-
ReflectSet(obj, prop, newValue, receiver);
|
|
654
|
-
// try to find a "generic" way to express that
|
|
655
|
-
touched1(obj, { type: oldVal !== absent ? 'set' : 'add', prop }, prop);
|
|
656
|
-
}
|
|
657
|
-
return true;
|
|
658
|
-
},
|
|
659
|
-
deleteProperty(obj, prop) {
|
|
660
|
-
if (!Object.hasOwn(obj, prop))
|
|
661
|
-
return false;
|
|
662
|
-
const oldVal = obj[prop];
|
|
663
|
-
// Remove back-references if this object has deep watchers
|
|
664
|
-
if (objectsWithDeepWatchers.has(obj) && typeof oldVal === 'object' && oldVal !== null) {
|
|
665
|
-
removeBackReference(oldVal, obj, prop);
|
|
666
|
-
}
|
|
667
|
-
delete obj[prop];
|
|
668
|
-
touched1(obj, { type: 'del', prop }, prop);
|
|
669
|
-
// Bubble up changes if this object has deep watchers
|
|
670
|
-
if (objectsWithDeepWatchers.has(obj)) {
|
|
671
|
-
bubbleUpChange(obj);
|
|
672
|
-
}
|
|
673
|
-
return true;
|
|
674
|
-
},
|
|
675
|
-
getPrototypeOf(obj) {
|
|
676
|
-
if (prototypeForwarding in obj)
|
|
677
|
-
return obj[prototypeForwarding];
|
|
678
|
-
return Object.getPrototypeOf(obj);
|
|
679
|
-
},
|
|
680
|
-
setPrototypeOf(obj, proto) {
|
|
681
|
-
if (prototypeForwarding in obj)
|
|
682
|
-
return false;
|
|
683
|
-
Object.setPrototypeOf(obj, proto);
|
|
684
|
-
return true;
|
|
685
|
-
},
|
|
686
|
-
ownKeys(obj) {
|
|
687
|
-
dependant(obj, allProps);
|
|
688
|
-
return Reflect.ownKeys(obj);
|
|
689
|
-
},
|
|
690
|
-
};
|
|
691
|
-
const reactiveClasses = new WeakSet();
|
|
692
|
-
// Create the ReactiveBase mixin
|
|
693
|
-
/**
|
|
694
|
-
* Base mixin for reactive classes that provides proper constructor reactivity
|
|
695
|
-
* Solves constructor reactivity issues in complex inheritance trees
|
|
696
|
-
*/
|
|
697
|
-
const ReactiveBase = mixin((base) => {
|
|
698
|
-
class ReactiveMixin extends base {
|
|
699
|
-
constructor(...args) {
|
|
700
|
-
super(...args);
|
|
701
|
-
// Only apply reactive transformation if the class is marked with @reactive
|
|
702
|
-
// This allows the mixin to work properly with method inheritance
|
|
703
|
-
// biome-ignore lint/correctness/noConstructorReturn: This is the whole point here
|
|
704
|
-
return reactiveClasses.has(new.target) ? reactive(this) : this;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
return ReactiveMixin;
|
|
708
|
-
});
|
|
709
|
-
/**
|
|
710
|
-
* Always-reactive mixin that makes classes inherently reactive
|
|
711
|
-
* Can be used as both a base class and a mixin function
|
|
712
|
-
*/
|
|
713
|
-
const Reactive = mixin((base) => {
|
|
714
|
-
class ReactiveMixin extends base {
|
|
715
|
-
constructor(...args) {
|
|
716
|
-
super(...args);
|
|
717
|
-
// Only apply reactive transformation if the class is marked with @reactive
|
|
718
|
-
// This allows the mixin to work properly with method inheritance
|
|
719
|
-
// biome-ignore lint/correctness/noConstructorReturn: This is the whole point here
|
|
720
|
-
return reactive(this);
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
ReactiveMixin.__isReactiveMixin = true;
|
|
724
|
-
return ReactiveMixin;
|
|
725
|
-
}, unwrap);
|
|
726
|
-
function reactiveObject(anyTarget) {
|
|
727
|
-
if (!anyTarget || typeof anyTarget !== 'object')
|
|
728
|
-
return anyTarget;
|
|
729
|
-
const target = anyTarget;
|
|
730
|
-
// If target is already a proxy, return it
|
|
731
|
-
if (proxyToObject.has(target) || isNonReactive(target))
|
|
732
|
-
return target;
|
|
733
|
-
// If we already have a proxy for this object, return it
|
|
734
|
-
if (objectToProxy.has(target))
|
|
735
|
-
return objectToProxy.get(target);
|
|
736
|
-
const proxied = nativeReactive in target && !(target instanceof target[nativeReactive])
|
|
737
|
-
? new target[nativeReactive](target)
|
|
738
|
-
: target;
|
|
739
|
-
if (proxied !== target)
|
|
740
|
-
proxyToObject.set(proxied, target);
|
|
741
|
-
const proxy = new Proxy(proxied, reactiveHandlers);
|
|
742
|
-
// Store the relationships
|
|
743
|
-
objectToProxy.set(target, proxy);
|
|
744
|
-
proxyToObject.set(proxy, target);
|
|
745
|
-
return proxy;
|
|
746
|
-
}
|
|
747
|
-
/**
|
|
748
|
-
* Main decorator for making classes reactive
|
|
749
|
-
* Automatically makes class instances reactive when created
|
|
750
|
-
*/
|
|
751
|
-
const reactive = decorator({
|
|
752
|
-
class(original) {
|
|
753
|
-
if (original.prototype instanceof ReactiveBase) {
|
|
754
|
-
reactiveClasses.add(original);
|
|
755
|
-
return original;
|
|
756
|
-
}
|
|
757
|
-
// Check if the class extends the Reactive mixin (not ReactiveBase) by checking the prototype chain
|
|
758
|
-
let current = original.prototype;
|
|
759
|
-
while (current && current !== Object.prototype) {
|
|
760
|
-
// Check if this is the Reactive mixin specifically (not ReactiveBase)
|
|
761
|
-
if (current.constructor && current.constructor.__isReactiveMixin) {
|
|
762
|
-
throw new Error('@reactive decorator cannot be used with Reactive mixin. Reactive mixin already provides reactivity.');
|
|
763
|
-
}
|
|
764
|
-
current = Object.getPrototypeOf(current);
|
|
765
|
-
}
|
|
766
|
-
class Reactive extends original {
|
|
767
|
-
constructor(...args) {
|
|
768
|
-
super(...args);
|
|
769
|
-
if (new.target !== Reactive && !reactiveClasses.has(new.target))
|
|
770
|
-
options.warn(`${original.name} has been inherited by ${this.constructor.name} that is not reactive.
|
|
771
|
-
@reactive decorator must be applied to the leaf class OR classes have to extend ReactiveBase.`);
|
|
772
|
-
// biome-ignore lint/correctness/noConstructorReturn: This is the whole point here
|
|
773
|
-
return reactive(this);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
Object.defineProperty(Reactive, 'name', {
|
|
777
|
-
value: `Reactive<${original.name}>`,
|
|
778
|
-
});
|
|
779
|
-
return Reactive;
|
|
780
|
-
},
|
|
781
|
-
get(original) {
|
|
782
|
-
return reactiveObject(original);
|
|
783
|
-
},
|
|
784
|
-
default: reactiveObject,
|
|
785
|
-
});
|
|
786
|
-
/**
|
|
787
|
-
* Gets the original, non-reactive object from a reactive proxy
|
|
788
|
-
* @param proxy - The reactive proxy
|
|
789
|
-
* @returns The original object
|
|
790
|
-
*/
|
|
791
|
-
function unwrap(proxy) {
|
|
792
|
-
// Return the original object
|
|
793
|
-
return proxyToObject.get(proxy) ?? proxy;
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* Checks if an object is a reactive proxy
|
|
797
|
-
* @param obj - The object to check
|
|
798
|
-
* @returns True if the object is reactive
|
|
799
|
-
*/
|
|
800
|
-
function isReactive(obj) {
|
|
801
|
-
return proxyToObject.has(obj);
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* Executes a function without tracking dependencies
|
|
805
|
-
* @param fn - The function to execute
|
|
806
|
-
*/
|
|
807
|
-
function untracked(fn) {
|
|
808
|
-
let rv;
|
|
809
|
-
withEffect(undefined, () => {
|
|
810
|
-
rv = fn();
|
|
811
|
-
} /*,
|
|
812
|
-
true*/);
|
|
813
|
-
return rv;
|
|
814
|
-
}
|
|
815
|
-
// runEffect -> set<stop>
|
|
816
|
-
const effectChildren = new WeakMap();
|
|
817
|
-
const fr = new FinalizationRegistry((f) => f());
|
|
818
|
-
/**
|
|
819
|
-
* @param fn - The effect function to run - provides the cleaner
|
|
820
|
-
* @returns The cleanup function
|
|
821
|
-
*/
|
|
822
|
-
/**
|
|
823
|
-
* Creates a reactive effect that automatically re-runs when dependencies change
|
|
824
|
-
* @param fn - The effect function that provides dependencies and may return a cleanup function
|
|
825
|
-
* @param args - Additional arguments that are forwarded to the effect function
|
|
826
|
-
* @returns A cleanup function to stop the effect
|
|
827
|
-
*/
|
|
828
|
-
function effect(fn, ...args) {
|
|
829
|
-
let cleanup = null;
|
|
830
|
-
// capture the parent effect at creation time for ascend
|
|
831
|
-
const parentForAscend = parentEffect;
|
|
832
|
-
const tracked = markWithRoot((cb) => withEffect(runEffect, cb), fn);
|
|
833
|
-
const ascend = markWithRoot((cb) => withEffect(parentForAscend, cb), getRoot(parentForAscend));
|
|
834
|
-
let effectStopped = false;
|
|
835
|
-
let init = true;
|
|
836
|
-
function runEffect() {
|
|
837
|
-
// The effect has been stopped after having been planned
|
|
838
|
-
if (effectStopped)
|
|
839
|
-
return;
|
|
840
|
-
// Clear previous dependencies
|
|
841
|
-
cleanup?.();
|
|
842
|
-
options.enter(getRoot(fn));
|
|
843
|
-
let reactionCleanup;
|
|
844
|
-
try {
|
|
845
|
-
reactionCleanup = withEffect(runEffect, () => fn({ tracked, ascend, init }, ...args));
|
|
846
|
-
}
|
|
847
|
-
finally {
|
|
848
|
-
init = false;
|
|
849
|
-
options.leave(fn);
|
|
850
|
-
}
|
|
851
|
-
// Create cleanup function for next run
|
|
852
|
-
cleanup = () => {
|
|
853
|
-
cleanup = null;
|
|
854
|
-
reactionCleanup?.();
|
|
855
|
-
// Remove this effect from all reactive objects it's watching
|
|
856
|
-
const effectObjects = effectToReactiveObjects.get(runEffect);
|
|
857
|
-
if (effectObjects) {
|
|
858
|
-
for (const reactiveObj of effectObjects) {
|
|
859
|
-
const objectWatchers = watchers.get(reactiveObj);
|
|
860
|
-
if (objectWatchers) {
|
|
861
|
-
for (const [prop, deps] of objectWatchers.entries()) {
|
|
862
|
-
deps.delete(runEffect);
|
|
863
|
-
if (deps.size === 0) {
|
|
864
|
-
objectWatchers.delete(prop);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
if (objectWatchers.size === 0) {
|
|
868
|
-
watchers.delete(reactiveObj);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
effectToReactiveObjects.delete(runEffect);
|
|
873
|
-
}
|
|
874
|
-
// Invoke all child stops (recursive via subEffectCleanup calling its own mainCleanup)
|
|
875
|
-
const children = effectChildren.get(runEffect);
|
|
876
|
-
if (children) {
|
|
877
|
-
for (const childCleanup of children)
|
|
878
|
-
childCleanup();
|
|
879
|
-
effectChildren.delete(runEffect);
|
|
880
|
-
}
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
// Mark the runEffect callback with the original function as its root
|
|
884
|
-
markWithRoot(runEffect, fn);
|
|
885
|
-
batch(runEffect, 'immediate');
|
|
886
|
-
const stopEffect = () => {
|
|
887
|
-
if (effectStopped)
|
|
888
|
-
return;
|
|
889
|
-
effectStopped = true;
|
|
890
|
-
cleanup?.();
|
|
891
|
-
fr.unregister(stopEffect);
|
|
892
|
-
};
|
|
893
|
-
const parent = parentEffect;
|
|
894
|
-
// Only ROOT effects are registered for GC cleanup
|
|
895
|
-
if (!parent) {
|
|
896
|
-
const callIfCollected = () => stopEffect();
|
|
897
|
-
fr.register(callIfCollected, stopEffect, stopEffect);
|
|
898
|
-
return callIfCollected;
|
|
899
|
-
}
|
|
900
|
-
// Register this effect to be stopped when the parent effect is cleaned up
|
|
901
|
-
let children = effectChildren.get(parent);
|
|
902
|
-
if (!children) {
|
|
903
|
-
children = new Set();
|
|
904
|
-
effectChildren.set(parent, children);
|
|
905
|
-
}
|
|
906
|
-
const subEffectCleanup = () => {
|
|
907
|
-
children.delete(subEffectCleanup);
|
|
908
|
-
if (children.size === 0) {
|
|
909
|
-
effectChildren.delete(parent);
|
|
910
|
-
}
|
|
911
|
-
// Execute this child effect cleanup (which triggers its own mainCleanup)
|
|
912
|
-
stopEffect();
|
|
913
|
-
};
|
|
914
|
-
children.add(subEffectCleanup);
|
|
915
|
-
return subEffectCleanup;
|
|
916
|
-
}
|
|
917
|
-
/**
|
|
918
|
-
* Mark an object as non-reactive. This object and all its properties will never be made reactive.
|
|
919
|
-
* @param obj - The object to mark as non-reactive
|
|
920
|
-
*/
|
|
921
|
-
function nonReactive(...obj) {
|
|
922
|
-
for (const o of obj) {
|
|
923
|
-
try {
|
|
924
|
-
Object.defineProperty(o, nonReactiveMark, {
|
|
925
|
-
value: true,
|
|
926
|
-
writable: false,
|
|
927
|
-
enumerable: false,
|
|
928
|
-
configurable: false,
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
catch { }
|
|
932
|
-
if (!(nonReactiveMark in o))
|
|
933
|
-
nonReactiveObjects.add(o);
|
|
934
|
-
}
|
|
935
|
-
return obj[0];
|
|
936
|
-
}
|
|
937
|
-
/**
|
|
938
|
-
* Set of functions that test if objects are immutable
|
|
939
|
-
* Objects that pass these tests will not be made reactive
|
|
940
|
-
*/
|
|
941
|
-
const immutables = new Set();
|
|
942
|
-
/**
|
|
943
|
-
* Checks if an object is marked as non-reactive
|
|
944
|
-
* @param obj - The object to check
|
|
945
|
-
* @returns True if the object is non-reactive
|
|
946
|
-
*/
|
|
947
|
-
function isNonReactive(obj) {
|
|
948
|
-
// Don't make primitives reactive
|
|
949
|
-
if (obj === null || typeof obj !== 'object')
|
|
950
|
-
return true;
|
|
951
|
-
// Check if the object itself is marked as non-reactive
|
|
952
|
-
if (nonReactiveObjects.has(obj))
|
|
953
|
-
return true;
|
|
954
|
-
// Check if the object has the non-reactive symbol
|
|
955
|
-
if (obj[nonReactiveMark])
|
|
956
|
-
return true;
|
|
957
|
-
// Check if the object is immutable
|
|
958
|
-
if (Array.from(immutables).some((fn) => fn(obj)))
|
|
959
|
-
return true;
|
|
960
|
-
return false;
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Marks classes as non-reactive
|
|
964
|
-
* @param cls - Classes to mark as non-reactive
|
|
965
|
-
* @returns The first class (for chaining)
|
|
966
|
-
*/
|
|
967
|
-
function nonReactiveClass(...cls) {
|
|
968
|
-
for (const c of cls)
|
|
969
|
-
if (c)
|
|
970
|
-
c.prototype[nonReactiveMark] = true;
|
|
971
|
-
return cls[0];
|
|
972
|
-
}
|
|
973
|
-
nonReactiveClass(Date, RegExp, Error, Promise, Function);
|
|
974
|
-
if (typeof window !== 'undefined')
|
|
975
|
-
nonReactive(window, document);
|
|
976
|
-
//if (typeof Element !== 'undefined') nonReactiveClass(Element, Node)
|
|
977
|
-
/**
|
|
978
|
-
* Registers a native class to use a specialized reactive wrapper
|
|
979
|
-
* @param originalClass - The original class to register
|
|
980
|
-
* @param reactiveClass - The reactive wrapper class
|
|
981
|
-
*/
|
|
982
|
-
function registerNativeReactivity(originalClass, reactiveClass) {
|
|
983
|
-
originalClass.prototype[nativeReactive] = reactiveClass;
|
|
984
|
-
nonReactiveClass(reactiveClass);
|
|
985
|
-
}
|
|
986
|
-
/**
|
|
987
|
-
* Deep watch an object and all its nested properties
|
|
988
|
-
* @param target - The object to watch deeply
|
|
989
|
-
* @param callback - The callback to call when any nested property changes
|
|
990
|
-
* @param options - Options for the deep watch
|
|
991
|
-
* @returns A cleanup function to stop watching
|
|
992
|
-
*/
|
|
993
|
-
/**
|
|
994
|
-
* Sets up deep watching for an object, tracking all nested property changes
|
|
995
|
-
* @param target - The object to watch
|
|
996
|
-
* @param callback - The callback to call when changes occur
|
|
997
|
-
* @param options - Options for deep watching
|
|
998
|
-
* @returns A cleanup function to stop deep watching
|
|
999
|
-
*/
|
|
1000
|
-
function deepWatch(target, callback, { immediate = false } = {}) {
|
|
1001
|
-
if (target === null || target === undefined)
|
|
1002
|
-
return undefined;
|
|
1003
|
-
if (typeof target !== 'object')
|
|
1004
|
-
throw new Error('Target of deep watching must be an object');
|
|
1005
|
-
// Create a wrapper callback that matches ScopedCallback signature
|
|
1006
|
-
const wrappedCallback = markWithRoot(() => callback(target), callback);
|
|
1007
|
-
// Use the existing effect system to register dependencies
|
|
1008
|
-
return effect(() => {
|
|
1009
|
-
// Mark the target object as having deep watchers
|
|
1010
|
-
objectsWithDeepWatchers.add(target);
|
|
1011
|
-
// Track which objects this effect is watching for cleanup
|
|
1012
|
-
let effectObjects = effectToDeepWatchedObjects.get(wrappedCallback);
|
|
1013
|
-
if (!effectObjects) {
|
|
1014
|
-
effectObjects = new Set();
|
|
1015
|
-
effectToDeepWatchedObjects.set(wrappedCallback, effectObjects);
|
|
1016
|
-
}
|
|
1017
|
-
effectObjects.add(target);
|
|
1018
|
-
// Traverse the object graph and register dependencies
|
|
1019
|
-
// This will re-run every time the effect runs, ensuring we catch all changes
|
|
1020
|
-
const visited = new WeakSet();
|
|
1021
|
-
function traverseAndTrack(obj, depth = 0) {
|
|
1022
|
-
// Prevent infinite recursion and excessive depth
|
|
1023
|
-
if (visited.has(obj) || !isObject(obj) || depth > options.maxDeepWatchDepth)
|
|
1024
|
-
return;
|
|
1025
|
-
// Do not traverse into unreactive objects
|
|
1026
|
-
if (isNonReactive(obj))
|
|
1027
|
-
return;
|
|
1028
|
-
visited.add(obj);
|
|
1029
|
-
// Mark this object as having deep watchers
|
|
1030
|
-
objectsWithDeepWatchers.add(obj);
|
|
1031
|
-
effectObjects.add(obj);
|
|
1032
|
-
// Traverse all properties to register dependencies
|
|
1033
|
-
// unwrap to avoid kicking dependency
|
|
1034
|
-
for (const key in unwrap(obj)) {
|
|
1035
|
-
if (Object.hasOwn(obj, key)) {
|
|
1036
|
-
// Access the property to register dependency
|
|
1037
|
-
const value = obj[key];
|
|
1038
|
-
// Make the value reactive if it's an object
|
|
1039
|
-
const reactiveValue = typeof value === 'object' && value !== null ? reactive(value) : value;
|
|
1040
|
-
traverseAndTrack(reactiveValue, depth + 1);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
// Also handle array indices and length
|
|
1044
|
-
// biome-ignore lint/suspicious/useIsArray: Check for both native arrays and reactive arrays
|
|
1045
|
-
if (Array.isArray(obj) || obj instanceof Array) {
|
|
1046
|
-
// Access array length to register dependency on length changes
|
|
1047
|
-
const length = obj.length;
|
|
1048
|
-
// Access all current array elements to register dependencies
|
|
1049
|
-
for (let i = 0; i < length; i++) {
|
|
1050
|
-
// Access the array element to register dependency
|
|
1051
|
-
const value = obj[i];
|
|
1052
|
-
// Make the value reactive if it's an object
|
|
1053
|
-
const reactiveValue = typeof value === 'object' && value !== null ? reactive(value) : value;
|
|
1054
|
-
traverseAndTrack(reactiveValue, depth + 1);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
// Handle Set values (deep watch values only, not keys since Sets don't have separate keys)
|
|
1058
|
-
else if (obj instanceof Set) {
|
|
1059
|
-
// Access all Set values to register dependencies
|
|
1060
|
-
for (const value of obj) {
|
|
1061
|
-
// Make the value reactive if it's an object
|
|
1062
|
-
const reactiveValue = typeof value === 'object' && value !== null ? reactive(value) : value;
|
|
1063
|
-
traverseAndTrack(reactiveValue, depth + 1);
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
// Handle Map values (deep watch values only, not keys)
|
|
1067
|
-
else if (obj instanceof Map) {
|
|
1068
|
-
// Access all Map values to register dependencies
|
|
1069
|
-
for (const [_key, value] of obj) {
|
|
1070
|
-
// Make the value reactive if it's an object
|
|
1071
|
-
const reactiveValue = typeof value === 'object' && value !== null ? reactive(value) : value;
|
|
1072
|
-
traverseAndTrack(reactiveValue, depth + 1);
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
// Note: WeakSet and WeakMap cannot be iterated, so we can't deep watch their contents
|
|
1076
|
-
// They will only trigger when the collection itself is replaced
|
|
1077
|
-
}
|
|
1078
|
-
// Traverse the target object to register all dependencies
|
|
1079
|
-
// This will register dependencies on all current properties and array elements
|
|
1080
|
-
traverseAndTrack(target);
|
|
1081
|
-
// Only call the callback if immediate is true or if it's not the first run
|
|
1082
|
-
if (immediate)
|
|
1083
|
-
callback(target);
|
|
1084
|
-
immediate = true;
|
|
1085
|
-
// Return a cleanup function that properly removes deep watcher tracking
|
|
1086
|
-
return () => {
|
|
1087
|
-
// Get the objects this effect was watching
|
|
1088
|
-
const effectObjects = effectToDeepWatchedObjects.get(wrappedCallback);
|
|
1089
|
-
if (effectObjects) {
|
|
1090
|
-
// Remove deep watcher tracking from all objects this effect was watching
|
|
1091
|
-
for (const obj of effectObjects) {
|
|
1092
|
-
// Check if this object still has other deep watchers
|
|
1093
|
-
const watchers = deepWatchers.get(obj);
|
|
1094
|
-
if (watchers) {
|
|
1095
|
-
// Remove this effect's callback from the watchers
|
|
1096
|
-
watchers.delete(wrappedCallback);
|
|
1097
|
-
// If no more watchers, remove the object from deep watchers tracking
|
|
1098
|
-
if (watchers.size === 0) {
|
|
1099
|
-
deepWatchers.delete(obj);
|
|
1100
|
-
objectsWithDeepWatchers.delete(obj);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
else {
|
|
1104
|
-
// No watchers found, remove from deep watchers tracking
|
|
1105
|
-
objectsWithDeepWatchers.delete(obj);
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
// Clean up the tracking data
|
|
1109
|
-
effectToDeepWatchedObjects.delete(wrappedCallback);
|
|
1110
|
-
}
|
|
1111
|
-
};
|
|
1112
|
-
});
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
//#region computed
|
|
1116
|
-
let computedInvalidations;
|
|
1117
|
-
/**
|
|
1118
|
-
* Registers a callback to be called when a computed property is invalidated
|
|
1119
|
-
* @param cb - The callback to register
|
|
1120
|
-
* @param warn - Whether to warn if used outside of a computed property
|
|
1121
|
-
*/
|
|
1122
|
-
function invalidateComputed(cb, warn = true) {
|
|
1123
|
-
if (computedInvalidations)
|
|
1124
|
-
computedInvalidations.push(cb);
|
|
1125
|
-
else if (warn)
|
|
1126
|
-
options.warn('Using `invalidateComputed` outside of a computed property');
|
|
1127
|
-
}
|
|
1128
|
-
const computedCache = new WeakMap();
|
|
1129
|
-
function computedFunction(getter) {
|
|
1130
|
-
const key = getRoot(getter);
|
|
1131
|
-
let invalidations = [];
|
|
1132
|
-
dependant(computedCache, key);
|
|
1133
|
-
if (computedCache.has(key))
|
|
1134
|
-
return computedCache.get(key);
|
|
1135
|
-
let stopped = false;
|
|
1136
|
-
const once = effect(markWithRoot((access) => {
|
|
1137
|
-
if (stopped)
|
|
1138
|
-
return;
|
|
1139
|
-
const oldCI = computedInvalidations;
|
|
1140
|
-
if (computedCache.has(key)) {
|
|
1141
|
-
// This should *not* be called in the cleanup chain, as its effects would be lost and cleaned-up
|
|
1142
|
-
for (const cb of invalidations)
|
|
1143
|
-
cb();
|
|
1144
|
-
invalidations = [];
|
|
1145
|
-
computedCache.delete(key);
|
|
1146
|
-
touched1(computedCache, { type: 'invalidate', prop: key }, key);
|
|
1147
|
-
once();
|
|
1148
|
-
stopped = true;
|
|
1149
|
-
}
|
|
1150
|
-
else
|
|
1151
|
-
try {
|
|
1152
|
-
computedInvalidations = invalidations;
|
|
1153
|
-
computedCache.set(key, getter(access));
|
|
1154
|
-
}
|
|
1155
|
-
finally {
|
|
1156
|
-
computedInvalidations = oldCI;
|
|
1157
|
-
}
|
|
1158
|
-
}, getter));
|
|
1159
|
-
return computedCache.get(key);
|
|
1160
|
-
}
|
|
1161
|
-
/**
|
|
1162
|
-
* Decorator and function for creating computed properties that cache their values
|
|
1163
|
-
* Only recomputes when dependencies change
|
|
1164
|
-
*/
|
|
1165
|
-
const computed = Object.assign(decorator({
|
|
1166
|
-
getter(original, propertyKey) {
|
|
1167
|
-
const computers = new WeakMap();
|
|
1168
|
-
return function () {
|
|
1169
|
-
if (!computers.has(this)) {
|
|
1170
|
-
computers.set(this, renamed(() => original.call(this), `${String(this.constructor.name)}.${String(propertyKey)}`));
|
|
1171
|
-
}
|
|
1172
|
-
return computedFunction(computers.get(this));
|
|
1173
|
-
};
|
|
1174
|
-
},
|
|
1175
|
-
default: computedFunction,
|
|
1176
|
-
}), {
|
|
1177
|
-
map: computedMap,
|
|
1178
|
-
memo: computedMapMemo,
|
|
1179
|
-
self: selfReferencing,
|
|
1180
|
-
});
|
|
1181
|
-
//#endregion
|
|
1182
|
-
//#region watch
|
|
1183
|
-
const unsetYet = Symbol('unset-yet');
|
|
1184
|
-
function watch(value, //object | ((dep: DependencyAccess) => object),
|
|
1185
|
-
changed, options = {}) {
|
|
1186
|
-
return typeof value === 'function'
|
|
1187
|
-
? watchCallBack(value, changed, options)
|
|
1188
|
-
: typeof value === 'object'
|
|
1189
|
-
? watchObject(value, changed, options)
|
|
1190
|
-
: (() => {
|
|
1191
|
-
throw new Error('watch: value must be a function or an object');
|
|
1192
|
-
})();
|
|
1193
|
-
}
|
|
1194
|
-
function watchObject(value, changed, { immediate = false, deep = false } = {}) {
|
|
1195
|
-
const myParentEffect = activeEffect;
|
|
1196
|
-
if (deep)
|
|
1197
|
-
return deepWatch(value, changed, { immediate });
|
|
1198
|
-
return effect(markWithRoot(function watchObjectEffect() {
|
|
1199
|
-
dependant(value);
|
|
1200
|
-
if (immediate)
|
|
1201
|
-
withEffect(myParentEffect, () => changed(value));
|
|
1202
|
-
immediate = true;
|
|
1203
|
-
}, changed));
|
|
1204
|
-
}
|
|
1205
|
-
function watchCallBack(value, changed, { immediate = false, deep = false } = {}) {
|
|
1206
|
-
const myParentEffect = activeEffect;
|
|
1207
|
-
let oldValue = unsetYet;
|
|
1208
|
-
let deepCleanup;
|
|
1209
|
-
const cbCleanup = effect(markWithRoot(function watchCallBackEffect(access) {
|
|
1210
|
-
const newValue = value(access);
|
|
1211
|
-
if (oldValue !== newValue)
|
|
1212
|
-
withEffect(myParentEffect, markWithRoot(() => {
|
|
1213
|
-
if (oldValue === unsetYet) {
|
|
1214
|
-
if (immediate)
|
|
1215
|
-
changed(newValue);
|
|
1216
|
-
}
|
|
1217
|
-
else
|
|
1218
|
-
changed(newValue, oldValue);
|
|
1219
|
-
oldValue = newValue;
|
|
1220
|
-
if (deep) {
|
|
1221
|
-
if (deepCleanup)
|
|
1222
|
-
deepCleanup();
|
|
1223
|
-
deepCleanup = deepWatch(newValue, markWithRoot((value) => changed(value, value), changed));
|
|
1224
|
-
}
|
|
1225
|
-
}, changed));
|
|
1226
|
-
}, value));
|
|
1227
|
-
return () => {
|
|
1228
|
-
cbCleanup();
|
|
1229
|
-
if (deepCleanup)
|
|
1230
|
-
deepCleanup();
|
|
1231
|
-
};
|
|
1232
|
-
}
|
|
1233
|
-
//#endregion
|
|
1234
|
-
//#region nonReactive
|
|
1235
|
-
/**
|
|
1236
|
-
* Mark an object as non-reactive. This object and all its properties will never be made reactive.
|
|
1237
|
-
* @param obj - The object to mark as non-reactive
|
|
1238
|
-
*/
|
|
1239
|
-
function deepNonReactive(obj) {
|
|
1240
|
-
obj = unwrap(obj);
|
|
1241
|
-
if (isNonReactive(obj))
|
|
1242
|
-
return obj;
|
|
1243
|
-
try {
|
|
1244
|
-
Object.defineProperty(obj, nonReactiveMark, {
|
|
1245
|
-
value: true,
|
|
1246
|
-
writable: false,
|
|
1247
|
-
enumerable: false,
|
|
1248
|
-
configurable: true,
|
|
1249
|
-
});
|
|
1250
|
-
}
|
|
1251
|
-
catch { }
|
|
1252
|
-
if (!(nonReactiveMark in obj))
|
|
1253
|
-
nonReactiveObjects.add(obj);
|
|
1254
|
-
for (const key in obj)
|
|
1255
|
-
deepNonReactive(obj[key]);
|
|
1256
|
-
return obj;
|
|
1257
|
-
}
|
|
1258
|
-
function unreactiveApplication(arg1, ...args) {
|
|
1259
|
-
return typeof arg1 === 'object'
|
|
1260
|
-
? deepNonReactive(arg1)
|
|
1261
|
-
: ((original) => {
|
|
1262
|
-
// Copy the parent's unreactive properties if they exist
|
|
1263
|
-
original.prototype[unreactiveProperties] = new Set(original.prototype[unreactiveProperties] || []);
|
|
1264
|
-
// Add all arguments (including the first one)
|
|
1265
|
-
original.prototype[unreactiveProperties].add(arg1);
|
|
1266
|
-
for (const arg of args)
|
|
1267
|
-
original.prototype[unreactiveProperties].add(arg);
|
|
1268
|
-
return original; // Return the class
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* Decorator that marks classes or properties as non-reactive
|
|
1273
|
-
* Prevents objects from being made reactive
|
|
1274
|
-
*/
|
|
1275
|
-
const unreactive = decorator({
|
|
1276
|
-
class(original) {
|
|
1277
|
-
// Called without arguments, mark entire class as non-reactive
|
|
1278
|
-
nonReactiveClass(original);
|
|
1279
|
-
},
|
|
1280
|
-
default: unreactiveApplication,
|
|
1281
|
-
});
|
|
1282
|
-
Object.assign(profileInfo, { computedCache });
|
|
1283
|
-
function cleanedBy(obj, cleanup) {
|
|
1284
|
-
return Object.defineProperty(obj, 'cleanup', {
|
|
1285
|
-
value: cleanup,
|
|
1286
|
-
writable: false,
|
|
1287
|
-
enumerable: false,
|
|
1288
|
-
configurable: true,
|
|
1289
|
-
});
|
|
1290
|
-
}
|
|
1291
|
-
function computedMap(inputs, compute, resize) {
|
|
1292
|
-
const result = reactive([]);
|
|
1293
|
-
const cleanups = [];
|
|
1294
|
-
function input(index) {
|
|
1295
|
-
return effect(function computedIndexedMapInputEffect() {
|
|
1296
|
-
result[index] = compute(inputs[index], index, result[index]);
|
|
1297
|
-
});
|
|
1298
|
-
}
|
|
1299
|
-
effect(function computedMapLengthEffect({ ascend }) {
|
|
1300
|
-
const length = inputs.length;
|
|
1301
|
-
const resultLength = untracked(() => result.length);
|
|
1302
|
-
resize?.(length, resultLength);
|
|
1303
|
-
if (length < resultLength) {
|
|
1304
|
-
const toCleanup = cleanups.splice(length);
|
|
1305
|
-
for (const cleanup of toCleanup)
|
|
1306
|
-
cleanup();
|
|
1307
|
-
result.length = length;
|
|
1308
|
-
}
|
|
1309
|
-
else if (length > resultLength)
|
|
1310
|
-
// the input effects will be registered as the call's children, so they will remain not cleaned with this effect on length
|
|
1311
|
-
ascend(function computedMapNewElements() {
|
|
1312
|
-
for (let i = resultLength; i < length; i++)
|
|
1313
|
-
cleanups.push(input(i));
|
|
1314
|
-
});
|
|
1315
|
-
});
|
|
1316
|
-
return cleanedBy(result, () => {
|
|
1317
|
-
for (const cleanup of cleanups)
|
|
1318
|
-
cleanup();
|
|
1319
|
-
cleanups.length = 0;
|
|
1320
|
-
});
|
|
1321
|
-
}
|
|
1322
|
-
let Memoized = (() => {
|
|
1323
|
-
let _classDecorators = [unreactive];
|
|
1324
|
-
let _classDescriptor;
|
|
1325
|
-
let _classExtraInitializers = [];
|
|
1326
|
-
let _classThis;
|
|
1327
|
-
_classThis = class {
|
|
1328
|
-
constructor(compute) {
|
|
1329
|
-
this.compute = compute;
|
|
1330
|
-
this.cache = new Map();
|
|
1331
|
-
this.keepCleanups = effect(({ ascend }) => {
|
|
1332
|
-
this.inEffect = ascend;
|
|
1333
|
-
return () => {
|
|
1334
|
-
this.cleanup();
|
|
1335
|
-
};
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
cleanup() {
|
|
1339
|
-
for (const { cleanup } of this.cache.values())
|
|
1340
|
-
cleanup();
|
|
1341
|
-
this.cache.clear();
|
|
1342
|
-
}
|
|
1343
|
-
get(input) {
|
|
1344
|
-
dependant(this, input);
|
|
1345
|
-
let cached;
|
|
1346
|
-
if (this.cache.has(input)) {
|
|
1347
|
-
cached = this.cache.get(input);
|
|
1348
|
-
}
|
|
1349
|
-
else {
|
|
1350
|
-
cached = {};
|
|
1351
|
-
cached.cleanup = this.inEffect(() => effect(Object.defineProperties(() => {
|
|
1352
|
-
if ('value' in cached) {
|
|
1353
|
-
this.cache.delete(input);
|
|
1354
|
-
touched1(this, { type: 'invalidate', prop: input }, input);
|
|
1355
|
-
}
|
|
1356
|
-
else {
|
|
1357
|
-
cached.value = this.compute(input);
|
|
1358
|
-
}
|
|
1359
|
-
}, { name: { value: 'Memoize' } })));
|
|
1360
|
-
this.cache.set(input, cached);
|
|
1361
|
-
}
|
|
1362
|
-
return cached.value;
|
|
1363
|
-
}
|
|
1364
|
-
reduceInputs(inputs) {
|
|
1365
|
-
for (const input of this.cache.keys()) {
|
|
1366
|
-
if (typeof inputs === 'function' ? !inputs(input) : !inputs.has(input)) {
|
|
1367
|
-
const entry = this.cache.get(input);
|
|
1368
|
-
entry.cleanup();
|
|
1369
|
-
this.cache.delete(input);
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
};
|
|
1374
|
-
__setFunctionName(_classThis, "Memoized");
|
|
1375
|
-
(() => {
|
|
1376
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
1377
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
1378
|
-
_classThis = _classDescriptor.value;
|
|
1379
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
1380
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
1381
|
-
})();
|
|
1382
|
-
return _classThis;
|
|
1383
|
-
})();
|
|
1384
|
-
function computedMapMemo(inputs, compute) {
|
|
1385
|
-
const memo = new Memoized(compute);
|
|
1386
|
-
const result = reactive([]);
|
|
1387
|
-
return cleanedBy(result, effect(() => {
|
|
1388
|
-
const inputArray = typeof inputs === 'function' ? inputs() : inputs;
|
|
1389
|
-
function reduceKeys() {
|
|
1390
|
-
untracked(() => {
|
|
1391
|
-
memo.reduceInputs(new Set(inputArray));
|
|
1392
|
-
});
|
|
1393
|
-
}
|
|
1394
|
-
const map = computedMap(inputArray, (input) => {
|
|
1395
|
-
addBatchCleanup(reduceKeys);
|
|
1396
|
-
return memo.get(input);
|
|
1397
|
-
}, () => addBatchCleanup(reduceKeys));
|
|
1398
|
-
effect(() => {
|
|
1399
|
-
result.length = 0;
|
|
1400
|
-
result.push(...map);
|
|
1401
|
-
});
|
|
1402
|
-
}));
|
|
1403
|
-
}
|
|
1404
|
-
function selfReferencing(compute) {
|
|
1405
|
-
let rv;
|
|
1406
|
-
const cleanup = effect((access) => {
|
|
1407
|
-
rv = compute(access);
|
|
1408
|
-
});
|
|
1409
|
-
return cleanedBy(rv, cleanup);
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
const native$2 = Symbol('native');
|
|
1413
|
-
const isArray = Array.isArray;
|
|
1414
|
-
Array.isArray = ((value) =>
|
|
1415
|
-
// biome-ignore lint/suspicious/useIsArray: We are defining it
|
|
1416
|
-
isArray(value) || (value instanceof Array && native$2 in value));
|
|
1417
|
-
class ReactiveBaseArray {
|
|
1418
|
-
}
|
|
1419
|
-
function* index(i, { length = true } = {}) {
|
|
1420
|
-
if (length)
|
|
1421
|
-
yield 'length';
|
|
1422
|
-
yield i;
|
|
1423
|
-
}
|
|
1424
|
-
function* range(a, b, { length = false } = {}) {
|
|
1425
|
-
const start = Math.min(a, b);
|
|
1426
|
-
const end = Math.max(a, b);
|
|
1427
|
-
if (length)
|
|
1428
|
-
yield 'length';
|
|
1429
|
-
for (let i = start; i <= end; i++)
|
|
1430
|
-
yield i;
|
|
1431
|
-
}
|
|
1432
|
-
/**
|
|
1433
|
-
* Reactive wrapper around JavaScript's Array class with full array method support
|
|
1434
|
-
* Tracks length changes, individual index operations, and collection-wide operations
|
|
1435
|
-
*/
|
|
1436
|
-
class ReactiveArray extends Indexable(ReactiveBaseArray, {
|
|
1437
|
-
get(i) {
|
|
1438
|
-
dependant(this[native$2], i);
|
|
1439
|
-
return reactive(this[native$2][i]);
|
|
1440
|
-
},
|
|
1441
|
-
set(i, value) {
|
|
1442
|
-
const added = i >= this[native$2].length;
|
|
1443
|
-
this[native$2][i] = value;
|
|
1444
|
-
touched(this[native$2], { type: 'set', prop: i }, index(i, { length: added }));
|
|
1445
|
-
},
|
|
1446
|
-
getLength() {
|
|
1447
|
-
dependant(this[native$2], 'length');
|
|
1448
|
-
return this[native$2].length;
|
|
1449
|
-
},
|
|
1450
|
-
setLength(value) {
|
|
1451
|
-
const oldLength = this[native$2].length;
|
|
1452
|
-
try {
|
|
1453
|
-
this[native$2].length = value;
|
|
1454
|
-
}
|
|
1455
|
-
finally {
|
|
1456
|
-
touched(this[native$2], { type: 'set', prop: 'length' }, range(oldLength, value, { length: true }));
|
|
1457
|
-
}
|
|
1458
|
-
},
|
|
1459
|
-
}) {
|
|
1460
|
-
constructor(original) {
|
|
1461
|
-
super();
|
|
1462
|
-
Object.defineProperties(this, {
|
|
1463
|
-
// We have to make it double, as [native] must be `unique symbol` - impossible through import
|
|
1464
|
-
[native$2]: { value: original },
|
|
1465
|
-
[prototypeForwarding]: { value: original },
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
// Safe array access with negative indices
|
|
1469
|
-
at(index) {
|
|
1470
|
-
const actualIndex = index < 0 ? this[native$2].length + index : index;
|
|
1471
|
-
dependant(this, actualIndex);
|
|
1472
|
-
if (actualIndex < 0 || actualIndex >= this[native$2].length)
|
|
1473
|
-
return undefined;
|
|
1474
|
-
return reactive(this[native$2][actualIndex]);
|
|
1475
|
-
}
|
|
1476
|
-
push(...items) {
|
|
1477
|
-
const oldLength = this[native$2].length;
|
|
1478
|
-
try {
|
|
1479
|
-
return this[native$2].push(...items);
|
|
1480
|
-
}
|
|
1481
|
-
finally {
|
|
1482
|
-
touched(this, { type: 'bunch', method: 'push' }, range(oldLength, oldLength + items.length - 1, { length: true }));
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
pop() {
|
|
1486
|
-
if (this[native$2].length === 0)
|
|
1487
|
-
return undefined;
|
|
1488
|
-
try {
|
|
1489
|
-
return reactive(this[native$2].pop());
|
|
1490
|
-
}
|
|
1491
|
-
finally {
|
|
1492
|
-
touched(this, { type: 'bunch', method: 'pop' }, index(this[native$2].length));
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
shift() {
|
|
1496
|
-
if (this[native$2].length === 0)
|
|
1497
|
-
return undefined;
|
|
1498
|
-
try {
|
|
1499
|
-
return reactive(this[native$2].shift());
|
|
1500
|
-
}
|
|
1501
|
-
finally {
|
|
1502
|
-
touched(this, { type: 'bunch', method: 'shift' }, range(0, this[native$2].length + 1, { length: true }));
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
unshift(...items) {
|
|
1506
|
-
try {
|
|
1507
|
-
return this[native$2].unshift(...items);
|
|
1508
|
-
}
|
|
1509
|
-
finally {
|
|
1510
|
-
touched(this, { type: 'bunch', method: 'unshift' }, range(0, this[native$2].length - items.length, { length: true }));
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
splice(start, deleteCount, ...items) {
|
|
1514
|
-
const oldLength = this[native$2].length;
|
|
1515
|
-
if (deleteCount === undefined)
|
|
1516
|
-
deleteCount = oldLength - start;
|
|
1517
|
-
try {
|
|
1518
|
-
if (deleteCount === undefined)
|
|
1519
|
-
return reactive(this[native$2].splice(start));
|
|
1520
|
-
return reactive(this[native$2].splice(start, deleteCount, ...items));
|
|
1521
|
-
}
|
|
1522
|
-
finally {
|
|
1523
|
-
touched(this, { type: 'bunch', method: 'splice' },
|
|
1524
|
-
// TODO: edge cases
|
|
1525
|
-
deleteCount === items.length
|
|
1526
|
-
? range(start, start + deleteCount)
|
|
1527
|
-
: range(start, oldLength + Math.max(items.length - deleteCount, 0), {
|
|
1528
|
-
length: true,
|
|
1529
|
-
}));
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
reverse() {
|
|
1533
|
-
try {
|
|
1534
|
-
return this[native$2].reverse();
|
|
1535
|
-
}
|
|
1536
|
-
finally {
|
|
1537
|
-
touched(this, { type: 'bunch', method: 'reverse' }, range(0, this[native$2].length - 1));
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
sort(compareFn) {
|
|
1541
|
-
compareFn = compareFn || ((a, b) => a.toString().localeCompare(b.toString()));
|
|
1542
|
-
try {
|
|
1543
|
-
return this[native$2].sort((a, b) => compareFn(reactive(a), reactive(b)));
|
|
1544
|
-
}
|
|
1545
|
-
finally {
|
|
1546
|
-
touched(this, { type: 'bunch', method: 'sort' }, range(0, this[native$2].length - 1));
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
fill(value, start, end) {
|
|
1550
|
-
try {
|
|
1551
|
-
if (start === undefined)
|
|
1552
|
-
return this[native$2].fill(value);
|
|
1553
|
-
if (end === undefined)
|
|
1554
|
-
return this[native$2].fill(value, start);
|
|
1555
|
-
return this[native$2].fill(value, start, end);
|
|
1556
|
-
}
|
|
1557
|
-
finally {
|
|
1558
|
-
touched(this, { type: 'bunch', method: 'fill' }, range(0, this[native$2].length - 1));
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
copyWithin(target, start, end) {
|
|
1562
|
-
try {
|
|
1563
|
-
if (end === undefined)
|
|
1564
|
-
return this[native$2].copyWithin(target, start);
|
|
1565
|
-
return this[native$2].copyWithin(target, start, end);
|
|
1566
|
-
}
|
|
1567
|
-
finally {
|
|
1568
|
-
touched(this, { type: 'bunch', method: 'copyWithin' },
|
|
1569
|
-
// TODO: calculate the range properly
|
|
1570
|
-
range(0, this[native$2].length - 1));
|
|
1571
|
-
}
|
|
1572
|
-
// Touch all affected indices with a single allProps call
|
|
1573
|
-
}
|
|
1574
|
-
// Immutable versions of mutator methods
|
|
1575
|
-
toReversed() {
|
|
1576
|
-
dependant(this);
|
|
1577
|
-
return reactive(this[native$2].toReversed());
|
|
1578
|
-
}
|
|
1579
|
-
toSorted(compareFn) {
|
|
1580
|
-
dependant(this);
|
|
1581
|
-
return reactive(this[native$2].toSorted(compareFn));
|
|
1582
|
-
}
|
|
1583
|
-
toSpliced(start, deleteCount, ...items) {
|
|
1584
|
-
dependant(this);
|
|
1585
|
-
return deleteCount === undefined
|
|
1586
|
-
? this[native$2].toSpliced(start)
|
|
1587
|
-
: this[native$2].toSpliced(start, deleteCount, ...items);
|
|
1588
|
-
}
|
|
1589
|
-
with(index, value) {
|
|
1590
|
-
dependant(this);
|
|
1591
|
-
return reactive(this[native$2].with(index, value));
|
|
1592
|
-
}
|
|
1593
|
-
// Iterator methods with reactivity tracking
|
|
1594
|
-
entries() {
|
|
1595
|
-
dependant(this);
|
|
1596
|
-
return makeReactiveEntriesIterator(this[native$2].entries());
|
|
1597
|
-
}
|
|
1598
|
-
keys() {
|
|
1599
|
-
dependant(this, 'length');
|
|
1600
|
-
return this[native$2].keys();
|
|
1601
|
-
}
|
|
1602
|
-
values() {
|
|
1603
|
-
dependant(this);
|
|
1604
|
-
return makeReactiveIterator(this[native$2].values());
|
|
1605
|
-
}
|
|
1606
|
-
[Symbol.iterator]() {
|
|
1607
|
-
dependant(this);
|
|
1608
|
-
const nativeIterator = this[native$2][Symbol.iterator]();
|
|
1609
|
-
return {
|
|
1610
|
-
next() {
|
|
1611
|
-
const result = nativeIterator.next();
|
|
1612
|
-
if (result.done) {
|
|
1613
|
-
return result;
|
|
1614
|
-
}
|
|
1615
|
-
return { value: reactive(result.value), done: false };
|
|
1616
|
-
},
|
|
1617
|
-
};
|
|
1618
|
-
}
|
|
1619
|
-
indexOf(searchElement, fromIndex) {
|
|
1620
|
-
dependant(this);
|
|
1621
|
-
return this[native$2].indexOf(searchElement, fromIndex);
|
|
1622
|
-
}
|
|
1623
|
-
lastIndexOf(searchElement, fromIndex) {
|
|
1624
|
-
dependant(this);
|
|
1625
|
-
return this[native$2].lastIndexOf(searchElement, fromIndex);
|
|
1626
|
-
}
|
|
1627
|
-
includes(searchElement, fromIndex) {
|
|
1628
|
-
dependant(this);
|
|
1629
|
-
return this[native$2].includes(searchElement, fromIndex);
|
|
1630
|
-
}
|
|
1631
|
-
find(predicate, thisArg) {
|
|
1632
|
-
dependant(this);
|
|
1633
|
-
return reactive(this[native$2].find(predicate, thisArg));
|
|
1634
|
-
}
|
|
1635
|
-
findIndex(predicate, thisArg) {
|
|
1636
|
-
dependant(this);
|
|
1637
|
-
return this[native$2].findIndex(predicate, thisArg);
|
|
1638
|
-
}
|
|
1639
|
-
flat() {
|
|
1640
|
-
dependant(this);
|
|
1641
|
-
return reactive(this[native$2].flat());
|
|
1642
|
-
}
|
|
1643
|
-
flatMap(callbackfn, thisArg) {
|
|
1644
|
-
dependant(this);
|
|
1645
|
-
return reactive(this[native$2].flatMap(callbackfn, thisArg));
|
|
1646
|
-
}
|
|
1647
|
-
filter(callbackfn, thisArg) {
|
|
1648
|
-
dependant(this);
|
|
1649
|
-
return reactive(this[native$2].filter((item, index, array) => callbackfn(reactive(item), index, array), thisArg));
|
|
1650
|
-
}
|
|
1651
|
-
map(callbackfn, thisArg) {
|
|
1652
|
-
dependant(this);
|
|
1653
|
-
return reactive(this[native$2].map((item, index, array) => callbackfn(reactive(item), index, array), thisArg));
|
|
1654
|
-
}
|
|
1655
|
-
reduce(callbackfn, initialValue) {
|
|
1656
|
-
dependant(this);
|
|
1657
|
-
const result = initialValue === undefined
|
|
1658
|
-
? this[native$2].reduce(callbackfn)
|
|
1659
|
-
: this[native$2].reduce(callbackfn, initialValue);
|
|
1660
|
-
return reactive(result);
|
|
1661
|
-
}
|
|
1662
|
-
reduceRight(callbackfn, initialValue) {
|
|
1663
|
-
dependant(this);
|
|
1664
|
-
const result = initialValue !== undefined
|
|
1665
|
-
? this[native$2].reduceRight(callbackfn, initialValue)
|
|
1666
|
-
: this[native$2].reduceRight(callbackfn);
|
|
1667
|
-
return reactive(result);
|
|
1668
|
-
}
|
|
1669
|
-
slice(start, end) {
|
|
1670
|
-
for (const i of range(start || 0, end || this[native$2].length - 1))
|
|
1671
|
-
dependant(this, i);
|
|
1672
|
-
return start === undefined
|
|
1673
|
-
? this[native$2].slice()
|
|
1674
|
-
: end === undefined
|
|
1675
|
-
? this[native$2].slice(start)
|
|
1676
|
-
: this[native$2].slice(start, end);
|
|
1677
|
-
}
|
|
1678
|
-
concat(...items) {
|
|
1679
|
-
dependant(this);
|
|
1680
|
-
return reactive(this[native$2].concat(...items));
|
|
1681
|
-
}
|
|
1682
|
-
join(separator) {
|
|
1683
|
-
dependant(this);
|
|
1684
|
-
return this[native$2].join(separator);
|
|
1685
|
-
}
|
|
1686
|
-
forEach(callbackfn, thisArg) {
|
|
1687
|
-
dependant(this);
|
|
1688
|
-
this[native$2].forEach(callbackfn, thisArg);
|
|
1689
|
-
}
|
|
1690
|
-
// TODO: re-implement for fun dependencies? (eg - every only check the first ones until it find some),
|
|
1691
|
-
// no need to make it dependant on indexes after the found one
|
|
1692
|
-
every(callbackfn, thisArg) {
|
|
1693
|
-
dependant(this);
|
|
1694
|
-
return this[native$2].every(callbackfn, thisArg);
|
|
1695
|
-
}
|
|
1696
|
-
some(callbackfn, thisArg) {
|
|
1697
|
-
dependant(this);
|
|
1698
|
-
return this[native$2].some(callbackfn, thisArg);
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
|
-
const native$1 = Symbol('native');
|
|
1703
|
-
/**
|
|
1704
|
-
* Reactive wrapper around JavaScript's WeakMap class
|
|
1705
|
-
* Only tracks individual key operations, no size tracking (WeakMap limitation)
|
|
1706
|
-
*/
|
|
1707
|
-
class ReactiveWeakMap {
|
|
1708
|
-
constructor(original) {
|
|
1709
|
-
Object.defineProperties(this, {
|
|
1710
|
-
[native$1]: { value: original },
|
|
1711
|
-
[prototypeForwarding]: { value: original },
|
|
1712
|
-
content: { value: Symbol('content') },
|
|
1713
|
-
[Symbol.toStringTag]: { value: 'ReactiveWeakMap' },
|
|
1714
|
-
});
|
|
1715
|
-
}
|
|
1716
|
-
// Implement WeakMap interface methods with reactivity
|
|
1717
|
-
delete(key) {
|
|
1718
|
-
const hadKey = this[native$1].has(key);
|
|
1719
|
-
const result = this[native$1].delete(key);
|
|
1720
|
-
if (hadKey)
|
|
1721
|
-
touched1(this.content, { type: 'del', prop: key }, key);
|
|
1722
|
-
return result;
|
|
1723
|
-
}
|
|
1724
|
-
get(key) {
|
|
1725
|
-
dependant(this.content, key);
|
|
1726
|
-
return reactive(this[native$1].get(key));
|
|
1727
|
-
}
|
|
1728
|
-
has(key) {
|
|
1729
|
-
dependant(this.content, key);
|
|
1730
|
-
return this[native$1].has(key);
|
|
1731
|
-
}
|
|
1732
|
-
set(key, value) {
|
|
1733
|
-
// Trigger effects for the specific key
|
|
1734
|
-
touched1(this.content, { type: this[native$1].has(key) ? 'set' : 'add', prop: key }, key);
|
|
1735
|
-
this[native$1].set(key, value);
|
|
1736
|
-
return this;
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
/**
|
|
1740
|
-
* Reactive wrapper around JavaScript's Map class
|
|
1741
|
-
* Tracks size changes, individual key operations, and collection-wide operations
|
|
1742
|
-
*/
|
|
1743
|
-
class ReactiveMap {
|
|
1744
|
-
constructor(original) {
|
|
1745
|
-
Object.defineProperties(this, {
|
|
1746
|
-
[native$1]: { value: original },
|
|
1747
|
-
[prototypeForwarding]: { value: original },
|
|
1748
|
-
content: { value: Symbol('content') },
|
|
1749
|
-
[Symbol.toStringTag]: { value: 'ReactiveMap' },
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
|
-
// Implement Map interface methods with reactivity
|
|
1753
|
-
get size() {
|
|
1754
|
-
dependant(this, 'size'); // The ReactiveMap instance still goes through proxy
|
|
1755
|
-
return this[native$1].size;
|
|
1756
|
-
}
|
|
1757
|
-
clear() {
|
|
1758
|
-
const hadEntries = this[native$1].size > 0;
|
|
1759
|
-
this[native$1].clear();
|
|
1760
|
-
if (hadEntries) {
|
|
1761
|
-
const evolution = { type: 'bunch', method: 'clear' };
|
|
1762
|
-
// Clear triggers all effects since all keys are affected
|
|
1763
|
-
touched1(this, evolution, 'size');
|
|
1764
|
-
touched(this.content, evolution);
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
entries() {
|
|
1768
|
-
dependant(this.content);
|
|
1769
|
-
return makeReactiveEntriesIterator(this[native$1].entries());
|
|
1770
|
-
}
|
|
1771
|
-
forEach(callbackfn, thisArg) {
|
|
1772
|
-
dependant(this.content);
|
|
1773
|
-
this[native$1].forEach(callbackfn, thisArg);
|
|
1774
|
-
}
|
|
1775
|
-
keys() {
|
|
1776
|
-
dependant(this.content);
|
|
1777
|
-
return this[native$1].keys();
|
|
1778
|
-
}
|
|
1779
|
-
values() {
|
|
1780
|
-
dependant(this.content);
|
|
1781
|
-
return makeReactiveIterator(this[native$1].values());
|
|
1782
|
-
}
|
|
1783
|
-
[Symbol.iterator]() {
|
|
1784
|
-
dependant(this.content);
|
|
1785
|
-
const nativeIterator = this[native$1][Symbol.iterator]();
|
|
1786
|
-
return {
|
|
1787
|
-
next() {
|
|
1788
|
-
const result = nativeIterator.next();
|
|
1789
|
-
if (result.done) {
|
|
1790
|
-
return result;
|
|
1791
|
-
}
|
|
1792
|
-
return {
|
|
1793
|
-
value: [result.value[0], reactive(result.value[1])],
|
|
1794
|
-
done: false,
|
|
1795
|
-
};
|
|
1796
|
-
},
|
|
1797
|
-
};
|
|
1798
|
-
}
|
|
1799
|
-
// Implement Map methods with reactivity
|
|
1800
|
-
delete(key) {
|
|
1801
|
-
const hadKey = this[native$1].has(key);
|
|
1802
|
-
const result = this[native$1].delete(key);
|
|
1803
|
-
if (hadKey) {
|
|
1804
|
-
const evolution = { type: 'del', prop: key };
|
|
1805
|
-
touched1(this.content, evolution, key);
|
|
1806
|
-
touched1(this, evolution, 'size');
|
|
1807
|
-
}
|
|
1808
|
-
return result;
|
|
1809
|
-
}
|
|
1810
|
-
get(key) {
|
|
1811
|
-
dependant(this.content, key);
|
|
1812
|
-
return reactive(this[native$1].get(key));
|
|
1813
|
-
}
|
|
1814
|
-
has(key) {
|
|
1815
|
-
dependant(this.content, key);
|
|
1816
|
-
return this[native$1].has(key);
|
|
1817
|
-
}
|
|
1818
|
-
set(key, value) {
|
|
1819
|
-
const hadKey = this[native$1].has(key);
|
|
1820
|
-
const oldValue = this[native$1].get(key);
|
|
1821
|
-
const reactiveValue = reactive(value);
|
|
1822
|
-
this[native$1].set(key, reactiveValue);
|
|
1823
|
-
if (!hadKey || oldValue !== reactiveValue) {
|
|
1824
|
-
const evolution = { type: hadKey ? 'set' : 'add', prop: key };
|
|
1825
|
-
touched1(this.content, evolution, key);
|
|
1826
|
-
touched1(this, evolution, 'size');
|
|
1827
|
-
}
|
|
1828
|
-
return this;
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
const native = Symbol('native');
|
|
1833
|
-
/**
|
|
1834
|
-
* Reactive wrapper around JavaScript's WeakSet class
|
|
1835
|
-
* Only tracks individual value operations, no size tracking (WeakSet limitation)
|
|
1836
|
-
*/
|
|
1837
|
-
class ReactiveWeakSet {
|
|
1838
|
-
constructor(original) {
|
|
1839
|
-
Object.defineProperties(this, {
|
|
1840
|
-
[native]: { value: original },
|
|
1841
|
-
[prototypeForwarding]: { value: original },
|
|
1842
|
-
content: { value: Symbol('content') },
|
|
1843
|
-
[Symbol.toStringTag]: { value: 'ReactiveWeakSet' },
|
|
1844
|
-
});
|
|
1845
|
-
}
|
|
1846
|
-
add(value) {
|
|
1847
|
-
const had = this[native].has(value);
|
|
1848
|
-
this[native].add(value);
|
|
1849
|
-
if (!had) {
|
|
1850
|
-
// touch the specific value and the collection view
|
|
1851
|
-
touched1(this.content, { type: 'add', prop: value }, value);
|
|
1852
|
-
// no size/allProps for WeakSet
|
|
1853
|
-
}
|
|
1854
|
-
return this;
|
|
1855
|
-
}
|
|
1856
|
-
delete(value) {
|
|
1857
|
-
const had = this[native].has(value);
|
|
1858
|
-
const res = this[native].delete(value);
|
|
1859
|
-
if (had)
|
|
1860
|
-
touched1(this.content, { type: 'del', prop: value }, value);
|
|
1861
|
-
return res;
|
|
1862
|
-
}
|
|
1863
|
-
has(value) {
|
|
1864
|
-
dependant(this.content, value);
|
|
1865
|
-
return this[native].has(value);
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
/**
|
|
1869
|
-
* Reactive wrapper around JavaScript's Set class
|
|
1870
|
-
* Tracks size changes, individual value operations, and collection-wide operations
|
|
1871
|
-
*/
|
|
1872
|
-
class ReactiveSet {
|
|
1873
|
-
constructor(original) {
|
|
1874
|
-
Object.defineProperties(this, {
|
|
1875
|
-
[native]: { value: original },
|
|
1876
|
-
[prototypeForwarding]: { value: original },
|
|
1877
|
-
content: { value: Symbol('content') },
|
|
1878
|
-
[Symbol.toStringTag]: { value: 'ReactiveSet' },
|
|
1879
|
-
});
|
|
1880
|
-
}
|
|
1881
|
-
get size() {
|
|
1882
|
-
// size depends on the wrapper instance, like Map counterpart
|
|
1883
|
-
dependant(this, 'size');
|
|
1884
|
-
return this[native].size;
|
|
1885
|
-
}
|
|
1886
|
-
add(value) {
|
|
1887
|
-
const had = this[native].has(value);
|
|
1888
|
-
const reactiveValue = reactive(value);
|
|
1889
|
-
this[native].add(reactiveValue);
|
|
1890
|
-
if (!had) {
|
|
1891
|
-
const evolution = { type: 'add', prop: reactiveValue };
|
|
1892
|
-
// touch for value-specific and aggregate dependencies
|
|
1893
|
-
touched1(this.content, evolution, reactiveValue);
|
|
1894
|
-
touched1(this, evolution, 'size');
|
|
1895
|
-
}
|
|
1896
|
-
return this;
|
|
1897
|
-
}
|
|
1898
|
-
clear() {
|
|
1899
|
-
const hadEntries = this[native].size > 0;
|
|
1900
|
-
this[native].clear();
|
|
1901
|
-
if (hadEntries) {
|
|
1902
|
-
const evolution = { type: 'bunch', method: 'clear' };
|
|
1903
|
-
touched1(this, evolution, 'size');
|
|
1904
|
-
touched(this.content, evolution);
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
delete(value) {
|
|
1908
|
-
const had = this[native].has(value);
|
|
1909
|
-
const res = this[native].delete(value);
|
|
1910
|
-
if (had) {
|
|
1911
|
-
const evolution = { type: 'del', prop: value };
|
|
1912
|
-
touched1(this.content, evolution, value);
|
|
1913
|
-
touched1(this, evolution, 'size');
|
|
1914
|
-
}
|
|
1915
|
-
return res;
|
|
1916
|
-
}
|
|
1917
|
-
has(value) {
|
|
1918
|
-
dependant(this.content, value);
|
|
1919
|
-
return this[native].has(value);
|
|
1920
|
-
}
|
|
1921
|
-
entries() {
|
|
1922
|
-
dependant(this.content);
|
|
1923
|
-
return makeReactiveEntriesIterator(this[native].entries());
|
|
1924
|
-
}
|
|
1925
|
-
forEach(callbackfn, thisArg) {
|
|
1926
|
-
dependant(this.content);
|
|
1927
|
-
this[native].forEach(callbackfn, thisArg);
|
|
1928
|
-
}
|
|
1929
|
-
keys() {
|
|
1930
|
-
dependant(this.content);
|
|
1931
|
-
return makeReactiveIterator(this[native].keys());
|
|
1932
|
-
}
|
|
1933
|
-
values() {
|
|
1934
|
-
dependant(this.content);
|
|
1935
|
-
return makeReactiveIterator(this[native].values());
|
|
1936
|
-
}
|
|
1937
|
-
[Symbol.iterator]() {
|
|
1938
|
-
dependant(this.content);
|
|
1939
|
-
const nativeIterator = this[native][Symbol.iterator]();
|
|
1940
|
-
return {
|
|
1941
|
-
next() {
|
|
1942
|
-
const result = nativeIterator.next();
|
|
1943
|
-
if (result.done) {
|
|
1944
|
-
return result;
|
|
1945
|
-
}
|
|
1946
|
-
return { value: reactive(result.value), done: false };
|
|
1947
|
-
},
|
|
1948
|
-
};
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
// Register native collection types to use specialized reactive wrappers
|
|
1953
|
-
registerNativeReactivity(WeakMap, ReactiveWeakMap);
|
|
1954
|
-
registerNativeReactivity(Map, ReactiveMap);
|
|
1955
|
-
registerNativeReactivity(WeakSet, ReactiveWeakSet);
|
|
1956
|
-
registerNativeReactivity(Set, ReactiveSet);
|
|
1957
|
-
registerNativeReactivity(Array, ReactiveArray);
|
|
1958
|
-
|
|
1959
|
-
export { Reactive as R, activeEffect as a, addBatchCleanup as b, atomic as c, isNonReactive as d, effect as e, isReactive as f, getState as g, ReactiveBase as h, immutables as i, ReactiveError as j, unwrap as k, cleanedBy as l, mixin as m, computed as n, options as o, profileInfo as p, invalidateComputed as q, reactive as r, unreactive as s, trackEffect as t, untracked as u, watch as w };
|
|
1960
|
-
//# sourceMappingURL=index-DBScoeCX.esm.js.map
|