@ersbeth/picoflow 0.2.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/.cursor/plans/update-js-e795d61b.plan.md +567 -0
  2. package/.gitlab-ci.yml +24 -0
  3. package/.vscode/settings.json +3 -3
  4. package/CHANGELOG.md +51 -0
  5. package/IMPLEMENTATION_GUIDE.md +1578 -0
  6. package/README.md +62 -25
  7. package/biome.json +32 -32
  8. package/dist/picoflow.js +557 -1099
  9. package/dist/types/advanced/array.d.ts +0 -6
  10. package/dist/types/advanced/array.d.ts.map +1 -1
  11. package/dist/types/advanced/index.d.ts +5 -5
  12. package/dist/types/advanced/index.d.ts.map +1 -1
  13. package/dist/types/advanced/map.d.ts +114 -23
  14. package/dist/types/advanced/map.d.ts.map +1 -1
  15. package/dist/types/advanced/resource.d.ts +51 -12
  16. package/dist/types/advanced/resource.d.ts.map +1 -1
  17. package/dist/types/advanced/resourceAsync.d.ts +28 -13
  18. package/dist/types/advanced/resourceAsync.d.ts.map +1 -1
  19. package/dist/types/advanced/stream.d.ts +74 -16
  20. package/dist/types/advanced/stream.d.ts.map +1 -1
  21. package/dist/types/advanced/streamAsync.d.ts +69 -15
  22. package/dist/types/advanced/streamAsync.d.ts.map +1 -1
  23. package/dist/types/basic/constant.d.ts +44 -16
  24. package/dist/types/basic/constant.d.ts.map +1 -1
  25. package/dist/types/basic/derivation.d.ts +73 -24
  26. package/dist/types/basic/derivation.d.ts.map +1 -1
  27. package/dist/types/basic/disposable.d.ts +65 -6
  28. package/dist/types/basic/disposable.d.ts.map +1 -1
  29. package/dist/types/basic/effect.d.ts +27 -16
  30. package/dist/types/basic/effect.d.ts.map +1 -1
  31. package/dist/types/basic/index.d.ts +7 -8
  32. package/dist/types/basic/index.d.ts.map +1 -1
  33. package/dist/types/basic/observable.d.ts +62 -13
  34. package/dist/types/basic/observable.d.ts.map +1 -1
  35. package/dist/types/basic/signal.d.ts +35 -6
  36. package/dist/types/basic/signal.d.ts.map +1 -1
  37. package/dist/types/basic/state.d.ts +25 -4
  38. package/dist/types/basic/state.d.ts.map +1 -1
  39. package/dist/types/basic/trackingContext.d.ts +33 -0
  40. package/dist/types/basic/trackingContext.d.ts.map +1 -0
  41. package/dist/types/creators.d.ts +271 -26
  42. package/dist/types/creators.d.ts.map +1 -1
  43. package/dist/types/index.d.ts +60 -7
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/dist/types/solid/converters.d.ts +5 -5
  46. package/dist/types/solid/converters.d.ts.map +1 -1
  47. package/dist/types/solid/index.d.ts +2 -2
  48. package/dist/types/solid/index.d.ts.map +1 -1
  49. package/dist/types/solid/primitives.d.ts +96 -4
  50. package/dist/types/solid/primitives.d.ts.map +1 -1
  51. package/docs/.vitepress/config.mts +110 -0
  52. package/docs/api/classes/FlowArray.md +489 -0
  53. package/docs/api/classes/FlowConstant.md +350 -0
  54. package/docs/api/classes/FlowDerivation.md +334 -0
  55. package/docs/api/classes/FlowEffect.md +100 -0
  56. package/docs/api/classes/FlowMap.md +512 -0
  57. package/docs/api/classes/FlowObservable.md +306 -0
  58. package/docs/api/classes/FlowResource.md +380 -0
  59. package/docs/api/classes/FlowResourceAsync.md +362 -0
  60. package/docs/api/classes/FlowSignal.md +160 -0
  61. package/docs/api/classes/FlowState.md +368 -0
  62. package/docs/api/classes/FlowStream.md +367 -0
  63. package/docs/api/classes/FlowStreamAsync.md +364 -0
  64. package/docs/api/classes/SolidDerivation.md +75 -0
  65. package/docs/api/classes/SolidResource.md +91 -0
  66. package/docs/api/classes/SolidState.md +71 -0
  67. package/docs/api/classes/TrackingContext.md +33 -0
  68. package/docs/api/functions/array.md +58 -0
  69. package/docs/api/functions/constant.md +45 -0
  70. package/docs/api/functions/derivation.md +53 -0
  71. package/docs/api/functions/effect.md +49 -0
  72. package/docs/api/functions/from.md +220 -0
  73. package/docs/api/functions/isDisposable.md +49 -0
  74. package/docs/api/functions/map.md +57 -0
  75. package/docs/api/functions/resource.md +52 -0
  76. package/docs/api/functions/resourceAsync.md +50 -0
  77. package/docs/api/functions/signal.md +36 -0
  78. package/docs/api/functions/state.md +47 -0
  79. package/docs/api/functions/stream.md +53 -0
  80. package/docs/api/functions/streamAsync.md +50 -0
  81. package/docs/api/index.md +118 -0
  82. package/docs/api/interfaces/FlowDisposable.md +65 -0
  83. package/docs/api/interfaces/SolidObservable.md +19 -0
  84. package/docs/api/type-aliases/FlowArrayAction.md +49 -0
  85. package/docs/api/type-aliases/FlowStreamDisposer.md +15 -0
  86. package/docs/api/type-aliases/FlowStreamSetter.md +27 -0
  87. package/docs/api/type-aliases/FlowStreamUpdater.md +32 -0
  88. package/docs/api/type-aliases/NotPromise.md +18 -0
  89. package/docs/api/type-aliases/SolidGetter.md +17 -0
  90. package/docs/api/typedoc-sidebar.json +1 -0
  91. package/docs/examples/examples.md +2313 -0
  92. package/docs/examples/patterns.md +649 -0
  93. package/docs/guide/advanced/disposal.md +426 -0
  94. package/docs/guide/advanced/solidjs.md +221 -0
  95. package/docs/guide/advanced/upgrading.md +464 -0
  96. package/docs/guide/introduction/concepts.md +56 -0
  97. package/docs/guide/introduction/conventions.md +61 -0
  98. package/docs/guide/introduction/getting-started.md +134 -0
  99. package/docs/guide/introduction/lifecycle.md +371 -0
  100. package/docs/guide/primitives/array.md +400 -0
  101. package/docs/guide/primitives/constant.md +380 -0
  102. package/docs/guide/primitives/derivations.md +348 -0
  103. package/docs/guide/primitives/effects.md +458 -0
  104. package/docs/guide/primitives/map.md +387 -0
  105. package/docs/guide/primitives/overview.md +175 -0
  106. package/docs/guide/primitives/resources.md +858 -0
  107. package/docs/guide/primitives/signal.md +259 -0
  108. package/docs/guide/primitives/state.md +368 -0
  109. package/docs/guide/primitives/streams.md +931 -0
  110. package/docs/index.md +47 -0
  111. package/docs/public/logo.svg +1 -0
  112. package/package.json +57 -41
  113. package/src/advanced/array.ts +208 -210
  114. package/src/advanced/index.ts +7 -7
  115. package/src/advanced/map.ts +178 -68
  116. package/src/advanced/resource.ts +87 -43
  117. package/src/advanced/resourceAsync.ts +62 -42
  118. package/src/advanced/stream.ts +113 -50
  119. package/src/advanced/streamAsync.ts +120 -61
  120. package/src/basic/constant.ts +82 -49
  121. package/src/basic/derivation.ts +128 -84
  122. package/src/basic/disposable.ts +74 -15
  123. package/src/basic/effect.ts +85 -77
  124. package/src/basic/index.ts +7 -8
  125. package/src/basic/observable.ts +94 -36
  126. package/src/basic/signal.ts +133 -105
  127. package/src/basic/state.ts +46 -25
  128. package/src/basic/trackingContext.ts +45 -0
  129. package/src/creators.ts +297 -54
  130. package/src/index.ts +96 -43
  131. package/src/solid/converters.ts +186 -67
  132. package/src/solid/index.ts +8 -2
  133. package/src/solid/primitives.ts +167 -65
  134. package/test/array.test.ts +592 -612
  135. package/test/constant.test.ts +31 -33
  136. package/test/derivation.test.ts +531 -536
  137. package/test/effect.test.ts +21 -21
  138. package/test/map.test.ts +233 -137
  139. package/test/resource.test.ts +119 -121
  140. package/test/resourceAsync.test.ts +98 -100
  141. package/test/signal.test.ts +51 -55
  142. package/test/state.test.ts +186 -168
  143. package/test/stream.test.ts +189 -189
  144. package/test/streamAsync.test.ts +186 -186
  145. package/tsconfig.json +19 -18
  146. package/typedoc.json +37 -0
  147. package/vite.config.ts +23 -20
  148. package/vitest.config.ts +7 -7
  149. package/api/doc/index.md +0 -31
  150. package/api/doc/picoflow.array.md +0 -55
  151. package/api/doc/picoflow.constant.md +0 -55
  152. package/api/doc/picoflow.derivation.md +0 -55
  153. package/api/doc/picoflow.effect.md +0 -55
  154. package/api/doc/picoflow.flowarray._constructor_.md +0 -49
  155. package/api/doc/picoflow.flowarray._lastaction.md +0 -13
  156. package/api/doc/picoflow.flowarray.clear.md +0 -17
  157. package/api/doc/picoflow.flowarray.dispose.md +0 -55
  158. package/api/doc/picoflow.flowarray.get.md +0 -19
  159. package/api/doc/picoflow.flowarray.length.md +0 -13
  160. package/api/doc/picoflow.flowarray.md +0 -273
  161. package/api/doc/picoflow.flowarray.pop.md +0 -17
  162. package/api/doc/picoflow.flowarray.push.md +0 -53
  163. package/api/doc/picoflow.flowarray.set.md +0 -53
  164. package/api/doc/picoflow.flowarray.setitem.md +0 -69
  165. package/api/doc/picoflow.flowarray.shift.md +0 -17
  166. package/api/doc/picoflow.flowarray.splice.md +0 -85
  167. package/api/doc/picoflow.flowarray.unshift.md +0 -53
  168. package/api/doc/picoflow.flowarrayaction.md +0 -37
  169. package/api/doc/picoflow.flowconstant._constructor_.md +0 -49
  170. package/api/doc/picoflow.flowconstant.get.md +0 -25
  171. package/api/doc/picoflow.flowconstant.md +0 -88
  172. package/api/doc/picoflow.flowderivation._constructor_.md +0 -49
  173. package/api/doc/picoflow.flowderivation.get.md +0 -23
  174. package/api/doc/picoflow.flowderivation.md +0 -86
  175. package/api/doc/picoflow.flowdisposable.dispose.md +0 -55
  176. package/api/doc/picoflow.flowdisposable.md +0 -43
  177. package/api/doc/picoflow.floweffect._constructor_.md +0 -54
  178. package/api/doc/picoflow.floweffect.dispose.md +0 -21
  179. package/api/doc/picoflow.floweffect.disposed.md +0 -13
  180. package/api/doc/picoflow.floweffect.md +0 -131
  181. package/api/doc/picoflow.flowgetter.md +0 -15
  182. package/api/doc/picoflow.flowmap._lastdeleted.md +0 -21
  183. package/api/doc/picoflow.flowmap._lastset.md +0 -21
  184. package/api/doc/picoflow.flowmap.delete.md +0 -61
  185. package/api/doc/picoflow.flowmap.md +0 -133
  186. package/api/doc/picoflow.flowmap.setat.md +0 -77
  187. package/api/doc/picoflow.flowobservable.get.md +0 -19
  188. package/api/doc/picoflow.flowobservable.md +0 -68
  189. package/api/doc/picoflow.flowobservable.subscribe.md +0 -55
  190. package/api/doc/picoflow.flowresource._constructor_.md +0 -49
  191. package/api/doc/picoflow.flowresource.fetch.md +0 -27
  192. package/api/doc/picoflow.flowresource.get.md +0 -23
  193. package/api/doc/picoflow.flowresource.md +0 -100
  194. package/api/doc/picoflow.flowresourceasync._constructor_.md +0 -49
  195. package/api/doc/picoflow.flowresourceasync.fetch.md +0 -27
  196. package/api/doc/picoflow.flowresourceasync.get.md +0 -23
  197. package/api/doc/picoflow.flowresourceasync.md +0 -100
  198. package/api/doc/picoflow.flowsignal.dispose.md +0 -59
  199. package/api/doc/picoflow.flowsignal.disposed.md +0 -18
  200. package/api/doc/picoflow.flowsignal.md +0 -112
  201. package/api/doc/picoflow.flowsignal.trigger.md +0 -21
  202. package/api/doc/picoflow.flowstate.md +0 -52
  203. package/api/doc/picoflow.flowstate.set.md +0 -61
  204. package/api/doc/picoflow.flowstream._constructor_.md +0 -49
  205. package/api/doc/picoflow.flowstream.dispose.md +0 -21
  206. package/api/doc/picoflow.flowstream.get.md +0 -23
  207. package/api/doc/picoflow.flowstream.md +0 -100
  208. package/api/doc/picoflow.flowstreamasync._constructor_.md +0 -54
  209. package/api/doc/picoflow.flowstreamasync.dispose.md +0 -21
  210. package/api/doc/picoflow.flowstreamasync.get.md +0 -23
  211. package/api/doc/picoflow.flowstreamasync.md +0 -100
  212. package/api/doc/picoflow.flowstreamdisposer.md +0 -13
  213. package/api/doc/picoflow.flowstreamsetter.md +0 -13
  214. package/api/doc/picoflow.flowstreamupdater.md +0 -19
  215. package/api/doc/picoflow.flowwatcher.md +0 -15
  216. package/api/doc/picoflow.from.md +0 -55
  217. package/api/doc/picoflow.from_1.md +0 -55
  218. package/api/doc/picoflow.from_2.md +0 -55
  219. package/api/doc/picoflow.from_3.md +0 -55
  220. package/api/doc/picoflow.from_4.md +0 -55
  221. package/api/doc/picoflow.from_5.md +0 -55
  222. package/api/doc/picoflow.isdisposable.md +0 -55
  223. package/api/doc/picoflow.map.md +0 -59
  224. package/api/doc/picoflow.md +0 -544
  225. package/api/doc/picoflow.resource.md +0 -55
  226. package/api/doc/picoflow.resourceasync.md +0 -55
  227. package/api/doc/picoflow.signal.md +0 -19
  228. package/api/doc/picoflow.solidderivation._constructor_.md +0 -49
  229. package/api/doc/picoflow.solidderivation.get.md +0 -13
  230. package/api/doc/picoflow.solidderivation.md +0 -94
  231. package/api/doc/picoflow.solidgetter.md +0 -13
  232. package/api/doc/picoflow.solidobservable.get.md +0 -13
  233. package/api/doc/picoflow.solidobservable.md +0 -57
  234. package/api/doc/picoflow.solidresource._constructor_.md +0 -49
  235. package/api/doc/picoflow.solidresource.get.md +0 -13
  236. package/api/doc/picoflow.solidresource.latest.md +0 -13
  237. package/api/doc/picoflow.solidresource.md +0 -157
  238. package/api/doc/picoflow.solidresource.refetch.md +0 -13
  239. package/api/doc/picoflow.solidresource.state.md +0 -13
  240. package/api/doc/picoflow.solidstate._constructor_.md +0 -49
  241. package/api/doc/picoflow.solidstate.get.md +0 -13
  242. package/api/doc/picoflow.solidstate.md +0 -115
  243. package/api/doc/picoflow.solidstate.set.md +0 -13
  244. package/api/doc/picoflow.state.md +0 -55
  245. package/api/doc/picoflow.stream.md +0 -55
  246. package/api/doc/picoflow.streamasync.md +0 -55
  247. package/api/picoflow.public.api.md +0 -244
  248. package/api-extractor.json +0 -61
package/dist/picoflow.js CHANGED
@@ -1,3 +1,88 @@
1
+ import { createMemo, createResource, createSignal, onMount, onCleanup } from 'solid-js';
2
+
3
+ class TrackingContext {
4
+ /** @internal */
5
+ constructor(_owner) {
6
+ this._owner = _owner;
7
+ }
8
+ /**
9
+ * Registers a dependency on the given signal.
10
+ * @internal
11
+ */
12
+ /** @internal */
13
+ _registerDependency(signal) {
14
+ this._owner._registerDependency(signal);
15
+ }
16
+ }
17
+
18
+ class FlowEffect {
19
+ /**
20
+ * Creates a new FlowEffect.
21
+ *
22
+ * @param apply - A side-effect function that receives a tracking context to
23
+ * access and register dependencies on reactive observables and signals.
24
+ *
25
+ * @remarks
26
+ * The provided function is executed immediately upon construction with a tracking context.
27
+ * Use the context parameter to call `.get(t)` on observables you want to track, or `.pick()`
28
+ * on observables you want to read without creating dependencies.
29
+ *
30
+ * @public
31
+ */
32
+ constructor(apply) {
33
+ this._trackedContext = new TrackingContext(this);
34
+ this._apply = apply;
35
+ this._exec();
36
+ }
37
+ /**
38
+ * Disposes the effect, unregistering all its tracked dependencies.
39
+ *
40
+ * @remarks
41
+ * Once disposed, the effect must no longer be used. Trying to dispose an effect
42
+ * that is already disposed will throw an error.
43
+ *
44
+ * @public
45
+ */
46
+ dispose() {
47
+ if (this._disposed) throw new Error("[PicoFlow] Effect is disposed");
48
+ Array.from(this._dependencies).forEach((dependency) => {
49
+ this._unregisterDependency(dependency);
50
+ });
51
+ this._disposed = true;
52
+ }
53
+ /**
54
+ * Indicates whether this effect has been disposed.
55
+ *
56
+ * @returns A boolean value that is true if the effect is disposed, false otherwise.
57
+ *
58
+ * @public
59
+ */
60
+ get disposed() {
61
+ return this._disposed;
62
+ }
63
+ /* INTERNAL ------------------------------------------------------------ */
64
+ _disposed = false;
65
+ _dependencies = /* @__PURE__ */ new Set();
66
+ _trackedContext;
67
+ _apply;
68
+ /** @internal */
69
+ _exec() {
70
+ if (this._disposed)
71
+ throw new Error("[PicoFlow] Effect is disposed");
72
+ this._apply(this._trackedContext);
73
+ }
74
+ /** @internal */
75
+ _registerDependency(dependency) {
76
+ this._dependencies.add(dependency);
77
+ dependency._registerEffect(this);
78
+ }
79
+ /** @internal */
80
+ _unregisterDependency(dependency) {
81
+ this._dependencies.delete(dependency);
82
+ dependency._unregisterEffect(this);
83
+ }
84
+ }
85
+
1
86
  class FlowSignal {
2
87
  /**
3
88
  * Triggers the FlowSignal.
@@ -9,6 +94,43 @@ class FlowSignal {
9
94
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
10
95
  this._notify();
11
96
  }
97
+ /**
98
+ * Watches the signal, registering it as a dependency in the tracking context.
99
+ *
100
+ * @param context - The tracking context in which to register this signal as a dependency.
101
+ *
102
+ * @remarks
103
+ * Use `watch()` when you want to track a signal without reading its value (signals don't
104
+ * have values to read). This is useful for triggering effects based on signal events
105
+ * without needing associated data.
106
+ *
107
+ * When the signal is triggered via `trigger()`, any effects or derivations that have
108
+ * watched this signal will automatically re-execute.
109
+ *
110
+ * This method must be called within an effect or derivation context where a TrackingContext
111
+ * is available. For observables (which hold values), use `.get(t)` instead, which both
112
+ * reads the value and watches for changes.
113
+ *
114
+ * @throws Error if the signal has been disposed.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const $signal = signal();
119
+ *
120
+ * effect((t) => {
121
+ * $signal.watch(t); // Track the signal
122
+ * console.log('Signal triggered!');
123
+ * });
124
+ *
125
+ * $signal.trigger(); // Logs: "Signal triggered!"
126
+ * ```
127
+ *
128
+ * @public
129
+ */
130
+ watch(context) {
131
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
132
+ context._registerDependency(this);
133
+ }
12
134
  /**
13
135
  * Disposes the FlowSignal.
14
136
  * Cleans up all registered effects, listeners, and dependencies.
@@ -19,17 +141,19 @@ class FlowSignal {
19
141
  dispose(options) {
20
142
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
21
143
  if (options?.self) {
22
- Array.from(this._effects).forEach(
23
- (effect) => effect._unregisterDependency(this)
24
- );
25
- Array.from(this._listeners).forEach(
26
- (listener) => listener._unregisterDependency(this)
27
- );
144
+ Array.from(this._effects).forEach((effect) => {
145
+ effect._unregisterDependency(this);
146
+ });
147
+ Array.from(this._listeners).forEach((listener) => {
148
+ listener._unregisterDependency(this);
149
+ });
28
150
  } else {
29
- Array.from(this._effects).forEach((effect) => effect.dispose());
30
- Array.from(this._listeners).forEach(
31
- (listener) => listener.dispose()
32
- );
151
+ Array.from(this._effects).forEach((effect) => {
152
+ effect.dispose();
153
+ });
154
+ Array.from(this._listeners).forEach((listener) => {
155
+ listener.dispose();
156
+ });
33
157
  }
34
158
  Array.from(this._dependencies).forEach((dependency) => {
35
159
  this._unregisterDependency(dependency);
@@ -45,141 +169,115 @@ class FlowSignal {
45
169
  return this._disposed;
46
170
  }
47
171
  /* INTERNAL ------------------------------------------------------------- */
48
- /*@internal*/
172
+ /** @internal */
49
173
  _disposed = false;
50
- /*@internal*/
174
+ /** @internal */
51
175
  _dependencies = /* @__PURE__ */ new Set();
52
- /*@internal*/
176
+ /** @internal */
53
177
  _listeners = /* @__PURE__ */ new Set();
54
- /*@internal*/
178
+ /** @internal */
55
179
  _effects = /* @__PURE__ */ new Set();
56
- /*@internal*/
57
- _watch() {
58
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
59
- }
60
- /*@internal*/
180
+ /** @internal */
61
181
  _notify() {
62
- this._listeners.forEach((listener) => listener._notify());
63
- this._effects.forEach((effect) => effect._exec());
64
- }
65
- /*@internal*/
66
- _watchFrom(listener) {
67
- listener._registerDependency(this);
68
- this._watch();
182
+ this._listeners.forEach((listener) => {
183
+ listener._notify();
184
+ });
185
+ this._effects.forEach((effect) => {
186
+ effect._exec();
187
+ });
69
188
  }
70
- /*@internal*/
189
+ /** @internal */
71
190
  _registerDependency(dependency) {
72
191
  this._dependencies.add(dependency);
73
192
  dependency._registerListener(this);
74
193
  }
75
- /*@internal*/
194
+ /** @internal */
76
195
  _unregisterDependency(dependency) {
77
196
  this._dependencies.delete(dependency);
78
197
  dependency._unregisterListener(this);
79
198
  }
80
- /*@internal*/
199
+ /** @internal */
81
200
  _registerListener(signal) {
82
201
  this._listeners.add(signal);
83
202
  }
84
- /*@internal*/
203
+ /** @internal */
85
204
  _unregisterListener(signal) {
86
205
  this._listeners.delete(signal);
87
206
  }
88
- /*@internal*/
207
+ /** @internal */
89
208
  _registerEffect(effect) {
90
209
  this._effects.add(effect);
91
210
  }
92
- /*@internal*/
211
+ /** @internal */
93
212
  _unregisterEffect(effect) {
94
213
  this._effects.delete(effect);
95
214
  }
96
215
  }
97
216
 
98
- class FlowEffect {
217
+ class FlowObservable extends FlowSignal {
99
218
  /**
100
- * Creates a new FlowEffect.
219
+ * Gets the current value with optional dependency tracking.
101
220
  *
102
- * @param apply - A side-effect function that receives a getter and a watcher to
103
- * access and register dependencies on reactive observables and signals.
221
+ * @param context - The tracking context for reactive tracking, or null for untracked access.
222
+ * When a context is provided, this observable is registered as a dependency. When null,
223
+ * the value is read without any tracking.
224
+ *
225
+ * @returns The current value of type T.
104
226
  *
105
227
  * @remarks
106
- * The provided function is executed immediately in a tracked mode to collect dependencies.
107
- * On subsequent executions, it runs in an untracked mode.
228
+ * Use `get(t)` within effects and derivations to create reactive dependencies.
229
+ * Use `get(null)` when you need to read a value without tracking (though `pick()` is more idiomatic).
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * effect((t) => {
234
+ * const tracked = $state.get(t); // Dependency registered
235
+ * const untracked = $other.get(null); // No dependency
236
+ * });
237
+ * ```
108
238
  *
109
239
  * @public
110
240
  */
111
- constructor(apply) {
112
- this._trackedExec = () => apply(this._trackedGet, this._trackedWatch);
113
- this._untrackedExec = () => apply(this._untrackedGet, this._untrackedWatch);
114
- this._exec();
241
+ get(context) {
242
+ if (context) {
243
+ this.watch(context);
244
+ }
245
+ return this._getRaw();
115
246
  }
116
247
  /**
117
- * Disposes the effect, unregistering all its tracked dependencies.
248
+ * Gets the current value without any dependency tracking.
249
+ *
250
+ * @returns The current value of type T.
118
251
  *
119
252
  * @remarks
120
- * Once disposed, the effect must no longer be used. Trying to dispose an effect
121
- * that is already disposed will throw an error.
253
+ * This method is equivalent to calling `get(null)` but provides a more semantic and readable API.
254
+ * Use `pick()` when you want to read a snapshot of the current value without creating a reactive
255
+ * dependency. This is useful for:
256
+ * - Reading initial values
257
+ * - Accessing configuration that shouldn't trigger updates
258
+ * - Mixing tracked and untracked reads in the same effect
122
259
  *
123
- * @public
124
- */
125
- dispose() {
126
- if (this._disposed) throw new Error("[PicoFlow] Effect is disposed");
127
- Array.from(this._dependencies).forEach((dependency) => {
128
- this._unregisterDependency(dependency);
129
- });
130
- this._disposed = true;
131
- }
132
- /**
133
- * Indicates whether this effect has been disposed.
260
+ * @example
261
+ * ```typescript
262
+ * // Read a snapshot outside reactive context
263
+ * const currentValue = $state.pick();
134
264
  *
135
- * @returns A boolean value that is true if the effect is disposed, false otherwise.
265
+ * // Mix tracked and untracked reads
266
+ * effect((t) => {
267
+ * const tracked = $reactive.get(t); // Triggers re-runs
268
+ * const snapshot = $config.pick(); // Doesn't trigger re-runs
269
+ * processData(tracked, snapshot);
270
+ * });
271
+ * ```
136
272
  *
137
273
  * @public
138
274
  */
139
- get disposed() {
140
- return this._disposed;
141
- }
142
- /* INTERNAL ------------------------------------------------------------ */
143
- _disposed = false;
144
- _initialized = false;
145
- _dependencies = /* @__PURE__ */ new Set();
146
- _trackedGet = (observable) => observable._getFrom(this);
147
- _trackedWatch = (signal) => signal._watchFrom(this);
148
- _untrackedGet = (observable) => observable.get();
149
- _untrackedWatch = (signal) => signal._watch();
150
- _trackedExec;
151
- _untrackedExec;
152
- /*@internal*/
153
- _exec() {
154
- if (this._disposed)
155
- throw new Error("[PicoFlow] Effect is disposed");
156
- if (this._initialized) this._untrackedExec();
157
- else {
158
- this._trackedExec();
159
- this._initialized = true;
160
- }
161
- }
162
- /*@internal*/
163
- _registerDependency(dependency) {
164
- this._dependencies.add(dependency);
165
- dependency._registerEffect(this);
166
- }
167
- /*@internal*/
168
- _unregisterDependency(dependency) {
169
- this._dependencies.delete(dependency);
170
- dependency._unregisterEffect(this);
275
+ pick() {
276
+ return this._getRaw();
171
277
  }
172
- }
173
-
174
- class FlowObservable extends FlowSignal {
175
278
  /* INTERNAL -------------------------------------------*/
176
- /*@internal*/
279
+ /** @internal */
177
280
  _value;
178
- /*@internal*/
179
- _getFrom(listener) {
180
- listener._registerDependency(this);
181
- return this.get();
182
- }
183
281
  /**
184
282
  * Subscribes a listener function to changes of the observable.
185
283
  * The listener is executed immediately with the current value and on subsequent updates.
@@ -187,8 +285,8 @@ class FlowObservable extends FlowSignal {
187
285
  * @returns A disposer function to cancel the subscription.
188
286
  */
189
287
  subscribe(listener) {
190
- const effect = new FlowEffect((get) => {
191
- listener(get(this));
288
+ const effect = new FlowEffect((t) => {
289
+ listener(this.get(t));
192
290
  });
193
291
  return () => effect.dispose();
194
292
  }
@@ -198,7 +296,10 @@ class FlowConstant extends FlowObservable {
198
296
  /**
199
297
  * Creates a new FlowConstant instance.
200
298
  *
201
- * @param value - Either a direct value of type T or a function returning a value of type T for lazy initialization.
299
+ * @param value - Either a direct value of type T or a function returning a value of type T.
300
+ * If a function is provided, it will be invoked lazily on the first value access.
301
+ * If a direct value is provided, it is stored immediately.
302
+ *
202
303
  * @public
203
304
  */
204
305
  constructor(value) {
@@ -206,26 +307,20 @@ class FlowConstant extends FlowObservable {
206
307
  this._initEager(value);
207
308
  }
208
309
  /**
209
- * Retrieves the constant value, computing it lazily if needed.
210
- *
211
- * Accessing this method will initialize the value if it has not been computed already.
212
- * Throws an error if the instance has been disposed or if lazy initialization fails.
213
- *
214
- * @returns The cached constant value.
215
- * @throws Error if the constant is disposed or cannot be initialized.
216
- * @public
310
+ * Internal method to get the raw value.
311
+ * @internal
217
312
  */
218
- get() {
313
+ _getRaw() {
219
314
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
220
315
  this._initLazy();
221
316
  return this._value;
222
317
  }
223
318
  /* INTERNAL --------------------------------------------------------- */
224
- /*@internal*/
319
+ /** @internal */
225
320
  _initialized = false;
226
- /*@internal*/
321
+ /** @internal */
227
322
  _init;
228
- /*@internal*/
323
+ /** @internal */
229
324
  _initEager(value) {
230
325
  if (typeof value === "function") {
231
326
  this._init = value;
@@ -234,7 +329,7 @@ class FlowConstant extends FlowObservable {
234
329
  this._initialized = true;
235
330
  }
236
331
  }
237
- /*@internal*/
332
+ /** @internal */
238
333
  _initLazy() {
239
334
  if (!this._initialized && this._init) {
240
335
  this._value = this._init();
@@ -245,81 +340,54 @@ class FlowConstant extends FlowObservable {
245
340
  }
246
341
  }
247
342
 
248
- class FlowState extends FlowConstant {
249
- /**
250
- * Updates the state with a new value.
251
- * @param value - A new value or a callback function that computes a new value based on the current state.
252
- * @remarks
253
- * If the computed new value is strictly equal to the current state value, no change is made and subscribers
254
- * will not be notified. Otherwise, the state is updated and all subscribers are informed of the change.
255
- * @throws Error if the state has been disposed.
256
- * @public
257
- */
258
- set(value) {
259
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
260
- const next = typeof value === "function" ? value(this._value) : value;
261
- if (next === this._value) return;
262
- this._value = next;
263
- this._notify();
264
- }
265
- }
266
-
267
343
  class FlowDerivation extends FlowObservable {
268
344
  /**
269
345
  * Creates a new FlowDerivation.
270
- * @param compute - A function that computes the derived value. It is provided with two parameters:
271
- * a getter and a watcher that respect dependency tracking.
346
+ *
347
+ * @param compute - A function that computes the derived value using a tracking context.
348
+ * The function receives a TrackingContext and should use it to access dependencies via
349
+ * `.get(t)`. The function is not executed immediately; it runs lazily on first access.
350
+ *
272
351
  * @public
273
352
  */
274
353
  constructor(compute) {
275
354
  super();
276
- this._initEager(compute);
355
+ this._compute = compute;
356
+ this._trackedContext = new TrackingContext(this);
277
357
  }
278
358
  /**
279
- * Gets the current derived value.
280
- * @returns The current computed value.
281
- * @remarks
282
- * This method lazily initializes and updates the derivation if it is marked as dirty. It throws an error
283
- * if the derivation has been disposed.
284
- * @public
359
+ * Internal method to get the raw value.
360
+ * @internal
285
361
  */
286
- get() {
362
+ _getRaw() {
287
363
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
288
364
  this._initLazy();
289
- this._compute();
365
+ this._update();
290
366
  return this._value;
291
367
  }
292
368
  /* INTERNAL --------------------------------------------------------- */
293
369
  _initialized = false;
294
370
  _dirty = false;
295
- _trackedGet = (observable) => observable._getFrom(this);
296
- _trackedWatch = (signal) => signal._watchFrom(this);
297
- _untrackedGet = (observable) => observable.get();
298
- _untrackedWatch = (signal) => signal._watch();
299
- _trackedCompute;
300
- _untrackedCompute;
301
- _initEager(compute) {
302
- this._trackedCompute = () => compute(this._trackedGet, this._trackedWatch);
303
- this._untrackedCompute = () => compute(this._untrackedGet, this._untrackedWatch);
304
- }
371
+ _compute;
372
+ _trackedContext;
305
373
  _initLazy() {
306
374
  if (!this._initialized) {
307
- this._value = this._trackedCompute();
375
+ this._value = this._compute(this._trackedContext);
308
376
  this._initialized = true;
309
377
  }
310
378
  }
311
379
  /* @internal */
312
- _compute() {
380
+ _update() {
313
381
  if (this._dirty) {
314
382
  const dependencies = [...this._dependencies];
315
383
  this._dependencies.clear();
316
- this._value = this._trackedCompute();
384
+ this._value = this._compute(this._trackedContext);
317
385
  const dependenciesToRemove = dependencies.filter(
318
386
  (dependency) => !this._dependencies.has(dependency)
319
387
  );
320
- dependenciesToRemove.forEach(
321
- (dependency) => dependency._unregisterDependency(this)
322
- );
388
+ dependenciesToRemove.forEach((dependency) => {
389
+ dependency._unregisterDependency(this);
390
+ });
323
391
  this._dirty = false;
324
392
  }
325
393
  }
@@ -328,259 +396,99 @@ class FlowDerivation extends FlowObservable {
328
396
  this._dirty = true;
329
397
  super._notify();
330
398
  }
331
- /* @internal */
332
- _watch() {
333
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
334
- this._initLazy();
335
- this._compute();
336
- }
337
- }
338
-
339
- function isDisposable(obj) {
340
- return obj !== null && obj !== void 0 && typeof obj.dispose === "function";
341
- }
342
-
343
- class FlowMap extends FlowState {
344
399
  /**
345
- * A reactive state that holds the most recent key and value that were set.
400
+ * Watches the derivation, registering it as a dependency in the given context.
346
401
  *
347
- * @remarks
348
- * When a key is set via {@link FlowMap.setAt}, this state is updated with
349
- * the corresponding key and value.
350
- *
351
- * @public
352
- */
353
- $lastSet = new FlowState({});
354
- /**
355
- * A reactive state that holds the most recent key and value that were deleted.
402
+ * @param context - The tracking context in which to register this derivation.
356
403
  *
357
404
  * @remarks
358
- * When a key is deleted via {@link FlowMap.delete}, this state is updated with
359
- * the corresponding key and its last known value.
405
+ * This method overrides the base `watch()` to handle a special case: when a derivation
406
+ * is watched without having its value read (e.g., `$derivation.watch(t)` instead of
407
+ * `$derivation.get(t)`), the derivation still needs to compute its value to establish
408
+ * its own dependencies. Otherwise, the derivation would be tracked but wouldn't track
409
+ * its own dependencies, breaking the reactive chain.
360
410
  *
361
- * @public
362
- */
363
- $lastDeleted = new FlowState({});
364
- /**
365
- * Sets a value at the specified key in the underlying map.
411
+ * This override ensures that:
412
+ * 1. The derivation is registered as a dependency in the provided context
413
+ * 2. The derivation computes its value (if not already computed)
414
+ * 3. The derivation tracks its own dependencies during computation
366
415
  *
367
- * @param key - The key at which to set the value.
368
- * @param value - The value to set.
369
- * @throws If the FlowMap instance is disposed.
416
+ * @example
417
+ * ```typescript
418
+ * const $derived = derivation((t) => $state.get(t) * 2);
370
419
  *
371
- * @remarks
372
- * Updates the internal map, emits the key-value pair via {@link FlowMap.$lastSet},
373
- * and notifies all subscribers of the change.
420
+ * effect((t) => {
421
+ * $derived.watch(t); // Derivation computes even without reading value
422
+ * doSomething(); // Effect re-runs when $derived's dependencies change
423
+ * });
424
+ * ```
374
425
  *
375
426
  * @public
376
427
  */
377
- setAt(key, value) {
378
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
379
- this._value.set(key, value);
380
- this.$lastSet.set({ key, value });
381
- this._notify();
428
+ watch(context) {
429
+ super.watch(context);
430
+ this._initLazy();
431
+ this._update();
382
432
  }
433
+ }
434
+
435
+ function isDisposable(obj) {
436
+ return obj !== null && obj !== void 0 && typeof obj.dispose === "function";
437
+ }
438
+
439
+ class FlowState extends FlowConstant {
383
440
  /**
384
- * Deletes the value at the specified key from the underlying map.
385
- *
386
- * @param key - The key to delete.
387
- * @throws If the FlowMap instance is disposed.
388
- *
441
+ * Updates the state with a new value.
442
+ * @param value - A new value or a callback function that computes a new value based on the current state.
389
443
  * @remarks
390
- * Removes the key from the internal map, emits the deleted key and its value via {@link FlowMap.$lastDeleted},
391
- * and notifies all subscribers of the change.
392
- *
444
+ * If the computed new value is strictly equal to the current state value, no change is made and subscribers
445
+ * will not be notified. Otherwise, the state is updated and all subscribers are informed of the change.
446
+ * @throws Error if the state has been disposed.
393
447
  * @public
394
448
  */
395
- delete(key) {
449
+ set(value) {
396
450
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
397
- const value = this._value.get(key);
398
- this._value.delete(key);
399
- this.$lastDeleted.set({ key, value });
451
+ const next = typeof value === "function" ? value(this._value) : value;
452
+ if (next === this._value) return;
453
+ this._value = next;
400
454
  this._notify();
401
455
  }
402
456
  }
403
457
 
404
- class FlowStream extends FlowObservable {
405
- /**
406
- * Creates a new FlowStream.
407
- * @param updater - A function that receives a setter to update the stream's value.
408
- * It should return a disposer function that will be called upon disposal.
409
- * @public
410
- */
411
- constructor(updater) {
412
- super();
413
- this._disposer = updater((value) => {
414
- this._set(value);
415
- });
416
- }
417
- /**
418
- * Retrieves the current value of the stream.
419
- * @returns The current value, or undefined if no value has been set yet.
420
- * @throws Error if the stream is disposed.
421
- * @public
422
- */
423
- get() {
424
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
425
- return this._value;
426
- }
427
- /**
428
- * Disposes the stream, releasing all resources.
429
- * @remarks
430
- * In addition to disposing the underlying observable, this method calls the disposer
431
- * returned by the updater.
432
- * @public
433
- */
434
- dispose() {
435
- super.dispose();
436
- this._disposer();
437
- }
438
- /* INTERNAL ------------------------------------------------------ */
439
- _disposer;
440
- _set(value) {
441
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
442
- if (value === this._value) return;
443
- this._value = value;
444
- this._notify();
445
- }
458
+ function signal() {
459
+ return new FlowSignal();
446
460
  }
447
-
448
- class FlowStreamAsync extends FlowObservable {
449
- /**
450
- * Creates a new asynchronous FlowStream.
451
- * @param updater - A function that receives a setter to update the stream's value.
452
- * It should return a disposer function that will be called upon disposal.
453
- * @remarks The updater function can invoke the setter asynchronously to update the stream.
454
- * @public
455
- */
456
- constructor(updater) {
457
- super();
458
- this._disposer = updater((value) => {
459
- this._set(value);
460
- });
461
- this._value = new Promise((resolve) => {
462
- this._resolve = resolve;
463
- });
464
- }
465
- /**
466
- * Retrieves the current value of the stream as a Promise.
467
- * @returns A Promise that resolves to the current value.
468
- * @throws Error if the stream is disposed.
469
- * @public
470
- */
471
- get() {
472
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
473
- return this._value;
474
- }
475
- /**
476
- * Disposes the stream, releasing all resources.
477
- * @remarks In addition to disposing the underlying observable, this method calls the disposer
478
- * returned by the updater.
479
- * @public
480
- */
481
- dispose() {
482
- super.dispose();
483
- this._disposer();
484
- }
485
- /* INTERNAL ------------------------------------------------------ */
486
- _initialized = false;
487
- _awaitedValue;
488
- _resolve;
489
- _disposer;
490
- _set(value) {
491
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
492
- if (!this._initialized) {
493
- this._resolve(value);
494
- this._initialized = true;
495
- this._awaitedValue = value;
496
- this._notify();
497
- return;
498
- }
499
- if (value === this._awaitedValue) return;
500
- this._value = Promise.resolve(value);
501
- this._awaitedValue = value;
502
- this._notify();
503
- }
461
+ function constant(value) {
462
+ return new FlowConstant(value);
504
463
  }
505
-
506
- class FlowResource extends FlowObservable {
507
- /**
508
- * Creates a new FlowResource.
509
- * @param fetch - An asynchronous function that retrieves the resource's value.
510
- *
511
- * @public
512
- */
513
- constructor(fetch) {
514
- super();
515
- this._fetch = fetch;
516
- }
517
- /**
518
- * Retrieves the current resource value.
519
- * @returns The current value, or undefined if the resource has not been fetched yet.
520
- * @throws Error if the resource is disposed.
521
- * @public
522
- */
523
- get() {
524
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
525
- return this._value;
526
- }
527
- /**
528
- * Asynchronously fetches a new value for the resource.
529
- * @remarks
530
- * Executes the internal fetch function. If the fetched value differs from the current one,
531
- * updates the resource's value and notifies subscribers.
532
- * @returns A Promise that resolves when the fetch operation is complete.
533
- * @throws Error if the resource is disposed.
534
- * @public
535
- */
536
- async fetch() {
537
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
538
- const value = await this._fetch();
539
- if (value === this._value) return;
540
- this._value = value;
541
- this._notify();
542
- }
543
- /* INTERNAL ------------------------------------------------ */
544
- _fetch;
464
+ function state(value) {
465
+ return new FlowState(value);
545
466
  }
546
-
547
- class FlowResourceAsync extends FlowObservable {
548
- /**
549
- * Creates a new FlowResource.
550
- * @param fetch - An asynchronous function that retrieves the resource's value.
551
- * @public
552
- */
553
- constructor(fetch) {
554
- super();
555
- this._fetch = fetch;
556
- }
557
- /**
558
- * Retrieves the current resource value.
559
- * @returns The current value, or undefined if the resource has not been fetched yet.
560
- * @throws Error if the resource is disposed.
561
- * @public
562
- */
563
- get() {
564
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
565
- if (!this._value) this._value = this._fetch();
566
- return this._value;
567
- }
568
- /**
569
- * Asynchronously fetches a new value for the resource.
570
- * @remarks
571
- * Executes the internal fetch function. If the fetched value differs from the current one,
572
- * updates the resource's value and notifies subscribers.
573
- * @returns A Promise that resolves when the fetch operation is complete.
574
- * @throws Error if the resource is disposed.
575
- * @public
576
- */
577
- async fetch() {
578
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
579
- this._value = this._fetch();
580
- this._notify();
581
- }
582
- /* INTERNAL ------------------------------------------------ */
583
- _fetch;
467
+ function resource(fn) {
468
+ return new FlowResource(fn);
469
+ }
470
+ function resourceAsync(fn) {
471
+ return new FlowResourceAsync(fn);
472
+ }
473
+ function stream(updater) {
474
+ return new FlowStream(updater);
475
+ }
476
+ function streamAsync(updater) {
477
+ return new FlowStreamAsync(updater);
478
+ }
479
+ function derivation(fn) {
480
+ return new FlowDerivation(fn);
481
+ }
482
+ function effect(fn) {
483
+ return new FlowEffect(fn);
484
+ }
485
+ function map(initial) {
486
+ return new FlowMap(
487
+ new Map(initial ? Object.entries(initial) : [])
488
+ );
489
+ }
490
+ function array(initial) {
491
+ return new FlowArray(initial);
584
492
  }
585
493
 
586
494
  class FlowArray extends FlowObservable {
@@ -612,11 +520,10 @@ class FlowArray extends FlowObservable {
612
520
  return this._value.length;
613
521
  }
614
522
  /**
615
- * Returns a copy of the internal array.
616
- * @returns A copy of the array.
617
- * @public
523
+ * Internal method to get the raw value.
524
+ * @internal
618
525
  */
619
- get() {
526
+ _getRaw() {
620
527
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
621
528
  return [...this._value];
622
529
  }
@@ -745,762 +652,313 @@ class FlowArray extends FlowObservable {
745
652
  this._value = [];
746
653
  }
747
654
  /* INTERNAL */
748
- /*@internal*/
655
+ /** @internal */
749
656
  _value = [];
750
657
  }
751
658
 
752
- function signal() {
753
- return new FlowSignal();
754
- }
755
- function constant(value) {
756
- return new FlowConstant(value);
757
- }
758
- function state(value) {
759
- return new FlowState(value);
760
- }
761
- function resource(fn) {
762
- return new FlowResource(fn);
763
- }
764
- function resourceAsync(fn) {
765
- return new FlowResourceAsync(fn);
766
- }
767
- function stream(updater) {
768
- return new FlowStream(updater);
769
- }
770
- function streamAsync(updater) {
771
- return new FlowStreamAsync(updater);
772
- }
773
- function derivation(fn) {
774
- return new FlowDerivation(fn);
775
- }
776
- function effect(fn) {
777
- return new FlowEffect(fn);
778
- }
779
- function map(initial) {
780
- return new FlowMap(
781
- new Map(initial ? Object.entries(initial) : [])
782
- );
783
- }
784
- function array(initial) {
785
- return new FlowArray(initial);
786
- }
787
-
788
- let FlowSignal$1 = class FlowSignal {
659
+ class FlowMap extends FlowState {
789
660
  /**
790
- * Triggers the FlowSignal.
791
- * Notifies all registered listeners and schedules execution of associated effects.
792
- * @throws If the FlowSignal has already been disposed.
661
+ * A reactive state that holds the most recent key and value that were added.
662
+ *
663
+ * @remarks
664
+ * When a key is added via {@link FlowMap.add}, this state is updated with
665
+ * the corresponding key and value.
666
+ *
793
667
  * @public
794
668
  */
795
- trigger() {
669
+ $lastAdded = new FlowState(null);
670
+ /**
671
+ * A reactive state that holds the most recent key and value that were updated.
672
+ *
673
+ * @remarks
674
+ * When a key is updated via {@link FlowMap.update}, this state is updated with
675
+ * the corresponding key and value.
676
+ *
677
+ * @public
678
+ */
679
+ $lastUpdated = new FlowState(null);
680
+ /**
681
+ * A reactive state that holds the most recent key and value that were deleted.
682
+ *
683
+ * @remarks
684
+ * When a key is deleted via {@link FlowMap.delete}, this state is updated with
685
+ * the corresponding key and its last known value.
686
+ *
687
+ * @public
688
+ */
689
+ $lastDeleted = new FlowState(null);
690
+ /**
691
+ * Adds a new key-value pair to the map.
692
+ *
693
+ * @param key - The key to add.
694
+ * @param value - The value to associate with the key.
695
+ * @throws If the FlowMap instance is disposed.
696
+ * @throws If the key already exists in the map.
697
+ *
698
+ * @remarks
699
+ * Adds a new entry to the internal map, emits the key-value pair via {@link FlowMap.$lastAdded},
700
+ * and notifies all subscribers of the change.
701
+ *
702
+ * @public
703
+ */
704
+ add(key, value) {
796
705
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
706
+ if (this._value.has(key)) {
707
+ throw new Error("[PicoFlow] Key already exists");
708
+ }
709
+ this._value.set(key, value);
710
+ this.$lastAdded.set({ key, value });
797
711
  this._notify();
798
712
  }
799
713
  /**
800
- * Disposes the FlowSignal.
801
- * Cleans up all registered effects, listeners, and dependencies.
802
- * Once disposed, further usage of the signal will throw an error.
803
- * @throws If the FlowSignal is already disposed.
714
+ * Updates an existing key-value pair in the map.
715
+ *
716
+ * @param key - The key to update.
717
+ * @param value - The new value to associate with the key.
718
+ * @throws If the FlowMap instance is disposed.
719
+ * @throws If the key does not exist in the map.
720
+ *
721
+ * @remarks
722
+ * Updates an existing entry in the internal map, emits the key-value pair via {@link FlowMap.$lastUpdated},
723
+ * and notifies all subscribers of the change.
724
+ *
804
725
  * @public
805
726
  */
806
- dispose(options) {
727
+ update(key, value) {
807
728
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
808
- if (options?.self) {
809
- Array.from(this._effects).forEach(
810
- (effect) => effect._unregisterDependency(this)
811
- );
812
- Array.from(this._listeners).forEach(
813
- (listener) => listener._unregisterDependency(this)
814
- );
815
- } else {
816
- Array.from(this._effects).forEach((effect) => effect.dispose());
817
- Array.from(this._listeners).forEach(
818
- (listener) => listener.dispose()
819
- );
729
+ if (!this._value.has(key)) {
730
+ throw new Error("[PicoFlow] Key does not exist");
820
731
  }
821
- Array.from(this._dependencies).forEach((dependency) => {
822
- this._unregisterDependency(dependency);
823
- });
824
- this._disposed = true;
732
+ this._value.set(key, value);
733
+ this.$lastUpdated.set({ key, value });
734
+ this._notify();
825
735
  }
826
736
  /**
827
- * Indicates whether the FlowSignal has been disposed.
828
- * @remarks Once disposed, the signal should not be used.
829
- * @public
830
- */
831
- get disposed() {
832
- return this._disposed;
833
- }
834
- /* INTERNAL ------------------------------------------------------------- */
835
- /*@internal*/
836
- _disposed = false;
837
- /*@internal*/
838
- _dependencies = /* @__PURE__ */ new Set();
839
- /*@internal*/
840
- _listeners = /* @__PURE__ */ new Set();
841
- /*@internal*/
842
- _effects = /* @__PURE__ */ new Set();
843
- /*@internal*/
844
- _watch() {
845
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
846
- }
847
- /*@internal*/
848
- _notify() {
849
- this._listeners.forEach((listener) => listener._notify());
850
- this._effects.forEach((effect) => effect._exec());
851
- }
852
- /*@internal*/
853
- _watchFrom(listener) {
854
- listener._registerDependency(this);
855
- this._watch();
856
- }
857
- /*@internal*/
858
- _registerDependency(dependency) {
859
- this._dependencies.add(dependency);
860
- dependency._registerListener(this);
861
- }
862
- /*@internal*/
863
- _unregisterDependency(dependency) {
864
- this._dependencies.delete(dependency);
865
- dependency._unregisterListener(this);
866
- }
867
- /*@internal*/
868
- _registerListener(signal) {
869
- this._listeners.add(signal);
870
- }
871
- /*@internal*/
872
- _unregisterListener(signal) {
873
- this._listeners.delete(signal);
874
- }
875
- /*@internal*/
876
- _registerEffect(effect) {
877
- this._effects.add(effect);
878
- }
879
- /*@internal*/
880
- _unregisterEffect(effect) {
881
- this._effects.delete(effect);
882
- }
883
- };
884
-
885
- let FlowEffect$1 = class FlowEffect {
886
- /**
887
- * Creates a new FlowEffect.
737
+ * Deletes the value at the specified key from the underlying map.
888
738
  *
889
- * @param apply - A side-effect function that receives a getter and a watcher to
890
- * access and register dependencies on reactive observables and signals.
739
+ * @param key - The key to delete.
740
+ * @throws If the FlowMap instance is disposed.
891
741
  *
892
742
  * @remarks
893
- * The provided function is executed immediately in a tracked mode to collect dependencies.
894
- * On subsequent executions, it runs in an untracked mode.
743
+ * Removes the key from the internal map, emits the deleted key and its value via {@link FlowMap.$lastDeleted},
744
+ * and notifies all subscribers of the change.
895
745
  *
896
746
  * @public
897
747
  */
898
- constructor(apply) {
899
- this._trackedExec = () => apply(this._trackedGet, this._trackedWatch);
900
- this._untrackedExec = () => apply(this._untrackedGet, this._untrackedWatch);
901
- this._exec();
748
+ delete(key) {
749
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
750
+ const value = this._value.get(key);
751
+ if (!value) throw new Error("[PicoFlow] Key does not exist");
752
+ this._value.delete(key);
753
+ this.$lastDeleted.set({ key, value });
754
+ this._notify();
902
755
  }
903
756
  /**
904
- * Disposes the effect, unregistering all its tracked dependencies.
905
- *
906
- * @remarks
907
- * Once disposed, the effect must no longer be used. Trying to dispose an effect
908
- * that is already disposed will throw an error.
909
- *
757
+ * Disposes the FlowMap and its values.
758
+ * @param options - Disposal options.
910
759
  * @public
911
760
  */
912
- dispose() {
913
- if (this._disposed) throw new Error("[PicoFlow] Effect is disposed");
914
- Array.from(this._dependencies).forEach((dependency) => {
915
- this._unregisterDependency(dependency);
761
+ dispose(options) {
762
+ super.dispose(options);
763
+ this._value.forEach((item) => {
764
+ if (isDisposable(item)) item.dispose(options);
916
765
  });
917
- this._disposed = true;
766
+ this._value.clear();
767
+ this.$lastAdded.dispose(options);
768
+ this.$lastUpdated.dispose(options);
769
+ this.$lastDeleted.dispose(options);
918
770
  }
771
+ }
772
+
773
+ class FlowResource extends FlowObservable {
919
774
  /**
920
- * Indicates whether this effect has been disposed.
775
+ * Creates a new FlowResource.
921
776
  *
922
- * @returns A boolean value that is true if the effect is disposed, false otherwise.
777
+ * @param fetch - An asynchronous function that retrieves the resource's value.
778
+ * This function is not invoked on construction; you must call the `fetch()` method
779
+ * to execute it.
923
780
  *
924
781
  * @public
925
782
  */
926
- get disposed() {
927
- return this._disposed;
928
- }
929
- /* INTERNAL ------------------------------------------------------------ */
930
- _disposed = false;
931
- _initialized = false;
932
- _dependencies = /* @__PURE__ */ new Set();
933
- _trackedGet = (observable) => observable._getFrom(this);
934
- _trackedWatch = (signal) => signal._watchFrom(this);
935
- _untrackedGet = (observable) => observable.get();
936
- _untrackedWatch = (signal) => signal._watch();
937
- _trackedExec;
938
- _untrackedExec;
939
- /*@internal*/
940
- _exec() {
941
- if (this._disposed)
942
- throw new Error("[PicoFlow] Effect is disposed");
943
- if (this._initialized) this._untrackedExec();
944
- else {
945
- this._trackedExec();
946
- this._initialized = true;
947
- }
948
- }
949
- /*@internal*/
950
- _registerDependency(dependency) {
951
- this._dependencies.add(dependency);
952
- dependency._registerEffect(this);
953
- }
954
- /*@internal*/
955
- _unregisterDependency(dependency) {
956
- this._dependencies.delete(dependency);
957
- dependency._unregisterEffect(this);
783
+ constructor(fetch) {
784
+ super();
785
+ this._fetch = fetch;
958
786
  }
959
- };
960
-
961
- let FlowObservable$1 = class FlowObservable extends FlowSignal$1 {
962
- /* INTERNAL -------------------------------------------*/
963
- /*@internal*/
964
- _value;
965
- /*@internal*/
966
- _getFrom(listener) {
967
- listener._registerDependency(this);
968
- return this.get();
787
+ /**
788
+ * Internal method to get the raw value.
789
+ * @internal
790
+ */
791
+ _getRaw() {
792
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
793
+ return this._value;
969
794
  }
970
795
  /**
971
- * Subscribes a listener function to changes of the observable.
972
- * The listener is executed immediately with the current value and on subsequent updates.
973
- * @param listener - A callback function that receives the new value.
974
- * @returns A disposer function to cancel the subscription.
796
+ * Asynchronously fetches a new value for the resource.
797
+ * @remarks
798
+ * Executes the internal fetch function. If the fetched value differs from the current one,
799
+ * updates the resource's value and notifies subscribers.
800
+ * @returns A Promise that resolves when the fetch operation is complete.
801
+ * @throws Error if the resource is disposed.
802
+ * @public
975
803
  */
976
- subscribe(listener) {
977
- const effect = new FlowEffect$1((get) => {
978
- listener(get(this));
979
- });
980
- return () => effect.dispose();
804
+ async fetch() {
805
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
806
+ const value = await this._fetch();
807
+ if (value === this._value) return;
808
+ this._value = value;
809
+ this._notify();
981
810
  }
982
- };
811
+ /* INTERNAL ------------------------------------------------ */
812
+ _fetch;
813
+ }
983
814
 
984
- let FlowDerivation$1 = class FlowDerivation extends FlowObservable$1 {
815
+ class FlowResourceAsync extends FlowObservable {
985
816
  /**
986
- * Creates a new FlowDerivation.
987
- * @param compute - A function that computes the derived value. It is provided with two parameters:
988
- * a getter and a watcher that respect dependency tracking.
817
+ * Creates a new FlowResource.
818
+ * @param fetch - An asynchronous function that retrieves the resource's value.
989
819
  * @public
990
820
  */
991
- constructor(compute) {
821
+ constructor(fetch) {
992
822
  super();
993
- this._initEager(compute);
823
+ this._fetch = fetch;
994
824
  }
995
825
  /**
996
- * Gets the current derived value.
997
- * @returns The current computed value.
998
- * @remarks
999
- * This method lazily initializes and updates the derivation if it is marked as dirty. It throws an error
1000
- * if the derivation has been disposed.
1001
- * @public
826
+ * Internal method to get the raw value.
827
+ * @internal
1002
828
  */
1003
- get() {
829
+ _getRaw() {
1004
830
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
1005
- this._initLazy();
1006
- this._compute();
831
+ if (!this._value) this._value = this._fetch();
1007
832
  return this._value;
1008
833
  }
1009
- /* INTERNAL --------------------------------------------------------- */
1010
- _initialized = false;
1011
- _dirty = false;
1012
- _trackedGet = (observable) => observable._getFrom(this);
1013
- _trackedWatch = (signal) => signal._watchFrom(this);
1014
- _untrackedGet = (observable) => observable.get();
1015
- _untrackedWatch = (signal) => signal._watch();
1016
- _trackedCompute;
1017
- _untrackedCompute;
1018
- _initEager(compute) {
1019
- this._trackedCompute = () => compute(this._trackedGet, this._trackedWatch);
1020
- this._untrackedCompute = () => compute(this._untrackedGet, this._untrackedWatch);
1021
- }
1022
- _initLazy() {
1023
- if (!this._initialized) {
1024
- this._value = this._trackedCompute();
1025
- this._initialized = true;
1026
- }
1027
- }
1028
- /* @internal */
1029
- _compute() {
1030
- if (this._dirty) {
1031
- const dependencies = [...this._dependencies];
1032
- this._dependencies.clear();
1033
- this._value = this._trackedCompute();
1034
- const dependenciesToRemove = dependencies.filter(
1035
- (dependency) => !this._dependencies.has(dependency)
1036
- );
1037
- dependenciesToRemove.forEach(
1038
- (dependency) => dependency._unregisterDependency(this)
1039
- );
1040
- this._dirty = false;
1041
- }
1042
- }
1043
- /* @internal */
1044
- _notify() {
1045
- this._dirty = true;
1046
- super._notify();
1047
- }
1048
- /* @internal */
1049
- _watch() {
834
+ /**
835
+ * Asynchronously fetches a new value for the resource.
836
+ * @remarks
837
+ * Executes the internal fetch function. If the fetched value differs from the current one,
838
+ * updates the resource's value and notifies subscribers.
839
+ * @returns A Promise that resolves when the fetch operation is complete.
840
+ * @throws Error if the resource is disposed.
841
+ * @public
842
+ */
843
+ async fetch() {
1050
844
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
1051
- this._initLazy();
1052
- this._compute();
845
+ this._value = this._fetch();
846
+ this._notify();
1053
847
  }
1054
- };
1055
-
1056
- const IS_DEV = false;
1057
- const equalFn = (a, b) => a === b;
1058
- const signalOptions = {
1059
- equals: equalFn
1060
- };
1061
- let runEffects = runQueue;
1062
- const STALE = 1;
1063
- const PENDING = 2;
1064
- const UNOWNED = {
1065
- };
1066
- const NO_INIT = {};
1067
- var Owner = null;
1068
- let Transition = null;
1069
- let ExternalSourceConfig = null;
1070
- let Listener = null;
1071
- let Updates = null;
1072
- let Effects = null;
1073
- let ExecCount = 0;
1074
- function createSignal(value, options) {
1075
- options = options ? Object.assign({}, signalOptions, options) : signalOptions;
1076
- const s = {
1077
- value,
1078
- observers: null,
1079
- observerSlots: null,
1080
- comparator: options.equals || undefined
1081
- };
1082
- const setter = value => {
1083
- if (typeof value === "function") {
1084
- value = value(s.value);
1085
- }
1086
- return writeSignal(s, value);
1087
- };
1088
- return [readSignal.bind(s), setter];
1089
- }
1090
- function createComputed(fn, value, options) {
1091
- const c = createComputation(fn, value, true, STALE);
1092
- updateComputation(c);
1093
- }
1094
- function createEffect(fn, value, options) {
1095
- runEffects = runUserEffects;
1096
- const c = createComputation(fn, value, false, STALE);
1097
- c.user = true;
1098
- Effects ? Effects.push(c) : updateComputation(c);
1099
- }
1100
- function createMemo(fn, value, options) {
1101
- options = options ? Object.assign({}, signalOptions, options) : signalOptions;
1102
- const c = createComputation(fn, value, true, 0);
1103
- c.observers = null;
1104
- c.observerSlots = null;
1105
- c.comparator = options.equals || undefined;
1106
- updateComputation(c);
1107
- return readSignal.bind(c);
1108
- }
1109
- function isPromise(v) {
1110
- return v && typeof v === "object" && "then" in v;
848
+ /* INTERNAL ------------------------------------------------ */
849
+ _fetch;
1111
850
  }
1112
- function createResource(pSource, pFetcher, pOptions) {
1113
- let source;
1114
- let fetcher;
1115
- let options;
1116
- {
1117
- source = true;
1118
- fetcher = pSource;
1119
- options = {};
1120
- }
1121
- let pr = null,
1122
- initP = NO_INIT,
1123
- scheduled = false,
1124
- resolved = "initialValue" in options,
1125
- dynamic = typeof source === "function" && createMemo(source);
1126
- const contexts = new Set(),
1127
- [value, setValue] = (options.storage || createSignal)(options.initialValue),
1128
- [error, setError] = createSignal(undefined),
1129
- [track, trigger] = createSignal(undefined, {
1130
- equals: false
1131
- }),
1132
- [state, setState] = createSignal(resolved ? "ready" : "unresolved");
1133
- function loadEnd(p, v, error, key) {
1134
- if (pr === p) {
1135
- pr = null;
1136
- key !== undefined && (resolved = true);
1137
- if ((p === initP || v === initP) && options.onHydrated) queueMicrotask(() => options.onHydrated(key, {
1138
- value: v
1139
- }));
1140
- initP = NO_INIT;
1141
- completeLoad(v, error);
1142
- }
1143
- return v;
1144
- }
1145
- function completeLoad(v, err) {
1146
- runUpdates(() => {
1147
- if (err === undefined) setValue(() => v);
1148
- setState(err !== undefined ? "errored" : resolved ? "ready" : "unresolved");
1149
- setError(err);
1150
- for (const c of contexts.keys()) c.decrement();
1151
- contexts.clear();
1152
- }, false);
1153
- }
1154
- function read() {
1155
- const c = SuspenseContext,
1156
- v = value(),
1157
- err = error();
1158
- if (err !== undefined && !pr) throw err;
1159
- if (Listener && !Listener.user && c) {
1160
- createComputed(() => {
1161
- track();
1162
- if (pr) {
1163
- if (c.resolved && Transition) ;else if (!contexts.has(c)) {
1164
- c.increment();
1165
- contexts.add(c);
1166
- }
1167
- }
1168
- });
1169
- }
1170
- return v;
1171
- }
1172
- function load(refetching = true) {
1173
- if (refetching !== false && scheduled) return;
1174
- scheduled = false;
1175
- const lookup = dynamic ? dynamic() : source;
1176
- if (lookup == null || lookup === false) {
1177
- loadEnd(pr, untrack(value));
1178
- return;
1179
- }
1180
- let error;
1181
- const p = initP !== NO_INIT ? initP : untrack(() => {
1182
- try {
1183
- return fetcher(lookup, {
1184
- value: value(),
1185
- refetching
1186
- });
1187
- } catch (fetcherError) {
1188
- error = fetcherError;
1189
- }
851
+
852
+ class FlowStream extends FlowObservable {
853
+ /**
854
+ * Creates a new FlowStream.
855
+ *
856
+ * @param updater - A function that receives a setter callback and returns a disposer.
857
+ * The setter should be called whenever new data is available. The disposer will be
858
+ * invoked when the stream is disposed to clean up resources.
859
+ *
860
+ * @remarks
861
+ * The updater is invoked immediately during construction. Make sure to return a proper
862
+ * cleanup function to avoid resource leaks.
863
+ *
864
+ * @public
865
+ */
866
+ constructor(updater) {
867
+ super();
868
+ this._disposer = updater((value) => {
869
+ this._set(value);
1190
870
  });
1191
- if (error !== undefined) {
1192
- loadEnd(pr, undefined, castError(error), lookup);
1193
- return;
1194
- } else if (!isPromise(p)) {
1195
- loadEnd(pr, p, undefined, lookup);
1196
- return p;
1197
- }
1198
- pr = p;
1199
- if ("v" in p) {
1200
- if (p.s === 1) loadEnd(pr, p.v, undefined, lookup);else loadEnd(pr, undefined, castError(p.v), lookup);
1201
- return p;
1202
- }
1203
- scheduled = true;
1204
- queueMicrotask(() => scheduled = false);
1205
- runUpdates(() => {
1206
- setState(resolved ? "refreshing" : "pending");
1207
- trigger();
1208
- }, false);
1209
- return p.then(v => loadEnd(p, v, undefined, lookup), e => loadEnd(p, undefined, castError(e), lookup));
1210
- }
1211
- Object.defineProperties(read, {
1212
- state: {
1213
- get: () => state()
1214
- },
1215
- error: {
1216
- get: () => error()
1217
- },
1218
- loading: {
1219
- get() {
1220
- const s = state();
1221
- return s === "pending" || s === "refreshing";
1222
- }
1223
- },
1224
- latest: {
1225
- get() {
1226
- if (!resolved) return read();
1227
- const err = error();
1228
- if (err && !pr) throw err;
1229
- return value();
1230
- }
1231
- }
1232
- });
1233
- let owner = Owner;
1234
- if (dynamic) createComputed(() => (owner = Owner, load(false)));else load(false);
1235
- return [read, {
1236
- refetch: info => runWithOwner(owner, () => load(info)),
1237
- mutate: setValue
1238
- }];
1239
- }
1240
- function untrack(fn) {
1241
- if (Listener === null) return fn();
1242
- const listener = Listener;
1243
- Listener = null;
1244
- try {
1245
- if (ExternalSourceConfig) ;
1246
- return fn();
1247
- } finally {
1248
- Listener = listener;
1249
- }
1250
- }
1251
- function onMount(fn) {
1252
- createEffect(() => untrack(fn));
1253
- }
1254
- function onCleanup(fn) {
1255
- if (Owner === null) ;else if (Owner.cleanups === null) Owner.cleanups = [fn];else Owner.cleanups.push(fn);
1256
- return fn;
1257
- }
1258
- function runWithOwner(o, fn) {
1259
- const prev = Owner;
1260
- const prevListener = Listener;
1261
- Owner = o;
1262
- Listener = null;
1263
- try {
1264
- return runUpdates(fn, true);
1265
- } catch (err) {
1266
- handleError(err);
1267
- } finally {
1268
- Owner = prev;
1269
- Listener = prevListener;
1270
- }
1271
- }
1272
- const [transPending, setTransPending] = /*@__PURE__*/createSignal(false);
1273
- let SuspenseContext;
1274
- function readSignal() {
1275
- if (this.sources && (this.state)) {
1276
- if ((this.state) === STALE) updateComputation(this);else {
1277
- const updates = Updates;
1278
- Updates = null;
1279
- runUpdates(() => lookUpstream(this), false);
1280
- Updates = updates;
1281
- }
1282
- }
1283
- if (Listener) {
1284
- const sSlot = this.observers ? this.observers.length : 0;
1285
- if (!Listener.sources) {
1286
- Listener.sources = [this];
1287
- Listener.sourceSlots = [sSlot];
1288
- } else {
1289
- Listener.sources.push(this);
1290
- Listener.sourceSlots.push(sSlot);
1291
- }
1292
- if (!this.observers) {
1293
- this.observers = [Listener];
1294
- this.observerSlots = [Listener.sources.length - 1];
1295
- } else {
1296
- this.observers.push(Listener);
1297
- this.observerSlots.push(Listener.sources.length - 1);
1298
- }
1299
871
  }
1300
- return this.value;
1301
- }
1302
- function writeSignal(node, value, isComp) {
1303
- let current = node.value;
1304
- if (!node.comparator || !node.comparator(current, value)) {
1305
- node.value = value;
1306
- if (node.observers && node.observers.length) {
1307
- runUpdates(() => {
1308
- for (let i = 0; i < node.observers.length; i += 1) {
1309
- const o = node.observers[i];
1310
- const TransitionRunning = Transition && Transition.running;
1311
- if (TransitionRunning && Transition.disposed.has(o)) ;
1312
- if (TransitionRunning ? !o.tState : !o.state) {
1313
- if (o.pure) Updates.push(o);else Effects.push(o);
1314
- if (o.observers) markDownstream(o);
1315
- }
1316
- if (!TransitionRunning) o.state = STALE;
1317
- }
1318
- if (Updates.length > 10e5) {
1319
- Updates = [];
1320
- if (IS_DEV) ;
1321
- throw new Error();
1322
- }
1323
- }, false);
1324
- }
1325
- }
1326
- return value;
1327
- }
1328
- function updateComputation(node) {
1329
- if (!node.fn) return;
1330
- cleanNode(node);
1331
- const time = ExecCount;
1332
- runComputation(node, node.value, time);
1333
- }
1334
- function runComputation(node, value, time) {
1335
- let nextValue;
1336
- const owner = Owner,
1337
- listener = Listener;
1338
- Listener = Owner = node;
1339
- try {
1340
- nextValue = node.fn(value);
1341
- } catch (err) {
1342
- if (node.pure) {
1343
- {
1344
- node.state = STALE;
1345
- node.owned && node.owned.forEach(cleanNode);
1346
- node.owned = null;
1347
- }
1348
- }
1349
- node.updatedAt = time + 1;
1350
- return handleError(err);
1351
- } finally {
1352
- Listener = listener;
1353
- Owner = owner;
1354
- }
1355
- if (!node.updatedAt || node.updatedAt <= time) {
1356
- if (node.updatedAt != null && "observers" in node) {
1357
- writeSignal(node, nextValue);
1358
- } else node.value = nextValue;
1359
- node.updatedAt = time;
872
+ /**
873
+ * Internal method to get the raw value.
874
+ * @internal
875
+ */
876
+ _getRaw() {
877
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
878
+ return this._value;
1360
879
  }
1361
- }
1362
- function createComputation(fn, init, pure, state = STALE, options) {
1363
- const c = {
1364
- fn,
1365
- state: state,
1366
- updatedAt: null,
1367
- owned: null,
1368
- sources: null,
1369
- sourceSlots: null,
1370
- cleanups: null,
1371
- value: init,
1372
- owner: Owner,
1373
- context: Owner ? Owner.context : null,
1374
- pure
1375
- };
1376
- if (Owner === null) ;else if (Owner !== UNOWNED) {
1377
- {
1378
- if (!Owner.owned) Owner.owned = [c];else Owner.owned.push(c);
1379
- }
880
+ /**
881
+ * Disposes the stream, releasing all resources.
882
+ * @remarks
883
+ * In addition to disposing the underlying observable, this method calls the disposer
884
+ * returned by the updater.
885
+ * @public
886
+ */
887
+ dispose() {
888
+ super.dispose();
889
+ this._disposer();
1380
890
  }
1381
- return c;
1382
- }
1383
- function runTop(node) {
1384
- if ((node.state) === 0) return;
1385
- if ((node.state) === PENDING) return lookUpstream(node);
1386
- if (node.suspense && untrack(node.suspense.inFallback)) return node.suspense.effects.push(node);
1387
- const ancestors = [node];
1388
- while ((node = node.owner) && (!node.updatedAt || node.updatedAt < ExecCount)) {
1389
- if (node.state) ancestors.push(node);
1390
- }
1391
- for (let i = ancestors.length - 1; i >= 0; i--) {
1392
- node = ancestors[i];
1393
- if ((node.state) === STALE) {
1394
- updateComputation(node);
1395
- } else if ((node.state) === PENDING) {
1396
- const updates = Updates;
1397
- Updates = null;
1398
- runUpdates(() => lookUpstream(node, ancestors[0]), false);
1399
- Updates = updates;
1400
- }
891
+ /* INTERNAL ------------------------------------------------------ */
892
+ _disposer;
893
+ _set(value) {
894
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
895
+ if (value === this._value) return;
896
+ this._value = value;
897
+ this._notify();
1401
898
  }
1402
899
  }
1403
- function runUpdates(fn, init) {
1404
- if (Updates) return fn();
1405
- let wait = false;
1406
- if (!init) Updates = [];
1407
- if (Effects) wait = true;else Effects = [];
1408
- ExecCount++;
1409
- try {
1410
- const res = fn();
1411
- completeUpdates(wait);
1412
- return res;
1413
- } catch (err) {
1414
- if (!wait) Effects = null;
1415
- Updates = null;
1416
- handleError(err);
900
+
901
+ class FlowStreamAsync extends FlowObservable {
902
+ /**
903
+ * Creates a new asynchronous FlowStream.
904
+ *
905
+ * @param updater - A function that receives a setter callback and returns a disposer.
906
+ * The setter should be called whenever new data is available (can be called asynchronously).
907
+ * The disposer will be invoked when the stream is disposed to clean up resources.
908
+ *
909
+ * @remarks
910
+ * The updater is invoked immediately during construction. An initial Promise is created
911
+ * that will resolve when the setter is first called. Make sure to return a proper cleanup
912
+ * function to avoid resource leaks.
913
+ *
914
+ * @public
915
+ */
916
+ constructor(updater) {
917
+ super();
918
+ this._disposer = updater((value) => {
919
+ this._set(value);
920
+ });
921
+ this._value = new Promise((resolve) => {
922
+ this._resolve = resolve;
923
+ });
1417
924
  }
1418
- }
1419
- function completeUpdates(wait) {
1420
- if (Updates) {
1421
- runQueue(Updates);
1422
- Updates = null;
1423
- }
1424
- if (wait) return;
1425
- const e = Effects;
1426
- Effects = null;
1427
- if (e.length) runUpdates(() => runEffects(e), false);
1428
- }
1429
- function runQueue(queue) {
1430
- for (let i = 0; i < queue.length; i++) runTop(queue[i]);
1431
- }
1432
- function runUserEffects(queue) {
1433
- let i,
1434
- userLength = 0;
1435
- for (i = 0; i < queue.length; i++) {
1436
- const e = queue[i];
1437
- if (!e.user) runTop(e);else queue[userLength++] = e;
1438
- }
1439
- for (i = 0; i < userLength; i++) runTop(queue[i]);
1440
- }
1441
- function lookUpstream(node, ignore) {
1442
- node.state = 0;
1443
- for (let i = 0; i < node.sources.length; i += 1) {
1444
- const source = node.sources[i];
1445
- if (source.sources) {
1446
- const state = source.state;
1447
- if (state === STALE) {
1448
- if (source !== ignore && (!source.updatedAt || source.updatedAt < ExecCount)) runTop(source);
1449
- } else if (state === PENDING) lookUpstream(source, ignore);
1450
- }
925
+ /**
926
+ * Internal method to get the raw value.
927
+ * @internal
928
+ */
929
+ _getRaw() {
930
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
931
+ return this._value;
1451
932
  }
1452
- }
1453
- function markDownstream(node) {
1454
- for (let i = 0; i < node.observers.length; i += 1) {
1455
- const o = node.observers[i];
1456
- if (!o.state) {
1457
- o.state = PENDING;
1458
- if (o.pure) Updates.push(o);else Effects.push(o);
1459
- o.observers && markDownstream(o);
1460
- }
933
+ /**
934
+ * Disposes the stream, releasing all resources.
935
+ * @remarks In addition to disposing the underlying observable, this method calls the disposer
936
+ * returned by the updater.
937
+ * @public
938
+ */
939
+ dispose() {
940
+ super.dispose();
941
+ this._disposer();
1461
942
  }
1462
- }
1463
- function cleanNode(node) {
1464
- let i;
1465
- if (node.sources) {
1466
- while (node.sources.length) {
1467
- const source = node.sources.pop(),
1468
- index = node.sourceSlots.pop(),
1469
- obs = source.observers;
1470
- if (obs && obs.length) {
1471
- const n = obs.pop(),
1472
- s = source.observerSlots.pop();
1473
- if (index < obs.length) {
1474
- n.sourceSlots[s] = index;
1475
- obs[index] = n;
1476
- source.observerSlots[index] = s;
1477
- }
1478
- }
943
+ /* INTERNAL ------------------------------------------------------ */
944
+ _initialized = false;
945
+ _awaitedValue;
946
+ _resolve;
947
+ _disposer;
948
+ _set(value) {
949
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
950
+ if (!this._initialized) {
951
+ this._resolve(value);
952
+ this._initialized = true;
953
+ this._awaitedValue = value;
954
+ this._notify();
955
+ return;
1479
956
  }
957
+ if (value === this._awaitedValue) return;
958
+ this._value = Promise.resolve(value);
959
+ this._awaitedValue = value;
960
+ this._notify();
1480
961
  }
1481
- if (node.tOwned) {
1482
- for (i = node.tOwned.length - 1; i >= 0; i--) cleanNode(node.tOwned[i]);
1483
- delete node.tOwned;
1484
- }
1485
- if (node.owned) {
1486
- for (i = node.owned.length - 1; i >= 0; i--) cleanNode(node.owned[i]);
1487
- node.owned = null;
1488
- }
1489
- if (node.cleanups) {
1490
- for (i = node.cleanups.length - 1; i >= 0; i--) node.cleanups[i]();
1491
- node.cleanups = null;
1492
- }
1493
- node.state = 0;
1494
- }
1495
- function castError(err) {
1496
- if (err instanceof Error) return err;
1497
- return new Error(typeof err === "string" ? err : "Unknown error", {
1498
- cause: err
1499
- });
1500
- }
1501
- function handleError(err, owner = Owner) {
1502
- const error = castError(err);
1503
- throw error;
1504
962
  }
1505
963
 
1506
964
  class SolidState {
@@ -1567,11 +1025,11 @@ class SolidResource {
1567
1025
  }
1568
1026
 
1569
1027
  function fromSync(state) {
1570
- const solidState = new SolidState(state.get());
1028
+ const solidState = new SolidState(state.pick());
1571
1029
  let fx;
1572
1030
  onMount(() => {
1573
- fx = new FlowEffect$1((get) => {
1574
- const value = get(state);
1031
+ fx = new FlowEffect((t) => {
1032
+ const value = state.get(t);
1575
1033
  solidState.set(() => value);
1576
1034
  });
1577
1035
  });
@@ -1580,13 +1038,13 @@ function fromSync(state) {
1580
1038
  }
1581
1039
  function fromAsync(derivation) {
1582
1040
  const solidResource = new SolidResource(async () => {
1583
- const value = await derivation.get();
1041
+ const value = await derivation.pick();
1584
1042
  return value;
1585
1043
  });
1586
1044
  let fx;
1587
1045
  onMount(() => {
1588
- fx = new FlowEffect$1(async (get) => {
1589
- await get(derivation);
1046
+ fx = new FlowEffect(async (t) => {
1047
+ await derivation.get(t);
1590
1048
  solidResource.refetch();
1591
1049
  });
1592
1050
  });
@@ -1594,7 +1052,7 @@ function fromAsync(derivation) {
1594
1052
  return solidResource;
1595
1053
  }
1596
1054
  function shallowFrom(flow) {
1597
- const initialValue = flow.get();
1055
+ const initialValue = flow.pick();
1598
1056
  const isAsync = initialValue instanceof Promise;
1599
1057
  if (isAsync) {
1600
1058
  return fromAsync(flow);
@@ -1602,10 +1060,10 @@ function shallowFrom(flow) {
1602
1060
  return fromSync(flow);
1603
1061
  }
1604
1062
  function deepFrom(getter) {
1605
- const derivation = new FlowDerivation$1((get) => {
1606
- return getter(get);
1063
+ const derivation = new FlowDerivation((t) => {
1064
+ return getter(t);
1607
1065
  });
1608
- const initialValue = derivation.get();
1066
+ const initialValue = derivation.pick();
1609
1067
  const isAsync = initialValue instanceof Promise;
1610
1068
  if (isAsync) {
1611
1069
  return fromAsync(derivation);
@@ -1613,10 +1071,10 @@ function deepFrom(getter) {
1613
1071
  return fromSync(derivation);
1614
1072
  }
1615
1073
  function from(flow) {
1616
- if (flow instanceof FlowObservable$1) {
1074
+ if (flow instanceof FlowObservable) {
1617
1075
  return shallowFrom(flow);
1618
1076
  }
1619
1077
  return deepFrom(flow);
1620
1078
  }
1621
1079
 
1622
- export { FlowArray, FlowConstant, FlowDerivation, FlowEffect, FlowMap, FlowObservable, FlowResource, FlowResourceAsync, FlowSignal, FlowState, FlowStream, FlowStreamAsync, SolidDerivation, SolidResource, SolidState, array, constant, derivation, effect, from, isDisposable, map, resource, resourceAsync, signal, state, stream, streamAsync };
1080
+ export { FlowArray, FlowConstant, FlowDerivation, FlowEffect, FlowMap, FlowObservable, FlowResource, FlowResourceAsync, FlowSignal, FlowState, FlowStream, FlowStreamAsync, SolidDerivation, SolidResource, SolidState, TrackingContext, array, constant, derivation, effect, from, isDisposable, map, resource, resourceAsync, signal, state, stream, streamAsync };