@ersbeth/picoflow 0.0.1 → 0.1.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/api/doc/index.md +1 -1
- package/api/doc/picoflow.constant.md +55 -0
- package/api/doc/picoflow.derivation.md +1 -1
- package/api/doc/picoflow.effect.md +1 -1
- package/api/doc/picoflow.flowconstant._constructor_.md +49 -0
- package/api/doc/picoflow.flowconstant.get.md +25 -0
- package/api/doc/picoflow.flowconstant.md +88 -0
- package/api/doc/picoflow.flowderivation._constructor_.md +2 -2
- package/api/doc/picoflow.flowderivation.get.md +2 -2
- package/api/doc/picoflow.flowderivation.md +2 -2
- package/api/doc/picoflow.floweffect._constructor_.md +7 -2
- package/api/doc/picoflow.floweffect.dispose.md +3 -3
- package/api/doc/picoflow.floweffect.disposed.md +1 -1
- package/api/doc/picoflow.floweffect.md +4 -4
- package/api/doc/picoflow.flowgetter.md +2 -2
- package/api/doc/picoflow.flowmap._lastdeleted.md +1 -1
- package/api/doc/picoflow.flowmap._lastset.md +1 -1
- package/api/doc/picoflow.flowmap.delete.md +6 -2
- package/api/doc/picoflow.flowmap.md +5 -7
- package/api/doc/picoflow.flowmap.setat.md +6 -2
- package/api/doc/picoflow.flowobservable.get.md +3 -3
- package/api/doc/picoflow.flowobservable.md +18 -4
- package/api/doc/picoflow.flowobservable.subscribe.md +55 -0
- package/api/doc/picoflow.flowresource._constructor_.md +2 -18
- package/api/doc/picoflow.flowresource.fetch.md +1 -1
- package/api/doc/picoflow.flowresource.get.md +4 -4
- package/api/doc/picoflow.flowresource.md +4 -4
- package/api/doc/picoflow.flowresourceasync._constructor_.md +49 -0
- package/api/doc/picoflow.flowresourceasync.fetch.md +27 -0
- package/api/doc/picoflow.flowresourceasync.get.md +23 -0
- package/api/doc/picoflow.flowresourceasync.md +100 -0
- package/api/doc/picoflow.flowsignal.dispose.md +3 -7
- package/api/doc/picoflow.flowsignal.disposed.md +2 -2
- package/api/doc/picoflow.flowsignal.md +5 -5
- package/api/doc/picoflow.flowsignal.trigger.md +3 -7
- package/api/doc/picoflow.flowstate.md +4 -52
- package/api/doc/picoflow.flowstate.set.md +5 -5
- package/api/doc/picoflow.flowstream._constructor_.md +3 -19
- package/api/doc/picoflow.flowstream.dispose.md +1 -1
- package/api/doc/picoflow.flowstream.get.md +4 -4
- package/api/doc/picoflow.flowstream.md +5 -5
- package/api/doc/picoflow.flowstreamasync._constructor_.md +54 -0
- package/api/doc/picoflow.flowstreamasync.dispose.md +21 -0
- package/api/doc/picoflow.flowstreamasync.get.md +23 -0
- package/api/doc/picoflow.flowstreamasync.md +100 -0
- package/api/doc/picoflow.flowstreamdisposer.md +13 -0
- package/api/doc/picoflow.flowstreamsetter.md +13 -0
- package/api/doc/picoflow.flowstreamupdater.md +19 -0
- package/api/doc/picoflow.flowwatcher.md +1 -1
- package/api/doc/picoflow.map.md +1 -1
- package/api/doc/picoflow.md +80 -14
- package/api/doc/picoflow.resource.md +2 -18
- package/api/doc/picoflow.resourceasync.md +55 -0
- package/api/doc/picoflow.signal.md +1 -1
- package/api/doc/picoflow.state.md +3 -3
- package/api/doc/picoflow.stream.md +2 -18
- package/api/doc/picoflow.streamasync.md +55 -0
- package/api/picoflow.public.api.md +131 -0
- package/api-extractor.json +2 -1
- package/dist/picoflow.js +326 -302
- package/dist/types/advanced/index.d.ts +7 -0
- package/dist/types/advanced/index.d.ts.map +1 -0
- package/dist/types/{map.d.ts → advanced/map.d.ts} +12 -12
- package/dist/types/advanced/map.d.ts.map +1 -0
- package/dist/types/advanced/resource.d.ts +39 -0
- package/dist/types/advanced/resource.d.ts.map +1 -0
- package/dist/types/{resource.d.ts → advanced/resourceAsync.d.ts} +6 -11
- package/dist/types/advanced/resourceAsync.d.ts.map +1 -0
- package/dist/types/advanced/stream.d.ts +59 -0
- package/dist/types/advanced/stream.d.ts.map +1 -0
- package/dist/types/advanced/streamAsync.d.ts +43 -0
- package/dist/types/advanced/streamAsync.d.ts.map +1 -0
- package/dist/types/basic/constant.d.ts +32 -0
- package/dist/types/basic/constant.d.ts.map +1 -0
- package/dist/types/basic/derivation.d.ts +40 -0
- package/dist/types/basic/derivation.d.ts.map +1 -0
- package/dist/types/basic/effect.d.ts +56 -0
- package/dist/types/basic/effect.d.ts.map +1 -0
- package/dist/types/basic/index.d.ts +9 -0
- package/dist/types/basic/index.d.ts.map +1 -0
- package/dist/types/basic/observable.d.ts +34 -0
- package/dist/types/basic/observable.d.ts.map +1 -0
- package/dist/types/basic/signal.d.ts +37 -0
- package/dist/types/basic/signal.d.ts.map +1 -0
- package/dist/types/basic/state.d.ts +26 -0
- package/dist/types/basic/state.d.ts.map +1 -0
- package/dist/types/creators.d.ts +29 -13
- package/dist/types/creators.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -9
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/advanced/index.ts +10 -0
- package/src/{map.ts → advanced/map.ts} +14 -14
- package/src/advanced/resource.ts +56 -0
- package/src/{resource.ts → advanced/resourceAsync.ts} +9 -16
- package/src/advanced/stream.ts +87 -0
- package/src/advanced/streamAsync.ts +82 -0
- package/src/basic/constant.ts +64 -0
- package/src/basic/derivation.ts +86 -0
- package/src/basic/effect.ts +96 -0
- package/src/basic/index.ts +8 -0
- package/src/basic/observable.ts +51 -0
- package/src/basic/signal.ts +105 -0
- package/src/basic/state.ts +39 -0
- package/src/creators.ts +54 -15
- package/src/index.ts +21 -11
- package/test/constant.test.ts +46 -0
- package/test/derivation.test.ts +30 -6
- package/test/effect.test.ts +29 -0
- package/test/map.test.ts +38 -0
- package/test/resource.test.ts +18 -16
- package/test/resourceAsync.test.ts +108 -0
- package/test/signal.test.ts +18 -1
- package/test/state.test.ts +107 -2
- package/test/stream.test.ts +38 -13
- package/test/streamAsync.test.ts +194 -0
- package/tsconfig.json +3 -1
- package/api/doc/picoflow.flowdisposer.md +0 -13
- package/api/doc/picoflow.flowsetter.md +0 -13
- package/api/doc/picoflow.flowstate._constructor_.md +0 -49
- package/api/doc/picoflow.flowstate.get.md +0 -23
- package/api/doc/picoflow.flowupdater.md +0 -19
- package/api/picoflow.api.md +0 -145
- package/dist/types/derivation.d.ts +0 -58
- package/dist/types/derivation.d.ts.map +0 -1
- package/dist/types/effect.d.ts +0 -108
- package/dist/types/effect.d.ts.map +0 -1
- package/dist/types/map.d.ts.map +0 -1
- package/dist/types/observable.d.ts +0 -40
- package/dist/types/observable.d.ts.map +0 -1
- package/dist/types/resource.d.ts.map +0 -1
- package/dist/types/signal.d.ts +0 -111
- package/dist/types/signal.d.ts.map +0 -1
- package/dist/types/state.d.ts +0 -39
- package/dist/types/state.d.ts.map +0 -1
- package/dist/types/stream.d.ts +0 -71
- package/dist/types/stream.d.ts.map +0 -1
- package/src/derivation.ts +0 -96
- package/src/effect.ts +0 -152
- package/src/observable.ts +0 -50
- package/src/signal.ts +0 -166
- package/src/state.ts +0 -52
- package/src/stream.ts +0 -99
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { FlowEffect } from "./effect";
|
|
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;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Represents a reactive observable that holds and tracks a value.
|
|
15
|
+
*
|
|
16
|
+
*
|
|
17
|
+
* @remarks Subclasses must implement the {@link FlowObservable.get} method to return the current value.
|
|
18
|
+
* @typeparam T - The type of the value held by the observable.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export abstract class FlowObservable<T> extends FlowSignal {
|
|
22
|
+
/**
|
|
23
|
+
* Retrieves the current value stored in the observable.
|
|
24
|
+
* Subclasses must override this method to provide the current value.
|
|
25
|
+
* @returns The current value of type T.
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
abstract get(): T;
|
|
29
|
+
|
|
30
|
+
/* INTERNAL -------------------------------------------*/
|
|
31
|
+
|
|
32
|
+
/*@internal*/ protected _value!: T;
|
|
33
|
+
|
|
34
|
+
/*@internal*/ _getFrom(listener: FlowObservable<unknown> | FlowEffect): T {
|
|
35
|
+
listener._registerDependency(this);
|
|
36
|
+
return this.get();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Subscribes a listener function to changes of the observable.
|
|
41
|
+
* The listener is executed immediately with the current value and on subsequent updates.
|
|
42
|
+
* @param listener - A callback function that receives the new value.
|
|
43
|
+
* @returns A disposer function to cancel the subscription.
|
|
44
|
+
*/
|
|
45
|
+
subscribe(listener: (value: T) => void): () => void {
|
|
46
|
+
const effect = new FlowEffect((get) => {
|
|
47
|
+
listener(get(this));
|
|
48
|
+
});
|
|
49
|
+
return () => effect.dispose();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { FlowEffect } from "./effect";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A function for watching a FlowSignal.
|
|
5
|
+
* @param signal - The FlowSignal that is being observed.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export type FlowWatcher = (signal: FlowSignal) => void;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Represents a reactive signal.
|
|
12
|
+
*
|
|
13
|
+
* @remarks Use FlowSignal to create reactive streams that notify listeners and execute associated effects.
|
|
14
|
+
* Signals can be triggered and disposed. Once disposed, interactions with the signal will throw errors.
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export class FlowSignal {
|
|
18
|
+
/**
|
|
19
|
+
* Triggers the FlowSignal.
|
|
20
|
+
* Notifies all registered listeners and schedules execution of associated effects.
|
|
21
|
+
* @throws If the FlowSignal has already been disposed.
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
public trigger(): void {
|
|
25
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
26
|
+
this._notify();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Disposes the FlowSignal.
|
|
31
|
+
* Cleans up all registered effects, listeners, and dependencies.
|
|
32
|
+
* Once disposed, further usage of the signal will throw an error.
|
|
33
|
+
* @throws If the FlowSignal is already disposed.
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
public dispose(): void {
|
|
37
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
38
|
+
Array.from(this._effects).forEach((effect) => effect.dispose());
|
|
39
|
+
Array.from(this._listeners).forEach((listener) => listener.dispose());
|
|
40
|
+
Array.from(this._dependencies).forEach((dependency) => {
|
|
41
|
+
this._unregisterDependency(dependency);
|
|
42
|
+
});
|
|
43
|
+
this._disposed = true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Indicates whether the FlowSignal has been disposed.
|
|
48
|
+
* @remarks Once disposed, the signal should not be used.
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
public get disposed(): boolean {
|
|
52
|
+
return this._disposed;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* INTERNAL ------------------------------------------------------------- */
|
|
56
|
+
|
|
57
|
+
/*@internal*/ protected _disposed = false;
|
|
58
|
+
|
|
59
|
+
/*@internal*/ protected _dependencies = new Set<FlowSignal>();
|
|
60
|
+
|
|
61
|
+
/*@internal*/ protected _listeners = new Set<FlowSignal>();
|
|
62
|
+
|
|
63
|
+
/*@internal*/ protected _effects = new Set<FlowEffect>();
|
|
64
|
+
|
|
65
|
+
/*@internal*/ _watch(): void {
|
|
66
|
+
/* v8 ignore next 1 */
|
|
67
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/*@internal*/ _notify(): void {
|
|
71
|
+
this._listeners.forEach((listener) => listener._notify());
|
|
72
|
+
this._effects.forEach((effect) => effect._exec());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*@internal*/ _watchFrom(listener: FlowSignal | FlowEffect): void {
|
|
76
|
+
listener._registerDependency(this);
|
|
77
|
+
this._watch();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*@internal*/ _registerDependency(dependency: FlowSignal): void {
|
|
81
|
+
this._dependencies.add(dependency);
|
|
82
|
+
dependency._registerListener(this);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/*@internal*/ _unregisterDependency(dependency: FlowSignal): void {
|
|
86
|
+
this._dependencies.delete(dependency);
|
|
87
|
+
dependency._unregisterListener(this);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/*@internal*/ _registerListener(signal: FlowSignal): void {
|
|
91
|
+
this._listeners.add(signal);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/*@internal*/ _unregisterListener(signal: FlowSignal): void {
|
|
95
|
+
this._listeners.delete(signal);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/*@internal*/ _registerEffect(effect: FlowEffect): void {
|
|
99
|
+
this._effects.add(effect);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/*@internal*/ _unregisterEffect(effect: FlowEffect): void {
|
|
103
|
+
this._effects.delete(effect);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FlowConstant } from "./constant";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a reactive state that holds a mutable value.
|
|
5
|
+
*
|
|
6
|
+
* @typeparam T - The type of the state value.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* FlowState extends FlowConstant, which provides the {@link FlowConstant.get} method to read
|
|
10
|
+
* the current state. Use the {@link FlowState.set} method to update the state. When the state is updated,
|
|
11
|
+
* subscribers are notified automatically. This class notifies subscribers only when the value changes.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export class FlowState<T> extends FlowConstant<T> {
|
|
16
|
+
/**
|
|
17
|
+
* Updates the state with a new value.
|
|
18
|
+
* @param value - A new value or a callback function that computes a new value based on the current state.
|
|
19
|
+
* @remarks
|
|
20
|
+
* If the computed new value is strictly equal to the current state value, no change is made and subscribers
|
|
21
|
+
* will not be notified. Otherwise, the state is updated and all subscribers are informed of the change.
|
|
22
|
+
* @throws Error if the state has been disposed.
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
set(value: T | ((current: T) => T)): void {
|
|
26
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
27
|
+
|
|
28
|
+
// compute new value
|
|
29
|
+
const next =
|
|
30
|
+
typeof value === "function"
|
|
31
|
+
? (value as (current: T) => T)(this._value)
|
|
32
|
+
: value;
|
|
33
|
+
|
|
34
|
+
// apply new value
|
|
35
|
+
if (next === this._value) return;
|
|
36
|
+
this._value = next;
|
|
37
|
+
this._notify();
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/creators.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
FlowMap,
|
|
3
|
+
FlowResource,
|
|
4
|
+
FlowResourceAsync,
|
|
5
|
+
FlowStream,
|
|
6
|
+
FlowStreamAsync,
|
|
7
|
+
} from "./advanced/";
|
|
8
|
+
import {
|
|
9
|
+
FlowConstant,
|
|
10
|
+
FlowDerivation,
|
|
11
|
+
FlowEffect,
|
|
12
|
+
FlowSignal,
|
|
13
|
+
FlowState,
|
|
14
|
+
} from "./basic/";
|
|
15
|
+
import type { FlowGetter, FlowWatcher } from "./basic/";
|
|
9
16
|
|
|
10
17
|
/**
|
|
11
18
|
* Creates a new reactive signal.
|
|
@@ -16,6 +23,16 @@ export function signal(): FlowSignal {
|
|
|
16
23
|
return new FlowSignal();
|
|
17
24
|
}
|
|
18
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new reactive constant.
|
|
28
|
+
* @param value - The value or a function that returns the value.
|
|
29
|
+
* @returns A new instance of {@link FlowConstant}.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export function constant<T>(value: T | (() => T)): FlowConstant<T> {
|
|
33
|
+
return new FlowConstant<T>(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
19
36
|
/**
|
|
20
37
|
* Creates a new reactive state holding a value.
|
|
21
38
|
* @typeparam T - The type of the state value.
|
|
@@ -23,7 +40,7 @@ export function signal(): FlowSignal {
|
|
|
23
40
|
* @returns A new instance of {@link FlowState}.
|
|
24
41
|
* @public
|
|
25
42
|
*/
|
|
26
|
-
export function state<T>(value: T): FlowState<T> {
|
|
43
|
+
export function state<T>(value: T | (() => T)): FlowState<T> {
|
|
27
44
|
return new FlowState(value);
|
|
28
45
|
}
|
|
29
46
|
|
|
@@ -31,12 +48,22 @@ export function state<T>(value: T): FlowState<T> {
|
|
|
31
48
|
* Creates a new reactive resource that asynchronously fetches its value.
|
|
32
49
|
* @typeparam T - The type of the resource value.
|
|
33
50
|
* @param fn - An asynchronous function that fetches the resource value.
|
|
34
|
-
* @param initial - The initial value of the resource.
|
|
35
51
|
* @returns A new instance of {@link FlowResource}.
|
|
36
52
|
* @public
|
|
37
53
|
*/
|
|
38
|
-
export function resource<T>(fn: () => Promise<T
|
|
39
|
-
return new FlowResource(fn
|
|
54
|
+
export function resource<T>(fn: () => Promise<T>): FlowResource<T> {
|
|
55
|
+
return new FlowResource(fn);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a new reactive asynchronous resource that fetches its value.
|
|
60
|
+
* @typeparam T - The type of the resource value.
|
|
61
|
+
* @param fn - An asynchronous function that fetches the resource value.
|
|
62
|
+
* @returns A new instance of {@link FlowResourceAsync}.
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export function resourceAsync<T>(fn: () => Promise<T>): FlowResourceAsync<T> {
|
|
66
|
+
return new FlowResourceAsync(fn);
|
|
40
67
|
}
|
|
41
68
|
|
|
42
69
|
/**
|
|
@@ -44,15 +71,27 @@ export function resource<T>(fn: () => Promise<T>, initial: T): FlowResource<T> {
|
|
|
44
71
|
* @typeparam T - The type of the stream value.
|
|
45
72
|
* @param updater - A function that receives a setter to update the stream's value.
|
|
46
73
|
* It should return a disposer function to clean up resources.
|
|
47
|
-
* @param initial - The initial value of the stream.
|
|
48
74
|
* @returns A new instance of {@link FlowStream}.
|
|
49
75
|
* @public
|
|
50
76
|
*/
|
|
51
77
|
export function stream<T>(
|
|
52
78
|
updater: (set: (value: T) => void) => () => void,
|
|
53
|
-
initial: T,
|
|
54
79
|
): FlowStream<T> {
|
|
55
|
-
return new FlowStream(updater
|
|
80
|
+
return new FlowStream(updater);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Creates a new reactive asynchronous stream.
|
|
85
|
+
* @typeparam T - The type of the stream value.
|
|
86
|
+
* @param updater - A function that receives a setter to update the stream's value.
|
|
87
|
+
* It should return a disposer function to clean up resources.
|
|
88
|
+
* @returns A new instance of {@link FlowStreamAsync}.
|
|
89
|
+
* @public
|
|
90
|
+
*/
|
|
91
|
+
export function streamAsync<T>(
|
|
92
|
+
updater: (set: (value: T) => void) => () => void,
|
|
93
|
+
): FlowStreamAsync<T> {
|
|
94
|
+
return new FlowStreamAsync(updater);
|
|
56
95
|
}
|
|
57
96
|
|
|
58
97
|
/**
|
package/src/index.ts
CHANGED
|
@@ -9,22 +9,32 @@
|
|
|
9
9
|
export {
|
|
10
10
|
signal,
|
|
11
11
|
state,
|
|
12
|
+
constant,
|
|
12
13
|
resource,
|
|
13
14
|
stream,
|
|
14
15
|
derivation,
|
|
15
16
|
effect,
|
|
16
17
|
map,
|
|
18
|
+
streamAsync,
|
|
19
|
+
resourceAsync,
|
|
17
20
|
} 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
21
|
export type {
|
|
22
|
+
FlowDerivation,
|
|
23
|
+
FlowEffect,
|
|
24
|
+
FlowGetter,
|
|
25
|
+
FlowObservable,
|
|
26
|
+
FlowSignal,
|
|
27
|
+
FlowWatcher,
|
|
28
|
+
FlowState,
|
|
29
|
+
FlowConstant,
|
|
30
|
+
} from "./basic/";
|
|
31
|
+
export type {
|
|
32
|
+
FlowResource,
|
|
33
|
+
FlowMap,
|
|
34
|
+
FlowResourceAsync,
|
|
35
|
+
FlowStreamAsync,
|
|
26
36
|
FlowStream,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} from "./
|
|
37
|
+
FlowStreamDisposer,
|
|
38
|
+
FlowStreamSetter,
|
|
39
|
+
FlowStreamUpdater,
|
|
40
|
+
} from "./advanced/";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { constant, effect } from "#package";
|
|
3
|
+
|
|
4
|
+
describe("constant", () => {
|
|
5
|
+
test("is initialized", () => {
|
|
6
|
+
const $constant = constant(1);
|
|
7
|
+
expect($constant.get()).toBe(1);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("is lazy initialized", () => {
|
|
11
|
+
const $constant = constant(() => 1);
|
|
12
|
+
expect($constant.get()).toBe(1);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("get throws when disposed", () => {
|
|
16
|
+
const $constant = constant(1);
|
|
17
|
+
|
|
18
|
+
expect($constant.get()).toBe(1);
|
|
19
|
+
|
|
20
|
+
$constant.dispose();
|
|
21
|
+
|
|
22
|
+
expect(() => $constant.get()).toThrow(
|
|
23
|
+
"[PicoFlow] Primitive is disposed",
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("effect", () => {
|
|
29
|
+
test("called with init value", () => {
|
|
30
|
+
const $constant = constant(1);
|
|
31
|
+
const effectFn = vi.fn();
|
|
32
|
+
effect((get) => effectFn(get($constant)));
|
|
33
|
+
|
|
34
|
+
expect(effectFn).toHaveBeenCalledTimes(1);
|
|
35
|
+
expect(effectFn).toHaveBeenLastCalledWith(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("called with lazy init value", () => {
|
|
39
|
+
const $constant = constant(() => 1);
|
|
40
|
+
const effectFn = vi.fn();
|
|
41
|
+
effect((get) => effectFn(get($constant)));
|
|
42
|
+
|
|
43
|
+
expect(effectFn).toHaveBeenCalledTimes(1);
|
|
44
|
+
expect(effectFn).toHaveBeenLastCalledWith(1);
|
|
45
|
+
});
|
|
46
|
+
});
|
package/test/derivation.test.ts
CHANGED
|
@@ -21,7 +21,9 @@ describe("derivation", () => {
|
|
|
21
21
|
expect($derivation.get()).toBe(4);
|
|
22
22
|
|
|
23
23
|
$derivation.dispose();
|
|
24
|
-
expect(() => $derivation.get()).toThrow(
|
|
24
|
+
expect(() => $derivation.get()).toThrow(
|
|
25
|
+
"[PicoFlow] Primitive is disposed",
|
|
26
|
+
);
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
test("get throws when state is disposed", () => {
|
|
@@ -33,7 +35,9 @@ describe("derivation", () => {
|
|
|
33
35
|
expect($derivation.get()).toBe(4);
|
|
34
36
|
|
|
35
37
|
$state.dispose();
|
|
36
|
-
expect(() => $derivation.get()).toThrow(
|
|
38
|
+
expect(() => $derivation.get()).toThrow(
|
|
39
|
+
"[PicoFlow] Primitive is disposed",
|
|
40
|
+
);
|
|
37
41
|
});
|
|
38
42
|
|
|
39
43
|
test("is updated (chained dependencies)", () => {
|
|
@@ -111,8 +115,8 @@ describe("derivation", () => {
|
|
|
111
115
|
resourceCounter++;
|
|
112
116
|
return resourceCounter;
|
|
113
117
|
};
|
|
114
|
-
const $resource = resource(fetchResource
|
|
115
|
-
const $derivation = derivation((get) => get($resource) * 2);
|
|
118
|
+
const $resource = resource(fetchResource);
|
|
119
|
+
const $derivation = derivation((get) => (get($resource) ?? 0) * 2);
|
|
116
120
|
|
|
117
121
|
expect($derivation.get()).toBe(0);
|
|
118
122
|
|
|
@@ -374,6 +378,26 @@ describe("effect", () => {
|
|
|
374
378
|
expect(effectFn).toHaveBeenLastCalledWith(4);
|
|
375
379
|
});
|
|
376
380
|
|
|
381
|
+
test("NOT called when derivation is disposed (watch)", () => {
|
|
382
|
+
const $state = state(1);
|
|
383
|
+
const $derivation = derivation((get) => get($state) * 2);
|
|
384
|
+
const effectFn = vi.fn();
|
|
385
|
+
effect((_, watch) => {
|
|
386
|
+
watch($derivation);
|
|
387
|
+
effectFn();
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
expect(effectFn).toHaveBeenCalledTimes(1);
|
|
391
|
+
|
|
392
|
+
$state.set(2);
|
|
393
|
+
expect(effectFn).toHaveBeenCalledTimes(2);
|
|
394
|
+
|
|
395
|
+
$derivation.dispose();
|
|
396
|
+
|
|
397
|
+
$state.set(3);
|
|
398
|
+
expect(effectFn).toHaveBeenCalledTimes(2);
|
|
399
|
+
});
|
|
400
|
+
|
|
377
401
|
test("NOT called when disposed", () => {
|
|
378
402
|
const $state = state(1);
|
|
379
403
|
const $derivation = derivation((get) => get($state) * 2);
|
|
@@ -402,8 +426,8 @@ describe("effect", () => {
|
|
|
402
426
|
resourceCounter++;
|
|
403
427
|
return resourceCounter;
|
|
404
428
|
};
|
|
405
|
-
const $resource = resource(fetchResource
|
|
406
|
-
const $derivation = derivation((get) => get($resource) * 2);
|
|
429
|
+
const $resource = resource(fetchResource);
|
|
430
|
+
const $derivation = derivation((get) => (get($resource) ?? 0) * 2);
|
|
407
431
|
const effectFn = vi.fn();
|
|
408
432
|
effect((get) => effectFn(get($derivation)));
|
|
409
433
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { effect, signal } from "#package";
|
|
3
|
+
|
|
4
|
+
describe("effect", () => {
|
|
5
|
+
test("disposed is correct", () => {
|
|
6
|
+
const $signal = signal();
|
|
7
|
+
const effectFn = vi.fn();
|
|
8
|
+
const $effect = effect((_, watch) => {
|
|
9
|
+
watch($signal);
|
|
10
|
+
effectFn();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
expect($effect.disposed).toBe(false);
|
|
14
|
+
$effect.dispose();
|
|
15
|
+
expect($effect.disposed).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("throw when disposed twice", () => {
|
|
19
|
+
const $signal = signal();
|
|
20
|
+
const effectFn = vi.fn();
|
|
21
|
+
const $effect = effect((_, watch) => {
|
|
22
|
+
watch($signal);
|
|
23
|
+
effectFn();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
$effect.dispose();
|
|
27
|
+
expect(() => $effect.dispose()).toThrow("Effect is disposed");
|
|
28
|
+
});
|
|
29
|
+
});
|
package/test/map.test.ts
CHANGED
|
@@ -2,6 +2,20 @@ import { describe, expect, test, vi } from "vitest";
|
|
|
2
2
|
import { effect, map } from "#package";
|
|
3
3
|
|
|
4
4
|
describe("map", () => {
|
|
5
|
+
test("is initialized", () => {
|
|
6
|
+
const $map = map({
|
|
7
|
+
key1: 1,
|
|
8
|
+
key2: 2,
|
|
9
|
+
key3: 3,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect(Array.from($map.get().entries())).toEqual([
|
|
13
|
+
["key1", 1],
|
|
14
|
+
["key2", 2],
|
|
15
|
+
["key3", 3],
|
|
16
|
+
]);
|
|
17
|
+
});
|
|
18
|
+
|
|
5
19
|
test("is updated", () => {
|
|
6
20
|
const $map = map();
|
|
7
21
|
|
|
@@ -26,6 +40,30 @@ describe("map", () => {
|
|
|
26
40
|
expect($map.get().get("key2")).toBe(2);
|
|
27
41
|
expect($map.$lastDeleted.get()).toEqual({ key: "key1", value: 3 });
|
|
28
42
|
});
|
|
43
|
+
|
|
44
|
+
test("get throws when disposed", () => {
|
|
45
|
+
const $map = map({ key1: 1, key2: 2, key3: 3 });
|
|
46
|
+
$map.dispose();
|
|
47
|
+
expect(() => $map.get()).toThrowError(
|
|
48
|
+
"[PicoFlow] Primitive is disposed",
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("set throws when disposed", () => {
|
|
53
|
+
const $map = map<string, number>();
|
|
54
|
+
$map.dispose();
|
|
55
|
+
expect(() => $map.setAt("key1", 2)).toThrowError(
|
|
56
|
+
"[PicoFlow] Primitive is disposed",
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("delete throws when disposed", () => {
|
|
61
|
+
const $map = map<string, number>({ key1: 1, key2: 2, key3: 3 });
|
|
62
|
+
$map.dispose();
|
|
63
|
+
expect(() => $map.delete("key1")).toThrowError(
|
|
64
|
+
"[PicoFlow] Primitive is disposed",
|
|
65
|
+
);
|
|
66
|
+
});
|
|
29
67
|
});
|
|
30
68
|
|
|
31
69
|
describe("effect", () => {
|
package/test/resource.test.ts
CHANGED
|
@@ -9,8 +9,8 @@ describe("resource", () => {
|
|
|
9
9
|
return resourceCounter;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
const $resource = resource(fetchResource
|
|
13
|
-
expect($resource.get()).toBe(
|
|
12
|
+
const $resource = resource(fetchResource);
|
|
13
|
+
expect($resource.get()).toBe(undefined);
|
|
14
14
|
|
|
15
15
|
await $resource.fetch();
|
|
16
16
|
expect($resource.get()).toBe(1);
|
|
@@ -26,15 +26,15 @@ describe("resource", () => {
|
|
|
26
26
|
return resourceCounter;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const $resource = resource(fetchResource
|
|
30
|
-
expect($resource.get()).toBe(
|
|
29
|
+
const $resource = resource(fetchResource);
|
|
30
|
+
expect($resource.get()).toBe(undefined);
|
|
31
31
|
|
|
32
32
|
await $resource.fetch();
|
|
33
33
|
expect($resource.get()).toBe(1);
|
|
34
34
|
|
|
35
35
|
$resource.dispose();
|
|
36
36
|
await expect(() => $resource.fetch()).rejects.toThrow(
|
|
37
|
-
"
|
|
37
|
+
"[PicoFlow] Primitive is disposed",
|
|
38
38
|
);
|
|
39
39
|
});
|
|
40
40
|
|
|
@@ -45,14 +45,16 @@ describe("resource", () => {
|
|
|
45
45
|
return resourceCounter;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
const $resource = resource(fetchResource
|
|
49
|
-
expect($resource.get()).toBe(
|
|
48
|
+
const $resource = resource(fetchResource);
|
|
49
|
+
expect($resource.get()).toBe(undefined);
|
|
50
50
|
|
|
51
51
|
await $resource.fetch();
|
|
52
52
|
expect($resource.get()).toBe(1);
|
|
53
53
|
|
|
54
54
|
$resource.dispose();
|
|
55
|
-
expect(() => $resource.get()).toThrow(
|
|
55
|
+
expect(() => $resource.get()).toThrow(
|
|
56
|
+
"[PicoFlow] Primitive is disposed",
|
|
57
|
+
);
|
|
56
58
|
});
|
|
57
59
|
});
|
|
58
60
|
|
|
@@ -64,12 +66,12 @@ describe("effect", () => {
|
|
|
64
66
|
return resourceCounter;
|
|
65
67
|
};
|
|
66
68
|
|
|
67
|
-
const $resource = resource(fetchResource
|
|
69
|
+
const $resource = resource(fetchResource);
|
|
68
70
|
const effectFn = vi.fn();
|
|
69
71
|
effect((get) => effectFn(get($resource)));
|
|
70
72
|
|
|
71
73
|
expect(effectFn).toHaveBeenCalledTimes(1);
|
|
72
|
-
expect(effectFn).toHaveBeenLastCalledWith(
|
|
74
|
+
expect(effectFn).toHaveBeenLastCalledWith(undefined);
|
|
73
75
|
|
|
74
76
|
await $resource.fetch();
|
|
75
77
|
expect(effectFn).toHaveBeenCalledTimes(2);
|
|
@@ -85,19 +87,19 @@ describe("effect", () => {
|
|
|
85
87
|
return 0;
|
|
86
88
|
};
|
|
87
89
|
|
|
88
|
-
const $resource = resource(fetchResource
|
|
90
|
+
const $resource = resource(fetchResource);
|
|
89
91
|
const effectFn = vi.fn();
|
|
90
92
|
effect((get) => effectFn(get($resource)));
|
|
91
93
|
|
|
92
94
|
expect(effectFn).toHaveBeenCalledTimes(1);
|
|
93
|
-
expect(effectFn).toHaveBeenLastCalledWith(
|
|
95
|
+
expect(effectFn).toHaveBeenLastCalledWith(undefined);
|
|
94
96
|
|
|
95
97
|
await $resource.fetch();
|
|
96
|
-
expect(effectFn).toHaveBeenCalledTimes(
|
|
98
|
+
expect(effectFn).toHaveBeenCalledTimes(2);
|
|
97
99
|
expect(effectFn).toHaveBeenLastCalledWith(0);
|
|
98
100
|
|
|
99
101
|
await $resource.fetch();
|
|
100
|
-
expect(effectFn).toHaveBeenCalledTimes(
|
|
102
|
+
expect(effectFn).toHaveBeenCalledTimes(2);
|
|
101
103
|
expect(effectFn).toHaveBeenLastCalledWith(0);
|
|
102
104
|
});
|
|
103
105
|
|
|
@@ -108,12 +110,12 @@ describe("effect", () => {
|
|
|
108
110
|
return resourceCounter;
|
|
109
111
|
};
|
|
110
112
|
|
|
111
|
-
const $resource = resource(fetchResource
|
|
113
|
+
const $resource = resource(fetchResource);
|
|
112
114
|
const effectFn = vi.fn();
|
|
113
115
|
const $effect = effect((get) => effectFn(get($resource)));
|
|
114
116
|
|
|
115
117
|
expect(effectFn).toHaveBeenCalledTimes(1);
|
|
116
|
-
expect(effectFn).toHaveBeenLastCalledWith(
|
|
118
|
+
expect(effectFn).toHaveBeenLastCalledWith(undefined);
|
|
117
119
|
|
|
118
120
|
await $resource.fetch();
|
|
119
121
|
expect(effectFn).toHaveBeenCalledTimes(2);
|