@zeix/cause-effect 0.13.1 → 0.13.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.
@@ -21,7 +21,7 @@ const UNSET: any = Symbol()
21
21
  * @param {unknown} value - value to check
22
22
  * @returns {boolean} - true if value is a Signal, false otherwise
23
23
  */
24
- const isSignal = /*#__PURE__*/ <T extends {}>(value: any): value is Signal<T> =>
24
+ const isSignal = /*#__PURE__*/ <T extends {}>(value: unknown): value is Signal<T> =>
25
25
  isState(value) || isComputed(value)
26
26
 
27
27
  /**
@@ -7,10 +7,10 @@ import { type TapMatcher, type EffectMatcher, effect } from './effect'
7
7
  /* === Types === */
8
8
 
9
9
  export type State<T extends {}> = {
10
- [Symbol.toStringTag]: 'State'
11
- get(): T
12
- set(v: T): void
13
- update(fn: (v: T) => T): void
10
+ [Symbol.toStringTag]: 'State'
11
+ get(): T
12
+ set(v: T): void
13
+ update(fn: (v: T) => T): void
14
14
  map<U extends {}>(fn: (v: T) => U | Promise<U>): Computed<U>
15
15
  tap(matcher: TapMatcher<T> | ((v: T) => void | (() => void))): () => void
16
16
  }
@@ -23,12 +23,14 @@ const TYPE_STATE = 'State'
23
23
 
24
24
  /**
25
25
  * Create a new state signal
26
- *
26
+ *
27
27
  * @since 0.9.0
28
28
  * @param {T} initialValue - initial value of the state
29
29
  * @returns {State<T>} - new state signal
30
30
  */
31
- export const state = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> => {
31
+ export const state = /*#__PURE__*/ <T extends {}>(
32
+ initialValue: T,
33
+ ): State<T> => {
32
34
  const watchers: Set<Watcher> = new Set()
33
35
  let value: T = initialValue
34
36
 
@@ -37,71 +39,69 @@ export const state = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> =>
37
39
 
38
40
  /**
39
41
  * Get the current value of the state
40
- *
42
+ *
41
43
  * @since 0.9.0
42
44
  * @returns {T} - current value of the state
43
45
  */
44
- get: (): T => {
46
+ get: (): T => {
45
47
  subscribe(watchers)
46
- return value
48
+ return value
47
49
  },
48
50
 
49
51
  /**
50
52
  * Set a new value of the state
51
- *
53
+ *
52
54
  * @since 0.9.0
53
55
  * @param {T} v
54
56
  * @returns {void}
55
57
  */
56
- set: (v: T): void => {
57
- if (Object.is(value, v)) return
58
- value = v
59
- notify(watchers)
58
+ set: (v: T): void => {
59
+ if (Object.is(value, v)) return
60
+ value = v
61
+ notify(watchers)
60
62
 
61
- // Setting to UNSET clears the watchers so the signal can be garbage collected
62
- if (UNSET === value) watchers.clear()
63
- },
63
+ // Setting to UNSET clears the watchers so the signal can be garbage collected
64
+ if (UNSET === value) watchers.clear()
65
+ },
64
66
 
65
67
  /**
66
68
  * Update the state with a new value using a function
67
- *
69
+ *
68
70
  * @since 0.10.0
69
71
  * @param {(v: T) => T} fn - function to update the state
70
72
  * @returns {void} - updates the state with the result of the function
71
73
  */
72
- update: (fn: (v: T) => T): void => {
73
- s.set(fn(value))
74
- },
74
+ update: (fn: (v: T) => T): void => {
75
+ s.set(fn(value))
76
+ },
75
77
 
76
78
  /**
77
79
  * Create a computed signal from the current state signal
78
- *
80
+ *
79
81
  * @since 0.9.0
80
82
  * @param {(v: T) => U | Promise<U>} fn - computed callback
81
83
  * @returns {Computed<U>} - computed signal
82
84
  */
83
- map: <U extends {}>(
84
- fn: (v: T) => U | Promise<U>
85
- ): Computed<U> =>
85
+ map: <U extends {}>(fn: (v: T) => U | Promise<U>): Computed<U> =>
86
86
  computed({
87
87
  signals: [s],
88
- ok: fn
88
+ ok: fn,
89
89
  }),
90
90
 
91
91
  /**
92
92
  * Case matching for the state signal with effect callbacks
93
- *
93
+ *
94
94
  * @since 0.13.0
95
95
  * @param {TapMatcher<T> | ((v: T) => void | (() => void))} matcher - tap matcher or effect callback
96
96
  * @returns {() => void} - cleanup function for the effect
97
97
  */
98
98
  tap: (
99
- matcher: TapMatcher<T> | ((v: T) => void | (() => void))
100
- ): () => void =>
99
+ matcher: TapMatcher<T> | ((v: T) => void | (() => void)),
100
+ ): (() => void) =>
101
101
  effect({
102
102
  signals: [s],
103
- ...(isFunction(matcher) ? { ok: matcher } : matcher)
104
- } as EffectMatcher<[State<T>]>)
103
+ ...(isFunction(matcher) ? { ok: matcher } : matcher),
104
+ } as EffectMatcher<[State<T>]>),
105
105
  }
106
106
 
107
107
  return s
@@ -109,10 +109,11 @@ export const state = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> =>
109
109
 
110
110
  /**
111
111
  * Check if the provided value is a State instance
112
- *
112
+ *
113
113
  * @since 0.9.0
114
114
  * @param {unknown} value - value to check
115
115
  * @returns {boolean} - true if the value is a State instance, false otherwise
116
116
  */
117
- export const isState = /*#__PURE__*/ <T extends {}>(value: unknown): value is State<T> =>
118
- isObjectOfType(value, TYPE_STATE)
117
+ export const isState = /*#__PURE__*/ <T extends {}>(
118
+ value: unknown,
119
+ ): value is State<T> => isObjectOfType(value, TYPE_STATE)
@@ -8,4 +8,4 @@ declare const toError: (reason: unknown) => Error;
8
8
  declare class CircularDependencyError extends Error {
9
9
  constructor(where: string);
10
10
  }
11
- export { isFunction, isAsyncFunction, isObjectOfType, isError, isAbortError, isPromise, toError, CircularDependencyError };
11
+ export { isFunction, isAsyncFunction, isObjectOfType, isError, isAbortError, isPromise, toError, CircularDependencyError, };
@@ -1,13 +1,18 @@
1
1
  /* === Utility Functions === */
2
2
 
3
- const isFunction = /*#__PURE__*/ <T>(value: unknown): value is (...args: unknown[]) => T =>
4
- typeof value === 'function'
3
+ const isFunction = /*#__PURE__*/ <T>(
4
+ value: unknown,
5
+ ): value is (...args: unknown[]) => T => typeof value === 'function'
5
6
 
6
- const isAsyncFunction = /*#__PURE__*/ <T>(value: unknown): value is (...args: unknown[]) => Promise<T> =>
7
+ const isAsyncFunction = /*#__PURE__*/ <T>(
8
+ value: unknown,
9
+ ): value is (...args: unknown[]) => Promise<T> =>
7
10
  isFunction(value) && value.constructor.name === 'AsyncFunction'
8
11
 
9
- const isObjectOfType = /*#__PURE__*/ <T>(value: unknown, type: string): value is T =>
10
- Object.prototype.toString.call(value) === `[object ${type}]`
12
+ const isObjectOfType = /*#__PURE__*/ <T>(
13
+ value: unknown,
14
+ type: string,
15
+ ): value is T => Object.prototype.toString.call(value) === `[object ${type}]`
11
16
 
12
17
  const isError = /*#__PURE__*/ (value: unknown): value is Error =>
13
18
  value instanceof Error
@@ -20,13 +25,18 @@ const toError = (reason: unknown): Error =>
20
25
 
21
26
  class CircularDependencyError extends Error {
22
27
  constructor(where: string) {
23
- super(`Circular dependency in ${where} detected`)
28
+ super(`Circular dependency in ${where} detected`)
24
29
  return this
25
- }
30
+ }
26
31
  }
27
32
 
28
33
  export {
29
- isFunction, isAsyncFunction,
30
- isObjectOfType, isError, isAbortError, isPromise, toError,
31
- CircularDependencyError
32
- }
34
+ isFunction,
35
+ isAsyncFunction,
36
+ isObjectOfType,
37
+ isError,
38
+ isAbortError,
39
+ isPromise,
40
+ toError,
41
+ CircularDependencyError,
42
+ }
@@ -1,5 +1,5 @@
1
1
  import { describe, test, expect } from 'bun:test'
2
- import { state, computed, effect, batch } from '../'
2
+ import { state, computed, batch } from '../'
3
3
 
4
4
  /* === Utility Functions === */
5
5
 
@@ -8,8 +8,7 @@ const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
8
8
  /* === Tests === */
9
9
 
10
10
  describe('Batch', function () {
11
-
12
- test('should be triggered only once after repeated state change', function() {
11
+ test('should be triggered only once after repeated state change', function () {
13
12
  const cause = state(0)
14
13
  let result = 0
15
14
  let count = 0
@@ -23,10 +22,10 @@ describe('Batch', function () {
23
22
  }
24
23
  })
25
24
  expect(result).toBe(10)
26
- expect(count).toBe(2); // + 1 for effect initialization
25
+ expect(count).toBe(2) // + 1 for effect initialization
27
26
  })
28
27
 
29
- test('should be triggered only once when multiple signals are set', function() {
28
+ test('should be triggered only once when multiple signals are set', function () {
30
29
  const a = state(3)
31
30
  const b = state(4)
32
31
  const c = state(5)
@@ -46,25 +45,26 @@ describe('Batch', function () {
46
45
  c.set(10)
47
46
  })
48
47
  expect(result).toBe(24)
49
- expect(count).toBe(2); // + 1 for effect initialization
48
+ expect(count).toBe(2) // + 1 for effect initialization
50
49
  })
51
50
 
52
- test('should prove example from README works', function() {
53
-
51
+ test('should prove example from README works', function () {
54
52
  // State: define an array of Signal<number>
55
53
  const signals = [state(2), state(3), state(5)]
56
54
 
57
55
  // Computed: derive a calculation ...
58
- const sum = computed(() => signals.reduce((total, v) => total + v.get(), 0))
59
- .map(v => { // ... perform validation and handle errors
60
- if (!Number.isFinite(v)) throw new Error('Invalid value')
61
- return v
62
- })
56
+ const sum = computed(() =>
57
+ signals.reduce((total, v) => total + v.get(), 0),
58
+ ).map(v => {
59
+ // ... perform validation and handle errors
60
+ if (!Number.isFinite(v)) throw new Error('Invalid value')
61
+ return v
62
+ })
63
63
 
64
64
  let result = 0
65
65
  let okCount = 0
66
66
  let errCount = 0
67
-
67
+
68
68
  // Effect: switch cases for the result
69
69
  sum.tap({
70
70
  ok: v => {
@@ -75,7 +75,7 @@ describe('Batch', function () {
75
75
  err: _error => {
76
76
  errCount++
77
77
  // console.error('Error:', error)
78
- }
78
+ },
79
79
  })
80
80
 
81
81
  expect(okCount).toBe(1)
@@ -92,9 +92,8 @@ describe('Batch', function () {
92
92
  // Provoke an error
93
93
  signals[0].set(NaN)
94
94
 
95
- expect(errCount).toBe(1)
96
- expect(okCount).toBe(2); // should not have changed due to error
97
- expect(result).toBe(20); // should not have changed due to error
95
+ expect(errCount).toBe(1)
96
+ expect(okCount).toBe(2) // should not have changed due to error
97
+ expect(result).toBe(20) // should not have changed due to error
98
98
  })
99
-
100
99
  })
@@ -1,6 +1,6 @@
1
1
  import { describe, test, expect, mock } from 'bun:test'
2
2
  import { state, computed, effect, batch } from '../'
3
- import { makeGraph, runGraph, Counter } from "./util/dependency-graph"
3
+ import { makeGraph, runGraph, Counter } from './util/dependency-graph'
4
4
 
5
5
  /* === Utility Functions === */
6
6
 
@@ -12,7 +12,7 @@ const busy = () => {
12
12
  }
13
13
 
14
14
  const framework = {
15
- name: "Cause & Effect",
15
+ name: 'Cause & Effect',
16
16
  signal: <T extends {}>(initialValue: T) => {
17
17
  const s = state<T>(initialValue)
18
18
  return {
@@ -45,7 +45,7 @@ function makeConfig() {
45
45
  }
46
46
 
47
47
  /* === Test functions === */
48
-
48
+
49
49
  /** some basic tests to validate the reactive framework
50
50
  * wrapper works and can run performance tests.
51
51
  */
@@ -55,24 +55,24 @@ describe('Basic test', function () {
55
55
  framework.withBuild(() => {
56
56
  const s = framework.signal(2)
57
57
  const c = framework.computed(() => s.read() * 2)
58
-
58
+
59
59
  expect(c.read()).toEqual(4)
60
60
  })
61
61
  })
62
-
62
+
63
63
  test(`${name} | simple write`, () => {
64
64
  framework.withBuild(() => {
65
65
  const s = framework.signal(2)
66
66
  const c = framework.computed(() => s.read() * 2)
67
67
  expect(s.read()).toEqual(2)
68
68
  expect(c.read()).toEqual(4)
69
-
69
+
70
70
  s.write(3)
71
71
  expect(s.read()).toEqual(3)
72
72
  expect(c.read()).toEqual(6)
73
73
  })
74
74
  })
75
-
75
+
76
76
  test(`${name} | static graph`, () => {
77
77
  const config = makeConfig()
78
78
  const counter = new Counter()
@@ -87,7 +87,7 @@ describe('Basic test', function () {
87
87
  expect(counter.count).toBeGreaterThanOrEqual(11)
88
88
  }
89
89
  })
90
-
90
+
91
91
  test(`${name} | static graph, read 2/3 of leaves`, () => {
92
92
  framework.withBuild(() => {
93
93
  const config = makeConfig()
@@ -98,7 +98,7 @@ describe('Basic test', function () {
98
98
  const graph = makeGraph(framework, config, counter)
99
99
  // @ts-expect-error
100
100
  const sum = runGraph(graph, 10, 2 / 3, framework)
101
-
101
+
102
102
  expect(sum).toEqual(71)
103
103
  if (testPullCounts) {
104
104
  expect(counter.count).toEqual(41)
@@ -107,7 +107,7 @@ describe('Basic test', function () {
107
107
  }
108
108
  })
109
109
  })
110
-
110
+
111
111
  test(`${name} | dynamic graph`, () => {
112
112
  framework.withBuild(() => {
113
113
  const config = makeConfig()
@@ -119,7 +119,7 @@ describe('Basic test', function () {
119
119
  const graph = makeGraph(framework, config, counter)
120
120
  // @ts-expect-error
121
121
  const sum = runGraph(graph, 10, 1, framework)
122
-
122
+
123
123
  expect(sum).toEqual(72)
124
124
  if (testPullCounts) {
125
125
  expect(counter.count).toEqual(22)
@@ -128,38 +128,38 @@ describe('Basic test', function () {
128
128
  }
129
129
  })
130
130
  })
131
-
131
+
132
132
  test(`${name} | withBuild`, () => {
133
133
  const r = framework.withBuild(() => {
134
134
  const s = framework.signal(2)
135
135
  const c = framework.computed(() => s.read() * 2)
136
-
136
+
137
137
  expect(c.read()).toEqual(4)
138
138
  return c.read()
139
139
  })
140
-
140
+
141
141
  // @ts-expect-error
142
142
  expect(r).toEqual(4)
143
143
  })
144
-
144
+
145
145
  test(`${name} | effect`, () => {
146
- const spy = (_v) => {}
147
- const spyMock = mock(spy)
148
-
146
+ const spy = _v => {}
147
+ const spyMock = mock(spy)
148
+
149
149
  const s = framework.signal(2)
150
150
  let c: any
151
-
151
+
152
152
  framework.withBuild(() => {
153
- c = framework.computed(() => s.read() * 2)
154
-
155
- framework.effect(() => {
156
- spyMock(c.read())
157
- })
153
+ c = framework.computed(() => s.read() * 2)
154
+
155
+ framework.effect(() => {
156
+ spyMock(c.read())
157
+ })
158
158
  })
159
159
  expect(spyMock.mock.calls.length).toBe(1)
160
-
160
+
161
161
  framework.withBatch(() => {
162
- s.write(3)
162
+ s.write(3)
163
163
  })
164
164
  expect(s.read()).toEqual(3)
165
165
  expect(c.read()).toEqual(6)
@@ -174,12 +174,14 @@ describe('Kairo tests', function () {
174
174
  const head = framework.signal(0)
175
175
  const computed1 = framework.computed(() => head.read())
176
176
  const computed2 = framework.computed(() => (computed1.read(), 0))
177
- const computed3 = framework.computed(() => (busy(), computed2.read()! + 1)); // heavy computation
177
+ const computed3 = framework.computed(
178
+ () => (busy(), computed2.read()! + 1),
179
+ ) // heavy computation
178
180
  const computed4 = framework.computed(() => computed3.read()! + 2)
179
181
  const computed5 = framework.computed(() => computed4.read()! + 3)
180
182
  framework.effect(() => {
181
183
  computed5.read()
182
- busy(); // heavy side effect
184
+ busy() // heavy side effect
183
185
  })
184
186
 
185
187
  return () => {
@@ -268,9 +270,7 @@ describe('Kairo tests', function () {
268
270
  const head = framework.signal(0)
269
271
  let current: { read(): number }[] = []
270
272
  for (let i = 0; i < width; i++) {
271
- current.push(
272
- framework.computed(() => head.read() + 1)
273
- )
273
+ current.push(framework.computed(() => head.read() + 1))
274
274
  }
275
275
  let sum = framework.computed(() => {
276
276
  return current.map(x => x.read()).reduce((a, b) => a + b, 0)
@@ -299,15 +299,15 @@ describe('Kairo tests', function () {
299
299
  })
300
300
 
301
301
  test(`${name} | mux`, function () {
302
- let heads = new Array(100).fill(null).map((_) => framework.signal(0))
302
+ let heads = new Array(100).fill(null).map(_ => framework.signal(0))
303
303
  const mux = framework.computed(() => {
304
- return Object.fromEntries(heads.map((h) => h.read()).entries())
304
+ return Object.fromEntries(heads.map(h => h.read()).entries())
305
305
  })
306
306
  const splited = heads
307
307
  .map((_, index) => framework.computed(() => mux.read()[index]))
308
- .map((x) => framework.computed(() => x.read() + 1))
308
+ .map(x => framework.computed(() => x.read() + 1))
309
309
 
310
- splited.forEach((x) => {
310
+ splited.forEach(x => {
311
311
  framework.effect(() => x.read())
312
312
  })
313
313
 
@@ -373,7 +373,7 @@ describe('Kairo tests', function () {
373
373
  })
374
374
  }
375
375
  let sum = framework.computed(() => {
376
- return list.map((x) => x.read()).reduce((a, b) => a + b, 0)
376
+ return list.map(x => x.read()).reduce((a, b) => a + b, 0)
377
377
  })
378
378
  let callCounter = new Counter()
379
379
  framework.effect(() => {