@zeix/cause-effect 0.13.2 → 0.14.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/README.md +160 -130
- package/index.d.ts +7 -5
- package/index.js +1 -1
- package/index.ts +6 -6
- package/package.json +1 -1
- package/src/computed.d.ts +15 -17
- package/src/computed.ts +26 -200
- package/src/effect.d.ts +9 -12
- package/src/effect.ts +54 -28
- package/src/memo.d.ts +13 -0
- package/src/memo.ts +91 -0
- package/src/scheduler.d.ts +15 -11
- package/src/scheduler.ts +32 -15
- package/src/signal.d.ts +6 -20
- package/src/signal.ts +34 -67
- package/src/state.d.ts +4 -7
- package/src/state.ts +9 -39
- package/src/task.d.ts +17 -0
- package/src/task.ts +153 -0
- package/src/util.ts +2 -0
- package/test/batch.test.ts +10 -14
- package/test/benchmark.test.ts +81 -69
- package/test/computed.test.ts +108 -123
- package/test/effect.test.ts +24 -16
- package/test/state.test.ts +2 -53
- package/test/util/dependency-graph.ts +2 -2
package/test/benchmark.test.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { describe, test, expect, mock } from 'bun:test'
|
|
2
|
-
import { state,
|
|
2
|
+
import { state, memo, effect, batch } from '../'
|
|
3
3
|
import { makeGraph, runGraph, Counter } from './util/dependency-graph'
|
|
4
|
+
import {
|
|
5
|
+
type ReactiveFramework,
|
|
6
|
+
type Computed,
|
|
7
|
+
} from './util/reactive-framework'
|
|
4
8
|
|
|
5
9
|
/* === Utility Functions === */
|
|
6
10
|
|
|
7
11
|
const busy = () => {
|
|
8
|
-
let
|
|
12
|
+
let _a = 0
|
|
9
13
|
for (let i = 0; i < 1_00; i++) {
|
|
10
|
-
|
|
14
|
+
_a++
|
|
11
15
|
}
|
|
12
16
|
}
|
|
13
17
|
|
|
@@ -21,14 +25,14 @@ const framework = {
|
|
|
21
25
|
}
|
|
22
26
|
},
|
|
23
27
|
computed: <T extends {}>(fn: () => T) => {
|
|
24
|
-
const c =
|
|
28
|
+
const c = memo(fn)
|
|
25
29
|
return {
|
|
26
30
|
read: () => c.get(),
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
33
|
effect: (fn: () => void) => effect(fn),
|
|
30
34
|
withBatch: (fn: () => void) => batch(fn),
|
|
31
|
-
withBuild: (fn: () =>
|
|
35
|
+
withBuild: <T>(fn: () => T) => fn(),
|
|
32
36
|
}
|
|
33
37
|
const testPullCounts = true
|
|
34
38
|
|
|
@@ -76,9 +80,9 @@ describe('Basic test', function () {
|
|
|
76
80
|
test(`${name} | static graph`, () => {
|
|
77
81
|
const config = makeConfig()
|
|
78
82
|
const counter = new Counter()
|
|
79
|
-
// @ts-expect-error
|
|
83
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
80
84
|
const graph = makeGraph(framework, config, counter)
|
|
81
|
-
// @ts-expect-error
|
|
85
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
82
86
|
const sum = runGraph(graph, 2, 1, framework)
|
|
83
87
|
expect(sum).toEqual(16)
|
|
84
88
|
if (testPullCounts) {
|
|
@@ -94,9 +98,9 @@ describe('Basic test', function () {
|
|
|
94
98
|
config.readFraction = 2 / 3
|
|
95
99
|
config.iterations = 10
|
|
96
100
|
const counter = new Counter()
|
|
97
|
-
// @ts-expect-error
|
|
101
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
98
102
|
const graph = makeGraph(framework, config, counter)
|
|
99
|
-
// @ts-expect-error
|
|
103
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
100
104
|
const sum = runGraph(graph, 10, 2 / 3, framework)
|
|
101
105
|
|
|
102
106
|
expect(sum).toEqual(71)
|
|
@@ -115,9 +119,9 @@ describe('Basic test', function () {
|
|
|
115
119
|
config.width = 4
|
|
116
120
|
config.totalLayers = 2
|
|
117
121
|
const counter = new Counter()
|
|
118
|
-
// @ts-expect-error
|
|
122
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
119
123
|
const graph = makeGraph(framework, config, counter)
|
|
120
|
-
// @ts-expect-error
|
|
124
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
121
125
|
const sum = runGraph(graph, 10, 1, framework)
|
|
122
126
|
|
|
123
127
|
expect(sum).toEqual(72)
|
|
@@ -137,8 +141,6 @@ describe('Basic test', function () {
|
|
|
137
141
|
expect(c.read()).toEqual(4)
|
|
138
142
|
return c.read()
|
|
139
143
|
})
|
|
140
|
-
|
|
141
|
-
// @ts-expect-error
|
|
142
144
|
expect(r).toEqual(4)
|
|
143
145
|
})
|
|
144
146
|
|
|
@@ -203,10 +205,10 @@ describe('Kairo tests', function () {
|
|
|
203
205
|
let last = head as { read: () => number }
|
|
204
206
|
const callCounter = new Counter()
|
|
205
207
|
for (let i = 0; i < 50; i++) {
|
|
206
|
-
|
|
208
|
+
const current = framework.computed(() => {
|
|
207
209
|
return head.read()! + i
|
|
208
210
|
})
|
|
209
|
-
|
|
211
|
+
const current2 = framework.computed(() => {
|
|
210
212
|
return current.read()! + 1
|
|
211
213
|
})
|
|
212
214
|
framework.effect(() => {
|
|
@@ -233,16 +235,16 @@ describe('Kairo tests', function () {
|
|
|
233
235
|
})
|
|
234
236
|
|
|
235
237
|
test(`${name} | deep propagation`, () => {
|
|
236
|
-
|
|
238
|
+
const len = 50
|
|
237
239
|
const head = framework.signal(0)
|
|
238
240
|
let current = head as { read: () => number }
|
|
239
241
|
for (let i = 0; i < len; i++) {
|
|
240
|
-
|
|
242
|
+
const c = current
|
|
241
243
|
current = framework.computed(() => {
|
|
242
244
|
return c.read() + 1
|
|
243
245
|
})
|
|
244
246
|
}
|
|
245
|
-
|
|
247
|
+
const callCounter = new Counter()
|
|
246
248
|
framework.effect(() => {
|
|
247
249
|
current.read()
|
|
248
250
|
callCounter.count++
|
|
@@ -266,16 +268,16 @@ describe('Kairo tests', function () {
|
|
|
266
268
|
})
|
|
267
269
|
|
|
268
270
|
test(`${name} | diamond`, function () {
|
|
269
|
-
|
|
271
|
+
const width = 5
|
|
270
272
|
const head = framework.signal(0)
|
|
271
|
-
|
|
273
|
+
const current: { read(): number }[] = []
|
|
272
274
|
for (let i = 0; i < width; i++) {
|
|
273
275
|
current.push(framework.computed(() => head.read() + 1))
|
|
274
276
|
}
|
|
275
|
-
|
|
277
|
+
const sum = framework.computed(() => {
|
|
276
278
|
return current.map(x => x.read()).reduce((a, b) => a + b, 0)
|
|
277
279
|
})
|
|
278
|
-
|
|
280
|
+
const callCounter = new Counter()
|
|
279
281
|
framework.effect(() => {
|
|
280
282
|
sum.read()
|
|
281
283
|
callCounter.count++
|
|
@@ -299,7 +301,7 @@ describe('Kairo tests', function () {
|
|
|
299
301
|
})
|
|
300
302
|
|
|
301
303
|
test(`${name} | mux`, function () {
|
|
302
|
-
|
|
304
|
+
const heads = new Array(100).fill(null).map(_ => framework.signal(0))
|
|
303
305
|
const mux = framework.computed(() => {
|
|
304
306
|
return Object.fromEntries(heads.map(h => h.read()).entries())
|
|
305
307
|
})
|
|
@@ -329,15 +331,15 @@ describe('Kairo tests', function () {
|
|
|
329
331
|
|
|
330
332
|
test(`${name} | repeated observers`, function () {
|
|
331
333
|
const size = 30
|
|
332
|
-
|
|
333
|
-
|
|
334
|
+
const head = framework.signal(0)
|
|
335
|
+
const current = framework.computed(() => {
|
|
334
336
|
let result = 0
|
|
335
337
|
for (let i = 0; i < size; i++) {
|
|
336
338
|
result += head.read()
|
|
337
339
|
}
|
|
338
340
|
return result
|
|
339
341
|
})
|
|
340
|
-
|
|
342
|
+
const callCounter = new Counter()
|
|
341
343
|
framework.effect(() => {
|
|
342
344
|
current.read()
|
|
343
345
|
callCounter.count++
|
|
@@ -361,28 +363,28 @@ describe('Kairo tests', function () {
|
|
|
361
363
|
})
|
|
362
364
|
|
|
363
365
|
test(`${name} | triangle`, function () {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
+
const width = 10
|
|
367
|
+
const head = framework.signal(0)
|
|
366
368
|
let current = head as { read: () => number }
|
|
367
|
-
|
|
369
|
+
const list: { read: () => number }[] = []
|
|
368
370
|
for (let i = 0; i < width; i++) {
|
|
369
|
-
|
|
371
|
+
const c = current
|
|
370
372
|
list.push(current)
|
|
371
373
|
current = framework.computed(() => {
|
|
372
374
|
return c.read() + 1
|
|
373
375
|
})
|
|
374
376
|
}
|
|
375
|
-
|
|
377
|
+
const sum = framework.computed(() => {
|
|
376
378
|
return list.map(x => x.read()).reduce((a, b) => a + b, 0)
|
|
377
379
|
})
|
|
378
|
-
|
|
380
|
+
const callCounter = new Counter()
|
|
379
381
|
framework.effect(() => {
|
|
380
382
|
sum.read()
|
|
381
383
|
callCounter.count++
|
|
382
384
|
})
|
|
383
385
|
|
|
384
386
|
return () => {
|
|
385
|
-
const count = (number:
|
|
387
|
+
const count = (number: number) => {
|
|
386
388
|
return new Array(number)
|
|
387
389
|
.fill(0)
|
|
388
390
|
.map((_, i) => i + 1)
|
|
@@ -406,17 +408,17 @@ describe('Kairo tests', function () {
|
|
|
406
408
|
})
|
|
407
409
|
|
|
408
410
|
test(`${name} | unstable`, function () {
|
|
409
|
-
|
|
411
|
+
const head = framework.signal(0)
|
|
410
412
|
const double = framework.computed(() => head.read() * 2)
|
|
411
413
|
const inverse = framework.computed(() => -head.read())
|
|
412
|
-
|
|
414
|
+
const current = framework.computed(() => {
|
|
413
415
|
let result = 0
|
|
414
416
|
for (let i = 0; i < 20; i++) {
|
|
415
417
|
result += head.read() % 2 ? double.read() : inverse.read()
|
|
416
418
|
}
|
|
417
419
|
return result
|
|
418
420
|
})
|
|
419
|
-
|
|
421
|
+
const callCounter = new Counter()
|
|
420
422
|
framework.effect(() => {
|
|
421
423
|
current.read()
|
|
422
424
|
callCounter.count++
|
|
@@ -440,39 +442,45 @@ describe('Kairo tests', function () {
|
|
|
440
442
|
})
|
|
441
443
|
})
|
|
442
444
|
|
|
443
|
-
|
|
445
|
+
describe('$mol_wire tests', function () {
|
|
444
446
|
const name = framework.name
|
|
445
447
|
|
|
446
|
-
test(`${name} | $mol_wire benchmark`, function() {
|
|
448
|
+
test(`${name} | $mol_wire benchmark`, function () {
|
|
447
449
|
const fib = (n: number) => {
|
|
448
450
|
if (n < 2) return 1
|
|
449
451
|
return fib(n - 1) + fib(n - 2)
|
|
450
452
|
}
|
|
451
|
-
const hard = (n: number,
|
|
453
|
+
const hard = (n: number, _log: string) => {
|
|
452
454
|
return n + fib(16)
|
|
453
455
|
}
|
|
454
456
|
const numbers = Array.from({ length: 5 }, (_, i) => i)
|
|
455
|
-
|
|
456
|
-
|
|
457
|
+
const res: (() => any)[] = []
|
|
458
|
+
framework.withBuild(() => {
|
|
457
459
|
const A = framework.signal(0)
|
|
458
460
|
const B = framework.signal(0)
|
|
459
461
|
const C = framework.computed(() => (A.read() % 2) + (B.read() % 2))
|
|
460
462
|
const D = framework.computed(() =>
|
|
461
|
-
numbers.map(
|
|
463
|
+
numbers.map(i => ({ x: i + (A.read() % 2) - (B.read() % 2) })),
|
|
462
464
|
)
|
|
463
465
|
const E = framework.computed(() =>
|
|
464
|
-
hard(C.read() + A.read() + D.read()[0].x,
|
|
466
|
+
hard(C.read() + A.read() + D.read()[0].x, 'E'),
|
|
467
|
+
)
|
|
468
|
+
const F = framework.computed(() =>
|
|
469
|
+
hard(D.read()[2].x || B.read(), 'F'),
|
|
465
470
|
)
|
|
466
|
-
const F = framework.computed(() => hard(D.read()[2].x || B.read(), "F"))
|
|
467
471
|
const G = framework.computed(
|
|
468
|
-
() =>
|
|
472
|
+
() =>
|
|
473
|
+
C.read() +
|
|
474
|
+
(C.read() || E.read() % 2) +
|
|
475
|
+
D.read()[4].x +
|
|
476
|
+
F.read(),
|
|
469
477
|
)
|
|
470
|
-
framework.effect(() => res.push(hard(G.read(),
|
|
471
|
-
framework.effect(() => res.push(G.read()))
|
|
472
|
-
framework.effect(() => res.push(hard(F.read(),
|
|
473
|
-
framework.effect(() => res[0] = hard(G.read(),
|
|
474
|
-
framework.effect(() => res[1] = G.read())
|
|
475
|
-
framework.effect(() => res[2] = hard(F.read(),
|
|
478
|
+
framework.effect(() => res.push(hard(G.read(), 'H')))
|
|
479
|
+
framework.effect(() => res.push(G.read())) // I
|
|
480
|
+
framework.effect(() => res.push(hard(F.read(), 'J')))
|
|
481
|
+
framework.effect(() => (res[0] = hard(G.read(), 'H')))
|
|
482
|
+
framework.effect(() => (res[1] = G.read())) // I
|
|
483
|
+
framework.effect(() => (res[2] = hard(F.read(), 'J')))
|
|
476
484
|
|
|
477
485
|
return (i: number) => {
|
|
478
486
|
res.length = 0
|
|
@@ -494,39 +502,42 @@ describe('Kairo tests', function () {
|
|
|
494
502
|
describe('CellX tests', function () {
|
|
495
503
|
const name = framework.name
|
|
496
504
|
|
|
497
|
-
test(`${name} | CellX benchmark`, function() {
|
|
505
|
+
test(`${name} | CellX benchmark`, function () {
|
|
498
506
|
const expected = {
|
|
499
|
-
|
|
500
|
-
[
|
|
501
|
-
[
|
|
502
|
-
],
|
|
503
|
-
2500: [
|
|
504
|
-
[-3, -6, -2, 2],
|
|
505
|
-
[-2, -4, 2, 3],
|
|
507
|
+
10: [
|
|
508
|
+
[3, 6, 2, -2],
|
|
509
|
+
[2, 4, -2, -3],
|
|
506
510
|
],
|
|
507
|
-
|
|
511
|
+
20: [
|
|
508
512
|
[2, 4, -1, -6],
|
|
509
513
|
[-2, 1, -4, -4],
|
|
510
|
-
]
|
|
514
|
+
],
|
|
515
|
+
50: [
|
|
516
|
+
[-2, -4, 1, 6],
|
|
517
|
+
[2, -1, 4, 4],
|
|
518
|
+
],
|
|
511
519
|
}
|
|
512
|
-
const results = {}
|
|
513
520
|
|
|
514
|
-
const cellx = (framework, layers) => {
|
|
521
|
+
const cellx = (framework: ReactiveFramework, layers: number) => {
|
|
515
522
|
const start = {
|
|
516
523
|
prop1: framework.signal(1),
|
|
517
524
|
prop2: framework.signal(2),
|
|
518
525
|
prop3: framework.signal(3),
|
|
519
526
|
prop4: framework.signal(4),
|
|
520
527
|
}
|
|
521
|
-
let layer = start
|
|
528
|
+
let layer: Record<string, Computed<number>> = start
|
|
522
529
|
|
|
523
530
|
for (let i = layers; i > 0; i--) {
|
|
524
531
|
const m = layer
|
|
525
532
|
const s = {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
533
|
+
prop1: framework.computed(() => m.prop2.read()),
|
|
534
|
+
prop2: framework.computed(
|
|
535
|
+
() => m.prop1.read() - m.prop3.read(),
|
|
536
|
+
),
|
|
537
|
+
prop3: framework.computed(
|
|
538
|
+
() => m.prop2.read() + m.prop4.read(),
|
|
539
|
+
),
|
|
540
|
+
prop4: framework.computed(() => m.prop3.read()),
|
|
530
541
|
}
|
|
531
542
|
|
|
532
543
|
framework.effect(() => s.prop1.read())
|
|
@@ -569,10 +580,11 @@ describe('CellX tests', function () {
|
|
|
569
580
|
}
|
|
570
581
|
|
|
571
582
|
for (const layers in expected) {
|
|
583
|
+
// @ts-expect-error - Framework object has incompatible type constraints with ReactiveFramework
|
|
572
584
|
const [before, after] = cellx(framework, layers)
|
|
573
585
|
const [expectedBefore, expectedAfter] = expected[layers]
|
|
574
586
|
expect(before.toString()).toBe(expectedBefore.toString())
|
|
575
587
|
expect(after.toString()).toBe(expectedAfter.toString())
|
|
576
588
|
}
|
|
577
589
|
})
|
|
578
|
-
})
|
|
590
|
+
})
|