@planet-matrix/mobius-model 0.1.3 → 0.3.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 (77) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +21 -0
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2 -2
  6. package/dist/index.js.map +12 -6
  7. package/dist/reactor/index.d.ts +3 -0
  8. package/dist/reactor/index.d.ts.map +1 -0
  9. package/dist/reactor/reactor-core/flags.d.ts.map +1 -0
  10. package/dist/reactor/reactor-core/index.d.ts.map +1 -0
  11. package/dist/reactor/reactor-core/primitive.d.ts +276 -0
  12. package/dist/reactor/reactor-core/primitive.d.ts.map +1 -0
  13. package/dist/{signal/signal-core → reactor/reactor-core}/reactive-system.d.ts +102 -22
  14. package/dist/reactor/reactor-core/reactive-system.d.ts.map +1 -0
  15. package/dist/reactor/reactor-operators/branch.d.ts +19 -0
  16. package/dist/reactor/reactor-operators/branch.d.ts.map +1 -0
  17. package/dist/reactor/reactor-operators/convert.d.ts +30 -0
  18. package/dist/reactor/reactor-operators/convert.d.ts.map +1 -0
  19. package/dist/reactor/reactor-operators/create.d.ts +26 -0
  20. package/dist/reactor/reactor-operators/create.d.ts.map +1 -0
  21. package/dist/reactor/reactor-operators/filter.d.ts +269 -0
  22. package/dist/reactor/reactor-operators/filter.d.ts.map +1 -0
  23. package/dist/reactor/reactor-operators/index.d.ts +8 -0
  24. package/dist/reactor/reactor-operators/index.d.ts.map +1 -0
  25. package/dist/reactor/reactor-operators/join.d.ts +48 -0
  26. package/dist/reactor/reactor-operators/join.d.ts.map +1 -0
  27. package/dist/reactor/reactor-operators/map.d.ts +165 -0
  28. package/dist/reactor/reactor-operators/map.d.ts.map +1 -0
  29. package/dist/reactor/reactor-operators/utility.d.ts +48 -0
  30. package/dist/reactor/reactor-operators/utility.d.ts.map +1 -0
  31. package/package.json +9 -12
  32. package/src/index.ts +1 -1
  33. package/src/reactor/README.md +18 -0
  34. package/src/reactor/index.ts +2 -0
  35. package/src/reactor/reactor-core/primitive.ts +1046 -0
  36. package/src/{signal/signal-core → reactor/reactor-core}/reactive-system.ts +392 -93
  37. package/src/reactor/reactor-operators/branch.ts +66 -0
  38. package/src/reactor/reactor-operators/convert.ts +70 -0
  39. package/src/reactor/reactor-operators/create.ts +66 -0
  40. package/src/reactor/reactor-operators/filter.ts +988 -0
  41. package/src/reactor/reactor-operators/index.ts +7 -0
  42. package/src/reactor/reactor-operators/join.ts +174 -0
  43. package/src/reactor/reactor-operators/map.ts +599 -0
  44. package/src/reactor/reactor-operators/utility.ts +102 -0
  45. package/tests/unit/{signal/computed.spec.ts → reactor/alien-signals-computed.spec.ts} +15 -10
  46. package/tests/unit/reactor/alien-signals-effect-scope.spec.ts +86 -0
  47. package/tests/unit/reactor/alien-signals-effect.spec.ts +395 -0
  48. package/tests/unit/reactor/alien-signals-topology.spec.ts +361 -0
  49. package/tests/unit/reactor/alien-signals-trigger.spec.ts +75 -0
  50. package/tests/unit/reactor/alien-signals-untrack.spec.ts +91 -0
  51. package/tests/unit/reactor/preact-signal.spec.ts +73 -0
  52. package/tests/unit/reactor/reactor-core.spec.ts +219 -0
  53. package/tests/unit/reactor/reactor-operators-branch.spec.ts +33 -0
  54. package/tests/unit/reactor/reactor-operators-convert.spec.ts +31 -0
  55. package/tests/unit/reactor/reactor-operators-create.spec.ts +47 -0
  56. package/tests/unit/reactor/reactor-operators-filter.spec.ts +604 -0
  57. package/tests/unit/reactor/reactor-operators-join.spec.ts +94 -0
  58. package/tests/unit/reactor/reactor-operators-map.spec.ts +327 -0
  59. package/tests/unit/reactor/reactor-operators-utility.spec.ts +55 -0
  60. package/dist/signal/index.d.ts +0 -3
  61. package/dist/signal/index.d.ts.map +0 -1
  62. package/dist/signal/signal-core/flags.d.ts.map +0 -1
  63. package/dist/signal/signal-core/index.d.ts.map +0 -1
  64. package/dist/signal/signal-core/primitive.d.ts +0 -67
  65. package/dist/signal/signal-core/primitive.d.ts.map +0 -1
  66. package/dist/signal/signal-core/reactive-system.d.ts.map +0 -1
  67. package/dist/signal/signal-operators/index.d.ts +0 -4
  68. package/dist/signal/signal-operators/index.d.ts.map +0 -1
  69. package/src/signal/index.ts +0 -2
  70. package/src/signal/signal-core/README.md +0 -4
  71. package/src/signal/signal-core/primitive.ts +0 -275
  72. package/src/signal/signal-operators/index.ts +0 -19
  73. package/tests/unit/signal/effect.spec.ts +0 -108
  74. /package/dist/{signal/signal-core → reactor/reactor-core}/flags.d.ts +0 -0
  75. /package/dist/{signal/signal-core → reactor/reactor-core}/index.d.ts +0 -0
  76. /package/src/{signal/signal-core → reactor/reactor-core}/flags.ts +0 -0
  77. /package/src/{signal/signal-core → reactor/reactor-core}/index.ts +0 -0
@@ -0,0 +1,361 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+
3
+ import { computed, effect, signal } from '#Source/reactor/index.ts';
4
+
5
+ /**
6
+ * 这个测试文件应该与 https://github.com/stackblitz/alien-signals/blob/master/tests/topology.spec.ts 保持同步。
7
+ */
8
+
9
+ describe("graph updates", () => {
10
+ test('should drop A->B->A updates', () => {
11
+ // A
12
+ // / |
13
+ // B | <- Looks like a flag doesn't it? :D
14
+ // \ |
15
+ // C
16
+ // |
17
+ // D
18
+ const a = signal(() => 2);
19
+
20
+ const b = computed(() => a.get() - 1);
21
+ const c = computed(() => a.get() + b.get());
22
+
23
+ const compute = vi.fn(() => `d: ${c.get()}`);
24
+ const d = computed(compute);
25
+
26
+ // Trigger read
27
+ expect(d.get()).toBe("d: 3");
28
+ expect(compute).toHaveBeenCalledOnce();
29
+ compute.mockClear();
30
+
31
+ a.set(4);
32
+ d.get();
33
+ expect(compute).toHaveBeenCalledOnce();
34
+ });
35
+
36
+ test('should only update every signal once (diamond graph)', () => {
37
+ // In this scenario "D" should only update once when "A" receives
38
+ // an update. This is sometimes referred to as the "diamond" scenario.
39
+ // A
40
+ // / \
41
+ // B C
42
+ // \ /
43
+ // D
44
+
45
+ const a = signal(() => "a");
46
+ const b = computed(() => a.get());
47
+ const c = computed(() => a.get());
48
+
49
+ const spy = vi.fn(() => `${b.get()} ${c.get()}`);
50
+ const d = computed(spy);
51
+
52
+ expect(d.get()).toBe("a a");
53
+ expect(spy).toHaveBeenCalledOnce();
54
+
55
+ a.set("aa");
56
+ expect(d.get()).toBe("aa aa");
57
+ expect(spy).toHaveBeenCalledTimes(2);
58
+ });
59
+
60
+ test('should only update every signal once (diamond graph + tail)', () => {
61
+ // "E" will be likely updated twice if our mark+sweep logic is buggy.
62
+ // A
63
+ // / \
64
+ // B C
65
+ // \ /
66
+ // D
67
+ // |
68
+ // E
69
+
70
+ const a = signal(() => "a");
71
+ const b = computed(() => a.get());
72
+ const c = computed(() => a.get());
73
+
74
+ const d = computed(() => `${b.get()} ${c.get()}`);
75
+
76
+ const spy = vi.fn(() => d.get());
77
+ const e = computed(spy);
78
+
79
+ expect(e.get()).toBe("a a");
80
+ expect(spy).toHaveBeenCalledOnce();
81
+
82
+ a.set("aa");
83
+ expect(e.get()).toBe("aa aa");
84
+ expect(spy).toHaveBeenCalledTimes(2);
85
+ });
86
+
87
+ test('should bail out if result is the same', () => {
88
+ // Bail out if value of "B" never changes
89
+ // A->B->C
90
+ const a = signal(() => "a");
91
+ const b = computed(() => {
92
+ a.get();
93
+ return "foo";
94
+ });
95
+
96
+ const spy = vi.fn(() => b.get());
97
+ const c = computed(spy);
98
+
99
+ expect(c.get()).toBe("foo");
100
+ expect(spy).toHaveBeenCalledOnce();
101
+
102
+ a.set("aa");
103
+ expect(c.get()).toBe("foo");
104
+ expect(spy).toHaveBeenCalledOnce();
105
+ });
106
+
107
+ test('should only update every signal once (jagged diamond graph + tails)', () => {
108
+ // "F" and "G" will be likely updated twice if our mark+sweep logic is buggy.
109
+ // A
110
+ // / \
111
+ // B C
112
+ // | |
113
+ // | D
114
+ // \ /
115
+ // E
116
+ // / \
117
+ // F G
118
+ const a = signal(() => "a");
119
+
120
+ const b = computed(() => a.get());
121
+ const c = computed(() => a.get());
122
+
123
+ const d = computed(() => c.get());
124
+
125
+ const eSpy = vi.fn(() => `${b.get()} ${d.get()}`);
126
+ const e = computed(eSpy);
127
+
128
+ const fSpy = vi.fn(() => e.get());
129
+ const f = computed(fSpy);
130
+ const gSpy = vi.fn(() => e.get());
131
+ const g = computed(gSpy);
132
+
133
+ expect(f.get()).toBe("a a");
134
+ expect(fSpy).toHaveBeenCalledTimes(1);
135
+
136
+ expect(g.get()).toBe("a a");
137
+ expect(gSpy).toHaveBeenCalledTimes(1);
138
+
139
+ eSpy.mockClear();
140
+ fSpy.mockClear();
141
+ gSpy.mockClear();
142
+
143
+ a.set("b");
144
+
145
+ expect(e.get()).toBe("b b");
146
+ expect(eSpy).toHaveBeenCalledTimes(1);
147
+
148
+ expect(f.get()).toBe("b b");
149
+ expect(fSpy).toHaveBeenCalledTimes(1);
150
+
151
+ expect(g.get()).toBe("b b");
152
+ expect(gSpy).toHaveBeenCalledTimes(1);
153
+
154
+ eSpy.mockClear();
155
+ fSpy.mockClear();
156
+ gSpy.mockClear();
157
+
158
+ a.set("c");
159
+
160
+ expect(e.get()).toBe("c c");
161
+ expect(eSpy).toHaveBeenCalledTimes(1);
162
+
163
+ expect(f.get()).toBe("c c");
164
+ expect(fSpy).toHaveBeenCalledTimes(1);
165
+
166
+ expect(g.get()).toBe("c c");
167
+ expect(gSpy).toHaveBeenCalledTimes(1);
168
+
169
+ // top to bottom
170
+ expect(eSpy).toHaveBeenCalledBefore(fSpy);
171
+ // left to right
172
+ expect(fSpy).toHaveBeenCalledBefore(gSpy);
173
+ });
174
+
175
+ test('should only subscribe to signals listened to', () => {
176
+ // *A
177
+ // / \
178
+ // *B C <- we don't listen to C
179
+ const a = signal(() => "a");
180
+
181
+ const b = computed(() => a.get());
182
+ const spy = vi.fn(() => a.get());
183
+ computed(spy);
184
+
185
+ expect(b.get()).toBe("a");
186
+ expect(spy).not.toHaveBeenCalled();
187
+
188
+ a.set("aa");
189
+ expect(b.get()).toBe("aa");
190
+ expect(spy).not.toHaveBeenCalled();
191
+ });
192
+
193
+ test('should only subscribe to signals listened to II', () => {
194
+ // Here both "B" and "C" are active in the beginning, but
195
+ // "B" becomes inactive later. At that point it should
196
+ // not receive any updates anymore.
197
+ // *A
198
+ // / \
199
+ // *B D <- we don't listen to C
200
+ // |
201
+ // *C
202
+ const a = signal(() => "a");
203
+ const spyB = vi.fn(() => a.get());
204
+ const b = computed(spyB);
205
+
206
+ const spyC = vi.fn(() => b.get());
207
+ const c = computed(spyC);
208
+
209
+ const d = computed(() => a.get());
210
+
211
+ let result = "";
212
+ const e = effect(() => {
213
+ result = c.get();
214
+ });
215
+
216
+ expect(result).toBe("a");
217
+ expect(d.get()).toBe("a");
218
+
219
+ spyB.mockClear();
220
+ spyC.mockClear();
221
+ e.dispose();
222
+
223
+ a.set("aa");
224
+
225
+ expect(spyB).not.toHaveBeenCalled();
226
+ expect(spyC).not.toHaveBeenCalled();
227
+ expect(d.get()).toBe("aa");
228
+ });
229
+
230
+ test('should ensure subs update even if one dep unmarks it', () => {
231
+ // In this scenario "C" always returns the same value. When "A"
232
+ // changes, "B" will update, then "C" at which point its update
233
+ // to "D" will be unmarked. But "D" must still update because
234
+ // "B" marked it. If "D" isn't updated, then we have a bug.
235
+ // A
236
+ // / \
237
+ // B *C <- returns same value every time
238
+ // \ /
239
+ // D
240
+ const a = signal(() => "a");
241
+ const b = computed(() => a.get());
242
+ const c = computed(() => {
243
+ a.get();
244
+ return "c";
245
+ });
246
+ const spy = vi.fn(() => `${b.get()} ${c.get()}`);
247
+ const d = computed(spy);
248
+
249
+ expect(d.get()).toBe("a c");
250
+ spy.mockClear();
251
+
252
+ a.set("aa");
253
+ d.get();
254
+ expect(spy).toHaveReturnedWith("aa c");
255
+ });
256
+
257
+ test('should ensure subs update even if two deps unmark it', () => {
258
+ // In this scenario both "C" and "D" always return the same
259
+ // value. But "E" must still update because "A" marked it.
260
+ // If "E" isn't updated, then we have a bug.
261
+ // A
262
+ // / | \
263
+ // B *C *D
264
+ // \ | /
265
+ // E
266
+ const a = signal(() => "a");
267
+ const b = computed(() => a.get());
268
+ const c = computed(() => {
269
+ a.get();
270
+ return "c";
271
+ });
272
+ const d = computed(() => {
273
+ a.get();
274
+ return "d";
275
+ });
276
+ const spy = vi.fn(() => `${b.get()} ${c.get()} ${d.get()}`);
277
+ const e = computed(spy);
278
+
279
+ expect(e.get()).toBe("a c d");
280
+ spy.mockClear();
281
+
282
+ a.set("aa");
283
+ e.get();
284
+ expect(spy).toHaveReturnedWith("aa c d");
285
+ });
286
+
287
+ test('should support lazy branches', () => {
288
+ const a = signal(() => 0);
289
+ const b = computed(() => a.get());
290
+ const c = computed(() => (a.get() > 0 ? a.get() : b.get()));
291
+
292
+ expect(c.get()).toBe(0);
293
+ a.set(1);
294
+ expect(c.get()).toBe(1);
295
+
296
+ a.set(0);
297
+ expect(c.get()).toBe(0);
298
+ });
299
+
300
+ test('should not update a sub if all deps unmark it', () => {
301
+ // In this scenario "B" and "C" always return the same value. When "A"
302
+ // changes, "D" should not update.
303
+ // A
304
+ // / \
305
+ // *B *C
306
+ // \ /
307
+ // D
308
+ const a = signal(() => "a");
309
+ const b = computed(() => {
310
+ a.get();
311
+ return "b";
312
+ });
313
+ const c = computed(() => {
314
+ a.get();
315
+ return "c";
316
+ });
317
+ const spy = vi.fn(() => `${b.get()} ${c.get()}`);
318
+ const d = computed(spy);
319
+
320
+ expect(d.get()).toBe("b c");
321
+ spy.mockClear();
322
+
323
+ a.set("aa");
324
+ expect(spy).not.toHaveBeenCalled();
325
+ });
326
+ });
327
+
328
+
329
+ describe("error handling", () => {
330
+ test('should keep graph consistent on errors during activation', () => {
331
+ const a = signal(() => 0);
332
+ const b = computed(() => {
333
+ throw new Error("fail");
334
+ });
335
+ const c = computed(() => a.get());
336
+
337
+ expect(() => b.get()).toThrow("fail");
338
+
339
+ a.set(1);
340
+ expect(c.get()).toBe(1);
341
+ });
342
+
343
+ test('should keep graph consistent on errors in computeds', () => {
344
+ const a = signal(() => 0);
345
+ const b = computed(() => {
346
+ if (a.get() === 1) {
347
+ throw new Error("fail");
348
+ }
349
+ return a.get();
350
+ });
351
+ const c = computed(() => b.get());
352
+
353
+ expect(c.get()).toBe(0);
354
+
355
+ a.set(1);
356
+ expect(() => b.get()).toThrow("fail");
357
+
358
+ a.set(2);
359
+ expect(c.get()).toBe(2);
360
+ });
361
+ });
@@ -0,0 +1,75 @@
1
+ import { expect, test } from 'vitest';
2
+
3
+ import { computed, effect, signal, trigger } from '#Source/reactor/index.ts';
4
+
5
+ /**
6
+ * 这个测试文件应该与 https://github.com/stackblitz/alien-signals/blob/master/tests/trigger.spec.ts 保持同步。
7
+ */
8
+
9
+ test('should not throw when triggering with no dependencies', () => {
10
+ trigger(() => {
11
+ // no op
12
+ });
13
+ });
14
+
15
+ test('should trigger updates for dependent computed signals', () => {
16
+ const arr = signal<number[]>(() => []);
17
+ const length = computed(() => arr.get().length);
18
+
19
+ expect(length.get()).toBe(0);
20
+ arr.get().push(1);
21
+ expect(length.get()).toBe(0);
22
+ trigger(() => {
23
+ arr.get();
24
+ });
25
+ expect(length.get()).toBe(1);
26
+ });
27
+
28
+ test('should trigger updates for the second source signal', () => {
29
+ const src1 = signal<number[]>(() => []);
30
+ const src2 = signal<number[]>(() => []);
31
+ const length = computed(() => src2.get().length);
32
+
33
+ expect(length.get()).toBe(0);
34
+ src2.get().push(1);
35
+ expect(length.get()).toBe(0);
36
+ trigger(() => {
37
+ src1.get();
38
+ src2.get();
39
+ });
40
+ expect(length.get()).toBe(1);
41
+ });
42
+
43
+ test('should trigger effect once', () => {
44
+ const src1 = signal<number[]>(() => []);
45
+ const src2 = signal<number[]>(() => []);
46
+
47
+ let effectRunTimes = 0;
48
+
49
+ effect(() => {
50
+ effectRunTimes = effectRunTimes + 1;
51
+ src1.get();
52
+ src2.get();
53
+ });
54
+ expect(effectRunTimes).toBe(1);
55
+
56
+ trigger(() => {
57
+ src1.get();
58
+ src2.get();
59
+ });
60
+ expect(effectRunTimes).toBe(2);
61
+ });
62
+
63
+ test('should not notify the trigger function sub', () => {
64
+ const src1 = signal<number[]>(() => []);
65
+ const src2 = computed(() => src1.get());
66
+
67
+ effect(() => {
68
+ src1.get();
69
+ src2.get();
70
+ });
71
+ trigger(() => {
72
+ src1.get();
73
+ src2.get();
74
+ });
75
+ });
@@ -0,0 +1,91 @@
1
+ import { expect, test } from 'vitest';
2
+
3
+ import { computed, effect, effectScope, reactiveSystem, signal } from '#Source/reactor/index.ts';
4
+
5
+ /**
6
+ * 这个测试文件应该与 https://github.com/stackblitz/alien-signals/blob/master/tests/untrack.spec.ts 保持同步。
7
+ */
8
+
9
+ test('should pause tracking in computed', () => {
10
+ const src = signal(() => 0);
11
+
12
+ let computedRunTimes = 0;
13
+ const c = computed(() => {
14
+ computedRunTimes = computedRunTimes + 1;
15
+ reactiveSystem.setNoActiveNodeAsSub();
16
+ const value = src.get();
17
+ reactiveSystem.resetActiveNodeAsSub();
18
+ return value;
19
+ });
20
+
21
+ expect(c.get()).toBe(0);
22
+ expect(computedRunTimes).toBe(1);
23
+
24
+ src.set(1);
25
+ src.set(2);
26
+ src.set(3);
27
+ expect(c.get()).toBe(0);
28
+ expect(computedRunTimes).toBe(1);
29
+ });
30
+
31
+ test('should pause tracking in effect', () => {
32
+ const src = signal(() => 0);
33
+ const is = signal(() => 0);
34
+
35
+ let effectRunTimes = 0;
36
+ effect(() => {
37
+ effectRunTimes = effectRunTimes + 1;
38
+ if (is.get() !== 0) {
39
+ reactiveSystem.setNoActiveNodeAsSub();
40
+ src.get();
41
+ reactiveSystem.resetActiveNodeAsSub();
42
+ }
43
+ });
44
+
45
+ expect(effectRunTimes).toBe(1);
46
+
47
+ is.set(1);
48
+ expect(effectRunTimes).toBe(2);
49
+
50
+ src.set(1);
51
+ src.set(2);
52
+ src.set(3);
53
+ expect(effectRunTimes).toBe(2);
54
+
55
+ is.set(2);
56
+ expect(effectRunTimes).toBe(3);
57
+
58
+ src.set(4);
59
+ src.set(5);
60
+ src.set(6);
61
+ expect(effectRunTimes).toBe(3);
62
+
63
+ is.set(0);
64
+ expect(effectRunTimes).toBe(4);
65
+
66
+ src.set(7);
67
+ src.set(8);
68
+ src.set(9);
69
+ expect(effectRunTimes).toBe(4);
70
+ });
71
+
72
+ test('should pause tracking in effect scope', () => {
73
+ const src = signal(() => 0);
74
+
75
+ let effectRunTimes = 0;
76
+ effectScope(() => {
77
+ effect(() => {
78
+ effectRunTimes = effectRunTimes + 1;
79
+ reactiveSystem.setNoActiveNodeAsSub();
80
+ src.get();
81
+ reactiveSystem.resetActiveNodeAsSub();
82
+ });
83
+ });
84
+
85
+ expect(effectRunTimes).toBe(1);
86
+
87
+ src.set(1);
88
+ src.set(2);
89
+ src.set(3);
90
+ expect(effectRunTimes).toBe(1);
91
+ });
@@ -0,0 +1,73 @@
1
+ import { describe, it, vi, expect } from "vitest";
2
+
3
+ import type { Effect } from '#Source/index.ts';
4
+ import { Signal, computed, effect, effectScope, endBatch, reactiveSystem, signal, startBatch } from '#Source/reactor/index.ts';
5
+
6
+ /**
7
+ * 这个测试文件应该与 https://github.com/preactjs/signals/blob/main/packages/core/test/signal.test.tsx 保持同步。
8
+ */
9
+
10
+ describe("signal", () => {
11
+ it("should return value", () => {
12
+ const v = [1, 2];
13
+ const s = signal(() => v);
14
+ expect(s.get()).to.equal(v);
15
+ });
16
+
17
+ it("should inherit from Signal", () => {
18
+ expect(signal(() => 0)).to.be.instanceOf(Signal);
19
+ });
20
+
21
+ it("should support .toString()", () => {
22
+ // const s = signal(123);
23
+ // expect(s.toString()).equal("123");
24
+ });
25
+
26
+ it("should support .toJSON()", () => {
27
+ // const s = signal(123);
28
+ // expect(s.toJSON()).equal(123);
29
+ });
30
+
31
+ it("should support JSON.Stringify()", () => {
32
+ // const s = signal(123);
33
+ // expect(JSON.stringify({ s })).equal(JSON.stringify({ s: 123 }));
34
+ });
35
+
36
+ it("should support .valueOf()", () => {
37
+ // const s = signal(123);
38
+ // expect(s).to.have.property("valueOf");
39
+ // expect(s.valueOf).to.be.a("function");
40
+ // expect(s.valueOf()).equal(123);
41
+ // expect(+s).equal(123);
42
+
43
+ // const a = signal(1);
44
+ // const b = signal(2);
45
+ // expect(a + b).to.equal(3);
46
+ });
47
+
48
+ it("should notify other listeners of changes after one listener is disposed", () => {
49
+ const s = signal(() => 0);
50
+ const spy1 = vi.fn(() => {
51
+ s.get();
52
+ });
53
+ const spy2 = vi.fn(() => {
54
+ s.get();
55
+ });
56
+ const spy3 = vi.fn(() => {
57
+ s.get();
58
+ });
59
+
60
+ effect(spy1);
61
+ const e2 = effect(spy2);
62
+ effect(spy3);
63
+ expect(spy1).toHaveBeenCalledOnce();
64
+ expect(spy2).toHaveBeenCalledOnce();
65
+ expect(spy3).toHaveBeenCalledOnce();
66
+
67
+ e2.dispose();
68
+ s.set(1);
69
+ expect(spy1).toHaveBeenCalledTimes(2);
70
+ expect(spy2).toHaveBeenCalledOnce();
71
+ expect(spy3).toHaveBeenCalledTimes(2);
72
+ });
73
+ });