@ersbeth/picoflow 1.0.1 → 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 +17 -1
- 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/advanced/map.ts
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { FlowState, isDisposable } from "../basic/";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Represents a reactive map that extends {@link FlowState} for tracking key-value pairs.
|
|
5
|
-
*
|
|
6
|
-
* @remarks
|
|
7
|
-
* FlowMap wraps a native JavaScript Map and provides reactive tracking at multiple granularity levels.
|
|
8
|
-
* Unlike plain reactive state, FlowMap offers fine-grained reactivity that lets you track:
|
|
9
|
-
*
|
|
10
|
-
* 1. **Whole map changes**: Via `get(t)` or `pick()` on the FlowMap itself
|
|
11
|
-
* 2. **Last add operation**: Via the `$lastAdded` signal, track which key-value pair was most recently added
|
|
12
|
-
* 3. **Last update operation**: Via the `$lastUpdated` signal, track which key-value pair was most recently updated
|
|
13
|
-
* 4. **Last delete operation**: Via the `$lastDeleted` signal, track which key-value pair was most recently removed
|
|
14
|
-
*
|
|
15
|
-
* **Reactive Signals:**
|
|
16
|
-
* - **$lastAdded**: A FlowState containing `{ key?: K, value?: V }` updated on each `add()` call
|
|
17
|
-
* - **$lastUpdated**: A FlowState containing `{ key?: K, value?: V }` updated on each `update()` call
|
|
18
|
-
* - **$lastDeleted**: A FlowState containing `{ key?: K, value?: V }` updated on each `delete()` call
|
|
19
|
-
*
|
|
20
|
-
* These signals enable fine-grained reactivity patterns where effects can respond to specific
|
|
21
|
-
* map operations without re-processing the entire map.
|
|
22
|
-
*
|
|
23
|
-
* **Use Cases:**
|
|
24
|
-
* - Entity stores where you want to track additions/removals separately
|
|
25
|
-
* - Cache implementations with granular invalidation
|
|
26
|
-
* - Collections where operations on individual keys matter
|
|
27
|
-
* - UI state where you want to animate specific additions or removals
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```typescript
|
|
31
|
-
* const $users = map<string, User>();
|
|
32
|
-
*
|
|
33
|
-
* // Track the whole map
|
|
34
|
-
* effect((t) => {
|
|
35
|
-
* const users = $users.get(t);
|
|
36
|
-
* console.log(`Total users: ${users.size}`);
|
|
37
|
-
* });
|
|
38
|
-
*
|
|
39
|
-
* // Track only additions
|
|
40
|
-
* effect((t) => {
|
|
41
|
-
* const { key, value } = $users.$lastAdded.get(t);
|
|
42
|
-
* if (key && value) {
|
|
43
|
-
* console.log(`User ${key} was added:`, value);
|
|
44
|
-
* }
|
|
45
|
-
* });
|
|
46
|
-
*
|
|
47
|
-
* // Track only updates
|
|
48
|
-
* effect((t) => {
|
|
49
|
-
* const { key, value } = $users.$lastUpdated.get(t);
|
|
50
|
-
* if (key && value) {
|
|
51
|
-
* console.log(`User ${key} was updated:`, value);
|
|
52
|
-
* }
|
|
53
|
-
* });
|
|
54
|
-
*
|
|
55
|
-
* // Track only deletions
|
|
56
|
-
* effect((t) => {
|
|
57
|
-
* const { key, value } = $users.$lastDeleted.get(t);
|
|
58
|
-
* if (key && value) {
|
|
59
|
-
* console.log(`User ${key} was deleted:`, value);
|
|
60
|
-
* }
|
|
61
|
-
* });
|
|
62
|
-
*
|
|
63
|
-
* // Modify the map
|
|
64
|
-
* $users.add('user1', { name: 'John', age: 30 });
|
|
65
|
-
* $users.add('user2', { name: 'Jane', age: 25 });
|
|
66
|
-
* $users.update('user1', { name: 'John', age: 31 });
|
|
67
|
-
* $users.delete('user1');
|
|
68
|
-
* ```
|
|
69
|
-
*
|
|
70
|
-
* @typeParam K - The type of the map keys.
|
|
71
|
-
* @typeParam V - The type of the map values.
|
|
72
|
-
*
|
|
73
|
-
* @public
|
|
74
|
-
*/
|
|
75
|
-
export class FlowMap<K, V> extends FlowState<Map<K, V>> {
|
|
76
|
-
/**
|
|
77
|
-
* A reactive state that holds the most recent key and value that were added.
|
|
78
|
-
*
|
|
79
|
-
* @remarks
|
|
80
|
-
* When a key is added via {@link FlowMap.add}, this state is updated with
|
|
81
|
-
* the corresponding key and value.
|
|
82
|
-
*
|
|
83
|
-
* @public
|
|
84
|
-
*/
|
|
85
|
-
public $lastAdded = new FlowState<{ key: K; value: V } | null>(null);
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* A reactive state that holds the most recent key and value that were updated.
|
|
89
|
-
*
|
|
90
|
-
* @remarks
|
|
91
|
-
* When a key is updated via {@link FlowMap.update}, this state is updated with
|
|
92
|
-
* the corresponding key and value.
|
|
93
|
-
*
|
|
94
|
-
* @public
|
|
95
|
-
*/
|
|
96
|
-
public $lastUpdated = new FlowState<{ key: K; value: V } | null>(null);
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* A reactive state that holds the most recent key and value that were deleted.
|
|
100
|
-
*
|
|
101
|
-
* @remarks
|
|
102
|
-
* When a key is deleted via {@link FlowMap.delete}, this state is updated with
|
|
103
|
-
* the corresponding key and its last known value.
|
|
104
|
-
*
|
|
105
|
-
* @public
|
|
106
|
-
*/
|
|
107
|
-
public $lastDeleted = new FlowState<{ key: K; value: V } | null>(null);
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Adds a new key-value pair to the map.
|
|
111
|
-
*
|
|
112
|
-
* @param key - The key to add.
|
|
113
|
-
* @param value - The value to associate with the key.
|
|
114
|
-
* @throws If the FlowMap instance is disposed.
|
|
115
|
-
* @throws If the key already exists in the map.
|
|
116
|
-
*
|
|
117
|
-
* @remarks
|
|
118
|
-
* Adds a new entry to the internal map, emits the key-value pair via {@link FlowMap.$lastAdded},
|
|
119
|
-
* and notifies all subscribers of the change.
|
|
120
|
-
*
|
|
121
|
-
* @public
|
|
122
|
-
*/
|
|
123
|
-
public add(key: K, value: V): void {
|
|
124
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
125
|
-
if (this._value.has(key)) {
|
|
126
|
-
throw new Error("[PicoFlow] Key already exists");
|
|
127
|
-
}
|
|
128
|
-
this._value.set(key, value);
|
|
129
|
-
this.$lastAdded.set({ key, value });
|
|
130
|
-
this._notify();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Updates an existing key-value pair in the map.
|
|
135
|
-
*
|
|
136
|
-
* @param key - The key to update.
|
|
137
|
-
* @param value - The new value to associate with the key.
|
|
138
|
-
* @throws If the FlowMap instance is disposed.
|
|
139
|
-
* @throws If the key does not exist in the map.
|
|
140
|
-
*
|
|
141
|
-
* @remarks
|
|
142
|
-
* Updates an existing entry in the internal map, emits the key-value pair via {@link FlowMap.$lastUpdated},
|
|
143
|
-
* and notifies all subscribers of the change.
|
|
144
|
-
*
|
|
145
|
-
* @public
|
|
146
|
-
*/
|
|
147
|
-
public update(key: K, value: V): void {
|
|
148
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
149
|
-
if (!this._value.has(key)) {
|
|
150
|
-
throw new Error("[PicoFlow] Key does not exist");
|
|
151
|
-
}
|
|
152
|
-
this._value.set(key, value);
|
|
153
|
-
this.$lastUpdated.set({ key, value });
|
|
154
|
-
this._notify();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Deletes the value at the specified key from the underlying map.
|
|
159
|
-
*
|
|
160
|
-
* @param key - The key to delete.
|
|
161
|
-
* @throws If the FlowMap instance is disposed.
|
|
162
|
-
*
|
|
163
|
-
* @remarks
|
|
164
|
-
* Removes the key from the internal map, emits the deleted key and its value via {@link FlowMap.$lastDeleted},
|
|
165
|
-
* and notifies all subscribers of the change.
|
|
166
|
-
*
|
|
167
|
-
* @public
|
|
168
|
-
*/
|
|
169
|
-
public delete(key: K): void {
|
|
170
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
171
|
-
const value = this._value.get(key);
|
|
172
|
-
if (!value) throw new Error("[PicoFlow] Key does not exist");
|
|
173
|
-
this._value.delete(key);
|
|
174
|
-
this.$lastDeleted.set({ key, value });
|
|
175
|
-
this._notify();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Disposes the FlowMap and its values.
|
|
180
|
-
* @param options - Disposal options.
|
|
181
|
-
* @public
|
|
182
|
-
*/
|
|
183
|
-
override dispose(options?: { self: boolean }): void {
|
|
184
|
-
super.dispose(options);
|
|
185
|
-
this._value.forEach((item) => {
|
|
186
|
-
if (isDisposable(item)) item.dispose(options);
|
|
187
|
-
});
|
|
188
|
-
this._value.clear();
|
|
189
|
-
this.$lastAdded.dispose(options);
|
|
190
|
-
this.$lastUpdated.dispose(options);
|
|
191
|
-
this.$lastDeleted.dispose(options);
|
|
192
|
-
}
|
|
193
|
-
}
|
package/src/basic/constant.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { FlowObservable } from "./observable";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Represents a reactive and immutable constant value that can be computed lazily upon first access.
|
|
5
|
-
*
|
|
6
|
-
* @remarks
|
|
7
|
-
* FlowConstant extends FlowObservable to provide an immutable reactive value. Unlike {@link FlowState},
|
|
8
|
-
* which is mutable via the `set()` method, a constant's value never changes after initialization.
|
|
9
|
-
*
|
|
10
|
-
* **Initialization Patterns:**
|
|
11
|
-
* - **Direct value**: Pass a value directly; it's stored immediately.
|
|
12
|
-
* - **Lazy initialization**: Pass a function; it's called only when the value is first accessed
|
|
13
|
-
* via `get()` or `pick()`.
|
|
14
|
-
*
|
|
15
|
-
* **Lazy Evaluation Benefits:**
|
|
16
|
-
* Lazy initialization is useful when:
|
|
17
|
-
* - The computation is expensive and may not be needed immediately
|
|
18
|
-
* - The value depends on resources that aren't available at construction time
|
|
19
|
-
* - You want to defer computation until the value is actually needed
|
|
20
|
-
*
|
|
21
|
-
* **Caching:**
|
|
22
|
-
* Once computed (either immediately or lazily), the value is cached permanently. All subsequent
|
|
23
|
-
* accesses return the cached value without re-computation.
|
|
24
|
-
*
|
|
25
|
-
* **Use Cases:**
|
|
26
|
-
* - Configuration values that don't change
|
|
27
|
-
* - Expensive computations that should only run once
|
|
28
|
-
* - Initialization values for other reactive primitives
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* // Direct value - initialized immediately
|
|
33
|
-
* const $config = constant({ apiUrl: 'https://api.example.com' });
|
|
34
|
-
*
|
|
35
|
-
* // Lazy initialization - computed on first access
|
|
36
|
-
* const $expensiveValue = constant(() => {
|
|
37
|
-
* console.log('Computing...');
|
|
38
|
-
* return performExpensiveCalculation();
|
|
39
|
-
* });
|
|
40
|
-
*
|
|
41
|
-
* $expensiveValue.pick(); // Logs: "Computing..."
|
|
42
|
-
* $expensiveValue.pick(); // No log - returns cached value
|
|
43
|
-
* ```
|
|
44
|
-
*
|
|
45
|
-
* @typeParam T - The type of the constant value.
|
|
46
|
-
*
|
|
47
|
-
* @public
|
|
48
|
-
*/
|
|
49
|
-
export class FlowConstant<T> extends FlowObservable<T> {
|
|
50
|
-
/**
|
|
51
|
-
* Creates a new FlowConstant instance.
|
|
52
|
-
*
|
|
53
|
-
* @param value - Either a direct value of type T or a function returning a value of type T.
|
|
54
|
-
* If a function is provided, it will be invoked lazily on the first value access.
|
|
55
|
-
* If a direct value is provided, it is stored immediately.
|
|
56
|
-
*
|
|
57
|
-
* @public
|
|
58
|
-
*/
|
|
59
|
-
constructor(value: T | (() => T)) {
|
|
60
|
-
super();
|
|
61
|
-
this._initEager(value);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Internal method to get the raw value.
|
|
66
|
-
* @internal
|
|
67
|
-
*/
|
|
68
|
-
protected _getRaw(): T {
|
|
69
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
70
|
-
this._initLazy();
|
|
71
|
-
return this._value;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/* INTERNAL --------------------------------------------------------- */
|
|
75
|
-
|
|
76
|
-
/** @internal */ protected _initialized = false;
|
|
77
|
-
/** @internal */ protected _init?: () => T;
|
|
78
|
-
|
|
79
|
-
/** @internal */ protected _initEager(value: T | (() => T)): void {
|
|
80
|
-
if (typeof value === "function") {
|
|
81
|
-
this._init = value as () => T;
|
|
82
|
-
} else {
|
|
83
|
-
this._value = value;
|
|
84
|
-
this._initialized = true;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** @internal */ protected _initLazy(): void {
|
|
89
|
-
if (!this._initialized && this._init) {
|
|
90
|
-
this._value = this._init();
|
|
91
|
-
this._initialized = true;
|
|
92
|
-
}
|
|
93
|
-
/* v8 ignore next 2 */
|
|
94
|
-
if (!this._initialized)
|
|
95
|
-
throw new Error("[PicoFlow] Primitive can't be initialized");
|
|
96
|
-
}
|
|
97
|
-
}
|
package/src/basic/derivation.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { FlowObservable } from "./observable";
|
|
2
|
-
import { TrackingContext } from "./trackingContext";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Represents a reactive derivation whose value is computed based on other reactive signals.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* FlowDerivation creates a computed value that automatically tracks its dependencies and
|
|
9
|
-
* recomputes when any dependency changes. The computation is lazy: it doesn't execute until
|
|
10
|
-
* the value is first accessed (via `get()` or `pick()`), and subsequent accesses return the
|
|
11
|
-
* cached value until a dependency changes.
|
|
12
|
-
*
|
|
13
|
-
* **Lazy Initialization:**
|
|
14
|
-
* The compute function doesn't run immediately upon creation. It runs only when:
|
|
15
|
-
* - The value is first read via `get()` or `pick()`
|
|
16
|
-
* - The derivation is watched via `watch()`
|
|
17
|
-
*
|
|
18
|
-
* **Dirty Checking and Recomputation:**
|
|
19
|
-
* When a tracked dependency changes, the derivation is marked as "dirty" but doesn't recompute
|
|
20
|
-
* immediately. Recomputation happens lazily on the next value access. This prevents unnecessary
|
|
21
|
-
* computations when multiple dependencies change in quick succession.
|
|
22
|
-
*
|
|
23
|
-
* **Dynamic Dependencies:**
|
|
24
|
-
* Dependencies are tracked dynamically during each computation. If the compute function
|
|
25
|
-
* conditionally tracks different observables, the dependency graph updates automatically.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```typescript
|
|
29
|
-
* const $firstName = state('John');
|
|
30
|
-
* const $lastName = state('Doe');
|
|
31
|
-
*
|
|
32
|
-
* const $fullName = derivation((t) => {
|
|
33
|
-
* return `${$firstName.get(t)} ${$lastName.get(t)}`;
|
|
34
|
-
* });
|
|
35
|
-
*
|
|
36
|
-
* // Compute function hasn't run yet (lazy)
|
|
37
|
-
* const name = $fullName.pick(); // Now it computes
|
|
38
|
-
* ```
|
|
39
|
-
*
|
|
40
|
-
* @typeParam T - The type of the computed value.
|
|
41
|
-
* @public
|
|
42
|
-
*/
|
|
43
|
-
export class FlowDerivation<T> extends FlowObservable<T> {
|
|
44
|
-
/**
|
|
45
|
-
* Creates a new FlowDerivation.
|
|
46
|
-
*
|
|
47
|
-
* @param compute - A function that computes the derived value using a tracking context.
|
|
48
|
-
* The function receives a TrackingContext and should use it to access dependencies via
|
|
49
|
-
* `.get(t)`. The function is not executed immediately; it runs lazily on first access.
|
|
50
|
-
*
|
|
51
|
-
* @public
|
|
52
|
-
*/
|
|
53
|
-
constructor(compute: (t: TrackingContext) => T) {
|
|
54
|
-
super();
|
|
55
|
-
this._compute = compute;
|
|
56
|
-
this._trackedContext = new TrackingContext(this);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Internal method to get the raw value.
|
|
61
|
-
* @internal
|
|
62
|
-
*/
|
|
63
|
-
protected _getRaw(): T {
|
|
64
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
65
|
-
this._initLazy();
|
|
66
|
-
this._update();
|
|
67
|
-
return this._value;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/* INTERNAL --------------------------------------------------------- */
|
|
71
|
-
|
|
72
|
-
private _initialized = false;
|
|
73
|
-
private _dirty = false;
|
|
74
|
-
private _compute: (t: TrackingContext) => T;
|
|
75
|
-
private _trackedContext: TrackingContext;
|
|
76
|
-
|
|
77
|
-
private _initLazy(): void {
|
|
78
|
-
if (!this._initialized) {
|
|
79
|
-
this._value = this._compute(this._trackedContext);
|
|
80
|
-
this._initialized = true;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/* @internal */ private _update(): void {
|
|
85
|
-
if (this._dirty) {
|
|
86
|
-
// Store current dependencies
|
|
87
|
-
const dependencies = [...this._dependencies];
|
|
88
|
-
|
|
89
|
-
// Clear current dependencies, compute and retrack dependencies
|
|
90
|
-
this._dependencies.clear();
|
|
91
|
-
this._value = this._compute(this._trackedContext);
|
|
92
|
-
|
|
93
|
-
// Unsubscribe from dependencies that are no longer needed
|
|
94
|
-
const dependenciesToRemove = dependencies.filter(
|
|
95
|
-
(dependency) => !this._dependencies.has(dependency),
|
|
96
|
-
);
|
|
97
|
-
dependenciesToRemove.forEach((dependency) => {
|
|
98
|
-
dependency._unregisterDependency(this);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
this._dirty = false;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/* @internal */ override _notify(): void {
|
|
106
|
-
this._dirty = true;
|
|
107
|
-
super._notify();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Watches the derivation, registering it as a dependency in the given context.
|
|
112
|
-
*
|
|
113
|
-
* @param context - The tracking context in which to register this derivation.
|
|
114
|
-
*
|
|
115
|
-
* @remarks
|
|
116
|
-
* This method overrides the base `watch()` to handle a special case: when a derivation
|
|
117
|
-
* is watched without having its value read (e.g., `$derivation.watch(t)` instead of
|
|
118
|
-
* `$derivation.get(t)`), the derivation still needs to compute its value to establish
|
|
119
|
-
* its own dependencies. Otherwise, the derivation would be tracked but wouldn't track
|
|
120
|
-
* its own dependencies, breaking the reactive chain.
|
|
121
|
-
*
|
|
122
|
-
* This override ensures that:
|
|
123
|
-
* 1. The derivation is registered as a dependency in the provided context
|
|
124
|
-
* 2. The derivation computes its value (if not already computed)
|
|
125
|
-
* 3. The derivation tracks its own dependencies during computation
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```typescript
|
|
129
|
-
* const $derived = derivation((t) => $state.get(t) * 2);
|
|
130
|
-
*
|
|
131
|
-
* effect((t) => {
|
|
132
|
-
* $derived.watch(t); // Derivation computes even without reading value
|
|
133
|
-
* doSomething(); // Effect re-runs when $derived's dependencies change
|
|
134
|
-
* });
|
|
135
|
-
* ```
|
|
136
|
-
*
|
|
137
|
-
* @public
|
|
138
|
-
*/
|
|
139
|
-
public override watch(context: TrackingContext): void {
|
|
140
|
-
super.watch(context); // Register this derivation as a dependency
|
|
141
|
-
|
|
142
|
-
// Ensure the derivation is initialized and up-to-date
|
|
143
|
-
// This is needed when watching a derivation without reading its value
|
|
144
|
-
this._initLazy();
|
|
145
|
-
this._update();
|
|
146
|
-
}
|
|
147
|
-
}
|
package/src/basic/disposable.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Represents an object with a disposable lifecycle that manages resources requiring cleanup.
|
|
3
|
-
*
|
|
4
|
-
* @remarks
|
|
5
|
-
* FlowDisposable is the interface for PicoFlow primitives that hold resources needing
|
|
6
|
-
* explicit cleanup. Implementing this interface ensures that objects can properly release
|
|
7
|
-
* resources such as subscriptions, event listeners, and dependent effects.
|
|
8
|
-
*
|
|
9
|
-
* **Disposal Behavior:**
|
|
10
|
-
* All PicoFlow reactive primitives (signals, states, effects, derivations, etc.) implement
|
|
11
|
-
* this interface. When disposed:
|
|
12
|
-
* - The primitive becomes unusable and throws errors on further access
|
|
13
|
-
* - All subscriptions and dependencies are cleaned up
|
|
14
|
-
* - Memory is freed for garbage collection
|
|
15
|
-
*
|
|
16
|
-
* **Options:**
|
|
17
|
-
* The optional `options` parameter controls disposal behavior:
|
|
18
|
-
* - `{ self: true }`: Dispose only this object, leaving dependent effects/listeners active
|
|
19
|
-
* - `{ self: false }` or omitted: Dispose this object AND all dependent effects/listeners (cascade)
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```typescript
|
|
23
|
-
* const $state = state(0);
|
|
24
|
-
* const fx = effect((t) => console.log($state.get(t)));
|
|
25
|
-
*
|
|
26
|
-
* // Cascade disposal - disposes $state and all its effects
|
|
27
|
-
* $state.dispose();
|
|
28
|
-
*
|
|
29
|
-
* // Self-only disposal - disposes $state but leaves effects intact
|
|
30
|
-
* $state.dispose({ self: true });
|
|
31
|
-
* ```
|
|
32
|
-
*
|
|
33
|
-
* @public
|
|
34
|
-
*/
|
|
35
|
-
export interface FlowDisposable {
|
|
36
|
-
/**
|
|
37
|
-
* Disposes resources held by this object.
|
|
38
|
-
*
|
|
39
|
-
* @param options - Optional configuration for disposal behavior.
|
|
40
|
-
* @param options.self - When true, disposes only this object without cascading to dependents.
|
|
41
|
-
* When false or omitted, disposes this object and all its dependent effects and listeners.
|
|
42
|
-
*
|
|
43
|
-
* @throws Error if the object has already been disposed (behavior may vary by implementation).
|
|
44
|
-
*/
|
|
45
|
-
dispose(options?: { self: boolean }): void;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Type guard that checks whether an object implements the FlowDisposable interface.
|
|
50
|
-
*
|
|
51
|
-
* @param obj - The object to test for disposability.
|
|
52
|
-
* @returns True if the object has a `dispose` method and is therefore disposable, false otherwise.
|
|
53
|
-
*
|
|
54
|
-
* @remarks
|
|
55
|
-
* This utility function is useful for safely checking if an object needs disposal before
|
|
56
|
-
* attempting cleanup operations. It performs a runtime check for the presence of a `dispose`
|
|
57
|
-
* method, making it safe to use with unknown types.
|
|
58
|
-
*
|
|
59
|
-
* **Common Use Cases:**
|
|
60
|
-
* - Conditionally disposing items in collections (arrays, maps)
|
|
61
|
-
* - Generic cleanup functions that handle both disposable and non-disposable objects
|
|
62
|
-
* - Defensive programming when working with mixed types
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```typescript
|
|
66
|
-
* function cleanupArray<T>(items: T[]) {
|
|
67
|
-
* items.forEach(item => {
|
|
68
|
-
* if (isDisposable(item)) {
|
|
69
|
-
* item.dispose();
|
|
70
|
-
* }
|
|
71
|
-
* });
|
|
72
|
-
* }
|
|
73
|
-
*
|
|
74
|
-
* const mixed = [state(1), "string", signal(), 42];
|
|
75
|
-
* cleanupArray(mixed); // Only disposes the state and signal
|
|
76
|
-
* ```
|
|
77
|
-
*
|
|
78
|
-
* @public
|
|
79
|
-
*/
|
|
80
|
-
export function isDisposable(obj: unknown): obj is FlowDisposable {
|
|
81
|
-
return (
|
|
82
|
-
obj !== null &&
|
|
83
|
-
obj !== undefined &&
|
|
84
|
-
typeof (obj as FlowDisposable).dispose === "function"
|
|
85
|
-
);
|
|
86
|
-
}
|
package/src/basic/effect.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import type { FlowSignal } from "./signal";
|
|
2
|
-
import { TrackingContext } from "./trackingContext";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Represents a reactive effect that executes side-effect functions based
|
|
6
|
-
* on its tracked dependencies.
|
|
7
|
-
*
|
|
8
|
-
* @remarks
|
|
9
|
-
* FlowEffect executes an apply function that performs side effects. The effect always runs
|
|
10
|
-
* with a tracking context, allowing you to explicitly control which observables and signals
|
|
11
|
-
* become dependencies using `.get(t)` for tracked reads or `.pick()` for untracked reads.
|
|
12
|
-
*
|
|
13
|
-
* When any tracked dependency changes, the effect automatically re-executes. The effect
|
|
14
|
-
* runs immediately upon creation and whenever its dependencies trigger updates.
|
|
15
|
-
*
|
|
16
|
-
* Unlike the old API, effects in the new TrackingContext-based system always execute with
|
|
17
|
-
* a tracking context available. You control reactivity by choosing which observables to track
|
|
18
|
-
* within the effect body.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```typescript
|
|
22
|
-
* const fx = effect((t) => {
|
|
23
|
-
* const reactive = $stateA.get(t); // Tracked - effect re-runs when $stateA changes
|
|
24
|
-
* const snapshot = $stateB.pick(); // Not tracked - changes don't trigger re-runs
|
|
25
|
-
* console.log(reactive, snapshot);
|
|
26
|
-
* });
|
|
27
|
-
* ```
|
|
28
|
-
*
|
|
29
|
-
* @public
|
|
30
|
-
*/
|
|
31
|
-
export class FlowEffect {
|
|
32
|
-
/**
|
|
33
|
-
* Creates a new FlowEffect.
|
|
34
|
-
*
|
|
35
|
-
* @param apply - A side-effect function that receives a tracking context to
|
|
36
|
-
* access and register dependencies on reactive observables and signals.
|
|
37
|
-
*
|
|
38
|
-
* @remarks
|
|
39
|
-
* The provided function is executed immediately upon construction with a tracking context.
|
|
40
|
-
* Use the context parameter to call `.get(t)` on observables you want to track, or `.pick()`
|
|
41
|
-
* on observables you want to read without creating dependencies.
|
|
42
|
-
*
|
|
43
|
-
* @public
|
|
44
|
-
*/
|
|
45
|
-
constructor(apply: (t: TrackingContext) => void) {
|
|
46
|
-
this._trackedContext = new TrackingContext(this);
|
|
47
|
-
this._apply = apply;
|
|
48
|
-
this._exec();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Disposes the effect, unregistering all its tracked dependencies.
|
|
53
|
-
*
|
|
54
|
-
* @remarks
|
|
55
|
-
* Once disposed, the effect must no longer be used. Trying to dispose an effect
|
|
56
|
-
* that is already disposed will throw an error.
|
|
57
|
-
*
|
|
58
|
-
* @public
|
|
59
|
-
*/
|
|
60
|
-
public dispose(): void {
|
|
61
|
-
if (this._disposed) throw new Error("[PicoFlow] Effect is disposed");
|
|
62
|
-
Array.from(this._dependencies).forEach((dependency) => {
|
|
63
|
-
this._unregisterDependency(dependency);
|
|
64
|
-
});
|
|
65
|
-
this._disposed = true;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Indicates whether this effect has been disposed.
|
|
70
|
-
*
|
|
71
|
-
* @returns A boolean value that is true if the effect is disposed, false otherwise.
|
|
72
|
-
*
|
|
73
|
-
* @public
|
|
74
|
-
*/
|
|
75
|
-
public get disposed(): boolean {
|
|
76
|
-
return this._disposed;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/* INTERNAL ------------------------------------------------------------ */
|
|
80
|
-
|
|
81
|
-
private _disposed = false;
|
|
82
|
-
private _dependencies = new Set<FlowSignal>();
|
|
83
|
-
private _trackedContext: TrackingContext;
|
|
84
|
-
private _apply: (t: TrackingContext) => void;
|
|
85
|
-
|
|
86
|
-
/** @internal */ _exec(): void {
|
|
87
|
-
if (this._disposed)
|
|
88
|
-
/* v8 ignore next 1 */
|
|
89
|
-
throw new Error("[PicoFlow] Effect is disposed");
|
|
90
|
-
|
|
91
|
-
// Always execute with tracking context
|
|
92
|
-
this._apply(this._trackedContext);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** @internal */ _registerDependency(dependency: FlowSignal): void {
|
|
96
|
-
this._dependencies.add(dependency);
|
|
97
|
-
dependency._registerEffect(this);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** @internal */ _unregisterDependency(dependency: FlowSignal): void {
|
|
101
|
-
this._dependencies.delete(dependency);
|
|
102
|
-
dependency._unregisterEffect(this);
|
|
103
|
-
}
|
|
104
|
-
}
|
package/src/basic/index.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export { FlowConstant } from "./constant";
|
|
2
|
-
export { FlowDerivation } from "./derivation";
|
|
3
|
-
export type { FlowDisposable } from "./disposable";
|
|
4
|
-
export { isDisposable } from "./disposable";
|
|
5
|
-
export { FlowEffect } from "./effect";
|
|
6
|
-
export { FlowObservable } from "./observable";
|
|
7
|
-
export { FlowSignal } from "./signal";
|
|
8
|
-
export { FlowState } from "./state";
|
|
9
|
-
export { TrackingContext } from "./trackingContext";
|