@zeix/cause-effect 0.17.3 → 0.18.1

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 (94) hide show
  1. package/.ai-context.md +169 -227
  2. package/.cursorrules +41 -35
  3. package/.github/copilot-instructions.md +176 -116
  4. package/ARCHITECTURE.md +276 -0
  5. package/CHANGELOG.md +29 -0
  6. package/CLAUDE.md +201 -143
  7. package/GUIDE.md +298 -0
  8. package/README.md +246 -193
  9. package/REQUIREMENTS.md +100 -0
  10. package/bench/reactivity.bench.ts +577 -0
  11. package/context7.json +4 -0
  12. package/examples/events-sensor.ts +187 -0
  13. package/examples/selector-sensor.ts +173 -0
  14. package/index.dev.js +1390 -1008
  15. package/index.js +1 -1
  16. package/index.ts +60 -74
  17. package/package.json +5 -2
  18. package/skills/changelog-keeper/SKILL.md +59 -0
  19. package/skills/changelog-keeper/agents/openai.yaml +4 -0
  20. package/src/errors.ts +118 -74
  21. package/src/graph.ts +612 -0
  22. package/src/nodes/collection.ts +512 -0
  23. package/src/nodes/effect.ts +149 -0
  24. package/src/nodes/list.ts +589 -0
  25. package/src/nodes/memo.ts +148 -0
  26. package/src/nodes/sensor.ts +149 -0
  27. package/src/nodes/state.ts +135 -0
  28. package/src/nodes/store.ts +378 -0
  29. package/src/nodes/task.ts +174 -0
  30. package/src/signal.ts +112 -66
  31. package/src/util.ts +26 -57
  32. package/test/batch.test.ts +96 -62
  33. package/test/benchmark.test.ts +473 -487
  34. package/test/collection.test.ts +456 -707
  35. package/test/effect.test.ts +293 -696
  36. package/test/list.test.ts +335 -592
  37. package/test/memo.test.ts +574 -0
  38. package/test/regression.test.ts +156 -0
  39. package/test/scope.test.ts +191 -0
  40. package/test/sensor.test.ts +454 -0
  41. package/test/signal.test.ts +220 -213
  42. package/test/state.test.ts +217 -265
  43. package/test/store.test.ts +346 -446
  44. package/test/task.test.ts +529 -0
  45. package/test/untrack.test.ts +167 -0
  46. package/types/index.d.ts +13 -15
  47. package/types/src/errors.d.ts +73 -17
  48. package/types/src/graph.d.ts +218 -0
  49. package/types/src/nodes/collection.d.ts +69 -0
  50. package/types/src/nodes/effect.d.ts +48 -0
  51. package/types/src/nodes/list.d.ts +66 -0
  52. package/types/src/nodes/memo.d.ts +63 -0
  53. package/types/src/nodes/sensor.d.ts +81 -0
  54. package/types/src/nodes/state.d.ts +78 -0
  55. package/types/src/nodes/store.d.ts +51 -0
  56. package/types/src/nodes/task.d.ts +79 -0
  57. package/types/src/signal.d.ts +43 -29
  58. package/types/src/util.d.ts +9 -16
  59. package/archive/benchmark.ts +0 -683
  60. package/archive/collection.ts +0 -253
  61. package/archive/composite.ts +0 -85
  62. package/archive/computed.ts +0 -195
  63. package/archive/list.ts +0 -483
  64. package/archive/memo.ts +0 -139
  65. package/archive/state.ts +0 -90
  66. package/archive/store.ts +0 -298
  67. package/archive/task.ts +0 -189
  68. package/src/classes/collection.ts +0 -245
  69. package/src/classes/computed.ts +0 -349
  70. package/src/classes/list.ts +0 -343
  71. package/src/classes/ref.ts +0 -70
  72. package/src/classes/state.ts +0 -102
  73. package/src/classes/store.ts +0 -262
  74. package/src/diff.ts +0 -138
  75. package/src/effect.ts +0 -93
  76. package/src/match.ts +0 -45
  77. package/src/resolve.ts +0 -49
  78. package/src/system.ts +0 -257
  79. package/test/computed.test.ts +0 -1108
  80. package/test/diff.test.ts +0 -955
  81. package/test/match.test.ts +0 -388
  82. package/test/ref.test.ts +0 -353
  83. package/test/resolve.test.ts +0 -154
  84. package/types/src/classes/collection.d.ts +0 -45
  85. package/types/src/classes/computed.d.ts +0 -94
  86. package/types/src/classes/list.d.ts +0 -43
  87. package/types/src/classes/ref.d.ts +0 -35
  88. package/types/src/classes/state.d.ts +0 -49
  89. package/types/src/classes/store.d.ts +0 -52
  90. package/types/src/diff.d.ts +0 -28
  91. package/types/src/effect.d.ts +0 -15
  92. package/types/src/match.d.ts +0 -21
  93. package/types/src/resolve.d.ts +0 -29
  94. package/types/src/system.d.ts +0 -78
package/src/system.ts DELETED
@@ -1,257 +0,0 @@
1
- import { assert, type Guard } from './errors'
2
- import type { UnknownSignal } from './signal'
3
-
4
- /* === Types === */
5
-
6
- type Cleanup = () => void
7
-
8
- // biome-ignore lint/suspicious/noConfusingVoidType: optional Cleanup return type
9
- type MaybeCleanup = Cleanup | undefined | void
10
-
11
- type Watcher = {
12
- (): void
13
- run(): void
14
- onCleanup(cleanup: Cleanup): void
15
- stop(): void
16
- }
17
-
18
- type SignalOptions<T extends unknown & {}> = {
19
- guard?: Guard<T>
20
- watched?: () => void
21
- unwatched?: () => void
22
- }
23
-
24
- /* === Internal === */
25
-
26
- // Currently active watcher
27
- let activeWatcher: Watcher | undefined
28
-
29
- const watchersMap = new WeakMap<UnknownSignal, Set<Watcher>>()
30
- const watchedCallbackMap = new WeakMap<object, () => void>()
31
- const unwatchedCallbackMap = new WeakMap<object, () => void>()
32
-
33
- // Queue of pending watcher reactions for batched change notifications
34
- const pendingReactions = new Set<() => void>()
35
- let batchDepth = 0
36
-
37
- /* === Constants === */
38
-
39
- // biome-ignore lint/suspicious/noExplicitAny: Deliberately using any to be used as a placeholder value in any signal
40
- const UNSET: any = Symbol()
41
-
42
- /* === Functions === */
43
-
44
- /**
45
- * Create a watcher to observe changes in signals.
46
- *
47
- * A watcher combines push and pull reaction functions with onCleanup and stop methods
48
- *
49
- * @since 0.17.3
50
- * @param {() => void} push - Function to be called when the state changes (push)
51
- * @param {() => void} pull - Function to be called on demand from consumers (pull)
52
- * @returns {Watcher} - Watcher object with off and cleanup methods
53
- */
54
- const createWatcher = (push: () => void, pull: () => void): Watcher => {
55
- const cleanups = new Set<Cleanup>()
56
- const watcher = push as Partial<Watcher>
57
- watcher.run = () => {
58
- const prev = activeWatcher
59
- activeWatcher = watcher as Watcher
60
- try {
61
- pull()
62
- } finally {
63
- activeWatcher = prev
64
- }
65
- }
66
- watcher.onCleanup = (cleanup: Cleanup) => {
67
- cleanups.add(cleanup)
68
- }
69
- watcher.stop = () => {
70
- try {
71
- for (const cleanup of cleanups) cleanup()
72
- } finally {
73
- cleanups.clear()
74
- }
75
- }
76
- return watcher as Watcher
77
- }
78
-
79
- /**
80
- * Run a function with signal reads in a non-tracking context.
81
- *
82
- * @param {() => void} callback - Callback
83
- */
84
- const untrack = (callback: () => void): void => {
85
- const prev = activeWatcher
86
- activeWatcher = undefined
87
- try {
88
- callback()
89
- } finally {
90
- activeWatcher = prev
91
- }
92
- }
93
-
94
- const registerWatchCallbacks = (
95
- signal: UnknownSignal,
96
- watched: () => void,
97
- unwatched?: () => void,
98
- ) => {
99
- watchedCallbackMap.set(signal, watched)
100
- if (unwatched) unwatchedCallbackMap.set(signal, unwatched)
101
- }
102
-
103
- /**
104
- * Subscribe active watcher to a signal.
105
- *
106
- * @param {UnknownSignal} signal - Signal to subscribe to
107
- * @returns {boolean} - true if the active watcher was subscribed,
108
- * false if the watcher was already subscribed or there was no active watcher
109
- */
110
- const subscribeTo = (signal: UnknownSignal): boolean => {
111
- if (!activeWatcher || watchersMap.get(signal)?.has(activeWatcher))
112
- return false
113
-
114
- const watcher = activeWatcher
115
- if (!watchersMap.has(signal)) watchersMap.set(signal, new Set<Watcher>())
116
-
117
- const watchers = watchersMap.get(signal)
118
- assert(watchers)
119
- if (!watchers.size) {
120
- const watchedCallback = watchedCallbackMap.get(signal)
121
- if (watchedCallback) untrack(watchedCallback)
122
- }
123
- watchers.add(watcher)
124
- watcher.onCleanup(() => {
125
- watchers.delete(watcher)
126
- if (!watchers.size) {
127
- const unwatchedCallback = unwatchedCallbackMap.get(signal)
128
- if (unwatchedCallback) untrack(unwatchedCallback)
129
- }
130
- })
131
- return true
132
- }
133
-
134
- const subscribeActiveWatcher = (watchers: Set<Watcher>) => {
135
- if (!activeWatcher || watchers.has(activeWatcher)) return false
136
-
137
- const watcher = activeWatcher
138
- watchers.add(watcher)
139
- if (!watchers.size) {
140
- const watchedCallback = watchedCallbackMap.get(watchers)
141
- if (watchedCallback) untrack(watchedCallback)
142
- }
143
- watcher.onCleanup(() => {
144
- watchers.delete(watcher)
145
- if (!watchers.size) {
146
- const unwatchedCallback = unwatchedCallbackMap.get(watchers)
147
- if (unwatchedCallback) untrack(unwatchedCallback)
148
- }
149
- })
150
- return true
151
- }
152
-
153
- /**
154
- * Unsubscribe all watchers from a signal so it can be garbage collected.
155
- *
156
- * @param {UnknownSignal} signal - Signal to unsubscribe from
157
- * @returns {void}
158
- */
159
- const unsubscribeAllFrom = (signal: UnknownSignal): void => {
160
- const watchers = watchersMap.get(signal)
161
- if (!watchers) return
162
-
163
- for (const watcher of watchers) watcher.stop()
164
- watchers.clear()
165
- }
166
-
167
- /**
168
- * Notify watchers of a signal change.
169
- *
170
- * @param {UnknownSignal} signal - Signal to notify watchers of
171
- * @returns {boolean} - Whether any watchers were notified
172
- */
173
- const notifyOf = (signal: UnknownSignal): boolean => {
174
- const watchers = watchersMap.get(signal)
175
- if (!watchers?.size) return false
176
-
177
- for (const react of watchers) {
178
- if (batchDepth) pendingReactions.add(react)
179
- else react()
180
- }
181
- return true
182
- }
183
-
184
- const notifyWatchers = (watchers: Set<Watcher>): boolean => {
185
- if (!watchers.size) return false
186
-
187
- for (const react of watchers) {
188
- if (batchDepth) pendingReactions.add(react)
189
- else react()
190
- }
191
- return true
192
- }
193
-
194
- /**
195
- * Flush all pending reactions of enqueued watchers.
196
- */
197
- const flush = () => {
198
- while (pendingReactions.size) {
199
- const watchers = Array.from(pendingReactions)
200
- pendingReactions.clear()
201
- for (const react of watchers) react()
202
- }
203
- }
204
-
205
- /**
206
- * Batch multiple signal writes.
207
- *
208
- * @param {() => void} callback - Function with multiple signal writes to be batched
209
- */
210
- const batch = (callback: () => void) => {
211
- batchDepth++
212
- try {
213
- callback()
214
- } finally {
215
- flush()
216
- batchDepth--
217
- }
218
- }
219
-
220
- /**
221
- * Run a function with signal reads in a tracking context (or temporarily untrack).
222
- *
223
- * @param {Watcher | false} watcher - Watcher to be called when the signal changes
224
- * or false for temporary untracking while inserting auto-hydrating DOM nodes
225
- * that might read signals (e.g., Web Components)
226
- * @param {() => void} run - Function to run the computation or effect
227
- */
228
- const track = (watcher: Watcher | false, run: () => void): void => {
229
- const prev = activeWatcher
230
- activeWatcher = watcher || undefined
231
- try {
232
- run()
233
- } finally {
234
- activeWatcher = prev
235
- }
236
- }
237
-
238
- /* === Exports === */
239
-
240
- export {
241
- type Cleanup,
242
- type MaybeCleanup,
243
- type Watcher,
244
- type SignalOptions,
245
- UNSET,
246
- createWatcher,
247
- registerWatchCallbacks,
248
- subscribeTo,
249
- subscribeActiveWatcher,
250
- unsubscribeAllFrom,
251
- notifyOf,
252
- notifyWatchers,
253
- flush,
254
- batch,
255
- track,
256
- untrack,
257
- }