@zeix/cause-effect 0.14.0 → 0.14.2

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,12 +1,11 @@
1
- import { describe, test, expect } from 'bun:test'
1
+ import { describe, expect, test } from 'bun:test'
2
2
  import {
3
- state,
4
- memo,
5
- task,
6
- UNSET,
3
+ computed,
4
+ effect,
7
5
  isComputed,
8
6
  isState,
9
- effect,
7
+ state,
8
+ UNSET,
10
9
  } from '../index.ts'
11
10
 
12
11
  /* === Utility Functions === */
@@ -16,40 +15,40 @@ const increment = (n: number) => (Number.isFinite(n) ? n + 1 : UNSET)
16
15
 
17
16
  /* === Tests === */
18
17
 
19
- describe('Computed', function () {
18
+ describe('Computed', () => {
20
19
  test('should identify computed signals with isComputed()', () => {
21
20
  const count = state(42)
22
- const doubled = memo(() => count.get() * 2)
21
+ const doubled = computed(() => count.get() * 2)
23
22
  expect(isComputed(doubled)).toBe(true)
24
23
  expect(isState(doubled)).toBe(false)
25
24
  })
26
25
 
27
- test('should compute a function', function () {
28
- const derived = memo(() => 1 + 2)
26
+ test('should compute a function', () => {
27
+ const derived = computed(() => 1 + 2)
29
28
  expect(derived.get()).toBe(3)
30
29
  })
31
30
 
32
- test('should compute function dependent on a signal', function () {
31
+ test('should compute function dependent on a signal', () => {
33
32
  const cause = state(42)
34
- const derived = memo(() => cause.get() + 1)
33
+ const derived = computed(() => cause.get() + 1)
35
34
  expect(derived.get()).toBe(43)
36
35
  })
37
36
 
38
- test('should compute function dependent on an updated signal', function () {
37
+ test('should compute function dependent on an updated signal', () => {
39
38
  const cause = state(42)
40
- const derived = memo(() => cause.get() + 1)
39
+ const derived = computed(() => cause.get() + 1)
41
40
  cause.set(24)
42
41
  expect(derived.get()).toBe(25)
43
42
  })
44
43
 
45
- test('should compute function dependent on an async signal', async function () {
44
+ test('should compute function dependent on an async signal', async () => {
46
45
  const status = state('pending')
47
- const promised = task(async () => {
46
+ const promised = computed(async () => {
48
47
  await wait(100)
49
48
  status.set('success')
50
49
  return 42
51
50
  })
52
- const derived = memo(() => increment(promised.get()))
51
+ const derived = computed(() => increment(promised.get()))
53
52
  expect(derived.get()).toBe(UNSET)
54
53
  expect(status.get()).toBe('pending')
55
54
  await wait(110)
@@ -57,16 +56,16 @@ describe('Computed', function () {
57
56
  expect(status.get()).toBe('success')
58
57
  })
59
58
 
60
- test('should handle errors from an async signal gracefully', async function () {
59
+ test('should handle errors from an async signal gracefully', async () => {
61
60
  const status = state('pending')
62
61
  const error = state('')
63
- const promised = task(async () => {
62
+ const promised = computed(async () => {
64
63
  await wait(100)
65
64
  status.set('error')
66
65
  error.set('error occurred')
67
66
  return 0
68
67
  })
69
- const derived = memo(() => increment(promised.get()))
68
+ const derived = computed(() => increment(promised.get()))
70
69
  expect(derived.get()).toBe(UNSET)
71
70
  expect(status.get()).toBe('pending')
72
71
  await wait(110)
@@ -74,16 +73,16 @@ describe('Computed', function () {
74
73
  expect(status.get()).toBe('error')
75
74
  })
76
75
 
77
- test('should compute task signals in parallel without waterfalls', async function () {
78
- const a = task(async () => {
76
+ test('should compute task signals in parallel without waterfalls', async () => {
77
+ const a = computed(async () => {
79
78
  await wait(100)
80
79
  return 10
81
80
  })
82
- const b = task(async () => {
81
+ const b = computed(async () => {
83
82
  await wait(100)
84
83
  return 20
85
84
  })
86
- const c = memo(() => {
85
+ const c = computed(() => {
87
86
  const aValue = a.get()
88
87
  const bValue = b.get()
89
88
  return aValue === UNSET || bValue === UNSET
@@ -95,31 +94,31 @@ describe('Computed', function () {
95
94
  expect(c.get()).toBe(30)
96
95
  })
97
96
 
98
- test('should compute function dependent on a chain of computed states dependent on a signal', function () {
97
+ test('should compute function dependent on a chain of computed states dependent on a signal', () => {
99
98
  const x = state(42)
100
- const a = memo(() => x.get() + 1)
101
- const b = memo(() => a.get() * 2)
102
- const c = memo(() => b.get() + 1)
99
+ const a = computed(() => x.get() + 1)
100
+ const b = computed(() => a.get() * 2)
101
+ const c = computed(() => b.get() + 1)
103
102
  expect(c.get()).toBe(87)
104
103
  })
105
104
 
106
- test('should compute function dependent on a chain of computed states dependent on an updated signal', function () {
105
+ test('should compute function dependent on a chain of computed states dependent on an updated signal', () => {
107
106
  const x = state(42)
108
- const a = memo(() => x.get() + 1)
109
- const b = memo(() => a.get() * 2)
110
- const c = memo(() => b.get() + 1)
107
+ const a = computed(() => x.get() + 1)
108
+ const b = computed(() => a.get() * 2)
109
+ const c = computed(() => b.get() + 1)
111
110
  x.set(24)
112
111
  expect(c.get()).toBe(51)
113
112
  })
114
113
 
115
- test('should drop X->B->X updates', function () {
114
+ test('should drop X->B->X updates', () => {
116
115
  let count = 0
117
116
  const x = state(2)
118
- const a = memo(() => x.get() - 1)
119
- const b = memo(() => x.get() + a.get())
120
- const c = memo(() => {
117
+ const a = computed(() => x.get() - 1)
118
+ const b = computed(() => x.get() + a.get())
119
+ const c = computed(() => {
121
120
  count++
122
- return 'c: ' + b.get()
121
+ return `c: ${b.get()}`
123
122
  })
124
123
  expect(c.get()).toBe('c: 3')
125
124
  expect(count).toBe(1)
@@ -128,12 +127,12 @@ describe('Computed', function () {
128
127
  expect(count).toBe(2)
129
128
  })
130
129
 
131
- test('should only update every signal once (diamond graph)', function () {
130
+ test('should only update every signal once (diamond graph)', () => {
132
131
  let count = 0
133
132
  const x = state('a')
134
- const a = memo(() => x.get())
135
- const b = memo(() => x.get())
136
- const c = memo(() => {
133
+ const a = computed(() => x.get())
134
+ const b = computed(() => x.get())
135
+ const c = computed(() => {
137
136
  count++
138
137
  return a.get() + ' ' + b.get()
139
138
  })
@@ -145,13 +144,13 @@ describe('Computed', function () {
145
144
  expect(count).toBe(2)
146
145
  })
147
146
 
148
- test('should only update every signal once (diamond graph + tail)', function () {
147
+ test('should only update every signal once (diamond graph + tail)', () => {
149
148
  let count = 0
150
149
  const x = state('a')
151
- const a = memo(() => x.get())
152
- const b = memo(() => x.get())
153
- const c = memo(() => a.get() + ' ' + b.get())
154
- const d = memo(() => {
150
+ const a = computed(() => x.get())
151
+ const b = computed(() => x.get())
152
+ const c = computed(() => `${a.get()} ${b.get()}`)
153
+ const d = computed(() => {
155
154
  count++
156
155
  return c.get()
157
156
  })
@@ -162,11 +161,11 @@ describe('Computed', function () {
162
161
  expect(count).toBe(2)
163
162
  })
164
163
 
165
- test('should update multiple times after multiple state changes', function () {
164
+ test('should update multiple times after multiple state changes', () => {
166
165
  const a = state(3)
167
166
  const b = state(4)
168
167
  let count = 0
169
- const sum = memo(() => {
168
+ const sum = computed(() => {
170
169
  count++
171
170
  return a.get() + b.get()
172
171
  })
@@ -186,14 +185,14 @@ describe('Computed', function () {
186
185
  * one-time performance cost that allows for efficient memoization and
187
186
  * error handling in most cases.
188
187
  */
189
- test('should bail out if result is the same', function () {
188
+ test('should bail out if result is the same', () => {
190
189
  let count = 0
191
190
  const x = state('a')
192
- const a = memo(() => {
191
+ const a = computed(() => {
193
192
  x.get()
194
193
  return 'foo'
195
194
  })
196
- const b = memo(() => {
195
+ const b = computed(() => {
197
196
  count++
198
197
  return a.get()
199
198
  })
@@ -206,12 +205,12 @@ describe('Computed', function () {
206
205
  expect(count).toBe(2)
207
206
  })
208
207
 
209
- test('should block if result remains unchanged', function () {
208
+ test('should block if result remains unchanged', () => {
210
209
  let count = 0
211
210
  const x = state(42)
212
- const a = memo(() => x.get() % 2)
213
- const b = memo(() => (a.get() ? 'odd' : 'even'))
214
- const c = memo(() => {
211
+ const a = computed(() => x.get() % 2)
212
+ const b = computed(() => (a.get() ? 'odd' : 'even'))
213
+ const c = computed(() => {
215
214
  count++
216
215
  return `c: ${b.get()}`
217
216
  })
@@ -224,27 +223,27 @@ describe('Computed', function () {
224
223
  expect(count).toBe(2)
225
224
  })
226
225
 
227
- test('should detect and throw error for circular dependencies', function () {
226
+ test('should detect and throw error for circular dependencies', () => {
228
227
  const a = state(1)
229
- const b = memo(() => c.get() + 1)
230
- const c = memo(() => b.get() + a.get())
228
+ const b = computed(() => c.get() + 1)
229
+ const c = computed(() => b.get() + a.get())
231
230
  expect(() => {
232
231
  b.get() // This should trigger the circular dependency
233
- }).toThrow('Circular dependency in memo detected')
232
+ }).toThrow('Circular dependency in computed detected')
234
233
  expect(a.get()).toBe(1)
235
234
  })
236
235
 
237
- test('should propagate error if an error occurred', function () {
236
+ test('should propagate error if an error occurred', () => {
238
237
  let okCount = 0
239
238
  let errCount = 0
240
239
  const x = state(0)
241
- const a = memo(() => {
240
+ const a = computed(() => {
242
241
  if (x.get() === 1) throw new Error('Calculation error')
243
242
  return 1
244
243
  })
245
244
 
246
245
  // Replace matcher with try/catch in a computed
247
- const b = memo(() => {
246
+ const b = computed(() => {
248
247
  try {
249
248
  a.get() // just check if it works
250
249
  return `c: success`
@@ -253,7 +252,7 @@ describe('Computed', function () {
253
252
  return `c: recovered`
254
253
  }
255
254
  })
256
- const c = memo(() => {
255
+ const c = computed(() => {
257
256
  okCount++
258
257
  return b.get()
259
258
  })
@@ -274,9 +273,9 @@ describe('Computed', function () {
274
273
  }
275
274
  })
276
275
 
277
- test('should create an effect that reacts on async computed changes', async function () {
276
+ test('should create an effect that reacts on async computed changes', async () => {
278
277
  const cause = state(42)
279
- const derived = task(async () => {
278
+ const derived = computed(async () => {
280
279
  await wait(100)
281
280
  return cause.get() + 1
282
281
  })
@@ -285,11 +284,11 @@ describe('Computed', function () {
285
284
  let result: number = 0
286
285
  effect({
287
286
  signals: [derived],
288
- ok: v => {
287
+ ok: (v): undefined => {
289
288
  result = v
290
289
  okCount++
291
290
  },
292
- nil: () => {
291
+ nil: (): undefined => {
293
292
  nilCount++
294
293
  },
295
294
  })
@@ -304,13 +303,13 @@ describe('Computed', function () {
304
303
  expect(result).toBe(44)
305
304
  })
306
305
 
307
- test('should handle complex computed signal with error and async dependencies', async function () {
306
+ test('should handle complex computed signal with error and async dependencies', async () => {
308
307
  const toggleState = state(true)
309
- const errorProne = memo(() => {
308
+ const errorProne = computed(() => {
310
309
  if (toggleState.get()) throw new Error('Intentional error')
311
310
  return 42
312
311
  })
313
- const asyncValue = task(async () => {
312
+ const asyncValue = computed(async () => {
314
313
  await wait(50)
315
314
  return 10
316
315
  })
@@ -319,8 +318,7 @@ describe('Computed', function () {
319
318
  let errCount = 0
320
319
  // let _result: number = 0
321
320
 
322
- // Replace matcher with try/catch in a computed
323
- const complexComputed = memo(() => {
321
+ const complexComputed = computed(() => {
324
322
  try {
325
323
  const x = errorProne.get()
326
324
  const y = asyncValue.get()
@@ -352,10 +350,10 @@ describe('Computed', function () {
352
350
  expect(errCount).toBeGreaterThan(0)
353
351
  })
354
352
 
355
- test('should handle signal changes during async computation', async function () {
353
+ test('should handle signal changes during async computation', async () => {
356
354
  const source = state(1)
357
355
  let computationCount = 0
358
- const derived = task(async abort => {
356
+ const derived = computed(async abort => {
359
357
  computationCount++
360
358
  expect(abort?.aborted).toBe(false)
361
359
  await wait(100)
@@ -373,10 +371,10 @@ describe('Computed', function () {
373
371
  expect(computationCount).toBe(1)
374
372
  })
375
373
 
376
- test('should handle multiple rapid changes during async computation', async function () {
374
+ test('should handle multiple rapid changes during async computation', async () => {
377
375
  const source = state(1)
378
376
  let computationCount = 0
379
- const derived = task(async abort => {
377
+ const derived = computed(async abort => {
380
378
  computationCount++
381
379
  expect(abort?.aborted).toBe(false)
382
380
  await wait(100)
@@ -398,9 +396,9 @@ describe('Computed', function () {
398
396
  expect(computationCount).toBe(1)
399
397
  })
400
398
 
401
- test('should handle errors in aborted computations', async function () {
399
+ test('should handle errors in aborted computations', async () => {
402
400
  const source = state(1)
403
- const derived = task(async () => {
401
+ const derived = computed(async () => {
404
402
  await wait(100)
405
403
  const value = source.get()
406
404
  if (value === 2) throw new Error('Intentional error')
@@ -1,5 +1,5 @@
1
- import { describe, test, expect, mock } from 'bun:test'
2
- import { state, task, effect, UNSET, memo } from '../'
1
+ import { describe, expect, mock, test } from 'bun:test'
2
+ import { computed, effect, state, UNSET } from '../'
3
3
 
4
4
  /* === Utility Functions === */
5
5
 
@@ -7,11 +7,11 @@ const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
7
7
 
8
8
  /* === Tests === */
9
9
 
10
- describe('Effect', function () {
11
- test('should be triggered after a state change', function () {
10
+ describe('Effect', () => {
11
+ test('should be triggered after a state change', () => {
12
12
  const cause = state('foo')
13
13
  let count = 0
14
- effect(() => {
14
+ effect((): undefined => {
15
15
  cause.get()
16
16
  count++
17
17
  })
@@ -20,12 +20,12 @@ describe('Effect', function () {
20
20
  expect(count).toBe(2)
21
21
  })
22
22
 
23
- test('should be triggered after computed async signals resolve without waterfalls', async function () {
24
- const a = task(async () => {
23
+ test('should be triggered after computed async signals resolve without waterfalls', async () => {
24
+ const a = computed(async () => {
25
25
  await wait(100)
26
26
  return 10
27
27
  })
28
- const b = task(async () => {
28
+ const b = computed(async () => {
29
29
  await wait(100)
30
30
  return 20
31
31
  })
@@ -33,7 +33,7 @@ describe('Effect', function () {
33
33
  let count = 0
34
34
  effect({
35
35
  signals: [a, b],
36
- ok: (aValue, bValue) => {
36
+ ok: (aValue, bValue): undefined => {
37
37
  result = aValue + bValue
38
38
  count++
39
39
  },
@@ -45,11 +45,11 @@ describe('Effect', function () {
45
45
  expect(count).toBe(1)
46
46
  })
47
47
 
48
- test('should be triggered repeatedly after repeated state change', async function () {
48
+ test('should be triggered repeatedly after repeated state change', async () => {
49
49
  const cause = state(0)
50
50
  let result = 0
51
51
  let count = 0
52
- effect(() => {
52
+ effect((): undefined => {
53
53
  result = cause.get()
54
54
  count++
55
55
  })
@@ -60,9 +60,9 @@ describe('Effect', function () {
60
60
  }
61
61
  })
62
62
 
63
- test('should handle errors in effects', function () {
63
+ test('should handle errors in effects', () => {
64
64
  const a = state(1)
65
- const b = memo(() => {
65
+ const b = computed(() => {
66
66
  const v = a.get()
67
67
  if (v > 5) throw new Error('Value too high')
68
68
  return v * 2
@@ -71,11 +71,11 @@ describe('Effect', function () {
71
71
  let errorCallCount = 0
72
72
  effect({
73
73
  signals: [b],
74
- ok: () => {
74
+ ok: (): undefined => {
75
75
  // console.log('Normal effect:', value)
76
76
  normalCallCount++
77
77
  },
78
- err: error => {
78
+ err: (error: Error): undefined => {
79
79
  // console.log('Error effect:', error)
80
80
  errorCallCount++
81
81
  expect(error.message).toBe('Value too high')
@@ -98,8 +98,8 @@ describe('Effect', function () {
98
98
  expect(errorCallCount).toBe(1)
99
99
  })
100
100
 
101
- test('should handle UNSET values in effects', async function () {
102
- const a = task(async () => {
101
+ test('should handle UNSET values in effects', async () => {
102
+ const a = computed(async () => {
103
103
  await wait(100)
104
104
  return 42
105
105
  })
@@ -107,11 +107,11 @@ describe('Effect', function () {
107
107
  let nilCount = 0
108
108
  effect({
109
109
  signals: [a],
110
- ok: aValue => {
110
+ ok: (aValue: number): undefined => {
111
111
  normalCallCount++
112
112
  expect(aValue).toBe(42)
113
113
  },
114
- nil: () => {
114
+ nil: (): undefined => {
115
115
  nilCount++
116
116
  },
117
117
  })
@@ -133,14 +133,14 @@ describe('Effect', function () {
133
133
 
134
134
  try {
135
135
  const a = state(1)
136
- const b = memo(() => {
136
+ const b = computed(() => {
137
137
  const v = a.get()
138
138
  if (v > 5) throw new Error('Value too high')
139
139
  return v * 2
140
140
  })
141
141
 
142
142
  // Create an effect without explicit error handling
143
- effect(() => {
143
+ effect((): undefined => {
144
144
  b.get()
145
145
  })
146
146
 
@@ -164,7 +164,7 @@ describe('Effect', function () {
164
164
  const count = state(42)
165
165
  let received = 0
166
166
 
167
- const cleanup = effect(() => {
167
+ const cleanup = effect((): undefined => {
168
168
  received = count.get()
169
169
  })
170
170
 
@@ -183,12 +183,12 @@ describe('Effect', function () {
183
183
 
184
184
  effect({
185
185
  signals: [count],
186
- ok: () => {
186
+ ok: (): undefined => {
187
187
  okCount++
188
188
  // This effect updates the signal it depends on, creating a circular dependency
189
189
  count.update(v => ++v)
190
190
  },
191
- err: e => {
191
+ err: (e): undefined => {
192
192
  errCount++
193
193
  expect(e).toBeInstanceOf(Error)
194
194
  expect(e.message).toBe('Circular dependency in effect detected')