@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.
Files changed (180) hide show
  1. package/.cursor/plans/unifier-flowresource-avec-flowderivation-c9506e24.plan.md +372 -0
  2. package/README.md +25 -171
  3. package/biome.json +4 -1
  4. package/dist/picoflow.js +1129 -661
  5. package/dist/types/flow/base/flowDisposable.d.ts +67 -0
  6. package/dist/types/flow/base/flowDisposable.d.ts.map +1 -0
  7. package/dist/types/flow/base/flowEffect.d.ts +127 -0
  8. package/dist/types/flow/base/flowEffect.d.ts.map +1 -0
  9. package/dist/types/flow/base/flowGraph.d.ts +97 -0
  10. package/dist/types/flow/base/flowGraph.d.ts.map +1 -0
  11. package/dist/types/flow/base/flowSignal.d.ts +134 -0
  12. package/dist/types/flow/base/flowSignal.d.ts.map +1 -0
  13. package/dist/types/flow/base/flowTracker.d.ts +15 -0
  14. package/dist/types/flow/base/flowTracker.d.ts.map +1 -0
  15. package/dist/types/flow/base/index.d.ts +7 -0
  16. package/dist/types/flow/base/index.d.ts.map +1 -0
  17. package/dist/types/flow/base/utils.d.ts +20 -0
  18. package/dist/types/flow/base/utils.d.ts.map +1 -0
  19. package/dist/types/{advanced/array.d.ts → flow/collections/flowArray.d.ts} +50 -12
  20. package/dist/types/flow/collections/flowArray.d.ts.map +1 -0
  21. package/dist/types/flow/collections/flowMap.d.ts +224 -0
  22. package/dist/types/flow/collections/flowMap.d.ts.map +1 -0
  23. package/dist/types/flow/collections/index.d.ts +3 -0
  24. package/dist/types/flow/collections/index.d.ts.map +1 -0
  25. package/dist/types/flow/index.d.ts +4 -0
  26. package/dist/types/flow/index.d.ts.map +1 -0
  27. package/dist/types/flow/nodes/async/flowConstantAsync.d.ts +137 -0
  28. package/dist/types/flow/nodes/async/flowConstantAsync.d.ts.map +1 -0
  29. package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts +137 -0
  30. package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts.map +1 -0
  31. package/dist/types/flow/nodes/async/flowNodeAsync.d.ts +343 -0
  32. package/dist/types/flow/nodes/async/flowNodeAsync.d.ts.map +1 -0
  33. package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts +81 -0
  34. package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts.map +1 -0
  35. package/dist/types/flow/nodes/async/flowStateAsync.d.ts +111 -0
  36. package/dist/types/flow/nodes/async/flowStateAsync.d.ts.map +1 -0
  37. package/dist/types/flow/nodes/async/index.d.ts +6 -0
  38. package/dist/types/flow/nodes/async/index.d.ts.map +1 -0
  39. package/dist/types/flow/nodes/index.d.ts +3 -0
  40. package/dist/types/flow/nodes/index.d.ts.map +1 -0
  41. package/dist/types/flow/nodes/sync/flowConstant.d.ts +108 -0
  42. package/dist/types/flow/nodes/sync/flowConstant.d.ts.map +1 -0
  43. package/dist/types/flow/nodes/sync/flowDerivation.d.ts +100 -0
  44. package/dist/types/flow/nodes/sync/flowDerivation.d.ts.map +1 -0
  45. package/dist/types/flow/nodes/sync/flowNode.d.ts +314 -0
  46. package/dist/types/flow/nodes/sync/flowNode.d.ts.map +1 -0
  47. package/dist/types/flow/nodes/sync/flowReadonly.d.ts +57 -0
  48. package/dist/types/flow/nodes/sync/flowReadonly.d.ts.map +1 -0
  49. package/dist/types/flow/nodes/sync/flowState.d.ts +96 -0
  50. package/dist/types/flow/nodes/sync/flowState.d.ts.map +1 -0
  51. package/dist/types/flow/nodes/sync/index.d.ts +6 -0
  52. package/dist/types/flow/nodes/sync/index.d.ts.map +1 -0
  53. package/dist/types/index.d.ts +1 -4
  54. package/dist/types/index.d.ts.map +1 -1
  55. package/dist/types/solid/converters.d.ts +34 -44
  56. package/dist/types/solid/converters.d.ts.map +1 -1
  57. package/dist/types/solid/primitives.d.ts +1 -0
  58. package/dist/types/solid/primitives.d.ts.map +1 -1
  59. package/docs/.vitepress/config.mts +1 -1
  60. package/docs/api/typedoc-sidebar.json +81 -1
  61. package/package.json +60 -58
  62. package/src/flow/base/flowDisposable.ts +71 -0
  63. package/src/flow/base/flowEffect.ts +171 -0
  64. package/src/flow/base/flowGraph.ts +288 -0
  65. package/src/flow/base/flowSignal.ts +207 -0
  66. package/src/flow/base/flowTracker.ts +17 -0
  67. package/src/flow/base/index.ts +6 -0
  68. package/src/flow/base/utils.ts +19 -0
  69. package/src/flow/collections/flowArray.ts +409 -0
  70. package/src/flow/collections/flowMap.ts +398 -0
  71. package/src/flow/collections/index.ts +2 -0
  72. package/src/flow/index.ts +3 -0
  73. package/src/flow/nodes/async/flowConstantAsync.ts +142 -0
  74. package/src/flow/nodes/async/flowDerivationAsync.ts +143 -0
  75. package/src/flow/nodes/async/flowNodeAsync.ts +474 -0
  76. package/src/flow/nodes/async/flowReadonlyAsync.ts +81 -0
  77. package/src/flow/nodes/async/flowStateAsync.ts +116 -0
  78. package/src/flow/nodes/async/index.ts +5 -0
  79. package/src/flow/nodes/await/advanced/index.ts +5 -0
  80. package/src/{advanced → flow/nodes/await/advanced}/resource.ts +37 -3
  81. package/src/{advanced → flow/nodes/await/advanced}/resourceAsync.ts +35 -3
  82. package/src/{advanced → flow/nodes/await/advanced}/stream.ts +40 -2
  83. package/src/{advanced → flow/nodes/await/advanced}/streamAsync.ts +38 -3
  84. package/src/flow/nodes/await/flowConstantAwait.ts +154 -0
  85. package/src/flow/nodes/await/flowDerivationAwait.ts +154 -0
  86. package/src/flow/nodes/await/flowNodeAwait.ts +508 -0
  87. package/src/flow/nodes/await/flowReadonlyAwait.ts +89 -0
  88. package/src/flow/nodes/await/flowStateAwait.ts +130 -0
  89. package/src/flow/nodes/await/index.ts +5 -0
  90. package/src/flow/nodes/index.ts +3 -0
  91. package/src/flow/nodes/sync/flowConstant.ts +111 -0
  92. package/src/flow/nodes/sync/flowDerivation.ts +105 -0
  93. package/src/flow/nodes/sync/flowNode.ts +439 -0
  94. package/src/flow/nodes/sync/flowReadonly.ts +57 -0
  95. package/src/flow/nodes/sync/flowState.ts +101 -0
  96. package/src/flow/nodes/sync/index.ts +5 -0
  97. package/src/index.ts +1 -47
  98. package/src/solid/converters.ts +59 -198
  99. package/src/solid/primitives.ts +4 -0
  100. package/test/base/flowEffect.test.ts +108 -0
  101. package/test/base/flowGraph.test.ts +485 -0
  102. package/test/base/flowSignal.test.ts +372 -0
  103. package/test/collections/flowArray.asyncStates.test.ts +1553 -0
  104. package/test/collections/flowArray.scalars.test.ts +1129 -0
  105. package/test/collections/flowArray.states.test.ts +1365 -0
  106. package/test/collections/flowMap.asyncStates.test.ts +1105 -0
  107. package/test/collections/flowMap.scalars.test.ts +877 -0
  108. package/test/collections/flowMap.states.test.ts +1097 -0
  109. package/test/nodes/async/flowConstantAsync.test.ts +860 -0
  110. package/test/nodes/async/flowDerivationAsync.test.ts +1517 -0
  111. package/test/nodes/async/flowStateAsync.test.ts +1387 -0
  112. package/test/{resource.test.ts → nodes/await/advanced/resource.test.ts} +21 -19
  113. package/test/{resourceAsync.test.ts → nodes/await/advanced/resourceAsync.test.ts} +3 -1
  114. package/test/{stream.test.ts → nodes/await/advanced/stream.test.ts} +30 -28
  115. package/test/{streamAsync.test.ts → nodes/await/advanced/streamAsync.test.ts} +16 -14
  116. package/test/nodes/await/flowConstantAwait.test.ts +643 -0
  117. package/test/nodes/await/flowDerivationAwait.test.ts +1583 -0
  118. package/test/nodes/await/flowStateAwait.test.ts +999 -0
  119. package/test/nodes/mixed/derivation.test.ts +1527 -0
  120. package/test/nodes/sync/flowConstant.test.ts +620 -0
  121. package/test/nodes/sync/flowDerivation.test.ts +1373 -0
  122. package/test/nodes/sync/flowState.test.ts +945 -0
  123. package/test/solid/converters.test.ts +721 -0
  124. package/test/solid/primitives.test.ts +1031 -0
  125. package/tsconfig.json +2 -1
  126. package/vitest.config.ts +7 -1
  127. package/IMPLEMENTATION_GUIDE.md +0 -1578
  128. package/dist/types/advanced/array.d.ts.map +0 -1
  129. package/dist/types/advanced/index.d.ts +0 -9
  130. package/dist/types/advanced/index.d.ts.map +0 -1
  131. package/dist/types/advanced/map.d.ts +0 -166
  132. package/dist/types/advanced/map.d.ts.map +0 -1
  133. package/dist/types/advanced/resource.d.ts +0 -78
  134. package/dist/types/advanced/resource.d.ts.map +0 -1
  135. package/dist/types/advanced/resourceAsync.d.ts +0 -56
  136. package/dist/types/advanced/resourceAsync.d.ts.map +0 -1
  137. package/dist/types/advanced/stream.d.ts +0 -117
  138. package/dist/types/advanced/stream.d.ts.map +0 -1
  139. package/dist/types/advanced/streamAsync.d.ts +0 -97
  140. package/dist/types/advanced/streamAsync.d.ts.map +0 -1
  141. package/dist/types/basic/constant.d.ts +0 -60
  142. package/dist/types/basic/constant.d.ts.map +0 -1
  143. package/dist/types/basic/derivation.d.ts +0 -89
  144. package/dist/types/basic/derivation.d.ts.map +0 -1
  145. package/dist/types/basic/disposable.d.ts +0 -82
  146. package/dist/types/basic/disposable.d.ts.map +0 -1
  147. package/dist/types/basic/effect.d.ts +0 -67
  148. package/dist/types/basic/effect.d.ts.map +0 -1
  149. package/dist/types/basic/index.d.ts +0 -10
  150. package/dist/types/basic/index.d.ts.map +0 -1
  151. package/dist/types/basic/observable.d.ts +0 -83
  152. package/dist/types/basic/observable.d.ts.map +0 -1
  153. package/dist/types/basic/signal.d.ts +0 -69
  154. package/dist/types/basic/signal.d.ts.map +0 -1
  155. package/dist/types/basic/state.d.ts +0 -47
  156. package/dist/types/basic/state.d.ts.map +0 -1
  157. package/dist/types/basic/trackingContext.d.ts +0 -33
  158. package/dist/types/basic/trackingContext.d.ts.map +0 -1
  159. package/dist/types/creators.d.ts +0 -340
  160. package/dist/types/creators.d.ts.map +0 -1
  161. package/src/advanced/array.ts +0 -222
  162. package/src/advanced/index.ts +0 -12
  163. package/src/advanced/map.ts +0 -193
  164. package/src/basic/constant.ts +0 -97
  165. package/src/basic/derivation.ts +0 -147
  166. package/src/basic/disposable.ts +0 -86
  167. package/src/basic/effect.ts +0 -104
  168. package/src/basic/index.ts +0 -9
  169. package/src/basic/observable.ts +0 -109
  170. package/src/basic/signal.ts +0 -145
  171. package/src/basic/state.ts +0 -60
  172. package/src/basic/trackingContext.ts +0 -45
  173. package/src/creators.ts +0 -395
  174. package/test/array.test.ts +0 -600
  175. package/test/constant.test.ts +0 -44
  176. package/test/derivation.test.ts +0 -539
  177. package/test/effect.test.ts +0 -29
  178. package/test/map.test.ts +0 -240
  179. package/test/signal.test.ts +0 -72
  180. package/test/state.test.ts +0 -212
@@ -0,0 +1,372 @@
1
+ <!-- c9506e24-37d6-4d95-a937-545ccc76fe61 744135bd-5bf9-4a27-9006-1c9cfd4392d8 -->
2
+ # Plan d'implémentation : Unification FlowResource avec FlowDerivation (approche intégrée)
3
+
4
+ ## 1. Modifications d'API
5
+
6
+ ### 1.1 FlowDerivation - Option `synchrone` et méthode `refresh()`
7
+
8
+ **Fichier**: `src/basic/derivation.ts`
9
+
10
+ **Modifications du constructeur**:
11
+
12
+ ```typescript
13
+ constructor(
14
+ compute: (t: TrackingContext) => T | Promise<T>,
15
+ options?: {
16
+ synchrone?: boolean; // Si true avec fonction async, retourne T | undefined
17
+ }
18
+ )
19
+ ```
20
+
21
+ **Nouvelle méthode**:
22
+
23
+ ```typescript
24
+ public refresh(): void
25
+ ```
26
+
27
+ **Comportement avec `synchrone: true`**:
28
+
29
+ - Quand `synchrone: true` est fourni avec une fonction async (`Promise<T>`):
30
+ - Le type de retour devient `FlowDerivation<T | undefined>` au lieu de `FlowDerivation<Promise<T>>`
31
+ - Retourne `undefined` initialement, puis `T` une fois la Promise résolue
32
+ - Compare les valeurs avec `===` avant de notifier (évite notifications inutiles)
33
+ - Gère les erreurs de Promise (throw)
34
+ - Quand `synchrone: true` est fourni avec une fonction synchrone:
35
+ - Erreur de type TypeScript (incompatibilité de types)
36
+ - `refresh()` marque comme dirty et notifie (même comportement que sans option)
37
+
38
+ **Usage**:
39
+
40
+ ```typescript
41
+ // Dérivation async normale
42
+ const $userAsync = derivation(async (t) => await fetchUser());
43
+ const userPromise = $userAsync.pick(); // Promise<User>
44
+
45
+ // Dérivation async synchronisée
46
+ const $user = derivation(async (t) => await fetchUser(), { synchrone: true });
47
+ const user = $user.pick(); // User | undefined
48
+
49
+ // Refresh
50
+ $user.refresh(); // Marque comme dirty
51
+ await $user.pick(); // Déclenche le recalcul et attend
52
+ ```
53
+
54
+ ### 1.2 Suppression de FlowSynchronizer
55
+
56
+ **Changement d'approche**:
57
+
58
+ - Plus besoin de classe `FlowSynchronizer` séparée
59
+ - La synchronisation est intégrée directement dans `FlowDerivation`
60
+
61
+ ### 1.3 Modifications des creators
62
+
63
+ **Fichier**: `src/creators.ts`
64
+
65
+ **Changements**:
66
+
67
+ 1. **`resourceAsync()`** - Simplifié:
68
+ ```typescript
69
+ // Avant: retourne FlowResourceAsync
70
+ // Après: retourne FlowDerivation<Promise<T>>
71
+ export function resourceAsync<T>(fn: () => Promise<T>): FlowDerivation<Promise<T>> {
72
+ return derivation(() => fn());
73
+ }
74
+ ```
75
+
76
+ 2. **`resource()`** - Utilise l'option `synchrone`:
77
+ ```typescript
78
+ // Avant: retourne FlowResource
79
+ // Après: retourne FlowDerivation<T | undefined>
80
+ export function resource<T>(fn: () => Promise<T>): FlowDerivation<T | undefined> {
81
+ return derivation(() => fn(), { synchrone: true });
82
+ }
83
+ ```
84
+
85
+
86
+ ### 1.4 Exports
87
+
88
+ **Fichiers**: `src/advanced/index.ts`, `src/index.ts`
89
+
90
+ **Suppressions** (après migration):
91
+
92
+ - `FlowResource` (remplacé par `FlowDerivation` avec `synchrone: true`)
93
+ - `FlowResourceAsync` (remplacé par `FlowDerivation<Promise<T>>`)
94
+ - Plus besoin d'exporter `FlowSynchronizer`
95
+
96
+ ### 1.5 Rétrocompatibilité
97
+
98
+ **Impact**:
99
+
100
+ - Les types de retour changent, mais l'API reste compatible fonctionnellement
101
+ - `resource()` et `resourceAsync()` continuent de fonctionner de la même manière
102
+ - Les tests existants doivent continuer de passer (avec ajustements de types si nécessaire)
103
+
104
+ ## 2. Implémentations détaillées
105
+
106
+ ### 2.1 FlowDerivation - Support de l'option `synchrone`
107
+
108
+ **Fichier**: `src/basic/derivation.ts`
109
+
110
+ **Modifications du constructeur**:
111
+
112
+ ```typescript
113
+ constructor(
114
+ compute: (t: TrackingContext) => T | Promise<T>,
115
+ options?: { synchrone?: boolean }
116
+ ) {
117
+ super();
118
+ this._compute = compute;
119
+ this._trackedContext = new TrackingContext(this);
120
+ this._synchrone = options?.synchrone ?? false;
121
+
122
+ // Si synchrone est activé, on doit gérer les Promises différemment
123
+ if (this._synchrone) {
124
+ // Initialiser la valeur à undefined pour le mode synchrone
125
+ this._value = undefined as T;
126
+ }
127
+ }
128
+ ```
129
+
130
+ **Modifications de `_getRaw()`**:
131
+
132
+ ```typescript
133
+ protected _getRaw(): T {
134
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
135
+
136
+ this._initLazy();
137
+ this._update();
138
+
139
+ if (this._synchrone) {
140
+ // En mode synchrone, la valeur peut être undefined si la Promise n'est pas encore résolue
141
+ return this._value as T;
142
+ }
143
+
144
+ return this._value;
145
+ }
146
+ ```
147
+
148
+ **Modifications de `_update()` pour gérer la synchronisation**:
149
+
150
+ ```typescript
151
+ private _update(): void {
152
+ if (this._dirty) {
153
+ // Store current dependencies
154
+ const dependencies = [...this._dependencies];
155
+
156
+ // Clear current dependencies, compute and retrack dependencies
157
+ this._dependencies.clear();
158
+ const computedValue = this._compute(this._trackedContext);
159
+
160
+ if (this._synchrone && computedValue instanceof Promise) {
161
+ // Mode synchrone avec Promise
162
+ this._handleSyncPromise(computedValue as Promise<T>);
163
+ } else {
164
+ // Mode normal
165
+ this._value = computedValue;
166
+ }
167
+
168
+ // Unsubscribe from dependencies that are no longer needed
169
+ const dependenciesToRemove = dependencies.filter(
170
+ (dependency) => !this._dependencies.has(dependency),
171
+ );
172
+ dependenciesToRemove.forEach((dependency) => {
173
+ dependency._unregisterDependency(this);
174
+ });
175
+
176
+ this._dirty = false;
177
+ }
178
+ }
179
+ ```
180
+
181
+ **Nouvelle méthode `_handleSyncPromise()`**:
182
+
183
+ ```typescript
184
+ private async _handleSyncPromise(promise: Promise<T>): Promise<void> {
185
+ // Marquer cette Promise comme la Promise courante
186
+ this._currentPromise = promise;
187
+
188
+ try {
189
+ const value = await promise;
190
+
191
+ // Vérifier que c'est toujours la Promise courante (éviter les race conditions)
192
+ if (this._currentPromise !== promise) {
193
+ return; // Une nouvelle Promise a été créée, ignorer cette résolution
194
+ }
195
+
196
+ // Comparer avec la valeur actuelle pour éviter les notifications inutiles
197
+ if (value === this._value) {
198
+ return;
199
+ }
200
+
201
+ // Mettre à jour la valeur et notifier
202
+ this._value = value as T;
203
+ this._notify();
204
+ } catch (error) {
205
+ // Vérifier que c'est toujours la Promise courante
206
+ if (this._currentPromise !== promise) {
207
+ return; // Une nouvelle Promise a été créée, ignorer cette erreur
208
+ }
209
+
210
+ // Throw l'erreur
211
+ throw error;
212
+ }
213
+ }
214
+ ```
215
+
216
+ **Nouvelle méthode `refresh()`**:
217
+
218
+ ```typescript
219
+ public refresh(): void {
220
+ if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
221
+
222
+ // Marquer comme dirty (le recalcul se fera au prochain accès)
223
+ this._dirty = true;
224
+
225
+ // Notifier les dépendants pour qu'ils se marquent aussi comme dirty
226
+ this._notify();
227
+ }
228
+ ```
229
+
230
+ **Nouveaux champs privés**:
231
+
232
+ ```typescript
233
+ private _synchrone: boolean;
234
+ private _currentPromise?: Promise<T>;
235
+ ```
236
+
237
+ **Points d'attention**:
238
+
239
+ 1. **Type safety**: Le type de retour doit être `FlowDerivation<T | undefined>` quand `synchrone: true` avec une fonction async
240
+ 2. **Gestion des Promises**: En mode synchrone, les Promises sont gérées de manière asynchrone dans `_handleSyncPromise()`
241
+ 3. **Comparaison de valeurs**: Utiliser `===` uniquement en mode synchrone
242
+ 4. **Race conditions**: Utiliser `_currentPromise` pour tracker la Promise courante
243
+ 5. **Initialisation**: En mode synchrone, initialiser `_value` à `undefined`
244
+ 6. **Lazy evaluation**: Le comportement lazy reste le même, mais la résolution de Promise est asynchrone
245
+
246
+ ### 2.2 Mise à jour des creators
247
+
248
+ **Fichier**: `src/creators.ts`
249
+
250
+ **Implémentation `resourceAsync()`**:
251
+
252
+ ```typescript
253
+ export function resourceAsync<T>(fn: () => Promise<T>): FlowDerivation<Promise<T>> {
254
+ return derivation(() => fn());
255
+ }
256
+ ```
257
+
258
+ **Points d'attention**:
259
+
260
+ - La fonction `fn` ne reçoit pas de `TrackingContext` (comportement actuel)
261
+ - Retourne directement une `FlowDerivation<Promise<T>>`
262
+
263
+ **Implémentation `resource()`**:
264
+
265
+ ```typescript
266
+ export function resource<T>(fn: () => Promise<T>): FlowDerivation<T | undefined> {
267
+ return derivation(() => fn(), { synchrone: true });
268
+ }
269
+ ```
270
+
271
+ **Points d'attention**:
272
+
273
+ - Utilise l'option `synchrone: true` pour activer le mode synchrone
274
+ - Retourne `FlowDerivation<T | undefined>`
275
+
276
+ ### 2.3 Mise à jour des exports
277
+
278
+ **Fichier**: `src/advanced/index.ts`
279
+
280
+ **Modifications**:
281
+
282
+ ```typescript
283
+ // Supprimer (après migration complète)
284
+ // export { FlowResource } from "./resource";
285
+ // export { FlowResourceAsync } from "./resourceAsync";
286
+ // Plus besoin de FlowSynchronizer
287
+ ```
288
+
289
+ **Fichier**: `src/index.ts`
290
+
291
+ **Modifications**:
292
+
293
+ ```typescript
294
+ // Supprimer FlowResource et FlowResourceAsync des exports
295
+ export {
296
+ FlowArray,
297
+ type FlowArrayAction,
298
+ FlowMap,
299
+ FlowStream,
300
+ FlowStreamAsync,
301
+ // FlowResource, // À supprimer
302
+ // FlowResourceAsync, // À supprimer
303
+ ...
304
+ } from "./advanced/";
305
+ ```
306
+
307
+ ### 2.4 Migration des tests
308
+
309
+ **Fichiers**: `test/resource.test.ts`, `test/resourceAsync.test.ts`
310
+
311
+ **Actions**:
312
+
313
+ 1. Mettre à jour les imports pour utiliser `FlowDerivation`
314
+ 2. Adapter les types de retour des fonctions `resource()` et `resourceAsync()`
315
+ 3. Adapter les appels à `refresh()` : `resource.refresh()` puis `await resource.pick()` si nécessaire
316
+ 4. Vérifier que tous les tests passent avec la nouvelle implémentation
317
+ 5. Potentiellement fusionner les tests si la logique est similaire
318
+
319
+ **Points d'attention**:
320
+
321
+ - Les tests de `resource()` doivent maintenant tester `FlowDerivation<T | undefined>` avec `synchrone: true`
322
+ - Les tests de `resourceAsync()` doivent tester `FlowDerivation<Promise<T>>`
323
+ - Vérifier que le comportement lazy est toujours correct
324
+ - Vérifier que la comparaison de valeurs fonctionne en mode synchrone
325
+ - Vérifier que les race conditions sont gérées correctement
326
+
327
+ ### 2.5 Suppression des anciennes classes
328
+
329
+ **Fichiers**: `src/advanced/resource.ts`, `src/advanced/resourceAsync.ts`
330
+
331
+ **Actions** (après vérification que tout fonctionne):
332
+
333
+ 1. Supprimer `src/advanced/resource.ts`
334
+ 2. Supprimer `src/advanced/resourceAsync.ts`
335
+ 3. Nettoyer les exports dans `src/advanced/index.ts`
336
+
337
+ **Points d'attention**:
338
+
339
+ - Ne supprimer qu'après avoir vérifié que tous les tests passent
340
+ - Vérifier qu'aucun code externe n'importe directement ces classes
341
+ - Mettre à jour la documentation si nécessaire
342
+
343
+ ## 3. Ordre d'implémentation recommandé
344
+
345
+ 1. **Étape 1**: Ajouter l'option `synchrone` et la méthode `refresh()` à `FlowDerivation` et tester
346
+ 2. **Étape 2**: Implémenter la gestion des Promises en mode synchrone avec comparaison de valeurs
347
+ 3. **Étape 3**: Mettre à jour les creators `resource()` et `resourceAsync()`
348
+ 4. **Étape 4**: Mettre à jour les exports
349
+ 5. **Étape 5**: Migrer et adapter les tests
350
+ 6. **Étape 6**: Supprimer les anciennes classes `FlowResource` et `FlowResourceAsync`
351
+
352
+ ## 4. Cas limites à tester
353
+
354
+ 1. **Race conditions**: Appels multiples rapides à `refresh()` avec mode synchrone
355
+ 2. **Erreurs async**: Promises rejetées dans les derivations en mode synchrone
356
+ 3. **Lazy initialization**: Vérifier que le fetch ne se fait qu'au premier accès
357
+ 4. **Comparaison de valeurs**: Vérifier que les notifications ne se déclenchent que si la valeur change (mode synchrone uniquement)
358
+ 5. **Disposal**: Vérifier le cleanup correct
359
+ 6. **Dérivations sync avec refresh()**: Vérifier que ça fonctionne aussi pour les derivations non-async
360
+ 7. **Refresh puis accès**: Vérifier que `refresh()` puis `pick()`/`get()` déclenche bien le recalcul
361
+ 8. **Refresh sur dérivation non-initialisée**: Vérifier que `refresh()` fonctionne même si la dérivation n'a pas encore été accédée
362
+ 9. **Mode synchrone avec fonction sync**: Vérifier que TypeScript lève une erreur de type
363
+ 10. **Plusieurs Promises successives**: Vérifier que seule la dernière Promise résolue met à jour la valeur
364
+
365
+ ### To-dos
366
+
367
+ - [ ] Ajouter la méthode refresh() à FlowDerivation dans src/basic/derivation.ts avec gestion des derivations sync et async
368
+ - [ ] Créer la classe FlowSynchronizer dans src/advanced/synchronizer.ts avec lazy initialization, gestion des race conditions, et comparaison de valeurs
369
+ - [ ] Mettre à jour resource() et resourceAsync() dans src/creators.ts pour utiliser FlowDerivation et FlowSynchronizer
370
+ - [ ] Mettre à jour les exports dans src/advanced/index.ts et src/index.ts pour exporter FlowSynchronizer et supprimer FlowResource/FlowResourceAsync
371
+ - [ ] Adapter les tests dans test/resource.test.ts et test/resourceAsync.test.ts pour la nouvelle API
372
+ - [ ] Supprimer src/advanced/resource.ts et src/advanced/resourceAsync.ts après vérification
package/README.md CHANGED
@@ -1,188 +1,42 @@
1
1
  # Picoflow
2
2
 
3
- **Picoflow** is a lightweight reactive dataflow library that provides fine-grained reactive primitives. It gives you an intuitive API for signals, state, asynchronous resources, streams, derivations, effects, and reactive maps. Picoflow uses an explicit tracking context to automatically track reactive dependencies.
3
+ **Picoflow** is a lightweight reactive dataflow library that provides fine-grained reactive primitives. It gives you an intuitive API for signals, state, (asynchronous) derivations, effects, and reactive maps/arrays. Picoflow uses an explicit tracking context to automatically track reactive dependencies.
4
4
 
5
- > **Upgrading from v0.x?** See the [Upgrade Guide](UPGRADING.md) for migration instructions.
6
-
7
- ## Features
8
-
9
- - **Reactive Signals:** Manually trigger updates using `$signal.trigger()`.
10
- - **Reactive State:** Manage state with observables that expose getter/setter APIs.
11
- - **Asynchronous Resources:** Fetch and update asynchronous data reactively.
12
- - **Reactive Streams:** Create streams that update over time using an updater function.
13
- - **Derivations:** Compute derived values from other reactive primitives.
14
- - **Automatic Caching:** Derived values are cached and are re-evaluated only when one or more of their dependencies change.
15
- - **Lazy Evaluation:** Derivations are evaluated lazily—that is, the computation only runs if and when an effect actually uses the derived value.
16
- - **Reactive Maps:** Manage collections reactively with granular update notifications.
17
- - **Effects:** Automatically run side-effect functions when dependencies change.
18
- - **Explicit Control:** Choose between reactive (`.get(t)`) and non-reactive (`.pick()`) reads for fine-grained control.
5
+ > **Upgrading from v0.x?** See the [Upgrade Guide](https://ersbeth-web.gitlab.io/picoflow/guide/advanced/upgrading.html) for migration instructions.
19
6
 
20
7
  ## Installation
21
8
 
22
- Install via npm:
23
-
24
9
  ```bash
10
+ # npm
25
11
  npm install @ersbeth/picoflow
26
- ```
27
-
28
- ## Usage
29
-
30
- In Picoflow, we advise to prefix all reactive primitives and effects with `$` for improved readability. Rather than using a subscribe method, you always create an effect that tracks dependencies using a tracking context. Below are some examples:
31
-
32
- ### Creating a Signal
33
-
34
- ```ts
35
- import { signal, effect } from '@ersbeth/picoflow';
36
-
37
- const $signal = signal();
38
-
39
- const $effect = effect((t) => {
40
- // Automatically tracks $signal
41
- $signal.watch(t);
42
- console.log('$signal has been triggered');
43
- });
44
-
45
- // Trigger the signal
46
- $signal.trigger();
47
-
48
- // LOG -> $signal has been triggered
49
- ```
50
-
51
- ### Creating Reactive State
52
-
53
- ```ts
54
- import { state, effect } from '@ersbeth/picoflow';
55
-
56
- const $count = state(0);
57
-
58
- const $effect = effect((t) => {
59
- // Automatically tracks $count
60
- console.log('Count changed:', $count.get(t));
61
- });
62
-
63
- // Update the state
64
- $count.set(42);
65
-
66
- // LOG -> Count changed: 42
67
- ```
68
-
69
- ### Creating an Asynchronous Resource
70
-
71
- ```ts
72
- import { resource, effect } from '@ersbeth/picoflow';
73
-
74
- async function fetchData() {
75
- const response = await fetch('https://api.example.com/data');
76
- return response.json();
77
- }
78
-
79
- const $data = resource(fetchData, {});
80
-
81
- // Create an effect to watch for updates
82
- const $effect = effect((t) => {
83
- console.log('Resource updated:', $data.get(t));
84
- });
85
-
86
- // Later, you can trigger a new fetch
87
- $data.fetch();
88
- ```
89
-
90
- ### Creating a Reactive Stream
91
12
 
92
- ```ts
93
- import { stream, effect } from '@ersbeth/picoflow';
13
+ # pnpm
14
+ pnpm add @ersbeth/picoflow
94
15
 
95
- const $ticker = stream(
96
- (set) => {
97
- const interval = setInterval(() => set(Date.now()), 1000);
98
- // Return a disposer to clear the interval when the stream is disposed.
99
- return () => clearInterval(interval);
100
- },
101
- Date.now()
102
- );
103
-
104
- const $effect = effect((t) => {
105
- console.log('Current time:', $ticker.get(t));
106
- });
107
- ```
108
-
109
- ### Creating a Derived Value
110
-
111
- ```ts
112
- import { state, derivation, effect } from '@ersbeth/picoflow';
113
-
114
- const $a = state(10);
115
- const $b = state(20);
116
-
117
- // Create a derived value that sums $a and $b.
118
- const $sum = derivation((t) => {
119
- return $a.get(t) + $b.get(t);
120
- });
121
-
122
- const $effect = effect((t) => {
123
- console.log('Sum:', $sum.get(t));
124
- });
125
-
126
- // Update a dependency to see the derivation update.
127
- $a.set(15);
128
- ```
129
-
130
- ### Creating a Reactive Map
131
-
132
- Reactive maps allow fine-grained tracking of collection updates.
133
-
134
- ```ts
135
- import { map, effect } from '@ersbeth/picoflow';
136
-
137
- const $map = map({ foo: 'bar' });
138
-
139
- const $effect = effect((t) => {
140
- console.log('Map updated:', $map.get(t));
141
- });
142
-
143
- // Update the map to trigger updates
144
- $map.setAt('baz', 'qux');
145
- $map.delete('foo');
146
- ```
147
-
148
- ### Non-Reactive Reads
149
-
150
- Sometimes you need to read a value without creating a dependency. Use `pick()` for this:
151
-
152
- ```ts
153
- import { state, signal, effect } from '@ersbeth/picoflow';
154
-
155
- const $data = state({ count: 0 });
156
- const $trigger = signal();
157
-
158
- const $effect = effect((t) => {
159
- // React to $trigger changes only
160
- $trigger.watch(t);
161
-
162
- // Read $data without creating a dependency
163
- const snapshot = $data.pick();
164
- console.log('Triggered! Current data:', snapshot);
165
- });
166
-
167
- $data.set({ count: 1 }); // Does NOT trigger the effect
168
- $trigger.trigger(); // Triggers the effect, logs: { count: 1 }
16
+ # yarn
17
+ yarn add @ersbeth/picoflow
169
18
  ```
170
19
 
171
20
  ## Documentation
172
21
 
173
- For comprehensive guides and API documentation, visit:
174
-
175
- - **[Getting Started](docs/guide/getting-started.md)** - Quick introduction to PicoFlow
176
- - **[API Reference](docs/api/index.md)** - Complete API documentation
177
-
178
- Or run the documentation locally:
179
-
180
- ```bash
181
- pnpm docs:dev
182
- ```
183
-
184
- Then open [http://localhost:5173](http://localhost:5173) in your browser.
22
+ For comprehensive guides and API documentation, visit the [official website](https://ersbeth-web.gitlab.io/picoflow/)
185
23
 
186
24
  ## License
187
25
 
188
- This project is licensed under the [MIT License](LICENSE).
26
+ This project is licensed under the [MIT License](LICENSE).
27
+
28
+ ## TODO
29
+
30
+ - [x] implement refresh on derivations
31
+ - [x] add tests for refresh
32
+ - [x] improve tests for array / maps
33
+ - [x] use same API for array and maps
34
+ - [x] use async API for array updates
35
+ - [x] manage unified disposal in collections
36
+ - [ ] fix tests
37
+ - [ ] implement sets
38
+ - [x] implement await nodes
39
+ - [ ] add tests for await nodes
40
+ - [ ] add tests for raw nodes
41
+ - [ ] replace resources by await nodes
42
+ - [ ] think about streams
package/biome.json CHANGED
@@ -15,7 +15,10 @@
15
15
  "linter": {
16
16
  "enabled": true,
17
17
  "rules": {
18
- "recommended": true
18
+ "recommended": true,
19
+ "complexity": {
20
+ "noStaticOnlyClass": "off"
21
+ }
19
22
  }
20
23
  },
21
24
  "javascript": {