watch-state 3.5.0-alpha.3 → 3.5.0

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 (84) hide show
  1. package/Cache/Cache.d.ts +5 -15
  2. package/Cache/Cache.es6.js +6 -68
  3. package/Cache/Cache.js +6 -68
  4. package/Cache/Cache.test.d.ts +1 -0
  5. package/Compute/Compute.d.ts +82 -0
  6. package/Compute/Compute.es6.js +184 -0
  7. package/Compute/Compute.js +191 -0
  8. package/Compute/Compute.test.d.ts +1 -0
  9. package/Compute/index.d.ts +1 -0
  10. package/Compute/index.es6.js +1 -0
  11. package/Compute/index.js +12 -0
  12. package/Observable/Observable.d.ts +21 -1
  13. package/Observable/Observable.es6.js +17 -0
  14. package/Observable/Observable.js +17 -0
  15. package/README.md +421 -111
  16. package/State/State.d.ts +45 -1
  17. package/State/State.es6.js +48 -4
  18. package/State/State.js +49 -5
  19. package/State/State.test.d.ts +1 -0
  20. package/Watch/Watch.d.ts +26 -5
  21. package/Watch/Watch.es6.js +30 -13
  22. package/Watch/Watch.js +30 -13
  23. package/Watch/Watch.test.d.ts +1 -0
  24. package/constants.d.ts +1 -0
  25. package/constants.es6.js +1 -0
  26. package/constants.js +1 -0
  27. package/helpers/bindObserver/bindObserver.d.ts +2 -0
  28. package/helpers/bindObserver/bindObserver.es6.js +13 -0
  29. package/helpers/bindObserver/bindObserver.js +17 -0
  30. package/helpers/bindObserver/index.d.ts +1 -0
  31. package/helpers/bindObserver/index.es6.js +1 -0
  32. package/helpers/bindObserver/index.js +9 -0
  33. package/helpers/{clearWatchers → clearWatcher}/clearWatcher.es6.js +1 -1
  34. package/helpers/{clearWatchers → clearWatcher}/clearWatcher.js +1 -1
  35. package/helpers/destroyWatchers/destroyWatchers.es6.js +1 -1
  36. package/helpers/destroyWatchers/destroyWatchers.js +1 -1
  37. package/helpers/index.d.ts +3 -3
  38. package/helpers/index.es6.js +3 -3
  39. package/helpers/index.js +3 -3
  40. package/helpers/invalidateCache/invalidateCache.d.ts +4 -1
  41. package/helpers/invalidateCache/invalidateCache.es6.js +8 -13
  42. package/helpers/invalidateCache/invalidateCache.js +8 -13
  43. package/helpers/watchWithScope/watchWithScope.d.ts +1 -1
  44. package/helpers/watchWithScope/watchWithScope.es6.js +4 -4
  45. package/helpers/watchWithScope/watchWithScope.js +4 -4
  46. package/index.d.ts +6 -5
  47. package/index.es6.js +17 -14
  48. package/index.js +31 -24
  49. package/index.min.js +1 -0
  50. package/index.test.d.ts +1 -0
  51. package/package.json +30 -7
  52. package/types.d.ts +24 -3
  53. package/utils/callEvent/callEvent.d.ts +50 -0
  54. package/utils/callEvent/callEvent.es6.js +69 -0
  55. package/utils/callEvent/callEvent.js +73 -0
  56. package/utils/callEvent/callEvent.test.d.ts +1 -0
  57. package/utils/callEvent/index.d.ts +1 -0
  58. package/utils/callEvent/index.es6.js +1 -0
  59. package/utils/callEvent/index.js +9 -0
  60. package/utils/createEvent/createEvent.d.ts +52 -6
  61. package/utils/createEvent/createEvent.es6.js +57 -11
  62. package/utils/createEvent/createEvent.js +58 -12
  63. package/utils/createEvent/createEvent.test.d.ts +1 -0
  64. package/utils/index.d.ts +3 -2
  65. package/utils/index.es6.js +3 -2
  66. package/utils/index.js +3 -2
  67. package/utils/onDestroy/onDestroy.d.ts +3 -2
  68. package/utils/onDestroy/onDestroy.es6.js +1 -1
  69. package/utils/onDestroy/onDestroy.js +1 -1
  70. package/utils/onDestroy/onDestroy.test.d.ts +1 -0
  71. package/utils/unwatch/unwatch.d.ts +10 -5
  72. package/utils/unwatch/unwatch.es6.js +10 -5
  73. package/utils/unwatch/unwatch.js +10 -5
  74. package/utils/unwatch/unwatch.test.d.ts +1 -0
  75. package/helpers/queueWatchers/index.d.ts +0 -1
  76. package/helpers/queueWatchers/index.es6.js +0 -1
  77. package/helpers/queueWatchers/index.js +0 -10
  78. package/helpers/queueWatchers/queueWatchers.d.ts +0 -3
  79. package/helpers/queueWatchers/queueWatchers.es6.js +0 -42
  80. package/helpers/queueWatchers/queueWatchers.js +0 -47
  81. /package/helpers/{clearWatchers → clearWatcher}/clearWatcher.d.ts +0 -0
  82. /package/helpers/{clearWatchers → clearWatcher}/index.d.ts +0 -0
  83. /package/helpers/{clearWatchers → clearWatcher}/index.es6.js +0 -0
  84. /package/helpers/{clearWatchers → clearWatcher}/index.js +0 -0
package/Cache/Cache.d.ts CHANGED
@@ -1,16 +1,6 @@
1
- import { Observable } from '../Observable';
2
- import { type Observer, type Watcher } from '../types';
3
- export declare class Cache<V = unknown> extends Observable<V> implements Observer {
4
- invalid: boolean;
5
- updated: boolean;
6
- destroyed: boolean;
7
- isCache: boolean;
8
- destructors: Set<Function>;
9
- childWatchers: Set<Observer>;
10
- readonly watcher: Watcher<V>;
11
- constructor(watcher: Watcher<V>, freeParent?: boolean, fireImmediately?: boolean);
12
- update(): void;
13
- forceUpdate(): void;
14
- get value(): V;
15
- destroy(): void;
1
+ import { Compute } from '../Compute';
2
+ /**
3
+ * @deprecated Use `Compute`
4
+ */
5
+ export declare class Cache<V = unknown> extends Compute<V> {
16
6
  }
@@ -1,72 +1,10 @@
1
- import { scope } from '../constants.es6.js';
2
- import '../helpers/index.es6.js';
3
- import '../Observable/index.es6.js';
4
- import '../Watch/index.es6.js';
5
- import { Observable } from '../Observable/Observable.es6.js';
6
- import { invalidateCache } from '../helpers/invalidateCache/invalidateCache.es6.js';
7
- import { Watch } from '../Watch/Watch.es6.js';
8
- import { watchWithScope } from '../helpers/watchWithScope/watchWithScope.es6.js';
9
- import { queueWatchers } from '../helpers/queueWatchers/queueWatchers.es6.js';
10
- import { destroyWatchers } from '../helpers/destroyWatchers/destroyWatchers.es6.js';
1
+ import '../Compute/index.es6.js';
2
+ import { Compute } from '../Compute/Compute.es6.js';
11
3
 
12
- class Cache extends Observable {
13
- constructor(watcher, freeParent, fireImmediately) {
14
- super();
15
- this.invalid = true;
16
- this.updated = false;
17
- this.destroyed = false;
18
- this.isCache = true;
19
- // Observer
20
- this.destructors = new Set();
21
- this.childWatchers = new Set();
22
- this.watcher = watcher;
23
- if (!freeParent) {
24
- const { activeWatcher } = scope;
25
- if (activeWatcher) {
26
- activeWatcher.childWatchers.add(this);
27
- activeWatcher.destructors.add(() => {
28
- activeWatcher.childWatchers.delete(this);
29
- });
30
- }
31
- }
32
- if (fireImmediately) {
33
- this.forceUpdate();
34
- }
35
- }
36
- update() {
37
- invalidateCache(this);
38
- const parents = [...this.observers];
39
- let parent;
40
- while ((parent = parents.pop())) {
41
- if (parent instanceof Watch) {
42
- return this.forceUpdate();
43
- }
44
- if (parent instanceof Cache) {
45
- parents.push(...parent.observers);
46
- }
47
- }
48
- }
49
- forceUpdate() {
50
- if (!this.destroyed) {
51
- this.invalid = false;
52
- watchWithScope(this, () => {
53
- const newValue = this.watcher(this.updated ? this.updated = true : false);
54
- if (newValue !== this.rawValue) {
55
- this.rawValue = newValue;
56
- queueWatchers(this.observers);
57
- }
58
- });
59
- }
60
- }
61
- get value() {
62
- if (this.invalid) {
63
- this.forceUpdate();
64
- }
65
- return this.destroyed ? this.rawValue : super.value;
66
- }
67
- destroy() {
68
- destroyWatchers(this);
69
- }
4
+ /**
5
+ * @deprecated Use `Compute`
6
+ */
7
+ class Cache extends Compute {
70
8
  }
71
9
 
72
10
  export { Cache };
package/Cache/Cache.js CHANGED
@@ -2,75 +2,13 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var constants = require('../constants.js');
6
- require('../helpers/index.js');
7
- require('../Observable/index.js');
8
- require('../Watch/index.js');
9
- var Observable = require('../Observable/Observable.js');
10
- var invalidateCache = require('../helpers/invalidateCache/invalidateCache.js');
11
- var Watch = require('../Watch/Watch.js');
12
- var watchWithScope = require('../helpers/watchWithScope/watchWithScope.js');
13
- var queueWatchers = require('../helpers/queueWatchers/queueWatchers.js');
14
- var destroyWatchers = require('../helpers/destroyWatchers/destroyWatchers.js');
5
+ require('../Compute/index.js');
6
+ var Compute = require('../Compute/Compute.js');
15
7
 
16
- class Cache extends Observable.Observable {
17
- constructor(watcher, freeParent, fireImmediately) {
18
- super();
19
- this.invalid = true;
20
- this.updated = false;
21
- this.destroyed = false;
22
- this.isCache = true;
23
- // Observer
24
- this.destructors = new Set();
25
- this.childWatchers = new Set();
26
- this.watcher = watcher;
27
- if (!freeParent) {
28
- const { activeWatcher } = constants.scope;
29
- if (activeWatcher) {
30
- activeWatcher.childWatchers.add(this);
31
- activeWatcher.destructors.add(() => {
32
- activeWatcher.childWatchers.delete(this);
33
- });
34
- }
35
- }
36
- if (fireImmediately) {
37
- this.forceUpdate();
38
- }
39
- }
40
- update() {
41
- invalidateCache.invalidateCache(this);
42
- const parents = [...this.observers];
43
- let parent;
44
- while ((parent = parents.pop())) {
45
- if (parent instanceof Watch.Watch) {
46
- return this.forceUpdate();
47
- }
48
- if (parent instanceof Cache) {
49
- parents.push(...parent.observers);
50
- }
51
- }
52
- }
53
- forceUpdate() {
54
- if (!this.destroyed) {
55
- this.invalid = false;
56
- watchWithScope.watchWithScope(this, () => {
57
- const newValue = this.watcher(this.updated ? this.updated = true : false);
58
- if (newValue !== this.rawValue) {
59
- this.rawValue = newValue;
60
- queueWatchers.queueWatchers(this.observers);
61
- }
62
- });
63
- }
64
- }
65
- get value() {
66
- if (this.invalid) {
67
- this.forceUpdate();
68
- }
69
- return this.destroyed ? this.rawValue : super.value;
70
- }
71
- destroy() {
72
- destroyWatchers.destroyWatchers(this);
73
- }
8
+ /**
9
+ * @deprecated Use `Compute`
10
+ */
11
+ class Cache extends Compute.Compute {
74
12
  }
75
13
 
76
14
  exports.Cache = Cache;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,82 @@
1
+ import { Observable } from '../Observable';
2
+ import type { Destructor, Observer, Watcher } from '../types';
3
+ export declare function forceQueueWatchers(): void;
4
+ export declare function queueWatchers(observers: Set<Observer>): void;
5
+ export declare function invalidateCompute(observer: Observer): void;
6
+ /**
7
+ * Cached reactive computation with memoization.
8
+ * Recalculates value only when dependencies change and when it is actively consumed
9
+ * by a Watcher or another Compute that is itself consumed by a Watcher.
10
+ *
11
+ * This ensures that computations are only evaluated when their output is actually needed,
12
+ * enabling efficient lazy evaluation and automatic subscription management.
13
+ *
14
+ * @class Compute
15
+ * @extends Observable<V>
16
+ * @implements {Observer}
17
+ * @template V - computed value type
18
+ *
19
+ * @example
20
+ * const fullName = new State('Mighty Mike')
21
+ * const name = new Compute(() => fullName.value.split(' ')[1])
22
+ *
23
+ * // Only when accessed inside an `Observer`, `Compute` becomes active:
24
+ *
25
+ * const nameWatcher = new Watch(() => console.log(name.value))
26
+ * // Triggers computation and subscribes to `name`
27
+ *
28
+ * // This does NOT trigger recomputation:
29
+ * console.log(name.value)
30
+ *
31
+ * // If used inside another `Compute` that is watched, it triggers:
32
+ * const greeting = new Compute(() => `${name.value} How are you?`)
33
+ *
34
+ * const greetingWatcher new Watch(() => console.log(greeting.value))
35
+ * // Triggers greeting
36
+ *
37
+ * fullName.value = 'Mighty Michael'
38
+ * // Triggers full chain: fullName → name → greeting → greetingWatcher
39
+ *
40
+ * fullName.value = 'Deight Michael'
41
+ * // Triggers part of chain: fullName → name
42
+ */
43
+ export declare class Compute<V = unknown> extends Observable<V> implements Observer {
44
+ /** Indicates if computed value is stale and needs recalculation. */
45
+ invalid: boolean;
46
+ /** Tracks if the computation has run at least once. */
47
+ updated: boolean;
48
+ /**
49
+ * Indicates if observer has been destroyed.
50
+ * Prevents accidental use after cleanup.
51
+ */
52
+ destroyed: boolean;
53
+ /** @deprecated Use `observer instanceof Compute` */
54
+ isCache: boolean;
55
+ /** Cleanup functions to run on destroy (e.g., unsubscribes). */
56
+ readonly destructors: Set<Destructor>;
57
+ /** Child watchers created within this watcher's scope */
58
+ readonly childrenObservers: Set<Observer>;
59
+ /** @deprecated Use `childrenObservers` */
60
+ get childWatchers(): Set<Observer>;
61
+ readonly watcher: Watcher<V>;
62
+ constructor(watcher: Watcher<V>, freeParent?: boolean, fireImmediately?: boolean);
63
+ /** Mark computation as invalid and trigger propagation to parent observers. */
64
+ update(): void;
65
+ forceUpdate(): void;
66
+ /**
67
+ * Current value with automatic subscription.
68
+ *
69
+ * Accessing `value` inside an `Observer` automatically subscribes the watcher.
70
+ *
71
+ * @example
72
+ * const count = new State(0)
73
+ * const text = new Compute(() => `Count: ${count.value}`)
74
+ *
75
+ * new Watch(() => console.log(text.value)) // Count: 0
76
+ *
77
+ * count.value++ // Count: 1
78
+ */
79
+ get value(): V;
80
+ /** Stop observation and remove all dependencies. */
81
+ destroy(): void;
82
+ }
@@ -0,0 +1,184 @@
1
+ import { scope } from '../constants.es6.js';
2
+ import '../helpers/bindObserver/index.es6.js';
3
+ import '../helpers/clearWatcher/index.es6.js';
4
+ import '../helpers/destroyWatchers/index.es6.js';
5
+ import '../helpers/watchWithScope/index.es6.js';
6
+ import '../Observable/index.es6.js';
7
+ import '../utils/shiftSet/index.es6.js';
8
+ import { shiftSet } from '../utils/shiftSet/shiftSet.es6.js';
9
+ import { clearWatcher } from '../helpers/clearWatcher/clearWatcher.es6.js';
10
+ import { Observable } from '../Observable/Observable.es6.js';
11
+ import { bindObserver } from '../helpers/bindObserver/bindObserver.es6.js';
12
+ import { watchWithScope } from '../helpers/watchWithScope/watchWithScope.es6.js';
13
+ import { destroyWatchers } from '../helpers/destroyWatchers/destroyWatchers.es6.js';
14
+
15
+ /* queue */
16
+ let currentCompute;
17
+ let currentObserver;
18
+ let forcedQueue;
19
+ const computeStack = new Set();
20
+ const observersStack = new Set();
21
+ function forceQueueWatchers() {
22
+ if (forcedQueue)
23
+ return;
24
+ forcedQueue = true;
25
+ while ((currentCompute = shiftSet(computeStack)) || (currentObserver = shiftSet(observersStack))) {
26
+ if (currentCompute) {
27
+ currentCompute.invalid = true;
28
+ continue;
29
+ }
30
+ clearWatcher(currentObserver);
31
+ currentObserver.update();
32
+ }
33
+ forcedQueue = false;
34
+ }
35
+ function queueWatchers(observers) {
36
+ const useLoop = !scope.eventDeep && !observersStack.size && !computeStack.size;
37
+ const oldObserversStack = [...observersStack];
38
+ observersStack.clear();
39
+ observers.forEach(watcher => {
40
+ observersStack.add(watcher);
41
+ if (watcher instanceof Compute) {
42
+ computeStack.add(watcher);
43
+ }
44
+ });
45
+ oldObserversStack.forEach(observer => observersStack.add(observer));
46
+ if (useLoop) {
47
+ forceQueueWatchers();
48
+ }
49
+ }
50
+ /* invalidateCompute */
51
+ const invalidateStack = [];
52
+ let currentInvalidateObserver;
53
+ function invalidateCompute(observer) {
54
+ const skipLoop = invalidateStack.length;
55
+ invalidateStack.push(observer);
56
+ if (skipLoop)
57
+ return;
58
+ while ((currentInvalidateObserver = invalidateStack.shift())) {
59
+ if (currentInvalidateObserver instanceof Compute) {
60
+ invalidateStack.push(...currentInvalidateObserver.observers);
61
+ currentInvalidateObserver.invalid = true;
62
+ }
63
+ }
64
+ }
65
+ /* Compute */
66
+ /**
67
+ * Cached reactive computation with memoization.
68
+ * Recalculates value only when dependencies change and when it is actively consumed
69
+ * by a Watcher or another Compute that is itself consumed by a Watcher.
70
+ *
71
+ * This ensures that computations are only evaluated when their output is actually needed,
72
+ * enabling efficient lazy evaluation and automatic subscription management.
73
+ *
74
+ * @class Compute
75
+ * @extends Observable<V>
76
+ * @implements {Observer}
77
+ * @template V - computed value type
78
+ *
79
+ * @example
80
+ * const fullName = new State('Mighty Mike')
81
+ * const name = new Compute(() => fullName.value.split(' ')[1])
82
+ *
83
+ * // Only when accessed inside an `Observer`, `Compute` becomes active:
84
+ *
85
+ * const nameWatcher = new Watch(() => console.log(name.value))
86
+ * // Triggers computation and subscribes to `name`
87
+ *
88
+ * // This does NOT trigger recomputation:
89
+ * console.log(name.value)
90
+ *
91
+ * // If used inside another `Compute` that is watched, it triggers:
92
+ * const greeting = new Compute(() => `${name.value} How are you?`)
93
+ *
94
+ * const greetingWatcher new Watch(() => console.log(greeting.value))
95
+ * // Triggers greeting
96
+ *
97
+ * fullName.value = 'Mighty Michael'
98
+ * // Triggers full chain: fullName → name → greeting → greetingWatcher
99
+ *
100
+ * fullName.value = 'Deight Michael'
101
+ * // Triggers part of chain: fullName → name
102
+ */
103
+ class Compute extends Observable {
104
+ // TODO: remove in major release
105
+ /** @deprecated Use `childrenObservers` */
106
+ get childWatchers() {
107
+ return this.childrenObservers;
108
+ }
109
+ constructor(watcher, freeParent, fireImmediately) {
110
+ super();
111
+ /** Indicates if computed value is stale and needs recalculation. */
112
+ this.invalid = true;
113
+ /** Tracks if the computation has run at least once. */
114
+ this.updated = false;
115
+ /**
116
+ * Indicates if observer has been destroyed.
117
+ * Prevents accidental use after cleanup.
118
+ */
119
+ this.destroyed = false;
120
+ // TODO: remove in major release
121
+ /** @deprecated Use `observer instanceof Compute` */
122
+ this.isCache = true;
123
+ /** Cleanup functions to run on destroy (e.g., unsubscribes). */
124
+ this.destructors = new Set();
125
+ /** Child watchers created within this watcher's scope */
126
+ this.childrenObservers = new Set();
127
+ this.watcher = watcher;
128
+ if (!freeParent) {
129
+ bindObserver(this);
130
+ }
131
+ if (fireImmediately) {
132
+ this.forceUpdate();
133
+ }
134
+ }
135
+ /** Mark computation as invalid and trigger propagation to parent observers. */
136
+ update() {
137
+ invalidateCompute(this);
138
+ const parents = [...this.observers];
139
+ let parent;
140
+ while ((parent = parents.pop())) {
141
+ if (!(parent instanceof Compute)) {
142
+ return this.forceUpdate();
143
+ }
144
+ parents.push(...parent.observers);
145
+ }
146
+ }
147
+ forceUpdate() {
148
+ if (!this.destroyed) {
149
+ this.invalid = false;
150
+ watchWithScope(this, () => {
151
+ const newValue = this.watcher(this.updated ? this.updated = true : false);
152
+ if (newValue !== this.rawValue) {
153
+ this.rawValue = newValue;
154
+ queueWatchers(this.observers);
155
+ }
156
+ });
157
+ }
158
+ }
159
+ /**
160
+ * Current value with automatic subscription.
161
+ *
162
+ * Accessing `value` inside an `Observer` automatically subscribes the watcher.
163
+ *
164
+ * @example
165
+ * const count = new State(0)
166
+ * const text = new Compute(() => `Count: ${count.value}`)
167
+ *
168
+ * new Watch(() => console.log(text.value)) // Count: 0
169
+ *
170
+ * count.value++ // Count: 1
171
+ */
172
+ get value() {
173
+ if (this.invalid) {
174
+ this.forceUpdate();
175
+ }
176
+ return this.destroyed ? this.rawValue : super.value;
177
+ }
178
+ /** Stop observation and remove all dependencies. */
179
+ destroy() {
180
+ destroyWatchers(this);
181
+ }
182
+ }
183
+
184
+ export { Compute, forceQueueWatchers, invalidateCompute, queueWatchers };
@@ -0,0 +1,191 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var constants = require('../constants.js');
6
+ require('../helpers/bindObserver/index.js');
7
+ require('../helpers/clearWatcher/index.js');
8
+ require('../helpers/destroyWatchers/index.js');
9
+ require('../helpers/watchWithScope/index.js');
10
+ require('../Observable/index.js');
11
+ require('../utils/shiftSet/index.js');
12
+ var shiftSet = require('../utils/shiftSet/shiftSet.js');
13
+ var clearWatcher = require('../helpers/clearWatcher/clearWatcher.js');
14
+ var Observable = require('../Observable/Observable.js');
15
+ var bindObserver = require('../helpers/bindObserver/bindObserver.js');
16
+ var watchWithScope = require('../helpers/watchWithScope/watchWithScope.js');
17
+ var destroyWatchers = require('../helpers/destroyWatchers/destroyWatchers.js');
18
+
19
+ /* queue */
20
+ let currentCompute;
21
+ let currentObserver;
22
+ let forcedQueue;
23
+ const computeStack = new Set();
24
+ const observersStack = new Set();
25
+ function forceQueueWatchers() {
26
+ if (forcedQueue)
27
+ return;
28
+ forcedQueue = true;
29
+ while ((currentCompute = shiftSet.shiftSet(computeStack)) || (currentObserver = shiftSet.shiftSet(observersStack))) {
30
+ if (currentCompute) {
31
+ currentCompute.invalid = true;
32
+ continue;
33
+ }
34
+ clearWatcher.clearWatcher(currentObserver);
35
+ currentObserver.update();
36
+ }
37
+ forcedQueue = false;
38
+ }
39
+ function queueWatchers(observers) {
40
+ const useLoop = !constants.scope.eventDeep && !observersStack.size && !computeStack.size;
41
+ const oldObserversStack = [...observersStack];
42
+ observersStack.clear();
43
+ observers.forEach(watcher => {
44
+ observersStack.add(watcher);
45
+ if (watcher instanceof Compute) {
46
+ computeStack.add(watcher);
47
+ }
48
+ });
49
+ oldObserversStack.forEach(observer => observersStack.add(observer));
50
+ if (useLoop) {
51
+ forceQueueWatchers();
52
+ }
53
+ }
54
+ /* invalidateCompute */
55
+ const invalidateStack = [];
56
+ let currentInvalidateObserver;
57
+ function invalidateCompute(observer) {
58
+ const skipLoop = invalidateStack.length;
59
+ invalidateStack.push(observer);
60
+ if (skipLoop)
61
+ return;
62
+ while ((currentInvalidateObserver = invalidateStack.shift())) {
63
+ if (currentInvalidateObserver instanceof Compute) {
64
+ invalidateStack.push(...currentInvalidateObserver.observers);
65
+ currentInvalidateObserver.invalid = true;
66
+ }
67
+ }
68
+ }
69
+ /* Compute */
70
+ /**
71
+ * Cached reactive computation with memoization.
72
+ * Recalculates value only when dependencies change and when it is actively consumed
73
+ * by a Watcher or another Compute that is itself consumed by a Watcher.
74
+ *
75
+ * This ensures that computations are only evaluated when their output is actually needed,
76
+ * enabling efficient lazy evaluation and automatic subscription management.
77
+ *
78
+ * @class Compute
79
+ * @extends Observable<V>
80
+ * @implements {Observer}
81
+ * @template V - computed value type
82
+ *
83
+ * @example
84
+ * const fullName = new State('Mighty Mike')
85
+ * const name = new Compute(() => fullName.value.split(' ')[1])
86
+ *
87
+ * // Only when accessed inside an `Observer`, `Compute` becomes active:
88
+ *
89
+ * const nameWatcher = new Watch(() => console.log(name.value))
90
+ * // Triggers computation and subscribes to `name`
91
+ *
92
+ * // This does NOT trigger recomputation:
93
+ * console.log(name.value)
94
+ *
95
+ * // If used inside another `Compute` that is watched, it triggers:
96
+ * const greeting = new Compute(() => `${name.value} How are you?`)
97
+ *
98
+ * const greetingWatcher new Watch(() => console.log(greeting.value))
99
+ * // Triggers greeting
100
+ *
101
+ * fullName.value = 'Mighty Michael'
102
+ * // Triggers full chain: fullName → name → greeting → greetingWatcher
103
+ *
104
+ * fullName.value = 'Deight Michael'
105
+ * // Triggers part of chain: fullName → name
106
+ */
107
+ class Compute extends Observable.Observable {
108
+ // TODO: remove in major release
109
+ /** @deprecated Use `childrenObservers` */
110
+ get childWatchers() {
111
+ return this.childrenObservers;
112
+ }
113
+ constructor(watcher, freeParent, fireImmediately) {
114
+ super();
115
+ /** Indicates if computed value is stale and needs recalculation. */
116
+ this.invalid = true;
117
+ /** Tracks if the computation has run at least once. */
118
+ this.updated = false;
119
+ /**
120
+ * Indicates if observer has been destroyed.
121
+ * Prevents accidental use after cleanup.
122
+ */
123
+ this.destroyed = false;
124
+ // TODO: remove in major release
125
+ /** @deprecated Use `observer instanceof Compute` */
126
+ this.isCache = true;
127
+ /** Cleanup functions to run on destroy (e.g., unsubscribes). */
128
+ this.destructors = new Set();
129
+ /** Child watchers created within this watcher's scope */
130
+ this.childrenObservers = new Set();
131
+ this.watcher = watcher;
132
+ if (!freeParent) {
133
+ bindObserver.bindObserver(this);
134
+ }
135
+ if (fireImmediately) {
136
+ this.forceUpdate();
137
+ }
138
+ }
139
+ /** Mark computation as invalid and trigger propagation to parent observers. */
140
+ update() {
141
+ invalidateCompute(this);
142
+ const parents = [...this.observers];
143
+ let parent;
144
+ while ((parent = parents.pop())) {
145
+ if (!(parent instanceof Compute)) {
146
+ return this.forceUpdate();
147
+ }
148
+ parents.push(...parent.observers);
149
+ }
150
+ }
151
+ forceUpdate() {
152
+ if (!this.destroyed) {
153
+ this.invalid = false;
154
+ watchWithScope.watchWithScope(this, () => {
155
+ const newValue = this.watcher(this.updated ? this.updated = true : false);
156
+ if (newValue !== this.rawValue) {
157
+ this.rawValue = newValue;
158
+ queueWatchers(this.observers);
159
+ }
160
+ });
161
+ }
162
+ }
163
+ /**
164
+ * Current value with automatic subscription.
165
+ *
166
+ * Accessing `value` inside an `Observer` automatically subscribes the watcher.
167
+ *
168
+ * @example
169
+ * const count = new State(0)
170
+ * const text = new Compute(() => `Count: ${count.value}`)
171
+ *
172
+ * new Watch(() => console.log(text.value)) // Count: 0
173
+ *
174
+ * count.value++ // Count: 1
175
+ */
176
+ get value() {
177
+ if (this.invalid) {
178
+ this.forceUpdate();
179
+ }
180
+ return this.destroyed ? this.rawValue : super.value;
181
+ }
182
+ /** Stop observation and remove all dependencies. */
183
+ destroy() {
184
+ destroyWatchers.destroyWatchers(this);
185
+ }
186
+ }
187
+
188
+ exports.Compute = Compute;
189
+ exports.forceQueueWatchers = forceQueueWatchers;
190
+ exports.invalidateCompute = invalidateCompute;
191
+ exports.queueWatchers = queueWatchers;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from './Compute';
@@ -0,0 +1 @@
1
+ export { Compute, forceQueueWatchers, invalidateCompute, queueWatchers } from './Compute.es6.js';
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var Compute = require('./Compute.js');
6
+
7
+
8
+
9
+ exports.Compute = Compute.Compute;
10
+ exports.forceQueueWatchers = Compute.forceQueueWatchers;
11
+ exports.invalidateCompute = Compute.invalidateCompute;
12
+ exports.queueWatchers = Compute.queueWatchers;