@ersbeth/picoflow 0.2.4 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +9 -134
  7. package/biome.json +32 -32
  8. package/dist/picoflow.js +610 -436
  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 -23
  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,387 @@
1
+ # Maps
2
+
3
+ PicoFlow provides reactive maps with **fine-grained tracking**. Instead of reacting to the entire map changing, you can react to specific operations like adding, updating, or deleting individual entries.
4
+
5
+ Imagine you have a map of 1000 users. In a naive reactive system, every time you add, remove, or update a single user, the entire map re-renders. **Inefficient!** With PicoFlow's reactive maps, you can track specific operations and only update what changed.
6
+
7
+ ### Key Characteristics
8
+
9
+ - **Fine-grained reactivity**: Track individual operations (add, update, delete) separately
10
+ - **Multiple tracking levels**: Track the whole map or specific operations
11
+ - **Native Map**: Uses JavaScript's native `Map<K, V>` internally
12
+ - **Operation-specific signals**: `$lastAdded`, `$lastUpdated`, `$lastDeleted` for granular tracking
13
+ - **Disposable**: Can be disposed to clean up resources
14
+
15
+ ## When to Use Maps
16
+
17
+ Use maps when you need to:
18
+
19
+ - ✅ Track large collections with fine-grained updates
20
+ - ✅ React to specific operations (additions, updates, deletions) separately
21
+ - ✅ Manage key-value pairs where individual key operations matter
22
+ - ✅ Optimize performance by avoiding full map re-processing
23
+ - ✅ Animate or handle UI updates for specific changes
24
+
25
+ Don't use maps when:
26
+
27
+ - ❌ You have a small, simple object (use `state` instead)
28
+ - ❌ You don't need fine-grained tracking (use `state` with a Record)
29
+ - ❌ You need array-like operations (use `array` instead)
30
+ - ❌ The collection is rarely modified (regular state might be simpler)
31
+
32
+ ## Creating Maps
33
+
34
+ Creating a reactive map is straightforward:
35
+
36
+ ```typescript
37
+ import { map } from '@ersbeth/picoflow'
38
+
39
+ // Create with initial values (key is user id, value is User object)
40
+ const $usersByID = map<string, User>({
41
+ aliceID: { name: 'Alice', email: 'alice@example.com', role: 'admin' },
42
+ bobID: { name: 'Bob', email: 'bob@example.com', role: 'user' }
43
+ })
44
+
45
+ // Create empty
46
+ const $cache = map<string, Data>()
47
+ ```
48
+
49
+ The initial values (if provided) are converted to a native JavaScript `Map`.
50
+
51
+ ## Using Maps
52
+
53
+ Maps provide multiple ways to track changes and perform operations.
54
+
55
+ ### Map Operations
56
+
57
+ Maps provide three main operations: `add()`, `update()`, and `delete()`.
58
+
59
+ ```typescript
60
+ const $cache = map<string, Data>()
61
+
62
+ // Add a new key-value pair
63
+ $cache.add('user:1', userData)
64
+
65
+ // Update an existing key-value pair
66
+ $cache.update('user:1', updatedUserData)
67
+
68
+ // Delete a key-value pair
69
+ $cache.delete('user:1')
70
+
71
+ // Read (reactive)
72
+ effect((t) => {
73
+ const data = $cache.get(t)
74
+ // Track entire map - returns Map<string, Data>
75
+ const hasUser = data.has('user:1')
76
+ if (hasUser) {
77
+ const user = data.get('user:1')
78
+ // Use the user data
79
+ }
80
+ })
81
+
82
+ // Read (non-reactive)
83
+ const snapshot = $cache.pick() // Returns Map<string, Data>
84
+ const user = snapshot.get('user:1')
85
+ ```
86
+
87
+ **Important**:
88
+ - `add(key, value)` throws an error if the key already exists
89
+ - `update(key, value)` throws an error if the key doesn't exist
90
+ - `delete(key)` throws an error if the key doesn't exist
91
+
92
+
93
+ ### Whole Map Tracking with `.get(t)`
94
+
95
+ Track when the entire map changes (any operation):
96
+
97
+ ```typescript
98
+ import { map, effect } from '@ersbeth/picoflow'
99
+
100
+ const $items = map<string, number>()
101
+
102
+ // Initialize with values
103
+ $items.add('a', 1)
104
+ $items.add('b', 2)
105
+
106
+ effect((t) => {
107
+ const items = $items.get(t) // Track all changes
108
+ console.log('Map size:', items.size)
109
+ })
110
+
111
+ $items.add('c', 3) // Logs: "Map size: 3"
112
+ $items.delete('a') // Logs: "Map size: 2"
113
+ ```
114
+
115
+ Use `.get(t)` when you need the entire map's current state. It returns a native `Map<K, V>`.
116
+
117
+ ### Fine-Grained: Track Additions
118
+
119
+ Track only when items are added:
120
+
121
+ ```typescript
122
+ const $items = map<string, number>()
123
+
124
+ effect((t) => {
125
+ const lastAdded = $items.$lastAdded.get(t)
126
+ if (lastAdded) {
127
+ console.log(`Added ${lastAdded.key} = ${lastAdded.value}`)
128
+ }
129
+ })
130
+
131
+ $items.add('a', 1) // Logs: "Added a = 1"
132
+ $items.add('b', 2) // Logs: "Added b = 2"
133
+ $items.update('a', 10) // No log - not an addition!
134
+ $items.delete('a') // No log - not an addition!
135
+ ```
136
+
137
+ ### Fine-Grained: Track Updates
138
+
139
+ Track only when existing items are updated:
140
+
141
+ ```typescript
142
+ const $items = map<string, number>()
143
+
144
+ // Add some initial values
145
+ $items.add('a', 1)
146
+ $items.add('b', 2)
147
+
148
+ effect((t) => {
149
+ const lastUpdated = $items.$lastUpdated.get(t)
150
+ if (lastUpdated) {
151
+ console.log(`Updated ${lastUpdated.key} = ${lastUpdated.value}`)
152
+ }
153
+ })
154
+
155
+ $items.update('a', 10) // Logs: "Updated a = 10"
156
+ $items.add('c', 3) // No log - not an update!
157
+ $items.delete('a') // No log - not an update!
158
+ ```
159
+
160
+ ### Fine-Grained: Track Deletions
161
+
162
+ Track only when items are deleted:
163
+
164
+ ```typescript
165
+ const $items = map<string, number>()
166
+
167
+ // Add some initial values
168
+ $items.add('a', 1)
169
+ $items.add('b', 2)
170
+ $items.add('c', 3)
171
+
172
+ effect((t) => {
173
+ const lastDeleted = $items.$lastDeleted.get(t)
174
+ if (lastDeleted) {
175
+ console.log(`Deleted key: ${lastDeleted.key}, value was: ${lastDeleted.value}`)
176
+ }
177
+ })
178
+
179
+ $items.delete('a') // Logs: "Deleted key: a, value was: 1"
180
+ $items.add('d', 4) // No log - not a deletion!
181
+ $items.update('b', 20) // No log - not a deletion!
182
+ $items.delete('b') // Logs: "Deleted key: b, value was: 20"
183
+ ```
184
+
185
+ ## Lifecycle
186
+
187
+ When you create an effect that tracks a map:
188
+
189
+ 1. **Registration**: The map registers the effect as a watcher
190
+ 2. **Operation**: When you call `add()`, `update()`, or `delete()`, the map updates internally
191
+ 3. **Signal Update**: The appropriate signal (`$lastAdded`, `$lastUpdated`, or `$lastDeleted`) is updated
192
+ 4. **Notification**: All watching effects are scheduled to run
193
+ 5. **Re-execution**: Each watching effect re-executes its function
194
+
195
+ ```mermaid
196
+ sequenceDiagram
197
+ participant User
198
+ participant Map as $users (Map)
199
+ participant Signal as $lastAdded
200
+ participant Effect
201
+
202
+ Note over User,Effect: 1. Setup Phase
203
+ User->>Effect: Create effect
204
+ activate Effect
205
+ Effect->>Map: get(t)
206
+ Note over Map: Register Effect as watcher
207
+ Effect->>Signal: get(t)
208
+ Note over Signal: Register Effect as watcher
209
+ Effect->>Effect: Execute function
210
+ Note over Effect: Initial render
211
+ deactivate Effect
212
+
213
+ Note over User,Effect: 2. Operation Phase
214
+ User->>Map: add('user1', userData)
215
+ activate Map
216
+ Note over Map: Update internal Map
217
+ Map->>Signal: set({ key, value })
218
+ activate Signal
219
+ Note over Signal: Notify watchers
220
+ Signal->>Effect: Schedule execution
221
+ deactivate Signal
222
+ Map->>Map: Notify whole map watchers
223
+ Map->>Effect: Schedule execution
224
+ deactivate Map
225
+
226
+ activate Effect
227
+ Note over Effect: Re-execute function
228
+ Effect->>Signal: get(t)
229
+ Signal-->>Effect: { key: 'user1', value: userData }
230
+ Effect->>Effect: Handle new user
231
+ Note over Effect: Update UI
232
+ deactivate Effect
233
+ ```
234
+
235
+ ## Best Practices
236
+
237
+ ### Choose the Right Granularity
238
+
239
+ ```typescript
240
+ // ✅ Use fine-grained for large maps
241
+ const $users = map<number, User>()
242
+ effect((t) => {
243
+ const lastAdded = $users.$lastAdded.get(t)
244
+ if (lastAdded) {
245
+ // Handle specific change
246
+ renderNewUser(lastAdded.value)
247
+ }
248
+ })
249
+
250
+ // ✅ Use coarse-grained for small maps
251
+ const $settings = state({ theme: 'dark', lang: 'en', size: 'large' })
252
+ effect((t) => {
253
+ const settings = $settings.get(t)
254
+ // Re-apply all settings (cheap for 3 items)
255
+ applySettings(settings)
256
+ })
257
+ ```
258
+
259
+ ### Combine for Best Results
260
+
261
+ ```typescript
262
+ // Fine-grained for UI updates
263
+ effect((t) => {
264
+ const lastAdded = $items.$lastAdded.get(t)
265
+ if (lastAdded) {
266
+ updateUIIncremental(lastAdded)
267
+ }
268
+ })
269
+
270
+ // Coarse-grained for totals
271
+ const $total = derivation((t) => {
272
+ const items = $items.get(t)
273
+ let sum = 0
274
+ for (const value of items.values()) {
275
+ sum += value.price
276
+ }
277
+ return sum
278
+ })
279
+ ```
280
+
281
+ ## Common Pitfalls
282
+
283
+ ### Forgetting to Handle Nulls
284
+
285
+ **Problem**: The operation signals can be `null` initially.
286
+
287
+ ```typescript
288
+ // ❌ Can be null initially
289
+ effect((t) => {
290
+ const lastAdded = $items.$lastAdded.get(t)
291
+ console.log(lastAdded.key) // Error if null!
292
+ })
293
+ ```
294
+
295
+ **Solution**: Always check for null:
296
+
297
+ ```typescript
298
+ // ✅ Check for null
299
+ effect((t) => {
300
+ const lastAdded = $items.$lastAdded.get(t)
301
+ if (lastAdded) {
302
+ console.log(lastAdded.key)
303
+ }
304
+ })
305
+ ```
306
+
307
+ ### Using Wrong Tracking
308
+
309
+ **Problem**: Tracking the entire map when you only need specific operations.
310
+
311
+ ```typescript
312
+ // ❌ Tracks entire map when you only need additions
313
+ effect((t) => {
314
+ const items = $items.get(t) // Runs on ALL changes
315
+ const keys = Array.from(items.keys())
316
+ const lastKey = keys[keys.length - 1]
317
+ animateNewItem(lastKey)
318
+ })
319
+ ```
320
+
321
+ **Solution**: Track only what you need:
322
+
323
+ ```typescript
324
+ // ✅ Track only additions
325
+ effect((t) => {
326
+ const lastAdded = $items.$lastAdded.get(t)
327
+ if (lastAdded) {
328
+ animateNewItem(lastAdded.key)
329
+ }
330
+ })
331
+ ```
332
+
333
+ ### Confusing Add and Update
334
+
335
+ **Problem**: Using `add()` when the key might already exist, or `update()` when it might not.
336
+
337
+ ```typescript
338
+ // ❌ Will throw if key exists
339
+ function setUser(id: number, user: User) {
340
+ $users.add(id, user) // Error if user already exists!
341
+ }
342
+
343
+ // ❌ Will throw if key doesn't exist
344
+ function changeUser(id: number, user: User) {
345
+ $users.update(id, user) // Error if user doesn't exist!
346
+ }
347
+ ```
348
+
349
+ **Solution**: Check first or handle errors:
350
+
351
+ ```typescript
352
+ // ✅ Check before operation
353
+ function setUser(id: number, user: User) {
354
+ const users = $users.pick()
355
+ if (users.has(id)) {
356
+ $users.update(id, user)
357
+ } else {
358
+ $users.add(id, user)
359
+ }
360
+ }
361
+ ```
362
+
363
+ ### Mutating Retrieved Maps
364
+
365
+ **Problem**: Mutating the map directly instead of using reactive methods.
366
+
367
+ ```typescript
368
+ // ❌ Mutating the map directly
369
+ const items = $items.pick()
370
+ items.set('newKey', newValue) // Doesn't trigger reactivity!
371
+
372
+ // ❌ Mutating the map directly
373
+ const items = $items.get(t)
374
+ items.set('newKey', newValue) // Changes internal state but doesn't notify properly!
375
+ ```
376
+
377
+ **Solution**: Use reactive methods:
378
+
379
+ ```typescript
380
+ // ✅ Use reactive methods
381
+ const hasItem = $items.pick().has('newKey');
382
+ if (hasItem) {
383
+ $items.update('newKey', newValue)
384
+ } else {
385
+ $items.add('newKey', newValue)
386
+ }
387
+ ```
@@ -0,0 +1,175 @@
1
+ # Primitives Overview
2
+
3
+ PicoFlow provides several reactive primitives, each designed for specific use cases in reactive programming. This guide introduces all the primitives and shows how they work together.
4
+
5
+ ::: tip
6
+ Looking for naming conventions? Check out the **[Conventions](/guide/introduction/conventions)** guide to learn about the `$` prefix.
7
+ :::
8
+
9
+ ## Listing
10
+
11
+ PicoFlow provides several reactive primitives for different use cases:
12
+
13
+ | Primitive | Purpose | Example |
14
+ |-----------|---------|---------|
15
+ | **Signal** | Event notifications | Manual refresh, synchronization |
16
+ | **State** | Mutable values | Form inputs, toggles, counters |
17
+ | **Constant** | Immutable values | Configuration, initial values |
18
+ | **Derivation** | Computed values | Totals, filtered lists, formatted strings |
19
+ | **Effect** | Side effects | DOM updates, logging, API calls |
20
+ | **Resource** | Async data fetching | API calls, file loading |
21
+ | **Stream** | Event-driven data | WebSockets, timers, DOM events |
22
+ | **Map** | Reactive dictionaries | Key-value stores, caches |
23
+ | **Array** | Reactive lists | Todo lists, dynamic collections |
24
+
25
+ ## Details
26
+
27
+ ### Signal
28
+
29
+ **Event-like notifications without values** - use when you want to trigger effects without carrying data.
30
+
31
+ ```typescript
32
+ const $refresh = signal()
33
+ $refresh.trigger() // Tell everyone: "Refresh now!"
34
+ ```
35
+
36
+ **Use cases:** Manual refresh triggers, event notifications, synchronization points
37
+
38
+ ### State
39
+
40
+ **Mutable reactive values** - use for data that changes over time.
41
+
42
+ ```typescript
43
+ const $count = state(0)
44
+ $count.set(1)
45
+ $count.set(n => n + 1) // Updater function
46
+ ```
47
+
48
+ **Use cases:** Form inputs, counters, toggles, user data
49
+
50
+ ### Constant
51
+
52
+ **Immutable reactive values** - use for values that never change but need to be part of the reactive graph.
53
+
54
+ ```typescript
55
+ const $config = constant({ apiUrl: 'https://api.example.com', timeout: 5000 })
56
+ const $version = constant('1.0.0')
57
+ ```
58
+
59
+ **Use cases:** Configuration values, application constants, initial data
60
+
61
+ **Key feature:** Unlike regular JavaScript constants, these can be tracked in the reactive graph, making them useful for dependencies that shouldn't change.
62
+
63
+ ### Derivation
64
+
65
+ **Computed values** - use when you want to calculate something based on other reactive values.
66
+
67
+ ```typescript
68
+ const $double = derivation((t) => $count.get(t) * 2)
69
+ ```
70
+
71
+ **Use cases:** Totals, filtered lists, formatted values, derived UI state
72
+
73
+ **Key feature:** Lazy evaluation and caching - only recomputes when dependencies change AND when the value is accessed.
74
+
75
+ ### Effect
76
+
77
+ **Side effects** - use when you want to do something in response to changes (DOM updates, logging, etc.).
78
+
79
+ ```typescript
80
+ effect((t) => {
81
+ console.log('Count is:', $count.get(t))
82
+ })
83
+ ```
84
+
85
+ **Use cases:** Logging, DOM manipulation, API calls, persistence
86
+
87
+ **Key feature:** Eager execution - runs immediately when created and whenever dependencies change.
88
+
89
+ ### Resource
90
+
91
+ **Async data fetching** - use when you need to load data asynchronously and track its loading state.
92
+
93
+ ```typescript
94
+ const $userId = state(1)
95
+ const $user = resource(
96
+ (t) => $userId.get(t),
97
+ (id) => fetchUser(id)
98
+ )
99
+ ```
100
+
101
+ **Use cases:** API calls, file loading, async data fetching
102
+
103
+ **Key feature:** Provides loading, error, and ready states automatically, with built-in cancellation of stale requests.
104
+
105
+ ### Stream
106
+
107
+ **Event-driven data** - use when you want to handle continuous streams of events or data.
108
+
109
+ ```typescript
110
+ const $clicks = stream<MouseEvent>((emit) => {
111
+ document.addEventListener('click', emit)
112
+ return () => document.removeEventListener('click', emit)
113
+ })
114
+ ```
115
+
116
+ **Use cases:** WebSocket connections, timers, DOM events, real-time data
117
+
118
+ **Key feature:** Push-based model for handling continuous event streams with automatic cleanup.
119
+
120
+ ### Map
121
+
122
+ **Reactive dictionaries** - use when you need fine-grained tracking of key-value pairs.
123
+
124
+ ```typescript
125
+ const $users = map<number, User>({
126
+ 1: { id: 1, name: 'Alice' },
127
+ 2: { id: 2, name: 'Bob' }
128
+ })
129
+ $users.setAt(3, { id: 3, name: 'Charlie' })
130
+ ```
131
+
132
+ **Use cases:** Caches, entity stores, key-value collections
133
+
134
+ **Key feature:** Fine-grained reactivity - track additions, deletions, or changes to the entire map separately.
135
+
136
+ ### Array
137
+
138
+ **Reactive lists** - use when you need fine-grained tracking of array operations.
139
+
140
+ ```typescript
141
+ const $todos = array<Todo>([
142
+ { id: 1, text: 'Learn PicoFlow', done: false },
143
+ { id: 2, text: 'Build app', done: false }
144
+ ])
145
+ $todos.push({ id: 3, text: 'Deploy', done: false })
146
+ ```
147
+
148
+ **Use cases:** Todo lists, dynamic collections, ordered data
149
+
150
+ **Key feature:** Fine-grained reactivity - track specific operations (push, pop, splice) instead of full array changes.
151
+
152
+ ## Derivations vs Effects
153
+
154
+ What's the difference between a derivation and an effect? Both react to changes, but they serve different purposes:
155
+
156
+ | Feature | Derivation | Effect |
157
+ |---------|-----------|--------|
158
+ | Purpose | **Compute a value** | **Perform side effects** |
159
+ | Returns | A value | Nothing (void) |
160
+ | Should be pure? | ✅ Yes | ❌ No |
161
+ | Side effects allowed? | ❌ No | ✅ Yes |
162
+ | When it runs | Lazily (when accessed) | Immediately and on changes |
163
+ | Use case | Calculate totals, filter lists | Update DOM, save data, log |
164
+
165
+ ```mermaid
166
+ flowchart LR
167
+ A[Reactive Values] --> B[Derivation]
168
+ A --> C[Effect]
169
+ B --> D[New Computed Value]
170
+ C --> E[Side Effects<br/>DOM, API, Storage]
171
+ D -.-> F[Can be used by<br/>other derivations]
172
+ D -.-> G[Can be used by<br/>effects]
173
+ ```
174
+
175
+ **Key insight:** Use derivations for **calculations**, use effects for **actions**.