@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,426 +0,0 @@
1
- # Disposal
2
-
3
- Memory management is crucial in long-running applications. PicoFlow provides explicit disposal mechanisms to prevent memory leaks and ensure resources are properly cleaned up.
4
-
5
- ## Why Disposal Matters
6
-
7
- All PicoFlow primitives are disposable. When you're done with them, you should clean them up:
8
-
9
- ```typescript
10
- const $count = state(0)
11
- const fx = effect((t) => {
12
- console.log($count.get(t))
13
- })
14
-
15
- // Later... clean up
16
- fx.dispose() // Effect stops running
17
- $count.dispose() // State is cleaned up
18
- ```
19
-
20
- **Why disposal matters:**
21
- - **Prevents memory leaks** - Unreferenced but undisposed primitives stay in memory
22
- - **Stops effects from running** - Effects continue executing until explicitly disposed
23
- - **Cleans up resources** - WebSocket connections, intervals, event listeners, etc.
24
- - **Important in long-running applications** - SPAs, dashboards, real-time apps
25
-
26
- ```mermaid
27
- flowchart LR
28
- A[Create primitive] --> B[Use in effects]
29
- B --> C[No longer needed]
30
- C --> D{Disposed?}
31
- D -->|Yes| E[Memory freed]
32
- D -->|No| F[Memory leak!]
33
-
34
- style E fill:#90EE90
35
- style F fill:#FFB6C6
36
- ```
37
-
38
- ## When to Dispose
39
-
40
- ### Component Cleanup
41
-
42
- In component-based architectures, dispose primitives when components unmount:
43
-
44
- ```typescript
45
- class TodoList {
46
- private $todos = state<Todo[]>([])
47
- private disposables: FlowDisposable[] = []
48
-
49
- constructor() {
50
- // Track all disposables
51
- this.disposables.push(
52
- effect((t) => {
53
- this.render($todos.get(t))
54
- })
55
- )
56
- }
57
-
58
- destroy() {
59
- // Clean up when component is destroyed
60
- this.disposables.forEach(d => d.dispose())
61
- this.$todos.dispose()
62
- }
63
- }
64
- ```
65
-
66
- ### Conditional Effects
67
-
68
- Dispose and recreate effects based on application state:
69
-
70
- ```typescript
71
- let currentEffect: FlowEffect | null = null
72
-
73
- function enableLogging(enable: boolean) {
74
- // Dispose previous effect
75
- currentEffect?.dispose()
76
-
77
- if (enable) {
78
- currentEffect = effect((t) => {
79
- console.log('Value:', $data.get(t))
80
- })
81
- }
82
- }
83
-
84
- // Toggle logging on/off
85
- enableLogging(true) // Effect starts
86
- enableLogging(false) // Effect disposed
87
- ```
88
-
89
- ### Resource Management
90
-
91
- Dispose primitives that manage external resources:
92
-
93
- ```typescript
94
- const $wsData = stream<string>((set) => {
95
- const ws = new WebSocket('ws://example.com')
96
- ws.onmessage = (e) => set(e.data)
97
-
98
- // Cleanup function called on disposal
99
- return () => ws.close()
100
- })
101
-
102
- // Use the stream
103
- effect((t) => {
104
- console.log('Message:', $wsData.get(t))
105
- })
106
-
107
- // Later... close the connection
108
- $wsData.dispose()
109
- ```
110
-
111
- ### Interval Cleanup
112
-
113
- ```typescript
114
- const $tick = stream<number>((set) => {
115
- let count = 0
116
- const interval = setInterval(() => {
117
- set(count++)
118
- }, 1000)
119
-
120
- // Cleanup: clear interval on disposal
121
- return () => clearInterval(interval)
122
- })
123
-
124
- // Later...
125
- $tick.dispose() // Interval cleared
126
- ```
127
-
128
- ## Automatic Cleanup
129
-
130
- Effects automatically clean up their dependencies when disposed:
131
-
132
- ```typescript
133
- const $a = state(1)
134
- const $b = state(2)
135
-
136
- const fx = effect((t) => {
137
- console.log($a.get(t) + $b.get(t))
138
- })
139
-
140
- fx.dispose()
141
-
142
- // These no longer trigger the effect
143
- $a.set(10) // No console log
144
- $b.set(20) // No console log
145
- ```
146
-
147
- When an effect is disposed:
148
- 1. It unregisters from all its dependencies
149
- 2. Future changes to those dependencies won't trigger it
150
- 3. The effect's memory is freed
151
-
152
- ## Best Practices
153
-
154
- ### 1. Always Dispose in Cleanup Hooks
155
-
156
- In any framework with lifecycle hooks, dispose in the cleanup phase:
157
-
158
- ```typescript
159
- // React
160
- useEffect(() => {
161
- const fx = effect((t) => {
162
- // ... effect code
163
- })
164
-
165
- return () => fx.dispose() // Cleanup
166
- }, [])
167
-
168
- // Vue
169
- onUnmounted(() => {
170
- fx.dispose()
171
- })
172
-
173
- // Svelte
174
- onDestroy(() => {
175
- fx.dispose()
176
- })
177
- ```
178
-
179
- ### 2. Group Related Disposables
180
-
181
- Use collections to manage multiple disposables:
182
-
183
- ```typescript
184
- class DataManager {
185
- private disposables = new Set<FlowDisposable>()
186
-
187
- track(disposable: FlowDisposable) {
188
- this.disposables.add(disposable)
189
- }
190
-
191
- cleanup() {
192
- this.disposables.forEach(d => d.dispose())
193
- this.disposables.clear()
194
- }
195
- }
196
-
197
- // Usage
198
- const manager = new DataManager()
199
- manager.track(effect((t) => { /* ... */ }))
200
- manager.track(effect((t) => { /* ... */ }))
201
- manager.track($someState)
202
-
203
- // Clean up everything
204
- manager.cleanup()
205
- ```
206
-
207
- ### 3. Dispose in Reverse Order of Creation
208
-
209
- Dispose dependents before dependencies:
210
-
211
- ```typescript
212
- const $data = state(0)
213
- const $derived = derivation((t) => $data.get(t) * 2)
214
- const fx = effect((t) => console.log($derived.get(t)))
215
-
216
- // Cleanup: dispose in reverse order
217
- fx.dispose() // Effect first (depends on $derived)
218
- $derived.dispose() // Derivation second (depends on $data)
219
- $data.dispose() // State last (no dependencies)
220
- ```
221
-
222
- **Why?** Disposing in reverse order prevents:
223
- - Accessing disposed dependencies
224
- - Unnecessary recomputations during cleanup
225
- - Race conditions during shutdown
226
-
227
- ### 4. Check Disposal State
228
-
229
- Use the `disposed` property to check if a primitive has been disposed:
230
-
231
- ```typescript
232
- const fx = effect((t) => {
233
- console.log($count.get(t))
234
- })
235
-
236
- console.log(fx.disposed) // false
237
-
238
- fx.dispose()
239
-
240
- console.log(fx.disposed) // true
241
- ```
242
-
243
- ### 5. Handle Disposal Errors
244
-
245
- Wrap disposal in try-catch for robust cleanup:
246
-
247
- ```typescript
248
- function cleanupResources(disposables: FlowDisposable[]) {
249
- const errors: Error[] = []
250
-
251
- for (const disposable of disposables) {
252
- try {
253
- disposable.dispose()
254
- } catch (error) {
255
- errors.push(error as Error)
256
- }
257
- }
258
-
259
- if (errors.length > 0) {
260
- console.error('Disposal errors:', errors)
261
- }
262
- }
263
- ```
264
-
265
- ## Common Pitfalls
266
-
267
- ### Pitfall 1: Not Disposing Effects
268
-
269
- ```typescript
270
- // ❌ Memory leak - effect never disposed
271
- function updateCounter() {
272
- effect((t) => {
273
- document.getElementById('count').textContent = $count.get(t)
274
- })
275
- }
276
-
277
- // Called 100 times = 100 effects running!
278
- for (let i = 0; i < 100; i++) {
279
- updateCounter()
280
- }
281
-
282
- // ✅ Proper cleanup
283
- function updateCounter(): () => void {
284
- const fx = effect((t) => {
285
- document.getElementById('count').textContent = $count.get(t)
286
- })
287
-
288
- return () => fx.dispose()
289
- }
290
-
291
- const cleanup = updateCounter()
292
- // Later...
293
- cleanup()
294
- ```
295
-
296
- ### Pitfall 2: Disposing Too Early
297
-
298
- ```typescript
299
- // ❌ Disposed too early
300
- const fx = effect((t) => {
301
- console.log($count.get(t))
302
- })
303
-
304
- fx.dispose() // Effect disposed immediately!
305
-
306
- $count.set(1) // No effect runs
307
-
308
- // ✅ Dispose at the right time
309
- const fx = effect((t) => {
310
- console.log($count.get(t))
311
- })
312
-
313
- // Use the effect...
314
- $count.set(1) // Effect runs
315
-
316
- // Now dispose when truly done
317
- fx.dispose()
318
- ```
319
-
320
- ### Pitfall 3: Forgetting Stream Cleanup
321
-
322
- ```typescript
323
- // ❌ WebSocket never closed
324
- function connectWebSocket() {
325
- const $ws = stream<string>((set) => {
326
- const socket = new WebSocket('ws://...')
327
- socket.onmessage = (e) => set(e.data)
328
- // Missing: return () => socket.close()
329
- })
330
- return $ws
331
- }
332
-
333
- // ✅ Proper cleanup
334
- function connectWebSocket() {
335
- const $ws = stream<string>((set) => {
336
- const socket = new WebSocket('ws://...')
337
- socket.onmessage = (e) => set(e.data)
338
-
339
- return () => socket.close() // Cleanup function
340
- })
341
- return $ws
342
- }
343
- ```
344
-
345
- ## Disposal Patterns
346
-
347
- ### Pattern 1: Disposable Builder
348
-
349
- ```typescript
350
- class DisposableBuilder {
351
- private disposables: FlowDisposable[] = []
352
-
353
- add<T extends FlowDisposable>(disposable: T): T {
354
- this.disposables.push(disposable)
355
- return disposable
356
- }
357
-
358
- disposeAll(): void {
359
- // Dispose in reverse order
360
- for (let i = this.disposables.length - 1; i >= 0; i--) {
361
- this.disposables[i].dispose()
362
- }
363
- this.disposables = []
364
- }
365
- }
366
-
367
- // Usage
368
- const builder = new DisposableBuilder()
369
- builder.add(effect((t) => { /* ... */ }))
370
- builder.add(effect((t) => { /* ... */ }))
371
- builder.disposeAll()
372
- ```
373
-
374
- ### Pattern 2: Scoped Disposables
375
-
376
- ```typescript
377
- function withDisposables<T>(
378
- fn: (track: <D extends FlowDisposable>(d: D) => D) => T
379
- ): T & { cleanup: () => void } {
380
- const disposables: FlowDisposable[] = []
381
-
382
- const track = <D extends FlowDisposable>(d: D): D => {
383
- disposables.push(d)
384
- return d
385
- }
386
-
387
- const result = fn(track) as T & { cleanup: () => void }
388
- result.cleanup = () => disposables.forEach(d => d.dispose())
389
-
390
- return result
391
- }
392
-
393
- // Usage
394
- const { cleanup } = withDisposables((track) => {
395
- const $count = track(state(0))
396
- const fx = track(effect((t) => console.log($count.get(t))))
397
- })
398
-
399
- // Clean up everything
400
- cleanup()
401
- ```
402
-
403
- ### Pattern 3: Auto-disposal on Condition
404
-
405
- ```typescript
406
- function createAutoDisposeEffect(
407
- condition: () => boolean,
408
- apply: (t: TrackingContext) => void
409
- ): FlowEffect {
410
- const fx = effect((t) => {
411
- if (!condition()) {
412
- fx.dispose()
413
- return
414
- }
415
- apply(t)
416
- })
417
- return fx
418
- }
419
-
420
- // Usage: effect auto-disposes when count > 10
421
- createAutoDisposeEffect(
422
- () => $count.pick() <= 10,
423
- (t) => console.log('Count:', $count.get(t))
424
- )
425
- ```
426
-