@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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Cause & Effect
2
2
 
3
- Version 0.15.1
3
+ Version 0.16.0
4
4
 
5
5
  **Cause & Effect** is a lightweight, reactive state management library for JavaScript applications. It uses fine-grained reactivity with signals to create predictable and efficient data flow in your app.
6
6
 
@@ -10,10 +10,10 @@ Version 0.15.1
10
10
 
11
11
  ### Core Concepts
12
12
 
13
- - **State signals**: Hold values that can be directly modified: `state()`
14
- - **Store signals**: Hold objects of nested reactive properties: `store()`
15
- - **Computed signals**: Derive memoized values from other signals: `computed()`
16
- - **Effects**: Run side effects when signals change: `effect()`
13
+ - **State signals**: Hold values that can be directly modified: `createState()`
14
+ - **Store signals**: Hold objects of nested reactive properties: `createStore()`
15
+ - **Computed signals**: Derive memoized values from other signals: `createComputed()`
16
+ - **Effects**: Run side effects when signals change: `createEffect()`
17
17
 
18
18
  ## Key Features
19
19
 
@@ -28,16 +28,16 @@ Version 0.15.1
28
28
  ## Quick Start
29
29
 
30
30
  ```js
31
- import { state, computed, effect } from '@zeix/cause-effect'
31
+ import { createState, createComputed, createEffect } from '@zeix/cause-effect'
32
32
 
33
33
  // 1. Create state
34
- const user = state({ name: 'Alice', age: 30 })
34
+ const user = createState({ name: 'Alice', age: 30 })
35
35
 
36
36
  // 2. Create computed values
37
- const greeting = computed(() => `Hello ${user.get().name}!`)
37
+ const greeting = createComputed(() => `Hello ${user.get().name}!`)
38
38
 
39
39
  // 3. React to changes
40
- effect(() => {
40
+ createEffect(() => {
41
41
  console.log(`${greeting.get()} You are ${user.get().age} years old`)
42
42
  })
43
43
 
@@ -59,13 +59,13 @@ bun add @zeix/cause-effect
59
59
 
60
60
  ### State Signals
61
61
 
62
- `state()` creates a mutable signal. Every signal has a `.get()` method to access its current value. State signals also provide `.set()` to directly assign a new value and `.update()` to modify the value with a function.
62
+ `createState()` creates a mutable signal. Every signal has a `.get()` method to access its current value. State signals also provide `.set()` to directly assign a new value and `.update()` to modify the value with a function.
63
63
 
64
64
  ```js
65
- import { state, effect } from '@zeix/cause-effect'
65
+ import { createState, createEffect } from '@zeix/cause-effect'
66
66
 
67
- const count = state(42)
68
- effect(() => {
67
+ const count = createState(42)
68
+ createEffect(() => {
69
69
  console.log(count.get()) // logs '42'
70
70
  })
71
71
  count.set(24) // logs '24'
@@ -77,12 +77,12 @@ document.querySelector('.increment').addEventListener('click', () => {
77
77
 
78
78
  ### Store Signals
79
79
 
80
- `store()` creates a mutable signal that holds an object with nested reactive properties. Each property automatically becomes its own signal with `.get()`, `.set()`, and `.update()` methods. Nested objects recursively become nested stores.
80
+ `createStore()` creates a mutable signal that holds an object with nested reactive properties. Each property automatically becomes its own signal with `.get()`, `.set()`, and `.update()` methods. Nested objects recursively become nested stores.
81
81
 
82
82
  ```js
83
- import { store, effect } from '@zeix/cause-effect'
83
+ import { createStore, createEffect } from '@zeix/cause-effect'
84
84
 
85
- const user = store({
85
+ const user = createStore({
86
86
  name: 'Alice',
87
87
  age: 30,
88
88
  preferences: {
@@ -92,12 +92,12 @@ const user = store({
92
92
  })
93
93
 
94
94
  // Individual properties are reactive
95
- effect(() => {
95
+ createEffect(() => {
96
96
  console.log(`${user.name.get()} is ${user.age.get()} years old`)
97
97
  })
98
98
 
99
99
  // Nested properties work the same way
100
- effect(() => {
100
+ createEffect(() => {
101
101
  console.log(`Theme: ${user.preferences.theme.get()}`)
102
102
  })
103
103
 
@@ -106,17 +106,24 @@ user.age.update(v => v + 1) // Logs: "Alice is 31 years old"
106
106
  user.preferences.theme.set('light') // Logs: "Theme: light"
107
107
 
108
108
  // Watch the entire store
109
- effect(() => {
109
+ createEffect(() => {
110
110
  console.log('User data:', user.get()) // Triggers on any nested change
111
111
  })
112
112
  ```
113
113
 
114
+ #### When to Use
115
+
116
+ **When to use stores vs states:**
117
+
118
+ - **Use `createStore()`** for objects with properties that you want to access and modify individually.
119
+ - **Use `createState()`** for primitive values (numbers, strings, booleans) or objects you access and replace entirely.
120
+
114
121
  #### Dynamic Properties
115
122
 
116
123
  Stores support dynamic property addition and removal at runtime using the `add()` and `remove()` methods:
117
124
 
118
125
  ```js
119
- import { store, effect } from '@zeix/cause-effect'
126
+ import { createStore, createEffect } from '@zeix/cause-effect'
120
127
 
121
128
  const settings = store({ autoSave: true })
122
129
 
@@ -140,61 +147,102 @@ The `add()` and `remove()` methods are optimized for performance:
140
147
  - They're perfect for frequent single-property additions/removals
141
148
  - They trigger the same events and reactivity as other store operations
142
149
 
143
- #### Store Events
150
+ #### Array-like Stores
151
+
152
+ Stores created from arrays behave like arrays with reactive properties. They support duck-typing with length property, single-parameter `add()`, and efficient sorting:
153
+
154
+ ```js
155
+ import { createStore, createEffect } from '@zeix/cause-effect'
156
+
157
+ const items = createStore(['banana', 'apple', 'cherry'])
158
+
159
+ // Duck-typing: behaves like an array
160
+ console.log(items.length) // 3
161
+ console.log(typeof items.length) // 'number'
162
+
163
+ // Individual items are reactive
164
+ createEffect(() => {
165
+ console.log(`First item: ${items[0].get()}`)
166
+ })
167
+
168
+ // Single-parameter add() appends to end
169
+ items.add('date') // Adds at index 3
170
+ console.log(items.get()) // ['banana', 'apple', 'cherry', 'date']
171
+
172
+ // Efficient sorting preserves signal references
173
+ items.sort() // Default: string comparison
174
+ console.log(items.get()) // ['apple', 'banana', 'cherry', 'date']
175
+
176
+ // Custom sorting
177
+ items.sort((a, b) => b.localeCompare(a)) // Reverse alphabetical
178
+ console.log(items.get()) // ['date', 'cherry', 'banana', 'apple']
179
+ ```
180
+
181
+ #### Store Change Notifications
144
182
 
145
- Stores emit events when properties are added, changed, or removed. You can listen to these events using standard `addEventListener()`:
183
+ Stores emit notifications (sort of light-weight events) when properties are added, changed, or removed. You can listen to these notications using the `.on()` method:
146
184
 
147
185
  ```js
148
- import { store } from '@zeix/cause-effect'
186
+ import { createStore } from '@zeix/cause-effect'
149
187
 
150
- const user = store({ name: 'Alice', age: 30 })
188
+ const user = createStore({ name: 'Alice', age: 30 })
151
189
 
152
190
  // Listen for property additions
153
- user.addEventListener('store-add', (event) => {
154
- console.log('Added properties:', event.detail)
191
+ const offAdd = user.on('add', (added) => {
192
+ console.log('Added properties:', added)
155
193
  })
156
194
 
157
195
  // Listen for property changes
158
- user.addEventListener('store-change', (event) => {
159
- console.log('Changed properties:', event.detail)
196
+ const offChange = user.on('change', (changed) => {
197
+ console.log('Changed properties:', changed)
160
198
  })
161
199
 
162
200
  // Listen for property removals
163
- user.addEventListener('store-remove', (event) => {
164
- console.log('Removed properties:', event.detail)
201
+ const offRemove = user.on('remove', (removed) => {
202
+ console.log('Removed properties:', removed)
165
203
  })
166
204
 
167
- // These will trigger the respective events:
205
+ // These will trigger the respective notifications:
168
206
  user.add('email', 'alice@example.com') // Logs: "Added properties: { email: 'alice@example.com' }"
169
207
  user.age.set(31) // Logs: "Changed properties: { age: 31 }"
170
208
  user.remove('email') // Logs: "Removed properties: { email: UNSET }"
209
+
210
+ // Listen for sort notifications (useful for UI animations)
211
+ const items = createStore(['banana', 'apple', 'cherry'])
212
+ items.sort((a, b) => b.localeCompare(a)) // Reverse alphabetical
213
+ const offSort = items.on('sort', (newOrder) => {
214
+ console.log('Items reordered:', newOrder) // ['2', '1', '0']
215
+ })
171
216
  ```
172
217
 
173
- Events are also fired when using `set()` or `update()` methods on the entire store:
218
+ Notifications are also fired when using `set()` or `update()` methods on the entire store:
174
219
 
175
220
  ```js
176
- // This will fire multiple events based on what changed
221
+ // This will fire multiple notifications based on what changed
177
222
  user.update(u => ({ ...u, name: 'Bob', city: 'New York' }))
178
223
  // Logs: "Changed properties: { name: 'Bob' }"
179
224
  // Logs: "Added properties: { city: 'New York' }"
180
225
  ```
181
226
 
182
- **When to use stores vs state:**
183
- - **Use `store()`** for objects with reactive properties that you want to access individually
184
- - **Use `state()`** for primitive values or objects you replace entirely
227
+ To stop listening to notifications, call the returned cleanup function:
185
228
 
186
- ### Computed Signals vs. Functions
229
+ ```js
230
+ offAdd() // Stops listening to add notifications
231
+ offChange() // Stops listening to change notifications
232
+ offRemove() // Stops listening to remove notifications
233
+ offSort() // Stops listening to sort notifications
234
+ ```
187
235
 
188
- #### When to Use Computed Signals
236
+ ### Computed Signals
189
237
 
190
- `computed()` creates a memoized read-only signal that automatically tracks dependencies and updates only when those dependencies change.
238
+ `createComputed()` creates a memoized read-only signal that automatically tracks dependencies and updates only when those dependencies change.
191
239
 
192
240
  ```js
193
- import { state, computed, effect } from '@zeix/cause-effect'
241
+ import { createState, createComputed, createEffect } from '@zeix/cause-effect'
194
242
 
195
- const count = state(42)
196
- const isEven = computed(() => !(count.get() % 2))
197
- effect(() => console.log(isEven.get())) // logs 'true'
243
+ const count = createState(42)
244
+ const isEven = createComputed(() => !(count.get() % 2))
245
+ createEffect(() => console.log(isEven.get())) // logs 'true'
198
246
  count.set(24) // logs nothing because 24 is also an even number
199
247
  document.querySelector('button.increment').addEventListener('click', () => {
200
248
  count.update(v => ++v)
@@ -202,7 +250,7 @@ document.querySelector('button.increment').addEventListener('click', () => {
202
250
  // Click on button logs 'false', 'true', and so on
203
251
  ```
204
252
 
205
- #### When to Use Functions
253
+ #### When to Use
206
254
 
207
255
  **Performance tip**: For simple derivations, plain functions often outperform computed signals:
208
256
 
@@ -213,16 +261,53 @@ const isEven = () => !(count.get() % 2)
213
261
 
214
262
  **When to use which approach:**
215
263
 
216
- - **Use functions when**: The calculation is simple, inexpensive, or called infrequently
217
- - **Use computed() when**:
264
+ - **Use functions when**: The calculation is simple, inexpensive, or called infrequently.
265
+ - **Use createComputed() when**:
218
266
  - The calculation is expensive
219
267
  - You need to share the result between multiple consumers
220
268
  - You're working with asynchronous operations
221
269
  - You need to track specific error states
270
+
271
+ #### Reducer-like Capabilities
272
+
273
+ `createComputed()` supports reducer-like patterns by accepting an initial value and providing access to the previous value in the callback:
274
+
275
+ ```js
276
+ import { createState, createComputed, createEffect } from '@zeix/cause-effect'
277
+
278
+ const actions = createState('increment')
279
+ const counter = createComputed((prev, abort) => {
280
+ const action = actions.get()
281
+ switch (action) {
282
+ case 'increment':
283
+ return prev + 1
284
+ case 'decrement':
285
+ return prev - 1
286
+ case 'reset':
287
+ return 0
288
+ default:
289
+ return prev
290
+ }
291
+ }, 0) // Initial value of 0
292
+
293
+ createEffect(() => console.log('Counter:', counter.get()))
294
+
295
+ // Dispatch actions
296
+ actions.set('increment') // Counter: 1
297
+ actions.set('increment') // Counter: 2
298
+ actions.set('decrement') // Counter: 1
299
+ actions.set('reset') // Counter: 0
300
+ ```
301
+
302
+ This pattern is particularly useful for:
303
+ - State machines with transitions based on current state
304
+ - Accumulating values over time
305
+ - Complex state updates that depend on previous state
306
+ - Building reactive reducers similar to Redux patterns
222
307
 
223
308
  #### Asynchronous Computations with Automatic Cancellation
224
309
 
225
- `computed()` seamlessly handles asynchronous operations with built-in cancellation support. When used with an async function, it:
310
+ `createComputed()` seamlessly handles asynchronous operations with built-in cancellation support. When used with an async function, it:
226
311
 
227
312
  1. Provides an `abort` signal parameter you can pass to fetch or other cancelable APIs
228
313
  2. Automatically cancels pending operations when dependencies change
@@ -230,10 +315,10 @@ const isEven = () => !(count.get() % 2)
230
315
  4. Properly handles errors from failed requests
231
316
 
232
317
  ```js
233
- import { state, computed, effect, resolve, match } from '@zeix/cause-effect'
318
+ import { createState, createComputed, createEffect, resolve, match } from '@zeix/cause-effect'
234
319
 
235
- const id = state(42)
236
- const data = computed(async abort => {
320
+ const id = createState(42)
321
+ const data = createComputed(async (_, abort) => {
237
322
  // The abort signal is automatically managed by the computed signal
238
323
  const response = await fetch(`/api/entries/${id.get()}`, { signal: abort })
239
324
  if (!response.ok) throw new Error(`Failed to fetch data: ${response.statusText}`)
@@ -241,7 +326,7 @@ const data = computed(async abort => {
241
326
  })
242
327
 
243
328
  // Handle all possible states using resolve and match helpers
244
- effect(() => {
329
+ createEffect(() => {
245
330
  match(resolve({ data }), {
246
331
  ok: ({ data: json }) => console.log('Data loaded:', json),
247
332
  nil: () => console.log('Loading...'),
@@ -255,19 +340,19 @@ document.querySelector('button.next').addEventListener('click', () => {
255
340
  })
256
341
  ```
257
342
 
258
- **Note**: Always use `computed()` (not plain functions) for async operations to benefit from automatic cancellation, memoization, and state management.
343
+ **Note**: Always use `createComputed()` (not plain functions) for async operations to benefit from automatic cancellation and memoization.
259
344
 
260
345
  ## Effects and Error Handling
261
346
 
262
- The `effect()` function supports both synchronous and asynchronous callbacks:
347
+ The `createEffect()` function supports both synchronous and asynchronous callbacks:
263
348
 
264
349
  ### Synchronous Effects
265
350
 
266
351
  ```js
267
- import { state, effect } from '@zeix/cause-effect'
352
+ import { createState, createEffect } from '@zeix/cause-effect'
268
353
 
269
- const count = state(42)
270
- effect(() => {
354
+ const count = createState(42)
355
+ createEffect(() => {
271
356
  console.log('Count changed:', count.get())
272
357
  })
273
358
  ```
@@ -277,10 +362,10 @@ effect(() => {
277
362
  Async effect callbacks receive an `AbortSignal` parameter that automatically cancels when the effect re-runs or is cleaned up:
278
363
 
279
364
  ```js
280
- import { state, effect } from '@zeix/cause-effect'
365
+ import { createState, createEffect } from '@zeix/cause-effect'
281
366
 
282
- const userId = state(1)
283
- effect(async (abort) => {
367
+ const userId = createState(1)
368
+ createEffect(async (abort) => {
284
369
  try {
285
370
  const response = await fetch(`/api/users/${userId.get()}`, { signal: abort })
286
371
  const user = await response.json()
@@ -298,16 +383,16 @@ effect(async (abort) => {
298
383
  For more sophisticated error handling, use the `resolve()` and `match()` helper functions:
299
384
 
300
385
  ```js
301
- import { state, computed, effect, resolve, match } from '@zeix/cause-effect'
386
+ import { createState, createEffect, resolve, match } from '@zeix/cause-effect'
302
387
 
303
- const userId = state(1)
304
- const userData = computed(async (abort) => {
388
+ const userId = createState(1)
389
+ const userData = createEffect(async (abort) => {
305
390
  const response = await fetch(`/api/users/${userId.get()}`, { signal: abort })
306
391
  if (!response.ok) throw new Error(`HTTP ${response.status}`)
307
392
  return response.json()
308
393
  })
309
394
 
310
- effect(() => {
395
+ createEffect(() => {
311
396
  match(resolve({ userData }), {
312
397
  ok: ({ userData: user }) => console.log('User loaded:', user),
313
398
  nil: () => console.log('Loading user...'),
@@ -318,90 +403,6 @@ effect(() => {
318
403
 
319
404
  The `resolve()` function extracts values from signals and returns a discriminated union result, while `match()` provides pattern matching for handling different states declaratively.
320
405
 
321
- ## DOM Updates
322
-
323
- The `enqueue()` function allows you to schedule DOM updates to be executed on the next animation frame. It returns a `Promise`, which makes it easy to track when updates are applied or handle errors.
324
-
325
- ```js
326
- import { enqueue } from '@zeix/cause-effect'
327
-
328
- // Schedule a DOM update
329
- enqueue(() => {
330
- document.getElementById('myElement').textContent = 'Updated content'
331
- })
332
- .then(() => console.log('Update applied successfully'))
333
- .catch(error => console.error('Update failed:', error))
334
- ```
335
-
336
- ### Deduplication with Symbols
337
-
338
- A powerful feature of `enqueue()` is deduplication, which ensures that only the most recent update for a specific operation is applied when multiple updates occur within a single animation frame. This is particularly useful for high-frequency events like typing, dragging, or scrolling.
339
-
340
- Deduplication is controlled using JavaScript Symbols:
341
-
342
- ```js
343
- import { state, effect, enqueue } from '@zeix/cause-effect'
344
-
345
- // Define a signal and update it in an event handler
346
- const name = state('')
347
- document.querySelector('input[name="name"]').addEventListener('input', e => {
348
- name.set(e.target.value) // Triggers an update on every keystroke
349
- })
350
-
351
- // Define an effect to react to signal changes
352
- effect(text => {
353
- // Create a Symbol for a specific update operation
354
- const NAME_UPDATE = Symbol('name-update')
355
- const text = name.get()
356
- const nameSpan = document.querySelector('.greeting .name')
357
- enqueue(() => {
358
- nameSpan.textContent = text
359
- return text
360
- }, NAME_UPDATE) // Using the Symbol for deduplication
361
- .then(result => console.log(`Name was updated to ${result}`))
362
- .catch(error => console.error('Failed to update name:', error))
363
- })
364
- ```
365
-
366
- In this example, as the user types "Jane" quickly, the intermediate values ('J', 'Ja', 'Jan') are deduplicated, and only the final value 'Jane' is applied to the DOM. Only the Promise for the final update is resolved.
367
-
368
- ### How Deduplication Works
369
-
370
- When multiple `enqueue` calls use the same Symbol before the next animation frame:
371
-
372
- 1. Only the last call will be executed
373
- 2. Previous calls are superseded
374
- 3. Only the Promise of the last call will be resolved
375
-
376
- This "last-write-wins" behavior optimizes DOM updates and prevents unnecessary work when many updates happen rapidly.
377
-
378
- ### Optional Deduplication
379
-
380
- The deduplication Symbol is optional. When not provided, a unique Symbol is created automatically, ensuring the update is always executed:
381
-
382
- ```js
383
- // No deduplication - always executed
384
- enqueue(() => document.title = 'New Page Title')
385
-
386
- // Create symbols for different types of updates
387
- const COLOR_UPDATE = Symbol('color-update')
388
- const SIZE_UPDATE = Symbol('size-update')
389
-
390
- // These won't interfere with each other (different symbols)
391
- enqueue(() => element.style.color = 'red', COLOR_UPDATE)
392
- enqueue(() => element.style.fontSize = '16px', SIZE_UPDATE)
393
-
394
- // This will replace the previous color update (same symbol)
395
- enqueue(() => element.style.color = 'blue', COLOR_UPDATE)
396
- ```
397
-
398
- Using Symbols for deduplication provides:
399
-
400
- - Clear semantic meaning for update operations
401
- - Type safety in TypeScript
402
- - Simple mechanism to control which updates should overwrite each other
403
- - Flexibility to run every update when needed
404
-
405
406
  ## Advanced Usage
406
407
 
407
408
  ### Batching Updates
@@ -409,13 +410,20 @@ Using Symbols for deduplication provides:
409
410
  Use `batch()` to group multiple signal updates, ensuring effects run only once after all changes are applied:
410
411
 
411
412
  ```js
412
- import { state, computed, effect, batch, resolve, match } from '@zeix/cause-effect'
413
-
414
- // State: define an array of State<number>
415
- const signals = [state(2), state(3), state(5)]
413
+ import {
414
+ createState,
415
+ createComputed,
416
+ createEffect,
417
+ batch,
418
+ resolve,
419
+ match
420
+ } from '@zeix/cause-effect'
421
+
422
+ // State: define an Array<State<number>>
423
+ const signals = [createState(2), createState(3), createState(5)]
416
424
 
417
425
  // Compute the sum of all signals
418
- const sum = computed(() => {
426
+ const sum = createComputed(() => {
419
427
  const v = signals.reduce((total, signal) => total + signal.get(), 0)
420
428
  // Validate the result
421
429
  if (!Number.isFinite(v)) throw new Error('Invalid value')
@@ -423,7 +431,7 @@ const sum = computed(() => {
423
431
  })
424
432
 
425
433
  // Effect: handle the result with error handling
426
- effect(() => {
434
+ createEffect(() => {
427
435
  match(resolve({ sum }), {
428
436
  ok: ({ sum: v }) => console.log('Sum:', v),
429
437
  err: errors => console.error('Error:', errors[0])
@@ -450,11 +458,11 @@ signals[0].set(NaN)
450
458
  Effects return a cleanup function. When executed, it will unsubscribe from signals and run cleanup functions returned by effect callbacks, for example to remove event listeners.
451
459
 
452
460
  ```js
453
- import { state, computed, effect } from '@zeix/cause-effect'
461
+ import { createState, createComputed, createEffect } from '@zeix/cause-effect'
454
462
 
455
- const user = state({ name: 'Alice', age: 30 })
463
+ const user = createState({ name: 'Alice', age: 30 })
456
464
  const greeting = () => `Hello ${user.get().name}!`
457
- const cleanup = effect(() => {
465
+ const cleanup = createEffect(() => {
458
466
  console.log(`${greeting()} You are ${user.get().age} years old`)
459
467
  return () => console.log('Cleanup') // Cleanup function
460
468
  })
@@ -472,10 +480,10 @@ user.set({ name: 'Bob', age: 28 }) // Won't trigger the effect anymore
472
480
  The `resolve()` function extracts values from multiple signals and returns a discriminated union result:
473
481
 
474
482
  ```js
475
- import { state, computed, resolve } from '@zeix/cause-effect'
483
+ import { createState, createComputed, resolve } from '@zeix/cause-effect'
476
484
 
477
- const name = state('Alice')
478
- const age = computed(() => 30)
485
+ const name = createState('Alice')
486
+ const age = createComputed(() => 30)
479
487
  const result = resolve({ name, age })
480
488
 
481
489
  if (result.ok) {
package/eslint.config.js CHANGED
@@ -1,5 +1,5 @@
1
- import globals from 'globals'
2
1
  import pluginJs from '@eslint/js'
2
+ import globals from 'globals'
3
3
  import tseslint from 'typescript-eslint'
4
4
 
5
5
  /** @type {import('eslint').Linter.Config[]} */