@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.
- 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 +610 -436
- 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 -23
- 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/basic/observable.ts
CHANGED
|
@@ -1,51 +1,109 @@
|
|
|
1
1
|
import { FlowEffect } from "./effect";
|
|
2
2
|
import { FlowSignal } from "./signal";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A function that retrieves the current value from a FlowObservable.
|
|
6
|
-
* @typeparam T - The type of the value held by the observable.
|
|
7
|
-
* @param observable - The FlowObservable instance to retrieve the value from.
|
|
8
|
-
* @returns The current value of the observable.
|
|
9
|
-
* @public
|
|
10
|
-
*/
|
|
11
|
-
export type FlowGetter = <T>(observable: FlowObservable<T>) => T;
|
|
3
|
+
import type { TrackingContext } from "./trackingContext";
|
|
12
4
|
|
|
13
5
|
/**
|
|
14
6
|
* Represents a reactive observable that holds and tracks a value.
|
|
15
7
|
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* FlowObservable is the base class for all reactive values in PicoFlow. It provides two ways
|
|
10
|
+
* to access the current value:
|
|
16
11
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
12
|
+
* 1. **Tracked access** via `get(context)`: Registers the observable as a dependency in the
|
|
13
|
+
* tracking context, so changes trigger re-execution of the effect or derivation.
|
|
14
|
+
*
|
|
15
|
+
* 2. **Untracked access** via `pick()` or `get(null)`: Reads the current value without registering
|
|
16
|
+
* a dependency, useful for reading values within effects that shouldn't trigger re-runs.
|
|
17
|
+
*
|
|
18
|
+
* Subclasses must implement the {@link FlowObservable._getRaw} method to provide the actual value.
|
|
19
|
+
*
|
|
20
|
+
* @typeParam T - The type of the value held by the observable.
|
|
19
21
|
* @public
|
|
20
22
|
*/
|
|
21
23
|
export abstract class FlowObservable<T> extends FlowSignal {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Gets the current value with optional dependency tracking.
|
|
26
|
+
*
|
|
27
|
+
* @param context - The tracking context for reactive tracking, or null for untracked access.
|
|
28
|
+
* When a context is provided, this observable is registered as a dependency. When null,
|
|
29
|
+
* the value is read without any tracking.
|
|
30
|
+
*
|
|
31
|
+
* @returns The current value of type T.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* Use `get(t)` within effects and derivations to create reactive dependencies.
|
|
35
|
+
* Use `get(null)` when you need to read a value without tracking (though `pick()` is more idiomatic).
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* effect((t) => {
|
|
40
|
+
* const tracked = $state.get(t); // Dependency registered
|
|
41
|
+
* const untracked = $other.get(null); // No dependency
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
get(context: TrackingContext | null): T {
|
|
48
|
+
if (context) {
|
|
49
|
+
this.watch(context);
|
|
50
|
+
}
|
|
51
|
+
return this._getRaw();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Gets the current value without any dependency tracking.
|
|
56
|
+
*
|
|
57
|
+
* @returns The current value of type T.
|
|
58
|
+
*
|
|
59
|
+
* @remarks
|
|
60
|
+
* This method is equivalent to calling `get(null)` but provides a more semantic and readable API.
|
|
61
|
+
* Use `pick()` when you want to read a snapshot of the current value without creating a reactive
|
|
62
|
+
* dependency. This is useful for:
|
|
63
|
+
* - Reading initial values
|
|
64
|
+
* - Accessing configuration that shouldn't trigger updates
|
|
65
|
+
* - Mixing tracked and untracked reads in the same effect
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* // Read a snapshot outside reactive context
|
|
70
|
+
* const currentValue = $state.pick();
|
|
71
|
+
*
|
|
72
|
+
* // Mix tracked and untracked reads
|
|
73
|
+
* effect((t) => {
|
|
74
|
+
* const tracked = $reactive.get(t); // Triggers re-runs
|
|
75
|
+
* const snapshot = $config.pick(); // Doesn't trigger re-runs
|
|
76
|
+
* processData(tracked, snapshot);
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
pick(): T {
|
|
83
|
+
return this._getRaw();
|
|
84
|
+
}
|
|
29
85
|
|
|
30
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Internal method to retrieve the raw value.
|
|
88
|
+
* Subclasses must override this method to provide the current value.
|
|
89
|
+
* @internal
|
|
90
|
+
*/
|
|
91
|
+
protected abstract _getRaw(): T;
|
|
31
92
|
|
|
32
|
-
|
|
93
|
+
/* INTERNAL -------------------------------------------*/
|
|
33
94
|
|
|
34
|
-
|
|
35
|
-
listener._registerDependency(this);
|
|
36
|
-
return this.get();
|
|
37
|
-
}
|
|
95
|
+
/** @internal */ protected _value!: T;
|
|
38
96
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Subscribes a listener function to changes of the observable.
|
|
99
|
+
* The listener is executed immediately with the current value and on subsequent updates.
|
|
100
|
+
* @param listener - A callback function that receives the new value.
|
|
101
|
+
* @returns A disposer function to cancel the subscription.
|
|
102
|
+
*/
|
|
103
|
+
subscribe(listener: (value: T) => void): () => void {
|
|
104
|
+
const effect = new FlowEffect((t) => {
|
|
105
|
+
listener(this.get(t));
|
|
106
|
+
});
|
|
107
|
+
return () => effect.dispose();
|
|
108
|
+
}
|
|
51
109
|
}
|
package/src/basic/signal.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import type { FlowDisposable } from "./disposable";
|
|
2
2
|
import type { FlowEffect } from "./effect";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A function for watching a FlowSignal.
|
|
6
|
-
* @param signal - The FlowSignal that is being observed.
|
|
7
|
-
* @public
|
|
8
|
-
*/
|
|
9
|
-
export type FlowWatcher = (signal: FlowSignal) => void;
|
|
3
|
+
import type { TrackingContext } from "./trackingContext";
|
|
10
4
|
|
|
11
5
|
/**
|
|
12
6
|
* Represents a reactive signal.
|
|
@@ -16,102 +10,136 @@ export type FlowWatcher = (signal: FlowSignal) => void;
|
|
|
16
10
|
* @public
|
|
17
11
|
*/
|
|
18
12
|
export class FlowSignal implements FlowDisposable {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Triggers the FlowSignal.
|
|
15
|
+
* Notifies all registered listeners and schedules execution of associated effects.
|
|
16
|
+
* @throws If the FlowSignal has already been disposed.
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
public trigger(): void {
|
|
20
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
21
|
+
this._notify();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Watches the signal, registering it as a dependency in the tracking context.
|
|
26
|
+
*
|
|
27
|
+
* @param context - The tracking context in which to register this signal as a dependency.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* Use `watch()` when you want to track a signal without reading its value (signals don't
|
|
31
|
+
* have values to read). This is useful for triggering effects based on signal events
|
|
32
|
+
* without needing associated data.
|
|
33
|
+
*
|
|
34
|
+
* When the signal is triggered via `trigger()`, any effects or derivations that have
|
|
35
|
+
* watched this signal will automatically re-execute.
|
|
36
|
+
*
|
|
37
|
+
* This method must be called within an effect or derivation context where a TrackingContext
|
|
38
|
+
* is available. For observables (which hold values), use `.get(t)` instead, which both
|
|
39
|
+
* reads the value and watches for changes.
|
|
40
|
+
*
|
|
41
|
+
* @throws Error if the signal has been disposed.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const $signal = signal();
|
|
46
|
+
*
|
|
47
|
+
* effect((t) => {
|
|
48
|
+
* $signal.watch(t); // Track the signal
|
|
49
|
+
* console.log('Signal triggered!');
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* $signal.trigger(); // Logs: "Signal triggered!"
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @public
|
|
56
|
+
*/
|
|
57
|
+
public watch(context: TrackingContext): void {
|
|
58
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
59
|
+
context._registerDependency(this);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Disposes the FlowSignal.
|
|
64
|
+
* Cleans up all registered effects, listeners, and dependencies.
|
|
65
|
+
* Once disposed, further usage of the signal will throw an error.
|
|
66
|
+
* @throws If the FlowSignal is already disposed.
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
public dispose(options?: { self: boolean }): void {
|
|
70
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
71
|
+
if (options?.self) {
|
|
72
|
+
Array.from(this._effects).forEach((effect) => {
|
|
73
|
+
effect._unregisterDependency(this);
|
|
74
|
+
});
|
|
75
|
+
Array.from(this._listeners).forEach((listener) => {
|
|
76
|
+
listener._unregisterDependency(this);
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
Array.from(this._effects).forEach((effect) => {
|
|
80
|
+
effect.dispose();
|
|
81
|
+
});
|
|
82
|
+
Array.from(this._listeners).forEach((listener) => {
|
|
83
|
+
listener.dispose();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
Array.from(this._dependencies).forEach((dependency) => {
|
|
87
|
+
this._unregisterDependency(dependency);
|
|
88
|
+
});
|
|
89
|
+
this._disposed = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Indicates whether the FlowSignal has been disposed.
|
|
94
|
+
* @remarks Once disposed, the signal should not be used.
|
|
95
|
+
* @public
|
|
96
|
+
*/
|
|
97
|
+
public get disposed(): boolean {
|
|
98
|
+
return this._disposed;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* INTERNAL ------------------------------------------------------------- */
|
|
102
|
+
|
|
103
|
+
/** @internal */ protected _disposed = false;
|
|
104
|
+
|
|
105
|
+
/** @internal */ protected _dependencies = new Set<FlowSignal>();
|
|
106
|
+
|
|
107
|
+
/** @internal */ protected _listeners = new Set<FlowSignal>();
|
|
108
|
+
|
|
109
|
+
/** @internal */ protected _effects = new Set<FlowEffect>();
|
|
110
|
+
|
|
111
|
+
/** @internal */ _notify(): void {
|
|
112
|
+
this._listeners.forEach((listener) => {
|
|
113
|
+
listener._notify();
|
|
114
|
+
});
|
|
115
|
+
this._effects.forEach((effect) => {
|
|
116
|
+
effect._exec();
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** @internal */ _registerDependency(dependency: FlowSignal): void {
|
|
121
|
+
this._dependencies.add(dependency);
|
|
122
|
+
dependency._registerListener(this);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @internal */ _unregisterDependency(dependency: FlowSignal): void {
|
|
126
|
+
this._dependencies.delete(dependency);
|
|
127
|
+
dependency._unregisterListener(this);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @internal */ _registerListener(signal: FlowSignal): void {
|
|
131
|
+
this._listeners.add(signal);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** @internal */ _unregisterListener(signal: FlowSignal): void {
|
|
135
|
+
this._listeners.delete(signal);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @internal */ _registerEffect(effect: FlowEffect): void {
|
|
139
|
+
this._effects.add(effect);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** @internal */ _unregisterEffect(effect: FlowEffect): void {
|
|
143
|
+
this._effects.delete(effect);
|
|
144
|
+
}
|
|
117
145
|
}
|
package/src/basic/state.ts
CHANGED
|
@@ -3,37 +3,58 @@ import { FlowConstant } from "./constant";
|
|
|
3
3
|
/**
|
|
4
4
|
* Represents a reactive state that holds a mutable value.
|
|
5
5
|
*
|
|
6
|
-
* @
|
|
6
|
+
* @typeParam T - The type of the state value.
|
|
7
7
|
*
|
|
8
8
|
* @remarks
|
|
9
|
-
* FlowState extends FlowConstant
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* FlowState extends FlowConstant and inherits reactive value access methods from FlowObservable.
|
|
10
|
+
* You can read the state value using `get(context)` for tracked access or `pick()` for untracked access.
|
|
11
|
+
* Use the {@link FlowState.set} method to update the state value.
|
|
12
|
+
*
|
|
13
|
+
* When the state is updated with a new value that differs from the current value, all dependent
|
|
14
|
+
* effects and derivations are automatically notified and re-executed. If the new value is strictly
|
|
15
|
+
* equal to the current value, no notification occurs.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const $count = state(0);
|
|
20
|
+
*
|
|
21
|
+
* // Read with tracking
|
|
22
|
+
* effect((t) => {
|
|
23
|
+
* console.log($count.get(t)); // Effect re-runs when $count changes
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Read without tracking
|
|
27
|
+
* const snapshot = $count.pick();
|
|
28
|
+
*
|
|
29
|
+
* // Update the value
|
|
30
|
+
* $count.set(1);
|
|
31
|
+
* $count.set(current => current + 1);
|
|
32
|
+
* ```
|
|
12
33
|
*
|
|
13
34
|
* @public
|
|
14
35
|
*/
|
|
15
36
|
export class FlowState<T> extends FlowConstant<T> {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Updates the state with a new value.
|
|
39
|
+
* @param value - A new value or a callback function that computes a new value based on the current state.
|
|
40
|
+
* @remarks
|
|
41
|
+
* If the computed new value is strictly equal to the current state value, no change is made and subscribers
|
|
42
|
+
* will not be notified. Otherwise, the state is updated and all subscribers are informed of the change.
|
|
43
|
+
* @throws Error if the state has been disposed.
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
set(value: T | ((current: T) => T)): void {
|
|
47
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
27
48
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
49
|
+
// compute new value
|
|
50
|
+
const next =
|
|
51
|
+
typeof value === "function"
|
|
52
|
+
? (value as (current: T) => T)(this._value)
|
|
53
|
+
: value;
|
|
33
54
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
55
|
+
// apply new value
|
|
56
|
+
if (next === this._value) return;
|
|
57
|
+
this._value = next;
|
|
58
|
+
this._notify();
|
|
59
|
+
}
|
|
39
60
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { FlowDerivation } from "./derivation";
|
|
2
|
+
import type { FlowEffect } from "./effect";
|
|
3
|
+
import type { FlowSignal } from "./signal";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a tracking context used to register dependencies during reactive computations.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* TrackingContext is the core mechanism that enables automatic dependency tracking in PicoFlow's
|
|
10
|
+
* reactive system. When you create an effect or derivation, PicoFlow automatically creates and
|
|
11
|
+
* manages a TrackingContext instance for you. This context is passed as a parameter (typically
|
|
12
|
+
* named `t`) to your computation functions.
|
|
13
|
+
*
|
|
14
|
+
* You use the tracking context to explicitly mark which observables should be tracked as dependencies:
|
|
15
|
+
* - Call `observable.get(t)` to read a value AND register it as a dependency
|
|
16
|
+
* - Call `observable.pick()` to read a value WITHOUT registering it as a dependency
|
|
17
|
+
* - Call `signal.watch(t)` to register a signal as a dependency without reading a value
|
|
18
|
+
*
|
|
19
|
+
* End-users typically don't instantiate TrackingContext directly; instead, they receive it as
|
|
20
|
+
* a parameter in effect and derivation callbacks.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // TrackingContext passed as parameter 't'
|
|
25
|
+
* effect((t) => {
|
|
26
|
+
* const value = $state.get(t); // Tracked dependency
|
|
27
|
+
* const snapshot = $other.pick(); // Not tracked
|
|
28
|
+
* console.log(value, snapshot);
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
export class TrackingContext {
|
|
35
|
+
/** @internal */
|
|
36
|
+
constructor(private _owner: FlowEffect | FlowDerivation<unknown>) {}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Registers a dependency on the given signal.
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
/** @internal */ _registerDependency(signal: FlowSignal): void {
|
|
43
|
+
this._owner._registerDependency(signal);
|
|
44
|
+
}
|
|
45
|
+
}
|