@planet-matrix/mobius-model 0.1.4 → 0.4.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 (102) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +25 -1
  3. package/dist/index.js +4 -2
  4. package/dist/index.js.map +27 -6
  5. package/package.json +9 -9
  6. package/scripts/build.ts +4 -4
  7. package/src/basic/README.md +143 -0
  8. package/src/basic/array.ts +872 -0
  9. package/src/basic/bigint.ts +114 -0
  10. package/src/basic/boolean.ts +180 -0
  11. package/src/basic/error.ts +51 -0
  12. package/src/basic/function.ts +453 -0
  13. package/src/basic/helper.ts +276 -0
  14. package/src/basic/index.ts +15 -0
  15. package/src/basic/is.ts +320 -0
  16. package/src/basic/number.ts +178 -0
  17. package/src/basic/object.ts +58 -0
  18. package/src/basic/promise.ts +464 -0
  19. package/src/basic/regexp.ts +7 -0
  20. package/src/basic/stream.ts +140 -0
  21. package/src/basic/string.ts +308 -0
  22. package/src/basic/symbol.ts +164 -0
  23. package/src/basic/temporal.ts +224 -0
  24. package/src/index.ts +3 -1
  25. package/src/reactor/README.md +18 -0
  26. package/src/reactor/index.ts +2 -0
  27. package/src/reactor/reactor-core/primitive.ts +1046 -0
  28. package/src/{signal/signal-core → reactor/reactor-core}/reactive-system.ts +392 -93
  29. package/src/reactor/reactor-operators/branch.ts +66 -0
  30. package/src/reactor/reactor-operators/convert.ts +70 -0
  31. package/src/reactor/reactor-operators/create.ts +66 -0
  32. package/src/reactor/reactor-operators/filter.ts +988 -0
  33. package/src/reactor/reactor-operators/index.ts +7 -0
  34. package/src/reactor/reactor-operators/join.ts +174 -0
  35. package/src/reactor/reactor-operators/map.ts +599 -0
  36. package/src/reactor/reactor-operators/utility.ts +102 -0
  37. package/src/type/README.md +330 -0
  38. package/src/type/array.ts +5 -0
  39. package/src/type/boolean.ts +471 -0
  40. package/src/type/class.ts +419 -0
  41. package/src/type/function.ts +1519 -0
  42. package/src/type/helper.ts +135 -0
  43. package/src/type/index.ts +14 -0
  44. package/src/type/intersection.ts +93 -0
  45. package/src/type/is.ts +247 -0
  46. package/src/type/iteration.ts +233 -0
  47. package/src/type/number.ts +732 -0
  48. package/src/type/object.ts +788 -0
  49. package/src/type/path.ts +73 -0
  50. package/src/type/string.ts +1004 -0
  51. package/src/type/tuple.ts +2424 -0
  52. package/src/type/union.ts +108 -0
  53. package/tests/unit/basic/array.spec.ts +290 -0
  54. package/tests/unit/basic/bigint.spec.ts +50 -0
  55. package/tests/unit/basic/boolean.spec.ts +74 -0
  56. package/tests/unit/basic/error.spec.ts +32 -0
  57. package/tests/unit/basic/function.spec.ts +175 -0
  58. package/tests/unit/basic/helper.spec.ts +118 -0
  59. package/tests/unit/basic/number.spec.ts +74 -0
  60. package/tests/unit/basic/object.spec.ts +15 -0
  61. package/tests/unit/basic/promise.spec.ts +232 -0
  62. package/tests/unit/basic/regexp.spec.ts +11 -0
  63. package/tests/unit/basic/stream.spec.ts +120 -0
  64. package/tests/unit/basic/string.spec.ts +74 -0
  65. package/tests/unit/basic/symbol.spec.ts +72 -0
  66. package/tests/unit/basic/temporal.spec.ts +78 -0
  67. package/tests/unit/{signal/computed.spec.ts → reactor/alien-signals-computed.spec.ts} +15 -10
  68. package/tests/unit/reactor/alien-signals-effect-scope.spec.ts +86 -0
  69. package/tests/unit/reactor/alien-signals-effect.spec.ts +395 -0
  70. package/tests/unit/reactor/alien-signals-topology.spec.ts +361 -0
  71. package/tests/unit/reactor/alien-signals-trigger.spec.ts +75 -0
  72. package/tests/unit/reactor/alien-signals-untrack.spec.ts +91 -0
  73. package/tests/unit/reactor/preact-signal.spec.ts +73 -0
  74. package/tests/unit/reactor/reactor-core.spec.ts +219 -0
  75. package/tests/unit/reactor/reactor-operators-branch.spec.ts +33 -0
  76. package/tests/unit/reactor/reactor-operators-convert.spec.ts +31 -0
  77. package/tests/unit/reactor/reactor-operators-create.spec.ts +47 -0
  78. package/tests/unit/reactor/reactor-operators-filter.spec.ts +604 -0
  79. package/tests/unit/reactor/reactor-operators-join.spec.ts +94 -0
  80. package/tests/unit/reactor/reactor-operators-map.spec.ts +327 -0
  81. package/tests/unit/reactor/reactor-operators-utility.spec.ts +55 -0
  82. package/dist/index.d.ts +0 -2
  83. package/dist/index.d.ts.map +0 -1
  84. package/dist/signal/index.d.ts +0 -3
  85. package/dist/signal/index.d.ts.map +0 -1
  86. package/dist/signal/signal-core/flags.d.ts +0 -99
  87. package/dist/signal/signal-core/flags.d.ts.map +0 -1
  88. package/dist/signal/signal-core/index.d.ts +0 -4
  89. package/dist/signal/signal-core/index.d.ts.map +0 -1
  90. package/dist/signal/signal-core/primitive.d.ts +0 -67
  91. package/dist/signal/signal-core/primitive.d.ts.map +0 -1
  92. package/dist/signal/signal-core/reactive-system.d.ts +0 -161
  93. package/dist/signal/signal-core/reactive-system.d.ts.map +0 -1
  94. package/dist/signal/signal-operators/index.d.ts +0 -4
  95. package/dist/signal/signal-operators/index.d.ts.map +0 -1
  96. package/src/signal/index.ts +0 -2
  97. package/src/signal/signal-core/README.md +0 -4
  98. package/src/signal/signal-core/primitive.ts +0 -275
  99. package/src/signal/signal-operators/index.ts +0 -19
  100. package/tests/unit/signal/effect.spec.ts +0 -108
  101. /package/src/{signal/signal-core → reactor/reactor-core}/flags.ts +0 -0
  102. /package/src/{signal/signal-core → reactor/reactor-core}/index.ts +0 -0
@@ -1,161 +0,0 @@
1
- import type { Flags } from './flags.ts';
2
- export interface Node {
3
- /**
4
- * 当前节点的上游连接链中的第一个连接。
5
- */
6
- headDepLink?: Link | undefined;
7
- /**
8
- * 当前节点的上游连接链中的最后一个连接。
9
- */
10
- tailDepLink?: Link | undefined;
11
- /**
12
- * 当前节点的下游连接链中的第一个连接。
13
- */
14
- headSubLink?: Link | undefined;
15
- /**
16
- * 当前节点的下游连接链中的最后一个连接。
17
- */
18
- tailSubLink?: Link | undefined;
19
- /**
20
- * 当前节点的标记。
21
- */
22
- flags: Flags;
23
- }
24
- /**
25
- * 连接用于表示某个节点(下游节点)依赖了某个节点(上游节点)。
26
- *
27
- * 多个连接连在一起形成连接链,连接链只有两种情况:
28
- *
29
- * 1. 下游连接链:链中所有连接的上游节点相同,通过该链可以找到该上游节点的所有下游节点。
30
- * 2. 上游连接链:链中所有连接的下游节点相同,通过该链可以找到该下游节点的所有上游节点。
31
- */
32
- export interface Link {
33
- /**
34
- * 连接中的上游节点。
35
- */
36
- dep: Node;
37
- /**
38
- * 连接中的上游节点的下游连接链中的上一个连接。(下一个下游连接)
39
- */
40
- prevSubLink?: Link | undefined;
41
- /**
42
- * 连接中的上游节点的下游连接链中的下一个连接。(上一个下游连接)
43
- */
44
- nextSubLink?: Link | undefined;
45
- /**
46
- * 连接中的下游节点。
47
- */
48
- sub: Node;
49
- /**
50
- * 连接中的下游节点的上游连接链中的上一个连接。(上一个上游连接)
51
- */
52
- prevDepLink?: Link | undefined;
53
- /**
54
- * 连接中的下游节点的上游连接链中的下一个连接。(下一个上游连接)
55
- */
56
- nextDepLink?: Link | undefined;
57
- }
58
- export interface CreateReactiveSystemOptions {
59
- /**
60
- * 当节点的值需要更新时此回调会执行,返回值表示更新前后值是否变化。
61
- */
62
- update(sub: Node): boolean;
63
- /**
64
- * 当节点的上游节点的值发生变化后此回调会执行。
65
- *
66
- * 注意:只有包含 Watching 标记的节点会触发此方法。
67
- */
68
- notify(node: Node): void;
69
- /**
70
- * 当节点不再拥有任何下游节点时此回调会执行。
71
- */
72
- unwatched(node: Node): void;
73
- }
74
- export interface ReactiveSystem {
75
- /**
76
- * 判断连接是否是节点的上游连接。
77
- */
78
- isDepLinkOfNode(link: Link, node: Node): boolean;
79
- /**
80
- * 判断连接是否是节点的下游连接。
81
- */
82
- isSubLinkOfNode(link: Link, node: Node): boolean;
83
- /**
84
- * 获取两个节点之间的连接。
85
- */
86
- getLinkOfNodes(dep: Node, sub: Node): Link | undefined;
87
- /**
88
- * 判断两个节点之间是否存在连接。
89
- */
90
- isLinked(nodeA: Node, nodeB: Node): boolean;
91
- /**
92
- * 为两个节点建立连接。
93
- */
94
- linkNode(dep: Node, sub: Node): void;
95
- /**
96
- * 为两个节点解除连接。
97
- */
98
- unlinkNode(nodeA: Node, nodeB: Node): void;
99
- /**
100
- * 解除连接。
101
- */
102
- unlink(link: Link): void;
103
- /**
104
- * 为节点解除所有上游连接。
105
- */
106
- unlinkAllDepLinksOfNode(node: Node): void;
107
- /**
108
- * 为节点解除所有下游连接。
109
- */
110
- unlinkAllSubLinksOfNode(node: Node): void;
111
- /**
112
- * 获取当前正在进行依赖收集的节点。
113
- */
114
- getActiveNodeAsSub(): Node | undefined;
115
- /**
116
- * 设置当前正在进行依赖收集的节点。
117
- */
118
- setActiveNodeAsSub(nodeAsSub: Node): void;
119
- /**
120
- * 重置当前正在进行依赖收集的节点。
121
- */
122
- resetActiveNodeAsSub(): void;
123
- /**
124
- * 将指定节点作为当前正在进行依赖收集的节点执行指定函数。
125
- */
126
- withActiveNodeAsSub<T>(nodeAsSub: Node, fn: () => T): T;
127
- /**
128
- * 开始对节点(作为下游节点)进行依赖收集。
129
- */
130
- startTracking(node: Node): void;
131
- /**
132
- * 结束对节点(作为下游节点)进行依赖收集。
133
- */
134
- endTracking(node: Node): void;
135
- /**
136
- * 对节点(作为下游节点)进行依赖收集。
137
- */
138
- withTracking<T>(node: Node, fn: () => T): T;
139
- /**
140
- * 为两个节点建立连接(依赖收集优化版)。
141
- */
142
- linkNodeOptimizedForTracking(dep: Node, sub: Node): void;
143
- /**
144
- * 将节点作为当前正在进行依赖收集的节点的上游节点。
145
- */
146
- track(node: Node): void;
147
- /**
148
- * 当节点的值发生变化后,调用此方法更新直接下游节点的标记。
149
- */
150
- shallowPropagate(node: Node): void;
151
- /**
152
- * 当节点的值发生变化后,调用此方法更新所有下游节点的标记。
153
- */
154
- deeeeepPropagate(node: Node): void;
155
- /**
156
- * 解决节点的 Pending 标记。
157
- */
158
- resolvePending(node: Node): void;
159
- }
160
- export declare const createReactiveSystem: (options: CreateReactiveSystemOptions) => ReactiveSystem;
161
- //# sourceMappingURL=reactive-system.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reactive-system.d.ts","sourceRoot":"","sources":["../../../src/signal/signal-core/reactive-system.ts"],"names":[],"mappings":"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"}
@@ -1,4 +0,0 @@
1
- import type { Signal } from "#Source/index.ts";
2
- export declare const withHistory: <T>(orginal: Signal<T>) => Signal<T[]>;
3
- export declare const zip: <T, U>(signal1: Signal<T>, signal2: Signal<U>) => Signal<[T, U]>;
4
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/signal/signal-operators/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAG9C,eAAO,MAAM,WAAW,GAAI,CAAC,oCAO5B,CAAA;AAED,eAAO,MAAM,GAAG,GAAI,CAAC,EAAE,CAAC,2DAMvB,CAAA"}
@@ -1,2 +0,0 @@
1
- export * from "./signal-core/index.ts"
2
- export * from "./signal-operators/index.ts"
@@ -1,4 +0,0 @@
1
- # Signals
2
-
3
- - https://github.com/stackblitz/alien-signals
4
- - https://github.com/johnsoncodehk/alien-signals-starter
@@ -1,275 +0,0 @@
1
- import type { Flags } from "./flags.ts"
2
- import { createReactiveSystem } from "./reactive-system.ts"
3
- import { flags } from "./flags.ts"
4
- import type { Link, Node } from "./reactive-system.ts"
5
-
6
- const VOID = Symbol("void") as unknown;
7
-
8
- const queue: Effect[] = [];
9
- const flush = (): void => {
10
- while (queue.length !== 0) {
11
- queue.shift()!.run();
12
- }
13
- }
14
-
15
- let batchDepth = 0;
16
- export const startBatch = (): void => {
17
- batchDepth = batchDepth + 1;
18
- }
19
- export const endBatch = (): void => {
20
- batchDepth = batchDepth - 1;
21
- if (batchDepth === 0) {
22
- flush();
23
- }
24
- }
25
-
26
- const reactiveSystem = createReactiveSystem({
27
- update(node: Signal | Computed) {
28
- return node.update();
29
- },
30
- notify(node: Effect) {
31
- const nodes = []
32
- let currentNode: Effect | undefined = node
33
- while (currentNode !== undefined) {
34
- if (currentNode.flags.hasWatching() === false) {
35
- break
36
- }
37
- node.notify();
38
- nodes.push(currentNode);
39
- const nextNode: Node | undefined = currentNode?.headSubLink?.sub
40
- currentNode = nextNode instanceof Effect ? nextNode : undefined
41
- }
42
- queue.push(...nodes.toReversed());
43
- },
44
- unwatched(node: Signal | Computed | Effect) {
45
- return node.stop();
46
- }
47
- });
48
- const {
49
- unlinkAllDepLinksOfNode,
50
-
51
- withTracking,
52
- track,
53
-
54
- shallowPropagate,
55
- deeeeepPropagate,
56
- resolvePending,
57
- } = reactiveSystem
58
-
59
- /**
60
- * Signal 的值只有在被获取的时候才会更新。
61
- */
62
- export class Signal<T = unknown> implements Node {
63
- headSubLink: Link | undefined;
64
- tailSubLink: Link | undefined;
65
- flags: Flags;
66
-
67
- value: T;
68
- pendingValue: T;
69
-
70
- constructor(value: T) {
71
- this.headSubLink = undefined;
72
- this.tailSubLink = undefined;
73
- this.flags = flags().setMutable();
74
-
75
- this.pendingValue = value;
76
- this.value = value;
77
- }
78
-
79
- get(): T {
80
- let valueChanged = false
81
-
82
- // 如果包含 Dirty 标记,则进行更新
83
- const shouldUpdate = this.flags.hasDirty();
84
- if (shouldUpdate === true) {
85
- valueChanged = this.update()
86
- }
87
-
88
- // 如果值发生变化,则更新直接下游节点的标记
89
- if (valueChanged === true) {
90
- shallowPropagate(this);
91
- }
92
-
93
- // 自动进行依赖收集
94
- track(this);
95
-
96
- return this.value;
97
- }
98
-
99
- set(value: T): void {
100
- // 将新值暂存起来,等用到的时候再执行更新
101
- this.pendingValue = value;
102
- // 为当前节点添加 Dirty 标记
103
- this.flags.setDirty();
104
-
105
- // 更新所有下游节点的标记
106
- deeeeepPropagate(this);
107
-
108
- // 执行更新队列
109
- if (batchDepth === 0) {
110
- flush();
111
- }
112
- }
113
-
114
- /**
115
- * 返回值表示更新前后值是否变化。
116
- */
117
- update(): boolean {
118
- this.flags.unsetDirty();
119
-
120
- const oldValue = this.value;
121
- const newValue = this.pendingValue;
122
- this.value = newValue;
123
-
124
- return oldValue !== newValue;
125
- }
126
-
127
- stop(): void {
128
- // do nothing
129
- }
130
- }
131
-
132
- /**
133
- * Computed 的值只有在被获取的时候才会更新。
134
- * Computed 的值只有在上游节点的值发生变化时才会更新。
135
- * Computed 的下游节点全部解除连接时会自动与所有上游节点解除连接,并回到初始状态。
136
- */
137
- export class Computed<T = unknown> implements Node {
138
- headDepLink: Link | undefined;
139
- tailDepLink: Link | undefined;
140
- headSubLink: Link | undefined;
141
- tailSubLink: Link | undefined;
142
- flags: Flags;
143
-
144
- value: T;
145
- getValue: () => T;
146
-
147
- constructor(getValue: () => T) {
148
- this.headDepLink = undefined;
149
- this.tailDepLink = undefined;
150
- this.headSubLink = undefined;
151
- this.tailSubLink = undefined;
152
- // 值在创建时尚未计算,因此初始时添加 Dirty 标记
153
- this.flags = flags().setMutable().setDirty();
154
-
155
- // undefined 可能是使用者期待的合法值,因此这里使用 VOID 占位
156
- // oxlint-disable-next-line no-unsafe-type-assertion
157
- this.value = VOID as T;
158
- this.getValue = getValue;
159
- }
160
-
161
- get(): T {
162
- let valueChanged = false;
163
-
164
- // 如果包含 Pending 标记,则进行解决
165
- const shouldResolve = this.flags.hasPending();
166
- if (shouldResolve === true) {
167
- resolvePending(this);
168
- }
169
-
170
- // 如果包含 Dirty 标记,则进行更新
171
- const shouldUpdate = this.flags.hasDirty();
172
- if (shouldUpdate === true) {
173
- valueChanged = this.update()
174
- }
175
-
176
- // 如果值变化,则通知下游(仅通知直接下游)
177
- if (valueChanged === true) {
178
- shallowPropagate(this);
179
- }
180
-
181
- // 自动进行依赖收集
182
- track(this);
183
-
184
- return this.value;
185
- }
186
-
187
- /**
188
- * 返回值表示更新前后值是否变化。
189
- */
190
- update(): boolean {
191
- const result = withTracking(this, () => {
192
- const oldValue = this.value;
193
- const newValue = this.getValue();
194
- this.value = newValue;
195
- return oldValue !== newValue;
196
- })
197
- return result;
198
- }
199
-
200
- stop(): void {
201
- unlinkAllDepLinksOfNode(this);
202
- this.flags = flags().setMutable().setDirty();
203
- }
204
- }
205
-
206
- /**
207
- * Effect 会在创建时立即执行一次传入的函数。
208
- * Effect 会在每次上游节点的值发生变化时重新执行传入的函数。
209
- * Effect 在任意时刻最多只能一个待运行。
210
- * Effect 可以与 Effect 建立连接。
211
- * 作为上游节点的 Effect 在没有下游节点时会自动与所有上游节点解除连接,并将标记清空。
212
- */
213
- export class Effect implements Node {
214
- headDepLink: Link | undefined;
215
- tailDepLink: Link | undefined;
216
- headSubLink: Link | undefined;
217
- tailSubLink: Link | undefined;
218
- flags: Flags;
219
-
220
- fn: () => void;
221
-
222
- constructor(fn: () => void) {
223
- this.headDepLink = undefined;
224
- this.tailDepLink = undefined;
225
- this.headSubLink = undefined;
226
- this.tailSubLink = undefined;
227
- // 在创建完成后即进入观察状态,因此初始时添加 Watching 标记
228
- this.flags = flags().setWatching();
229
-
230
- this.fn = fn;
231
- this.internalRun();
232
- }
233
-
234
- notify(): void {
235
- this.flags.unsetWatching();
236
- }
237
-
238
- run(): void {
239
- this.flags.setWatching();
240
-
241
- // 如果包含 Pending 标记,则进行解决
242
- const shouldResolve = this.flags.hasPending();
243
- if (shouldResolve === true) {
244
- resolvePending(this);
245
- }
246
-
247
- // 如果包含 Dirty 标记,则进行更新
248
- const shouldRun = this.flags.hasDirty();
249
- if (shouldRun === true) {
250
- this.internalRun();
251
- }
252
- }
253
-
254
- private internalRun(): void {
255
- track(this);
256
- withTracking(this, () => {
257
- return this.fn();
258
- })
259
- }
260
-
261
- stop(): void {
262
- unlinkAllDepLinksOfNode(this);
263
- this.flags.clear();
264
- }
265
- }
266
-
267
- export const signal = <T>(initialValue: T): Signal<T> => {
268
- return new Signal(initialValue);
269
- }
270
- export const computed = <T>(getter: () => T): Computed<T> => {
271
- return new Computed<T>(getter);
272
- }
273
- export const effect = (fn: () => void): Effect => {
274
- return new Effect(fn);
275
- }
@@ -1,19 +0,0 @@
1
- import type { Signal } from "#Source/index.ts"
2
- import { effect, signal } from "#Source/index.ts"
3
-
4
- export const withHistory = <T>(orginal: Signal<T>): Signal<T[]> => {
5
- const history: T[] = []
6
- effect(() => {
7
- history.push(orginal.get())
8
- })
9
- const result = signal<T[]>(history)
10
- return result
11
- }
12
-
13
- export const zip = <T, U>(signal1: Signal<T>, signal2: Signal<U>): Signal<[T, U]> => {
14
- const result = signal<[T, U]>([signal1.get(), signal2.get()])
15
- effect(() => {
16
- result.set([signal1.get(), signal2.get()])
17
- })
18
- return result
19
- }
@@ -1,108 +0,0 @@
1
- import { expect, test } from 'vitest';
2
- import { computed, effect, endBatch, signal, startBatch } from '#Source/index.ts';
3
-
4
- test('should clear subscriptions when untracked by all subscribers', () => {
5
- /**
6
- * a
7
- * |
8
- * b
9
- * |
10
- * e
11
- */
12
- let bRunTimes = 0;
13
- const a = signal(1);
14
- const b = computed(() => {
15
- bRunTimes = bRunTimes + 1;
16
- return a.get() * 2;
17
- });
18
- const e = effect(() => {
19
- b.get();
20
- });
21
-
22
- expect(bRunTimes).toBe(1);
23
- a.set(2);
24
- expect(bRunTimes).toBe(2);
25
- e.stop();
26
- a.set(3);
27
- expect(bRunTimes).toBe(2);
28
- });
29
-
30
- test('should not run untracked inner effect', () => {
31
- /**
32
- * a
33
- * / \
34
- * b e2(inner)
35
- * \ /
36
- * e1(outer)
37
- */
38
- let countOfInnerEffect = 0;
39
- const a = signal(3);
40
- const b = computed(() => a.get() > 0);
41
- effect(() => {
42
- if (b.get() === true) {
43
- countOfInnerEffect = countOfInnerEffect + 1;
44
- effect(() => {
45
- if (a.get() === 0) {
46
- throw new Error("bad");
47
- }
48
- });
49
- }
50
- });
51
-
52
- a.set(2);
53
- a.set(1);
54
- a.set(0);
55
- });
56
-
57
- test('should run outer effect first', () => {
58
- /**
59
- * a b
60
- * | \ /
61
- * | \ /
62
- * | e2(inner)
63
- * \ /
64
- * e1(outer)
65
- */
66
- const a = signal(1);
67
- const b = signal(1);
68
- effect(() => {
69
- if (a.get() !== 0) {
70
- effect(() => {
71
- b.get();
72
- if (a.get() === 0) {
73
- throw new Error("bad");
74
- }
75
- });
76
- }
77
- });
78
-
79
- startBatch();
80
- b.set(0);
81
- a.set(0);
82
- endBatch();
83
- });
84
-
85
- test('should not trigger inner effect when resolve maybe dirty', () => {
86
- /**
87
- * a
88
- * |
89
- * b
90
- * e2(inner)
91
- * |
92
- * e1(outer)
93
- */
94
- const a = signal(0);
95
- const b = computed(() => a.get() % 2);
96
- let innerTriggerTimes = 0;
97
- effect(() => {
98
- effect(() => {
99
- b.get();
100
- innerTriggerTimes = innerTriggerTimes + 1;
101
- if (innerTriggerTimes >= 2) {
102
- throw new Error("bad");
103
- }
104
- });
105
- });
106
-
107
- a.set(2);
108
- });