@zeix/cause-effect 0.15.1 → 0.16.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.
Files changed (48) hide show
  1. package/.ai-context.md +254 -0
  2. package/.cursorrules +54 -0
  3. package/.github/copilot-instructions.md +132 -0
  4. package/CLAUDE.md +319 -0
  5. package/README.md +167 -159
  6. package/eslint.config.js +1 -1
  7. package/index.dev.js +528 -407
  8. package/index.js +1 -1
  9. package/index.ts +36 -25
  10. package/package.json +1 -1
  11. package/src/computed.ts +41 -30
  12. package/src/diff.ts +57 -44
  13. package/src/effect.ts +15 -16
  14. package/src/errors.ts +64 -0
  15. package/src/match.ts +2 -2
  16. package/src/resolve.ts +2 -2
  17. package/src/signal.ts +27 -49
  18. package/src/state.ts +27 -19
  19. package/src/store.ts +410 -209
  20. package/src/system.ts +122 -0
  21. package/src/util.ts +45 -6
  22. package/test/batch.test.ts +18 -11
  23. package/test/benchmark.test.ts +4 -4
  24. package/test/computed.test.ts +508 -72
  25. package/test/diff.test.ts +321 -4
  26. package/test/effect.test.ts +61 -61
  27. package/test/match.test.ts +38 -28
  28. package/test/resolve.test.ts +16 -16
  29. package/test/signal.test.ts +19 -147
  30. package/test/state.test.ts +212 -25
  31. package/test/store.test.ts +1370 -134
  32. package/test/util/dependency-graph.ts +1 -1
  33. package/types/index.d.ts +10 -9
  34. package/types/src/collection.d.ts +26 -0
  35. package/types/src/computed.d.ts +9 -9
  36. package/types/src/diff.d.ts +5 -3
  37. package/types/src/effect.d.ts +3 -3
  38. package/types/src/errors.d.ts +22 -0
  39. package/types/src/match.d.ts +1 -1
  40. package/types/src/resolve.d.ts +1 -1
  41. package/types/src/signal.d.ts +12 -19
  42. package/types/src/state.d.ts +5 -5
  43. package/types/src/store.d.ts +40 -36
  44. package/types/src/system.d.ts +44 -0
  45. package/types/src/util.d.ts +7 -5
  46. package/index.d.ts +0 -36
  47. package/src/scheduler.ts +0 -172
  48. package/types/test-new-effect.d.ts +0 -1
package/src/signal.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  type Computed,
3
3
  type ComputedCallback,
4
- computed,
4
+ createComputed,
5
5
  isComputed,
6
6
  isComputedCallback,
7
7
  } from './computed'
8
- import { isState, type State, state } from './state'
9
- import { isStore, type Store, store } from './store'
8
+ import { createState, isState, type State } from './state'
9
+ import { createStore, isStore, type Store } from './store'
10
10
  import { isRecord } from './util'
11
11
 
12
12
  /* === Types === */
@@ -14,7 +14,6 @@ import { isRecord } from './util'
14
14
  type Signal<T extends {}> = {
15
15
  get(): T
16
16
  }
17
- type MaybeSignal<T extends {}> = T | Signal<T> | ComputedCallback<T>
18
17
 
19
18
  type UnknownSignalRecord = Record<string, Signal<unknown & {}>>
20
19
 
@@ -22,15 +21,10 @@ type SignalValues<S extends UnknownSignalRecord> = {
22
21
  [K in keyof S]: S[K] extends Signal<infer T> ? T : never
23
22
  }
24
23
 
25
- /* === Constants === */
26
-
27
- // biome-ignore lint/suspicious/noExplicitAny: Deliberately using any to be used as a placeholder value in any signal
28
- const UNSET: any = Symbol()
29
-
30
24
  /* === Functions === */
31
25
 
32
26
  /**
33
- * Check whether a value is a Signal or not
27
+ * Check whether a value is a Signal
34
28
  *
35
29
  * @since 0.9.0
36
30
  * @param {unknown} value - value to check
@@ -40,6 +34,17 @@ const isSignal = /*#__PURE__*/ <T extends {}>(
40
34
  value: unknown,
41
35
  ): value is Signal<T> => isState(value) || isComputed(value) || isStore(value)
42
36
 
37
+ /**
38
+ * Check whether a value is a State or Store
39
+ *
40
+ * @since 0.15.2
41
+ * @param {unknown} value - value to check
42
+ * @returns {boolean} - true if value is a State or Store, false otherwise
43
+ */
44
+ const isMutableSignal = /*#__PURE__*/ <T extends {}>(
45
+ value: unknown,
46
+ ): value is State<T> | Store<T> => isState(value) || isStore(value)
47
+
43
48
  /**
44
49
  * Convert a value to a Signal if it's not already a Signal
45
50
  *
@@ -47,10 +52,6 @@ const isSignal = /*#__PURE__*/ <T extends {}>(
47
52
  * @param {T} value - value to convert
48
53
  * @returns {Signal<T>} - Signal instance
49
54
  */
50
- function toSignal<T extends {}>(value: T[]): Store<Record<string, T>>
51
- function toSignal<T extends {}>(
52
- value: (() => T) | ((abort: AbortSignal) => Promise<T>),
53
- ): Computed<T>
54
55
  function toSignal<T extends {}>(
55
56
  value: T,
56
57
  ): T extends Store<infer U>
@@ -61,50 +62,27 @@ function toSignal<T extends {}>(
61
62
  ? Computed<U>
62
63
  : T extends Signal<infer U>
63
64
  ? Signal<U>
64
- : T extends Record<string, unknown & {}>
65
- ? Store<{ [K in keyof T]: T[K] }>
66
- : State<T>
67
- function toSignal<T extends {}>(value: MaybeSignal<T>): Signal<T> {
65
+ : T extends ReadonlyArray<infer U extends {}>
66
+ ? Store<U[]>
67
+ : T extends Record<string, unknown & {}>
68
+ ? Store<{ [K in keyof T]: T[K] }>
69
+ : T extends ComputedCallback<infer U extends {}>
70
+ ? Computed<U>
71
+ : State<T>
72
+ function toSignal<T extends {}>(value: T) {
68
73
  if (isSignal<T>(value)) return value
69
- if (isComputedCallback(value)) return computed(value)
70
- if (Array.isArray(value)) return store(value as T)
71
- if (Array.isArray(value) || isRecord(value)) return store(value)
72
- return state(value)
73
- }
74
-
75
- /**
76
- * Convert a value to a mutable Signal if it's not already a Signal
77
- *
78
- * @since 0.15.0
79
- * @param {T} value - value to convert
80
- * @returns {State<T> | Store<T>} - Signal instance
81
- */
82
- function toMutableSignal<T extends {}>(value: T[]): Store<Record<string, T>>
83
- function toMutableSignal<T extends {}>(
84
- value: T,
85
- ): T extends Store<infer U>
86
- ? Store<U>
87
- : T extends State<infer U>
88
- ? State<U>
89
- : T extends Record<string, unknown & {}>
90
- ? Store<{ [K in keyof T]: T[K] }>
91
- : State<T>
92
- function toMutableSignal<T extends {}>(value: T): State<T> | Store<T> {
93
- if (isState<T>(value) || isStore<T>(value)) return value
94
- if (Array.isArray(value)) return store(value as T)
95
- if (isRecord(value)) return store(value)
96
- return state(value)
74
+ if (isComputedCallback(value)) return createComputed(value)
75
+ if (Array.isArray(value) || isRecord(value)) return createStore(value)
76
+ return createState(value)
97
77
  }
98
78
 
99
79
  /* === Exports === */
100
80
 
101
81
  export {
102
82
  type Signal,
103
- type MaybeSignal,
104
83
  type UnknownSignalRecord,
105
84
  type SignalValues,
106
- UNSET,
107
85
  isSignal,
86
+ isMutableSignal,
108
87
  toSignal,
109
- toMutableSignal,
110
88
  }
package/src/state.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import { isEqual } from './diff'
2
- import { notify, subscribe, type Watcher } from './scheduler'
3
- import { UNSET } from './signal'
4
- import { isObjectOfType } from './util'
2
+ import { InvalidCallbackError, NullishSignalValueError } from './errors'
3
+ import { notify, subscribe, type Watcher } from './system'
4
+ import { isFunction, isObjectOfType, UNSET, valueString } from './util'
5
5
 
6
6
  /* === Types === */
7
7
 
8
8
  type State<T extends {}> = {
9
- [Symbol.toStringTag]: 'State'
9
+ readonly [Symbol.toStringTag]: 'State'
10
10
  get(): T
11
- set(v: T): void
12
- update(fn: (v: T) => T): void
11
+ set(newValue: T): void
12
+ update(updater: (oldValue: T) => T): void
13
13
  }
14
14
 
15
15
  /* === Constants === */
@@ -25,18 +25,20 @@ const TYPE_STATE = 'State'
25
25
  * @param {T} initialValue - initial value of the state
26
26
  * @returns {State<T>} - new state signal
27
27
  */
28
- const state = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> => {
28
+ const createState = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> => {
29
+ if (initialValue == null) throw new NullishSignalValueError('state')
30
+
29
31
  const watchers: Set<Watcher> = new Set()
30
32
  let value: T = initialValue
31
33
 
32
- const s: State<T> = {
34
+ const state: State<T> = {
33
35
  [Symbol.toStringTag]: TYPE_STATE,
34
36
 
35
37
  /**
36
38
  * Get the current value of the state
37
39
  *
38
40
  * @since 0.9.0
39
- * @returns {T} - current value of the state
41
+ * @returns {T} - Current value of the state
40
42
  */
41
43
  get: (): T => {
42
44
  subscribe(watchers)
@@ -47,12 +49,13 @@ const state = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> => {
47
49
  * Set a new value of the state
48
50
  *
49
51
  * @since 0.9.0
50
- * @param {T} v
52
+ * @param {T} newValue - New value of the state
51
53
  * @returns {void}
52
54
  */
53
- set: (v: T): void => {
54
- if (isEqual(value, v)) return
55
- value = v
55
+ set: (newValue: T): void => {
56
+ if (newValue == null) throw new NullishSignalValueError('state')
57
+ if (isEqual(value, newValue)) return
58
+ value = newValue
56
59
  notify(watchers)
57
60
 
58
61
  // Setting to UNSET clears the watchers so the signal can be garbage collected
@@ -63,15 +66,20 @@ const state = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> => {
63
66
  * Update the state with a new value using a function
64
67
  *
65
68
  * @since 0.10.0
66
- * @param {(v: T) => T} fn - function to update the state
67
- * @returns {void} - updates the state with the result of the function
69
+ * @param {(v: T) => T} updater - Function to update the state
70
+ * @returns {void}
68
71
  */
69
- update: (fn: (v: T) => T): void => {
70
- s.set(fn(value))
72
+ update: (updater: (oldValue: T) => T): void => {
73
+ if (!isFunction(updater))
74
+ throw new InvalidCallbackError(
75
+ 'state update',
76
+ valueString(updater),
77
+ )
78
+ state.set(updater(value))
71
79
  },
72
80
  }
73
81
 
74
- return s
82
+ return state
75
83
  }
76
84
 
77
85
  /**
@@ -87,4 +95,4 @@ const isState = /*#__PURE__*/ <T extends {}>(
87
95
 
88
96
  /* === Exports === */
89
97
 
90
- export { TYPE_STATE, isState, state, type State }
98
+ export { TYPE_STATE, isState, createState, type State }