@ersbeth/picoflow 1.0.0 → 1.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/.cursor/plans/unifier-flowresource-avec-flowderivation-c9506e24.plan.md +372 -0
- package/README.md +25 -171
- package/biome.json +4 -1
- package/dist/picoflow.js +1129 -661
- package/dist/types/flow/base/flowDisposable.d.ts +67 -0
- package/dist/types/flow/base/flowDisposable.d.ts.map +1 -0
- package/dist/types/flow/base/flowEffect.d.ts +127 -0
- package/dist/types/flow/base/flowEffect.d.ts.map +1 -0
- package/dist/types/flow/base/flowGraph.d.ts +97 -0
- package/dist/types/flow/base/flowGraph.d.ts.map +1 -0
- package/dist/types/flow/base/flowSignal.d.ts +134 -0
- package/dist/types/flow/base/flowSignal.d.ts.map +1 -0
- package/dist/types/flow/base/flowTracker.d.ts +15 -0
- package/dist/types/flow/base/flowTracker.d.ts.map +1 -0
- package/dist/types/flow/base/index.d.ts +7 -0
- package/dist/types/flow/base/index.d.ts.map +1 -0
- package/dist/types/flow/base/utils.d.ts +20 -0
- package/dist/types/flow/base/utils.d.ts.map +1 -0
- package/dist/types/{advanced/array.d.ts → flow/collections/flowArray.d.ts} +50 -12
- package/dist/types/flow/collections/flowArray.d.ts.map +1 -0
- package/dist/types/flow/collections/flowMap.d.ts +224 -0
- package/dist/types/flow/collections/flowMap.d.ts.map +1 -0
- package/dist/types/flow/collections/index.d.ts +3 -0
- package/dist/types/flow/collections/index.d.ts.map +1 -0
- package/dist/types/flow/index.d.ts +4 -0
- package/dist/types/flow/index.d.ts.map +1 -0
- package/dist/types/flow/nodes/async/flowConstantAsync.d.ts +137 -0
- package/dist/types/flow/nodes/async/flowConstantAsync.d.ts.map +1 -0
- package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts +137 -0
- package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts.map +1 -0
- package/dist/types/flow/nodes/async/flowNodeAsync.d.ts +343 -0
- package/dist/types/flow/nodes/async/flowNodeAsync.d.ts.map +1 -0
- package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts +81 -0
- package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts.map +1 -0
- package/dist/types/flow/nodes/async/flowStateAsync.d.ts +111 -0
- package/dist/types/flow/nodes/async/flowStateAsync.d.ts.map +1 -0
- package/dist/types/flow/nodes/async/index.d.ts +6 -0
- package/dist/types/flow/nodes/async/index.d.ts.map +1 -0
- package/dist/types/flow/nodes/index.d.ts +3 -0
- package/dist/types/flow/nodes/index.d.ts.map +1 -0
- package/dist/types/flow/nodes/sync/flowConstant.d.ts +108 -0
- package/dist/types/flow/nodes/sync/flowConstant.d.ts.map +1 -0
- package/dist/types/flow/nodes/sync/flowDerivation.d.ts +100 -0
- package/dist/types/flow/nodes/sync/flowDerivation.d.ts.map +1 -0
- package/dist/types/flow/nodes/sync/flowNode.d.ts +314 -0
- package/dist/types/flow/nodes/sync/flowNode.d.ts.map +1 -0
- package/dist/types/flow/nodes/sync/flowReadonly.d.ts +57 -0
- package/dist/types/flow/nodes/sync/flowReadonly.d.ts.map +1 -0
- package/dist/types/flow/nodes/sync/flowState.d.ts +96 -0
- package/dist/types/flow/nodes/sync/flowState.d.ts.map +1 -0
- package/dist/types/flow/nodes/sync/index.d.ts +6 -0
- package/dist/types/flow/nodes/sync/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/solid/converters.d.ts +34 -44
- package/dist/types/solid/converters.d.ts.map +1 -1
- package/dist/types/solid/primitives.d.ts +1 -0
- package/dist/types/solid/primitives.d.ts.map +1 -1
- package/docs/.vitepress/config.mts +1 -1
- package/docs/api/typedoc-sidebar.json +81 -1
- package/package.json +60 -58
- package/src/flow/base/flowDisposable.ts +71 -0
- package/src/flow/base/flowEffect.ts +171 -0
- package/src/flow/base/flowGraph.ts +288 -0
- package/src/flow/base/flowSignal.ts +207 -0
- package/src/flow/base/flowTracker.ts +17 -0
- package/src/flow/base/index.ts +6 -0
- package/src/flow/base/utils.ts +19 -0
- package/src/flow/collections/flowArray.ts +409 -0
- package/src/flow/collections/flowMap.ts +398 -0
- package/src/flow/collections/index.ts +2 -0
- package/src/flow/index.ts +3 -0
- package/src/flow/nodes/async/flowConstantAsync.ts +142 -0
- package/src/flow/nodes/async/flowDerivationAsync.ts +143 -0
- package/src/flow/nodes/async/flowNodeAsync.ts +474 -0
- package/src/flow/nodes/async/flowReadonlyAsync.ts +81 -0
- package/src/flow/nodes/async/flowStateAsync.ts +116 -0
- package/src/flow/nodes/async/index.ts +5 -0
- package/src/flow/nodes/await/advanced/index.ts +5 -0
- package/src/{advanced → flow/nodes/await/advanced}/resource.ts +37 -3
- package/src/{advanced → flow/nodes/await/advanced}/resourceAsync.ts +35 -3
- package/src/{advanced → flow/nodes/await/advanced}/stream.ts +40 -2
- package/src/{advanced → flow/nodes/await/advanced}/streamAsync.ts +38 -3
- package/src/flow/nodes/await/flowConstantAwait.ts +154 -0
- package/src/flow/nodes/await/flowDerivationAwait.ts +154 -0
- package/src/flow/nodes/await/flowNodeAwait.ts +508 -0
- package/src/flow/nodes/await/flowReadonlyAwait.ts +89 -0
- package/src/flow/nodes/await/flowStateAwait.ts +130 -0
- package/src/flow/nodes/await/index.ts +5 -0
- package/src/flow/nodes/index.ts +3 -0
- package/src/flow/nodes/sync/flowConstant.ts +111 -0
- package/src/flow/nodes/sync/flowDerivation.ts +105 -0
- package/src/flow/nodes/sync/flowNode.ts +439 -0
- package/src/flow/nodes/sync/flowReadonly.ts +57 -0
- package/src/flow/nodes/sync/flowState.ts +101 -0
- package/src/flow/nodes/sync/index.ts +5 -0
- package/src/index.ts +1 -47
- package/src/solid/converters.ts +59 -198
- package/src/solid/primitives.ts +4 -0
- package/test/base/flowEffect.test.ts +108 -0
- package/test/base/flowGraph.test.ts +485 -0
- package/test/base/flowSignal.test.ts +372 -0
- package/test/collections/flowArray.asyncStates.test.ts +1553 -0
- package/test/collections/flowArray.scalars.test.ts +1129 -0
- package/test/collections/flowArray.states.test.ts +1365 -0
- package/test/collections/flowMap.asyncStates.test.ts +1105 -0
- package/test/collections/flowMap.scalars.test.ts +877 -0
- package/test/collections/flowMap.states.test.ts +1097 -0
- package/test/nodes/async/flowConstantAsync.test.ts +860 -0
- package/test/nodes/async/flowDerivationAsync.test.ts +1517 -0
- package/test/nodes/async/flowStateAsync.test.ts +1387 -0
- package/test/{resource.test.ts → nodes/await/advanced/resource.test.ts} +21 -19
- package/test/{resourceAsync.test.ts → nodes/await/advanced/resourceAsync.test.ts} +3 -1
- package/test/{stream.test.ts → nodes/await/advanced/stream.test.ts} +30 -28
- package/test/{streamAsync.test.ts → nodes/await/advanced/streamAsync.test.ts} +16 -14
- package/test/nodes/await/flowConstantAwait.test.ts +643 -0
- package/test/nodes/await/flowDerivationAwait.test.ts +1583 -0
- package/test/nodes/await/flowStateAwait.test.ts +999 -0
- package/test/nodes/mixed/derivation.test.ts +1527 -0
- package/test/nodes/sync/flowConstant.test.ts +620 -0
- package/test/nodes/sync/flowDerivation.test.ts +1373 -0
- package/test/nodes/sync/flowState.test.ts +945 -0
- package/test/solid/converters.test.ts +721 -0
- package/test/solid/primitives.test.ts +1031 -0
- package/tsconfig.json +2 -1
- package/vitest.config.ts +7 -1
- package/IMPLEMENTATION_GUIDE.md +0 -1578
- package/dist/types/advanced/array.d.ts.map +0 -1
- package/dist/types/advanced/index.d.ts +0 -9
- package/dist/types/advanced/index.d.ts.map +0 -1
- package/dist/types/advanced/map.d.ts +0 -166
- package/dist/types/advanced/map.d.ts.map +0 -1
- package/dist/types/advanced/resource.d.ts +0 -78
- package/dist/types/advanced/resource.d.ts.map +0 -1
- package/dist/types/advanced/resourceAsync.d.ts +0 -56
- package/dist/types/advanced/resourceAsync.d.ts.map +0 -1
- package/dist/types/advanced/stream.d.ts +0 -117
- package/dist/types/advanced/stream.d.ts.map +0 -1
- package/dist/types/advanced/streamAsync.d.ts +0 -97
- package/dist/types/advanced/streamAsync.d.ts.map +0 -1
- package/dist/types/basic/constant.d.ts +0 -60
- package/dist/types/basic/constant.d.ts.map +0 -1
- package/dist/types/basic/derivation.d.ts +0 -89
- package/dist/types/basic/derivation.d.ts.map +0 -1
- package/dist/types/basic/disposable.d.ts +0 -82
- package/dist/types/basic/disposable.d.ts.map +0 -1
- package/dist/types/basic/effect.d.ts +0 -67
- package/dist/types/basic/effect.d.ts.map +0 -1
- package/dist/types/basic/index.d.ts +0 -10
- package/dist/types/basic/index.d.ts.map +0 -1
- package/dist/types/basic/observable.d.ts +0 -83
- package/dist/types/basic/observable.d.ts.map +0 -1
- package/dist/types/basic/signal.d.ts +0 -69
- package/dist/types/basic/signal.d.ts.map +0 -1
- package/dist/types/basic/state.d.ts +0 -47
- package/dist/types/basic/state.d.ts.map +0 -1
- package/dist/types/basic/trackingContext.d.ts +0 -33
- package/dist/types/basic/trackingContext.d.ts.map +0 -1
- package/dist/types/creators.d.ts +0 -340
- package/dist/types/creators.d.ts.map +0 -1
- package/src/advanced/array.ts +0 -222
- package/src/advanced/index.ts +0 -12
- package/src/advanced/map.ts +0 -193
- package/src/basic/constant.ts +0 -97
- package/src/basic/derivation.ts +0 -147
- package/src/basic/disposable.ts +0 -86
- package/src/basic/effect.ts +0 -104
- package/src/basic/index.ts +0 -9
- package/src/basic/observable.ts +0 -109
- package/src/basic/signal.ts +0 -145
- package/src/basic/state.ts +0 -60
- package/src/basic/trackingContext.ts +0 -45
- package/src/creators.ts +0 -395
- package/test/array.test.ts +0 -600
- package/test/constant.test.ts +0 -44
- package/test/derivation.test.ts +0 -539
- package/test/effect.test.ts +0 -29
- package/test/map.test.ts +0 -240
- package/test/signal.test.ts +0 -72
- package/test/state.test.ts +0 -212
package/src/solid/converters.ts
CHANGED
|
@@ -1,63 +1,34 @@
|
|
|
1
1
|
import { onCleanup, onMount } from "solid-js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "../basic";
|
|
8
|
-
import { type SolidDerivation, SolidResource, SolidState } from "./primitives";
|
|
2
|
+
import { FlowNode, type FlowReadonly, type FlowReadonlyAsync } from "../flow";
|
|
3
|
+
import { FlowEffect } from "../flow/base";
|
|
4
|
+
import type { FlowTracker } from "../flow/base/flowTracker";
|
|
5
|
+
import { FlowNodeAsync } from "../flow/nodes/async/flowNodeAsync";
|
|
6
|
+
import { SolidResource } from "./primitives";
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
|
-
* Converts
|
|
9
|
+
* Converts an asynchronous FlowNodeAsync (Promise-based) into a SolidJS resource.
|
|
12
10
|
*
|
|
13
|
-
* @param
|
|
14
|
-
* @returns A
|
|
11
|
+
* @param node - The FlowNodeAsync to convert.
|
|
12
|
+
* @returns A SolidResource that wraps the FlowNodeAsync's value.
|
|
15
13
|
*
|
|
16
14
|
* @remarks
|
|
17
|
-
* This internal helper bridges PicoFlow's reactive system with SolidJS by
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* 3. Properly disposing the effect when the Solid component unmounts
|
|
15
|
+
* This internal helper bridges PicoFlow's async reactive system with SolidJS by creating
|
|
16
|
+
* a SolidResource that automatically tracks the FlowNodeAsync and updates when its value changes.
|
|
17
|
+
* The resource is properly cleaned up when the Solid component unmounts.
|
|
21
18
|
*
|
|
22
19
|
* @internal
|
|
23
20
|
*/
|
|
24
|
-
function
|
|
25
|
-
|
|
21
|
+
function fromNode<T>(node: FlowNodeAsync<T> | FlowNode<T>): SolidResource<T> {
|
|
22
|
+
let initialized = false;
|
|
26
23
|
|
|
27
|
-
let fx: FlowEffect;
|
|
28
|
-
|
|
29
|
-
onMount(() => {
|
|
30
|
-
fx = new FlowEffect((t) => {
|
|
31
|
-
const value = state.get(t);
|
|
32
|
-
solidState.set(() => value);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
onCleanup(() => fx.dispose());
|
|
37
|
-
|
|
38
|
-
return solidState;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Converts an asynchronous FlowObservable (Promise-based) into a SolidJS resource.
|
|
43
|
-
*
|
|
44
|
-
* @param derivation - The FlowObservable that resolves to a Promise.
|
|
45
|
-
* @returns A SolidResource that mirrors the FlowObservable's async value.
|
|
46
|
-
*
|
|
47
|
-
* @remarks
|
|
48
|
-
* This internal helper bridges PicoFlow's async reactive system with SolidJS by:
|
|
49
|
-
* 1. Creating a SolidResource with an initial fetch using `pick()` (untracked)
|
|
50
|
-
* 2. Setting up a FlowEffect that tracks the Promise observable
|
|
51
|
-
* 3. Refetching the SolidResource whenever the Promise observable changes
|
|
52
|
-
* 4. Properly disposing the effect when the Solid component unmounts
|
|
53
|
-
*
|
|
54
|
-
* @internal
|
|
55
|
-
*/
|
|
56
|
-
function fromAsync<T>(
|
|
57
|
-
derivation: FlowObservable<Promise<T>>,
|
|
58
|
-
): SolidResource<T> {
|
|
59
24
|
const solidResource = new SolidResource<T>(async () => {
|
|
60
|
-
|
|
25
|
+
let value: T;
|
|
26
|
+
if (initialized) {
|
|
27
|
+
value = await node.get(null); // don't request graph read
|
|
28
|
+
} else {
|
|
29
|
+
value = await node.pick();
|
|
30
|
+
initialized = true;
|
|
31
|
+
}
|
|
61
32
|
return value;
|
|
62
33
|
});
|
|
63
34
|
|
|
@@ -65,7 +36,7 @@ function fromAsync<T>(
|
|
|
65
36
|
|
|
66
37
|
onMount(() => {
|
|
67
38
|
fx = new FlowEffect(async (t) => {
|
|
68
|
-
|
|
39
|
+
node.watch(t);
|
|
69
40
|
solidResource.refetch();
|
|
70
41
|
});
|
|
71
42
|
});
|
|
@@ -76,68 +47,27 @@ function fromAsync<T>(
|
|
|
76
47
|
}
|
|
77
48
|
|
|
78
49
|
/**
|
|
79
|
-
* Converts a
|
|
50
|
+
* Converts a getter function into a SolidResource.
|
|
80
51
|
*
|
|
81
|
-
* @param
|
|
82
|
-
* @returns A
|
|
52
|
+
* @param getter - A function that computes a value using a FlowTracker.
|
|
53
|
+
* @returns A SolidResource that wraps the computed value.
|
|
83
54
|
*
|
|
84
55
|
* @remarks
|
|
85
|
-
* This internal helper performs "
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
56
|
+
* This internal helper performs "deep" conversion by creating a FlowNodeAsync from
|
|
57
|
+
* the getter function and then converting it to a SolidResource. This allows users
|
|
58
|
+
* to pass computation functions directly to `from()` without manually creating a
|
|
59
|
+
* FlowNodeAsync first.
|
|
89
60
|
*
|
|
90
61
|
* @internal
|
|
91
62
|
*/
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
): SolidDerivation<T> | SolidResource<T> {
|
|
97
|
-
const initialValue = flow.pick();
|
|
98
|
-
const isAsync = initialValue instanceof Promise;
|
|
99
|
-
if (isAsync) {
|
|
100
|
-
return fromAsync(flow as FlowObservable<Promise<T>>);
|
|
101
|
-
}
|
|
102
|
-
return fromSync(flow as FlowObservable<T>);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Converts a getter function with TrackingContext into a Solid primitive (signal or resource).
|
|
107
|
-
*
|
|
108
|
-
* @param getter - A function that computes a value using a TrackingContext.
|
|
109
|
-
* @returns A SolidDerivation for sync values or SolidResource for async values.
|
|
110
|
-
*
|
|
111
|
-
* @remarks
|
|
112
|
-
* This internal helper performs "deep" conversion by:
|
|
113
|
-
* 1. Creating a FlowDerivation from the getter function
|
|
114
|
-
* 2. Inspecting the initial computed value to determine if it's a Promise
|
|
115
|
-
* 3. Delegating to the appropriate converter (fromSync or fromAsync)
|
|
116
|
-
*
|
|
117
|
-
* This allows users to pass computation functions directly to `from()` without
|
|
118
|
-
* manually creating a FlowDerivation first.
|
|
119
|
-
*
|
|
120
|
-
* @internal
|
|
121
|
-
*/
|
|
122
|
-
function deepFrom<T>(getter: (t: TrackingContext) => T): SolidDerivation<T>;
|
|
123
|
-
function deepFrom<T>(
|
|
124
|
-
getter: (t: TrackingContext) => Promise<T>,
|
|
125
|
-
): SolidResource<T>;
|
|
126
|
-
function deepFrom<T>(
|
|
127
|
-
getter: (t: TrackingContext) => T | Promise<T>,
|
|
128
|
-
): SolidDerivation<T> | SolidResource<T> {
|
|
129
|
-
const derivation = new FlowDerivation((t) => {
|
|
63
|
+
function fromGetter<T>(
|
|
64
|
+
getter: (t: FlowTracker) => T | Promise<T>,
|
|
65
|
+
): SolidResource<T> {
|
|
66
|
+
const derivation = new FlowNodeAsync<T>(async (t) => {
|
|
130
67
|
return getter(t);
|
|
131
68
|
});
|
|
132
69
|
|
|
133
|
-
|
|
134
|
-
const isAsync = initialValue instanceof Promise;
|
|
135
|
-
|
|
136
|
-
if (isAsync) {
|
|
137
|
-
return fromAsync(derivation as FlowObservable<Promise<T>>);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return fromSync(derivation as FlowObservable<T>);
|
|
70
|
+
return fromNode(derivation);
|
|
141
71
|
}
|
|
142
72
|
|
|
143
73
|
/**
|
|
@@ -149,91 +79,23 @@ function deepFrom<T>(
|
|
|
149
79
|
export type NotPromise<T> = T extends Promise<unknown> ? never : T;
|
|
150
80
|
|
|
151
81
|
/**
|
|
152
|
-
* Converts a
|
|
82
|
+
* Converts a FlowNode, FlowNodeAsync, or getter function into a SolidResource.
|
|
153
83
|
*
|
|
154
|
-
* @param flow - The
|
|
155
|
-
* @returns A SolidResource
|
|
156
|
-
*
|
|
157
|
-
* @public
|
|
158
|
-
*/
|
|
159
|
-
export function from<T>(
|
|
160
|
-
flow: FlowObservable<Promise<NotPromise<T>>>,
|
|
161
|
-
): SolidResource<NotPromise<T>>;
|
|
162
|
-
/**
|
|
163
|
-
* Converts a FlowObservable of a non-Promise value into a SolidDerivation.
|
|
164
|
-
*
|
|
165
|
-
* @param flow - The FlowObservable to convert.
|
|
166
|
-
* @returns A SolidDerivation wrapping the observable.
|
|
167
|
-
*
|
|
168
|
-
* @public
|
|
169
|
-
*/
|
|
170
|
-
export function from<T>(
|
|
171
|
-
flow: FlowObservable<NotPromise<T>>,
|
|
172
|
-
): SolidDerivation<NotPromise<T>>;
|
|
173
|
-
/**
|
|
174
|
-
* Converts a FlowObservable into a Solid derivation or resource, depending on whether the value is synchronous or asynchronous.
|
|
175
|
-
*
|
|
176
|
-
* @param flow - The FlowObservable to convert.
|
|
177
|
-
* @returns A SolidDerivation or SolidResource, depending on the input type.
|
|
178
|
-
*
|
|
179
|
-
* @public
|
|
180
|
-
*/
|
|
181
|
-
export function from<T>(
|
|
182
|
-
flow: FlowObservable<Promise<NotPromise<T>>> | FlowObservable<NotPromise<T>>,
|
|
183
|
-
): SolidDerivation<NotPromise<T>> | SolidResource<NotPromise<T>>;
|
|
184
|
-
/**
|
|
185
|
-
* Converts a getter function returning a non-Promise value into a SolidDerivation.
|
|
186
|
-
*
|
|
187
|
-
* @param flow - The getter function to convert.
|
|
188
|
-
* @returns A SolidDerivation wrapping the getter.
|
|
189
|
-
*
|
|
190
|
-
* @public
|
|
191
|
-
*/
|
|
192
|
-
export function from<T>(
|
|
193
|
-
flow: (t: TrackingContext) => NotPromise<T>,
|
|
194
|
-
): SolidDerivation<NotPromise<T>>;
|
|
195
|
-
/**
|
|
196
|
-
* Converts a getter function returning a Promise into a SolidResource.
|
|
197
|
-
*
|
|
198
|
-
* @param flow - The getter function to convert.
|
|
199
|
-
* @returns A SolidResource wrapping the getter.
|
|
200
|
-
*
|
|
201
|
-
* @public
|
|
202
|
-
*/
|
|
203
|
-
export function from<T>(
|
|
204
|
-
flow: (t: TrackingContext) => Promise<NotPromise<T>>,
|
|
205
|
-
): SolidResource<NotPromise<T>>;
|
|
206
|
-
/**
|
|
207
|
-
* Converts a getter function into a Solid derivation or resource, depending on whether the returned value is synchronous or asynchronous.
|
|
208
|
-
*
|
|
209
|
-
* @param flow - The getter function to convert.
|
|
210
|
-
* @returns A SolidDerivation or SolidResource, depending on the input type.
|
|
211
|
-
*
|
|
212
|
-
* @public
|
|
213
|
-
*/
|
|
214
|
-
export function from<T>(
|
|
215
|
-
flow:
|
|
216
|
-
| ((t: TrackingContext) => NotPromise<T>)
|
|
217
|
-
| ((t: TrackingContext) => Promise<NotPromise<T>>),
|
|
218
|
-
): SolidDerivation<T> | SolidResource<T>;
|
|
219
|
-
/**
|
|
220
|
-
* Converts a FlowObservable or getter function into a Solid derivation or resource, depending on whether the value is synchronous or asynchronous.
|
|
221
|
-
*
|
|
222
|
-
* @param flow - The FlowObservable or getter function to convert.
|
|
223
|
-
* @returns A SolidDerivation or SolidResource, depending on the input type.
|
|
84
|
+
* @param flow - The FlowNode, FlowNodeAsync, or getter function to convert.
|
|
85
|
+
* @returns A SolidResource that wraps the value or computation.
|
|
224
86
|
*
|
|
225
87
|
* @remarks
|
|
226
88
|
* This function bridges PicoFlow's reactive system with SolidJS, allowing you to use
|
|
227
|
-
* PicoFlow
|
|
228
|
-
*
|
|
89
|
+
* PicoFlow nodes and computations within Solid components. All conversions return a
|
|
90
|
+
* `SolidResource`, which can handle both synchronous and asynchronous values seamlessly.
|
|
229
91
|
*
|
|
230
|
-
*
|
|
231
|
-
* - **
|
|
232
|
-
* - **
|
|
233
|
-
* - **Getter
|
|
92
|
+
* The conversion works with:
|
|
93
|
+
* - **FlowNode**: Synchronous reactive nodes (e.g., from `state()` or `derivation()`)
|
|
94
|
+
* - **FlowNodeAsync**: Asynchronous reactive nodes (e.g., from `resourceAsync()`)
|
|
95
|
+
* - **Getter functions**: Computation functions that use a FlowTracker to access reactive values
|
|
234
96
|
*
|
|
235
|
-
* The created
|
|
236
|
-
*
|
|
97
|
+
* The created SolidResource automatically subscribes to the PicoFlow nodes and updates
|
|
98
|
+
* when their values change. The subscription is properly cleaned up when the Solid
|
|
237
99
|
* component unmounts.
|
|
238
100
|
*
|
|
239
101
|
* @example
|
|
@@ -241,19 +103,18 @@ export function from<T>(
|
|
|
241
103
|
* import { from } from 'picoflow/solid';
|
|
242
104
|
* import { state } from 'picoflow';
|
|
243
105
|
*
|
|
244
|
-
* // Convert a PicoFlow state to a Solid
|
|
106
|
+
* // Convert a PicoFlow state to a Solid resource
|
|
245
107
|
* const $count = state(0);
|
|
246
108
|
* const solidCount = from($count);
|
|
247
109
|
*
|
|
248
110
|
* // Use in a Solid component
|
|
249
111
|
* function Counter() {
|
|
250
|
-
*
|
|
251
|
-
* return <div>Count: {count}</div>;
|
|
112
|
+
* return <div>Count: {solidCount.get()}</div>;
|
|
252
113
|
* }
|
|
253
114
|
*
|
|
254
115
|
* // Or convert a computation function
|
|
255
116
|
* const solidDerived = from((t) => {
|
|
256
|
-
* return
|
|
117
|
+
* return solidCount.get(t) * 2;
|
|
257
118
|
* });
|
|
258
119
|
* ```
|
|
259
120
|
*
|
|
@@ -261,18 +122,18 @@ export function from<T>(
|
|
|
261
122
|
*/
|
|
262
123
|
export function from<T>(
|
|
263
124
|
flow:
|
|
264
|
-
|
|
|
265
|
-
|
|
|
266
|
-
| ((t:
|
|
267
|
-
| ((t:
|
|
268
|
-
):
|
|
269
|
-
if (flow instanceof
|
|
270
|
-
return
|
|
271
|
-
|
|
272
|
-
|
|
125
|
+
| FlowReadonlyAsync<T>
|
|
126
|
+
| FlowReadonly<T>
|
|
127
|
+
| ((t: FlowTracker) => T)
|
|
128
|
+
| ((t: FlowTracker) => Promise<T>),
|
|
129
|
+
): SolidResource<T> {
|
|
130
|
+
if (flow instanceof FlowNodeAsync || flow instanceof FlowNode) {
|
|
131
|
+
return fromNode(flow);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (typeof flow === "function") {
|
|
135
|
+
return fromGetter(flow);
|
|
273
136
|
}
|
|
274
137
|
|
|
275
|
-
|
|
276
|
-
| SolidDerivation<T>
|
|
277
|
-
| SolidResource<T>;
|
|
138
|
+
throw new Error("Invalid flow type");
|
|
278
139
|
}
|
package/src/solid/primitives.ts
CHANGED
|
@@ -197,6 +197,8 @@ export class SolidResource<T> implements SolidObservable<T | undefined> {
|
|
|
197
197
|
*/
|
|
198
198
|
readonly refetch: () => void;
|
|
199
199
|
|
|
200
|
+
readonly set: (param: SolidGetter<T>) => void;
|
|
201
|
+
|
|
200
202
|
/**
|
|
201
203
|
* Creates a new SolidResource from a fetcher function.
|
|
202
204
|
* @param fetcher - The async fetcher function.
|
|
@@ -207,5 +209,7 @@ export class SolidResource<T> implements SolidObservable<T | undefined> {
|
|
|
207
209
|
this.state = () => get.state;
|
|
208
210
|
this.latest = () => get.latest;
|
|
209
211
|
this.refetch = () => set.refetch();
|
|
212
|
+
//@ts-expect-error - SolidGetter is not assignable to parameter of type 'Exclude<T, Function> | ((prev: T | undefined) => T)'
|
|
213
|
+
this.set = (value: SolidGetter<T>) => set.mutate(value);
|
|
210
214
|
}
|
|
211
215
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { effect } from "#package";
|
|
3
|
+
|
|
4
|
+
describe("FlowEffect", () => {
|
|
5
|
+
describe("disposal", () => {
|
|
6
|
+
it("should have disposed property set to false initially and true after disposal", () => {
|
|
7
|
+
const effectFn = vi.fn();
|
|
8
|
+
const $effect = effect(() => {
|
|
9
|
+
effectFn();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect($effect.disposed).toBe(false);
|
|
13
|
+
$effect.dispose();
|
|
14
|
+
expect($effect.disposed).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should throw error when disposed twice", () => {
|
|
18
|
+
const effectFn = vi.fn();
|
|
19
|
+
const $effect = effect(() => {
|
|
20
|
+
effectFn();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
$effect.dispose();
|
|
24
|
+
expect(() => $effect.dispose()).toThrow("Effect is disposed");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("execution", () => {
|
|
29
|
+
it("should execute immediately upon creation", async () => {
|
|
30
|
+
const effectFn = vi.fn();
|
|
31
|
+
effect(() => {
|
|
32
|
+
effectFn();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should execute callback with tracking context as parameter", async () => {
|
|
39
|
+
const effectFn = vi.fn();
|
|
40
|
+
let receivedContext: unknown = null;
|
|
41
|
+
|
|
42
|
+
const $effect = effect((t) => {
|
|
43
|
+
receivedContext = t;
|
|
44
|
+
effectFn();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
|
|
48
|
+
expect(receivedContext).toBe($effect);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("async support", () => {
|
|
53
|
+
it("should support async callback functions", async () => {
|
|
54
|
+
const effectFn = vi.fn();
|
|
55
|
+
let asyncCompleted = false;
|
|
56
|
+
|
|
57
|
+
effect(async () => {
|
|
58
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
59
|
+
asyncCompleted = true;
|
|
60
|
+
effectFn();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
|
|
64
|
+
expect(asyncCompleted).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should await async callback before completing", async () => {
|
|
68
|
+
const executionOrder: string[] = [];
|
|
69
|
+
const effectFn = vi.fn();
|
|
70
|
+
|
|
71
|
+
effect(async () => {
|
|
72
|
+
executionOrder.push("start");
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
74
|
+
executionOrder.push("async-complete");
|
|
75
|
+
effectFn();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
executionOrder.push("after-effect-creation");
|
|
79
|
+
|
|
80
|
+
await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
|
|
81
|
+
expect(executionOrder).toEqual([
|
|
82
|
+
"start",
|
|
83
|
+
"after-effect-creation",
|
|
84
|
+
"async-complete",
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("error handling", () => {
|
|
90
|
+
it("should throw error when trying to execute after disposal", async () => {
|
|
91
|
+
const effectFn = vi.fn();
|
|
92
|
+
const $effect = effect(() => {
|
|
93
|
+
effectFn();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
|
|
97
|
+
|
|
98
|
+
$effect.dispose();
|
|
99
|
+
|
|
100
|
+
// The internal _exec method should throw when called after disposal
|
|
101
|
+
// This is tested indirectly - if we try to trigger execution after disposal,
|
|
102
|
+
// it should fail. However, since we're testing in isolation without dependencies,
|
|
103
|
+
// we verify that the disposed state prevents further execution
|
|
104
|
+
expect($effect.disposed).toBe(true);
|
|
105
|
+
expect(() => $effect.dispose()).toThrow("Effect is disposed");
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|