@ersbeth/picoflow 0.2.4 → 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 +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
package/dist/picoflow.js CHANGED
@@ -1,4 +1,87 @@
1
- import { createSignal, createResource, createMemo, onMount, onCleanup } from 'solid-js';
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
+ }
2
85
 
3
86
  class FlowSignal {
4
87
  /**
@@ -11,6 +94,43 @@ class FlowSignal {
11
94
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
12
95
  this._notify();
13
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
+ }
14
134
  /**
15
135
  * Disposes the FlowSignal.
16
136
  * Cleans up all registered effects, listeners, and dependencies.
@@ -21,17 +141,19 @@ class FlowSignal {
21
141
  dispose(options) {
22
142
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
23
143
  if (options?.self) {
24
- Array.from(this._effects).forEach(
25
- (effect) => effect._unregisterDependency(this)
26
- );
27
- Array.from(this._listeners).forEach(
28
- (listener) => listener._unregisterDependency(this)
29
- );
144
+ Array.from(this._effects).forEach((effect) => {
145
+ effect._unregisterDependency(this);
146
+ });
147
+ Array.from(this._listeners).forEach((listener) => {
148
+ listener._unregisterDependency(this);
149
+ });
30
150
  } else {
31
- Array.from(this._effects).forEach((effect) => effect.dispose());
32
- Array.from(this._listeners).forEach(
33
- (listener) => listener.dispose()
34
- );
151
+ Array.from(this._effects).forEach((effect) => {
152
+ effect.dispose();
153
+ });
154
+ Array.from(this._listeners).forEach((listener) => {
155
+ listener.dispose();
156
+ });
35
157
  }
36
158
  Array.from(this._dependencies).forEach((dependency) => {
37
159
  this._unregisterDependency(dependency);
@@ -47,141 +169,115 @@ class FlowSignal {
47
169
  return this._disposed;
48
170
  }
49
171
  /* INTERNAL ------------------------------------------------------------- */
50
- /*@internal*/
172
+ /** @internal */
51
173
  _disposed = false;
52
- /*@internal*/
174
+ /** @internal */
53
175
  _dependencies = /* @__PURE__ */ new Set();
54
- /*@internal*/
176
+ /** @internal */
55
177
  _listeners = /* @__PURE__ */ new Set();
56
- /*@internal*/
178
+ /** @internal */
57
179
  _effects = /* @__PURE__ */ new Set();
58
- /*@internal*/
59
- _watch() {
60
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
61
- }
62
- /*@internal*/
180
+ /** @internal */
63
181
  _notify() {
64
- this._listeners.forEach((listener) => listener._notify());
65
- this._effects.forEach((effect) => effect._exec());
66
- }
67
- /*@internal*/
68
- _watchFrom(listener) {
69
- listener._registerDependency(this);
70
- this._watch();
182
+ this._listeners.forEach((listener) => {
183
+ listener._notify();
184
+ });
185
+ this._effects.forEach((effect) => {
186
+ effect._exec();
187
+ });
71
188
  }
72
- /*@internal*/
189
+ /** @internal */
73
190
  _registerDependency(dependency) {
74
191
  this._dependencies.add(dependency);
75
192
  dependency._registerListener(this);
76
193
  }
77
- /*@internal*/
194
+ /** @internal */
78
195
  _unregisterDependency(dependency) {
79
196
  this._dependencies.delete(dependency);
80
197
  dependency._unregisterListener(this);
81
198
  }
82
- /*@internal*/
199
+ /** @internal */
83
200
  _registerListener(signal) {
84
201
  this._listeners.add(signal);
85
202
  }
86
- /*@internal*/
203
+ /** @internal */
87
204
  _unregisterListener(signal) {
88
205
  this._listeners.delete(signal);
89
206
  }
90
- /*@internal*/
207
+ /** @internal */
91
208
  _registerEffect(effect) {
92
209
  this._effects.add(effect);
93
210
  }
94
- /*@internal*/
211
+ /** @internal */
95
212
  _unregisterEffect(effect) {
96
213
  this._effects.delete(effect);
97
214
  }
98
215
  }
99
216
 
100
- class FlowEffect {
217
+ class FlowObservable extends FlowSignal {
101
218
  /**
102
- * Creates a new FlowEffect.
219
+ * Gets the current value with optional dependency tracking.
103
220
  *
104
- * @param apply - A side-effect function that receives a getter and a watcher to
105
- * 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.
106
226
  *
107
227
  * @remarks
108
- * The provided function is executed immediately in a tracked mode to collect dependencies.
109
- * 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
+ * ```
110
238
  *
111
239
  * @public
112
240
  */
113
- constructor(apply) {
114
- this._trackedExec = () => apply(this._trackedGet, this._trackedWatch);
115
- this._untrackedExec = () => apply(this._untrackedGet, this._untrackedWatch);
116
- this._exec();
241
+ get(context) {
242
+ if (context) {
243
+ this.watch(context);
244
+ }
245
+ return this._getRaw();
117
246
  }
118
247
  /**
119
- * 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.
120
251
  *
121
252
  * @remarks
122
- * Once disposed, the effect must no longer be used. Trying to dispose an effect
123
- * 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
124
259
  *
125
- * @public
126
- */
127
- dispose() {
128
- if (this._disposed) throw new Error("[PicoFlow] Effect is disposed");
129
- Array.from(this._dependencies).forEach((dependency) => {
130
- this._unregisterDependency(dependency);
131
- });
132
- this._disposed = true;
133
- }
134
- /**
135
- * Indicates whether this effect has been disposed.
260
+ * @example
261
+ * ```typescript
262
+ * // Read a snapshot outside reactive context
263
+ * const currentValue = $state.pick();
136
264
  *
137
- * @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
+ * ```
138
272
  *
139
273
  * @public
140
274
  */
141
- get disposed() {
142
- return this._disposed;
143
- }
144
- /* INTERNAL ------------------------------------------------------------ */
145
- _disposed = false;
146
- _initialized = false;
147
- _dependencies = /* @__PURE__ */ new Set();
148
- _trackedGet = (observable) => observable._getFrom(this);
149
- _trackedWatch = (signal) => signal._watchFrom(this);
150
- _untrackedGet = (observable) => observable.get();
151
- _untrackedWatch = (signal) => signal._watch();
152
- _trackedExec;
153
- _untrackedExec;
154
- /*@internal*/
155
- _exec() {
156
- if (this._disposed)
157
- throw new Error("[PicoFlow] Effect is disposed");
158
- if (this._initialized) this._untrackedExec();
159
- else {
160
- this._trackedExec();
161
- this._initialized = true;
162
- }
163
- }
164
- /*@internal*/
165
- _registerDependency(dependency) {
166
- this._dependencies.add(dependency);
167
- dependency._registerEffect(this);
168
- }
169
- /*@internal*/
170
- _unregisterDependency(dependency) {
171
- this._dependencies.delete(dependency);
172
- dependency._unregisterEffect(this);
275
+ pick() {
276
+ return this._getRaw();
173
277
  }
174
- }
175
-
176
- class FlowObservable extends FlowSignal {
177
278
  /* INTERNAL -------------------------------------------*/
178
- /*@internal*/
279
+ /** @internal */
179
280
  _value;
180
- /*@internal*/
181
- _getFrom(listener) {
182
- listener._registerDependency(this);
183
- return this.get();
184
- }
185
281
  /**
186
282
  * Subscribes a listener function to changes of the observable.
187
283
  * The listener is executed immediately with the current value and on subsequent updates.
@@ -189,8 +285,8 @@ class FlowObservable extends FlowSignal {
189
285
  * @returns A disposer function to cancel the subscription.
190
286
  */
191
287
  subscribe(listener) {
192
- const effect = new FlowEffect((get) => {
193
- listener(get(this));
288
+ const effect = new FlowEffect((t) => {
289
+ listener(this.get(t));
194
290
  });
195
291
  return () => effect.dispose();
196
292
  }
@@ -200,7 +296,10 @@ class FlowConstant extends FlowObservable {
200
296
  /**
201
297
  * Creates a new FlowConstant instance.
202
298
  *
203
- * @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
+ *
204
303
  * @public
205
304
  */
206
305
  constructor(value) {
@@ -208,26 +307,20 @@ class FlowConstant extends FlowObservable {
208
307
  this._initEager(value);
209
308
  }
210
309
  /**
211
- * Retrieves the constant value, computing it lazily if needed.
212
- *
213
- * Accessing this method will initialize the value if it has not been computed already.
214
- * Throws an error if the instance has been disposed or if lazy initialization fails.
215
- *
216
- * @returns The cached constant value.
217
- * @throws Error if the constant is disposed or cannot be initialized.
218
- * @public
310
+ * Internal method to get the raw value.
311
+ * @internal
219
312
  */
220
- get() {
313
+ _getRaw() {
221
314
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
222
315
  this._initLazy();
223
316
  return this._value;
224
317
  }
225
318
  /* INTERNAL --------------------------------------------------------- */
226
- /*@internal*/
319
+ /** @internal */
227
320
  _initialized = false;
228
- /*@internal*/
321
+ /** @internal */
229
322
  _init;
230
- /*@internal*/
323
+ /** @internal */
231
324
  _initEager(value) {
232
325
  if (typeof value === "function") {
233
326
  this._init = value;
@@ -236,7 +329,7 @@ class FlowConstant extends FlowObservable {
236
329
  this._initialized = true;
237
330
  }
238
331
  }
239
- /*@internal*/
332
+ /** @internal */
240
333
  _initLazy() {
241
334
  if (!this._initialized && this._init) {
242
335
  this._value = this._init();
@@ -247,81 +340,54 @@ class FlowConstant extends FlowObservable {
247
340
  }
248
341
  }
249
342
 
250
- class FlowState extends FlowConstant {
251
- /**
252
- * Updates the state with a new value.
253
- * @param value - A new value or a callback function that computes a new value based on the current state.
254
- * @remarks
255
- * If the computed new value is strictly equal to the current state value, no change is made and subscribers
256
- * will not be notified. Otherwise, the state is updated and all subscribers are informed of the change.
257
- * @throws Error if the state has been disposed.
258
- * @public
259
- */
260
- set(value) {
261
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
262
- const next = typeof value === "function" ? value(this._value) : value;
263
- if (next === this._value) return;
264
- this._value = next;
265
- this._notify();
266
- }
267
- }
268
-
269
343
  class FlowDerivation extends FlowObservable {
270
344
  /**
271
345
  * Creates a new FlowDerivation.
272
- * @param compute - A function that computes the derived value. It is provided with two parameters:
273
- * 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
+ *
274
351
  * @public
275
352
  */
276
353
  constructor(compute) {
277
354
  super();
278
- this._initEager(compute);
355
+ this._compute = compute;
356
+ this._trackedContext = new TrackingContext(this);
279
357
  }
280
358
  /**
281
- * Gets the current derived value.
282
- * @returns The current computed value.
283
- * @remarks
284
- * This method lazily initializes and updates the derivation if it is marked as dirty. It throws an error
285
- * if the derivation has been disposed.
286
- * @public
359
+ * Internal method to get the raw value.
360
+ * @internal
287
361
  */
288
- get() {
362
+ _getRaw() {
289
363
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
290
364
  this._initLazy();
291
- this._compute();
365
+ this._update();
292
366
  return this._value;
293
367
  }
294
368
  /* INTERNAL --------------------------------------------------------- */
295
369
  _initialized = false;
296
370
  _dirty = false;
297
- _trackedGet = (observable) => observable._getFrom(this);
298
- _trackedWatch = (signal) => signal._watchFrom(this);
299
- _untrackedGet = (observable) => observable.get();
300
- _untrackedWatch = (signal) => signal._watch();
301
- _trackedCompute;
302
- _untrackedCompute;
303
- _initEager(compute) {
304
- this._trackedCompute = () => compute(this._trackedGet, this._trackedWatch);
305
- this._untrackedCompute = () => compute(this._untrackedGet, this._untrackedWatch);
306
- }
371
+ _compute;
372
+ _trackedContext;
307
373
  _initLazy() {
308
374
  if (!this._initialized) {
309
- this._value = this._trackedCompute();
375
+ this._value = this._compute(this._trackedContext);
310
376
  this._initialized = true;
311
377
  }
312
378
  }
313
379
  /* @internal */
314
- _compute() {
380
+ _update() {
315
381
  if (this._dirty) {
316
382
  const dependencies = [...this._dependencies];
317
383
  this._dependencies.clear();
318
- this._value = this._trackedCompute();
384
+ this._value = this._compute(this._trackedContext);
319
385
  const dependenciesToRemove = dependencies.filter(
320
386
  (dependency) => !this._dependencies.has(dependency)
321
387
  );
322
- dependenciesToRemove.forEach(
323
- (dependency) => dependency._unregisterDependency(this)
324
- );
388
+ dependenciesToRemove.forEach((dependency) => {
389
+ dependency._unregisterDependency(this);
390
+ });
325
391
  this._dirty = false;
326
392
  }
327
393
  }
@@ -330,259 +396,99 @@ class FlowDerivation extends FlowObservable {
330
396
  this._dirty = true;
331
397
  super._notify();
332
398
  }
333
- /* @internal */
334
- _watch() {
335
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
336
- this._initLazy();
337
- this._compute();
338
- }
339
- }
340
-
341
- function isDisposable(obj) {
342
- return obj !== null && obj !== void 0 && typeof obj.dispose === "function";
343
- }
344
-
345
- class FlowMap extends FlowState {
346
- /**
347
- * A reactive state that holds the most recent key and value that were set.
348
- *
349
- * @remarks
350
- * When a key is set via {@link FlowMap.setAt}, this state is updated with
351
- * the corresponding key and value.
352
- *
353
- * @public
354
- */
355
- $lastSet = new FlowState({});
356
- /**
357
- * A reactive state that holds the most recent key and value that were deleted.
358
- *
359
- * @remarks
360
- * When a key is deleted via {@link FlowMap.delete}, this state is updated with
361
- * the corresponding key and its last known value.
362
- *
363
- * @public
364
- */
365
- $lastDeleted = new FlowState({});
366
399
  /**
367
- * Sets a value at the specified key in the underlying map.
400
+ * Watches the derivation, registering it as a dependency in the given context.
368
401
  *
369
- * @param key - The key at which to set the value.
370
- * @param value - The value to set.
371
- * @throws If the FlowMap instance is disposed.
402
+ * @param context - The tracking context in which to register this derivation.
372
403
  *
373
404
  * @remarks
374
- * Updates the internal map, emits the key-value pair via {@link FlowMap.$lastSet},
375
- * and notifies all subscribers of the change.
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.
376
410
  *
377
- * @public
378
- */
379
- setAt(key, value) {
380
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
381
- this._value.set(key, value);
382
- this.$lastSet.set({ key, value });
383
- this._notify();
384
- }
385
- /**
386
- * Deletes the value at the specified key from 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
387
415
  *
388
- * @param key - The key to delete.
389
- * @throws If the FlowMap instance is disposed.
416
+ * @example
417
+ * ```typescript
418
+ * const $derived = derivation((t) => $state.get(t) * 2);
390
419
  *
391
- * @remarks
392
- * Removes the key from the internal map, emits the deleted key and its value via {@link FlowMap.$lastDeleted},
393
- * 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
+ * ```
394
425
  *
395
426
  * @public
396
427
  */
397
- delete(key) {
398
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
399
- const value = this._value.get(key);
400
- this._value.delete(key);
401
- this.$lastDeleted.set({ key, value });
402
- this._notify();
428
+ watch(context) {
429
+ super.watch(context);
430
+ this._initLazy();
431
+ this._update();
403
432
  }
404
433
  }
405
434
 
406
- class FlowStream extends FlowObservable {
407
- /**
408
- * Creates a new FlowStream.
409
- * @param updater - A function that receives a setter to update the stream's value.
410
- * It should return a disposer function that will be called upon disposal.
411
- * @public
412
- */
413
- constructor(updater) {
414
- super();
415
- this._disposer = updater((value) => {
416
- this._set(value);
417
- });
418
- }
419
- /**
420
- * Retrieves the current value of the stream.
421
- * @returns The current value, or undefined if no value has been set yet.
422
- * @throws Error if the stream is disposed.
423
- * @public
424
- */
425
- get() {
426
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
427
- return this._value;
428
- }
435
+ function isDisposable(obj) {
436
+ return obj !== null && obj !== void 0 && typeof obj.dispose === "function";
437
+ }
438
+
439
+ class FlowState extends FlowConstant {
429
440
  /**
430
- * Disposes the stream, releasing all resources.
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.
431
443
  * @remarks
432
- * In addition to disposing the underlying observable, this method calls the disposer
433
- * returned by the updater.
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.
434
447
  * @public
435
448
  */
436
- dispose() {
437
- super.dispose();
438
- this._disposer();
439
- }
440
- /* INTERNAL ------------------------------------------------------ */
441
- _disposer;
442
- _set(value) {
449
+ set(value) {
443
450
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
444
- if (value === this._value) return;
445
- this._value = value;
451
+ const next = typeof value === "function" ? value(this._value) : value;
452
+ if (next === this._value) return;
453
+ this._value = next;
446
454
  this._notify();
447
455
  }
448
456
  }
449
457
 
450
- class FlowStreamAsync extends FlowObservable {
451
- /**
452
- * Creates a new asynchronous FlowStream.
453
- * @param updater - A function that receives a setter to update the stream's value.
454
- * It should return a disposer function that will be called upon disposal.
455
- * @remarks The updater function can invoke the setter asynchronously to update the stream.
456
- * @public
457
- */
458
- constructor(updater) {
459
- super();
460
- this._disposer = updater((value) => {
461
- this._set(value);
462
- });
463
- this._value = new Promise((resolve) => {
464
- this._resolve = resolve;
465
- });
466
- }
467
- /**
468
- * Retrieves the current value of the stream as a Promise.
469
- * @returns A Promise that resolves to the current value.
470
- * @throws Error if the stream is disposed.
471
- * @public
472
- */
473
- get() {
474
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
475
- return this._value;
476
- }
477
- /**
478
- * Disposes the stream, releasing all resources.
479
- * @remarks In addition to disposing the underlying observable, this method calls the disposer
480
- * returned by the updater.
481
- * @public
482
- */
483
- dispose() {
484
- super.dispose();
485
- this._disposer();
486
- }
487
- /* INTERNAL ------------------------------------------------------ */
488
- _initialized = false;
489
- _awaitedValue;
490
- _resolve;
491
- _disposer;
492
- _set(value) {
493
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
494
- if (!this._initialized) {
495
- this._resolve(value);
496
- this._initialized = true;
497
- this._awaitedValue = value;
498
- this._notify();
499
- return;
500
- }
501
- if (value === this._awaitedValue) return;
502
- this._value = Promise.resolve(value);
503
- this._awaitedValue = value;
504
- this._notify();
505
- }
458
+ function signal() {
459
+ return new FlowSignal();
506
460
  }
507
-
508
- class FlowResource extends FlowObservable {
509
- /**
510
- * Creates a new FlowResource.
511
- * @param fetch - An asynchronous function that retrieves the resource's value.
512
- *
513
- * @public
514
- */
515
- constructor(fetch) {
516
- super();
517
- this._fetch = fetch;
518
- }
519
- /**
520
- * Retrieves the current resource value.
521
- * @returns The current value, or undefined if the resource has not been fetched yet.
522
- * @throws Error if the resource is disposed.
523
- * @public
524
- */
525
- get() {
526
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
527
- return this._value;
528
- }
529
- /**
530
- * Asynchronously fetches a new value for the resource.
531
- * @remarks
532
- * Executes the internal fetch function. If the fetched value differs from the current one,
533
- * updates the resource's value and notifies subscribers.
534
- * @returns A Promise that resolves when the fetch operation is complete.
535
- * @throws Error if the resource is disposed.
536
- * @public
537
- */
538
- async fetch() {
539
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
540
- const value = await this._fetch();
541
- if (value === this._value) return;
542
- this._value = value;
543
- this._notify();
544
- }
545
- /* INTERNAL ------------------------------------------------ */
546
- _fetch;
461
+ function constant(value) {
462
+ return new FlowConstant(value);
547
463
  }
548
-
549
- class FlowResourceAsync extends FlowObservable {
550
- /**
551
- * Creates a new FlowResource.
552
- * @param fetch - An asynchronous function that retrieves the resource's value.
553
- * @public
554
- */
555
- constructor(fetch) {
556
- super();
557
- this._fetch = fetch;
558
- }
559
- /**
560
- * Retrieves the current resource value.
561
- * @returns The current value, or undefined if the resource has not been fetched yet.
562
- * @throws Error if the resource is disposed.
563
- * @public
564
- */
565
- get() {
566
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
567
- if (!this._value) this._value = this._fetch();
568
- return this._value;
569
- }
570
- /**
571
- * Asynchronously fetches a new value for the resource.
572
- * @remarks
573
- * Executes the internal fetch function. If the fetched value differs from the current one,
574
- * updates the resource's value and notifies subscribers.
575
- * @returns A Promise that resolves when the fetch operation is complete.
576
- * @throws Error if the resource is disposed.
577
- * @public
578
- */
579
- async fetch() {
580
- if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
581
- this._value = this._fetch();
582
- this._notify();
583
- }
584
- /* INTERNAL ------------------------------------------------ */
585
- _fetch;
464
+ function state(value) {
465
+ return new FlowState(value);
466
+ }
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);
586
492
  }
587
493
 
588
494
  class FlowArray extends FlowObservable {
@@ -614,11 +520,10 @@ class FlowArray extends FlowObservable {
614
520
  return this._value.length;
615
521
  }
616
522
  /**
617
- * Returns a copy of the internal array.
618
- * @returns A copy of the array.
619
- * @public
523
+ * Internal method to get the raw value.
524
+ * @internal
620
525
  */
621
- get() {
526
+ _getRaw() {
622
527
  if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
623
528
  return [...this._value];
624
529
  }
@@ -747,44 +652,313 @@ class FlowArray extends FlowObservable {
747
652
  this._value = [];
748
653
  }
749
654
  /* INTERNAL */
750
- /*@internal*/
655
+ /** @internal */
751
656
  _value = [];
752
657
  }
753
658
 
754
- function signal() {
755
- return new FlowSignal();
756
- }
757
- function constant(value) {
758
- return new FlowConstant(value);
759
- }
760
- function state(value) {
761
- return new FlowState(value);
762
- }
763
- function resource(fn) {
764
- return new FlowResource(fn);
765
- }
766
- function resourceAsync(fn) {
767
- return new FlowResourceAsync(fn);
768
- }
769
- function stream(updater) {
770
- return new FlowStream(updater);
771
- }
772
- function streamAsync(updater) {
773
- return new FlowStreamAsync(updater);
659
+ class FlowMap extends FlowState {
660
+ /**
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
+ *
667
+ * @public
668
+ */
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) {
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 });
711
+ this._notify();
712
+ }
713
+ /**
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
+ *
725
+ * @public
726
+ */
727
+ update(key, value) {
728
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
729
+ if (!this._value.has(key)) {
730
+ throw new Error("[PicoFlow] Key does not exist");
731
+ }
732
+ this._value.set(key, value);
733
+ this.$lastUpdated.set({ key, value });
734
+ this._notify();
735
+ }
736
+ /**
737
+ * Deletes the value at the specified key from the underlying map.
738
+ *
739
+ * @param key - The key to delete.
740
+ * @throws If the FlowMap instance is disposed.
741
+ *
742
+ * @remarks
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.
745
+ *
746
+ * @public
747
+ */
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();
755
+ }
756
+ /**
757
+ * Disposes the FlowMap and its values.
758
+ * @param options - Disposal options.
759
+ * @public
760
+ */
761
+ dispose(options) {
762
+ super.dispose(options);
763
+ this._value.forEach((item) => {
764
+ if (isDisposable(item)) item.dispose(options);
765
+ });
766
+ this._value.clear();
767
+ this.$lastAdded.dispose(options);
768
+ this.$lastUpdated.dispose(options);
769
+ this.$lastDeleted.dispose(options);
770
+ }
774
771
  }
775
- function derivation(fn) {
776
- return new FlowDerivation(fn);
772
+
773
+ class FlowResource extends FlowObservable {
774
+ /**
775
+ * Creates a new FlowResource.
776
+ *
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.
780
+ *
781
+ * @public
782
+ */
783
+ constructor(fetch) {
784
+ super();
785
+ this._fetch = fetch;
786
+ }
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;
794
+ }
795
+ /**
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
803
+ */
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();
810
+ }
811
+ /* INTERNAL ------------------------------------------------ */
812
+ _fetch;
777
813
  }
778
- function effect(fn) {
779
- return new FlowEffect(fn);
814
+
815
+ class FlowResourceAsync extends FlowObservable {
816
+ /**
817
+ * Creates a new FlowResource.
818
+ * @param fetch - An asynchronous function that retrieves the resource's value.
819
+ * @public
820
+ */
821
+ constructor(fetch) {
822
+ super();
823
+ this._fetch = fetch;
824
+ }
825
+ /**
826
+ * Internal method to get the raw value.
827
+ * @internal
828
+ */
829
+ _getRaw() {
830
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
831
+ if (!this._value) this._value = this._fetch();
832
+ return this._value;
833
+ }
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() {
844
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
845
+ this._value = this._fetch();
846
+ this._notify();
847
+ }
848
+ /* INTERNAL ------------------------------------------------ */
849
+ _fetch;
780
850
  }
781
- function map(initial) {
782
- return new FlowMap(
783
- new Map(initial ? Object.entries(initial) : [])
784
- );
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);
870
+ });
871
+ }
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;
879
+ }
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();
890
+ }
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();
898
+ }
785
899
  }
786
- function array(initial) {
787
- return new FlowArray(initial);
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
+ });
924
+ }
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;
932
+ }
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();
942
+ }
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;
956
+ }
957
+ if (value === this._awaitedValue) return;
958
+ this._value = Promise.resolve(value);
959
+ this._awaitedValue = value;
960
+ this._notify();
961
+ }
788
962
  }
789
963
 
790
964
  class SolidState {
@@ -851,11 +1025,11 @@ class SolidResource {
851
1025
  }
852
1026
 
853
1027
  function fromSync(state) {
854
- const solidState = new SolidState(state.get());
1028
+ const solidState = new SolidState(state.pick());
855
1029
  let fx;
856
1030
  onMount(() => {
857
- fx = new FlowEffect((get) => {
858
- const value = get(state);
1031
+ fx = new FlowEffect((t) => {
1032
+ const value = state.get(t);
859
1033
  solidState.set(() => value);
860
1034
  });
861
1035
  });
@@ -864,13 +1038,13 @@ function fromSync(state) {
864
1038
  }
865
1039
  function fromAsync(derivation) {
866
1040
  const solidResource = new SolidResource(async () => {
867
- const value = await derivation.get();
1041
+ const value = await derivation.pick();
868
1042
  return value;
869
1043
  });
870
1044
  let fx;
871
1045
  onMount(() => {
872
- fx = new FlowEffect(async (get) => {
873
- await get(derivation);
1046
+ fx = new FlowEffect(async (t) => {
1047
+ await derivation.get(t);
874
1048
  solidResource.refetch();
875
1049
  });
876
1050
  });
@@ -878,7 +1052,7 @@ function fromAsync(derivation) {
878
1052
  return solidResource;
879
1053
  }
880
1054
  function shallowFrom(flow) {
881
- const initialValue = flow.get();
1055
+ const initialValue = flow.pick();
882
1056
  const isAsync = initialValue instanceof Promise;
883
1057
  if (isAsync) {
884
1058
  return fromAsync(flow);
@@ -886,10 +1060,10 @@ function shallowFrom(flow) {
886
1060
  return fromSync(flow);
887
1061
  }
888
1062
  function deepFrom(getter) {
889
- const derivation = new FlowDerivation((get) => {
890
- return getter(get);
1063
+ const derivation = new FlowDerivation((t) => {
1064
+ return getter(t);
891
1065
  });
892
- const initialValue = derivation.get();
1066
+ const initialValue = derivation.pick();
893
1067
  const isAsync = initialValue instanceof Promise;
894
1068
  if (isAsync) {
895
1069
  return fromAsync(derivation);
@@ -903,4 +1077,4 @@ function from(flow) {
903
1077
  return deepFrom(flow);
904
1078
  }
905
1079
 
906
- 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 };