@esportsplus/reactivity 0.31.3 → 0.32.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 (68) hide show
  1. package/.claude/CHANGELOG.md +43 -0
  2. package/README.md +13 -6
  3. package/bench/cellx.bench.ts +61 -0
  4. package/bench/dynamic.bench.ts +103 -0
  5. package/bench/kairo.bench.ts +368 -0
  6. package/bench/lib/reactive-adapter.ts +51 -0
  7. package/bench/molbench.bench.ts +75 -0
  8. package/{tests/bench/system.ts → bench/system.bench.ts} +3 -3
  9. package/build/compiler/array.d.ts +1 -1
  10. package/build/compiler/constants.d.ts +7 -6
  11. package/build/compiler/constants.js +6 -7
  12. package/build/compiler/object.d.ts +1 -1
  13. package/build/compiler/object.js +1 -2
  14. package/build/compiler/primitives.d.ts +1 -1
  15. package/build/constants.d.ts +3 -1
  16. package/build/constants.js +3 -1
  17. package/build/reactive/array.d.ts +8 -5
  18. package/build/reactive/array.js +14 -14
  19. package/build/reactive/index.d.ts +2 -2
  20. package/build/reactive/index.js +1 -1
  21. package/build/reactive/object.d.ts +4 -4
  22. package/build/reactive/object.js +8 -23
  23. package/build/system.d.ts +15 -6
  24. package/build/system.js +415 -69
  25. package/build/types.d.ts +16 -4
  26. package/package.json +5 -2
  27. package/src/compiler/array.ts +1 -1
  28. package/src/compiler/constants.ts +8 -6
  29. package/src/compiler/index.ts +2 -1
  30. package/src/compiler/object.ts +2 -2
  31. package/src/compiler/plugins/vite.ts +1 -0
  32. package/src/compiler/primitives.ts +1 -1
  33. package/src/constants.ts +6 -2
  34. package/src/reactive/array.ts +26 -23
  35. package/src/reactive/index.ts +6 -6
  36. package/src/reactive/object.ts +16 -41
  37. package/src/system.ts +612 -80
  38. package/src/types.ts +29 -9
  39. package/test/async-computed.test.ts +323 -0
  40. package/test/async-errors.test.ts +146 -0
  41. package/test/async-hardening.test.ts +73 -0
  42. package/test/async-iterable.test.ts +221 -0
  43. package/test/async-nested.test.ts +19 -0
  44. package/test/deep-graphs.test.ts +215 -0
  45. package/{tests/effects.ts → test/effects.test.ts} +60 -0
  46. package/test/equals.test.ts +142 -0
  47. package/test/errors.test.ts +201 -0
  48. package/test/flush.test.ts +185 -0
  49. package/test/glitch-freedom.test.ts +115 -0
  50. package/test/invalidate.test.ts +147 -0
  51. package/test/lib/wait-for.ts +28 -0
  52. package/test/pending-writes.test.ts +139 -0
  53. package/{tests/reactive.ts → test/reactive/reactive.test.ts} +20 -19
  54. package/test/read-dedup.test.ts +120 -0
  55. package/test/signal-selector.test.ts +187 -0
  56. package/{tests/system.ts → test/system.test.ts} +214 -23
  57. package/test/tsconfig.json +16 -0
  58. package/test/untrack.test.ts +146 -0
  59. package/vitest.config.ts +2 -2
  60. package/tests/async-computed.ts +0 -239
  61. /package/{tests/bench/array.ts → bench/reactive/array.bench.ts} +0 -0
  62. /package/{tests/bench/reactive-object.ts → bench/reactive/reactive-object.bench.ts} +0 -0
  63. /package/{tests → bench}/tsconfig.json +0 -0
  64. /package/{tests/compiler.ts → test/compiler/compiler.test.ts} +0 -0
  65. /package/{tests/primitives.ts → test/primitives.test.ts} +0 -0
  66. /package/{tests/array.ts → test/reactive/array.test.ts} +0 -0
  67. /package/{tests/nested.ts → test/reactive/nested.test.ts} +0 -0
  68. /package/{tests/objects.ts → test/reactive/objects.test.ts} +0 -0
@@ -1,239 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { asyncComputed, effect, read, root, signal, write } from '~/system';
3
-
4
-
5
- describe('asyncComputed', () => {
6
- it('initial value is undefined', () => {
7
- root(() => {
8
- let node = asyncComputed(() => Promise.resolve(42));
9
-
10
- expect(read(node)).toBeUndefined();
11
- });
12
- });
13
-
14
- it('resolves to correct value', async () => {
15
- let node!: ReturnType<typeof asyncComputed<number>>;
16
-
17
- root(() => {
18
- node = asyncComputed(() => Promise.resolve(42));
19
- });
20
-
21
- await new Promise((r) => setTimeout(r, 10));
22
-
23
- expect(read(node)).toBe(42);
24
- });
25
-
26
- it('updates when dependency changes', async () => {
27
- let node!: ReturnType<typeof asyncComputed<string>>,
28
- s = signal('hello');
29
-
30
- root(() => {
31
- node = asyncComputed(() => Promise.resolve(read(s)));
32
- });
33
-
34
- await new Promise((r) => setTimeout(r, 10));
35
-
36
- expect(read(node)).toBe('hello');
37
-
38
- write(s, 'world');
39
- await new Promise((r) => setTimeout(r, 10));
40
-
41
- expect(read(node)).toBe('world');
42
- });
43
-
44
- it('race condition — rapid changes, only latest promise writes', async () => {
45
- let node!: ReturnType<typeof asyncComputed<number>>,
46
- resolvers: ((v: number) => void)[] = [],
47
- s = signal(1);
48
-
49
- root(() => {
50
- node = asyncComputed(() => {
51
- read(s);
52
- return new Promise<number>((resolve) => {
53
- resolvers.push(resolve);
54
- });
55
- });
56
- });
57
-
58
- expect(read(node)).toBeUndefined();
59
-
60
- write(s, 2);
61
- await Promise.resolve();
62
- await Promise.resolve();
63
-
64
- write(s, 3);
65
- await Promise.resolve();
66
- await Promise.resolve();
67
-
68
- // Resolve first (stale)
69
- resolvers[0](100);
70
- await Promise.resolve();
71
-
72
- expect(read(node)).toBeUndefined();
73
-
74
- // Resolve second (stale)
75
- resolvers[1](200);
76
- await Promise.resolve();
77
-
78
- expect(read(node)).toBeUndefined();
79
-
80
- // Resolve latest
81
- resolvers[2](300);
82
- await Promise.resolve();
83
-
84
- expect(read(node)).toBe(300);
85
- });
86
-
87
- it('onCleanup works for abort controller', async () => {
88
- let aborted = false,
89
- s = signal(1);
90
-
91
- root(() => {
92
- asyncComputed((onCleanup) => {
93
- let controller = new AbortController();
94
-
95
- controller.signal.addEventListener('abort', () => {
96
- aborted = true;
97
- });
98
-
99
- onCleanup(() => controller.abort());
100
-
101
- return Promise.resolve(read(s));
102
- });
103
- });
104
-
105
- await new Promise((r) => setTimeout(r, 10));
106
-
107
- expect(aborted).toBe(false);
108
-
109
- write(s, 2);
110
- await new Promise((r) => setTimeout(r, 10));
111
-
112
- expect(aborted).toBe(true);
113
- });
114
-
115
- it('effect tracks async computed', async () => {
116
- let node!: ReturnType<typeof asyncComputed<number>>,
117
- s = signal(10),
118
- values: (number | undefined)[] = [];
119
-
120
- root(() => {
121
- node = asyncComputed(() => Promise.resolve(read(s)));
122
-
123
- effect(() => {
124
- values.push(read(node));
125
- });
126
- });
127
-
128
- expect(values).toEqual([undefined]);
129
-
130
- await new Promise((r) => setTimeout(r, 10));
131
-
132
- expect(values).toEqual([undefined, 10]);
133
-
134
- write(s, 20);
135
- await new Promise((r) => setTimeout(r, 10));
136
-
137
- expect(values).toEqual([undefined, 10, 20]);
138
- });
139
-
140
- it('dispose stops updates', async () => {
141
- let node!: ReturnType<typeof asyncComputed<number>>,
142
- s = signal(1);
143
-
144
- let disposeRoot!: VoidFunction;
145
-
146
- root((dispose) => {
147
- disposeRoot = dispose;
148
- node = asyncComputed(() => Promise.resolve(read(s)));
149
- });
150
-
151
- await new Promise((r) => setTimeout(r, 10));
152
-
153
- expect(read(node)).toBe(1);
154
-
155
- disposeRoot();
156
-
157
- write(s, 2);
158
- await new Promise((r) => setTimeout(r, 10));
159
-
160
- expect(read(node)).toBe(1);
161
- });
162
-
163
- it('nested asyncComputed — B depends on A', async () => {
164
- let s = signal(5),
165
- nodeA!: ReturnType<typeof asyncComputed<number>>,
166
- nodeB!: ReturnType<typeof asyncComputed<number>>;
167
-
168
- root(() => {
169
- nodeA = asyncComputed(() => Promise.resolve(read(s) * 2));
170
-
171
- nodeB = asyncComputed(() => {
172
- let a = read(nodeA);
173
-
174
- if (a === undefined) {
175
- return Promise.resolve(0);
176
- }
177
-
178
- return Promise.resolve(a + 100);
179
- });
180
- });
181
-
182
- // Initially both undefined
183
- expect(read(nodeA)).toBeUndefined();
184
- expect(read(nodeB)).toBeUndefined();
185
-
186
- // Wait for A to resolve
187
- await new Promise((r) => setTimeout(r, 10));
188
-
189
- expect(read(nodeA)).toBe(10);
190
-
191
- // Wait for B to react to A's resolved value
192
- await new Promise((r) => setTimeout(r, 10));
193
-
194
- expect(read(nodeB)).toBe(110);
195
-
196
- // Update source signal — A and B should both update
197
- write(s, 20);
198
- await new Promise((r) => setTimeout(r, 20));
199
-
200
- expect(read(nodeA)).toBe(40);
201
-
202
- await new Promise((r) => setTimeout(r, 10));
203
-
204
- expect(read(nodeB)).toBe(140);
205
- });
206
-
207
- it('rejected promise does not crash and retains previous value', async () => {
208
- let node!: ReturnType<typeof asyncComputed<number>>,
209
- s = signal(1);
210
-
211
- root(() => {
212
- node = asyncComputed(() => {
213
- let v = read(s);
214
-
215
- if (v === 2) {
216
- return Promise.reject(new Error('fail'));
217
- }
218
-
219
- return Promise.resolve(v);
220
- });
221
- });
222
-
223
- await new Promise((r) => setTimeout(r, 10));
224
-
225
- expect(read(node)).toBe(1);
226
-
227
- write(s, 2);
228
- await new Promise((r) => setTimeout(r, 10));
229
-
230
- // Value should remain 1 after rejection
231
- expect(read(node)).toBe(1);
232
-
233
- write(s, 3);
234
- await new Promise((r) => setTimeout(r, 10));
235
-
236
- // Resumes after non-rejected promise
237
- expect(read(node)).toBe(3);
238
- });
239
- });
File without changes
File without changes