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