@ersbeth/picoflow 2.0.1 → 2.0.3

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 (154) hide show
  1. package/README.md +10 -0
  2. package/SKILL.md +106 -0
  3. package/dist/types/api/base/flowSubscribable.d.ts +3 -3
  4. package/dist/types/api/base/flowSubscribable.d.ts.map +1 -1
  5. package/package.json +5 -1
  6. package/.gitlab-ci.yml +0 -24
  7. package/.vscode/settings.json +0 -5
  8. package/CHANGELOG.md +0 -94
  9. package/biome.json +0 -47
  10. package/docs/.vitepress/config.mts +0 -145
  11. package/docs/api/functions/array.md +0 -35
  12. package/docs/api/functions/constant.md +0 -33
  13. package/docs/api/functions/constantAsync.md +0 -69
  14. package/docs/api/functions/derivation.md +0 -34
  15. package/docs/api/functions/derivationAsync.md +0 -34
  16. package/docs/api/functions/from.md +0 -129
  17. package/docs/api/functions/isDisposable.md +0 -27
  18. package/docs/api/functions/map.md +0 -36
  19. package/docs/api/functions/signal.md +0 -21
  20. package/docs/api/functions/state.md +0 -67
  21. package/docs/api/functions/stateAsync.md +0 -69
  22. package/docs/api/functions/subscribe.md +0 -40
  23. package/docs/api/functions/writableDerivation.md +0 -33
  24. package/docs/api/functions/writableDerivationAsync.md +0 -34
  25. package/docs/api/index.md +0 -61
  26. package/docs/api/interfaces/FlowArray.md +0 -439
  27. package/docs/api/interfaces/FlowConstant.md +0 -220
  28. package/docs/api/interfaces/FlowConstantAsync.md +0 -221
  29. package/docs/api/interfaces/FlowDerivation.md +0 -241
  30. package/docs/api/interfaces/FlowDerivationAsync.md +0 -242
  31. package/docs/api/interfaces/FlowDisposable.md +0 -59
  32. package/docs/api/interfaces/FlowEffect.md +0 -64
  33. package/docs/api/interfaces/FlowMap.md +0 -374
  34. package/docs/api/interfaces/FlowObservable.md +0 -155
  35. package/docs/api/interfaces/FlowSignal.md +0 -156
  36. package/docs/api/interfaces/FlowState.md +0 -269
  37. package/docs/api/interfaces/FlowStateAsync.md +0 -268
  38. package/docs/api/interfaces/FlowSubscribable.md +0 -55
  39. package/docs/api/interfaces/FlowTracker.md +0 -61
  40. package/docs/api/interfaces/FlowValue.md +0 -222
  41. package/docs/api/interfaces/FlowWritableDerivation.md +0 -292
  42. package/docs/api/interfaces/FlowWritableDerivationAsync.md +0 -293
  43. package/docs/api/type-aliases/DerivationFunction.md +0 -28
  44. package/docs/api/type-aliases/DerivationFunctionAsync.md +0 -28
  45. package/docs/api/type-aliases/FlowArrayAction.md +0 -60
  46. package/docs/api/type-aliases/FlowDataTracker.md +0 -33
  47. package/docs/api/type-aliases/FlowMapAction.md +0 -48
  48. package/docs/api/type-aliases/FlowOnDataListener.md +0 -33
  49. package/docs/api/type-aliases/FlowOnErrorListener.md +0 -27
  50. package/docs/api/type-aliases/FlowOnPendingListener.md +0 -21
  51. package/docs/api/type-aliases/FlowReadonly.md +0 -22
  52. package/docs/api/type-aliases/InitFunction.md +0 -21
  53. package/docs/api/type-aliases/InitFunctionAsync.md +0 -21
  54. package/docs/api/type-aliases/NotPromise.md +0 -21
  55. package/docs/api/type-aliases/UpdateFunction.md +0 -27
  56. package/docs/api/type-aliases/UpdateFunctionAsync.md +0 -27
  57. package/docs/api/typedoc-sidebar.json +0 -65
  58. package/docs/examples/examples.md +0 -2311
  59. package/docs/examples/patterns.md +0 -649
  60. package/docs/guide/advanced/architecture.md +0 -1234
  61. package/docs/guide/advanced/disposal.md +0 -426
  62. package/docs/guide/advanced/migration-v1.md +0 -464
  63. package/docs/guide/advanced/migration-v2.md +0 -204
  64. package/docs/guide/advanced/solidjs.md +0 -135
  65. package/docs/guide/introduction/concepts.md +0 -57
  66. package/docs/guide/introduction/conventions.md +0 -30
  67. package/docs/guide/introduction/getting-started.md +0 -139
  68. package/docs/guide/introduction/lifecycle.md +0 -368
  69. package/docs/guide/primitives/array.md +0 -286
  70. package/docs/guide/primitives/constant.md +0 -207
  71. package/docs/guide/primitives/derivations.md +0 -281
  72. package/docs/guide/primitives/effects.md +0 -372
  73. package/docs/guide/primitives/map.md +0 -265
  74. package/docs/guide/primitives/overview.md +0 -92
  75. package/docs/guide/primitives/signal.md +0 -222
  76. package/docs/guide/primitives/state.md +0 -272
  77. package/docs/index.md +0 -47
  78. package/docs/public/logo.svg +0 -1
  79. package/src/api/base/flowDisposable.ts +0 -44
  80. package/src/api/base/flowObservable.ts +0 -28
  81. package/src/api/base/flowSubscribable.ts +0 -87
  82. package/src/api/base/flowTracker.ts +0 -7
  83. package/src/api/base/index.ts +0 -4
  84. package/src/api/index.ts +0 -2
  85. package/src/api/nodes/async/flowConstantAsync.ts +0 -36
  86. package/src/api/nodes/async/flowDerivationAsync.ts +0 -42
  87. package/src/api/nodes/async/flowStateAsync.ts +0 -47
  88. package/src/api/nodes/async/flowWritableDerivationAsync.ts +0 -33
  89. package/src/api/nodes/async/index.ts +0 -4
  90. package/src/api/nodes/collections/flowArray.ts +0 -155
  91. package/src/api/nodes/collections/flowMap.ts +0 -115
  92. package/src/api/nodes/collections/index.ts +0 -2
  93. package/src/api/nodes/flowEffect.ts +0 -42
  94. package/src/api/nodes/flowSignal.ts +0 -28
  95. package/src/api/nodes/flowValue.ts +0 -37
  96. package/src/api/nodes/index.ts +0 -7
  97. package/src/api/nodes/sync/flowConstant.ts +0 -33
  98. package/src/api/nodes/sync/flowDerivation.ts +0 -41
  99. package/src/api/nodes/sync/flowState.ts +0 -45
  100. package/src/api/nodes/sync/flowWritableDerivation.ts +0 -31
  101. package/src/api/nodes/sync/index.ts +0 -4
  102. package/src/api/nodes/utils.ts +0 -24
  103. package/src/base/disposable.ts +0 -18
  104. package/src/base/executionStack.ts +0 -42
  105. package/src/base/index.ts +0 -5
  106. package/src/base/node.ts +0 -98
  107. package/src/base/observable.ts +0 -92
  108. package/src/base/observer.ts +0 -51
  109. package/src/converters/index.ts +0 -1
  110. package/src/converters/solid.ts +0 -109
  111. package/src/index.ts +0 -2
  112. package/src/nodes/arrayNode.ts +0 -180
  113. package/src/nodes/effectNode.ts +0 -58
  114. package/src/nodes/index.ts +0 -7
  115. package/src/nodes/mapNode.ts +0 -125
  116. package/src/nodes/signalNode.ts +0 -19
  117. package/src/nodes/valueAsyncNode.ts +0 -85
  118. package/src/nodes/valueNode.ts +0 -148
  119. package/src/nodes/valueSyncNode.ts +0 -125
  120. package/src/schedulers/asyncResolver.ts +0 -78
  121. package/src/schedulers/asyncScheduler.ts +0 -66
  122. package/src/schedulers/index.ts +0 -4
  123. package/src/schedulers/pendingError.ts +0 -13
  124. package/src/schedulers/scheduler.ts +0 -9
  125. package/src/schedulers/syncResolver.ts +0 -69
  126. package/src/schedulers/syncScheduler.ts +0 -55
  127. package/test/base/pendingError.test.ts +0 -67
  128. package/test/converters/solid.derivation.browser.test.tsx +0 -69
  129. package/test/converters/solid.node.test.ts +0 -654
  130. package/test/converters/solid.state.browser.test.tsx +0 -1592
  131. package/test/reactivity/flowSignal.test.ts +0 -226
  132. package/test/reactivity/nodes/async/asyncScheduler/asyncResolver.test.ts +0 -593
  133. package/test/reactivity/nodes/async/asyncScheduler/asyncScheduler.test.ts +0 -317
  134. package/test/reactivity/nodes/async/flowConstantAsync.test.ts +0 -652
  135. package/test/reactivity/nodes/async/flowDerivation.test.ts +0 -898
  136. package/test/reactivity/nodes/async/flowDerivationAsync.test.ts +0 -1716
  137. package/test/reactivity/nodes/async/flowStateAsync.test.ts +0 -708
  138. package/test/reactivity/nodes/async/flowWritableDerivationAsync.test.ts +0 -614
  139. package/test/reactivity/nodes/collections/flowArray.asyncStates.test.ts +0 -1289
  140. package/test/reactivity/nodes/collections/flowArray.scalars.test.ts +0 -961
  141. package/test/reactivity/nodes/collections/flowArray.states.test.ts +0 -1035
  142. package/test/reactivity/nodes/collections/flowMap.asyncStates.test.ts +0 -960
  143. package/test/reactivity/nodes/collections/flowMap.scalars.test.ts +0 -775
  144. package/test/reactivity/nodes/collections/flowMap.states.test.ts +0 -958
  145. package/test/reactivity/nodes/sync/flowConstant.test.ts +0 -377
  146. package/test/reactivity/nodes/sync/flowDerivation.test.ts +0 -896
  147. package/test/reactivity/nodes/sync/flowState.test.ts +0 -341
  148. package/test/reactivity/nodes/sync/flowWritableDerivation.test.ts +0 -603
  149. package/test/vitest.d.ts +0 -10
  150. package/tsconfig.json +0 -37
  151. package/typedoc.json +0 -37
  152. package/vite.config.ts +0 -31
  153. package/vitest.browser.config.ts +0 -21
  154. package/vitest.config.ts +0 -17
@@ -1,92 +0,0 @@
1
- # Primitives Overview
2
-
3
- PicoFlow distinguishes between two main concepts:
4
- - **Primitives**: Reactive values (state, derivation, signal, collections)
5
- - **Side Effects**: Created by subscribing to primitives
6
-
7
- ::: tip
8
- Looking for naming conventions? Check out the **[Conventions](/guide/introduction/conventions)** guide to learn about the `$` prefix.
9
- :::
10
-
11
- ## Primitives
12
-
13
- PicoFlow provides several reactive primitives, each with specific capabilities. Primitive may exist in synchrone and asynchrone flavours.
14
-
15
- | Primitive | Sync | Async | Purpose |
16
- |-----------|:----:|:-----:|---------|
17
- | **Signal** | ✓ | - | Event notifications without values |
18
- | **State** | ✓ | ✓ | Mutable reactive values |
19
- | **Constant** | ✓ | ✓ | Immutable reactive values |
20
- | **Derivation** | ✓ | ✓ | Computed values from dependencies |
21
- | **Writable Derivation** | ✓ | ✓ | Derivations with custom setters |
22
- | **Array** | ✓ | - | Reactive lists with fine-grained tracking |
23
- | **Map** | ✓ | - | Reactive dictionaries with fine-grained tracking |
24
-
25
- ## Side Effects
26
-
27
- You may subscribe to any primitive to perform side effects on change. When you subscribe, you get a `FlowEffect` that can be disposed.
28
-
29
-
30
- When reacting to a single primitive, use the `.subscribe()` method:
31
-
32
- ```typescript
33
- const effect = $count.subscribe((value) => {
34
- console.log('Count is:', value)
35
- })
36
-
37
- // Later: dispose the effect
38
- effect.dispose()
39
- ```
40
-
41
-
42
- When combining multiple primitives, use the `subscribe()` function:
43
-
44
- ```typescript
45
- const effect = subscribe(
46
- (t) => ({ count: $count.get(t), name: $name.get(t) }),
47
- (data) => {
48
- console.log('Count:', data.count, 'Name:', data.name)
49
- }
50
- )
51
- ```
52
-
53
- ### Use cases
54
-
55
- - Logging and debugging
56
- - DOM manipulation
57
- - API calls and data persistence
58
- - Analytics and tracking
59
- - Side effects that don't produce values
60
-
61
- ### Key features
62
-
63
- - **Eager execution**: Runs immediately when created
64
- - **Automatic re-execution**: Runs whenever dependencies change
65
- - **Returns FlowEffect**: Can be disposed to stop the effect
66
- - **Error handling**: Optional `onError` and `onPending` callbacks
67
-
68
- ## Derivations vs Side Effects
69
-
70
- What's the difference between a derivation and a side effect? Both react to changes, but they serve different purposes:
71
-
72
- | Feature | Derivation | Side Effect (subscribe) |
73
- |---------|-----------|---------|
74
- | Purpose | **Compute a value** | **Perform side effects** |
75
- | Returns | A reactive value | FlowEffect (disposable) |
76
- | Should be pure? | ✅ Yes | ❌ No |
77
- | Side effects allowed? | ❌ No | ✅ Yes |
78
- | When it runs | Lazily (when accessed) | Immediately and on changes |
79
- | How to create | `derivation()` | `subscribe()` or `.subscribe()` |
80
- | Use case | Calculate totals, filter lists | Update DOM, save data, log |
81
-
82
- ```mermaid
83
- flowchart LR
84
- A[Reactive Values] --> B[Derivation]
85
- A --> C[Effect]
86
- B --> D[New Computed Value]
87
- C --> E[Side Effects<br/>DOM, API, Storage]
88
- D -.-> F[Can be used by<br/>other derivations]
89
- D -.-> G[Can be used by<br/>effects]
90
- ```
91
-
92
- **Key insight:** Use derivations for **calculations**, use effects for **actions**.
@@ -1,222 +0,0 @@
1
- # Signals
2
-
3
- A **signal** is a reactive primitive that represents an event or notification. Unlike state or derivations, signals don't hold values - they simply notify watchers that something has happened.
4
-
5
- ## When to Use Signals
6
-
7
- Use signals when you need to:
8
-
9
- - ✅ Trigger manual refreshes or updates
10
- - ✅ Coordinate actions between independent parts of your app
11
- - ✅ Signal that an event has occurred without sending data
12
- - ✅ Create synchronization points in reactive flows
13
-
14
- Don't use signals when:
15
-
16
- - ❌ You need to pass data (use state instead)
17
-
18
- ## Creating Signals
19
-
20
- Creating a signal is straightforward:
21
-
22
- ```typescript
23
- import { signal } from '@ersbeth/picoflow'
24
-
25
- const $refreshTrigger = signal()
26
- const $saveComplete = signal()
27
- const $userLoggedOut = signal()
28
- ```
29
-
30
- Signals don't take any parameters since they don't hold values.
31
-
32
- ## Using Signals
33
-
34
- Signals have three main operations: watching, triggering, and disposing.
35
-
36
- ### Watch
37
-
38
- Inside a subscription or derivation, use `.watch(t)` to track a signal:
39
-
40
- ```typescript
41
- import { signal, subscribe } from '@ersbeth/picoflow'
42
-
43
- const $refresh = signal()
44
-
45
- subscribe(
46
- (t) => { $refresh.watch(t) },
47
- () => { console.log('Refresh triggered!') }
48
- )
49
-
50
- $refresh.trigger() // Logs: "Refresh triggered!"
51
- $refresh.trigger() // Logs: "Refresh triggered!" again
52
- ```
53
-
54
- Every time the signal is triggered, the subscription re-executes.
55
-
56
- ### Trigger
57
-
58
- Call `.trigger()` to notify all watchers:
59
-
60
- ```typescript
61
- const $notification = signal()
62
-
63
- // Setup a subscription that watches the signal
64
- subscribe(
65
- (t) => { $notification.watch(t) },
66
- () => { showNotification('New activity!') }
67
- )
68
-
69
- // Trigger from anywhere
70
- function onNewMessage() {
71
- $notification.trigger()
72
- }
73
-
74
- function onNewLike() {
75
- $notification.trigger()
76
- }
77
- ```
78
-
79
- ### Dispose
80
-
81
- Signals can be disposed to free resources and prevent memory leaks:
82
-
83
- ```typescript
84
- const $mySignal = signal()
85
-
86
- // Use the signal
87
- subscribe(
88
- (t) => { $mySignal.watch(t) },
89
- () => { console.log('Signal triggered!') }
90
- )
91
-
92
- // Later, clean up
93
- $mySignal.dispose()
94
-
95
- // Subsequent operations will throw
96
- $mySignal.trigger() // Error: Primitive is disposed
97
- ```
98
-
99
- ## Lifecyle
100
-
101
- When you create a subscription that watches a signal:
102
-
103
- 1. **Registration**: The signal registers the subscription as a watcher
104
- 2. **Waiting**: The signal waits for `.trigger()` to be called
105
- 3. **Notification**: When triggered, all watching subscriptions are scheduled to run
106
- 4. **Re-execution**: Each watching subscription re-executes its function
107
-
108
- ```mermaid
109
- sequenceDiagram
110
- participant User
111
- participant Signal as $refresh (Signal)
112
- participant Subscription
113
-
114
- Note over User,Subscription: 1. Setup Phase
115
- User->>Subscription: Create subscription
116
- activate Subscription
117
- Subscription->>Signal: watch(t)
118
- Note over Signal: Register Subscription as watcher
119
- Signal-->>Subscription: Registered
120
- Subscription->>Subscription: Execute function
121
- Note over Subscription: Log: "Ready to refresh"
122
- deactivate Subscription
123
-
124
- Note over User,Subscription: 2. Trigger Phase
125
- User->>Signal: trigger()
126
- activate Signal
127
- Note over Signal: Notify all watchers
128
- Signal->>Subscription: Schedule execution
129
- deactivate Signal
130
-
131
- activate Subscription
132
- Note over Subscription: Re-execute function
133
- Subscription->>Subscription: Perform refresh
134
- Note over Subscription: Log: "Refreshing..."
135
- deactivate Subscription
136
- ```
137
-
138
- ## Best Practices
139
-
140
- ### Name Signals Clearly
141
-
142
- Use names that describe the event, not the action:
143
-
144
- ```typescript
145
- // ✅ Good - describes the event
146
- const $refreshRequested = signal()
147
- const $saveComplete = signal()
148
- const $validationFailed = signal()
149
-
150
- // ❌ Bad - describes the handler
151
- const $doRefresh = signal()
152
- const $handleSave = signal()
153
- ```
154
-
155
- ## Common Pitfalls
156
-
157
- ### Using Signals When State Would Be Better
158
-
159
- **Problem**: Using a signal when you actually need to track a value.
160
-
161
- ```typescript
162
- // ❌ Bad - should use state
163
- const $userUpdated = signal()
164
- let currentUser: User | null = null
165
-
166
- subscribe(
167
- (t) => {
168
- $userUpdated.watch(t)
169
- return currentUser
170
- },
171
- (user) => {
172
- if (user) {
173
- renderUser(user)
174
- }
175
- }
176
- )
177
-
178
- function setUser(user: User) {
179
- currentUser = user
180
- $userUpdated.trigger()
181
- }
182
- ```
183
-
184
- **Solution**: Use state to hold the value:
185
-
186
- ```typescript
187
- // ✅ Good - state holds the value
188
- const $currentUser = state<User | null>(null)
189
-
190
- $currentUser.subscribe((user) => {
191
- if (user) {
192
- renderUser(user)
193
- }
194
- })
195
-
196
- function setUser(user: User) {
197
- $currentUser.set(user)
198
- }
199
- ```
200
-
201
- ### Over-Triggering
202
-
203
- **Problem**: Triggering a signal in a loop or too frequently.
204
-
205
- ```typescript
206
- // ❌ Bad - triggers in a loop
207
- for (let i = 0; i < 100; i++) {
208
- processItem(i)
209
- $itemProcessed.trigger() // 100 triggers!
210
- }
211
- ```
212
-
213
- **Solution**: Trigger once after the batch:
214
-
215
- ```typescript
216
- // ✅ Good - single trigger after batch
217
- for (let i = 0; i < 100; i++) {
218
- processItem(i)
219
- }
220
- $batchProcessed.trigger() // 1 trigger
221
- ```
222
-
@@ -1,272 +0,0 @@
1
- # State
2
-
3
- **State** is a reactive primitive that holds a mutable value.
4
-
5
- ### Key Characteristics
6
-
7
- - **Mutable**: Can be updated with `.set()`
8
- - **Reactive**: Automatically notifies watchers on changes
9
- - **Equality checking**: Only notifies if the new value differs from the current value
10
-
11
- ## When to Use State
12
-
13
- Use state when you need to:
14
-
15
- - ✅ Track values that change over time
16
-
17
- Don't use state when:
18
-
19
- - ❌ The value never changes (use constants instead)
20
- - ❌ The value is derived from other reactive values (use derivations instead)
21
- - ❌ You're tracking events without data (use signals instead)
22
-
23
- ## Creating State
24
-
25
- Creating state is straightforward:
26
-
27
- ```typescript
28
- import { state } from '@ersbeth/picoflow'
29
-
30
- const $count = state(0)
31
- const $name = state('Alice')
32
- const $user = state({ id: 1, name: 'Bob', age: 25 })
33
- const $items = state<string[]>([])
34
- ```
35
-
36
- The value you pass becomes the initial value. It can be any type: numbers, strings, objects, arrays, etc.
37
-
38
- ## Using State
39
-
40
- State provides methods for reading, updating, and disposing.
41
-
42
- ### Get
43
-
44
- Inside a subscription or derivation, use `.get(t)` to read the value and create a dependency:
45
-
46
- ```typescript
47
- const $count = state(0)
48
- const $double = derivation((t) => $count.get(t) * 2)
49
-
50
- $double.subscribe((value) => {
51
- console.log('Double is:', value)
52
- })
53
-
54
- $count.set(1) // Console logs: "Double is: 2"
55
- $count.set(2) // Console logs: "Double is: 4"
56
- ```
57
-
58
- ### Pick
59
-
60
- Use `.pick()` when you want the current value without creating a dependency:
61
-
62
- ```typescript
63
- const $count = state(0)
64
-
65
- // Read without tracking (e.g., outside a subscription)
66
- const currentValue = await $count.pick()
67
- console.log(currentValue) // 0
68
- ```
69
-
70
- ### Update
71
-
72
- Pass the new value directly to `.set()`:
73
-
74
- ```typescript
75
- const $count = state(0)
76
-
77
- $count.set(5) // Set to 5
78
- $count.set(10) // Set to 10
79
- ```
80
-
81
- Pass a function that receives the current value and returns the new value:
82
-
83
- ```typescript
84
- const $count = state(0)
85
-
86
- $count.set(n => n + 1) // Increment by 1
87
- $count.set(n => n * 2) // Double the value
88
- $count.set(n => Math.max(0, n - 1)) // Decrement, min 0
89
- ```
90
-
91
- **When to use updater functions:**
92
- - When the new value depends on the current value
93
- - To guarantee you're working with the latest value
94
- - For atomic updates (important in concurrent scenarios)
95
-
96
- ### Dispose
97
-
98
- State can be disposed to free resources and prevent memory leaks:
99
-
100
- ```typescript
101
- const $count = state(0)
102
-
103
- // Use the state
104
- $count.subscribe((value) => {
105
- console.log(value)
106
- })
107
-
108
- // Later, clean up
109
- $count.dispose()
110
-
111
- // Subsequent operations will throw
112
- $count.set(5) // Error: Primitive is disposed
113
- ```
114
-
115
- ## Lifecycle
116
-
117
- When you update state, a specific flow occurs to ensure efficiency and consistency.
118
-
119
- ```mermaid
120
- sequenceDiagram
121
- participant User
122
- participant $count as $count (State)
123
- participant $double as $double (Derivation)
124
- participant Subscription
125
-
126
- User->>$count: set(n => n + 1)
127
- activate $count
128
- Note over $count: Get current: 0<br/>Compute new: 0 + 1 = 1
129
- Note over $count: Check: 1 !== 0 ✓
130
- Note over $count: Update: 0 → 1
131
-
132
- $count->>$double: Notify (mark dirty)
133
- activate $double
134
- Note over $double: Mark as dirty<br/>NO recompute yet
135
- deactivate $double
136
-
137
- $count->>Subscription: Schedule execution
138
- deactivate $count
139
-
140
- activate Subscription
141
- Note over Subscription: Execute function
142
-
143
- Subscription->>$count: get(t)
144
- activate $count
145
- $count-->>Subscription: 1
146
- deactivate $count
147
-
148
- Subscription->>$double: get(t)
149
- activate $double
150
- Note over $double: Dirty? YES<br/>Recompute now
151
-
152
- $double->>$count: get(t)
153
- activate $count
154
- $count-->>$double: 1
155
- deactivate $count
156
-
157
- Note over $double: Compute: 1 * 2 = 2
158
- $double-->>Subscription: 2
159
- deactivate $double
160
-
161
- Note over Subscription: Log: "Count: 1, Double: 2"
162
- deactivate Subscription
163
- ```
164
-
165
- **How it works:**
166
-
167
- 1. **Compute new value**: If updater function, call it with current value
168
- 2. **Equality check**: Compare new value with current using `===`
169
- 3. **Early return**: If equal, skip notification entirely
170
- 4. **Update value**: Store the new value
171
- 5. **Notify dependents**:
172
- - Derivations are marked dirty (not recomputed yet)
173
- - Subscriptions are scheduled for execution
174
- 6. **Pull-based recompute**: When subscriptions run and read derivations, they recompute
175
-
176
- ### Equality Check Example
177
-
178
- State only notifies when the value actually changes:
179
-
180
- ```mermaid
181
- sequenceDiagram
182
- participant User
183
- participant $count as $count (State)
184
- participant Subscription
185
-
186
- Note over User,Subscription: Initial state: $count = 5
187
-
188
- User->>$count: set(5)
189
- activate $count
190
- Note over $count: Check: 5 === 5?<br/>YES - no change
191
- Note over $count: Skip notification
192
- deactivate $count
193
- Note over Subscription: Subscription does NOT run
194
-
195
- User->>$count: set(10)
196
- activate $count
197
- Note over $count: Check: 10 === 5?<br/>NO - value changed
198
- Note over $count: Update: 5 → 10
199
- $count->>Subscription: Notify & schedule
200
- deactivate $count
201
-
202
- activate Subscription
203
- Note over Subscription: Subscription runs
204
- deactivate Subscription
205
- ```
206
-
207
- ## Best Practices
208
-
209
- ### Use Updater Functions for Computed Updates
210
-
211
- Always use updater functions when the new value depends on the current value:
212
-
213
- ```typescript
214
- // ❌ Bad - race condition risk
215
- const current = $count.pick()
216
- $count.set(current + 1)
217
-
218
- // ✅ Good - atomic update
219
- $count.set(n => n + 1)
220
- ```
221
-
222
- ### Immutable Updates for Objects
223
-
224
- Create new objects instead of mutating:
225
-
226
- ```typescript
227
- // ❌ Bad - mutation
228
- $user.set(user => {
229
- user.name = 'Bob' // Mutates the object!
230
- return user
231
- })
232
-
233
- // ✅ Good - immutable
234
- $user.set(user => ({
235
- ...user,
236
- name: 'Bob'
237
- }))
238
- ```
239
-
240
- ### Granular State Over Large Objects
241
-
242
- Prefer multiple small state values when fields update independently:
243
-
244
- ```typescript
245
- // ❌ Less efficient - entire object tracked
246
- const $form = state({
247
- email: '',
248
- password: '',
249
- confirmPassword: ''
250
- })
251
-
252
- // ✅ More efficient - independent tracking
253
- const form = {
254
- $email: state(''),
255
- $password = state(''),
256
- $confirmPassword = state(''),
257
- }
258
- ```
259
-
260
-
261
- Use the `$` prefix for all reactive values:
262
-
263
- ```typescript
264
- // ✅ Good - clear reactive naming
265
- const $count = state(0)
266
- const $user = state({...})
267
- const $isLoading = state(false)
268
-
269
- // ❌ Less clear
270
- const count = state(0)
271
- const user = state({...})
272
- ```
package/docs/index.md DELETED
@@ -1,47 +0,0 @@
1
- ---
2
- layout: home
3
-
4
- hero:
5
- name: PicoFlow
6
- text: Lightweight Reactive Dataflow
7
- tagline: Fine-grained reactive primitives for TypeScript with explicit dependency tracking
8
- image:
9
- src: /logo.svg
10
- alt: PicoFlow
11
- actions:
12
- - theme: brand
13
- text: Get Started
14
- link: /guide/introduction/getting-started
15
- - theme: alt
16
- text: API Reference
17
- link: /api/
18
- - theme: alt
19
- text: View on GitLab
20
- link: https://gitlab.com/ersbeth-web/picoflow
21
-
22
- features:
23
- - icon: 🎯
24
- title: Explicit Tracking
25
- details: Full control over reactive dependencies with TrackingContext. Choose between reactive reads with get(t) and non-reactive reads with pick().
26
-
27
- - icon: ⚡
28
- title: Lazy Evaluation
29
- details: Derivations compute only when accessed, not on construction. Smart caching ensures computations run only when dependencies change.
30
-
31
- - icon: 🔧
32
- title: Fine-grained Reactivity
33
- details: Track specific operations on collections with reactive arrays and maps. Subscribe to additions, deletions, or entire collection changes.
34
-
35
- - icon: 🧹
36
- title: Resource Management
37
- details: Automatic cleanup with dispose patterns. Manage async resources, streams, and effects with confidence.
38
-
39
- - icon: 📦
40
- title: Lightweight
41
- details: Minimal bundle size with zero dependencies. Tree-shakeable exports ensure you only ship what you use.
42
-
43
- - icon: 🔄
44
- title: SolidJS Integration
45
- details: Seamless integration with SolidJS through built-in converters. Use PicoFlow primitives directly in Solid components.
46
- ---
47
-
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M176 120C189.3 120 200 130.7 200 144C200 157.3 189.3 168 176 168C162.7 168 152 157.3 152 144C152 130.7 162.7 120 176 120zM208.4 217.2C236.4 204.8 256 176.7 256 144C256 99.8 220.2 64 176 64C131.8 64 96 99.8 96 144C96 176.8 115.7 205 144 217.3L144 422.6C115.7 435 96 463.2 96 496C96 540.2 131.8 576 176 576C220.2 576 256 540.2 256 496C256 463.2 236.3 435 208 422.7L208 336.1C234.7 356.2 268 368.1 304 368.1L390.7 368.1C403 396.4 431.2 416.1 464 416.1C508.2 416.1 544 380.3 544 336.1C544 291.9 508.2 256.1 464 256.1C431.2 256.1 403 275.8 390.7 304.1L304 304C254.1 304 213 265.9 208.4 217.2zM176 472C189.3 472 200 482.7 200 496C200 509.3 189.3 520 176 520C162.7 520 152 509.3 152 496C152 482.7 162.7 472 176 472zM440 336C440 322.7 450.7 312 464 312C477.3 312 488 322.7 488 336C488 349.3 477.3 360 464 360C450.7 360 440 349.3 440 336z" fill="#5672cd"/></svg>
@@ -1,44 +0,0 @@
1
- /**
2
- * Contract for resources that can be explicitly released to prevent memory leaks.
3
- *
4
- * All reactive primitives in PicoFlow implement this interface, allowing manual cleanup of subscriptions
5
- * and dependencies. Once disposed, the resource cannot be reused and will throw errors on further access.
6
- * Call `dispose()` when you no longer need a reactive primitive to free its resources.
7
- *
8
- * @public
9
- */
10
- export interface FlowDisposable {
11
- /**
12
- * Indicates whether this resource has been disposed.
13
- *
14
- * Returns `true` if `dispose()` has been called, `false` otherwise. Once disposed,
15
- * the resource is no longer usable and any operations on it will throw errors.
16
- */
17
- get disposed(): boolean;
18
-
19
- /**
20
- * Releases all resources held by this disposable object.
21
- *
22
- * Cleans up all subscriptions, dependencies, and internal state. After calling this method,
23
- * the resource enters a disposed state and cannot be reused. Any subsequent operations will
24
- * throw errors. This method should be called when you no longer need the reactive primitive
25
- * to prevent memory leaks.
26
- */
27
- dispose(): void;
28
- }
29
-
30
- /**
31
- * Type guard to check if an object implements the FlowDisposable interface.
32
- *
33
- * Useful for conditionally disposing objects that may or may not be disposable,
34
- * such as when cleaning up mixed collections of values. Returns true if the object
35
- * has a `dispose` method, providing type-safe access to disposal functionality.
36
- *
37
- * @param obj - The object to check
38
- * @returns True if the object implements FlowDisposable, false otherwise
39
- *
40
- * @public
41
- */
42
- export function isDisposable(obj: unknown): obj is FlowDisposable {
43
- return obj !== null && obj !== undefined && typeof (obj as FlowDisposable).dispose === "function";
44
- }
@@ -1,28 +0,0 @@
1
- import type { FlowDisposable } from "./flowDisposable";
2
- import type { FlowSubscribable } from "./flowSubscribable";
3
- import type { FlowTracker } from "./flowTracker";
4
-
5
- /**
6
- * Contract for reactive primitives that emit changes and notify their dependents in the reactivity graph.
7
- * @public
8
- */
9
- export interface FlowObservable<T> extends FlowDisposable, FlowSubscribable<T> {
10
- /**
11
- * Establishes a reactive dependency without reading the value.
12
- *
13
- * Registers this observable as a dependency of the given tracker, allowing it to be notified
14
- * when changes occur. This enables dependency tracking without accessing the actual value.
15
- *
16
- * @param tracker - Tracker that records this observable as a dependency
17
- */
18
- watch(tracker: FlowTracker): void;
19
-
20
- /**
21
- * Manually triggers notifications to all dependents.
22
- *
23
- * Forces all dependents of this observable to be notified and re-executed, even if the
24
- * value hasn't actually changed. Useful for imperatively triggering updates when the
25
- * reactive system cannot detect changes automatically.
26
- */
27
- trigger(): void;
28
- }