@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
@@ -1,83 +1,193 @@
1
- import { FlowState } from "../basic/";
1
+ import { FlowState, isDisposable } from "../basic/";
2
2
 
3
3
  /**
4
- * Represents a reactive map of states extending {@link FlowState} for a Map of key/value pairs.
4
+ * Represents a reactive map that extends {@link FlowState} for tracking key-value pairs.
5
5
  *
6
6
  * @remarks
7
- * FlowMap wraps a native Map and provides reactive signals for fine-grained tracking
8
- * of updates to the map. In addition to the reactive capabilities inherited from FlowState,
9
- * it exposes two reactive signals:
7
+ * FlowMap wraps a native JavaScript Map and provides reactive tracking at multiple granularity levels.
8
+ * Unlike plain reactive state, FlowMap offers fine-grained reactivity that lets you track:
10
9
  *
11
- * $lastSet: Holds the most recent key-value pair that was set.
12
- * $lastDeleted: Holds the most recent key-value pair that was deleted.
10
+ * 1. **Whole map changes**: Via `get(t)` or `pick()` on the FlowMap itself
11
+ * 2. **Last add operation**: Via the `$lastAdded` signal, track which key-value pair was most recently added
12
+ * 3. **Last update operation**: Via the `$lastUpdated` signal, track which key-value pair was most recently updated
13
+ * 4. **Last delete operation**: Via the `$lastDeleted` signal, track which key-value pair was most recently removed
13
14
  *
14
- * Use {@link FlowMap.setAt} to add or update a key-value pair and {@link FlowMap.delete} to remove a key.
15
+ * **Reactive Signals:**
16
+ * - **$lastAdded**: A FlowState containing `{ key?: K, value?: V }` updated on each `add()` call
17
+ * - **$lastUpdated**: A FlowState containing `{ key?: K, value?: V }` updated on each `update()` call
18
+ * - **$lastDeleted**: A FlowState containing `{ key?: K, value?: V }` updated on each `delete()` call
15
19
  *
16
- * @typeparam K - The type of the map keys.
17
- * @typeparam V - The type of the map values.
20
+ * These signals enable fine-grained reactivity patterns where effects can respond to specific
21
+ * map operations without re-processing the entire map.
22
+ *
23
+ * **Use Cases:**
24
+ * - Entity stores where you want to track additions/removals separately
25
+ * - Cache implementations with granular invalidation
26
+ * - Collections where operations on individual keys matter
27
+ * - UI state where you want to animate specific additions or removals
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const $users = map<string, User>();
32
+ *
33
+ * // Track the whole map
34
+ * effect((t) => {
35
+ * const users = $users.get(t);
36
+ * console.log(`Total users: ${users.size}`);
37
+ * });
38
+ *
39
+ * // Track only additions
40
+ * effect((t) => {
41
+ * const { key, value } = $users.$lastAdded.get(t);
42
+ * if (key && value) {
43
+ * console.log(`User ${key} was added:`, value);
44
+ * }
45
+ * });
46
+ *
47
+ * // Track only updates
48
+ * effect((t) => {
49
+ * const { key, value } = $users.$lastUpdated.get(t);
50
+ * if (key && value) {
51
+ * console.log(`User ${key} was updated:`, value);
52
+ * }
53
+ * });
54
+ *
55
+ * // Track only deletions
56
+ * effect((t) => {
57
+ * const { key, value } = $users.$lastDeleted.get(t);
58
+ * if (key && value) {
59
+ * console.log(`User ${key} was deleted:`, value);
60
+ * }
61
+ * });
62
+ *
63
+ * // Modify the map
64
+ * $users.add('user1', { name: 'John', age: 30 });
65
+ * $users.add('user2', { name: 'Jane', age: 25 });
66
+ * $users.update('user1', { name: 'John', age: 31 });
67
+ * $users.delete('user1');
68
+ * ```
69
+ *
70
+ * @typeParam K - The type of the map keys.
71
+ * @typeParam V - The type of the map values.
18
72
  *
19
73
  * @public
20
74
  */
21
75
  export class FlowMap<K, V> extends FlowState<Map<K, V>> {
22
- /**
23
- * A reactive state that holds the most recent key and value that were set.
24
- *
25
- * @remarks
26
- * When a key is set via {@link FlowMap.setAt}, this state is updated with
27
- * the corresponding key and value.
28
- *
29
- * @public
30
- */
31
- public $lastSet: FlowState<{ key?: K; value?: V }> = new FlowState({});
76
+ /**
77
+ * A reactive state that holds the most recent key and value that were added.
78
+ *
79
+ * @remarks
80
+ * When a key is added via {@link FlowMap.add}, this state is updated with
81
+ * the corresponding key and value.
82
+ *
83
+ * @public
84
+ */
85
+ public $lastAdded = new FlowState<{ key: K; value: V } | null>(null);
86
+
87
+ /**
88
+ * A reactive state that holds the most recent key and value that were updated.
89
+ *
90
+ * @remarks
91
+ * When a key is updated via {@link FlowMap.update}, this state is updated with
92
+ * the corresponding key and value.
93
+ *
94
+ * @public
95
+ */
96
+ public $lastUpdated = new FlowState<{ key: K; value: V } | null>(null);
97
+
98
+ /**
99
+ * A reactive state that holds the most recent key and value that were deleted.
100
+ *
101
+ * @remarks
102
+ * When a key is deleted via {@link FlowMap.delete}, this state is updated with
103
+ * the corresponding key and its last known value.
104
+ *
105
+ * @public
106
+ */
107
+ public $lastDeleted = new FlowState<{ key: K; value: V } | null>(null);
108
+
109
+ /**
110
+ * Adds a new key-value pair to the map.
111
+ *
112
+ * @param key - The key to add.
113
+ * @param value - The value to associate with the key.
114
+ * @throws If the FlowMap instance is disposed.
115
+ * @throws If the key already exists in the map.
116
+ *
117
+ * @remarks
118
+ * Adds a new entry to the internal map, emits the key-value pair via {@link FlowMap.$lastAdded},
119
+ * and notifies all subscribers of the change.
120
+ *
121
+ * @public
122
+ */
123
+ public add(key: K, value: V): void {
124
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
125
+ if (this._value.has(key)) {
126
+ throw new Error("[PicoFlow] Key already exists");
127
+ }
128
+ this._value.set(key, value);
129
+ this.$lastAdded.set({ key, value });
130
+ this._notify();
131
+ }
32
132
 
33
- /**
34
- * A reactive state that holds the most recent key and value that were deleted.
35
- *
36
- * @remarks
37
- * When a key is deleted via {@link FlowMap.delete}, this state is updated with
38
- * the corresponding key and its last known value.
39
- *
40
- * @public
41
- */
42
- public $lastDeleted: FlowState<{ key?: K; value?: V }> = new FlowState({});
133
+ /**
134
+ * Updates an existing key-value pair in the map.
135
+ *
136
+ * @param key - The key to update.
137
+ * @param value - The new value to associate with the key.
138
+ * @throws If the FlowMap instance is disposed.
139
+ * @throws If the key does not exist in the map.
140
+ *
141
+ * @remarks
142
+ * Updates an existing entry in the internal map, emits the key-value pair via {@link FlowMap.$lastUpdated},
143
+ * and notifies all subscribers of the change.
144
+ *
145
+ * @public
146
+ */
147
+ public update(key: K, value: V): void {
148
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
149
+ if (!this._value.has(key)) {
150
+ throw new Error("[PicoFlow] Key does not exist");
151
+ }
152
+ this._value.set(key, value);
153
+ this.$lastUpdated.set({ key, value });
154
+ this._notify();
155
+ }
43
156
 
44
- /**
45
- * Sets a value at the specified key in the underlying map.
46
- *
47
- * @param key - The key at which to set the value.
48
- * @param value - The value to set.
49
- * @throws If the FlowMap instance is disposed.
50
- *
51
- * @remarks
52
- * Updates the internal map, emits the key-value pair via {@link FlowMap.$lastSet},
53
- * and notifies all subscribers of the change.
54
- *
55
- * @public
56
- */
57
- public setAt(key: K, value: V): void {
58
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
59
- this._value.set(key, value);
60
- this.$lastSet.set({ key, value });
61
- this._notify();
62
- }
157
+ /**
158
+ * Deletes the value at the specified key from the underlying map.
159
+ *
160
+ * @param key - The key to delete.
161
+ * @throws If the FlowMap instance is disposed.
162
+ *
163
+ * @remarks
164
+ * Removes the key from the internal map, emits the deleted key and its value via {@link FlowMap.$lastDeleted},
165
+ * and notifies all subscribers of the change.
166
+ *
167
+ * @public
168
+ */
169
+ public delete(key: K): void {
170
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
171
+ const value = this._value.get(key);
172
+ if (!value) throw new Error("[PicoFlow] Key does not exist");
173
+ this._value.delete(key);
174
+ this.$lastDeleted.set({ key, value });
175
+ this._notify();
176
+ }
63
177
 
64
- /**
65
- * Deletes the value at the specified key from the underlying map.
66
- *
67
- * @param key - The key to delete.
68
- * @throws If the FlowMap instance is disposed.
69
- *
70
- * @remarks
71
- * Removes the key from the internal map, emits the deleted key and its value via {@link FlowMap.$lastDeleted},
72
- * and notifies all subscribers of the change.
73
- *
74
- * @public
75
- */
76
- public delete(key: K): void {
77
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
78
- const value = this._value.get(key);
79
- this._value.delete(key);
80
- this.$lastDeleted.set({ key, value });
81
- this._notify();
82
- }
178
+ /**
179
+ * Disposes the FlowMap and its values.
180
+ * @param options - Disposal options.
181
+ * @public
182
+ */
183
+ override dispose(options?: { self: boolean }): void {
184
+ super.dispose(options);
185
+ this._value.forEach((item) => {
186
+ if (isDisposable(item)) item.dispose(options);
187
+ });
188
+ this._value.clear();
189
+ this.$lastAdded.dispose(options);
190
+ this.$lastUpdated.dispose(options);
191
+ this.$lastDeleted.dispose(options);
192
+ }
83
193
  }
@@ -1,56 +1,100 @@
1
1
  import { FlowObservable } from "../basic";
2
2
 
3
3
  /**
4
- * Represents a reactive resource that asynchronously fetches its value.
4
+ * Represents a reactive resource that asynchronously fetches its value and returns `T | undefined`.
5
5
  *
6
- * @remarks A FlowResource extends FlowObservable and encapsulates an asynchronous fetch function.
7
- * It is used to retrieve and update its value asynchronously. When the fetch is executed,
8
- * if the new value differs from the current value, the resource is updated and its subscribers
9
- * are notified.
6
+ * @remarks
7
+ * FlowResource extends FlowObservable to manage asynchronous data fetching with reactive updates.
8
+ * Unlike {@link FlowResourceAsync} which always returns a Promise, FlowResource returns the resolved
9
+ * value directly (or `undefined` if not yet fetched), making it more convenient for synchronous access
10
+ * patterns in effects and derivations.
11
+ *
12
+ * **Key Characteristics:**
13
+ * - The value is `undefined` initially, before the first fetch
14
+ * - Call `fetch()` to trigger the asynchronous fetch operation
15
+ * - When fetched, the value is compared to the current value; only different values trigger updates
16
+ * - Reading via `get(t)` or `pick()` returns the current value (possibly `undefined`) without triggering a fetch
17
+ *
18
+ * **Fetch Behavior:**
19
+ * The fetch function is NOT called automatically on construction. You must explicitly call the
20
+ * `fetch()` method to retrieve the resource. This gives you control over when network requests
21
+ * or expensive async operations occur.
22
+ *
23
+ * **Use Cases:**
24
+ * - API data fetching where you want synchronous access to the cached value
25
+ * - Lazy-loaded data that shouldn't fetch on construction
26
+ * - Resources that need manual refresh control
27
+ * - Data that may or may not be available (hence `T | undefined`)
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const $user = resource(() => fetchUserFromAPI());
32
+ *
33
+ * // Initially undefined
34
+ * console.log($user.pick()); // undefined
35
+ *
36
+ * // Trigger the fetch
37
+ * await $user.fetch();
38
+ * console.log($user.pick()); // { id: 1, name: 'John' }
39
+ *
40
+ * // Use in an effect
41
+ * effect((t) => {
42
+ * const user = $user.get(t);
43
+ * if (user) {
44
+ * console.log(`Hello, ${user.name}`);
45
+ * }
46
+ * });
47
+ *
48
+ * // Refetch to update
49
+ * await $user.fetch();
50
+ * ```
51
+ *
52
+ * @typeParam T - The type of the resource value (not including the undefined case).
10
53
  *
11
54
  * @public
12
55
  */
13
56
  export class FlowResource<T> extends FlowObservable<T | undefined> {
14
- /**
15
- * Creates a new FlowResource.
16
- * @param fetch - An asynchronous function that retrieves the resource's value.
17
- *
18
- * @public
19
- */
20
- constructor(fetch: () => Promise<T>) {
21
- super();
22
- this._fetch = fetch;
23
- }
57
+ /**
58
+ * Creates a new FlowResource.
59
+ *
60
+ * @param fetch - An asynchronous function that retrieves the resource's value.
61
+ * This function is not invoked on construction; you must call the `fetch()` method
62
+ * to execute it.
63
+ *
64
+ * @public
65
+ */
66
+ constructor(fetch: () => Promise<T>) {
67
+ super();
68
+ this._fetch = fetch;
69
+ }
24
70
 
25
- /**
26
- * Retrieves the current resource value.
27
- * @returns The current value, or undefined if the resource has not been fetched yet.
28
- * @throws Error if the resource is disposed.
29
- * @public
30
- */
31
- public get(): T | undefined {
32
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
33
- return this._value;
34
- }
71
+ /**
72
+ * Internal method to get the raw value.
73
+ * @internal
74
+ */
75
+ protected _getRaw(): T | undefined {
76
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
77
+ return this._value;
78
+ }
35
79
 
36
- /**
37
- * Asynchronously fetches a new value for the resource.
38
- * @remarks
39
- * Executes the internal fetch function. If the fetched value differs from the current one,
40
- * updates the resource's value and notifies subscribers.
41
- * @returns A Promise that resolves when the fetch operation is complete.
42
- * @throws Error if the resource is disposed.
43
- * @public
44
- */
45
- public async fetch(): Promise<void> {
46
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
47
- const value = await this._fetch();
48
- if (value === this._value) return;
49
- this._value = value;
50
- this._notify();
51
- }
80
+ /**
81
+ * Asynchronously fetches a new value for the resource.
82
+ * @remarks
83
+ * Executes the internal fetch function. If the fetched value differs from the current one,
84
+ * updates the resource's value and notifies subscribers.
85
+ * @returns A Promise that resolves when the fetch operation is complete.
86
+ * @throws Error if the resource is disposed.
87
+ * @public
88
+ */
89
+ public async fetch(): Promise<void> {
90
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
91
+ const value = await this._fetch();
92
+ if (value === this._value) return;
93
+ this._value = value;
94
+ this._notify();
95
+ }
52
96
 
53
- /* INTERNAL ------------------------------------------------ */
97
+ /* INTERNAL ------------------------------------------------ */
54
98
 
55
- private _fetch: () => Promise<T>;
99
+ private _fetch: () => Promise<T>;
56
100
  }
@@ -1,57 +1,77 @@
1
1
  import { FlowObservable } from "../basic/";
2
2
 
3
3
  /**
4
- * Represents a reactive resource that asynchronously fetches its value.
4
+ * Represents a reactive resource that asynchronously fetches its value and always returns a Promise.
5
5
  *
6
6
  * @remarks
7
- * A FlowResource extends FlowObservable and encapsulates an asynchronous fetch function.
8
- * It is used to retrieve and update its value asynchronously. When the fetch is executed,
9
- * if the new value differs from the current value, the resource is updated and its subscribers
10
- * are notified.
7
+ * FlowResourceAsync extends FlowObservable and encapsulates an asynchronous fetch function.
8
+ * Unlike {@link FlowResource} which returns `T | undefined`, FlowResourceAsync always returns
9
+ * a `Promise<T>`, making it suitable for async/await patterns.
11
10
  *
12
- * @typeparam T - The type of the resource value.
11
+ * **Key Characteristics:**
12
+ * - The first call to `get()` or `pick()` creates and caches a Promise
13
+ * - Subsequent calls return the same Promise until `fetch()` is called
14
+ * - Calling `fetch()` creates a new Promise and notifies subscribers
15
+ * - The Promise resolves to the fetched value of type T
16
+ *
17
+ * **Lazy Promise Creation:**
18
+ * The fetch function doesn't execute until the resource's value is first accessed.
19
+ * This allows you to define resources without immediately triggering network requests.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const $user = resourceAsync(() => fetchUserFromAPI());
24
+ *
25
+ * effect(async (t) => {
26
+ * const user = await $user.get(t); // Tracked, effect re-runs on fetch()
27
+ * console.log(user.name);
28
+ * });
29
+ *
30
+ * // Trigger a refetch
31
+ * await $user.fetch();
32
+ * ```
33
+ *
34
+ * @typeParam T - The type of the resource value (not the Promise itself).
13
35
  *
14
36
  * @public
15
37
  */
16
38
  export class FlowResourceAsync<T> extends FlowObservable<Promise<T>> {
17
- /**
18
- * Creates a new FlowResource.
19
- * @param fetch - An asynchronous function that retrieves the resource's value.
20
- * @public
21
- */
22
- constructor(fetch: () => Promise<T>) {
23
- super();
24
- this._fetch = fetch;
25
- }
39
+ /**
40
+ * Creates a new FlowResource.
41
+ * @param fetch - An asynchronous function that retrieves the resource's value.
42
+ * @public
43
+ */
44
+ constructor(fetch: () => Promise<T>) {
45
+ super();
46
+ this._fetch = fetch;
47
+ }
26
48
 
27
- /**
28
- * Retrieves the current resource value.
29
- * @returns The current value, or undefined if the resource has not been fetched yet.
30
- * @throws Error if the resource is disposed.
31
- * @public
32
- */
33
- public get(): Promise<T> {
34
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
35
- if (!this._value) this._value = this._fetch();
36
- return this._value;
37
- }
49
+ /**
50
+ * Internal method to get the raw value.
51
+ * @internal
52
+ */
53
+ protected _getRaw(): Promise<T> {
54
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
55
+ if (!this._value) this._value = this._fetch();
56
+ return this._value;
57
+ }
38
58
 
39
- /**
40
- * Asynchronously fetches a new value for the resource.
41
- * @remarks
42
- * Executes the internal fetch function. If the fetched value differs from the current one,
43
- * updates the resource's value and notifies subscribers.
44
- * @returns A Promise that resolves when the fetch operation is complete.
45
- * @throws Error if the resource is disposed.
46
- * @public
47
- */
48
- public async fetch(): Promise<void> {
49
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
50
- this._value = this._fetch();
51
- this._notify();
52
- }
59
+ /**
60
+ * Asynchronously fetches a new value for the resource.
61
+ * @remarks
62
+ * Executes the internal fetch function. If the fetched value differs from the current one,
63
+ * updates the resource's value and notifies subscribers.
64
+ * @returns A Promise that resolves when the fetch operation is complete.
65
+ * @throws Error if the resource is disposed.
66
+ * @public
67
+ */
68
+ public async fetch(): Promise<void> {
69
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
70
+ this._value = this._fetch();
71
+ this._notify();
72
+ }
53
73
 
54
- /* INTERNAL ------------------------------------------------ */
74
+ /* INTERNAL ------------------------------------------------ */
55
75
 
56
- private _fetch: () => Promise<T>;
76
+ private _fetch: () => Promise<T>;
57
77
  }