@ersbeth/picoflow 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +5 -0
- package/README.md +151 -0
- package/api/doc/index.md +31 -0
- package/api/doc/picoflow.derivation.md +55 -0
- package/api/doc/picoflow.effect.md +55 -0
- package/api/doc/picoflow.flowderivation._constructor_.md +49 -0
- package/api/doc/picoflow.flowderivation.get.md +23 -0
- package/api/doc/picoflow.flowderivation.md +86 -0
- package/api/doc/picoflow.flowdisposer.md +13 -0
- package/api/doc/picoflow.floweffect._constructor_.md +49 -0
- package/api/doc/picoflow.floweffect.dispose.md +21 -0
- package/api/doc/picoflow.floweffect.disposed.md +13 -0
- package/api/doc/picoflow.floweffect.md +131 -0
- package/api/doc/picoflow.flowgetter.md +15 -0
- package/api/doc/picoflow.flowmap._lastdeleted.md +21 -0
- package/api/doc/picoflow.flowmap._lastset.md +21 -0
- package/api/doc/picoflow.flowmap.delete.md +57 -0
- package/api/doc/picoflow.flowmap.md +135 -0
- package/api/doc/picoflow.flowmap.setat.md +73 -0
- package/api/doc/picoflow.flowobservable.get.md +19 -0
- package/api/doc/picoflow.flowobservable.md +54 -0
- package/api/doc/picoflow.flowresource._constructor_.md +65 -0
- package/api/doc/picoflow.flowresource.fetch.md +27 -0
- package/api/doc/picoflow.flowresource.get.md +23 -0
- package/api/doc/picoflow.flowresource.md +100 -0
- package/api/doc/picoflow.flowsetter.md +13 -0
- package/api/doc/picoflow.flowsignal.dispose.md +25 -0
- package/api/doc/picoflow.flowsignal.disposed.md +18 -0
- package/api/doc/picoflow.flowsignal.md +111 -0
- package/api/doc/picoflow.flowsignal.trigger.md +25 -0
- package/api/doc/picoflow.flowstate._constructor_.md +49 -0
- package/api/doc/picoflow.flowstate.get.md +23 -0
- package/api/doc/picoflow.flowstate.md +100 -0
- package/api/doc/picoflow.flowstate.set.md +61 -0
- package/api/doc/picoflow.flowstream._constructor_.md +65 -0
- package/api/doc/picoflow.flowstream.dispose.md +21 -0
- package/api/doc/picoflow.flowstream.get.md +23 -0
- package/api/doc/picoflow.flowstream.md +100 -0
- package/api/doc/picoflow.flowupdater.md +19 -0
- package/api/doc/picoflow.flowwatcher.md +15 -0
- package/api/doc/picoflow.map.md +59 -0
- package/api/doc/picoflow.md +287 -0
- package/api/doc/picoflow.resource.md +71 -0
- package/api/doc/picoflow.signal.md +19 -0
- package/api/doc/picoflow.state.md +55 -0
- package/api/doc/picoflow.stream.md +71 -0
- package/api/picoflow.api.md +145 -0
- package/api-extractor.json +60 -0
- package/biome.json +34 -0
- package/dist/picoflow.js +572 -0
- package/dist/types/creators.d.ts +70 -0
- package/dist/types/creators.d.ts.map +1 -0
- package/dist/types/derivation.d.ts +58 -0
- package/dist/types/derivation.d.ts.map +1 -0
- package/dist/types/effect.d.ts +108 -0
- package/dist/types/effect.d.ts.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/map.d.ts +75 -0
- package/dist/types/map.d.ts.map +1 -0
- package/dist/types/observable.d.ts +40 -0
- package/dist/types/observable.d.ts.map +1 -0
- package/dist/types/resource.d.ts +46 -0
- package/dist/types/resource.d.ts.map +1 -0
- package/dist/types/signal.d.ts +111 -0
- package/dist/types/signal.d.ts.map +1 -0
- package/dist/types/state.d.ts +39 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/stream.d.ts +71 -0
- package/dist/types/stream.d.ts.map +1 -0
- package/package.json +40 -0
- package/src/creators.ts +101 -0
- package/src/derivation.ts +96 -0
- package/src/effect.ts +152 -0
- package/src/index.ts +30 -0
- package/src/map.ts +83 -0
- package/src/observable.ts +50 -0
- package/src/resource.ts +64 -0
- package/src/signal.ts +166 -0
- package/src/state.ts +52 -0
- package/src/stream.ts +99 -0
- package/test/derivation.test.ts +422 -0
- package/test/map.test.ts +106 -0
- package/test/resource.test.ts +127 -0
- package/test/signal.test.ts +59 -0
- package/test/state.test.ts +89 -0
- package/test/stream.test.ts +171 -0
- package/tsconfig.json +22 -0
- package/vite.config.ts +26 -0
- package/vitest.config.ts +11 -0
package/src/creators.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { FlowDerivation } from "./derivation";
|
|
2
|
+
import { FlowEffect } from "./effect";
|
|
3
|
+
import { FlowMap } from "./map";
|
|
4
|
+
import type { FlowGetter } from "./observable";
|
|
5
|
+
import { FlowResource } from "./resource";
|
|
6
|
+
import { FlowSignal, type FlowWatcher } from "./signal";
|
|
7
|
+
import { FlowState } from "./state";
|
|
8
|
+
import { FlowStream } from "./stream";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new reactive signal.
|
|
12
|
+
* @returns A new instance of {@link FlowSignal}.
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export function signal(): FlowSignal {
|
|
16
|
+
return new FlowSignal();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new reactive state holding a value.
|
|
21
|
+
* @typeparam T - The type of the state value.
|
|
22
|
+
* @param value - The initial value for the state.
|
|
23
|
+
* @returns A new instance of {@link FlowState}.
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export function state<T>(value: T): FlowState<T> {
|
|
27
|
+
return new FlowState(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new reactive resource that asynchronously fetches its value.
|
|
32
|
+
* @typeparam T - The type of the resource value.
|
|
33
|
+
* @param fn - An asynchronous function that fetches the resource value.
|
|
34
|
+
* @param initial - The initial value of the resource.
|
|
35
|
+
* @returns A new instance of {@link FlowResource}.
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
export function resource<T>(fn: () => Promise<T>, initial: T): FlowResource<T> {
|
|
39
|
+
return new FlowResource(fn, initial);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates a new reactive stream.
|
|
44
|
+
* @typeparam T - The type of the stream value.
|
|
45
|
+
* @param updater - A function that receives a setter to update the stream's value.
|
|
46
|
+
* It should return a disposer function to clean up resources.
|
|
47
|
+
* @param initial - The initial value of the stream.
|
|
48
|
+
* @returns A new instance of {@link FlowStream}.
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
export function stream<T>(
|
|
52
|
+
updater: (set: (value: T) => void) => () => void,
|
|
53
|
+
initial: T,
|
|
54
|
+
): FlowStream<T> {
|
|
55
|
+
return new FlowStream(updater, initial);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a new reactive derivation whose value is computed based on other reactive signals.
|
|
60
|
+
* @typeparam T - The type of the derived value.
|
|
61
|
+
* @param fn - A function that computes the derived value. It receives a getter and a watcher
|
|
62
|
+
* function to access and register dependencies.
|
|
63
|
+
* @returns A new instance of {@link FlowDerivation}.
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
export function derivation<T>(
|
|
67
|
+
fn: (get: FlowGetter, watch: FlowWatcher) => T,
|
|
68
|
+
): FlowDerivation<T> {
|
|
69
|
+
return new FlowDerivation(fn);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a new reactive effect that executes a side-effect function based on its dependencies.
|
|
74
|
+
* @param fn - A function that performs side effects. It receives a getter and a watcher
|
|
75
|
+
* function for tracking reactive dependencies.
|
|
76
|
+
* @returns A new instance of {@link FlowEffect}.
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export function effect(
|
|
80
|
+
fn: (get: FlowGetter, watch: FlowWatcher) => void,
|
|
81
|
+
): FlowEffect {
|
|
82
|
+
return new FlowEffect(fn);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Creates a new reactive map state.
|
|
87
|
+
* @typeparam K - The type of the keys.
|
|
88
|
+
* @typeparam V - The type of the values.
|
|
89
|
+
* @param initial - An optional record of key-value pairs to initialize the map.
|
|
90
|
+
* @returns A new instance of {@link FlowMap}.
|
|
91
|
+
* @remarks
|
|
92
|
+
* The initial record is converted to a native Map before being used.
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
export function map<K extends string | number | symbol, V>(
|
|
96
|
+
initial?: Record<K, V>,
|
|
97
|
+
): FlowMap<K, V> {
|
|
98
|
+
return new FlowMap<K, V>(
|
|
99
|
+
new Map<K, V>(initial ? (Object.entries(initial) as [K, V][]) : []),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type FlowGetter, FlowObservable } from "./observable";
|
|
2
|
+
import type { FlowWatcher } from "./signal";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a reactive derivation whose value is computed based on other reactive signals.
|
|
6
|
+
* @remarks
|
|
7
|
+
* A FlowDerivation automatically tracks its dependencies and recomputes its value when needed.
|
|
8
|
+
* @typeparam T - The type of the computed value.
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export class FlowDerivation<T> extends FlowObservable<T> {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new FlowDerivation.
|
|
14
|
+
* @param compute - A function that computes the derived value. It is provided with a getter
|
|
15
|
+
* and a watcher function to access observables and signals dependencies.
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
constructor(compute: (get: FlowGetter, watch: FlowWatcher) => T) {
|
|
19
|
+
super();
|
|
20
|
+
this._trackedExec = () => compute(this._trackedGet, this._trackedWatch);
|
|
21
|
+
this._untrackedExec = () =>
|
|
22
|
+
compute(this._untrackedGet, this._untrackedWatch);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gets the current derived value.
|
|
27
|
+
* @returns The current computed value.
|
|
28
|
+
* @remarks
|
|
29
|
+
* This method ensures that the derivation is up-to-date before returning the value.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
public get(): T {
|
|
33
|
+
this._exec();
|
|
34
|
+
return this._value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* INTERNAL MEMBERS AND METHODS */
|
|
38
|
+
|
|
39
|
+
/** @internal */
|
|
40
|
+
private _initialized = false;
|
|
41
|
+
|
|
42
|
+
/** @internal */
|
|
43
|
+
private _dirty = true;
|
|
44
|
+
|
|
45
|
+
/** @internal */
|
|
46
|
+
private _trackedGet: FlowGetter = (observable) => observable._getFrom(this);
|
|
47
|
+
|
|
48
|
+
/** @internal */
|
|
49
|
+
private _trackedWatch: FlowWatcher = (signal) => signal._watchFrom(this);
|
|
50
|
+
|
|
51
|
+
/** @internal */
|
|
52
|
+
private _untrackedGet: FlowGetter = (observable) => observable.get();
|
|
53
|
+
|
|
54
|
+
/** @internal */
|
|
55
|
+
private _untrackedWatch: FlowWatcher = (signal) => signal._watch();
|
|
56
|
+
|
|
57
|
+
/** @internal */
|
|
58
|
+
private _trackedExec: () => T;
|
|
59
|
+
|
|
60
|
+
/** @internal */
|
|
61
|
+
private _untrackedExec: () => T;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @internal
|
|
65
|
+
* Executes the compute function if necessary to update the derived value.
|
|
66
|
+
*/
|
|
67
|
+
_exec(): void {
|
|
68
|
+
if (this._disposed) throw new Error("Effect is disposed");
|
|
69
|
+
|
|
70
|
+
if (this._dirty) {
|
|
71
|
+
if (this._initialized) this._value = this._untrackedExec();
|
|
72
|
+
else {
|
|
73
|
+
this._value = this._trackedExec();
|
|
74
|
+
this._initialized = true;
|
|
75
|
+
}
|
|
76
|
+
this._dirty = false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @internal
|
|
82
|
+
* Marks the derivation as dirty and notifies downstream dependencies.
|
|
83
|
+
*/
|
|
84
|
+
override _notify(): void {
|
|
85
|
+
this._dirty = true;
|
|
86
|
+
super._notify();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @internal
|
|
91
|
+
* Ensures that the derivation is up-to-date when it is watched.
|
|
92
|
+
*/
|
|
93
|
+
override _watch(): void {
|
|
94
|
+
this._exec();
|
|
95
|
+
}
|
|
96
|
+
}
|
package/src/effect.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { FlowGetter } from "./observable";
|
|
2
|
+
import type { FlowSignal, FlowWatcher } from "./signal";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a reactive effect that executes a side‐effect function
|
|
6
|
+
* based on its tracked dependencies.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* A FlowEffect runs an apply function to perform side effects. The apply function
|
|
10
|
+
* is executed in two modes:
|
|
11
|
+
* Initially, in a tracked mode to register dependencies.
|
|
12
|
+
* Subsequently, in an untracked mode to only re-execute the effect .
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export class FlowEffect {
|
|
17
|
+
/* API --------------------------------------------------- */
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new FlowEffect.
|
|
21
|
+
*
|
|
22
|
+
* @param apply - A function that performs the side effect. It receives a getter and a watcher
|
|
23
|
+
* function to access and register dependencies on reactive observables and signals.
|
|
24
|
+
*
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
constructor(apply: (get: FlowGetter, watch: FlowWatcher) => void) {
|
|
28
|
+
this._trackedExec = () => apply(this._trackedGet, this._trackedWatch);
|
|
29
|
+
this._untrackedExec = () =>
|
|
30
|
+
apply(this._untrackedGet, this._untrackedWatch);
|
|
31
|
+
this._exec();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Disposes the effect, unregistering all its dependencies.
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
* After disposal, the effect should no longer be used. Calling this method on an already
|
|
39
|
+
* disposed effect will throw an error.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
public dispose(): void {
|
|
44
|
+
if (this._disposed) throw new Error("Effect is disposed");
|
|
45
|
+
Array.from(this._dependencies).forEach((dependency) => {
|
|
46
|
+
this._unregisterDependency(dependency);
|
|
47
|
+
});
|
|
48
|
+
this._disposed = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Indicates whether this effect has been disposed.
|
|
53
|
+
*
|
|
54
|
+
* @returns A boolean value indicating if the effect is disposed.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
public get disposed(): boolean {
|
|
59
|
+
return this._disposed;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* INTERNAL ------------------------------------------------------------ */
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
private _disposed = false;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
private _initialized = false;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
private _dependencies = new Set<FlowSignal>();
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @internal
|
|
81
|
+
* A tracked getter that registers a dependency when accessing an observable.
|
|
82
|
+
*/
|
|
83
|
+
private _trackedGet: FlowGetter = (observable) => observable._getFrom(this);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @internal
|
|
87
|
+
* A tracked watcher that registers a dependency when watching a signal.
|
|
88
|
+
*/
|
|
89
|
+
private _trackedWatch: FlowWatcher = (signal) => signal._watchFrom(this);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @internal
|
|
93
|
+
* An untracked getter that simply retrieves the current value from an observable.
|
|
94
|
+
*/
|
|
95
|
+
private _untrackedGet: FlowGetter = (observable) => observable.get();
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @internal
|
|
99
|
+
* An untracked watcher that calls the default watch on a signal.
|
|
100
|
+
*/
|
|
101
|
+
private _untrackedWatch: FlowWatcher = (signal) => signal._watch();
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @internal
|
|
105
|
+
* Execution function used during initialization (tracked mode).
|
|
106
|
+
*/
|
|
107
|
+
private _trackedExec: () => void;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @internal
|
|
111
|
+
* Execution function used after initialization (untracked mode).
|
|
112
|
+
*/
|
|
113
|
+
private _untrackedExec: () => void;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @internal
|
|
117
|
+
* Executes the effect. If the effect has not been initialized, it runs in tracked mode;
|
|
118
|
+
* otherwise, it runs in untracked mode.
|
|
119
|
+
*
|
|
120
|
+
* @throws Error if the effect has been disposed.
|
|
121
|
+
*/
|
|
122
|
+
_exec(): void {
|
|
123
|
+
if (this._disposed) throw new Error("Effect is disposed");
|
|
124
|
+
if (this._initialized) this._untrackedExec();
|
|
125
|
+
else {
|
|
126
|
+
this._trackedExec();
|
|
127
|
+
this._initialized = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @internal
|
|
133
|
+
* Registers a dependency on the given signal.
|
|
134
|
+
*
|
|
135
|
+
* @param dependency - The FlowSignal to register as a dependency.
|
|
136
|
+
*/
|
|
137
|
+
_registerDependency(dependency: FlowSignal): void {
|
|
138
|
+
this._dependencies.add(dependency);
|
|
139
|
+
dependency._registerEffect(this);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @internal
|
|
144
|
+
* Unregisters the given dependency.
|
|
145
|
+
*
|
|
146
|
+
* @param dependency - The FlowSignal to unregister.
|
|
147
|
+
*/
|
|
148
|
+
_unregisterDependency(dependency: FlowSignal): void {
|
|
149
|
+
this._dependencies.delete(dependency);
|
|
150
|
+
dependency._unregisterEffect(this);
|
|
151
|
+
}
|
|
152
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
*
|
|
4
|
+
* PicoFlow is a lightweight reactive dataflow library that provides a set of
|
|
5
|
+
* reactive primitives such as signals, state, resources, streams, derivations,
|
|
6
|
+
* effects, and reactive maps.
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export {
|
|
10
|
+
signal,
|
|
11
|
+
state,
|
|
12
|
+
resource,
|
|
13
|
+
stream,
|
|
14
|
+
derivation,
|
|
15
|
+
effect,
|
|
16
|
+
map,
|
|
17
|
+
} from "./creators";
|
|
18
|
+
export type { FlowDerivation } from "./derivation";
|
|
19
|
+
export type { FlowEffect } from "./effect";
|
|
20
|
+
export type { FlowGetter, FlowObservable } from "./observable";
|
|
21
|
+
export type { FlowResource } from "./resource";
|
|
22
|
+
export type { FlowSignal, FlowWatcher } from "./signal";
|
|
23
|
+
export type { FlowState } from "./state";
|
|
24
|
+
export type { FlowMap } from "./map";
|
|
25
|
+
export type {
|
|
26
|
+
FlowStream,
|
|
27
|
+
FlowDisposer,
|
|
28
|
+
FlowSetter,
|
|
29
|
+
FlowUpdater,
|
|
30
|
+
} from "./stream";
|
package/src/map.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { FlowState } from "./state";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a reactive map of states that extends {@link FlowState} for a Map of key/value pairs.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* FlowMap wraps a native Map and provides reactive signals for fine-grained tracking
|
|
8
|
+
* of updates to the map. In addition to the reactive capabilities inherited from FlowState,
|
|
9
|
+
* it exposes two public signals:
|
|
10
|
+
*
|
|
11
|
+
* **$lastSet**: A FlowState that holds the most recent key-value pair that was set.
|
|
12
|
+
*
|
|
13
|
+
* **$lastDeleted**: A FlowState that holds the most recent key-value pair that was deleted.
|
|
14
|
+
*
|
|
15
|
+
* Use {@link FlowMap.setAt} to set a key-value pair and {@link FlowMap.delete} to remove a key.
|
|
16
|
+
*
|
|
17
|
+
* @typeparam K - The type of the map keys.
|
|
18
|
+
* @typeparam V - The type of the map values.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
export class FlowMap<K, V> extends FlowState<Map<K, V>> {
|
|
23
|
+
/**
|
|
24
|
+
* A reactive state that holds the most recent key and value that were set.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* When a key is set via {@link FlowMap.setAt}, this state is updated with
|
|
28
|
+
* the corresponding key and value.
|
|
29
|
+
*
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
public $lastSet: FlowState<{ key?: K; value?: V }> = new FlowState({});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A reactive state that holds the most recent key and value that were deleted.
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
* When a key is deleted via {@link FlowMap.delete}, this state is updated with
|
|
39
|
+
* the corresponding key and its last known value.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
public $lastDeleted: FlowState<{ key?: K; value?: V }> = new FlowState({});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Sets a value at the specified key in the underlying map.
|
|
47
|
+
*
|
|
48
|
+
* @param key - The key at which to set the value.
|
|
49
|
+
* @param value - The value to set.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* This method updates the internal map with the given key and value, emits the new
|
|
53
|
+
* key-value pair via {@link FlowMap.$lastSet}, and notifies subscribers of the change.
|
|
54
|
+
*
|
|
55
|
+
* @public
|
|
56
|
+
*/
|
|
57
|
+
public setAt(key: K, value: V): void {
|
|
58
|
+
if (this._disposed) throw new Error("StateMap is disposed");
|
|
59
|
+
this._value.set(key, value);
|
|
60
|
+
this.$lastSet.set({ key, value });
|
|
61
|
+
this._notify();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Deletes the value at the specified key from the underlying map.
|
|
66
|
+
*
|
|
67
|
+
* @param key - The key to delete.
|
|
68
|
+
*
|
|
69
|
+
* @remarks
|
|
70
|
+
* This method removes the key from the internal map, emits the deleted key and its
|
|
71
|
+
* corresponding value via {@link FlowMap.$lastDeleted}, and notifies subscribers
|
|
72
|
+
* of the change.
|
|
73
|
+
*
|
|
74
|
+
* @public
|
|
75
|
+
*/
|
|
76
|
+
public delete(key: K): void {
|
|
77
|
+
if (this._disposed) throw new Error("StateMap is disposed");
|
|
78
|
+
const value = this._value.get(key);
|
|
79
|
+
this._value.delete(key);
|
|
80
|
+
this.$lastDeleted.set({ key, value });
|
|
81
|
+
this._notify();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FlowEffect } from "./effect";
|
|
2
|
+
import { FlowSignal } from "./signal";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A function for retrieving the current value from a FlowObservable.
|
|
6
|
+
* @typeparam T - The type of the value held by the observable.
|
|
7
|
+
* @param atom - The FlowObservable from which to get the value.
|
|
8
|
+
* @returns The current value of the observable.
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export type FlowGetter = <T>(observable: FlowObservable<T>) => T;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Represents a reactive observable that carries a value.
|
|
15
|
+
* @typeparam T - The type of the value held by the observable.
|
|
16
|
+
* @remarks
|
|
17
|
+
* A FlowObservable extends the basic FlowSignal to store a value. Subclasses must
|
|
18
|
+
* implement the abstract {@link FlowObservable.get} method to return the current value.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export abstract class FlowObservable<T> extends FlowSignal {
|
|
22
|
+
/* API ----------------------------------------------- */
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Retrieves the current value of the observable.
|
|
26
|
+
* @returns The current value.
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
abstract get(): T;
|
|
30
|
+
|
|
31
|
+
/* INTERNAL -------------------------------------------*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @internal
|
|
35
|
+
* Internal storage for the observable's value.
|
|
36
|
+
*/
|
|
37
|
+
protected _value!: T;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @internal
|
|
41
|
+
* Retrieves the current value from the observable and registers a dependency
|
|
42
|
+
* from the provided listener.
|
|
43
|
+
* @param listener - The FlowObservable or FlowEffect that is accessing this observable.
|
|
44
|
+
* @returns The current value, as returned by {@link FlowObservable.get}.
|
|
45
|
+
*/
|
|
46
|
+
_getFrom(listener: FlowObservable<unknown> | FlowEffect): T {
|
|
47
|
+
listener._registerDependency(this);
|
|
48
|
+
return this.get();
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/resource.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { FlowObservable } from "./observable";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a reactive resource that asynchronously fetches its value.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* A FlowResource extends FlowObservable and encapsulates an asynchronous fetch function.
|
|
8
|
+
* It is used to retrieve and update its value asynchronously. When the fetch is executed,
|
|
9
|
+
* if the new value differs from the current value, the resource is updated and its subscribers
|
|
10
|
+
* are notified.
|
|
11
|
+
*
|
|
12
|
+
* @typeparam T - The type of the resource value.
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export class FlowResource<T> extends FlowObservable<T> {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new FlowResource.
|
|
19
|
+
* @param fetch - An asynchronous function that retrieves the resource's value.
|
|
20
|
+
* @param initial - The initial value of the resource.
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
constructor(fetch: () => Promise<T>, initial: T) {
|
|
24
|
+
super();
|
|
25
|
+
this._value = initial;
|
|
26
|
+
this._fetch = fetch;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Retrieves the current resource value.
|
|
31
|
+
* @returns The current value.
|
|
32
|
+
* @throws Error if the resource is disposed.
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
35
|
+
public get(): T {
|
|
36
|
+
if (this._disposed) throw new Error("Resource is disposed");
|
|
37
|
+
return this._value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Asynchronously fetches a new value for the resource.
|
|
42
|
+
* @remarks
|
|
43
|
+
* Executes the internal fetch function. If the fetched value differs from the current one,
|
|
44
|
+
* updates the resource's value and notifies subscribers.
|
|
45
|
+
* @returns A Promise that resolves when the fetch operation is complete.
|
|
46
|
+
* @throws Error if the resource is disposed.
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
public async fetch(): Promise<void> {
|
|
50
|
+
if (this._disposed) throw new Error("Resource is disposed");
|
|
51
|
+
const value = await this._fetch();
|
|
52
|
+
if (value === this._value) return;
|
|
53
|
+
this._value = value;
|
|
54
|
+
this._notify();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* INTERNAL ------------------------------------------------ */
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @internal
|
|
61
|
+
* The asynchronous function used to fetch the resource value.
|
|
62
|
+
*/
|
|
63
|
+
private _fetch: () => Promise<T>;
|
|
64
|
+
}
|