@zeix/cause-effect 0.13.1 → 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.
@@ -1,18 +1,22 @@
1
1
  import { describe, test, expect, mock } from 'bun:test'
2
- import { state, computed, effect, batch } from '../'
3
- import { makeGraph, runGraph, Counter } from "./util/dependency-graph"
2
+ import { state, memo, effect, batch } from '../'
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 a = 0
12
+ let _a = 0
9
13
  for (let i = 0; i < 1_00; i++) {
10
- a++
14
+ _a++
11
15
  }
12
16
  }
13
17
 
14
18
  const framework = {
15
- name: "Cause & Effect",
19
+ name: 'Cause & Effect',
16
20
  signal: <T extends {}>(initialValue: T) => {
17
21
  const s = state<T>(initialValue)
18
22
  return {
@@ -21,14 +25,14 @@ const framework = {
21
25
  }
22
26
  },
23
27
  computed: <T extends {}>(fn: () => T) => {
24
- const c = computed(fn)
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: () => void) => fn(),
35
+ withBuild: <T>(fn: () => T) => fn(),
32
36
  }
33
37
  const testPullCounts = true
34
38
 
@@ -45,7 +49,7 @@ function makeConfig() {
45
49
  }
46
50
 
47
51
  /* === Test functions === */
48
-
52
+
49
53
  /** some basic tests to validate the reactive framework
50
54
  * wrapper works and can run performance tests.
51
55
  */
@@ -55,30 +59,30 @@ describe('Basic test', function () {
55
59
  framework.withBuild(() => {
56
60
  const s = framework.signal(2)
57
61
  const c = framework.computed(() => s.read() * 2)
58
-
62
+
59
63
  expect(c.read()).toEqual(4)
60
64
  })
61
65
  })
62
-
66
+
63
67
  test(`${name} | simple write`, () => {
64
68
  framework.withBuild(() => {
65
69
  const s = framework.signal(2)
66
70
  const c = framework.computed(() => s.read() * 2)
67
71
  expect(s.read()).toEqual(2)
68
72
  expect(c.read()).toEqual(4)
69
-
73
+
70
74
  s.write(3)
71
75
  expect(s.read()).toEqual(3)
72
76
  expect(c.read()).toEqual(6)
73
77
  })
74
78
  })
75
-
79
+
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) {
@@ -87,18 +91,18 @@ describe('Basic test', function () {
87
91
  expect(counter.count).toBeGreaterThanOrEqual(11)
88
92
  }
89
93
  })
90
-
94
+
91
95
  test(`${name} | static graph, read 2/3 of leaves`, () => {
92
96
  framework.withBuild(() => {
93
97
  const config = makeConfig()
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)
103
107
  if (testPullCounts) {
104
108
  expect(counter.count).toEqual(41)
@@ -107,7 +111,7 @@ describe('Basic test', function () {
107
111
  }
108
112
  })
109
113
  })
110
-
114
+
111
115
  test(`${name} | dynamic graph`, () => {
112
116
  framework.withBuild(() => {
113
117
  const config = makeConfig()
@@ -115,11 +119,11 @@ 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)
124
128
  if (testPullCounts) {
125
129
  expect(counter.count).toEqual(22)
@@ -128,38 +132,36 @@ describe('Basic test', function () {
128
132
  }
129
133
  })
130
134
  })
131
-
135
+
132
136
  test(`${name} | withBuild`, () => {
133
137
  const r = framework.withBuild(() => {
134
138
  const s = framework.signal(2)
135
139
  const c = framework.computed(() => s.read() * 2)
136
-
140
+
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
+
145
147
  test(`${name} | effect`, () => {
146
- const spy = (_v) => {}
147
- const spyMock = mock(spy)
148
-
148
+ const spy = _v => {}
149
+ const spyMock = mock(spy)
150
+
149
151
  const s = framework.signal(2)
150
152
  let c: any
151
-
153
+
152
154
  framework.withBuild(() => {
153
- c = framework.computed(() => s.read() * 2)
154
-
155
- framework.effect(() => {
156
- spyMock(c.read())
157
- })
155
+ c = framework.computed(() => s.read() * 2)
156
+
157
+ framework.effect(() => {
158
+ spyMock(c.read())
159
+ })
158
160
  })
159
161
  expect(spyMock.mock.calls.length).toBe(1)
160
-
162
+
161
163
  framework.withBatch(() => {
162
- s.write(3)
164
+ s.write(3)
163
165
  })
164
166
  expect(s.read()).toEqual(3)
165
167
  expect(c.read()).toEqual(6)
@@ -174,12 +176,14 @@ describe('Kairo tests', function () {
174
176
  const head = framework.signal(0)
175
177
  const computed1 = framework.computed(() => head.read())
176
178
  const computed2 = framework.computed(() => (computed1.read(), 0))
177
- const computed3 = framework.computed(() => (busy(), computed2.read()! + 1)); // heavy computation
179
+ const computed3 = framework.computed(
180
+ () => (busy(), computed2.read()! + 1),
181
+ ) // heavy computation
178
182
  const computed4 = framework.computed(() => computed3.read()! + 2)
179
183
  const computed5 = framework.computed(() => computed4.read()! + 3)
180
184
  framework.effect(() => {
181
185
  computed5.read()
182
- busy(); // heavy side effect
186
+ busy() // heavy side effect
183
187
  })
184
188
 
185
189
  return () => {
@@ -201,10 +205,10 @@ describe('Kairo tests', function () {
201
205
  let last = head as { read: () => number }
202
206
  const callCounter = new Counter()
203
207
  for (let i = 0; i < 50; i++) {
204
- let current = framework.computed(() => {
208
+ const current = framework.computed(() => {
205
209
  return head.read()! + i
206
210
  })
207
- let current2 = framework.computed(() => {
211
+ const current2 = framework.computed(() => {
208
212
  return current.read()! + 1
209
213
  })
210
214
  framework.effect(() => {
@@ -231,16 +235,16 @@ describe('Kairo tests', function () {
231
235
  })
232
236
 
233
237
  test(`${name} | deep propagation`, () => {
234
- let len = 50
238
+ const len = 50
235
239
  const head = framework.signal(0)
236
240
  let current = head as { read: () => number }
237
241
  for (let i = 0; i < len; i++) {
238
- let c = current
242
+ const c = current
239
243
  current = framework.computed(() => {
240
244
  return c.read() + 1
241
245
  })
242
246
  }
243
- let callCounter = new Counter()
247
+ const callCounter = new Counter()
244
248
  framework.effect(() => {
245
249
  current.read()
246
250
  callCounter.count++
@@ -264,18 +268,16 @@ describe('Kairo tests', function () {
264
268
  })
265
269
 
266
270
  test(`${name} | diamond`, function () {
267
- let width = 5
271
+ const width = 5
268
272
  const head = framework.signal(0)
269
- let current: { read(): number }[] = []
273
+ const current: { read(): number }[] = []
270
274
  for (let i = 0; i < width; i++) {
271
- current.push(
272
- framework.computed(() => head.read() + 1)
273
- )
275
+ current.push(framework.computed(() => head.read() + 1))
274
276
  }
275
- let sum = framework.computed(() => {
277
+ const sum = framework.computed(() => {
276
278
  return current.map(x => x.read()).reduce((a, b) => a + b, 0)
277
279
  })
278
- let callCounter = new Counter()
280
+ const callCounter = new Counter()
279
281
  framework.effect(() => {
280
282
  sum.read()
281
283
  callCounter.count++
@@ -299,15 +301,15 @@ describe('Kairo tests', function () {
299
301
  })
300
302
 
301
303
  test(`${name} | mux`, function () {
302
- let heads = new Array(100).fill(null).map((_) => framework.signal(0))
304
+ const heads = new Array(100).fill(null).map(_ => framework.signal(0))
303
305
  const mux = framework.computed(() => {
304
- return Object.fromEntries(heads.map((h) => h.read()).entries())
306
+ return Object.fromEntries(heads.map(h => h.read()).entries())
305
307
  })
306
308
  const splited = heads
307
309
  .map((_, index) => framework.computed(() => mux.read()[index]))
308
- .map((x) => framework.computed(() => x.read() + 1))
310
+ .map(x => framework.computed(() => x.read() + 1))
309
311
 
310
- splited.forEach((x) => {
312
+ splited.forEach(x => {
311
313
  framework.effect(() => x.read())
312
314
  })
313
315
 
@@ -329,15 +331,15 @@ describe('Kairo tests', function () {
329
331
 
330
332
  test(`${name} | repeated observers`, function () {
331
333
  const size = 30
332
- let head = framework.signal(0)
333
- let current = framework.computed(() => {
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
- let callCounter = new Counter()
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
- let width = 10
365
- let head = framework.signal(0)
366
+ const width = 10
367
+ const head = framework.signal(0)
366
368
  let current = head as { read: () => number }
367
- let list: { read: () => number }[] = []
369
+ const list: { read: () => number }[] = []
368
370
  for (let i = 0; i < width; i++) {
369
- let c = current
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
- let sum = framework.computed(() => {
376
- return list.map((x) => x.read()).reduce((a, b) => a + b, 0)
377
+ const sum = framework.computed(() => {
378
+ return list.map(x => x.read()).reduce((a, b) => a + b, 0)
377
379
  })
378
- let callCounter = new Counter()
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: 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
- let head = framework.signal(0)
411
+ const head = framework.signal(0)
410
412
  const double = framework.computed(() => head.read() * 2)
411
413
  const inverse = framework.computed(() => -head.read())
412
- let current = framework.computed(() => {
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
- let callCounter = new Counter()
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
- /* describe('$mol_wire tests', function () {
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, log: string) => {
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
- let res: (() => any)[] = []
456
- const iter = framework.withBuild(() => {
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((i) => ({ x: i + (A.read() % 2) - (B.read() % 2) }))
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, "E")
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
- () => C.read() + (C.read() || E.read() % 2) + D.read()[4].x + F.read()
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(), "H")))
471
- framework.effect(() => res.push(G.read())); // I
472
- framework.effect(() => res.push(hard(F.read(), "J")))
473
- framework.effect(() => res[0] = hard(G.read(), "H"))
474
- framework.effect(() => res[1] = G.read()); // I
475
- framework.effect(() => res[2] = hard(F.read(), "J"))
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
- 1000: [
500
- [-3, -6, -2, 2],
501
- [-2, -4, 2, 3],
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
- 5000: [
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
- prop1: framework.computed(() => m.prop2.read()),
527
- prop2: framework.computed(() => m.prop1.read() - m.prop3.read()),
528
- prop3: framework.computed(() => m.prop2.read() + m.prop4.read()),
529
- prop4: framework.computed(() => m.prop3.read()),
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
+ })