@ersbeth/picoflow 2.0.2 → 2.1.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 (244) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/SKILL.md +106 -0
  4. package/dist/picoflow.js +204 -60
  5. package/dist/types/api/base/flowObservable.d.ts +3 -3
  6. package/dist/types/api/base/flowObservable.d.ts.map +1 -1
  7. package/dist/types/api/base/flowSubscribable.d.ts +2 -2
  8. package/dist/types/api/base/flowSubscribable.d.ts.map +1 -1
  9. package/dist/types/api/base/flowTracker.d.ts +1 -1
  10. package/dist/types/api/base/flowTracker.d.ts.map +1 -1
  11. package/dist/types/api/base/index.d.ts +4 -4
  12. package/dist/types/api/base/index.d.ts.map +1 -1
  13. package/dist/types/api/index.d.ts +2 -2
  14. package/dist/types/api/index.d.ts.map +1 -1
  15. package/dist/types/api/nodes/async/flowConstantAsync.d.ts +1 -1
  16. package/dist/types/api/nodes/async/flowConstantAsync.d.ts.map +1 -1
  17. package/dist/types/api/nodes/async/flowDerivationAsync.d.ts +3 -3
  18. package/dist/types/api/nodes/async/flowDerivationAsync.d.ts.map +1 -1
  19. package/dist/types/api/nodes/async/flowStateAsync.d.ts +3 -3
  20. package/dist/types/api/nodes/async/flowStateAsync.d.ts.map +1 -1
  21. package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts +2 -2
  22. package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts.map +1 -1
  23. package/dist/types/api/nodes/async/index.d.ts +4 -4
  24. package/dist/types/api/nodes/async/index.d.ts.map +1 -1
  25. package/dist/types/api/nodes/collections/flowArray.d.ts +7 -4
  26. package/dist/types/api/nodes/collections/flowArray.d.ts.map +1 -1
  27. package/dist/types/api/nodes/collections/flowMap.d.ts +3 -2
  28. package/dist/types/api/nodes/collections/flowMap.d.ts.map +1 -1
  29. package/dist/types/api/nodes/collections/index.d.ts +2 -2
  30. package/dist/types/api/nodes/collections/index.d.ts.map +1 -1
  31. package/dist/types/api/nodes/flowEffect.d.ts +2 -2
  32. package/dist/types/api/nodes/flowEffect.d.ts.map +1 -1
  33. package/dist/types/api/nodes/flowSignal.d.ts +1 -1
  34. package/dist/types/api/nodes/flowSignal.d.ts.map +1 -1
  35. package/dist/types/api/nodes/flowValue.d.ts +2 -2
  36. package/dist/types/api/nodes/flowValue.d.ts.map +1 -1
  37. package/dist/types/api/nodes/index.d.ts +7 -7
  38. package/dist/types/api/nodes/index.d.ts.map +1 -1
  39. package/dist/types/api/nodes/sync/flowConstant.d.ts +2 -2
  40. package/dist/types/api/nodes/sync/flowConstant.d.ts.map +1 -1
  41. package/dist/types/api/nodes/sync/flowDerivation.d.ts +3 -3
  42. package/dist/types/api/nodes/sync/flowDerivation.d.ts.map +1 -1
  43. package/dist/types/api/nodes/sync/flowState.d.ts +3 -3
  44. package/dist/types/api/nodes/sync/flowState.d.ts.map +1 -1
  45. package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts +2 -2
  46. package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts.map +1 -1
  47. package/dist/types/api/nodes/sync/index.d.ts +4 -4
  48. package/dist/types/api/nodes/sync/index.d.ts.map +1 -1
  49. package/dist/types/api/nodes/utils.d.ts +1 -1
  50. package/dist/types/api/nodes/utils.d.ts.map +1 -1
  51. package/dist/types/base/disposable.d.ts +1 -1
  52. package/dist/types/base/disposable.d.ts.map +1 -1
  53. package/dist/types/base/executionStack.d.ts +22 -1
  54. package/dist/types/base/executionStack.d.ts.map +1 -1
  55. package/dist/types/base/index.d.ts +5 -5
  56. package/dist/types/base/index.d.ts.map +1 -1
  57. package/dist/types/base/node.d.ts +4 -4
  58. package/dist/types/base/node.d.ts.map +1 -1
  59. package/dist/types/base/observable.d.ts +3 -3
  60. package/dist/types/base/observable.d.ts.map +1 -1
  61. package/dist/types/base/observer.d.ts +2 -2
  62. package/dist/types/base/observer.d.ts.map +1 -1
  63. package/dist/types/converters/index.d.ts +1 -1
  64. package/dist/types/converters/index.d.ts.map +1 -1
  65. package/dist/types/converters/solid.d.ts +12 -8
  66. package/dist/types/converters/solid.d.ts.map +1 -1
  67. package/dist/types/index.d.ts +2 -2
  68. package/dist/types/index.d.ts.map +1 -1
  69. package/dist/types/nodes/actionNode.d.ts +23 -0
  70. package/dist/types/nodes/actionNode.d.ts.map +1 -0
  71. package/dist/types/nodes/arrayNode.d.ts +4 -3
  72. package/dist/types/nodes/arrayNode.d.ts.map +1 -1
  73. package/dist/types/nodes/effectNode.d.ts +18 -2
  74. package/dist/types/nodes/effectNode.d.ts.map +1 -1
  75. package/dist/types/nodes/index.d.ts +8 -7
  76. package/dist/types/nodes/index.d.ts.map +1 -1
  77. package/dist/types/nodes/mapNode.d.ts +4 -3
  78. package/dist/types/nodes/mapNode.d.ts.map +1 -1
  79. package/dist/types/nodes/signalNode.d.ts +2 -2
  80. package/dist/types/nodes/signalNode.d.ts.map +1 -1
  81. package/dist/types/nodes/valueAsyncNode.d.ts +3 -3
  82. package/dist/types/nodes/valueAsyncNode.d.ts.map +1 -1
  83. package/dist/types/nodes/valueNode.d.ts +3 -3
  84. package/dist/types/nodes/valueNode.d.ts.map +1 -1
  85. package/dist/types/nodes/valueSyncNode.d.ts +3 -3
  86. package/dist/types/nodes/valueSyncNode.d.ts.map +1 -1
  87. package/dist/types/schedulers/asyncScheduler.d.ts +2 -1
  88. package/dist/types/schedulers/asyncScheduler.d.ts.map +1 -1
  89. package/dist/types/schedulers/index.d.ts +4 -4
  90. package/dist/types/schedulers/index.d.ts.map +1 -1
  91. package/dist/types/schedulers/syncResolver.d.ts +1 -2
  92. package/dist/types/schedulers/syncResolver.d.ts.map +1 -1
  93. package/dist/types/schedulers/syncScheduler.d.ts +1 -1
  94. package/dist/types/schedulers/syncScheduler.d.ts.map +1 -1
  95. package/package.json +24 -1
  96. package/.gitlab-ci.yml +0 -24
  97. package/.vscode/settings.json +0 -5
  98. package/CHANGELOG.md +0 -94
  99. package/biome.json +0 -47
  100. package/docs/.vitepress/config.mts +0 -145
  101. package/docs/api/functions/array.md +0 -35
  102. package/docs/api/functions/constant.md +0 -33
  103. package/docs/api/functions/constantAsync.md +0 -69
  104. package/docs/api/functions/derivation.md +0 -34
  105. package/docs/api/functions/derivationAsync.md +0 -34
  106. package/docs/api/functions/from.md +0 -129
  107. package/docs/api/functions/isDisposable.md +0 -27
  108. package/docs/api/functions/map.md +0 -36
  109. package/docs/api/functions/signal.md +0 -21
  110. package/docs/api/functions/state.md +0 -67
  111. package/docs/api/functions/stateAsync.md +0 -69
  112. package/docs/api/functions/subscribe.md +0 -40
  113. package/docs/api/functions/writableDerivation.md +0 -33
  114. package/docs/api/functions/writableDerivationAsync.md +0 -34
  115. package/docs/api/index.md +0 -61
  116. package/docs/api/interfaces/FlowArray.md +0 -439
  117. package/docs/api/interfaces/FlowConstant.md +0 -220
  118. package/docs/api/interfaces/FlowConstantAsync.md +0 -221
  119. package/docs/api/interfaces/FlowDerivation.md +0 -241
  120. package/docs/api/interfaces/FlowDerivationAsync.md +0 -242
  121. package/docs/api/interfaces/FlowDisposable.md +0 -59
  122. package/docs/api/interfaces/FlowEffect.md +0 -64
  123. package/docs/api/interfaces/FlowMap.md +0 -374
  124. package/docs/api/interfaces/FlowObservable.md +0 -155
  125. package/docs/api/interfaces/FlowSignal.md +0 -156
  126. package/docs/api/interfaces/FlowState.md +0 -269
  127. package/docs/api/interfaces/FlowStateAsync.md +0 -268
  128. package/docs/api/interfaces/FlowSubscribable.md +0 -55
  129. package/docs/api/interfaces/FlowTracker.md +0 -61
  130. package/docs/api/interfaces/FlowValue.md +0 -222
  131. package/docs/api/interfaces/FlowWritableDerivation.md +0 -292
  132. package/docs/api/interfaces/FlowWritableDerivationAsync.md +0 -293
  133. package/docs/api/type-aliases/DerivationFunction.md +0 -28
  134. package/docs/api/type-aliases/DerivationFunctionAsync.md +0 -28
  135. package/docs/api/type-aliases/FlowArrayAction.md +0 -60
  136. package/docs/api/type-aliases/FlowDataTracker.md +0 -33
  137. package/docs/api/type-aliases/FlowMapAction.md +0 -48
  138. package/docs/api/type-aliases/FlowOnDataListener.md +0 -33
  139. package/docs/api/type-aliases/FlowOnErrorListener.md +0 -27
  140. package/docs/api/type-aliases/FlowOnPendingListener.md +0 -21
  141. package/docs/api/type-aliases/FlowReadonly.md +0 -22
  142. package/docs/api/type-aliases/InitFunction.md +0 -21
  143. package/docs/api/type-aliases/InitFunctionAsync.md +0 -21
  144. package/docs/api/type-aliases/NotPromise.md +0 -21
  145. package/docs/api/type-aliases/UpdateFunction.md +0 -27
  146. package/docs/api/type-aliases/UpdateFunctionAsync.md +0 -27
  147. package/docs/api/typedoc-sidebar.json +0 -65
  148. package/docs/examples/examples.md +0 -2311
  149. package/docs/examples/patterns.md +0 -649
  150. package/docs/guide/advanced/architecture.md +0 -1234
  151. package/docs/guide/advanced/disposal.md +0 -426
  152. package/docs/guide/advanced/migration-v1.md +0 -464
  153. package/docs/guide/advanced/migration-v2.md +0 -204
  154. package/docs/guide/advanced/solidjs.md +0 -135
  155. package/docs/guide/introduction/concepts.md +0 -57
  156. package/docs/guide/introduction/conventions.md +0 -30
  157. package/docs/guide/introduction/getting-started.md +0 -139
  158. package/docs/guide/introduction/lifecycle.md +0 -368
  159. package/docs/guide/primitives/array.md +0 -286
  160. package/docs/guide/primitives/constant.md +0 -207
  161. package/docs/guide/primitives/derivations.md +0 -281
  162. package/docs/guide/primitives/effects.md +0 -372
  163. package/docs/guide/primitives/map.md +0 -265
  164. package/docs/guide/primitives/overview.md +0 -92
  165. package/docs/guide/primitives/signal.md +0 -222
  166. package/docs/guide/primitives/state.md +0 -272
  167. package/docs/index.md +0 -47
  168. package/docs/public/logo.svg +0 -1
  169. package/src/api/base/flowDisposable.ts +0 -44
  170. package/src/api/base/flowObservable.ts +0 -28
  171. package/src/api/base/flowSubscribable.ts +0 -87
  172. package/src/api/base/flowTracker.ts +0 -7
  173. package/src/api/base/index.ts +0 -4
  174. package/src/api/index.ts +0 -2
  175. package/src/api/nodes/async/flowConstantAsync.ts +0 -36
  176. package/src/api/nodes/async/flowDerivationAsync.ts +0 -42
  177. package/src/api/nodes/async/flowStateAsync.ts +0 -47
  178. package/src/api/nodes/async/flowWritableDerivationAsync.ts +0 -33
  179. package/src/api/nodes/async/index.ts +0 -4
  180. package/src/api/nodes/collections/flowArray.ts +0 -155
  181. package/src/api/nodes/collections/flowMap.ts +0 -115
  182. package/src/api/nodes/collections/index.ts +0 -2
  183. package/src/api/nodes/flowEffect.ts +0 -42
  184. package/src/api/nodes/flowSignal.ts +0 -28
  185. package/src/api/nodes/flowValue.ts +0 -37
  186. package/src/api/nodes/index.ts +0 -7
  187. package/src/api/nodes/sync/flowConstant.ts +0 -33
  188. package/src/api/nodes/sync/flowDerivation.ts +0 -41
  189. package/src/api/nodes/sync/flowState.ts +0 -45
  190. package/src/api/nodes/sync/flowWritableDerivation.ts +0 -31
  191. package/src/api/nodes/sync/index.ts +0 -4
  192. package/src/api/nodes/utils.ts +0 -24
  193. package/src/base/disposable.ts +0 -18
  194. package/src/base/executionStack.ts +0 -42
  195. package/src/base/index.ts +0 -5
  196. package/src/base/node.ts +0 -98
  197. package/src/base/observable.ts +0 -92
  198. package/src/base/observer.ts +0 -51
  199. package/src/converters/index.ts +0 -1
  200. package/src/converters/solid.ts +0 -109
  201. package/src/index.ts +0 -2
  202. package/src/nodes/arrayNode.ts +0 -180
  203. package/src/nodes/effectNode.ts +0 -58
  204. package/src/nodes/index.ts +0 -7
  205. package/src/nodes/mapNode.ts +0 -125
  206. package/src/nodes/signalNode.ts +0 -19
  207. package/src/nodes/valueAsyncNode.ts +0 -85
  208. package/src/nodes/valueNode.ts +0 -148
  209. package/src/nodes/valueSyncNode.ts +0 -125
  210. package/src/schedulers/asyncResolver.ts +0 -78
  211. package/src/schedulers/asyncScheduler.ts +0 -66
  212. package/src/schedulers/index.ts +0 -4
  213. package/src/schedulers/pendingError.ts +0 -13
  214. package/src/schedulers/scheduler.ts +0 -9
  215. package/src/schedulers/syncResolver.ts +0 -69
  216. package/src/schedulers/syncScheduler.ts +0 -55
  217. package/test/base/pendingError.test.ts +0 -67
  218. package/test/converters/solid.derivation.browser.test.tsx +0 -69
  219. package/test/converters/solid.node.test.ts +0 -654
  220. package/test/converters/solid.state.browser.test.tsx +0 -1592
  221. package/test/reactivity/flowSignal.test.ts +0 -226
  222. package/test/reactivity/nodes/async/asyncScheduler/asyncResolver.test.ts +0 -593
  223. package/test/reactivity/nodes/async/asyncScheduler/asyncScheduler.test.ts +0 -317
  224. package/test/reactivity/nodes/async/flowConstantAsync.test.ts +0 -652
  225. package/test/reactivity/nodes/async/flowDerivation.test.ts +0 -898
  226. package/test/reactivity/nodes/async/flowDerivationAsync.test.ts +0 -1716
  227. package/test/reactivity/nodes/async/flowStateAsync.test.ts +0 -708
  228. package/test/reactivity/nodes/async/flowWritableDerivationAsync.test.ts +0 -614
  229. package/test/reactivity/nodes/collections/flowArray.asyncStates.test.ts +0 -1289
  230. package/test/reactivity/nodes/collections/flowArray.scalars.test.ts +0 -961
  231. package/test/reactivity/nodes/collections/flowArray.states.test.ts +0 -1035
  232. package/test/reactivity/nodes/collections/flowMap.asyncStates.test.ts +0 -960
  233. package/test/reactivity/nodes/collections/flowMap.scalars.test.ts +0 -775
  234. package/test/reactivity/nodes/collections/flowMap.states.test.ts +0 -958
  235. package/test/reactivity/nodes/sync/flowConstant.test.ts +0 -377
  236. package/test/reactivity/nodes/sync/flowDerivation.test.ts +0 -896
  237. package/test/reactivity/nodes/sync/flowState.test.ts +0 -341
  238. package/test/reactivity/nodes/sync/flowWritableDerivation.test.ts +0 -603
  239. package/test/vitest.d.ts +0 -10
  240. package/tsconfig.json +0 -37
  241. package/typedoc.json +0 -37
  242. package/vite.config.ts +0 -31
  243. package/vitest.browser.config.ts +0 -21
  244. package/vitest.config.ts +0 -17
@@ -1,368 +0,0 @@
1
- # Lifecycle
2
-
3
- Understanding PicoFlow's internal execution model is key to mastering reactive programming. This guide walks through exactly what happens during creation, updates, and disposal using a concrete example.
4
-
5
- ## The Example
6
-
7
- Throughout this guide, we'll use this simple but complete example:
8
-
9
- ```typescript
10
- const $a = state(1)
11
- const $b = state(2)
12
- const $c = derivation((t) => $a.get(t) + $b.get(t))
13
- $c.subscribe((c) => console.log('C =', c))
14
- ```
15
-
16
- This example demonstrates:
17
- - **Two states**: `$a` and `$b`
18
- - **One derivation**: `$c` that computes the sum
19
- - **One effect**: that logs the result
20
-
21
- Let's see what happens at each stage of the lifecycle.
22
-
23
- ## Creation Flow
24
-
25
- When you run the example code, here's the exact sequence of events:
26
-
27
- ### Step-by-Step
28
-
29
- 1. **`state(1)` creates `$a`**
30
- - A new `FlowState` instance is created
31
- - Internal value is set to `1`
32
- - No dependencies yet
33
-
34
- 2. **`state(2)` creates `$b`**
35
- - Another `FlowState` instance is created
36
- - Internal value is set to `2`
37
- - No dependencies yet
38
-
39
- 3. **`derivation(...)` creates `$c`**
40
- - A new `FlowDerivation` instance is created
41
- - The compute function is stored but **NOT executed** (lazy evaluation)
42
- - A `FlowTracker` will be created when the derivation is first accessed
43
- - Still no computed value yet
44
-
45
- 4. **`$c.subscribe(...)` creates the effect**
46
- - A new `FlowEffect` instance is created
47
- - **The effect executes immediately** (not lazy!)
48
- - During execution:
49
- - The subscribe method internally calls `$c.get(t)` with a `FlowTracker`
50
- - This triggers `$c` to initialize (first access)
51
- - `$c` creates its own `FlowTracker` and runs its compute function
52
- - Compute calls `$a.get(t)` → returns `1`, registers `$a` as dependency of `$c`
53
- - Compute calls `$b.get(t)` → returns `2`, registers `$b` as dependency of `$c`
54
- - Compute returns `3`
55
- - The value `3` is registered as a dependency of the effect
56
- - Callback is called with `3` and logs: `"C = 3"`
57
-
58
- ### Creation Sequence Diagram
59
-
60
- ```mermaid
61
- sequenceDiagram
62
- participant User
63
- participant $a as $a (State)
64
- participant $b as $b (State)
65
- participant $c as $c (Derivation)
66
- participant Effect
67
- participant Context
68
-
69
- User->>$a: state(1)
70
- activate $a
71
- Note over $a: Store value: 1
72
- deactivate $a
73
-
74
- User->>$b: state(2)
75
- activate $b
76
- Note over $b: Store value: 2
77
- deactivate $b
78
-
79
- User->>$c: derivation((t) => ...)
80
- activate $c
81
- Note over $c: Store compute fn<br/>NOT executed yet<br/>(lazy)
82
- deactivate $c
83
-
84
- User->>Effect: $c.subscribe(...)
85
- activate Effect
86
- Note over Effect: Create & execute<br/>immediately
87
-
88
- Effect->>$c: get(t)
89
- activate $c
90
- Note over $c: First access!<br/>Initialize & compute
91
-
92
- $c->>$a: get(t)
93
- activate $a
94
- $a-->>$c: 1
95
- $a->>$c: Register as listener
96
- Note over $a,$c: $c now depends on $a
97
- deactivate $a
98
-
99
- $c->>$b: get(t)
100
- activate $b
101
- $b-->>$c: 2
102
- $b->>$c: Register as listener
103
- Note over $b,$c: $c now depends on $b
104
- deactivate $b
105
-
106
- Note over $c: Compute: 1 + 2 = 3
107
- $c-->>Effect: 3
108
- $c->>Effect: Register as effect
109
- Note over $c,Effect: Effect depends on $c
110
- deactivate $c
111
-
112
- Note over Effect: Log: "C = 3"
113
- deactivate Effect
114
- ```
115
-
116
- **Key Insights:**
117
- - States are simple: just store the value
118
- - Derivations are **lazy**: they don't compute until accessed
119
- - Effects are **eager**: they execute immediately on creation
120
- - Dependencies are registered **during execution** through `.get(t)`
121
-
122
- ## Update Flow
123
-
124
- Now let's see what happens when we update `$a`:
125
-
126
- ```typescript
127
- $a.set(5)
128
- ```
129
-
130
- ### The Push-Pull Model
131
-
132
- PicoFlow uses a "**push notification, pull computation**" model:
133
-
134
- - **Push**: When a value changes, notifications are pushed to all dependents immediately
135
- - **Pull**: Actual recomputation only happens when a value is accessed (pulled)
136
-
137
- This is efficient because:
138
- - Derivations that aren't currently needed don't recompute
139
- - Multiple state changes can batch before triggering computation
140
- - Only the necessary chain of dependencies is recomputed
141
-
142
- ### Step-by-Step
143
-
144
- 1. **`$a.set(5)` is called**
145
- - `$a` checks if the value changed: `5 !== 1` ✓
146
- - `$a` updates its internal value: `1 → 5`
147
- - `$a` notifies all its dependents
148
-
149
- 2. **`$a` notifies its dependents**
150
- - `$a` sends notifications to all listeners (includes `$c`)
151
- - `$c` receives the notification
152
-
153
- 3. **`$c` receives notification**
154
- - `$c` marks itself as needing recomputation
155
- - **Does NOT recompute yet!** (lazy evaluation)
156
- - `$c` propagates the notification to its dependents
157
-
158
- 4. **`$c` notifies its dependents**
159
- - `$c` sends notifications to all effects (includes our effect)
160
- - The effect is scheduled to execute
161
-
162
- 5. **Effect executes**
163
- - Effect runs its function with tracking context
164
- - Calls `$c.get(t)`
165
-
166
- 6. **`$c` recomputes (pull)**
167
- - `$c.get(t)` detects that recomputation is needed
168
- - Clears old dependencies
169
- - Runs compute function again
170
- - Compute calls `$a.get(t)` → returns `5`
171
- - Compute calls `$b.get(t)` → returns `2`
172
- - Computes new value: `5 + 2 = 7`
173
- - Re-registers dependencies
174
- - Returns `7`
175
-
176
- 7. **Effect logs the result**
177
- - Effect receives `7` from `$c.get(t)`
178
- - Logs: `"C = 7"`
179
-
180
- ### Update Sequence Diagram
181
-
182
- ```mermaid
183
- sequenceDiagram
184
- participant User
185
- participant $a as $a (State)
186
- participant $c as $c (Derivation)
187
- participant Effect
188
-
189
- User->>$a: set(5)
190
- activate $a
191
- Note over $a: Check: 5 !== 1 ✓<br/>Update: 1 → 5
192
-
193
- $a->>$c: notify
194
- activate $c
195
- Note over $c: Mark as needs<br/>recomputation<br/>NO recompute yet!
196
-
197
- $c->>Effect: notify
198
- deactivate $c
199
- deactivate $a
200
-
201
- activate Effect
202
- Note over Effect: Execute function
203
-
204
- Effect->>$c: get(t)
205
- activate $c
206
- Note over $c: Recomputation<br/>needed? YES
207
-
208
- Note over $c: Recompute
209
- activate $c
210
- Note over $c: Clear old deps
211
-
212
- $c->>$a: get(t)
213
- activate $a
214
- $a-->>$c: 5
215
- deactivate $a
216
-
217
- $c->>$b: get(t)
218
- activate $b
219
- $b-->>$c: 2
220
- deactivate $b
221
-
222
- Note over $c: Compute: 5 + 2 = 7<br/>Re-register deps
223
- deactivate $c
224
-
225
- $c-->>Effect: 7
226
- deactivate $c
227
-
228
- Note over Effect: Log: "C = 7"
229
- deactivate Effect
230
- ```
231
-
232
- **Key Insights:**
233
- - **Notifications are pushed** immediately when state changes
234
- - **Derivations mark as dirty** but don't recompute until needed
235
- - **Effects execute immediately** when notified
236
- - **Recomputation is pulled** when effects access derivations
237
- - **Dependencies are re-registered** on each execution (dynamic tracking)
238
-
239
- ## Disposal Flow
240
-
241
- Finally, let's see what happens when we dispose a primitive:
242
-
243
- ```typescript
244
- $a.dispose()
245
- ```
246
-
247
- ### Step-by-Step
248
-
249
- 1. **`$a.dispose()` is called**
250
- - `$a` checks if already disposed (throws if yes)
251
- - Iterates through all listeners (dependents like `$c`)
252
- - Iterates through all effects (none in this case)
253
-
254
- 2. **Cleanup listeners**
255
- - For each listener, calls `listener.dispose()`
256
- - This triggers `$c.dispose()`
257
-
258
- 3. **`$c.dispose()` cascades**
259
- - `$c` iterates through its listeners (none)
260
- - `$c` iterates through its effects (includes our effect)
261
- - Calls `effect.dispose()`
262
-
263
- 4. **Effect cleanup**
264
- - Effect unregisters from all its dependencies (`$c`)
265
- - Effect marks itself as disposed
266
- - Future notifications won't trigger this effect
267
-
268
- 5. **State cleanup**
269
- - `$a` unregisters from all its listeners
270
- - `$a` marks itself as disposed
271
- - Future attempts to use `$a` will throw errors
272
-
273
- 6. **Result**
274
- - The entire dependency graph is cleaned up
275
- - Memory is freed
276
- - No more reactions will occur
277
-
278
- ### Disposal Sequence Diagram
279
-
280
- ```mermaid
281
- sequenceDiagram
282
- participant User
283
- participant $a as $a (State)
284
- participant $c as $c (Derivation)
285
- participant Effect
286
-
287
- User->>$a: dispose()
288
- activate $a
289
- Note over $a: Check if disposed<br/>Iterate listeners
290
-
291
- $a->>$c: dispose()
292
- activate $c
293
- Note over $c: Iterate effects
294
-
295
- $c->>Effect: dispose()
296
- activate Effect
297
-
298
- Effect->>$c: Unregister
299
- Note over Effect,$c: Remove dependency
300
-
301
- Note over Effect: Mark disposed<br/>Stop running
302
- deactivate Effect
303
-
304
- $c->>$a: Unregister
305
- Note over $c,$a: Remove dependency
306
-
307
- Note over $c: Mark disposed
308
- deactivate $c
309
-
310
- Note over $a: Mark disposed<br/>Clean up listeners
311
- deactivate $a
312
-
313
- Note over User,Effect: All cleaned up!<br/>Memory freed
314
- ```
315
-
316
- **Key Insights:**
317
- - **Disposal cascades** through the dependency graph
318
- - **Dependents are disposed first** (effects before derivations before states)
319
- - **All registrations are cleaned up** to prevent memory leaks
320
- - **Disposed primitives throw errors** if you try to use them
321
- - **Order matters**: dispose dependents before dependencies when doing manual cleanup
322
-
323
- ## Practical Implications
324
-
325
- Understanding these flows helps you:
326
-
327
- ### 1. Optimize Performance
328
-
329
- ```typescript
330
- // Derivation won't compute if effect is disposed
331
- const $expensive = derivation((t) => {
332
- return expensiveCalculation($data.get(t))
333
- })
334
-
335
- const effect = $expensive.subscribe((value) => {
336
- if (shouldDisplay) display(value)
337
- })
338
-
339
- // When not needed, just dispose the effect
340
- effect.dispose() // $expensive stops computing
341
- ```
342
-
343
- ### 2. Debug Dependency Issues
344
-
345
- ```typescript
346
- // If logs don't appear, check:
347
- // 1. Is the effect created? (runs immediately)
348
- // 2. Are you using .subscribe()? (not .pick())
349
- // 3. Is anything disposed?
350
-
351
- $state.subscribe((value) => {
352
- console.log('Effect created') // Should log immediately
353
- console.log('Value:', value)
354
- })
355
- ```
356
-
357
- ### 3. Avoid Memory Leaks
358
-
359
- ```typescript
360
- // Always dispose effects when done
361
- const disposables: FlowDisposable[] = []
362
-
363
- disposables.push(subscribe((t) => { /* ... */ }, () => { /* ... */ }))
364
- disposables.push(subscribe((t) => { /* ... */ }, () => { /* ... */ }))
365
-
366
- // Clean up
367
- disposables.forEach(d => d.dispose())
368
- ```
@@ -1,286 +0,0 @@
1
- # Arrays
2
-
3
- PicoFlow provides reactive arrays with **fine-grained tracking**. Instead of reacting to the entire array changing, you can react to specific operations like pushing, popping, or splicing individual items.
4
-
5
-
6
- ## When to Use Arrays
7
-
8
- Use arrays when you need to:
9
-
10
- - ✅ Track large collections with fine-grained updates
11
- - ✅ React to specific operations (additions, removals, updates) separately
12
- - ✅ Optimize performance by avoiding full array re-processing
13
-
14
- Don't use arrays when:
15
-
16
- - ❌ You don't need fine-grained tracking (use `state` with an array)
17
- - ❌ You need key-value operations (use `map` instead)
18
-
19
- ## Creating Arrays
20
-
21
- Creating a reactive array is straightforward:
22
-
23
- ```typescript
24
- import { array } from '@ersbeth/picoflow'
25
-
26
- // Create with initial values
27
- const $todos = array([
28
- { id: 1, text: 'Learn PicoFlow', done: false },
29
- { id: 2, text: 'Build app', done: false }
30
- ])
31
-
32
- // Create empty
33
- const $items = array<string>()
34
- ```
35
-
36
- ## Using Arrays
37
-
38
- Arrays provide multiple ways to track changes and perform operations.
39
-
40
- ### Array Operations
41
-
42
- Arrays provide several operations: `set()`, `update()`, `push()`, `pop()`, `unshift()`, `shift()`, `splice()`, and `clear()`.
43
-
44
- ```typescript
45
- const $list = array<number>([])
46
-
47
- // Replace entire array
48
- $list.set([1, 2, 3])
49
-
50
- // Replace item at index
51
- $list.update(0, 10)
52
-
53
- // Add to end
54
- $list.push(4)
55
-
56
- // Add to start
57
- $list.unshift(0)
58
-
59
- // Remove from end
60
- $list.pop()
61
-
62
- // Remove from start
63
- $list.shift()
64
-
65
- // Splice (remove/add at index)
66
- $list.splice(1, 1, 99) // Remove 1 item at index 1, add 99
67
-
68
- // Clear all
69
- $list.clear()
70
-
71
- // Read (reactive)
72
- $list.subscribe((items) => {
73
- // Track entire array
74
- console.log(items)
75
- })
76
-
77
- // Read (non-reactive)
78
- const snapshot = $list.pick()
79
- ```
80
-
81
- **Important**:
82
- - `update(index, item)` throws an error if the index is out of bounds
83
- - `push(item)` only accepts a single item
84
- - `splice(start, deleteCount, ...items)` accepts multiple items to add
85
-
86
- ### Whole Array Tracking
87
-
88
- Track when the entire array changes (any operation):
89
-
90
- ```typescript
91
- const $items = array([1, 2, 3])
92
-
93
- $items.subscribe((items) => {
94
- console.log('Array changed:', items)
95
- })
96
-
97
- $items.push(4) // Logs: "Array changed: [1,2,3,4]"
98
- $items.pop() // Logs: "Array changed: [1,2,3]"
99
- $items.splice(1, 1) // Logs: "Array changed: [1,3]"
100
- ```
101
-
102
- Use `.get(t)` when you need the entire array's current state. It returns a copy of the array.
103
-
104
- ### Fine-Grained Tracking
105
-
106
- Track specific operations with `$lastAction`:
107
-
108
- ```typescript
109
- const $items = array<number>([])
110
-
111
- $items.$lastAction.subscribe(
112
- (action) => {
113
- if (!action) return
114
-
115
- switch (action.type) {
116
- case 'push':
117
- console.log('Pushed:', action.item)
118
- break
119
- case 'pop':
120
- console.log('Popped')
121
- break
122
- case 'splice':
123
- console.log('Splice at', action.start, ':', action.deleteCount, 'deleted,', action.items.length, 'added')
124
- break
125
- case 'update':
126
- console.log('Set item at', action.index, 'to', action.item)
127
- break
128
- // ... other operations
129
- }
130
- }
131
- )
132
-
133
- $items.push(1) // Logs: "Pushed: 1"
134
- $items.push(2) // Logs: "Pushed: 2"
135
- $items.pop() // Logs: "Popped"
136
- $items.splice(0, 1) // Logs: "Splice at 0: 1 deleted, 0 added"
137
- ```
138
-
139
- ## Lifecycle
140
-
141
- When you create a subscription that tracks an array:
142
-
143
- 1. **Registration**: The array registers the subscription as a watcher
144
- 2. **Operation**: When you call `push()`, `pop()`, `splice()`, etc., the array updates internally
145
- 3. **Signal Update**: The `$lastAction` signal is updated with the operation details
146
- 4. **Notification**: All watching subscriptions are scheduled to run
147
- 5. **Re-execution**: Each watching subscription re-executes its function
148
-
149
- ```mermaid
150
- sequenceDiagram
151
- participant User
152
- participant Array as $todos (Array)
153
- participant Signal as $lastAction
154
- participant Subscription
155
-
156
- Note over User,Subscription: 1. Setup Phase
157
- User->>Subscription: Create subscription
158
- activate Subscription
159
- Subscription->>Array: get(t)
160
- Note over Array: Register Subscription as watcher
161
- Subscription->>Signal: get(t)
162
- Note over Signal: Register Subscription as watcher
163
- Subscription->>Subscription: Execute function
164
- Note over Subscription: Initial render
165
- deactivate Subscription
166
-
167
- Note over User,Subscription: 2. Operation Phase
168
- User->>Array: push(todo)
169
- activate Array
170
- Note over Array: Update internal array
171
- Array->>Signal: set({ type: 'push', item: todo })
172
- activate Signal
173
- Note over Signal: Notify watchers
174
- Signal->>Subscription: Schedule execution
175
- deactivate Signal
176
- Array->>Array: Notify whole array watchers
177
- Array->>Subscription: Schedule execution
178
- deactivate Array
179
-
180
- activate Subscription
181
- Note over Subscription: Re-execute function
182
- Subscription->>Signal: get(t)
183
- Signal-->>Subscription: { type: 'push', item: todo }
184
- Subscription->>Subscription: Handle new todo
185
- Note over Subscription: Update UI
186
- deactivate Subscription
187
- ```
188
-
189
- ## Best Practices
190
-
191
- ### Handle All Action Types
192
-
193
- ```typescript
194
- subscribe(
195
- (t) => $items.$lastAction.get(t),
196
- (action) => {
197
- if (!action) return
198
-
199
- switch (action.type) {
200
- case 'push':
201
- // Handle push
202
- break
203
- case 'pop':
204
- // Handle pop
205
- break
206
- case 'splice':
207
- // Handle splice
208
- break
209
- case 'update':
210
- // Handle update
211
- break
212
- case 'clear':
213
- // Handle clear
214
- break
215
- case 'unshift':
216
- // Handle unshift
217
- break
218
- case 'shift':
219
- // Handle shift
220
- break
221
- case 'set':
222
- // Handle set (replace entire array)
223
- break
224
- }
225
- }
226
- )
227
- ```
228
-
229
- ### Dispose Items Properly
230
-
231
- ```typescript
232
- // When removing disposable items
233
- subscribe(
234
- (t) => $resources.$lastAction.get(t),
235
- (action) => {
236
- if (action && action.type === 'splice') {
237
- // Dispose removed items
238
- }
239
- }
240
- )
241
- ```
242
-
243
- ## Common Pitfalls
244
-
245
- ### Using Wrong Tracking
246
-
247
- **Problem**: Tracking the entire array when you only need specific operations.
248
-
249
- ```typescript
250
- // ❌ Tracks entire array when you only need additions
251
- $items.subscribe((items) => {
252
- const lastItem = items[items.length - 1]
253
- animateNewItem(lastItem) // Runs on ALL changes
254
- })
255
- ```
256
-
257
- **Solution**: Track only what you need:
258
-
259
- ```typescript
260
- // ✅ Track only pushes
261
- subscribe(
262
- (t) => $items.$lastAction.get(t),
263
- (action) => {
264
- if (action && action.type === 'push') {
265
- animateNewItem(action.item)
266
- }
267
- }
268
- )
269
- ```
270
-
271
- ### Mutating Retrieved Arrays
272
-
273
- **Problem**: Mutating the array directly instead of using reactive methods.
274
-
275
- ```typescript
276
- // ❌ Mutating the array
277
- const items = $items.pick()
278
- items.push(newItem) // Doesn't update reactive array!
279
- ```
280
-
281
- **Solution**: Use reactive methods:
282
-
283
- ```typescript
284
- // ✅ Use reactive methods
285
- $items.push(newItem)
286
- ```