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.
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-79Kk8D6e.esm.js +4857 -0
  11. package/dist/chunks/index-79Kk8D6e.esm.js.map +1 -0
  12. package/dist/chunks/index-GRBSx0mB.js +4908 -0
  13. package/dist/chunks/index-GRBSx0mB.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 +37 -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 +602 -97
  47. package/dist/reactive.esm.js +3 -3
  48. package/dist/reactive.js +32 -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 +1345 -0
  78. package/src/reactive/index.ts +76 -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 +289 -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,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