@zeix/cause-effect 0.17.2 → 0.18.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.
- package/.ai-context.md +163 -226
- package/.cursorrules +41 -35
- package/.github/copilot-instructions.md +166 -116
- package/.zed/settings.json +3 -0
- package/ARCHITECTURE.md +274 -0
- package/CLAUDE.md +197 -202
- package/COLLECTION_REFACTORING.md +161 -0
- package/GUIDE.md +298 -0
- package/README.md +241 -220
- package/REQUIREMENTS.md +100 -0
- package/bench/reactivity.bench.ts +577 -0
- package/index.dev.js +1326 -1174
- package/index.js +1 -1
- package/index.ts +58 -85
- package/package.json +9 -6
- package/src/errors.ts +118 -70
- package/src/graph.ts +601 -0
- package/src/nodes/collection.ts +474 -0
- package/src/nodes/effect.ts +149 -0
- package/src/nodes/list.ts +588 -0
- package/src/nodes/memo.ts +120 -0
- package/src/nodes/sensor.ts +139 -0
- package/src/nodes/state.ts +135 -0
- package/src/nodes/store.ts +383 -0
- package/src/nodes/task.ts +146 -0
- package/src/signal.ts +112 -64
- package/src/util.ts +26 -57
- package/test/batch.test.ts +96 -69
- package/test/benchmark.test.ts +473 -485
- package/test/collection.test.ts +455 -955
- package/test/effect.test.ts +293 -696
- package/test/list.test.ts +332 -857
- package/test/memo.test.ts +380 -0
- package/test/regression.test.ts +156 -0
- package/test/scope.test.ts +191 -0
- package/test/sensor.test.ts +454 -0
- package/test/signal.test.ts +220 -213
- package/test/state.test.ts +217 -271
- package/test/store.test.ts +346 -898
- package/test/task.test.ts +395 -0
- package/test/untrack.test.ts +167 -0
- package/test/util/dependency-graph.ts +2 -2
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +5 -7
- package/types/index.d.ts +13 -15
- package/types/src/errors.d.ts +73 -19
- package/types/src/graph.d.ts +208 -0
- package/types/src/nodes/collection.d.ts +64 -0
- package/types/src/nodes/effect.d.ts +48 -0
- package/types/src/nodes/list.d.ts +65 -0
- package/types/src/nodes/memo.d.ts +57 -0
- package/types/src/nodes/sensor.d.ts +75 -0
- package/types/src/nodes/state.d.ts +78 -0
- package/types/src/nodes/store.d.ts +51 -0
- package/types/src/nodes/task.d.ts +73 -0
- package/types/src/signal.d.ts +43 -28
- package/types/src/util.d.ts +9 -16
- package/archive/benchmark.ts +0 -688
- package/archive/collection.ts +0 -310
- package/archive/computed.ts +0 -198
- package/archive/list.ts +0 -544
- package/archive/memo.ts +0 -140
- package/archive/state.ts +0 -90
- package/archive/store.ts +0 -357
- package/archive/task.ts +0 -191
- package/src/classes/collection.ts +0 -298
- package/src/classes/composite.ts +0 -171
- package/src/classes/computed.ts +0 -392
- package/src/classes/list.ts +0 -310
- package/src/classes/ref.ts +0 -96
- package/src/classes/state.ts +0 -131
- package/src/classes/store.ts +0 -227
- package/src/diff.ts +0 -138
- package/src/effect.ts +0 -96
- package/src/match.ts +0 -45
- package/src/resolve.ts +0 -49
- package/src/system.ts +0 -275
- package/test/computed.test.ts +0 -1126
- package/test/diff.test.ts +0 -955
- package/test/match.test.ts +0 -388
- package/test/ref.test.ts +0 -381
- package/test/resolve.test.ts +0 -154
- package/types/src/classes/collection.d.ts +0 -47
- package/types/src/classes/composite.d.ts +0 -15
- package/types/src/classes/computed.d.ts +0 -114
- package/types/src/classes/list.d.ts +0 -41
- package/types/src/classes/ref.d.ts +0 -48
- package/types/src/classes/state.d.ts +0 -61
- package/types/src/classes/store.d.ts +0 -51
- package/types/src/diff.d.ts +0 -28
- package/types/src/effect.d.ts +0 -15
- package/types/src/match.d.ts +0 -21
- package/types/src/resolve.d.ts +0 -29
- package/types/src/system.d.ts +0 -81
package/test/benchmark.test.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { describe, expect, mock, test } from 'bun:test'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
batch as batchNext,
|
|
4
|
+
createEffect as createEffectNext,
|
|
5
|
+
createMemo,
|
|
6
|
+
createState,
|
|
7
|
+
} from '../index.ts'
|
|
3
8
|
import { Counter, makeGraph, runGraph } from './util/dependency-graph'
|
|
4
9
|
import type { Computed, ReactiveFramework } from './util/reactive-framework'
|
|
5
10
|
|
|
@@ -12,25 +17,27 @@ const busy = () => {
|
|
|
12
17
|
}
|
|
13
18
|
}
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
/* === Framework Adapters === */
|
|
21
|
+
|
|
22
|
+
const v18: ReactiveFramework = {
|
|
23
|
+
name: 'v0.18.0 (graph)',
|
|
24
|
+
// @ts-expect-error ReactiveFramework doesn't have non-nullable signals
|
|
17
25
|
signal: <T extends {}>(initialValue: T) => {
|
|
18
|
-
const s =
|
|
19
|
-
return {
|
|
20
|
-
write: (v: T) => s.set(v),
|
|
21
|
-
read: () => s.get(),
|
|
22
|
-
}
|
|
26
|
+
const s = createState(initialValue)
|
|
27
|
+
return { write: s.set, read: s.get }
|
|
23
28
|
},
|
|
29
|
+
// @ts-expect-error ReactiveFramework doesn't have non-nullable signals
|
|
24
30
|
computed: <T extends {}>(fn: () => T) => {
|
|
25
|
-
const c =
|
|
26
|
-
return {
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
const c = createMemo(fn)
|
|
32
|
+
return { read: c.get }
|
|
33
|
+
},
|
|
34
|
+
effect: (fn: () => undefined) => {
|
|
35
|
+
createEffectNext(() => fn())
|
|
29
36
|
},
|
|
30
|
-
|
|
31
|
-
withBatch: (fn: () => undefined) => batchSignalWrites(fn),
|
|
37
|
+
withBatch: fn => batchNext(fn),
|
|
32
38
|
withBuild: <T>(fn: () => T) => fn(),
|
|
33
39
|
}
|
|
40
|
+
|
|
34
41
|
const testPullCounts = true
|
|
35
42
|
|
|
36
43
|
function makeConfig() {
|
|
@@ -45,582 +52,563 @@ function makeConfig() {
|
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
|
|
48
|
-
/* === Test
|
|
55
|
+
/* === Parameterized Test Suite === */
|
|
49
56
|
|
|
50
|
-
|
|
51
|
-
* wrapper works and can run performance tests.
|
|
52
|
-
*/
|
|
53
|
-
describe('Basic test', () => {
|
|
57
|
+
for (const framework of [v18]) {
|
|
54
58
|
const name = framework.name
|
|
55
|
-
test(`${name} | simple dependency executes`, () => {
|
|
56
|
-
framework.withBuild(() => {
|
|
57
|
-
const s = framework.signal(2)
|
|
58
|
-
const c = framework.computed(() => s.read() * 2)
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
describe(`Basic tests [${name}]`, () => {
|
|
61
|
+
test('simple dependency executes', () => {
|
|
62
|
+
framework.withBuild(() => {
|
|
63
|
+
const s = framework.signal(2)
|
|
64
|
+
const c = framework.computed(() => s.read() * 2)
|
|
65
|
+
expect(c.read()).toEqual(4)
|
|
66
|
+
})
|
|
61
67
|
})
|
|
62
|
-
})
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
test('simple write', () => {
|
|
70
|
+
framework.withBuild(() => {
|
|
71
|
+
const s = framework.signal(2)
|
|
72
|
+
const c = framework.computed(() => s.read() * 2)
|
|
73
|
+
expect(s.read()).toEqual(2)
|
|
74
|
+
expect(c.read()).toEqual(4)
|
|
70
75
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
s.write(3)
|
|
77
|
+
expect(s.read()).toEqual(3)
|
|
78
|
+
expect(c.read()).toEqual(6)
|
|
79
|
+
})
|
|
74
80
|
})
|
|
75
|
-
})
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
const config = makeConfig()
|
|
79
|
-
const counter = new Counter()
|
|
80
|
-
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
81
|
-
const graph = makeGraph(framework, config, counter)
|
|
82
|
-
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
83
|
-
const sum = runGraph(graph, 2, 1, framework)
|
|
84
|
-
expect(sum).toEqual(16)
|
|
85
|
-
if (testPullCounts) {
|
|
86
|
-
expect(counter.count).toEqual(11)
|
|
87
|
-
} else {
|
|
88
|
-
expect(counter.count).toBeGreaterThanOrEqual(11)
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
test(`${name} | static graph, read 2/3 of leaves`, () => {
|
|
93
|
-
framework.withBuild(() => {
|
|
82
|
+
test('static graph', () => {
|
|
94
83
|
const config = makeConfig()
|
|
95
|
-
config.readFraction = 2 / 3
|
|
96
|
-
config.iterations = 10
|
|
97
84
|
const counter = new Counter()
|
|
98
|
-
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
99
85
|
const graph = makeGraph(framework, config, counter)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
expect(sum).toEqual(71)
|
|
86
|
+
const sum = runGraph(graph, 2, 1, framework)
|
|
87
|
+
expect(sum).toEqual(16)
|
|
104
88
|
if (testPullCounts) {
|
|
105
|
-
expect(counter.count).toEqual(
|
|
89
|
+
expect(counter.count).toEqual(11)
|
|
106
90
|
} else {
|
|
107
|
-
expect(counter.count).toBeGreaterThanOrEqual(
|
|
91
|
+
expect(counter.count).toBeGreaterThanOrEqual(11)
|
|
108
92
|
}
|
|
109
93
|
})
|
|
110
|
-
})
|
|
111
94
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
expect(counter.count).toBeGreaterThanOrEqual(22)
|
|
129
|
-
}
|
|
95
|
+
test('static graph, read 2/3 of leaves', () => {
|
|
96
|
+
framework.withBuild(() => {
|
|
97
|
+
const config = makeConfig()
|
|
98
|
+
config.readFraction = 2 / 3
|
|
99
|
+
config.iterations = 10
|
|
100
|
+
const counter = new Counter()
|
|
101
|
+
const graph = makeGraph(framework, config, counter)
|
|
102
|
+
const sum = runGraph(graph, 10, 2 / 3, framework)
|
|
103
|
+
|
|
104
|
+
expect(sum).toEqual(71)
|
|
105
|
+
if (testPullCounts) {
|
|
106
|
+
expect(counter.count).toEqual(41)
|
|
107
|
+
} else {
|
|
108
|
+
expect(counter.count).toBeGreaterThanOrEqual(41)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
130
111
|
})
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
test(`${name} | withBuild`, () => {
|
|
134
|
-
const r = framework.withBuild(() => {
|
|
135
|
-
const s = framework.signal(2)
|
|
136
|
-
const c = framework.computed(() => s.read() * 2)
|
|
137
112
|
|
|
138
|
-
|
|
139
|
-
|
|
113
|
+
test('dynamic graph', () => {
|
|
114
|
+
framework.withBuild(() => {
|
|
115
|
+
const config = makeConfig()
|
|
116
|
+
config.staticFraction = 0.5
|
|
117
|
+
config.width = 4
|
|
118
|
+
config.totalLayers = 2
|
|
119
|
+
const counter = new Counter()
|
|
120
|
+
const graph = makeGraph(framework, config, counter)
|
|
121
|
+
const sum = runGraph(graph, 10, 1, framework)
|
|
122
|
+
|
|
123
|
+
expect(sum).toEqual(72)
|
|
124
|
+
if (testPullCounts) {
|
|
125
|
+
expect(counter.count).toEqual(22)
|
|
126
|
+
} else {
|
|
127
|
+
expect(counter.count).toBeGreaterThanOrEqual(22)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
140
130
|
})
|
|
141
|
-
expect(r).toEqual(4)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
test(`${name} | effect`, () => {
|
|
145
|
-
const spy = (_v: number) => {}
|
|
146
|
-
const spyMock = mock(spy)
|
|
147
|
-
|
|
148
|
-
const s = framework.signal(2)
|
|
149
|
-
let c: { read: () => number } = { read: () => 0 }
|
|
150
131
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
132
|
+
test('withBuild', () => {
|
|
133
|
+
const r = framework.withBuild(() => {
|
|
134
|
+
const s = framework.signal(2)
|
|
135
|
+
const c = framework.computed(() => s.read() * 2)
|
|
136
|
+
expect(c.read()).toEqual(4)
|
|
137
|
+
return c.read()
|
|
156
138
|
})
|
|
139
|
+
expect(r).toEqual(4)
|
|
157
140
|
})
|
|
158
|
-
expect(spyMock.mock.calls.length).toBe(1)
|
|
159
141
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
expect(s.read()).toEqual(3)
|
|
164
|
-
expect(c.read()).toEqual(6)
|
|
165
|
-
expect(spyMock.mock.calls.length).toBe(2)
|
|
166
|
-
})
|
|
167
|
-
})
|
|
142
|
+
test('effect', () => {
|
|
143
|
+
const spy = (_v: number) => {}
|
|
144
|
+
const spyMock = mock(spy)
|
|
168
145
|
|
|
169
|
-
|
|
170
|
-
|
|
146
|
+
const s = framework.signal(2)
|
|
147
|
+
let c: { read: () => number } = { read: () => 0 }
|
|
171
148
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const computed3 = framework.computed(() => {
|
|
180
|
-
busy()
|
|
181
|
-
return computed2.read() + 1
|
|
182
|
-
}) // heavy computation
|
|
183
|
-
const computed4 = framework.computed(() => computed3.read() + 2)
|
|
184
|
-
const computed5 = framework.computed(() => computed4.read() + 3)
|
|
185
|
-
framework.effect(() => {
|
|
186
|
-
computed5.read()
|
|
187
|
-
busy() // heavy side effect
|
|
188
|
-
})
|
|
149
|
+
framework.withBuild(() => {
|
|
150
|
+
c = framework.computed(() => s.read() * 2)
|
|
151
|
+
framework.effect(() => {
|
|
152
|
+
spyMock(c.read())
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
expect(spyMock.mock.calls.length).toBe(1)
|
|
189
156
|
|
|
190
|
-
return () => {
|
|
191
157
|
framework.withBatch(() => {
|
|
192
|
-
|
|
158
|
+
s.write(3)
|
|
193
159
|
})
|
|
194
|
-
expect(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
})
|
|
199
|
-
expect(computed5.read()).toBe(6)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
160
|
+
expect(s.read()).toEqual(3)
|
|
161
|
+
expect(c.read()).toEqual(6)
|
|
162
|
+
expect(spyMock.mock.calls.length).toBe(2)
|
|
163
|
+
})
|
|
202
164
|
})
|
|
203
165
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return
|
|
166
|
+
describe(`Kairo tests [${name}]`, () => {
|
|
167
|
+
test('avoidable propagation', async () => {
|
|
168
|
+
const head = framework.signal(0)
|
|
169
|
+
const computed1 = framework.computed(() => head.read())
|
|
170
|
+
const computed2 = framework.computed(() => {
|
|
171
|
+
computed1.read()
|
|
172
|
+
return 0
|
|
211
173
|
})
|
|
212
|
-
const
|
|
213
|
-
|
|
174
|
+
const computed3 = framework.computed(() => {
|
|
175
|
+
busy()
|
|
176
|
+
return computed2.read() + 1
|
|
214
177
|
})
|
|
178
|
+
const computed4 = framework.computed(() => computed3.read() + 2)
|
|
179
|
+
const computed5 = framework.computed(() => computed4.read() + 3)
|
|
215
180
|
framework.effect(() => {
|
|
216
|
-
|
|
217
|
-
|
|
181
|
+
computed5.read()
|
|
182
|
+
busy()
|
|
218
183
|
})
|
|
219
|
-
last = current2
|
|
220
|
-
}
|
|
221
184
|
|
|
222
|
-
|
|
223
|
-
framework.withBatch(() => {
|
|
224
|
-
head.write(1)
|
|
225
|
-
})
|
|
226
|
-
const atleast = 50 * 50
|
|
227
|
-
callCounter.count = 0
|
|
228
|
-
for (let i = 0; i < 50; i++) {
|
|
185
|
+
return () => {
|
|
229
186
|
framework.withBatch(() => {
|
|
230
|
-
head.write(
|
|
187
|
+
head.write(1)
|
|
231
188
|
})
|
|
232
|
-
expect(
|
|
189
|
+
expect(computed5.read()).toBe(6)
|
|
190
|
+
for (let i = 0; i < 10; i++) {
|
|
191
|
+
framework.withBatch(() => {
|
|
192
|
+
head.write(i)
|
|
193
|
+
})
|
|
194
|
+
expect(computed5.read()).toBe(6)
|
|
195
|
+
}
|
|
233
196
|
}
|
|
234
|
-
expect(callCounter.count).toBe(atleast)
|
|
235
|
-
}
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
test(`${name} | deep propagation`, async () => {
|
|
239
|
-
const len = 50
|
|
240
|
-
const head = framework.signal(0)
|
|
241
|
-
let current = head as { read: () => number }
|
|
242
|
-
for (let i = 0; i < len; i++) {
|
|
243
|
-
const c = current
|
|
244
|
-
current = framework.computed(() => {
|
|
245
|
-
return c.read() + 1
|
|
246
|
-
})
|
|
247
|
-
}
|
|
248
|
-
const callCounter = new Counter()
|
|
249
|
-
framework.effect(() => {
|
|
250
|
-
current.read()
|
|
251
|
-
callCounter.count++
|
|
252
197
|
})
|
|
253
|
-
const iter = 50
|
|
254
198
|
|
|
255
|
-
|
|
256
|
-
framework.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
framework.
|
|
263
|
-
|
|
199
|
+
test('broad propagation', async () => {
|
|
200
|
+
const head = framework.signal(0)
|
|
201
|
+
let last = head as { read: () => number }
|
|
202
|
+
const callCounter = new Counter()
|
|
203
|
+
for (let i = 0; i < 50; i++) {
|
|
204
|
+
const current = framework.computed(() => head.read() + i)
|
|
205
|
+
const current2 = framework.computed(() => current.read() + 1)
|
|
206
|
+
framework.effect(() => {
|
|
207
|
+
current2.read()
|
|
208
|
+
callCounter.count++
|
|
264
209
|
})
|
|
265
|
-
|
|
210
|
+
last = current2
|
|
266
211
|
}
|
|
267
|
-
expect(callCounter.count).toBe(atleast)
|
|
268
|
-
}
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
test(`${name} | diamond`, async () => {
|
|
272
|
-
const width = 5
|
|
273
|
-
const head = framework.signal(0)
|
|
274
|
-
const current: { read(): number }[] = []
|
|
275
|
-
for (let i = 0; i < width; i++) {
|
|
276
|
-
current.push(framework.computed(() => head.read() + 1))
|
|
277
|
-
}
|
|
278
|
-
const sum = framework.computed(() => {
|
|
279
|
-
return current.map(x => x.read()).reduce((a, b) => a + b, 0)
|
|
280
|
-
})
|
|
281
|
-
const callCounter = new Counter()
|
|
282
|
-
framework.effect(() => {
|
|
283
|
-
sum.read()
|
|
284
|
-
callCounter.count++
|
|
285
|
-
})
|
|
286
212
|
|
|
287
|
-
|
|
288
|
-
framework.withBatch(() => {
|
|
289
|
-
head.write(1)
|
|
290
|
-
})
|
|
291
|
-
expect(sum.read()).toBe(2 * width)
|
|
292
|
-
const atleast = 500
|
|
293
|
-
callCounter.count = 0
|
|
294
|
-
for (let i = 0; i < 500; i++) {
|
|
213
|
+
return () => {
|
|
295
214
|
framework.withBatch(() => {
|
|
296
|
-
head.write(
|
|
215
|
+
head.write(1)
|
|
297
216
|
})
|
|
298
|
-
|
|
217
|
+
const atleast = 50 * 50
|
|
218
|
+
callCounter.count = 0
|
|
219
|
+
for (let i = 0; i < 50; i++) {
|
|
220
|
+
framework.withBatch(() => {
|
|
221
|
+
head.write(i)
|
|
222
|
+
})
|
|
223
|
+
expect(last.read()).toBe(i + 50)
|
|
224
|
+
}
|
|
225
|
+
expect(callCounter.count).toBe(atleast)
|
|
299
226
|
}
|
|
300
|
-
expect(callCounter.count).toBe(atleast)
|
|
301
|
-
}
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
test(`${name} | mux`, async () => {
|
|
305
|
-
const heads = new Array(100).fill(null).map(_ => framework.signal(0))
|
|
306
|
-
const mux = framework.computed(() => {
|
|
307
|
-
return Object.fromEntries(heads.map(h => h.read()).entries())
|
|
308
227
|
})
|
|
309
|
-
const splited = heads
|
|
310
|
-
.map((_, index) => framework.computed(() => mux.read()[index]))
|
|
311
|
-
.map(x => framework.computed(() => x.read() + 1))
|
|
312
228
|
|
|
313
|
-
|
|
229
|
+
test('deep propagation', async () => {
|
|
230
|
+
const len = 50
|
|
231
|
+
const head = framework.signal(0)
|
|
232
|
+
let current = head as { read: () => number }
|
|
233
|
+
for (let i = 0; i < len; i++) {
|
|
234
|
+
const c = current
|
|
235
|
+
current = framework.computed(() => c.read() + 1)
|
|
236
|
+
}
|
|
237
|
+
const callCounter = new Counter()
|
|
314
238
|
framework.effect(() => {
|
|
315
|
-
|
|
239
|
+
current.read()
|
|
240
|
+
callCounter.count++
|
|
316
241
|
})
|
|
317
|
-
|
|
242
|
+
const iter = 50
|
|
318
243
|
|
|
319
|
-
|
|
320
|
-
for (let i = 0; i < 10; i++) {
|
|
321
|
-
framework.withBatch(() => {
|
|
322
|
-
heads[i].write(i)
|
|
323
|
-
})
|
|
324
|
-
expect(splited[i].read()).toBe(i + 1)
|
|
325
|
-
}
|
|
326
|
-
for (let i = 0; i < 10; i++) {
|
|
244
|
+
return () => {
|
|
327
245
|
framework.withBatch(() => {
|
|
328
|
-
|
|
246
|
+
head.write(1)
|
|
329
247
|
})
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
let result = 0
|
|
340
|
-
for (let i = 0; i < size; i++) {
|
|
341
|
-
result += head.read()
|
|
248
|
+
const atleast = iter
|
|
249
|
+
callCounter.count = 0
|
|
250
|
+
for (let i = 0; i < iter; i++) {
|
|
251
|
+
framework.withBatch(() => {
|
|
252
|
+
head.write(i)
|
|
253
|
+
})
|
|
254
|
+
expect(current.read()).toBe(len + i)
|
|
255
|
+
}
|
|
256
|
+
expect(callCounter.count).toBe(atleast)
|
|
342
257
|
}
|
|
343
|
-
return result
|
|
344
|
-
})
|
|
345
|
-
const callCounter = new Counter()
|
|
346
|
-
framework.effect(() => {
|
|
347
|
-
current.read()
|
|
348
|
-
callCounter.count++
|
|
349
258
|
})
|
|
350
259
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
260
|
+
test('diamond', async () => {
|
|
261
|
+
const width = 5
|
|
262
|
+
const head = framework.signal(0)
|
|
263
|
+
const current: { read(): number }[] = []
|
|
264
|
+
for (let i = 0; i < width; i++) {
|
|
265
|
+
current.push(framework.computed(() => head.read() + 1))
|
|
266
|
+
}
|
|
267
|
+
const sum = framework.computed(() =>
|
|
268
|
+
current.map(x => x.read()).reduce((a, b) => a + b, 0),
|
|
269
|
+
)
|
|
270
|
+
const callCounter = new Counter()
|
|
271
|
+
framework.effect(() => {
|
|
272
|
+
sum.read()
|
|
273
|
+
callCounter.count++
|
|
354
274
|
})
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
callCounter.count = 0
|
|
358
|
-
for (let i = 0; i < 100; i++) {
|
|
275
|
+
|
|
276
|
+
return () => {
|
|
359
277
|
framework.withBatch(() => {
|
|
360
|
-
head.write(
|
|
278
|
+
head.write(1)
|
|
361
279
|
})
|
|
362
|
-
expect(
|
|
280
|
+
expect(sum.read()).toBe(2 * width)
|
|
281
|
+
const atleast = 500
|
|
282
|
+
callCounter.count = 0
|
|
283
|
+
for (let i = 0; i < 500; i++) {
|
|
284
|
+
framework.withBatch(() => {
|
|
285
|
+
head.write(i)
|
|
286
|
+
})
|
|
287
|
+
expect(sum.read()).toBe((i + 1) * width)
|
|
288
|
+
}
|
|
289
|
+
expect(callCounter.count).toBe(atleast)
|
|
363
290
|
}
|
|
364
|
-
expect(callCounter.count).toBe(atleast)
|
|
365
|
-
}
|
|
366
|
-
})
|
|
367
|
-
|
|
368
|
-
test(`${name} | triangle`, async () => {
|
|
369
|
-
const width = 10
|
|
370
|
-
const head = framework.signal(0)
|
|
371
|
-
let current = head as { read: () => number }
|
|
372
|
-
const list: { read: () => number }[] = []
|
|
373
|
-
for (let i = 0; i < width; i++) {
|
|
374
|
-
const c = current
|
|
375
|
-
list.push(current)
|
|
376
|
-
current = framework.computed(() => {
|
|
377
|
-
return c.read() + 1
|
|
378
|
-
})
|
|
379
|
-
}
|
|
380
|
-
const sum = framework.computed(() => {
|
|
381
|
-
return list.map(x => x.read()).reduce((a, b) => a + b, 0)
|
|
382
|
-
})
|
|
383
|
-
const callCounter = new Counter()
|
|
384
|
-
framework.effect(() => {
|
|
385
|
-
sum.read()
|
|
386
|
-
callCounter.count++
|
|
387
291
|
})
|
|
388
292
|
|
|
389
|
-
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
for (let i = 0; i < 100; i++) {
|
|
404
|
-
framework.withBatch(() => {
|
|
405
|
-
head.write(i)
|
|
293
|
+
test('mux', async () => {
|
|
294
|
+
const heads = new Array(100)
|
|
295
|
+
.fill(null)
|
|
296
|
+
.map(_ => framework.signal(0))
|
|
297
|
+
const mux = framework.computed(() =>
|
|
298
|
+
Object.fromEntries(heads.map(h => h.read()).entries()),
|
|
299
|
+
)
|
|
300
|
+
const splited = heads
|
|
301
|
+
.map((_, index) => framework.computed(() => mux.read()[index]))
|
|
302
|
+
.map(x => framework.computed(() => x.read() + 1))
|
|
303
|
+
|
|
304
|
+
for (const x of splited) {
|
|
305
|
+
framework.effect(() => {
|
|
306
|
+
x.read()
|
|
406
307
|
})
|
|
407
|
-
expect(sum.read()).toBe(constant - width + i * width)
|
|
408
308
|
}
|
|
409
|
-
expect(callCounter.count).toBe(atleast)
|
|
410
|
-
}
|
|
411
|
-
})
|
|
412
309
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
310
|
+
return () => {
|
|
311
|
+
for (let i = 0; i < 10; i++) {
|
|
312
|
+
framework.withBatch(() => {
|
|
313
|
+
heads[i].write(i)
|
|
314
|
+
})
|
|
315
|
+
expect(splited[i].read()).toBe(i + 1)
|
|
316
|
+
}
|
|
317
|
+
for (let i = 0; i < 10; i++) {
|
|
318
|
+
framework.withBatch(() => {
|
|
319
|
+
heads[i].write(i * 2)
|
|
320
|
+
})
|
|
321
|
+
expect(splited[i].read()).toBe(i * 2 + 1)
|
|
322
|
+
}
|
|
421
323
|
}
|
|
422
|
-
return result
|
|
423
|
-
})
|
|
424
|
-
const callCounter = new Counter()
|
|
425
|
-
framework.effect(() => {
|
|
426
|
-
current.read()
|
|
427
|
-
callCounter.count++
|
|
428
324
|
})
|
|
429
325
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
326
|
+
test('repeated observers', async () => {
|
|
327
|
+
const size = 30
|
|
328
|
+
const head = framework.signal(0)
|
|
329
|
+
const current = framework.computed(() => {
|
|
330
|
+
let result = 0
|
|
331
|
+
for (let i = 0; i < size; i++) {
|
|
332
|
+
result += head.read()
|
|
333
|
+
}
|
|
334
|
+
return result
|
|
433
335
|
})
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
336
|
+
const callCounter = new Counter()
|
|
337
|
+
framework.effect(() => {
|
|
338
|
+
current.read()
|
|
339
|
+
callCounter.count++
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
return () => {
|
|
438
343
|
framework.withBatch(() => {
|
|
439
|
-
head.write(
|
|
344
|
+
head.write(1)
|
|
440
345
|
})
|
|
441
|
-
expect(current.read()).toBe(
|
|
346
|
+
expect(current.read()).toBe(size)
|
|
347
|
+
const atleast = 100
|
|
348
|
+
callCounter.count = 0
|
|
349
|
+
for (let i = 0; i < 100; i++) {
|
|
350
|
+
framework.withBatch(() => {
|
|
351
|
+
head.write(i)
|
|
352
|
+
})
|
|
353
|
+
expect(current.read()).toBe(i * size)
|
|
354
|
+
}
|
|
355
|
+
expect(callCounter.count).toBe(atleast)
|
|
442
356
|
}
|
|
443
|
-
|
|
444
|
-
}
|
|
445
|
-
})
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
describe('$mol_wire tests', () => {
|
|
449
|
-
const name = framework.name
|
|
357
|
+
})
|
|
450
358
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
const B = framework.signal(0)
|
|
464
|
-
const C = framework.computed(() => (A.read() % 2) + (B.read() % 2))
|
|
465
|
-
const D = framework.computed(() =>
|
|
466
|
-
numbers.map(i => ({ x: i + (A.read() % 2) - (B.read() % 2) })),
|
|
467
|
-
)
|
|
468
|
-
const E = framework.computed(() =>
|
|
469
|
-
hard(C.read() + A.read() + D.read()[0].x, 'E'),
|
|
470
|
-
)
|
|
471
|
-
const F = framework.computed(() =>
|
|
472
|
-
hard(D.read()[2].x || B.read(), 'F'),
|
|
473
|
-
)
|
|
474
|
-
const G = framework.computed(
|
|
475
|
-
() =>
|
|
476
|
-
C.read() +
|
|
477
|
-
(C.read() || E.read() % 2) +
|
|
478
|
-
D.read()[4].x +
|
|
479
|
-
F.read(),
|
|
359
|
+
test('triangle', async () => {
|
|
360
|
+
const width = 10
|
|
361
|
+
const head = framework.signal(0)
|
|
362
|
+
let current = head as { read: () => number }
|
|
363
|
+
const list: { read: () => number }[] = []
|
|
364
|
+
for (let i = 0; i < width; i++) {
|
|
365
|
+
const c = current
|
|
366
|
+
list.push(current)
|
|
367
|
+
current = framework.computed(() => c.read() + 1)
|
|
368
|
+
}
|
|
369
|
+
const sum = framework.computed(() =>
|
|
370
|
+
list.map(x => x.read()).reduce((a, b) => a + b, 0),
|
|
480
371
|
)
|
|
372
|
+
const callCounter = new Counter()
|
|
481
373
|
framework.effect(() => {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
framework.effect(() => {
|
|
485
|
-
res.push(G.read())
|
|
486
|
-
}) // I
|
|
487
|
-
framework.effect(() => {
|
|
488
|
-
res.push(hard(F.read(), 'J'))
|
|
374
|
+
sum.read()
|
|
375
|
+
callCounter.count++
|
|
489
376
|
})
|
|
490
|
-
|
|
491
|
-
|
|
377
|
+
|
|
378
|
+
return () => {
|
|
379
|
+
const count = (number: number) =>
|
|
380
|
+
new Array(number)
|
|
381
|
+
.fill(0)
|
|
382
|
+
.map((_, i) => i + 1)
|
|
383
|
+
.reduce((x, y) => x + y, 0)
|
|
384
|
+
const constant = count(width)
|
|
385
|
+
framework.withBatch(() => {
|
|
386
|
+
head.write(1)
|
|
387
|
+
})
|
|
388
|
+
expect(sum.read()).toBe(constant)
|
|
389
|
+
const atleast = 100
|
|
390
|
+
callCounter.count = 0
|
|
391
|
+
for (let i = 0; i < 100; i++) {
|
|
392
|
+
framework.withBatch(() => {
|
|
393
|
+
head.write(i)
|
|
394
|
+
})
|
|
395
|
+
expect(sum.read()).toBe(constant - width + i * width)
|
|
396
|
+
}
|
|
397
|
+
expect(callCounter.count).toBe(atleast)
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
test('unstable', async () => {
|
|
402
|
+
const head = framework.signal(0)
|
|
403
|
+
const double = framework.computed(() => head.read() * 2)
|
|
404
|
+
const inverse = framework.computed(() => -head.read())
|
|
405
|
+
const current = framework.computed(() => {
|
|
406
|
+
let result = 0
|
|
407
|
+
for (let i = 0; i < 20; i++) {
|
|
408
|
+
result += head.read() % 2 ? double.read() : inverse.read()
|
|
409
|
+
}
|
|
410
|
+
return result
|
|
492
411
|
})
|
|
412
|
+
const callCounter = new Counter()
|
|
493
413
|
framework.effect(() => {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
framework.effect(() => {
|
|
497
|
-
res[2] = hard(F.read(), 'J')
|
|
414
|
+
current.read()
|
|
415
|
+
callCounter.count++
|
|
498
416
|
})
|
|
499
417
|
|
|
500
|
-
return (
|
|
501
|
-
res.length = 0
|
|
502
|
-
framework.withBatch(() => {
|
|
503
|
-
B.write(1)
|
|
504
|
-
A.write(1 + i * 2)
|
|
505
|
-
})
|
|
418
|
+
return () => {
|
|
506
419
|
framework.withBatch(() => {
|
|
507
|
-
|
|
508
|
-
B.write(2)
|
|
420
|
+
head.write(1)
|
|
509
421
|
})
|
|
422
|
+
expect(current.read()).toBe(40)
|
|
423
|
+
const atleast = 100
|
|
424
|
+
callCounter.count = 0
|
|
425
|
+
for (let i = 0; i < 100; i++) {
|
|
426
|
+
framework.withBatch(() => {
|
|
427
|
+
head.write(i)
|
|
428
|
+
})
|
|
429
|
+
expect(current.read()).toBe(i % 2 ? i * 2 * 10 : i * -10)
|
|
430
|
+
}
|
|
431
|
+
expect(callCounter.count).toBe(atleast)
|
|
510
432
|
}
|
|
511
433
|
})
|
|
512
|
-
|
|
513
|
-
expect(res.toString()).toBe([3201, 1604, 3196].toString())
|
|
514
434
|
})
|
|
515
|
-
})
|
|
516
435
|
|
|
517
|
-
describe(
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
[3, 6, 2, -2],
|
|
524
|
-
[2, 4, -2, -3],
|
|
525
|
-
],
|
|
526
|
-
20: [
|
|
527
|
-
[2, 4, -1, -6],
|
|
528
|
-
[-2, 1, -4, -4],
|
|
529
|
-
],
|
|
530
|
-
50: [
|
|
531
|
-
[-2, -4, 1, 6],
|
|
532
|
-
[2, -1, 4, 4],
|
|
533
|
-
],
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const cellx = (framework: ReactiveFramework, layers: number) => {
|
|
537
|
-
const start = {
|
|
538
|
-
prop1: framework.signal(1),
|
|
539
|
-
prop2: framework.signal(2),
|
|
540
|
-
prop3: framework.signal(3),
|
|
541
|
-
prop4: framework.signal(4),
|
|
436
|
+
describe(`$mol_wire tests [${name}]`, () => {
|
|
437
|
+
test('$mol_wire benchmark', () => {
|
|
438
|
+
// @ts-expect-error test
|
|
439
|
+
const fib = (n: number) => {
|
|
440
|
+
if (n < 2) return 1
|
|
441
|
+
return fib(n - 1) + fib(n - 2)
|
|
542
442
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
443
|
+
const hard = (n: number, _log: string) => n + fib(16)
|
|
444
|
+
const numbers = Array.from({ length: 5 }, (_, i) => i)
|
|
445
|
+
const res: (() => unknown)[] = []
|
|
446
|
+
framework.withBuild(() => {
|
|
447
|
+
const A = framework.signal(0)
|
|
448
|
+
const B = framework.signal(0)
|
|
449
|
+
const C = framework.computed(
|
|
450
|
+
() => (A.read() % 2) + (B.read() % 2),
|
|
451
|
+
)
|
|
452
|
+
const D = framework.computed(() =>
|
|
453
|
+
numbers.map(i => ({
|
|
454
|
+
x: i + (A.read() % 2) - (B.read() % 2),
|
|
455
|
+
})),
|
|
456
|
+
)
|
|
457
|
+
const E = framework.computed(() =>
|
|
458
|
+
hard(C.read() + A.read() + D.read()[0].x, 'E'),
|
|
459
|
+
)
|
|
460
|
+
const F = framework.computed(() =>
|
|
461
|
+
hard(D.read()[2].x || B.read(), 'F'),
|
|
462
|
+
)
|
|
463
|
+
const G = framework.computed(
|
|
464
|
+
() =>
|
|
465
|
+
C.read() +
|
|
466
|
+
(C.read() || E.read() % 2) +
|
|
467
|
+
D.read()[4].x +
|
|
468
|
+
F.read(),
|
|
469
|
+
)
|
|
558
470
|
framework.effect(() => {
|
|
559
|
-
|
|
471
|
+
res.push(hard(G.read(), 'H'))
|
|
560
472
|
})
|
|
561
473
|
framework.effect(() => {
|
|
562
|
-
|
|
474
|
+
res.push(G.read())
|
|
563
475
|
})
|
|
564
476
|
framework.effect(() => {
|
|
565
|
-
|
|
477
|
+
res.push(hard(F.read(), 'J'))
|
|
566
478
|
})
|
|
567
479
|
framework.effect(() => {
|
|
568
|
-
|
|
480
|
+
res[0] = hard(G.read(), 'H')
|
|
569
481
|
})
|
|
570
|
-
|
|
571
482
|
framework.effect(() => {
|
|
572
|
-
|
|
483
|
+
res[1] = G.read()
|
|
573
484
|
})
|
|
574
485
|
framework.effect(() => {
|
|
575
|
-
|
|
486
|
+
res[2] = hard(F.read(), 'J')
|
|
576
487
|
})
|
|
577
|
-
|
|
488
|
+
|
|
489
|
+
return (i: number) => {
|
|
490
|
+
res.length = 0
|
|
491
|
+
framework.withBatch(() => {
|
|
492
|
+
B.write(1)
|
|
493
|
+
A.write(1 + i * 2)
|
|
494
|
+
})
|
|
495
|
+
framework.withBatch(() => {
|
|
496
|
+
A.write(2 + i * 2)
|
|
497
|
+
B.write(2)
|
|
498
|
+
})
|
|
499
|
+
}
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
expect(res.toString()).toBe([3201, 1604, 3196].toString())
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
describe(`CellX tests [${name}]`, () => {
|
|
507
|
+
test('CellX benchmark', () => {
|
|
508
|
+
const expected = {
|
|
509
|
+
10: [
|
|
510
|
+
[3, 6, 2, -2],
|
|
511
|
+
[2, 4, -2, -3],
|
|
512
|
+
],
|
|
513
|
+
20: [
|
|
514
|
+
[2, 4, -1, -6],
|
|
515
|
+
[-2, 1, -4, -4],
|
|
516
|
+
],
|
|
517
|
+
50: [
|
|
518
|
+
[-2, -4, 1, 6],
|
|
519
|
+
[2, -1, 4, 4],
|
|
520
|
+
],
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const cellx = (framework: ReactiveFramework, layers: number) => {
|
|
524
|
+
const start = {
|
|
525
|
+
prop1: framework.signal(1),
|
|
526
|
+
prop2: framework.signal(2),
|
|
527
|
+
prop3: framework.signal(3),
|
|
528
|
+
prop4: framework.signal(4),
|
|
529
|
+
}
|
|
530
|
+
let layer: Record<string, Computed<number>> = start
|
|
531
|
+
|
|
532
|
+
for (let i = layers; i > 0; i--) {
|
|
533
|
+
const m = layer
|
|
534
|
+
const s = {
|
|
535
|
+
prop1: framework.computed(() => m.prop2.read()),
|
|
536
|
+
prop2: framework.computed(
|
|
537
|
+
() => m.prop1.read() - m.prop3.read(),
|
|
538
|
+
),
|
|
539
|
+
prop3: framework.computed(
|
|
540
|
+
() => m.prop2.read() + m.prop4.read(),
|
|
541
|
+
),
|
|
542
|
+
prop4: framework.computed(() => m.prop3.read()),
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
framework.effect(() => {
|
|
546
|
+
s.prop1.read()
|
|
547
|
+
})
|
|
548
|
+
framework.effect(() => {
|
|
549
|
+
s.prop2.read()
|
|
550
|
+
})
|
|
551
|
+
framework.effect(() => {
|
|
552
|
+
s.prop3.read()
|
|
553
|
+
})
|
|
554
|
+
framework.effect(() => {
|
|
555
|
+
s.prop4.read()
|
|
556
|
+
})
|
|
557
|
+
framework.effect(() => {
|
|
558
|
+
s.prop1.read()
|
|
559
|
+
})
|
|
560
|
+
framework.effect(() => {
|
|
561
|
+
s.prop2.read()
|
|
562
|
+
})
|
|
563
|
+
framework.effect(() => {
|
|
564
|
+
s.prop3.read()
|
|
565
|
+
})
|
|
566
|
+
framework.effect(() => {
|
|
567
|
+
s.prop4.read()
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
s.prop1.read()
|
|
571
|
+
s.prop2.read()
|
|
578
572
|
s.prop3.read()
|
|
579
|
-
})
|
|
580
|
-
framework.effect(() => {
|
|
581
573
|
s.prop4.read()
|
|
582
|
-
})
|
|
583
574
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
s.prop3.read()
|
|
587
|
-
s.prop4.read()
|
|
575
|
+
layer = s
|
|
576
|
+
}
|
|
588
577
|
|
|
589
|
-
|
|
590
|
-
}
|
|
578
|
+
const end = layer
|
|
591
579
|
|
|
592
|
-
|
|
580
|
+
const before = [
|
|
581
|
+
end.prop1.read(),
|
|
582
|
+
end.prop2.read(),
|
|
583
|
+
end.prop3.read(),
|
|
584
|
+
end.prop4.read(),
|
|
585
|
+
]
|
|
593
586
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
587
|
+
framework.withBatch(() => {
|
|
588
|
+
start.prop1.write(4)
|
|
589
|
+
start.prop2.write(3)
|
|
590
|
+
start.prop3.write(2)
|
|
591
|
+
start.prop4.write(1)
|
|
592
|
+
})
|
|
600
593
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
594
|
+
const after = [
|
|
595
|
+
end.prop1.read(),
|
|
596
|
+
end.prop2.read(),
|
|
597
|
+
end.prop3.read(),
|
|
598
|
+
end.prop4.read(),
|
|
599
|
+
]
|
|
607
600
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
const [before, after] = cellx(framework, layers)
|
|
621
|
-
const [expectedBefore, expectedAfter] = expected[layers]
|
|
622
|
-
expect(before.toString()).toBe(expectedBefore.toString())
|
|
623
|
-
expect(after.toString()).toBe(expectedAfter.toString())
|
|
624
|
-
}
|
|
601
|
+
return [before, after]
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
for (const layers in expected) {
|
|
605
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
606
|
+
const [before, after] = cellx(framework, layers)
|
|
607
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
608
|
+
const [expectedBefore, expectedAfter] = expected[layers]
|
|
609
|
+
expect(before.toString()).toBe(expectedBefore.toString())
|
|
610
|
+
expect(after.toString()).toBe(expectedAfter.toString())
|
|
611
|
+
}
|
|
612
|
+
})
|
|
625
613
|
})
|
|
626
|
-
}
|
|
614
|
+
}
|