@zeix/cause-effect 0.15.2 → 0.16.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.
package/.ai-context.md ADDED
@@ -0,0 +1,254 @@
1
+ # AI Context for Cause & Effect
2
+
3
+ ## What is Cause & Effect?
4
+
5
+ Cause & Effect is a modern reactive state management library for JavaScript/TypeScript that implements the signals pattern. It provides fine-grained reactivity with automatic dependency tracking, making it easy to build responsive applications with predictable state updates.
6
+
7
+ ## Core Architecture
8
+
9
+ ### Signal Types
10
+ - **Signal**: Base interface with `.get()` method for value access
11
+ - **State**: Mutable signals for primitive values (numbers, strings, booleans)
12
+ - **Store**: Mutable signals for objects with individually reactive properties
13
+ - **Computed**: Read-only derived signals with automatic memoization, reducer-like capabilities and asyc handling
14
+ - **Effect**: Side effect handlers that react to signal changes
15
+
16
+ ### Key Principles
17
+ 1. **Functional Programming**: Immutable by default, pure functions
18
+ 2. **Type Safety**: Full TypeScript support with strict type constraints
19
+ 3. **Performance**: Minimal re-computations through dependency tracking
20
+ 4. **Async Support**: Built-in cancellation with AbortSignal
21
+ 5. **Tree-shaking**: Optimized for minimal bundle size
22
+
23
+ ## Project Structure
24
+
25
+ ```
26
+ cause-effect/
27
+ ├── src/
28
+ │ ├── signal.ts # Base signal types and utilities
29
+ │ ├── state.ts # Mutable state signals (createState)
30
+ │ ├── store.ts # Object stores with reactive properties
31
+ │ ├── computed.ts # Computed/derived signals (createComputed)
32
+ │ ├── effect.ts # Effect system (createEffect)
33
+ │ ├── system.ts # Core reactivity (watchers, batching, subscriptions)
34
+ │ ├── resolve.ts # Helper for extracting signal values
35
+ │ ├── match.ts # Pattern matching utility
36
+ │ ├── diff.ts # Object comparison utilities
37
+ │ ├── errors.ts # Custom error classes
38
+ │ └── util.ts # Utilities and constants
39
+ ├── index.ts # Main export file
40
+ ├── types/index.d.ts # TypeScript declarations
41
+ └── package.json # NPM package configuration
42
+ ```
43
+
44
+ ## API Patterns
45
+
46
+ ### Signal Creation
47
+ ```typescript
48
+ // State signals for primitives
49
+ const count = createState(42)
50
+ const name = createState('Alice')
51
+ const actions = createState<'increment' | 'decrement' | 'reset'>('reset')
52
+
53
+ // Store signals for objects
54
+ const user = createStore({ name: 'Alice', age: 30 })
55
+
56
+ // Computed signals for derived values
57
+ const doubled = createComputed(() => count.get() * 2)
58
+
59
+ // Computed with reducer-like capabilities (access to previous value)
60
+ const counter = createComputed((prev) => {
61
+ const action = actions.get()
62
+ switch (action) {
63
+ case 'increment': return prev + 1
64
+ case 'decrement': return prev - 1
65
+ case 'reset': return 0
66
+ default: return prev
67
+ }
68
+ }, 0) // Initial value
69
+
70
+ // Async computed with access to previous value
71
+ const userData = createComputed(async (prev, abort) => {
72
+ const id = userId.get()
73
+ if (!id) return prev // Keep previous data if no user ID
74
+ const response = await fetch(`/users/${id}`, { signal: abort })
75
+ return response.json()
76
+ })
77
+ ```
78
+
79
+ ### Reactivity
80
+ ```typescript
81
+ // Effects react to signal changes
82
+ createEffect(() => {
83
+ console.log(`Count: ${count.get()}`)
84
+ })
85
+
86
+ // Async effects with automatic cancellation
87
+ createEffect(async (abort) => {
88
+ const response = await fetch('/api', { signal: abort })
89
+ return response.json()
90
+ })
91
+ ```
92
+
93
+ ### Value Access Patterns
94
+ ```typescript
95
+ // All signals use .get() for value access
96
+ const value = signal.get()
97
+
98
+ // Mutable signals have .set() and .update()
99
+ state.set(newValue)
100
+ state.update(current => current + 1)
101
+
102
+ // Store properties are individually reactive
103
+ user.name.set("Bob") // Only name watchers trigger
104
+ user.age.update(age => age + 1) // Only age watchers trigger
105
+ ```
106
+
107
+ ## Coding Conventions
108
+
109
+ ### TypeScript Style
110
+ - Generic constraints: `T extends {}` to exclude null/undefined
111
+ - Use `const` for immutable values
112
+ - Function overloads for complex type scenarios
113
+ - JSDoc comments on all public APIs
114
+ - Pure function annotations: `/*#__PURE__*/` for tree-shaking
115
+
116
+ ### Naming Conventions
117
+ - Factory functions: `create*` prefix (createState, createStore, createComputed)
118
+ - Type predicates: `is*` prefix (isSignal, isState, isComputed)
119
+ - Type constants: `TYPE_*` format (TYPE_STATE, TYPE_STORE, TYPE_COMPUTED)
120
+ - Utility constants: UPPER_CASE (UNSET)
121
+
122
+ ### Error Handling
123
+ - Custom error classes in `src/errors.ts`
124
+ - Descriptive error messages with context
125
+ - Input validation at public API boundaries
126
+ - Use `UNSET` symbol for uninitialized/pending states
127
+
128
+ ## Performance Considerations
129
+
130
+ ### Optimization Patterns
131
+ - Dependency tracking with `Set<Watcher>` for O(1) operations
132
+ - Batched updates to minimize effect re-runs
133
+ - Shallow equality checks with `isEqual()` utility
134
+ - Memoization in computed signals prevents unnecessary recalculations
135
+ - Tree-shaking friendly with pure function annotations
136
+
137
+ ### Memory Management
138
+ - Automatic cleanup of watchers when effects are disposed
139
+ - WeakMap usage for object-to-signal mappings where appropriate
140
+ - AbortSignal integration for canceling async operations
141
+
142
+ ## Common Implementation Patterns
143
+
144
+ ### State Management
145
+ ```typescript
146
+ // Simple state
147
+ const loading = createState(false)
148
+ const error = createState('') // Empty string means no error
149
+
150
+ // Complex state with stores
151
+ const appState = createStore({
152
+ user: { id: 1, name: "Alice" },
153
+ settings: { theme: "dark", notifications: true }
154
+ })
155
+ ```
156
+
157
+ ### Derived State
158
+ ```typescript
159
+ // Reducer-like pattern for state machines
160
+ const appState = createComputed((currentState) => {
161
+ const event = events.get()
162
+ switch (currentState) {
163
+ case 'idle':
164
+ return event === 'start' ? 'loading' : 'idle'
165
+ case 'loading':
166
+ return event === 'success' ? 'loaded' : event === 'error' ? 'error' : 'loading'
167
+ case 'loaded':
168
+ return event === 'refresh' ? 'loading' : 'loaded'
169
+ case 'error':
170
+ return event === 'retry' ? 'loading' : 'error'
171
+ default:
172
+ return 'idle'
173
+ }
174
+ }, 'idle') // Initial state
175
+ ```
176
+
177
+ ### Async Operations
178
+ ```typescript
179
+ // Computed with async data fetching
180
+ const userData = createComputed(async (oldValue, abort) => {
181
+ const id = userId.get()
182
+ if (!id) return oldValue // Retain previous data when no ID
183
+ const response = await fetch(`/users/${id}`, { signal: abort })
184
+ if (!response.ok) throw new Error('Failed to fetch user')
185
+ return response.json()
186
+ })
187
+ ```
188
+
189
+ ### Error Handling
190
+ ```typescript
191
+ // Handle loading/error states
192
+ createEffect(() => {
193
+ match(resolve({ userData }), {
194
+ ok: ({ userData }) => console.log('User:', userData),
195
+ nil: () => console.log('Loading...'),
196
+ err: (errors) => console.error('Error:', errors[0])
197
+ })
198
+ })
199
+ ```
200
+
201
+ ### Helper Functions Usage
202
+ ```typescript
203
+ // Extract values from multiple signals
204
+ const result = resolve({ name, age, email })
205
+
206
+ // Pattern matching for discriminated unions
207
+ match(result, {
208
+ ok: (values) => console.log('Success:', values),
209
+ nil: () => console.log('Some values pending'),
210
+ err: (errors) => console.error('Errors:', errors)
211
+ })
212
+
213
+ // Object diffing
214
+ const changes = diff(oldUser, newUser)
215
+ console.log('Changed:', changes.change)
216
+ console.log('Added:', changes.add)
217
+ console.log('Removed:', changes.remove)
218
+ ```
219
+
220
+ ## Build and Development
221
+
222
+ ### Tools
223
+ - **Runtime**: Bun (also works with Node.js)
224
+ - **Build**: Bun build with TypeScript compilation
225
+ - **Testing**: Bun test runner
226
+ - **Linting**: Biome for code formatting and linting
227
+
228
+ ### Package Configuration
229
+ - ES modules only (`"type": "module"`)
230
+ - Dual exports: TypeScript source and compiled JavaScript
231
+ - Tree-shaking friendly with proper sideEffects configuration
232
+ - TypeScript declarations generated automatically
233
+
234
+ ## Key Design Decisions
235
+
236
+ ### Why Signals?
237
+ - Fine-grained reactivity (update only what changed)
238
+ - Explicit dependency tracking (no hidden dependencies)
239
+ - Composable and testable
240
+ - Framework agnostic
241
+
242
+ ### Why TypeScript-First?
243
+ - Better developer experience with autocomplete
244
+ - Compile-time error catching
245
+ - Self-documenting APIs
246
+ - Better refactoring support
247
+
248
+ ### Why Functional Approach?
249
+ - Predictable behavior
250
+ - Easier testing
251
+ - Better composition
252
+ - Minimal API surface
253
+
254
+ When working with this codebase, focus on maintaining the established patterns, ensuring type safety, and optimizing for performance while keeping the API simple and predictable.
package/.cursorrules ADDED
@@ -0,0 +1,54 @@
1
+ # Cause & Effect - Reactive State Management Library
2
+
3
+ ## Project Type
4
+ TypeScript/JavaScript reactive state management library using signals pattern
5
+
6
+ ## Core Concepts
7
+ - Signals: Base reactive primitives with .get() method
8
+ - State: createState() for primitives/simple values
9
+ - Store: createStore() for objects with reactive properties
10
+ - Computed: createComputed() for derived/memoized values
11
+ - Effects: createEffect() for side effects
12
+
13
+ ## Code Style
14
+ - Use const for immutable values
15
+ - Generic constraints: T extends {} (excludes null/undefined)
16
+ - Factory functions: create* prefix
17
+ - Type predicates: is* prefix
18
+ - Constants: TYPE_* or UPPER_CASE
19
+ - Pure functions marked with /*#__PURE__*/ comment
20
+
21
+ ## Key Patterns
22
+ - All signals have .get() method
23
+ - Mutable signals have .set(value) and .update(fn)
24
+ - Store properties auto-become reactive signals
25
+ - Computed callbacks receive oldValue as first parameter for reducer patterns
26
+ - Async computed and effect callbacks receive AbortSignal for async cancellation
27
+ - Use UNSET symbol for uninitialized states
28
+ - Batch updates for performance
29
+ - JSDoc comments on all public APIs
30
+
31
+ ## File Structure
32
+ - src/signal.ts - Base signal types
33
+ - src/state.ts - Mutable state signals
34
+ - src/store.ts - Object stores
35
+ - src/computed.ts - Computed signals
36
+ - src/effect.ts - Effect system
37
+ - src/system.ts - Core reactivity (watchers, batching)
38
+ - index.ts - Main exports
39
+
40
+ ## Error Handling
41
+ - Custom error classes in src/errors.ts
42
+ - Validate inputs, throw descriptive errors
43
+ - Support AbortSignal in async operations
44
+
45
+ ## Performance
46
+ - Use Set<Watcher> for subscriptions
47
+ - Shallow equality with isEqual() utility
48
+ - Tree-shaking friendly code
49
+ - Minimize effect re-runs
50
+
51
+ ## Build
52
+ - Bun build tool and runtime
53
+ - ES modules only
54
+ - TypeScript with declaration files
@@ -0,0 +1,132 @@
1
+ # Copilot Instructions for Cause & Effect
2
+
3
+ ## Project Overview
4
+
5
+ Cause & Effect is a reactive state management library for JavaScript/TypeScript that provides signals-based reactivity. The library is built with modern JavaScript/TypeScript and follows functional programming principles with a focus on performance and type safety.
6
+
7
+ ## Core Architecture
8
+
9
+ - **Signals**: Base reactive primitives with `.get()` method
10
+ - **State**: Mutable signals for primitive values (`createState()`)
11
+ - **Store**: Mutable signals for objects with reactive properties (`createStore()`)
12
+ - **Computed**: Derived read-only signals with memoization, reducer-like capabilities and async support (`createComputed()`)
13
+ - **Effects**: Side effect handlers that react to signal changes (`createEffect()`)
14
+
15
+ ## Key Files Structure
16
+
17
+ - `src/signal.ts` - Base signal types and utilities
18
+ - `src/state.ts` - Mutable state signals
19
+ - `src/store.ts` - Object stores with reactive properties
20
+ - `src/computed.ts` - Computed/derived signals
21
+ - `src/effect.ts` - Effect system
22
+ - `src/system.ts` - Core reactivity system (watchers, batching)
23
+ - `src/util.ts` - Utility functions and constants
24
+ - `index.ts` - Main export file
25
+
26
+ ## Coding Conventions
27
+
28
+ ### TypeScript Style
29
+ - Use `const` for immutable values, prefer immutability
30
+ - Generic constraints: `T extends {}` to exclude nullish values
31
+ - Function overloads for complex type inference
32
+ - Pure functions marked with `/*#__PURE__*/` for tree-shaking
33
+ - JSDoc comments for all public APIs
34
+
35
+ ### Naming Conventions
36
+ - Factory functions: `create*` (e.g., `createState`, `createStore`)
37
+ - Type predicates: `is*` (e.g., `isSignal`, `isState`)
38
+ - Constants: `TYPE_*` for type tags, `UPPER_CASE` for values
39
+ - Private variables: use descriptive names, no underscore prefix
40
+
41
+ ### Error Handling
42
+ - Custom error classes in `src/errors.ts`
43
+ - Validate inputs and throw descriptive errors
44
+ - Use `UNSET` symbol for uninitialized/pending states
45
+ - Support AbortSignal for cancellation in async operations
46
+
47
+ ### Performance Patterns
48
+ - Use `Set<Watcher>` for efficient subscription management
49
+ - Batch updates to minimize effect re-runs
50
+ - Memoization in computed signals
51
+ - Shallow equality checks with `isEqual()` utility
52
+ - Tree-shaking friendly with pure function annotations
53
+
54
+ ### API Design Principles
55
+ - All signals have `.get()` method for value access
56
+ - Mutable signals have `.set(value)` and `.update(fn)` methods
57
+ - Store properties are automatically reactive signals
58
+ - Effects receive AbortSignal for async cancellation
59
+ - Helper functions like `resolve()`, `match()`, `diff()` for ergonomics
60
+
61
+ ### Testing Patterns
62
+ - Use Bun test runner
63
+ - Test reactivity chains and dependency tracking
64
+ - Test async cancellation behavior
65
+ - Test error conditions and edge cases
66
+
67
+ ## Common Code Patterns
68
+
69
+ ### Creating Signals
70
+ ```typescript
71
+ // State for primitives
72
+ const count = createState(42)
73
+ const name = createState('Alice')
74
+ const actions = createState<'increment' | 'decrement'>('increment')
75
+
76
+ // Store for objects
77
+ const user = createStore({ name: 'Alice', age: 30 })
78
+
79
+ // Computed for derived values
80
+ const doubled = createComputed(() => count.get() * 2)
81
+
82
+ // Computed with reducer-like capabilities
83
+ const counter = createComputed((prev) => {
84
+ const action = actions.get()
85
+ return action === 'increment' ? prev + 1 : prev - 1
86
+ }, 0) // Initial value
87
+ ```
88
+
89
+ ### Reactivity
90
+ ```typescript
91
+ // Effects run when dependencies change
92
+ createEffect(() => {
93
+ console.log(`Count is ${count.get()}`)
94
+ })
95
+
96
+ // Async effects with cancellation
97
+ createEffect(async (abort) => {
98
+ const response = await fetch('/api', { signal: abort })
99
+ return response.json()
100
+ })
101
+
102
+ // Async computed with old value access
103
+ const userData = createComputed(async (prev, abort) => {
104
+ if (!userId.get()) return prev // Keep previous data if no user
105
+ const response = await fetch(`/users/${userId.get()}`, { signal: abort })
106
+ return response.json()
107
+ })
108
+ ```
109
+
110
+ ### Type Safety
111
+ ```typescript
112
+ // Use proper generic constraints
113
+ function createSignal<T extends {}>(value: T): Signal<T>
114
+
115
+ // Type predicates for runtime checks
116
+ function isSignal<T extends {}>(value: unknown): value is Signal<T>
117
+ ```
118
+
119
+ ## Build System
120
+ - Uses Bun as build tool and runtime
121
+ - TypeScript compilation with declaration files
122
+ - Minified production build
123
+ - ES modules only (`"type": "module"`)
124
+
125
+ ## When suggesting code:
126
+ 1. Follow the established patterns for signal creation and usage
127
+ 2. Use proper TypeScript types and generics
128
+ 3. Include JSDoc for public APIs
129
+ 4. Consider performance implications
130
+ 5. Handle errors appropriately
131
+ 6. Support async operations with AbortSignal when relevant
132
+ 7. Use the established utility functions (`UNSET`, `isEqual`, etc.)