@ersbeth/picoflow 0.2.3 → 1.0.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 (248) hide show
  1. package/.cursor/plans/update-js-e795d61b.plan.md +567 -0
  2. package/.gitlab-ci.yml +24 -0
  3. package/.vscode/settings.json +3 -3
  4. package/CHANGELOG.md +51 -0
  5. package/IMPLEMENTATION_GUIDE.md +1578 -0
  6. package/README.md +62 -25
  7. package/biome.json +32 -32
  8. package/dist/picoflow.js +557 -1099
  9. package/dist/types/advanced/array.d.ts +0 -6
  10. package/dist/types/advanced/array.d.ts.map +1 -1
  11. package/dist/types/advanced/index.d.ts +5 -5
  12. package/dist/types/advanced/index.d.ts.map +1 -1
  13. package/dist/types/advanced/map.d.ts +114 -23
  14. package/dist/types/advanced/map.d.ts.map +1 -1
  15. package/dist/types/advanced/resource.d.ts +51 -12
  16. package/dist/types/advanced/resource.d.ts.map +1 -1
  17. package/dist/types/advanced/resourceAsync.d.ts +28 -13
  18. package/dist/types/advanced/resourceAsync.d.ts.map +1 -1
  19. package/dist/types/advanced/stream.d.ts +74 -16
  20. package/dist/types/advanced/stream.d.ts.map +1 -1
  21. package/dist/types/advanced/streamAsync.d.ts +69 -15
  22. package/dist/types/advanced/streamAsync.d.ts.map +1 -1
  23. package/dist/types/basic/constant.d.ts +44 -16
  24. package/dist/types/basic/constant.d.ts.map +1 -1
  25. package/dist/types/basic/derivation.d.ts +73 -24
  26. package/dist/types/basic/derivation.d.ts.map +1 -1
  27. package/dist/types/basic/disposable.d.ts +65 -6
  28. package/dist/types/basic/disposable.d.ts.map +1 -1
  29. package/dist/types/basic/effect.d.ts +27 -16
  30. package/dist/types/basic/effect.d.ts.map +1 -1
  31. package/dist/types/basic/index.d.ts +7 -8
  32. package/dist/types/basic/index.d.ts.map +1 -1
  33. package/dist/types/basic/observable.d.ts +62 -13
  34. package/dist/types/basic/observable.d.ts.map +1 -1
  35. package/dist/types/basic/signal.d.ts +35 -6
  36. package/dist/types/basic/signal.d.ts.map +1 -1
  37. package/dist/types/basic/state.d.ts +25 -4
  38. package/dist/types/basic/state.d.ts.map +1 -1
  39. package/dist/types/basic/trackingContext.d.ts +33 -0
  40. package/dist/types/basic/trackingContext.d.ts.map +1 -0
  41. package/dist/types/creators.d.ts +271 -26
  42. package/dist/types/creators.d.ts.map +1 -1
  43. package/dist/types/index.d.ts +60 -7
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/dist/types/solid/converters.d.ts +5 -5
  46. package/dist/types/solid/converters.d.ts.map +1 -1
  47. package/dist/types/solid/index.d.ts +2 -2
  48. package/dist/types/solid/index.d.ts.map +1 -1
  49. package/dist/types/solid/primitives.d.ts +96 -4
  50. package/dist/types/solid/primitives.d.ts.map +1 -1
  51. package/docs/.vitepress/config.mts +110 -0
  52. package/docs/api/classes/FlowArray.md +489 -0
  53. package/docs/api/classes/FlowConstant.md +350 -0
  54. package/docs/api/classes/FlowDerivation.md +334 -0
  55. package/docs/api/classes/FlowEffect.md +100 -0
  56. package/docs/api/classes/FlowMap.md +512 -0
  57. package/docs/api/classes/FlowObservable.md +306 -0
  58. package/docs/api/classes/FlowResource.md +380 -0
  59. package/docs/api/classes/FlowResourceAsync.md +362 -0
  60. package/docs/api/classes/FlowSignal.md +160 -0
  61. package/docs/api/classes/FlowState.md +368 -0
  62. package/docs/api/classes/FlowStream.md +367 -0
  63. package/docs/api/classes/FlowStreamAsync.md +364 -0
  64. package/docs/api/classes/SolidDerivation.md +75 -0
  65. package/docs/api/classes/SolidResource.md +91 -0
  66. package/docs/api/classes/SolidState.md +71 -0
  67. package/docs/api/classes/TrackingContext.md +33 -0
  68. package/docs/api/functions/array.md +58 -0
  69. package/docs/api/functions/constant.md +45 -0
  70. package/docs/api/functions/derivation.md +53 -0
  71. package/docs/api/functions/effect.md +49 -0
  72. package/docs/api/functions/from.md +220 -0
  73. package/docs/api/functions/isDisposable.md +49 -0
  74. package/docs/api/functions/map.md +57 -0
  75. package/docs/api/functions/resource.md +52 -0
  76. package/docs/api/functions/resourceAsync.md +50 -0
  77. package/docs/api/functions/signal.md +36 -0
  78. package/docs/api/functions/state.md +47 -0
  79. package/docs/api/functions/stream.md +53 -0
  80. package/docs/api/functions/streamAsync.md +50 -0
  81. package/docs/api/index.md +118 -0
  82. package/docs/api/interfaces/FlowDisposable.md +65 -0
  83. package/docs/api/interfaces/SolidObservable.md +19 -0
  84. package/docs/api/type-aliases/FlowArrayAction.md +49 -0
  85. package/docs/api/type-aliases/FlowStreamDisposer.md +15 -0
  86. package/docs/api/type-aliases/FlowStreamSetter.md +27 -0
  87. package/docs/api/type-aliases/FlowStreamUpdater.md +32 -0
  88. package/docs/api/type-aliases/NotPromise.md +18 -0
  89. package/docs/api/type-aliases/SolidGetter.md +17 -0
  90. package/docs/api/typedoc-sidebar.json +1 -0
  91. package/docs/examples/examples.md +2313 -0
  92. package/docs/examples/patterns.md +649 -0
  93. package/docs/guide/advanced/disposal.md +426 -0
  94. package/docs/guide/advanced/solidjs.md +221 -0
  95. package/docs/guide/advanced/upgrading.md +464 -0
  96. package/docs/guide/introduction/concepts.md +56 -0
  97. package/docs/guide/introduction/conventions.md +61 -0
  98. package/docs/guide/introduction/getting-started.md +134 -0
  99. package/docs/guide/introduction/lifecycle.md +371 -0
  100. package/docs/guide/primitives/array.md +400 -0
  101. package/docs/guide/primitives/constant.md +380 -0
  102. package/docs/guide/primitives/derivations.md +348 -0
  103. package/docs/guide/primitives/effects.md +458 -0
  104. package/docs/guide/primitives/map.md +387 -0
  105. package/docs/guide/primitives/overview.md +175 -0
  106. package/docs/guide/primitives/resources.md +858 -0
  107. package/docs/guide/primitives/signal.md +259 -0
  108. package/docs/guide/primitives/state.md +368 -0
  109. package/docs/guide/primitives/streams.md +931 -0
  110. package/docs/index.md +47 -0
  111. package/docs/public/logo.svg +1 -0
  112. package/package.json +57 -41
  113. package/src/advanced/array.ts +208 -210
  114. package/src/advanced/index.ts +7 -7
  115. package/src/advanced/map.ts +178 -68
  116. package/src/advanced/resource.ts +87 -43
  117. package/src/advanced/resourceAsync.ts +62 -42
  118. package/src/advanced/stream.ts +113 -50
  119. package/src/advanced/streamAsync.ts +120 -61
  120. package/src/basic/constant.ts +82 -49
  121. package/src/basic/derivation.ts +128 -84
  122. package/src/basic/disposable.ts +74 -15
  123. package/src/basic/effect.ts +85 -77
  124. package/src/basic/index.ts +7 -8
  125. package/src/basic/observable.ts +94 -36
  126. package/src/basic/signal.ts +133 -105
  127. package/src/basic/state.ts +46 -25
  128. package/src/basic/trackingContext.ts +45 -0
  129. package/src/creators.ts +297 -54
  130. package/src/index.ts +96 -43
  131. package/src/solid/converters.ts +186 -67
  132. package/src/solid/index.ts +8 -2
  133. package/src/solid/primitives.ts +167 -65
  134. package/test/array.test.ts +592 -612
  135. package/test/constant.test.ts +31 -33
  136. package/test/derivation.test.ts +531 -536
  137. package/test/effect.test.ts +21 -21
  138. package/test/map.test.ts +233 -137
  139. package/test/resource.test.ts +119 -121
  140. package/test/resourceAsync.test.ts +98 -100
  141. package/test/signal.test.ts +51 -55
  142. package/test/state.test.ts +186 -168
  143. package/test/stream.test.ts +189 -189
  144. package/test/streamAsync.test.ts +186 -186
  145. package/tsconfig.json +19 -18
  146. package/typedoc.json +37 -0
  147. package/vite.config.ts +23 -20
  148. package/vitest.config.ts +7 -7
  149. package/api/doc/index.md +0 -31
  150. package/api/doc/picoflow.array.md +0 -55
  151. package/api/doc/picoflow.constant.md +0 -55
  152. package/api/doc/picoflow.derivation.md +0 -55
  153. package/api/doc/picoflow.effect.md +0 -55
  154. package/api/doc/picoflow.flowarray._constructor_.md +0 -49
  155. package/api/doc/picoflow.flowarray._lastaction.md +0 -13
  156. package/api/doc/picoflow.flowarray.clear.md +0 -17
  157. package/api/doc/picoflow.flowarray.dispose.md +0 -55
  158. package/api/doc/picoflow.flowarray.get.md +0 -19
  159. package/api/doc/picoflow.flowarray.length.md +0 -13
  160. package/api/doc/picoflow.flowarray.md +0 -273
  161. package/api/doc/picoflow.flowarray.pop.md +0 -17
  162. package/api/doc/picoflow.flowarray.push.md +0 -53
  163. package/api/doc/picoflow.flowarray.set.md +0 -53
  164. package/api/doc/picoflow.flowarray.setitem.md +0 -69
  165. package/api/doc/picoflow.flowarray.shift.md +0 -17
  166. package/api/doc/picoflow.flowarray.splice.md +0 -85
  167. package/api/doc/picoflow.flowarray.unshift.md +0 -53
  168. package/api/doc/picoflow.flowarrayaction.md +0 -37
  169. package/api/doc/picoflow.flowconstant._constructor_.md +0 -49
  170. package/api/doc/picoflow.flowconstant.get.md +0 -25
  171. package/api/doc/picoflow.flowconstant.md +0 -88
  172. package/api/doc/picoflow.flowderivation._constructor_.md +0 -49
  173. package/api/doc/picoflow.flowderivation.get.md +0 -23
  174. package/api/doc/picoflow.flowderivation.md +0 -86
  175. package/api/doc/picoflow.flowdisposable.dispose.md +0 -55
  176. package/api/doc/picoflow.flowdisposable.md +0 -43
  177. package/api/doc/picoflow.floweffect._constructor_.md +0 -54
  178. package/api/doc/picoflow.floweffect.dispose.md +0 -21
  179. package/api/doc/picoflow.floweffect.disposed.md +0 -13
  180. package/api/doc/picoflow.floweffect.md +0 -131
  181. package/api/doc/picoflow.flowgetter.md +0 -15
  182. package/api/doc/picoflow.flowmap._lastdeleted.md +0 -21
  183. package/api/doc/picoflow.flowmap._lastset.md +0 -21
  184. package/api/doc/picoflow.flowmap.delete.md +0 -61
  185. package/api/doc/picoflow.flowmap.md +0 -133
  186. package/api/doc/picoflow.flowmap.setat.md +0 -77
  187. package/api/doc/picoflow.flowobservable.get.md +0 -19
  188. package/api/doc/picoflow.flowobservable.md +0 -68
  189. package/api/doc/picoflow.flowobservable.subscribe.md +0 -55
  190. package/api/doc/picoflow.flowresource._constructor_.md +0 -49
  191. package/api/doc/picoflow.flowresource.fetch.md +0 -27
  192. package/api/doc/picoflow.flowresource.get.md +0 -23
  193. package/api/doc/picoflow.flowresource.md +0 -100
  194. package/api/doc/picoflow.flowresourceasync._constructor_.md +0 -49
  195. package/api/doc/picoflow.flowresourceasync.fetch.md +0 -27
  196. package/api/doc/picoflow.flowresourceasync.get.md +0 -23
  197. package/api/doc/picoflow.flowresourceasync.md +0 -100
  198. package/api/doc/picoflow.flowsignal.dispose.md +0 -59
  199. package/api/doc/picoflow.flowsignal.disposed.md +0 -18
  200. package/api/doc/picoflow.flowsignal.md +0 -112
  201. package/api/doc/picoflow.flowsignal.trigger.md +0 -21
  202. package/api/doc/picoflow.flowstate.md +0 -52
  203. package/api/doc/picoflow.flowstate.set.md +0 -61
  204. package/api/doc/picoflow.flowstream._constructor_.md +0 -49
  205. package/api/doc/picoflow.flowstream.dispose.md +0 -21
  206. package/api/doc/picoflow.flowstream.get.md +0 -23
  207. package/api/doc/picoflow.flowstream.md +0 -100
  208. package/api/doc/picoflow.flowstreamasync._constructor_.md +0 -54
  209. package/api/doc/picoflow.flowstreamasync.dispose.md +0 -21
  210. package/api/doc/picoflow.flowstreamasync.get.md +0 -23
  211. package/api/doc/picoflow.flowstreamasync.md +0 -100
  212. package/api/doc/picoflow.flowstreamdisposer.md +0 -13
  213. package/api/doc/picoflow.flowstreamsetter.md +0 -13
  214. package/api/doc/picoflow.flowstreamupdater.md +0 -19
  215. package/api/doc/picoflow.flowwatcher.md +0 -15
  216. package/api/doc/picoflow.from.md +0 -55
  217. package/api/doc/picoflow.from_1.md +0 -55
  218. package/api/doc/picoflow.from_2.md +0 -55
  219. package/api/doc/picoflow.from_3.md +0 -55
  220. package/api/doc/picoflow.from_4.md +0 -55
  221. package/api/doc/picoflow.from_5.md +0 -55
  222. package/api/doc/picoflow.isdisposable.md +0 -55
  223. package/api/doc/picoflow.map.md +0 -59
  224. package/api/doc/picoflow.md +0 -544
  225. package/api/doc/picoflow.resource.md +0 -55
  226. package/api/doc/picoflow.resourceasync.md +0 -55
  227. package/api/doc/picoflow.signal.md +0 -19
  228. package/api/doc/picoflow.solidderivation._constructor_.md +0 -49
  229. package/api/doc/picoflow.solidderivation.get.md +0 -13
  230. package/api/doc/picoflow.solidderivation.md +0 -94
  231. package/api/doc/picoflow.solidgetter.md +0 -13
  232. package/api/doc/picoflow.solidobservable.get.md +0 -13
  233. package/api/doc/picoflow.solidobservable.md +0 -57
  234. package/api/doc/picoflow.solidresource._constructor_.md +0 -49
  235. package/api/doc/picoflow.solidresource.get.md +0 -13
  236. package/api/doc/picoflow.solidresource.latest.md +0 -13
  237. package/api/doc/picoflow.solidresource.md +0 -157
  238. package/api/doc/picoflow.solidresource.refetch.md +0 -13
  239. package/api/doc/picoflow.solidresource.state.md +0 -13
  240. package/api/doc/picoflow.solidstate._constructor_.md +0 -49
  241. package/api/doc/picoflow.solidstate.get.md +0 -13
  242. package/api/doc/picoflow.solidstate.md +0 -115
  243. package/api/doc/picoflow.solidstate.set.md +0 -13
  244. package/api/doc/picoflow.state.md +0 -55
  245. package/api/doc/picoflow.stream.md +0 -55
  246. package/api/doc/picoflow.streamasync.md +0 -55
  247. package/api/picoflow.public.api.md +0 -244
  248. package/api-extractor.json +0 -61
@@ -0,0 +1,649 @@
1
+ # Common Patterns
2
+
3
+ This guide presents common patterns and best practices for working with PicoFlow's reactive primitives. These patterns will help you write more efficient and maintainable reactive code.
4
+
5
+ ## Pattern 1: Computed Display Values
6
+
7
+ Use derivations to create formatted or computed values for display:
8
+
9
+ ```typescript
10
+ const $price = state(99.99)
11
+ const $quantity = state(2)
12
+
13
+ const $formatted = derivation((t) => {
14
+ const price = $price.get(t)
15
+ const quantity = $quantity.get(t)
16
+ const total = price * quantity
17
+
18
+ return `${quantity} × $${price.toFixed(2)} = $${total.toFixed(2)}`
19
+ })
20
+
21
+ effect((t) => {
22
+ console.log($formatted.get(t))
23
+ })
24
+
25
+ $quantity.set(3) // Logs: "3 × $99.99 = $299.97"
26
+ ```
27
+
28
+ **Use cases:** Formatted strings, currency display, computed totals, user-friendly messages
29
+
30
+ ## Pattern 2: Conditional Tracking
31
+
32
+ Only track a dependency when certain conditions are met:
33
+
34
+ ```typescript
35
+ const $enabled = state(true)
36
+ const $data = state('some data')
37
+
38
+ effect((t) => {
39
+ if ($enabled.get(t)) {
40
+ // Only tracks $data when enabled
41
+ console.log('Data:', $data.get(t))
42
+ } else {
43
+ console.log('Disabled')
44
+ }
45
+ })
46
+
47
+ $data.set('new data') // Logs if enabled
48
+ $enabled.set(false) // Now data changes won't log
49
+ ```
50
+
51
+ **Use cases:** Feature flags, conditional data fetching, dynamic subscriptions
52
+
53
+ ## Pattern 3: Non-Reactive Snapshot
54
+
55
+ Take a snapshot of a reactive value without creating a dependency:
56
+
57
+ ```typescript
58
+ const $livePrice = state(100)
59
+ const $refreshTrigger = signal()
60
+
61
+ effect((t) => {
62
+ // React to trigger only
63
+ $refreshTrigger.watch(t)
64
+
65
+ // Take snapshot of current price (no dependency)
66
+ const snapshot = $livePrice.pick()
67
+ console.log('Snapshot taken:', snapshot)
68
+ })
69
+
70
+ $livePrice.set(200) // Does NOT trigger effect
71
+ $refreshTrigger.trigger() // DOES trigger, logs current price
72
+ ```
73
+
74
+ **Use cases:** Manual refresh with current values, avoiding circular dependencies, comparison snapshots
75
+
76
+ ## Pattern 4: Snapshot + Reactive
77
+
78
+ Mix reactive and non-reactive reads:
79
+
80
+ ```typescript
81
+ effect((t) => {
82
+ const currentId = $userId.get(t) // Reactive - triggers re-run
83
+ const config = $appConfig.pick() // Non-reactive snapshot
84
+
85
+ fetchUserData(currentId, config)
86
+ })
87
+ ```
88
+
89
+ ### Use Cases
90
+
91
+ - Using configuration that shouldn't trigger updates
92
+ - Taking snapshots for comparison
93
+ - Avoiding circular dependencies
94
+
95
+ ### Example: API Call with Static Config
96
+
97
+ ```typescript
98
+ const $searchQuery = state('')
99
+ const $apiConfig = state({ baseUrl: 'https://api.example.com', timeout: 5000 })
100
+
101
+ effect((t) => {
102
+ const query = $searchQuery.get(t) // React to query changes
103
+ const config = $apiConfig.pick() // Use current config without tracking
104
+
105
+ if (query.length > 2) {
106
+ fetch(`${config.baseUrl}/search?q=${query}`, {
107
+ signal: AbortSignal.timeout(config.timeout)
108
+ })
109
+ }
110
+ })
111
+ ```
112
+
113
+ ### Example: Previous Value Comparison
114
+
115
+ ```typescript
116
+ const $count = state(0)
117
+
118
+ effect((t) => {
119
+ const previous = $count.pick() // Snapshot before reactive read
120
+ const current = $count.get(t) // Reactive read
121
+
122
+ if (current > previous) {
123
+ console.log('Count increased')
124
+ }
125
+ })
126
+ ```
127
+
128
+ ## Pattern 5: Batch Updates
129
+
130
+ Multiple effects can depend on the same state:
131
+
132
+ ```typescript
133
+ const $data = state({ count: 0, name: 'test' })
134
+
135
+ effect((t) => console.log('Count:', $data.get(t).count))
136
+ effect((t) => console.log('Name:', $data.get(t).name))
137
+
138
+ $data.set({ count: 1, name: 'updated' }) // Both effects run once
139
+ ```
140
+
141
+ ### Use Cases
142
+
143
+ - Multiple UI components reacting to the same state
144
+ - Coordinated updates across different parts of the app
145
+ - Efficient bulk operations
146
+
147
+ ### Example: Form State Management
148
+
149
+ ```typescript
150
+ interface FormState {
151
+ username: string
152
+ email: string
153
+ isValid: boolean
154
+ }
155
+
156
+ const $form = state<FormState>({
157
+ username: '',
158
+ email: '',
159
+ isValid: false
160
+ })
161
+
162
+ // Multiple effects react to different parts
163
+ effect((t) => {
164
+ const username = $form.get(t).username
165
+ validateUsername(username)
166
+ })
167
+
168
+ effect((t) => {
169
+ const email = $form.get(t).email
170
+ validateEmail(email)
171
+ })
172
+
173
+ effect((t) => {
174
+ const form = $form.get(t)
175
+ updateSubmitButton(form.isValid)
176
+ })
177
+
178
+ // Single update triggers all effects once
179
+ function updateForm(updates: Partial<FormState>) {
180
+ $form.set(current => ({ ...current, ...updates }))
181
+ }
182
+ ```
183
+
184
+ ## Pattern 6: Derived State Chain
185
+
186
+ Create chains of computed values:
187
+
188
+ ```typescript
189
+ const $items = state<Item[]>([])
190
+
191
+ const $activeItems = derivation((t) =>
192
+ $items.get(t).filter(item => item.active)
193
+ )
194
+
195
+ const $itemCount = derivation((t) =>
196
+ $activeItems.get(t).length
197
+ )
198
+
199
+ const $statusMessage = derivation((t) => {
200
+ const count = $itemCount.get(t)
201
+ return count === 0 ? 'No items' : `${count} items`
202
+ })
203
+
204
+ effect((t) => {
205
+ console.log($statusMessage.get(t))
206
+ })
207
+ ```
208
+
209
+ ### Benefits
210
+
211
+ - Clear separation of concerns
212
+ - Efficient caching at each level
213
+ - Easy to test individual derivations
214
+
215
+ ## Pattern 7: Signal for Manual Refresh
216
+
217
+ Use signals to trigger manual updates:
218
+
219
+ ```typescript
220
+ const $refreshTrigger = signal()
221
+ const $data = state<Data>({ cached: true })
222
+
223
+ effect((t) => {
224
+ $refreshTrigger.watch(t) // React to manual trigger
225
+
226
+ // Fetch fresh data
227
+ fetchData().then(data => $data.set(data))
228
+ })
229
+
230
+ // Trigger refresh from anywhere
231
+ $refreshTrigger.trigger()
232
+ ```
233
+
234
+ ### Use Cases
235
+
236
+ - Manual data refresh
237
+ - Force recalculation
238
+ - Synchronization points
239
+
240
+ ## Pattern 8: Cleanup with Effect Disposer
241
+
242
+ Effects can return cleanup functions:
243
+
244
+ ```typescript
245
+ const $url = state('https://api.example.com/data')
246
+
247
+ effect((t) => {
248
+ const url = $url.get(t)
249
+ const controller = new AbortController()
250
+
251
+ fetch(url, { signal: controller.signal })
252
+ .then(/* handle response */)
253
+
254
+ // Cleanup when effect re-runs or disposes
255
+ return () => controller.abort()
256
+ })
257
+ ```
258
+
259
+ ### Use Cases
260
+
261
+ - Canceling pending requests
262
+ - Clearing timers
263
+ - Unsubscribing from events
264
+
265
+ ## Pattern 9: Lazy Loading with Resources
266
+
267
+ Use resources for async data that depends on other reactive values:
268
+
269
+ ```typescript
270
+ const $userId = state(1)
271
+
272
+ const $user = resource(
273
+ (t) => $userId.get(t), // Reactive source
274
+ (id) => fetchUser(id) // Async fetcher
275
+ )
276
+
277
+ effect((t) => {
278
+ const user = $user.get(t)
279
+ if (user.state === 'ready') {
280
+ console.log('User:', user.value)
281
+ }
282
+ })
283
+ ```
284
+
285
+ ### Benefits
286
+
287
+ - Built-in loading/error states
288
+ - Automatic refetching when dependencies change
289
+ - Cancellation of stale requests
290
+
291
+ ## Pattern 10: Conditional Computation (Derivations)
292
+
293
+ Skip expensive derivation computation when not needed:
294
+
295
+ ```typescript
296
+ const $enabled = state(false)
297
+ const $data = state([1, 2, 3, 4, 5])
298
+
299
+ const $processedData = derivation((t) => {
300
+ if (!$enabled.get(t)) {
301
+ return [] // Skip computation when disabled
302
+ }
303
+
304
+ // Only compute when enabled
305
+ return $data.get(t).map(n => n * 2).filter(n => n > 5)
306
+ })
307
+ ```
308
+
309
+ ### Use Cases
310
+
311
+ - Feature flags that control expensive computations
312
+ - Conditional data processing
313
+ - Performance optimization for optional features
314
+
315
+ ## Pattern 11: Memoization Within Derivation
316
+
317
+ Use derivations to cache expensive theme or configuration calculations:
318
+
319
+ ```typescript
320
+ const $config = state({ theme: 'dark', lang: 'en' })
321
+
322
+ const $themeColors = derivation((t) => {
323
+ const theme = $config.get(t).theme
324
+
325
+ // Expensive computation
326
+ return theme === 'dark'
327
+ ? generateDarkPalette()
328
+ : generateLightPalette()
329
+ })
330
+ ```
331
+
332
+ ### Use Cases
333
+
334
+ - Theme color palette generation
335
+ - Expensive configuration parsing
336
+ - Complex UI calculations
337
+
338
+ ## Pattern 12: Combining Multiple Derivations
339
+
340
+ Chain derivations to create a computation pipeline:
341
+
342
+ ```typescript
343
+ const $sales = state([100, 200, 150])
344
+ const $expenses = state([50, 80, 60])
345
+
346
+ const $totalSales = derivation((t) => {
347
+ return $sales.get(t).reduce((a, b) => a + b, 0)
348
+ })
349
+
350
+ const $totalExpenses = derivation((t) => {
351
+ return $expenses.get(t).reduce((a, b) => a + b, 0)
352
+ })
353
+
354
+ const $profit = derivation((t) => {
355
+ return $totalSales.get(t) - $totalExpenses.get(t)
356
+ })
357
+
358
+ const $profitMargin = derivation((t) => {
359
+ const profit = $profit.get(t)
360
+ const sales = $totalSales.get(t)
361
+ return sales > 0 ? (profit / sales) * 100 : 0
362
+ })
363
+ ```
364
+
365
+ Dependency graph:
366
+
367
+ ```mermaid
368
+ graph TD
369
+ A[$sales] --> C[$totalSales]
370
+ B[$expenses] --> D[$totalExpenses]
371
+ C --> E[$profit]
372
+ D --> E
373
+ E --> F[$profitMargin]
374
+ C --> F
375
+
376
+ style A fill:#FFE6E6
377
+ style B fill:#FFE6E6
378
+ style C fill:#E6F3FF
379
+ style D fill:#E6F3FF
380
+ style E fill:#E6F3FF
381
+ style F fill:#E6F3FF
382
+ ```
383
+
384
+ ### Benefits
385
+
386
+ - Each derivation is simple and focused
387
+ - Efficient caching at each level
388
+ - Easy to test and debug
389
+ - Clear dependency relationships
390
+
391
+ ## Pattern 13: Logging and Debugging with Effects
392
+
393
+ Use effects for reactive logging and debugging:
394
+
395
+ ```typescript
396
+ const $count = state(0)
397
+
398
+ // Debug logger
399
+ effect((t) => {
400
+ const value = $count.get(t)
401
+ console.log('[DEBUG] count changed:', value, 'at', new Date())
402
+ })
403
+ ```
404
+
405
+ ### Use Cases
406
+
407
+ - Development debugging
408
+ - Tracking state changes
409
+ - Performance monitoring
410
+ - Audit logs
411
+
412
+ ## Pattern 14: DOM Updates with Effects
413
+
414
+ Effects are perfect for updating the DOM based on reactive state:
415
+
416
+ ```typescript
417
+ const $username = state('')
418
+
419
+ effect((t) => {
420
+ const name = $username.get(t)
421
+ document.getElementById('username').textContent = name
422
+ })
423
+ ```
424
+
425
+ ### Use Cases
426
+
427
+ - Updating text content
428
+ - Toggling classes
429
+ - Managing visibility
430
+ - Synchronizing UI with state
431
+
432
+ ## Pattern 15: LocalStorage Sync with Effects
433
+
434
+ Automatically persist state to localStorage:
435
+
436
+ ```typescript
437
+ const $preferences = state({ theme: 'light', lang: 'en' })
438
+
439
+ effect((t) => {
440
+ const prefs = $preferences.get(t)
441
+ localStorage.setItem('preferences', JSON.stringify(prefs))
442
+ })
443
+ ```
444
+
445
+ ### Use Cases
446
+
447
+ - User preferences
448
+ - Draft autosave
449
+ - Cache management
450
+ - Offline-first applications
451
+
452
+ ## Pattern 16: Derived Side Effects
453
+
454
+ Combine derivations with effects for complex reactive behaviors:
455
+
456
+ ```typescript
457
+ const $items = state([1, 2, 3])
458
+ const $total = derivation((t) => {
459
+ return $items.get(t).reduce((sum, n) => sum + n, 0)
460
+ })
461
+
462
+ effect((t) => {
463
+ const total = $total.get(t)
464
+ if (total > 100) {
465
+ alert('Total exceeded limit!')
466
+ }
467
+ })
468
+ ```
469
+
470
+ ### Use Cases
471
+
472
+ - Threshold alerts
473
+ - Conditional notifications
474
+ - Validation side effects
475
+ - Derived triggers
476
+
477
+ ## Pattern 17: Batch Updates with Maps
478
+
479
+ When updating multiple map entries, consider the notification pattern:
480
+
481
+ ```typescript
482
+ // ❌ Multiple updates = multiple notifications
483
+ $map.add('a', 1)
484
+ $map.add('b', 2)
485
+ $map.add('c', 3)
486
+ // Each operation triggers notifications
487
+
488
+ // ✅ Consider if batching is possible at a higher level
489
+ // Note: PicoFlow doesn't have built-in batching, but you can
490
+ // minimize unnecessary work by grouping related operations
491
+ const updates = [
492
+ { key: 'a', value: 1 },
493
+ { key: 'b', value: 2 },
494
+ { key: 'c', value: 3 }
495
+ ]
496
+ for (const { key, value } of updates) {
497
+ const existing = $map.pick().has(key)
498
+ if (existing) {
499
+ $map.update(key, value)
500
+ } else {
501
+ $map.add(key, value)
502
+ }
503
+ }
504
+ ```
505
+
506
+ **Use cases:** Bulk operations, data synchronization, minimizing UI updates during batch changes
507
+
508
+ ## Pattern 18: Selective Tracking with Maps
509
+
510
+ Track only the operations you need, not all operations:
511
+
512
+ ```typescript
513
+ // Track only additions, not updates or deletions
514
+ effect((t) => {
515
+ const lastAdded = $users.$lastAdded.get(t)
516
+ if (lastAdded) {
517
+ handleNewUser(lastAdded.value)
518
+ }
519
+ })
520
+
521
+ // Separate effect for deletions
522
+ effect((t) => {
523
+ const lastDeleted = $users.$lastDeleted.get(t)
524
+ if (lastDeleted) {
525
+ handleRemovedUser(lastDeleted.key)
526
+ }
527
+ })
528
+
529
+ // Separate effect for updates
530
+ effect((t) => {
531
+ const lastUpdated = $users.$lastUpdated.get(t)
532
+ if (lastUpdated) {
533
+ handleUpdatedUser(lastUpdated.value)
534
+ }
535
+ })
536
+ ```
537
+
538
+ **Use cases:** Different UI animations for different operations, selective logging, operation-specific side effects
539
+
540
+ ## Pattern 19: Combine Coarse and Fine-Grained Tracking
541
+
542
+ Use both whole-map tracking and fine-grained tracking together:
543
+
544
+ ```typescript
545
+ // Coarse: Update count (reacts to any change)
546
+ effect((t) => {
547
+ const count = $items.get(t).size
548
+ updateCountDisplay(count)
549
+ })
550
+
551
+ // Fine: Animate specific changes
552
+ effect((t) => {
553
+ const lastAdded = $items.$lastAdded.get(t)
554
+ if (lastAdded) {
555
+ animateNewItem(lastAdded.key, lastAdded.value)
556
+ }
557
+ })
558
+
559
+ effect((t) => {
560
+ const lastDeleted = $items.$lastDeleted.get(t)
561
+ if (lastDeleted) {
562
+ animateRemovedItem(lastDeleted.key)
563
+ }
564
+ })
565
+ ```
566
+
567
+ **Use cases:** Combining aggregate statistics with individual item animations, efficient UI updates that need both totals and specific changes
568
+
569
+ ## Pattern 20: Batch Updates with Arrays
570
+
571
+ Minimize notifications by batching multiple operations:
572
+
573
+ ```typescript
574
+ // ❌ Multiple updates = multiple notifications
575
+ $items.push(1)
576
+ $items.push(2)
577
+ $items.push(3)
578
+
579
+ // ✅ Use splice for batch additions
580
+ $items.splice($items.length, 0, 1, 2, 3)
581
+
582
+ // Or use set() to replace entire array if appropriate
583
+ const newItems = [...$items.pick(), 1, 2, 3]
584
+ $items.set(newItems)
585
+ ```
586
+
587
+ **Use cases:** Bulk operations, data synchronization, minimizing UI updates during batch changes
588
+
589
+ ## Pattern 21: Selective Tracking with Arrays
590
+
591
+ Track only the operations you need, not all operations:
592
+
593
+ ```typescript
594
+ // Track only additions, not deletions
595
+ effect((t) => {
596
+ const action = $items.$lastAction.get(t)
597
+ if (action && action.type === 'push') {
598
+ handleNewItem(action.item)
599
+ }
600
+ })
601
+
602
+ // Separate effect for deletions
603
+ effect((t) => {
604
+ const action = $items.$lastAction.get(t)
605
+ if (action && (action.type === 'pop' || action.type === 'splice')) {
606
+ handleRemovedItems(action)
607
+ }
608
+ })
609
+
610
+ // Separate effect for updates
611
+ effect((t) => {
612
+ const action = $items.$lastAction.get(t)
613
+ if (action && action.type === 'setItem') {
614
+ handleUpdatedItem(action.index, action.item)
615
+ }
616
+ })
617
+ ```
618
+
619
+ **Use cases:** Different UI animations for different operations, selective logging, operation-specific side effects
620
+
621
+ ## Pattern 22: Combine Coarse and Fine-Grained Array Tracking
622
+
623
+ Use both whole-array tracking and fine-grained tracking together:
624
+
625
+ ```typescript
626
+ // Coarse: Update count (reacts to any change)
627
+ effect((t) => {
628
+ const count = $items.get(t).length
629
+ updateCountDisplay(count)
630
+ })
631
+
632
+ // Fine: Animate specific changes
633
+ effect((t) => {
634
+ const action = $items.$lastAction.get(t)
635
+ if (action && action.type === 'push') {
636
+ animateNewItem(action.item)
637
+ }
638
+ })
639
+
640
+ effect((t) => {
641
+ const action = $items.$lastAction.get(t)
642
+ if (action && action.type === 'splice') {
643
+ animateRemovedItems(action.start, action.deleteCount)
644
+ }
645
+ })
646
+ ```
647
+
648
+ **Use cases:** Combining aggregate statistics with individual item animations, efficient UI updates that need both totals and specific changes
649
+