@zeix/cause-effect 0.17.3 → 0.18.1
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 +169 -227
- package/.cursorrules +41 -35
- package/.github/copilot-instructions.md +176 -116
- package/ARCHITECTURE.md +276 -0
- package/CHANGELOG.md +29 -0
- package/CLAUDE.md +201 -143
- package/GUIDE.md +298 -0
- package/README.md +246 -193
- package/REQUIREMENTS.md +100 -0
- package/bench/reactivity.bench.ts +577 -0
- package/context7.json +4 -0
- package/examples/events-sensor.ts +187 -0
- package/examples/selector-sensor.ts +173 -0
- package/index.dev.js +1390 -1008
- package/index.js +1 -1
- package/index.ts +60 -74
- package/package.json +5 -2
- package/skills/changelog-keeper/SKILL.md +59 -0
- package/skills/changelog-keeper/agents/openai.yaml +4 -0
- package/src/errors.ts +118 -74
- package/src/graph.ts +612 -0
- package/src/nodes/collection.ts +512 -0
- package/src/nodes/effect.ts +149 -0
- package/src/nodes/list.ts +589 -0
- package/src/nodes/memo.ts +148 -0
- package/src/nodes/sensor.ts +149 -0
- package/src/nodes/state.ts +135 -0
- package/src/nodes/store.ts +378 -0
- package/src/nodes/task.ts +174 -0
- package/src/signal.ts +112 -66
- package/src/util.ts +26 -57
- package/test/batch.test.ts +96 -62
- package/test/benchmark.test.ts +473 -487
- package/test/collection.test.ts +456 -707
- package/test/effect.test.ts +293 -696
- package/test/list.test.ts +335 -592
- package/test/memo.test.ts +574 -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 -265
- package/test/store.test.ts +346 -446
- package/test/task.test.ts +529 -0
- package/test/untrack.test.ts +167 -0
- package/types/index.d.ts +13 -15
- package/types/src/errors.d.ts +73 -17
- package/types/src/graph.d.ts +218 -0
- package/types/src/nodes/collection.d.ts +69 -0
- package/types/src/nodes/effect.d.ts +48 -0
- package/types/src/nodes/list.d.ts +66 -0
- package/types/src/nodes/memo.d.ts +63 -0
- package/types/src/nodes/sensor.d.ts +81 -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 +79 -0
- package/types/src/signal.d.ts +43 -29
- package/types/src/util.d.ts +9 -16
- package/archive/benchmark.ts +0 -683
- package/archive/collection.ts +0 -253
- package/archive/composite.ts +0 -85
- package/archive/computed.ts +0 -195
- package/archive/list.ts +0 -483
- package/archive/memo.ts +0 -139
- package/archive/state.ts +0 -90
- package/archive/store.ts +0 -298
- package/archive/task.ts +0 -189
- package/src/classes/collection.ts +0 -245
- package/src/classes/computed.ts +0 -349
- package/src/classes/list.ts +0 -343
- package/src/classes/ref.ts +0 -70
- package/src/classes/state.ts +0 -102
- package/src/classes/store.ts +0 -262
- package/src/diff.ts +0 -138
- package/src/effect.ts +0 -93
- package/src/match.ts +0 -45
- package/src/resolve.ts +0 -49
- package/src/system.ts +0 -257
- package/test/computed.test.ts +0 -1108
- package/test/diff.test.ts +0 -955
- package/test/match.test.ts +0 -388
- package/test/ref.test.ts +0 -353
- package/test/resolve.test.ts +0 -154
- package/types/src/classes/collection.d.ts +0 -45
- package/types/src/classes/computed.d.ts +0 -94
- package/types/src/classes/list.d.ts +0 -43
- package/types/src/classes/ref.d.ts +0 -35
- package/types/src/classes/state.d.ts +0 -49
- package/types/src/classes/store.d.ts +0 -52
- 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 -78
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) => batch(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,584 +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 A = framework.signal(0)
|
|
464
|
-
const B = framework.signal(0)
|
|
465
|
-
const C = framework.computed(() => (A.read() % 2) + (B.read() % 2))
|
|
466
|
-
const D = framework.computed(() =>
|
|
467
|
-
numbers.map(i => ({ x: i + (A.read() % 2) - (B.read() % 2) })),
|
|
468
|
-
)
|
|
469
|
-
const E = framework.computed(() =>
|
|
470
|
-
hard(C.read() + A.read() + D.read()[0].x, 'E'),
|
|
471
|
-
)
|
|
472
|
-
const F = framework.computed(() =>
|
|
473
|
-
hard(D.read()[2].x || B.read(), 'F'),
|
|
474
|
-
)
|
|
475
|
-
const G = framework.computed(
|
|
476
|
-
() =>
|
|
477
|
-
C.read() +
|
|
478
|
-
(C.read() || E.read() % 2) +
|
|
479
|
-
D.read()[4].x +
|
|
480
|
-
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),
|
|
481
371
|
)
|
|
372
|
+
const callCounter = new Counter()
|
|
482
373
|
framework.effect(() => {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
framework.effect(() => {
|
|
486
|
-
res.push(G.read())
|
|
487
|
-
}) // I
|
|
488
|
-
framework.effect(() => {
|
|
489
|
-
res.push(hard(F.read(), 'J'))
|
|
374
|
+
sum.read()
|
|
375
|
+
callCounter.count++
|
|
490
376
|
})
|
|
491
|
-
|
|
492
|
-
|
|
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
|
|
493
411
|
})
|
|
412
|
+
const callCounter = new Counter()
|
|
494
413
|
framework.effect(() => {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
framework.effect(() => {
|
|
498
|
-
res[2] = hard(F.read(), 'J')
|
|
414
|
+
current.read()
|
|
415
|
+
callCounter.count++
|
|
499
416
|
})
|
|
500
417
|
|
|
501
|
-
return (
|
|
502
|
-
res.length = 0
|
|
503
|
-
framework.withBatch(() => {
|
|
504
|
-
B.write(1)
|
|
505
|
-
A.write(1 + i * 2)
|
|
506
|
-
})
|
|
418
|
+
return () => {
|
|
507
419
|
framework.withBatch(() => {
|
|
508
|
-
|
|
509
|
-
B.write(2)
|
|
420
|
+
head.write(1)
|
|
510
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)
|
|
511
432
|
}
|
|
512
433
|
})
|
|
513
|
-
|
|
514
|
-
expect(res.toString()).toBe([3201, 1604, 3196].toString())
|
|
515
434
|
})
|
|
516
|
-
})
|
|
517
435
|
|
|
518
|
-
describe(
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
[3, 6, 2, -2],
|
|
525
|
-
[2, 4, -2, -3],
|
|
526
|
-
],
|
|
527
|
-
20: [
|
|
528
|
-
[2, 4, -1, -6],
|
|
529
|
-
[-2, 1, -4, -4],
|
|
530
|
-
],
|
|
531
|
-
50: [
|
|
532
|
-
[-2, -4, 1, 6],
|
|
533
|
-
[2, -1, 4, 4],
|
|
534
|
-
],
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
const cellx = (framework: ReactiveFramework, layers: number) => {
|
|
538
|
-
const start = {
|
|
539
|
-
prop1: framework.signal(1),
|
|
540
|
-
prop2: framework.signal(2),
|
|
541
|
-
prop3: framework.signal(3),
|
|
542
|
-
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)
|
|
543
442
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
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
|
+
)
|
|
559
470
|
framework.effect(() => {
|
|
560
|
-
|
|
471
|
+
res.push(hard(G.read(), 'H'))
|
|
561
472
|
})
|
|
562
473
|
framework.effect(() => {
|
|
563
|
-
|
|
474
|
+
res.push(G.read())
|
|
564
475
|
})
|
|
565
476
|
framework.effect(() => {
|
|
566
|
-
|
|
477
|
+
res.push(hard(F.read(), 'J'))
|
|
567
478
|
})
|
|
568
479
|
framework.effect(() => {
|
|
569
|
-
|
|
480
|
+
res[0] = hard(G.read(), 'H')
|
|
570
481
|
})
|
|
571
|
-
|
|
572
482
|
framework.effect(() => {
|
|
573
|
-
|
|
483
|
+
res[1] = G.read()
|
|
574
484
|
})
|
|
575
485
|
framework.effect(() => {
|
|
576
|
-
|
|
486
|
+
res[2] = hard(F.read(), 'J')
|
|
577
487
|
})
|
|
578
|
-
|
|
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()
|
|
579
572
|
s.prop3.read()
|
|
580
|
-
})
|
|
581
|
-
framework.effect(() => {
|
|
582
573
|
s.prop4.read()
|
|
583
|
-
})
|
|
584
574
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
s.prop3.read()
|
|
588
|
-
s.prop4.read()
|
|
575
|
+
layer = s
|
|
576
|
+
}
|
|
589
577
|
|
|
590
|
-
|
|
591
|
-
}
|
|
578
|
+
const end = layer
|
|
592
579
|
|
|
593
|
-
|
|
580
|
+
const before = [
|
|
581
|
+
end.prop1.read(),
|
|
582
|
+
end.prop2.read(),
|
|
583
|
+
end.prop3.read(),
|
|
584
|
+
end.prop4.read(),
|
|
585
|
+
]
|
|
594
586
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
587
|
+
framework.withBatch(() => {
|
|
588
|
+
start.prop1.write(4)
|
|
589
|
+
start.prop2.write(3)
|
|
590
|
+
start.prop3.write(2)
|
|
591
|
+
start.prop4.write(1)
|
|
592
|
+
})
|
|
601
593
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
594
|
+
const after = [
|
|
595
|
+
end.prop1.read(),
|
|
596
|
+
end.prop2.read(),
|
|
597
|
+
end.prop3.read(),
|
|
598
|
+
end.prop4.read(),
|
|
599
|
+
]
|
|
608
600
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
const [before, after] = cellx(framework, layers)
|
|
622
|
-
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
623
|
-
const [expectedBefore, expectedAfter] = expected[layers]
|
|
624
|
-
expect(before.toString()).toBe(expectedBefore.toString())
|
|
625
|
-
expect(after.toString()).toBe(expectedAfter.toString())
|
|
626
|
-
}
|
|
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
|
+
})
|
|
627
613
|
})
|
|
628
|
-
}
|
|
614
|
+
}
|