@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
@@ -2,7 +2,7 @@ import { FlowObservable } from "../basic/";
2
2
 
3
3
  /**
4
4
  * A function type that sets a new value for the reactive stream.
5
- * @typeparam T - The type of the value.
5
+ * @typeParam T - The type of the value.
6
6
  * @public
7
7
  */
8
8
  export type FlowStreamSetter<T> = (value: T) => void;
@@ -18,70 +18,133 @@ export type FlowStreamDisposer = () => void;
18
18
  * @remarks
19
19
  * The updater receives a setter function to update the stream's value.
20
20
  * It should return a disposer function to release any resources or subscriptions.
21
- * @typeparam T - The type of the stream value.
21
+ * @typeParam T - The type of the stream value.
22
22
  * @public
23
23
  */
24
24
  export type FlowStreamUpdater<T> = (
25
- set: FlowStreamSetter<T>,
25
+ set: FlowStreamSetter<T>,
26
26
  ) => FlowStreamDisposer;
27
27
 
28
28
  /**
29
- * Represents a reactive stream that updates its value based on an updater function.
29
+ * Represents a reactive stream that updates its value based on an external updater function.
30
30
  *
31
31
  * @remarks
32
- * FlowStream extends FlowObservable to encapsulate an update mechanism provided by an external updater function.
33
- * The updater is invoked during construction with a setter, and it returns a disposer to be called upon disposal.
34
- * Note: The stream's current value may be undefined until explicitly set.
32
+ * FlowStream extends FlowObservable to bridge external event sources with PicoFlow's reactive
33
+ * system. It's designed for integrating with event emitters, WebSocket connections, timers,
34
+ * or any push-based data source that sends updates over time.
35
35
  *
36
- * @typeparam T - The type of the stream's value.
36
+ * **How It Works:**
37
+ * 1. You provide an updater function that receives a setter callback
38
+ * 2. The updater sets up subscriptions to external events and calls the setter with new values
39
+ * 3. When the setter is called, the stream notifies all dependent effects and derivations
40
+ * 4. The updater returns a disposer function for cleanup
41
+ *
42
+ * **Initial Value:**
43
+ * The stream's value is `undefined` until the first time the setter is called. This allows
44
+ * you to check if any data has been received yet.
45
+ *
46
+ * **Change Detection:**
47
+ * The stream only notifies subscribers when the new value differs from the current value
48
+ * (using strict equality `===`). This prevents unnecessary updates for duplicate values.
49
+ *
50
+ * **Resource Management:**
51
+ * The disposer function returned by your updater is automatically called when the stream
52
+ * is disposed. Use it to clean up subscriptions, close connections, or clear timers.
53
+ *
54
+ * **Use Cases:**
55
+ * - WebSocket message streams
56
+ * - DOM event listeners
57
+ * - setInterval/setTimeout timers
58
+ * - Server-sent events (SSE)
59
+ * - Observable subscriptions from other libraries
60
+ * - Any push-based data source
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // WebSocket stream
65
+ * const $messages = stream<string>((set) => {
66
+ * const ws = new WebSocket('ws://example.com');
67
+ * ws.onmessage = (event) => set(event.data);
68
+ * return () => ws.close();
69
+ * });
70
+ *
71
+ * // Timer stream
72
+ * const $tick = stream<number>((set) => {
73
+ * let count = 0;
74
+ * const id = setInterval(() => set(count++), 1000);
75
+ * return () => clearInterval(id);
76
+ * });
77
+ *
78
+ * // DOM event stream
79
+ * const $clicks = stream<MouseEvent>((set) => {
80
+ * const handler = (e: MouseEvent) => set(e);
81
+ * document.addEventListener('click', handler);
82
+ * return () => document.removeEventListener('click', handler);
83
+ * });
84
+ *
85
+ * // Use in an effect
86
+ * effect((t) => {
87
+ * const message = $messages.get(t);
88
+ * if (message) {
89
+ * console.log('Received:', message);
90
+ * }
91
+ * });
92
+ * ```
93
+ *
94
+ * @typeParam T - The type of the values emitted by the stream.
37
95
  * @public
38
96
  */
39
97
  export class FlowStream<T> extends FlowObservable<T | undefined> {
40
- /**
41
- * Creates a new FlowStream.
42
- * @param updater - A function that receives a setter to update the stream's value.
43
- * It should return a disposer function that will be called upon disposal.
44
- * @public
45
- */
46
- constructor(updater: FlowStreamUpdater<T>) {
47
- super();
48
- this._disposer = updater((value: T) => {
49
- this._set(value);
50
- });
51
- }
98
+ /**
99
+ * Creates a new FlowStream.
100
+ *
101
+ * @param updater - A function that receives a setter callback and returns a disposer.
102
+ * The setter should be called whenever new data is available. The disposer will be
103
+ * invoked when the stream is disposed to clean up resources.
104
+ *
105
+ * @remarks
106
+ * The updater is invoked immediately during construction. Make sure to return a proper
107
+ * cleanup function to avoid resource leaks.
108
+ *
109
+ * @public
110
+ */
111
+ constructor(updater: FlowStreamUpdater<T>) {
112
+ super();
113
+ this._disposer = updater((value: T) => {
114
+ this._set(value);
115
+ });
116
+ }
52
117
 
53
- /**
54
- * Retrieves the current value of the stream.
55
- * @returns The current value, or undefined if no value has been set yet.
56
- * @throws Error if the stream is disposed.
57
- * @public
58
- */
59
- public get(): T | undefined {
60
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
61
- return this._value;
62
- }
118
+ /**
119
+ * Internal method to get the raw value.
120
+ * @internal
121
+ */
122
+ protected _getRaw(): T | undefined {
123
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
124
+ return this._value;
125
+ }
63
126
 
64
- /**
65
- * Disposes the stream, releasing all resources.
66
- * @remarks
67
- * In addition to disposing the underlying observable, this method calls the disposer
68
- * returned by the updater.
69
- * @public
70
- */
71
- public override dispose(): void {
72
- super.dispose();
73
- this._disposer();
74
- }
127
+ /**
128
+ * Disposes the stream, releasing all resources.
129
+ * @remarks
130
+ * In addition to disposing the underlying observable, this method calls the disposer
131
+ * returned by the updater.
132
+ * @public
133
+ */
134
+ public override dispose(): void {
135
+ super.dispose();
136
+ this._disposer();
137
+ }
75
138
 
76
- /* INTERNAL ------------------------------------------------------ */
139
+ /* INTERNAL ------------------------------------------------------ */
77
140
 
78
- private _disposer: FlowStreamDisposer;
141
+ private _disposer: FlowStreamDisposer;
79
142
 
80
- private _set(value: T): void {
81
- /* v8 ignore next */
82
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
83
- if (value === this._value) return;
84
- this._value = value;
85
- this._notify();
86
- }
143
+ private _set(value: T): void {
144
+ /* v8 ignore next */
145
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
146
+ if (value === this._value) return;
147
+ this._value = value;
148
+ this._notify();
149
+ }
87
150
  }
@@ -2,81 +2,140 @@ import { FlowObservable } from "../basic";
2
2
  import type { FlowStreamDisposer, FlowStreamUpdater } from "./stream";
3
3
 
4
4
  /**
5
- * Represents an asynchronous reactive stream that updates its value based on an updater function.
5
+ * Represents an asynchronous reactive stream that always returns a Promise and updates based on an updater function.
6
6
  *
7
7
  * @remarks
8
- * A FlowStreamAsync extends FlowObservable and encapsulates a mechanism for asynchronously updating its value.
9
- * The updater is invoked during construction with a setter, and its returned disposer is called upon disposal.
10
- * Note: The stream's current value may be undefined until it is set.
8
+ * FlowStreamAsync extends FlowObservable to bridge external async event sources with PicoFlow's
9
+ * reactive system. Unlike {@link FlowStream} which returns `T | undefined`, FlowStreamAsync always
10
+ * returns a `Promise<T>`, making it suitable for use with async/await patterns.
11
11
  *
12
- * @typeparam T - The type of the stream's value.
12
+ * **How It Works:**
13
+ * 1. On construction, creates an initial Promise that resolves when the first value arrives
14
+ * 2. Your updater function receives a setter callback and sets up event subscriptions
15
+ * 3. When the setter is called with a value, the Promise resolves (first call) or a new Promise is created (subsequent calls)
16
+ * 4. The updater returns a disposer function for cleanup
17
+ *
18
+ * **Promise Behavior:**
19
+ * - **First call to setter**: Resolves the initial Promise created at construction
20
+ * - **Subsequent calls**: Creates a new `Promise.resolve(value)` for immediate resolution
21
+ * - **Reading the value**: Always returns a Promise, either pending (initial) or resolved
22
+ *
23
+ * **Change Detection:**
24
+ * After the first value is set, the stream only notifies subscribers when the new value
25
+ * differs from the previous value (using strict equality `===`). This prevents unnecessary
26
+ * updates for duplicate values.
27
+ *
28
+ * **Resource Management:**
29
+ * The disposer function returned by your updater is automatically called when the stream
30
+ * is disposed. Use it to clean up subscriptions, close connections, or clear timers.
31
+ *
32
+ * **Use Cases:**
33
+ * - Async WebSocket message streams
34
+ * - Server-sent events that you want to await
35
+ * - Async event handlers
36
+ * - Integration with async iterators
37
+ * - Any push-based async data source
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // Async WebSocket stream
42
+ * const $messages = streamAsync<string>((set) => {
43
+ * const ws = new WebSocket('ws://example.com');
44
+ * ws.onmessage = (event) => set(event.data);
45
+ * return () => ws.close();
46
+ * });
47
+ *
48
+ * // Use with async/await
49
+ * effect(async (t) => {
50
+ * const message = await $messages.get(t);
51
+ * console.log('Received:', message);
52
+ * });
53
+ *
54
+ * // Wait for the first message
55
+ * const firstMessage = await $messages.pick();
56
+ * console.log('First message:', firstMessage);
57
+ *
58
+ * // Async timer stream
59
+ * const $asyncTick = streamAsync<number>((set) => {
60
+ * let count = 0;
61
+ * const id = setInterval(() => set(count++), 1000);
62
+ * return () => clearInterval(id);
63
+ * });
64
+ * ```
65
+ *
66
+ * @typeParam T - The type of the values emitted by the stream (not the Promise itself).
13
67
  * @public
14
68
  */
15
69
  export class FlowStreamAsync<T> extends FlowObservable<Promise<T>> {
16
- /**
17
- * Creates a new asynchronous FlowStream.
18
- * @param updater - A function that receives a setter to update the stream's value.
19
- * It should return a disposer function that will be called upon disposal.
20
- * @remarks The updater function can invoke the setter asynchronously to update the stream.
21
- * @public
22
- */
23
- constructor(updater: FlowStreamUpdater<T>) {
24
- super();
25
- this._disposer = updater((value: T) => {
26
- this._set(value);
27
- });
70
+ /**
71
+ * Creates a new asynchronous FlowStream.
72
+ *
73
+ * @param updater - A function that receives a setter callback and returns a disposer.
74
+ * The setter should be called whenever new data is available (can be called asynchronously).
75
+ * The disposer will be invoked when the stream is disposed to clean up resources.
76
+ *
77
+ * @remarks
78
+ * The updater is invoked immediately during construction. An initial Promise is created
79
+ * that will resolve when the setter is first called. Make sure to return a proper cleanup
80
+ * function to avoid resource leaks.
81
+ *
82
+ * @public
83
+ */
84
+ constructor(updater: FlowStreamUpdater<T>) {
85
+ super();
86
+ this._disposer = updater((value: T) => {
87
+ this._set(value);
88
+ });
28
89
 
29
- this._value = new Promise((resolve) => {
30
- this._resolve = resolve;
31
- });
32
- }
90
+ this._value = new Promise((resolve) => {
91
+ this._resolve = resolve;
92
+ });
93
+ }
33
94
 
34
- /**
35
- * Retrieves the current value of the stream as a Promise.
36
- * @returns A Promise that resolves to the current value.
37
- * @throws Error if the stream is disposed.
38
- * @public
39
- */
40
- public get(): Promise<T> {
41
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
42
- return this._value;
43
- }
95
+ /**
96
+ * Internal method to get the raw value.
97
+ * @internal
98
+ */
99
+ protected _getRaw(): Promise<T> {
100
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
101
+ return this._value;
102
+ }
44
103
 
45
- /**
46
- * Disposes the stream, releasing all resources.
47
- * @remarks In addition to disposing the underlying observable, this method calls the disposer
48
- * returned by the updater.
49
- * @public
50
- */
51
- public override dispose(): void {
52
- super.dispose();
53
- this._disposer();
54
- }
104
+ /**
105
+ * Disposes the stream, releasing all resources.
106
+ * @remarks In addition to disposing the underlying observable, this method calls the disposer
107
+ * returned by the updater.
108
+ * @public
109
+ */
110
+ public override dispose(): void {
111
+ super.dispose();
112
+ this._disposer();
113
+ }
55
114
 
56
- /* INTERNAL ------------------------------------------------------ */
115
+ /* INTERNAL ------------------------------------------------------ */
57
116
 
58
- private _initialized = false;
59
- private _awaitedValue?: T;
117
+ private _initialized = false;
118
+ private _awaitedValue?: T;
60
119
 
61
- private _resolve!: (value: T) => void;
62
- private _disposer: FlowStreamDisposer;
120
+ private _resolve!: (value: T) => void;
121
+ private _disposer: FlowStreamDisposer;
63
122
 
64
- private _set(value: T): void {
65
- /* v8 ignore next */
66
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
123
+ private _set(value: T): void {
124
+ /* v8 ignore next */
125
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
67
126
 
68
- if (!this._initialized) {
69
- this._resolve(value);
70
- this._initialized = true;
71
- this._awaitedValue = value;
72
- this._notify();
73
- return;
74
- }
127
+ if (!this._initialized) {
128
+ this._resolve(value);
129
+ this._initialized = true;
130
+ this._awaitedValue = value;
131
+ this._notify();
132
+ return;
133
+ }
75
134
 
76
- if (value === this._awaitedValue) return;
135
+ if (value === this._awaitedValue) return;
77
136
 
78
- this._value = Promise.resolve(value);
79
- this._awaitedValue = value;
80
- this._notify();
81
- }
137
+ this._value = Promise.resolve(value);
138
+ this._awaitedValue = value;
139
+ this._notify();
140
+ }
82
141
  }
@@ -1,64 +1,97 @@
1
1
  import { FlowObservable } from "./observable";
2
2
 
3
3
  /**
4
- * Represents a reactive and immutable constant value computed lazily upon first access.
4
+ * Represents a reactive and immutable constant value that can be computed lazily upon first access.
5
5
  *
6
- * @remarks This class extends FlowObservable and supports initializing the constant using either a direct value
7
- * or a lazy initializer function. Once computed, the value is cached for all subsequent accesses.
6
+ * @remarks
7
+ * FlowConstant extends FlowObservable to provide an immutable reactive value. Unlike {@link FlowState},
8
+ * which is mutable via the `set()` method, a constant's value never changes after initialization.
8
9
  *
9
- * @typeparam T - The type of the constant value.
10
+ * **Initialization Patterns:**
11
+ * - **Direct value**: Pass a value directly; it's stored immediately.
12
+ * - **Lazy initialization**: Pass a function; it's called only when the value is first accessed
13
+ * via `get()` or `pick()`.
14
+ *
15
+ * **Lazy Evaluation Benefits:**
16
+ * Lazy initialization is useful when:
17
+ * - The computation is expensive and may not be needed immediately
18
+ * - The value depends on resources that aren't available at construction time
19
+ * - You want to defer computation until the value is actually needed
20
+ *
21
+ * **Caching:**
22
+ * Once computed (either immediately or lazily), the value is cached permanently. All subsequent
23
+ * accesses return the cached value without re-computation.
24
+ *
25
+ * **Use Cases:**
26
+ * - Configuration values that don't change
27
+ * - Expensive computations that should only run once
28
+ * - Initialization values for other reactive primitives
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // Direct value - initialized immediately
33
+ * const $config = constant({ apiUrl: 'https://api.example.com' });
34
+ *
35
+ * // Lazy initialization - computed on first access
36
+ * const $expensiveValue = constant(() => {
37
+ * console.log('Computing...');
38
+ * return performExpensiveCalculation();
39
+ * });
40
+ *
41
+ * $expensiveValue.pick(); // Logs: "Computing..."
42
+ * $expensiveValue.pick(); // No log - returns cached value
43
+ * ```
44
+ *
45
+ * @typeParam T - The type of the constant value.
10
46
  *
11
47
  * @public
12
48
  */
13
49
  export class FlowConstant<T> extends FlowObservable<T> {
14
- /**
15
- * Creates a new FlowConstant instance.
16
- *
17
- * @param value - Either a direct value of type T or a function returning a value of type T for lazy initialization.
18
- * @public
19
- */
20
- constructor(value: T | (() => T)) {
21
- super();
22
- this._initEager(value);
23
- }
50
+ /**
51
+ * Creates a new FlowConstant instance.
52
+ *
53
+ * @param value - Either a direct value of type T or a function returning a value of type T.
54
+ * If a function is provided, it will be invoked lazily on the first value access.
55
+ * If a direct value is provided, it is stored immediately.
56
+ *
57
+ * @public
58
+ */
59
+ constructor(value: T | (() => T)) {
60
+ super();
61
+ this._initEager(value);
62
+ }
24
63
 
25
- /**
26
- * Retrieves the constant value, computing it lazily if needed.
27
- *
28
- * Accessing this method will initialize the value if it has not been computed already.
29
- * Throws an error if the instance has been disposed or if lazy initialization fails.
30
- *
31
- * @returns The cached constant value.
32
- * @throws Error if the constant is disposed or cannot be initialized.
33
- * @public
34
- */
35
- get(): T {
36
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
37
- this._initLazy();
38
- return this._value;
39
- }
64
+ /**
65
+ * Internal method to get the raw value.
66
+ * @internal
67
+ */
68
+ protected _getRaw(): T {
69
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
70
+ this._initLazy();
71
+ return this._value;
72
+ }
40
73
 
41
- /* INTERNAL --------------------------------------------------------- */
74
+ /* INTERNAL --------------------------------------------------------- */
42
75
 
43
- /*@internal*/ protected _initialized = false;
44
- /*@internal*/ protected _init?: () => T;
76
+ /** @internal */ protected _initialized = false;
77
+ /** @internal */ protected _init?: () => T;
45
78
 
46
- /*@internal*/ protected _initEager(value: T | (() => T)): void {
47
- if (typeof value === "function") {
48
- this._init = value as () => T;
49
- } else {
50
- this._value = value;
51
- this._initialized = true;
52
- }
53
- }
79
+ /** @internal */ protected _initEager(value: T | (() => T)): void {
80
+ if (typeof value === "function") {
81
+ this._init = value as () => T;
82
+ } else {
83
+ this._value = value;
84
+ this._initialized = true;
85
+ }
86
+ }
54
87
 
55
- /*@internal*/ protected _initLazy(): void {
56
- if (!this._initialized && this._init) {
57
- this._value = this._init();
58
- this._initialized = true;
59
- }
60
- /* v8 ignore next 2 */
61
- if (!this._initialized)
62
- throw new Error("[PicoFlow] Primitive can't be initialized");
63
- }
88
+ /** @internal */ protected _initLazy(): void {
89
+ if (!this._initialized && this._init) {
90
+ this._value = this._init();
91
+ this._initialized = true;
92
+ }
93
+ /* v8 ignore next 2 */
94
+ if (!this._initialized)
95
+ throw new Error("[PicoFlow] Primitive can't be initialized");
96
+ }
64
97
  }