@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.
- package/.cursor/plans/update-js-e795d61b.plan.md +567 -0
- package/.gitlab-ci.yml +24 -0
- package/.vscode/settings.json +3 -3
- package/CHANGELOG.md +51 -0
- package/IMPLEMENTATION_GUIDE.md +1578 -0
- package/README.md +62 -25
- package/biome.json +32 -32
- package/dist/picoflow.js +557 -1099
- package/dist/types/advanced/array.d.ts +0 -6
- package/dist/types/advanced/array.d.ts.map +1 -1
- package/dist/types/advanced/index.d.ts +5 -5
- package/dist/types/advanced/index.d.ts.map +1 -1
- package/dist/types/advanced/map.d.ts +114 -23
- package/dist/types/advanced/map.d.ts.map +1 -1
- package/dist/types/advanced/resource.d.ts +51 -12
- package/dist/types/advanced/resource.d.ts.map +1 -1
- package/dist/types/advanced/resourceAsync.d.ts +28 -13
- package/dist/types/advanced/resourceAsync.d.ts.map +1 -1
- package/dist/types/advanced/stream.d.ts +74 -16
- package/dist/types/advanced/stream.d.ts.map +1 -1
- package/dist/types/advanced/streamAsync.d.ts +69 -15
- package/dist/types/advanced/streamAsync.d.ts.map +1 -1
- package/dist/types/basic/constant.d.ts +44 -16
- package/dist/types/basic/constant.d.ts.map +1 -1
- package/dist/types/basic/derivation.d.ts +73 -24
- package/dist/types/basic/derivation.d.ts.map +1 -1
- package/dist/types/basic/disposable.d.ts +65 -6
- package/dist/types/basic/disposable.d.ts.map +1 -1
- package/dist/types/basic/effect.d.ts +27 -16
- package/dist/types/basic/effect.d.ts.map +1 -1
- package/dist/types/basic/index.d.ts +7 -8
- package/dist/types/basic/index.d.ts.map +1 -1
- package/dist/types/basic/observable.d.ts +62 -13
- package/dist/types/basic/observable.d.ts.map +1 -1
- package/dist/types/basic/signal.d.ts +35 -6
- package/dist/types/basic/signal.d.ts.map +1 -1
- package/dist/types/basic/state.d.ts +25 -4
- package/dist/types/basic/state.d.ts.map +1 -1
- package/dist/types/basic/trackingContext.d.ts +33 -0
- package/dist/types/basic/trackingContext.d.ts.map +1 -0
- package/dist/types/creators.d.ts +271 -26
- package/dist/types/creators.d.ts.map +1 -1
- package/dist/types/index.d.ts +60 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/solid/converters.d.ts +5 -5
- package/dist/types/solid/converters.d.ts.map +1 -1
- package/dist/types/solid/index.d.ts +2 -2
- package/dist/types/solid/index.d.ts.map +1 -1
- package/dist/types/solid/primitives.d.ts +96 -4
- package/dist/types/solid/primitives.d.ts.map +1 -1
- package/docs/.vitepress/config.mts +110 -0
- package/docs/api/classes/FlowArray.md +489 -0
- package/docs/api/classes/FlowConstant.md +350 -0
- package/docs/api/classes/FlowDerivation.md +334 -0
- package/docs/api/classes/FlowEffect.md +100 -0
- package/docs/api/classes/FlowMap.md +512 -0
- package/docs/api/classes/FlowObservable.md +306 -0
- package/docs/api/classes/FlowResource.md +380 -0
- package/docs/api/classes/FlowResourceAsync.md +362 -0
- package/docs/api/classes/FlowSignal.md +160 -0
- package/docs/api/classes/FlowState.md +368 -0
- package/docs/api/classes/FlowStream.md +367 -0
- package/docs/api/classes/FlowStreamAsync.md +364 -0
- package/docs/api/classes/SolidDerivation.md +75 -0
- package/docs/api/classes/SolidResource.md +91 -0
- package/docs/api/classes/SolidState.md +71 -0
- package/docs/api/classes/TrackingContext.md +33 -0
- package/docs/api/functions/array.md +58 -0
- package/docs/api/functions/constant.md +45 -0
- package/docs/api/functions/derivation.md +53 -0
- package/docs/api/functions/effect.md +49 -0
- package/docs/api/functions/from.md +220 -0
- package/docs/api/functions/isDisposable.md +49 -0
- package/docs/api/functions/map.md +57 -0
- package/docs/api/functions/resource.md +52 -0
- package/docs/api/functions/resourceAsync.md +50 -0
- package/docs/api/functions/signal.md +36 -0
- package/docs/api/functions/state.md +47 -0
- package/docs/api/functions/stream.md +53 -0
- package/docs/api/functions/streamAsync.md +50 -0
- package/docs/api/index.md +118 -0
- package/docs/api/interfaces/FlowDisposable.md +65 -0
- package/docs/api/interfaces/SolidObservable.md +19 -0
- package/docs/api/type-aliases/FlowArrayAction.md +49 -0
- package/docs/api/type-aliases/FlowStreamDisposer.md +15 -0
- package/docs/api/type-aliases/FlowStreamSetter.md +27 -0
- package/docs/api/type-aliases/FlowStreamUpdater.md +32 -0
- package/docs/api/type-aliases/NotPromise.md +18 -0
- package/docs/api/type-aliases/SolidGetter.md +17 -0
- package/docs/api/typedoc-sidebar.json +1 -0
- package/docs/examples/examples.md +2313 -0
- package/docs/examples/patterns.md +649 -0
- package/docs/guide/advanced/disposal.md +426 -0
- package/docs/guide/advanced/solidjs.md +221 -0
- package/docs/guide/advanced/upgrading.md +464 -0
- package/docs/guide/introduction/concepts.md +56 -0
- package/docs/guide/introduction/conventions.md +61 -0
- package/docs/guide/introduction/getting-started.md +134 -0
- package/docs/guide/introduction/lifecycle.md +371 -0
- package/docs/guide/primitives/array.md +400 -0
- package/docs/guide/primitives/constant.md +380 -0
- package/docs/guide/primitives/derivations.md +348 -0
- package/docs/guide/primitives/effects.md +458 -0
- package/docs/guide/primitives/map.md +387 -0
- package/docs/guide/primitives/overview.md +175 -0
- package/docs/guide/primitives/resources.md +858 -0
- package/docs/guide/primitives/signal.md +259 -0
- package/docs/guide/primitives/state.md +368 -0
- package/docs/guide/primitives/streams.md +931 -0
- package/docs/index.md +47 -0
- package/docs/public/logo.svg +1 -0
- package/package.json +57 -41
- package/src/advanced/array.ts +208 -210
- package/src/advanced/index.ts +7 -7
- package/src/advanced/map.ts +178 -68
- package/src/advanced/resource.ts +87 -43
- package/src/advanced/resourceAsync.ts +62 -42
- package/src/advanced/stream.ts +113 -50
- package/src/advanced/streamAsync.ts +120 -61
- package/src/basic/constant.ts +82 -49
- package/src/basic/derivation.ts +128 -84
- package/src/basic/disposable.ts +74 -15
- package/src/basic/effect.ts +85 -77
- package/src/basic/index.ts +7 -8
- package/src/basic/observable.ts +94 -36
- package/src/basic/signal.ts +133 -105
- package/src/basic/state.ts +46 -25
- package/src/basic/trackingContext.ts +45 -0
- package/src/creators.ts +297 -54
- package/src/index.ts +96 -43
- package/src/solid/converters.ts +186 -67
- package/src/solid/index.ts +8 -2
- package/src/solid/primitives.ts +167 -65
- package/test/array.test.ts +592 -612
- package/test/constant.test.ts +31 -33
- package/test/derivation.test.ts +531 -536
- package/test/effect.test.ts +21 -21
- package/test/map.test.ts +233 -137
- package/test/resource.test.ts +119 -121
- package/test/resourceAsync.test.ts +98 -100
- package/test/signal.test.ts +51 -55
- package/test/state.test.ts +186 -168
- package/test/stream.test.ts +189 -189
- package/test/streamAsync.test.ts +186 -186
- package/tsconfig.json +19 -18
- package/typedoc.json +37 -0
- package/vite.config.ts +23 -20
- package/vitest.config.ts +7 -7
- package/api/doc/index.md +0 -31
- package/api/doc/picoflow.array.md +0 -55
- package/api/doc/picoflow.constant.md +0 -55
- package/api/doc/picoflow.derivation.md +0 -55
- package/api/doc/picoflow.effect.md +0 -55
- package/api/doc/picoflow.flowarray._constructor_.md +0 -49
- package/api/doc/picoflow.flowarray._lastaction.md +0 -13
- package/api/doc/picoflow.flowarray.clear.md +0 -17
- package/api/doc/picoflow.flowarray.dispose.md +0 -55
- package/api/doc/picoflow.flowarray.get.md +0 -19
- package/api/doc/picoflow.flowarray.length.md +0 -13
- package/api/doc/picoflow.flowarray.md +0 -273
- package/api/doc/picoflow.flowarray.pop.md +0 -17
- package/api/doc/picoflow.flowarray.push.md +0 -53
- package/api/doc/picoflow.flowarray.set.md +0 -53
- package/api/doc/picoflow.flowarray.setitem.md +0 -69
- package/api/doc/picoflow.flowarray.shift.md +0 -17
- package/api/doc/picoflow.flowarray.splice.md +0 -85
- package/api/doc/picoflow.flowarray.unshift.md +0 -53
- package/api/doc/picoflow.flowarrayaction.md +0 -37
- package/api/doc/picoflow.flowconstant._constructor_.md +0 -49
- package/api/doc/picoflow.flowconstant.get.md +0 -25
- package/api/doc/picoflow.flowconstant.md +0 -88
- package/api/doc/picoflow.flowderivation._constructor_.md +0 -49
- package/api/doc/picoflow.flowderivation.get.md +0 -23
- package/api/doc/picoflow.flowderivation.md +0 -86
- package/api/doc/picoflow.flowdisposable.dispose.md +0 -55
- package/api/doc/picoflow.flowdisposable.md +0 -43
- package/api/doc/picoflow.floweffect._constructor_.md +0 -54
- package/api/doc/picoflow.floweffect.dispose.md +0 -21
- package/api/doc/picoflow.floweffect.disposed.md +0 -13
- package/api/doc/picoflow.floweffect.md +0 -131
- package/api/doc/picoflow.flowgetter.md +0 -15
- package/api/doc/picoflow.flowmap._lastdeleted.md +0 -21
- package/api/doc/picoflow.flowmap._lastset.md +0 -21
- package/api/doc/picoflow.flowmap.delete.md +0 -61
- package/api/doc/picoflow.flowmap.md +0 -133
- package/api/doc/picoflow.flowmap.setat.md +0 -77
- package/api/doc/picoflow.flowobservable.get.md +0 -19
- package/api/doc/picoflow.flowobservable.md +0 -68
- package/api/doc/picoflow.flowobservable.subscribe.md +0 -55
- package/api/doc/picoflow.flowresource._constructor_.md +0 -49
- package/api/doc/picoflow.flowresource.fetch.md +0 -27
- package/api/doc/picoflow.flowresource.get.md +0 -23
- package/api/doc/picoflow.flowresource.md +0 -100
- package/api/doc/picoflow.flowresourceasync._constructor_.md +0 -49
- package/api/doc/picoflow.flowresourceasync.fetch.md +0 -27
- package/api/doc/picoflow.flowresourceasync.get.md +0 -23
- package/api/doc/picoflow.flowresourceasync.md +0 -100
- package/api/doc/picoflow.flowsignal.dispose.md +0 -59
- package/api/doc/picoflow.flowsignal.disposed.md +0 -18
- package/api/doc/picoflow.flowsignal.md +0 -112
- package/api/doc/picoflow.flowsignal.trigger.md +0 -21
- package/api/doc/picoflow.flowstate.md +0 -52
- package/api/doc/picoflow.flowstate.set.md +0 -61
- package/api/doc/picoflow.flowstream._constructor_.md +0 -49
- package/api/doc/picoflow.flowstream.dispose.md +0 -21
- package/api/doc/picoflow.flowstream.get.md +0 -23
- package/api/doc/picoflow.flowstream.md +0 -100
- package/api/doc/picoflow.flowstreamasync._constructor_.md +0 -54
- package/api/doc/picoflow.flowstreamasync.dispose.md +0 -21
- package/api/doc/picoflow.flowstreamasync.get.md +0 -23
- package/api/doc/picoflow.flowstreamasync.md +0 -100
- package/api/doc/picoflow.flowstreamdisposer.md +0 -13
- package/api/doc/picoflow.flowstreamsetter.md +0 -13
- package/api/doc/picoflow.flowstreamupdater.md +0 -19
- package/api/doc/picoflow.flowwatcher.md +0 -15
- package/api/doc/picoflow.from.md +0 -55
- package/api/doc/picoflow.from_1.md +0 -55
- package/api/doc/picoflow.from_2.md +0 -55
- package/api/doc/picoflow.from_3.md +0 -55
- package/api/doc/picoflow.from_4.md +0 -55
- package/api/doc/picoflow.from_5.md +0 -55
- package/api/doc/picoflow.isdisposable.md +0 -55
- package/api/doc/picoflow.map.md +0 -59
- package/api/doc/picoflow.md +0 -544
- package/api/doc/picoflow.resource.md +0 -55
- package/api/doc/picoflow.resourceasync.md +0 -55
- package/api/doc/picoflow.signal.md +0 -19
- package/api/doc/picoflow.solidderivation._constructor_.md +0 -49
- package/api/doc/picoflow.solidderivation.get.md +0 -13
- package/api/doc/picoflow.solidderivation.md +0 -94
- package/api/doc/picoflow.solidgetter.md +0 -13
- package/api/doc/picoflow.solidobservable.get.md +0 -13
- package/api/doc/picoflow.solidobservable.md +0 -57
- package/api/doc/picoflow.solidresource._constructor_.md +0 -49
- package/api/doc/picoflow.solidresource.get.md +0 -13
- package/api/doc/picoflow.solidresource.latest.md +0 -13
- package/api/doc/picoflow.solidresource.md +0 -157
- package/api/doc/picoflow.solidresource.refetch.md +0 -13
- package/api/doc/picoflow.solidresource.state.md +0 -13
- package/api/doc/picoflow.solidstate._constructor_.md +0 -49
- package/api/doc/picoflow.solidstate.get.md +0 -13
- package/api/doc/picoflow.solidstate.md +0 -115
- package/api/doc/picoflow.solidstate.set.md +0 -13
- package/api/doc/picoflow.state.md +0 -55
- package/api/doc/picoflow.stream.md +0 -55
- package/api/doc/picoflow.streamasync.md +0 -55
- package/api/picoflow.public.api.md +0 -244
- package/api-extractor.json +0 -61
package/src/advanced/stream.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { FlowObservable } from "../basic/";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A function type that sets a new value for the reactive stream.
|
|
5
|
-
* @
|
|
5
|
+
* @typeParam T - The type of the value.
|
|
6
6
|
* @public
|
|
7
7
|
*/
|
|
8
8
|
export type FlowStreamSetter<T> = (value: T) => void;
|
|
@@ -18,70 +18,133 @@ export type FlowStreamDisposer = () => void;
|
|
|
18
18
|
* @remarks
|
|
19
19
|
* The updater receives a setter function to update the stream's value.
|
|
20
20
|
* It should return a disposer function to release any resources or subscriptions.
|
|
21
|
-
* @
|
|
21
|
+
* @typeParam T - The type of the stream value.
|
|
22
22
|
* @public
|
|
23
23
|
*/
|
|
24
24
|
export type FlowStreamUpdater<T> = (
|
|
25
|
-
|
|
25
|
+
set: FlowStreamSetter<T>,
|
|
26
26
|
) => FlowStreamDisposer;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Represents a reactive stream that updates its value based on an updater function.
|
|
29
|
+
* Represents a reactive stream that updates its value based on an external updater function.
|
|
30
30
|
*
|
|
31
31
|
* @remarks
|
|
32
|
-
* FlowStream extends FlowObservable to
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* FlowStream extends FlowObservable to bridge external event sources with PicoFlow's reactive
|
|
33
|
+
* system. It's designed for integrating with event emitters, WebSocket connections, timers,
|
|
34
|
+
* or any push-based data source that sends updates over time.
|
|
35
35
|
*
|
|
36
|
-
*
|
|
36
|
+
* **How It Works:**
|
|
37
|
+
* 1. You provide an updater function that receives a setter callback
|
|
38
|
+
* 2. The updater sets up subscriptions to external events and calls the setter with new values
|
|
39
|
+
* 3. When the setter is called, the stream notifies all dependent effects and derivations
|
|
40
|
+
* 4. The updater returns a disposer function for cleanup
|
|
41
|
+
*
|
|
42
|
+
* **Initial Value:**
|
|
43
|
+
* The stream's value is `undefined` until the first time the setter is called. This allows
|
|
44
|
+
* you to check if any data has been received yet.
|
|
45
|
+
*
|
|
46
|
+
* **Change Detection:**
|
|
47
|
+
* The stream only notifies subscribers when the new value differs from the current value
|
|
48
|
+
* (using strict equality `===`). This prevents unnecessary updates for duplicate values.
|
|
49
|
+
*
|
|
50
|
+
* **Resource Management:**
|
|
51
|
+
* The disposer function returned by your updater is automatically called when the stream
|
|
52
|
+
* is disposed. Use it to clean up subscriptions, close connections, or clear timers.
|
|
53
|
+
*
|
|
54
|
+
* **Use Cases:**
|
|
55
|
+
* - WebSocket message streams
|
|
56
|
+
* - DOM event listeners
|
|
57
|
+
* - setInterval/setTimeout timers
|
|
58
|
+
* - Server-sent events (SSE)
|
|
59
|
+
* - Observable subscriptions from other libraries
|
|
60
|
+
* - Any push-based data source
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // WebSocket stream
|
|
65
|
+
* const $messages = stream<string>((set) => {
|
|
66
|
+
* const ws = new WebSocket('ws://example.com');
|
|
67
|
+
* ws.onmessage = (event) => set(event.data);
|
|
68
|
+
* return () => ws.close();
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Timer stream
|
|
72
|
+
* const $tick = stream<number>((set) => {
|
|
73
|
+
* let count = 0;
|
|
74
|
+
* const id = setInterval(() => set(count++), 1000);
|
|
75
|
+
* return () => clearInterval(id);
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* // DOM event stream
|
|
79
|
+
* const $clicks = stream<MouseEvent>((set) => {
|
|
80
|
+
* const handler = (e: MouseEvent) => set(e);
|
|
81
|
+
* document.addEventListener('click', handler);
|
|
82
|
+
* return () => document.removeEventListener('click', handler);
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* // Use in an effect
|
|
86
|
+
* effect((t) => {
|
|
87
|
+
* const message = $messages.get(t);
|
|
88
|
+
* if (message) {
|
|
89
|
+
* console.log('Received:', message);
|
|
90
|
+
* }
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* @typeParam T - The type of the values emitted by the stream.
|
|
37
95
|
* @public
|
|
38
96
|
*/
|
|
39
97
|
export class FlowStream<T> extends FlowObservable<T | undefined> {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Creates a new FlowStream.
|
|
100
|
+
*
|
|
101
|
+
* @param updater - A function that receives a setter callback and returns a disposer.
|
|
102
|
+
* The setter should be called whenever new data is available. The disposer will be
|
|
103
|
+
* invoked when the stream is disposed to clean up resources.
|
|
104
|
+
*
|
|
105
|
+
* @remarks
|
|
106
|
+
* The updater is invoked immediately during construction. Make sure to return a proper
|
|
107
|
+
* cleanup function to avoid resource leaks.
|
|
108
|
+
*
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
constructor(updater: FlowStreamUpdater<T>) {
|
|
112
|
+
super();
|
|
113
|
+
this._disposer = updater((value: T) => {
|
|
114
|
+
this._set(value);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
52
117
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return this._value;
|
|
62
|
-
}
|
|
118
|
+
/**
|
|
119
|
+
* Internal method to get the raw value.
|
|
120
|
+
* @internal
|
|
121
|
+
*/
|
|
122
|
+
protected _getRaw(): T | undefined {
|
|
123
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
124
|
+
return this._value;
|
|
125
|
+
}
|
|
63
126
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Disposes the stream, releasing all resources.
|
|
129
|
+
* @remarks
|
|
130
|
+
* In addition to disposing the underlying observable, this method calls the disposer
|
|
131
|
+
* returned by the updater.
|
|
132
|
+
* @public
|
|
133
|
+
*/
|
|
134
|
+
public override dispose(): void {
|
|
135
|
+
super.dispose();
|
|
136
|
+
this._disposer();
|
|
137
|
+
}
|
|
75
138
|
|
|
76
|
-
|
|
139
|
+
/* INTERNAL ------------------------------------------------------ */
|
|
77
140
|
|
|
78
|
-
|
|
141
|
+
private _disposer: FlowStreamDisposer;
|
|
79
142
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
143
|
+
private _set(value: T): void {
|
|
144
|
+
/* v8 ignore next */
|
|
145
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
146
|
+
if (value === this._value) return;
|
|
147
|
+
this._value = value;
|
|
148
|
+
this._notify();
|
|
149
|
+
}
|
|
87
150
|
}
|
|
@@ -2,81 +2,140 @@ import { FlowObservable } from "../basic";
|
|
|
2
2
|
import type { FlowStreamDisposer, FlowStreamUpdater } from "./stream";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Represents an asynchronous reactive stream that
|
|
5
|
+
* Represents an asynchronous reactive stream that always returns a Promise and updates based on an updater function.
|
|
6
6
|
*
|
|
7
7
|
* @remarks
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* FlowStreamAsync extends FlowObservable to bridge external async event sources with PicoFlow's
|
|
9
|
+
* reactive system. Unlike {@link FlowStream} which returns `T | undefined`, FlowStreamAsync always
|
|
10
|
+
* returns a `Promise<T>`, making it suitable for use with async/await patterns.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* **How It Works:**
|
|
13
|
+
* 1. On construction, creates an initial Promise that resolves when the first value arrives
|
|
14
|
+
* 2. Your updater function receives a setter callback and sets up event subscriptions
|
|
15
|
+
* 3. When the setter is called with a value, the Promise resolves (first call) or a new Promise is created (subsequent calls)
|
|
16
|
+
* 4. The updater returns a disposer function for cleanup
|
|
17
|
+
*
|
|
18
|
+
* **Promise Behavior:**
|
|
19
|
+
* - **First call to setter**: Resolves the initial Promise created at construction
|
|
20
|
+
* - **Subsequent calls**: Creates a new `Promise.resolve(value)` for immediate resolution
|
|
21
|
+
* - **Reading the value**: Always returns a Promise, either pending (initial) or resolved
|
|
22
|
+
*
|
|
23
|
+
* **Change Detection:**
|
|
24
|
+
* After the first value is set, the stream only notifies subscribers when the new value
|
|
25
|
+
* differs from the previous value (using strict equality `===`). This prevents unnecessary
|
|
26
|
+
* updates for duplicate values.
|
|
27
|
+
*
|
|
28
|
+
* **Resource Management:**
|
|
29
|
+
* The disposer function returned by your updater is automatically called when the stream
|
|
30
|
+
* is disposed. Use it to clean up subscriptions, close connections, or clear timers.
|
|
31
|
+
*
|
|
32
|
+
* **Use Cases:**
|
|
33
|
+
* - Async WebSocket message streams
|
|
34
|
+
* - Server-sent events that you want to await
|
|
35
|
+
* - Async event handlers
|
|
36
|
+
* - Integration with async iterators
|
|
37
|
+
* - Any push-based async data source
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Async WebSocket stream
|
|
42
|
+
* const $messages = streamAsync<string>((set) => {
|
|
43
|
+
* const ws = new WebSocket('ws://example.com');
|
|
44
|
+
* ws.onmessage = (event) => set(event.data);
|
|
45
|
+
* return () => ws.close();
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* // Use with async/await
|
|
49
|
+
* effect(async (t) => {
|
|
50
|
+
* const message = await $messages.get(t);
|
|
51
|
+
* console.log('Received:', message);
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* // Wait for the first message
|
|
55
|
+
* const firstMessage = await $messages.pick();
|
|
56
|
+
* console.log('First message:', firstMessage);
|
|
57
|
+
*
|
|
58
|
+
* // Async timer stream
|
|
59
|
+
* const $asyncTick = streamAsync<number>((set) => {
|
|
60
|
+
* let count = 0;
|
|
61
|
+
* const id = setInterval(() => set(count++), 1000);
|
|
62
|
+
* return () => clearInterval(id);
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @typeParam T - The type of the values emitted by the stream (not the Promise itself).
|
|
13
67
|
* @public
|
|
14
68
|
*/
|
|
15
69
|
export class FlowStreamAsync<T> extends FlowObservable<Promise<T>> {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Creates a new asynchronous FlowStream.
|
|
72
|
+
*
|
|
73
|
+
* @param updater - A function that receives a setter callback and returns a disposer.
|
|
74
|
+
* The setter should be called whenever new data is available (can be called asynchronously).
|
|
75
|
+
* The disposer will be invoked when the stream is disposed to clean up resources.
|
|
76
|
+
*
|
|
77
|
+
* @remarks
|
|
78
|
+
* The updater is invoked immediately during construction. An initial Promise is created
|
|
79
|
+
* that will resolve when the setter is first called. Make sure to return a proper cleanup
|
|
80
|
+
* function to avoid resource leaks.
|
|
81
|
+
*
|
|
82
|
+
* @public
|
|
83
|
+
*/
|
|
84
|
+
constructor(updater: FlowStreamUpdater<T>) {
|
|
85
|
+
super();
|
|
86
|
+
this._disposer = updater((value: T) => {
|
|
87
|
+
this._set(value);
|
|
88
|
+
});
|
|
28
89
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
90
|
+
this._value = new Promise((resolve) => {
|
|
91
|
+
this._resolve = resolve;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
33
94
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return this._value;
|
|
43
|
-
}
|
|
95
|
+
/**
|
|
96
|
+
* Internal method to get the raw value.
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
protected _getRaw(): Promise<T> {
|
|
100
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
101
|
+
return this._value;
|
|
102
|
+
}
|
|
44
103
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Disposes the stream, releasing all resources.
|
|
106
|
+
* @remarks In addition to disposing the underlying observable, this method calls the disposer
|
|
107
|
+
* returned by the updater.
|
|
108
|
+
* @public
|
|
109
|
+
*/
|
|
110
|
+
public override dispose(): void {
|
|
111
|
+
super.dispose();
|
|
112
|
+
this._disposer();
|
|
113
|
+
}
|
|
55
114
|
|
|
56
|
-
|
|
115
|
+
/* INTERNAL ------------------------------------------------------ */
|
|
57
116
|
|
|
58
|
-
|
|
59
|
-
|
|
117
|
+
private _initialized = false;
|
|
118
|
+
private _awaitedValue?: T;
|
|
60
119
|
|
|
61
|
-
|
|
62
|
-
|
|
120
|
+
private _resolve!: (value: T) => void;
|
|
121
|
+
private _disposer: FlowStreamDisposer;
|
|
63
122
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
123
|
+
private _set(value: T): void {
|
|
124
|
+
/* v8 ignore next */
|
|
125
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
67
126
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
127
|
+
if (!this._initialized) {
|
|
128
|
+
this._resolve(value);
|
|
129
|
+
this._initialized = true;
|
|
130
|
+
this._awaitedValue = value;
|
|
131
|
+
this._notify();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
75
134
|
|
|
76
|
-
|
|
135
|
+
if (value === this._awaitedValue) return;
|
|
77
136
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
137
|
+
this._value = Promise.resolve(value);
|
|
138
|
+
this._awaitedValue = value;
|
|
139
|
+
this._notify();
|
|
140
|
+
}
|
|
82
141
|
}
|
package/src/basic/constant.ts
CHANGED
|
@@ -1,64 +1,97 @@
|
|
|
1
1
|
import { FlowObservable } from "./observable";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Represents a reactive and immutable constant value computed lazily upon first access.
|
|
4
|
+
* Represents a reactive and immutable constant value that can be computed lazily upon first access.
|
|
5
5
|
*
|
|
6
|
-
* @remarks
|
|
7
|
-
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* FlowConstant extends FlowObservable to provide an immutable reactive value. Unlike {@link FlowState},
|
|
8
|
+
* which is mutable via the `set()` method, a constant's value never changes after initialization.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* **Initialization Patterns:**
|
|
11
|
+
* - **Direct value**: Pass a value directly; it's stored immediately.
|
|
12
|
+
* - **Lazy initialization**: Pass a function; it's called only when the value is first accessed
|
|
13
|
+
* via `get()` or `pick()`.
|
|
14
|
+
*
|
|
15
|
+
* **Lazy Evaluation Benefits:**
|
|
16
|
+
* Lazy initialization is useful when:
|
|
17
|
+
* - The computation is expensive and may not be needed immediately
|
|
18
|
+
* - The value depends on resources that aren't available at construction time
|
|
19
|
+
* - You want to defer computation until the value is actually needed
|
|
20
|
+
*
|
|
21
|
+
* **Caching:**
|
|
22
|
+
* Once computed (either immediately or lazily), the value is cached permanently. All subsequent
|
|
23
|
+
* accesses return the cached value without re-computation.
|
|
24
|
+
*
|
|
25
|
+
* **Use Cases:**
|
|
26
|
+
* - Configuration values that don't change
|
|
27
|
+
* - Expensive computations that should only run once
|
|
28
|
+
* - Initialization values for other reactive primitives
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Direct value - initialized immediately
|
|
33
|
+
* const $config = constant({ apiUrl: 'https://api.example.com' });
|
|
34
|
+
*
|
|
35
|
+
* // Lazy initialization - computed on first access
|
|
36
|
+
* const $expensiveValue = constant(() => {
|
|
37
|
+
* console.log('Computing...');
|
|
38
|
+
* return performExpensiveCalculation();
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* $expensiveValue.pick(); // Logs: "Computing..."
|
|
42
|
+
* $expensiveValue.pick(); // No log - returns cached value
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @typeParam T - The type of the constant value.
|
|
10
46
|
*
|
|
11
47
|
* @public
|
|
12
48
|
*/
|
|
13
49
|
export class FlowConstant<T> extends FlowObservable<T> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new FlowConstant instance.
|
|
52
|
+
*
|
|
53
|
+
* @param value - Either a direct value of type T or a function returning a value of type T.
|
|
54
|
+
* If a function is provided, it will be invoked lazily on the first value access.
|
|
55
|
+
* If a direct value is provided, it is stored immediately.
|
|
56
|
+
*
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
59
|
+
constructor(value: T | (() => T)) {
|
|
60
|
+
super();
|
|
61
|
+
this._initEager(value);
|
|
62
|
+
}
|
|
24
63
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*/
|
|
35
|
-
get(): T {
|
|
36
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
37
|
-
this._initLazy();
|
|
38
|
-
return this._value;
|
|
39
|
-
}
|
|
64
|
+
/**
|
|
65
|
+
* Internal method to get the raw value.
|
|
66
|
+
* @internal
|
|
67
|
+
*/
|
|
68
|
+
protected _getRaw(): T {
|
|
69
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
70
|
+
this._initLazy();
|
|
71
|
+
return this._value;
|
|
72
|
+
}
|
|
40
73
|
|
|
41
|
-
|
|
74
|
+
/* INTERNAL --------------------------------------------------------- */
|
|
42
75
|
|
|
43
|
-
|
|
44
|
-
|
|
76
|
+
/** @internal */ protected _initialized = false;
|
|
77
|
+
/** @internal */ protected _init?: () => T;
|
|
45
78
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
79
|
+
/** @internal */ protected _initEager(value: T | (() => T)): void {
|
|
80
|
+
if (typeof value === "function") {
|
|
81
|
+
this._init = value as () => T;
|
|
82
|
+
} else {
|
|
83
|
+
this._value = value;
|
|
84
|
+
this._initialized = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
54
87
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
/** @internal */ protected _initLazy(): void {
|
|
89
|
+
if (!this._initialized && this._init) {
|
|
90
|
+
this._value = this._init();
|
|
91
|
+
this._initialized = true;
|
|
92
|
+
}
|
|
93
|
+
/* v8 ignore next 2 */
|
|
94
|
+
if (!this._initialized)
|
|
95
|
+
throw new Error("[PicoFlow] Primitive can't be initialized");
|
|
96
|
+
}
|
|
64
97
|
}
|