@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
@@ -1,5 +1,6 @@
1
- import { FlowObservable, TrackingContext } from '../basic';
2
- import { SolidDerivation, SolidResource } from './primitives';
1
+ import { FlowReadonly, FlowReadonlyAsync } from '../flow';
2
+ import { FlowTracker } from '../flow/base/flowTracker';
3
+ import { SolidResource } from './primitives';
3
4
  /**
4
5
  * Utility type that excludes Promise types from T.
5
6
  * Used to ensure type safety for synchronous derivations/resources.
@@ -8,57 +9,46 @@ import { SolidDerivation, SolidResource } from './primitives';
8
9
  */
9
10
  export type NotPromise<T> = T extends Promise<unknown> ? never : T;
10
11
  /**
11
- * Converts a FlowObservable of a Promise value into a SolidResource.
12
+ * Converts a FlowNode, FlowNodeAsync, or getter function into a SolidResource.
12
13
  *
13
- * @param flow - The FlowObservable to convert.
14
- * @returns A SolidResource wrapping the observable.
14
+ * @param flow - The FlowNode, FlowNodeAsync, or getter function to convert.
15
+ * @returns A SolidResource that wraps the value or computation.
15
16
  *
16
- * @public
17
- */
18
- export declare function from<T>(flow: FlowObservable<Promise<NotPromise<T>>>): SolidResource<NotPromise<T>>;
19
- /**
20
- * Converts a FlowObservable of a non-Promise value into a SolidDerivation.
21
- *
22
- * @param flow - The FlowObservable to convert.
23
- * @returns A SolidDerivation wrapping the observable.
24
- *
25
- * @public
26
- */
27
- export declare function from<T>(flow: FlowObservable<NotPromise<T>>): SolidDerivation<NotPromise<T>>;
28
- /**
29
- * Converts a FlowObservable into a Solid derivation or resource, depending on whether the value is synchronous or asynchronous.
30
- *
31
- * @param flow - The FlowObservable to convert.
32
- * @returns A SolidDerivation or SolidResource, depending on the input type.
17
+ * @remarks
18
+ * This function bridges PicoFlow's reactive system with SolidJS, allowing you to use
19
+ * PicoFlow nodes and computations within Solid components. All conversions return a
20
+ * `SolidResource`, which can handle both synchronous and asynchronous values seamlessly.
33
21
  *
34
- * @public
35
- */
36
- export declare function from<T>(flow: FlowObservable<Promise<NotPromise<T>>> | FlowObservable<NotPromise<T>>): SolidDerivation<NotPromise<T>> | SolidResource<NotPromise<T>>;
37
- /**
38
- * Converts a getter function returning a non-Promise value into a SolidDerivation.
22
+ * The conversion works with:
23
+ * - **FlowNode**: Synchronous reactive nodes (e.g., from `state()` or `derivation()`)
24
+ * - **FlowNodeAsync**: Asynchronous reactive nodes (e.g., from `resourceAsync()`)
25
+ * - **Getter functions**: Computation functions that use a FlowTracker to access reactive values
39
26
  *
40
- * @param flow - The getter function to convert.
41
- * @returns A SolidDerivation wrapping the getter.
27
+ * The created SolidResource automatically subscribes to the PicoFlow nodes and updates
28
+ * when their values change. The subscription is properly cleaned up when the Solid
29
+ * component unmounts.
42
30
  *
43
- * @public
44
- */
45
- export declare function from<T>(flow: (t: TrackingContext) => NotPromise<T>): SolidDerivation<NotPromise<T>>;
46
- /**
47
- * Converts a getter function returning a Promise into a SolidResource.
31
+ * @example
32
+ * ```typescript
33
+ * import { from } from 'picoflow/solid';
34
+ * import { state } from 'picoflow';
48
35
  *
49
- * @param flow - The getter function to convert.
50
- * @returns A SolidResource wrapping the getter.
36
+ * // Convert a PicoFlow state to a Solid resource
37
+ * const $count = state(0);
38
+ * const solidCount = from($count);
51
39
  *
52
- * @public
53
- */
54
- export declare function from<T>(flow: (t: TrackingContext) => Promise<NotPromise<T>>): SolidResource<NotPromise<T>>;
55
- /**
56
- * Converts a getter function into a Solid derivation or resource, depending on whether the returned value is synchronous or asynchronous.
40
+ * // Use in a Solid component
41
+ * function Counter() {
42
+ * return <div>Count: {solidCount.get()}</div>;
43
+ * }
57
44
  *
58
- * @param flow - The getter function to convert.
59
- * @returns A SolidDerivation or SolidResource, depending on the input type.
45
+ * // Or convert a computation function
46
+ * const solidDerived = from((t) => {
47
+ * return solidCount.get(t) * 2;
48
+ * });
49
+ * ```
60
50
  *
61
51
  * @public
62
52
  */
63
- export declare function from<T>(flow: ((t: TrackingContext) => NotPromise<T>) | ((t: TrackingContext) => Promise<NotPromise<T>>)): SolidDerivation<T> | SolidResource<T>;
53
+ export declare function from<T>(flow: FlowReadonlyAsync<T> | FlowReadonly<T> | ((t: FlowTracker) => T) | ((t: FlowTracker) => Promise<T>)): SolidResource<T>;
64
54
  //# sourceMappingURL=converters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../../../src/solid/converters.ts"],"names":[],"mappings":"AACA,OAAO,EAGN,cAAc,EACd,KAAK,eAAe,EACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,KAAK,eAAe,EAAE,aAAa,EAAc,MAAM,cAAc,CAAC;AAuI/E;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAEnE;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACrB,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAC1C,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACrB,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GACjC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACrB,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAC1E,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AACjE;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACrB,IAAI,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,UAAU,CAAC,CAAC,CAAC,GACzC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACrB,IAAI,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAClD,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACrB,IAAI,EACD,CAAC,CAAC,CAAC,EAAE,eAAe,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,GACvC,CAAC,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GACjD,eAAe,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../../../src/solid/converters.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAmE7C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACrB,IAAI,EACD,iBAAiB,CAAC,CAAC,CAAC,GACpB,YAAY,CAAC,CAAC,CAAC,GACf,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,CAAC,CAAC,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,GACjC,aAAa,CAAC,CAAC,CAAC,CAUlB"}
@@ -171,6 +171,7 @@ export declare class SolidResource<T> implements SolidObservable<T | undefined>
171
171
  * Triggers a refetch of the resource.
172
172
  */
173
173
  readonly refetch: () => void;
174
+ readonly set: (param: SolidGetter<T>) => void;
174
175
  /**
175
176
  * Creates a new SolidResource from a fetcher function.
176
177
  * @param fetcher - The async fetcher function.
@@ -1 +1 @@
1
- {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/solid/primitives.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,eAAe,EACpB,MAAM,UAAU,CAAC;AAElB;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IACjC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,CAAC;IACvD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAE9C;;;OAGG;gBACS,YAAY,EAAE,CAAC;CAK3B;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,eAAe,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,CAAC;IAC5D;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEtB;;;OAGG;gBACS,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;CAItC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,aAAa,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,GAAG,SAAS,CAAC;IACtE;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAClC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MACb,YAAY,GACZ,SAAS,GACT,SAAS,GACT,OAAO,GACP,YAAY,CAAC;IAChB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACrC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAE7B;;;OAGG;gBACS,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC;CAOtD"}
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/solid/primitives.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,eAAe,EACpB,MAAM,UAAU,CAAC;AAElB;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IACjC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,CAAC;IACvD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAE9C;;;OAGG;gBACS,YAAY,EAAE,CAAC;CAK3B;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,eAAe,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,CAAC;IAC5D;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEtB;;;OAGG;gBACS,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;CAItC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,aAAa,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,GAAG,SAAS,CAAC;IACtE;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAClC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MACb,YAAY,GACZ,SAAS,GACT,SAAS,GACT,OAAO,GACP,YAAY,CAAC;IAChB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACrC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAE7B,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAE9C;;;OAGG;gBACS,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC;CAStD"}
@@ -64,7 +64,7 @@ export default withMermaid(
64
64
  {
65
65
  text: "Upgrade from v0.x",
66
66
  link: "/guide/advanced/upgrading",
67
- }
67
+ },
68
68
  ],
69
69
  },
70
70
  ],
@@ -1 +1,81 @@
1
- [{"text":"Classes","collapsed":true,"items":[{"text":"FlowArray","link":"/api/classes/FlowArray.md"},{"text":"FlowConstant","link":"/api/classes/FlowConstant.md"},{"text":"FlowDerivation","link":"/api/classes/FlowDerivation.md"},{"text":"FlowEffect","link":"/api/classes/FlowEffect.md"},{"text":"FlowMap","link":"/api/classes/FlowMap.md"},{"text":"FlowObservable","link":"/api/classes/FlowObservable.md"},{"text":"FlowResource","link":"/api/classes/FlowResource.md"},{"text":"FlowResourceAsync","link":"/api/classes/FlowResourceAsync.md"},{"text":"FlowSignal","link":"/api/classes/FlowSignal.md"},{"text":"FlowState","link":"/api/classes/FlowState.md"},{"text":"FlowStream","link":"/api/classes/FlowStream.md"},{"text":"FlowStreamAsync","link":"/api/classes/FlowStreamAsync.md"},{"text":"SolidDerivation","link":"/api/classes/SolidDerivation.md"},{"text":"SolidResource","link":"/api/classes/SolidResource.md"},{"text":"SolidState","link":"/api/classes/SolidState.md"},{"text":"TrackingContext","link":"/api/classes/TrackingContext.md"}]},{"text":"Interfaces","collapsed":true,"items":[{"text":"FlowDisposable","link":"/api/interfaces/FlowDisposable.md"},{"text":"SolidObservable","link":"/api/interfaces/SolidObservable.md"}]},{"text":"Type Aliases","collapsed":true,"items":[{"text":"FlowArrayAction","link":"/api/type-aliases/FlowArrayAction.md"},{"text":"FlowStreamDisposer","link":"/api/type-aliases/FlowStreamDisposer.md"},{"text":"FlowStreamSetter","link":"/api/type-aliases/FlowStreamSetter.md"},{"text":"FlowStreamUpdater","link":"/api/type-aliases/FlowStreamUpdater.md"},{"text":"NotPromise","link":"/api/type-aliases/NotPromise.md"},{"text":"SolidGetter","link":"/api/type-aliases/SolidGetter.md"}]},{"text":"Functions","collapsed":true,"items":[{"text":"array","link":"/api/functions/array.md"},{"text":"constant","link":"/api/functions/constant.md"},{"text":"derivation","link":"/api/functions/derivation.md"},{"text":"effect","link":"/api/functions/effect.md"},{"text":"from","link":"/api/functions/from.md"},{"text":"isDisposable","link":"/api/functions/isDisposable.md"},{"text":"map","link":"/api/functions/map.md"},{"text":"resource","link":"/api/functions/resource.md"},{"text":"resourceAsync","link":"/api/functions/resourceAsync.md"},{"text":"signal","link":"/api/functions/signal.md"},{"text":"state","link":"/api/functions/state.md"},{"text":"stream","link":"/api/functions/stream.md"},{"text":"streamAsync","link":"/api/functions/streamAsync.md"}]}]
1
+ [
2
+ {
3
+ "text": "Classes",
4
+ "collapsed": true,
5
+ "items": [
6
+ { "text": "FlowArray", "link": "/api/classes/FlowArray.md" },
7
+ { "text": "FlowConstant", "link": "/api/classes/FlowConstant.md" },
8
+ { "text": "FlowDerivation", "link": "/api/classes/FlowDerivation.md" },
9
+ { "text": "FlowEffect", "link": "/api/classes/FlowEffect.md" },
10
+ { "text": "FlowMap", "link": "/api/classes/FlowMap.md" },
11
+ { "text": "FlowObservable", "link": "/api/classes/FlowObservable.md" },
12
+ { "text": "FlowResource", "link": "/api/classes/FlowResource.md" },
13
+ {
14
+ "text": "FlowResourceAsync",
15
+ "link": "/api/classes/FlowResourceAsync.md"
16
+ },
17
+ { "text": "FlowSignal", "link": "/api/classes/FlowSignal.md" },
18
+ { "text": "FlowState", "link": "/api/classes/FlowState.md" },
19
+ { "text": "FlowStream", "link": "/api/classes/FlowStream.md" },
20
+ { "text": "FlowStreamAsync", "link": "/api/classes/FlowStreamAsync.md" },
21
+ { "text": "SolidDerivation", "link": "/api/classes/SolidDerivation.md" },
22
+ { "text": "SolidResource", "link": "/api/classes/SolidResource.md" },
23
+ { "text": "SolidState", "link": "/api/classes/SolidState.md" },
24
+ { "text": "TrackingContext", "link": "/api/classes/TrackingContext.md" }
25
+ ]
26
+ },
27
+ {
28
+ "text": "Interfaces",
29
+ "collapsed": true,
30
+ "items": [
31
+ { "text": "FlowDisposable", "link": "/api/interfaces/FlowDisposable.md" },
32
+ {
33
+ "text": "SolidObservable",
34
+ "link": "/api/interfaces/SolidObservable.md"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "text": "Type Aliases",
40
+ "collapsed": true,
41
+ "items": [
42
+ {
43
+ "text": "FlowArrayAction",
44
+ "link": "/api/type-aliases/FlowArrayAction.md"
45
+ },
46
+ {
47
+ "text": "FlowStreamDisposer",
48
+ "link": "/api/type-aliases/FlowStreamDisposer.md"
49
+ },
50
+ {
51
+ "text": "FlowStreamSetter",
52
+ "link": "/api/type-aliases/FlowStreamSetter.md"
53
+ },
54
+ {
55
+ "text": "FlowStreamUpdater",
56
+ "link": "/api/type-aliases/FlowStreamUpdater.md"
57
+ },
58
+ { "text": "NotPromise", "link": "/api/type-aliases/NotPromise.md" },
59
+ { "text": "SolidGetter", "link": "/api/type-aliases/SolidGetter.md" }
60
+ ]
61
+ },
62
+ {
63
+ "text": "Functions",
64
+ "collapsed": true,
65
+ "items": [
66
+ { "text": "array", "link": "/api/functions/array.md" },
67
+ { "text": "constant", "link": "/api/functions/constant.md" },
68
+ { "text": "derivation", "link": "/api/functions/derivation.md" },
69
+ { "text": "effect", "link": "/api/functions/effect.md" },
70
+ { "text": "from", "link": "/api/functions/from.md" },
71
+ { "text": "isDisposable", "link": "/api/functions/isDisposable.md" },
72
+ { "text": "map", "link": "/api/functions/map.md" },
73
+ { "text": "resource", "link": "/api/functions/resource.md" },
74
+ { "text": "resourceAsync", "link": "/api/functions/resourceAsync.md" },
75
+ { "text": "signal", "link": "/api/functions/signal.md" },
76
+ { "text": "state", "link": "/api/functions/state.md" },
77
+ { "text": "stream", "link": "/api/functions/stream.md" },
78
+ { "text": "streamAsync", "link": "/api/functions/streamAsync.md" }
79
+ ]
80
+ }
81
+ ]
package/package.json CHANGED
@@ -1,59 +1,61 @@
1
1
  {
2
- "name": "@ersbeth/picoflow",
3
- "version": "1.0.0",
4
- "description": "Minimal Dataflow librairy for TypeScript",
5
- "type": "module",
6
- "exports": {
7
- "types": "./dist/types/index.d.ts",
8
- "default": "./dist/picoflow.js"
9
- },
10
- "imports": {
11
- "#package": "./src/index.ts"
12
- },
13
- "scripts": {
14
- "check": "pnpm exec tsc --noEmit",
15
- "lint": "pnpm exec biome check --fix",
16
- "build": "pnpm exec vite build --config vite.config.ts",
17
- "test": "pnpm exec vitest",
18
- "docs:dev": "vitepress dev docs",
19
- "docs:build": "pnpm docs:generate && vitepress build docs",
20
- "docs:preview": "vitepress preview docs",
21
- "docs:generate": "typedoc",
22
- "api:extract": "pnpm exec api-extractor run --local",
23
- "api:generate": "pnpm exec api-documenter markdown -i ./api/temp -o ./api/doc",
24
- "api:clean": "rm ./api/temp/*",
25
- "api": "pnpm api:extract && pnpm api:generate && pnpm api:clean"
26
- },
27
- "peerDependencies": {
28
- "solid-js": "^1.9.7"
29
- },
30
- "devDependencies": {
31
- "@biomejs/biome": "2.3.8",
32
- "@braintree/sanitize-url": "^7.1.1",
33
- "@vitest/coverage-v8": "^3.0.9",
34
- "cytoscape": "^3.33.1",
35
- "cytoscape-cose-bilkent": "^4.1.0",
36
- "dayjs": "^1.11.19",
37
- "dompurify": "^3.3.0",
38
- "mermaid": "^11.12.1",
39
- "typedoc": "^0.28.15",
40
- "typedoc-plugin-markdown": "^4.9.0",
41
- "typedoc-vitepress-theme": "^1.1.2",
42
- "typescript": "^5.9.3",
43
- "vite": "^6.2.3",
44
- "vite-plugin-dts": "^4.5.3",
45
- "vitepress": "^1.6.4",
46
- "vitepress-plugin-mermaid": "^2.0.17",
47
- "vitest": "^3.0.9"
48
- },
49
- "repository": {
50
- "type": "git",
51
- "url": "git+ssh://git@gitlab.com/ersbeth-web/picoflow.git"
52
- },
53
- "author": "Elisabeth Rousset",
54
- "license": "MIT",
55
- "bugs": {
56
- "url": "https://gitlab.com/ersbeth-web/picoflow/issues"
57
- },
58
- "homepage": "https://gitlab.com/ersbeth-web/picoflow#readme"
59
- }
2
+ "name": "@ersbeth/picoflow",
3
+ "version": "1.1.0",
4
+ "description": "Minimal Dataflow librairy for TypeScript",
5
+ "type": "module",
6
+ "exports": {
7
+ "types": "./dist/types/index.d.ts",
8
+ "default": "./dist/picoflow.js"
9
+ },
10
+ "imports": {
11
+ "#package": "./src/index.ts"
12
+ },
13
+ "peerDependencies": {
14
+ "solid-js": "^1.9.7"
15
+ },
16
+ "devDependencies": {
17
+ "@biomejs/biome": "2.3.8",
18
+ "@braintree/sanitize-url": "^7.1.1",
19
+ "@vitest/coverage-v8": "^3.0.9",
20
+ "cytoscape": "^3.33.1",
21
+ "cytoscape-cose-bilkent": "^4.1.0",
22
+ "dayjs": "^1.11.19",
23
+ "dompurify": "^3.3.0",
24
+ "jsdom": "^27.3.0",
25
+ "mermaid": "^11.12.1",
26
+ "typedoc": "^0.28.15",
27
+ "typedoc-plugin-markdown": "^4.9.0",
28
+ "typedoc-vitepress-theme": "^1.1.2",
29
+ "typescript": "^5.9.3",
30
+ "vite": "^6.2.3",
31
+ "vite-plugin-dts": "^4.5.3",
32
+ "vite-plugin-solid": "^2.11.10",
33
+ "vitepress": "^1.6.4",
34
+ "vitepress-plugin-mermaid": "^2.0.17",
35
+ "vitest": "^3.0.9"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+ssh://git@gitlab.com/ersbeth-web/picoflow.git"
40
+ },
41
+ "author": "Elisabeth Rousset",
42
+ "license": "MIT",
43
+ "bugs": {
44
+ "url": "https://gitlab.com/ersbeth-web/picoflow/issues"
45
+ },
46
+ "homepage": "https://gitlab.com/ersbeth-web/picoflow#readme",
47
+ "scripts": {
48
+ "check": "pnpm exec tsc --noEmit",
49
+ "lint": "pnpm exec biome check --fix",
50
+ "build": "pnpm exec vite build --config vite.config.ts",
51
+ "test": "pnpm exec vitest",
52
+ "docs:dev": "vitepress dev docs",
53
+ "docs:build": "pnpm docs:generate && vitepress build docs",
54
+ "docs:preview": "vitepress preview docs",
55
+ "docs:generate": "typedoc",
56
+ "api:extract": "pnpm exec api-extractor run --local",
57
+ "api:generate": "pnpm exec api-documenter markdown -i ./api/temp -o ./api/doc",
58
+ "api:clean": "rm ./api/temp/*",
59
+ "api": "pnpm api:extract && pnpm api:generate && pnpm api:clean"
60
+ }
61
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Contract for objects that own resources and must be cleaned up explicitly.
3
+ *
4
+ * @remarks
5
+ * Many PicoFlow primitives expose `dispose()` so you can tear down subscriptions,
6
+ * listeners, or reactive links when they are no longer needed (e.g., component
7
+ * unmount, feature teardown, test cleanup).
8
+ *
9
+ * Typical options:
10
+ * - `{ self: true }` — dispose only this instance (dependents decide their own lifecycle).
11
+ * - `{ self: false }` or omitted — also dispose dependents, if the implementation supports it.
12
+ *
13
+ * The exact cascade behavior depends on the concrete primitive; consult its docs.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const $state = state(0);
18
+ * const fx = effect((t) => console.log($state.get(t)));
19
+ *
20
+ * $state.dispose(); // Common teardown path
21
+ * $state.dispose({ self: true }); // Dispose only this instance if you want dependents to persist
22
+ * ```
23
+ *
24
+ * @public
25
+ */
26
+ export interface FlowDisposable {
27
+ /**
28
+ * Disposes resources held by this object.
29
+ *
30
+ * @param options - Optional disposal behavior.
31
+ * @param options.self - When true, dispose only this object. When false or
32
+ * omitted, the implementation may also dispose dependents.
33
+ *
34
+ * @remarks
35
+ * Use `dispose()` during teardown (unmount, test cleanup, feature shutdown).
36
+ * Calling `dispose()` more than once may throw; behavior is defined by the
37
+ * concrete implementation.
38
+ *
39
+ * @throws Error if the object has already been disposed (implementation-specific).
40
+ */
41
+ dispose(options?: { self: boolean }): void;
42
+ }
43
+
44
+ /**
45
+ * Runtime type guard that checks whether an object is disposable.
46
+ *
47
+ * @param obj - Value to test.
48
+ * @returns `true` if `obj` exposes a callable `dispose`; otherwise `false`.
49
+ *
50
+ * @remarks
51
+ * Use this for defensive cleanup in mixed collections or optional resources.
52
+ * It performs a runtime shape check; no assumptions about specific types are made.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * function cleanup(items: unknown[]) {
57
+ * for (const item of items) {
58
+ * if (isDisposable(item)) item.dispose();
59
+ * }
60
+ * }
61
+ * ```
62
+ *
63
+ * @public
64
+ */
65
+ export function isDisposable(obj: unknown): obj is FlowDisposable {
66
+ return (
67
+ obj !== null &&
68
+ obj !== undefined &&
69
+ typeof (obj as FlowDisposable).dispose === "function"
70
+ );
71
+ }
@@ -0,0 +1,171 @@
1
+ import { FlowGraph } from "./flowGraph";
2
+ import type { FlowSignal } from "./flowSignal";
3
+ import type { FlowTracker } from "./flowTracker";
4
+
5
+ /**
6
+ * Runs side-effect code in response to reactive state changes.
7
+ *
8
+ * @remarks
9
+ * A `FlowEffect` is the bridge between reactive data and the outside world
10
+ * (UI updates, logging, network calls, timers, etc.). It executes a callback
11
+ * with a tracking context so you can choose which observables/signals should
12
+ * re-trigger the effect:
13
+ *
14
+ * - Use `observable.get(t)` or `signal.watch(t)` to create dependencies.
15
+ * - Use `observable.pick()` for untracked reads that should not re-run the effect.
16
+ *
17
+ * The callback runs immediately when the effect is created and re-runs whenever
18
+ * any tracked dependency changes. The effect itself is the tracking context
19
+ * (it implements `FlowTracker`), so `t` in your callback is the effect instance.
20
+ *
21
+ * Async callbacks are supported: if the callback returns a promise, the effect
22
+ * awaits it before finishing the current run. A later change can re-run the
23
+ * effect even if a previous async run is still pending, so write idempotent
24
+ * side effects when working with async flows.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // Mixed tracked/untracked reads
29
+ * const fx = effect((t) => {
30
+ * const tracked = $stateA.get(t); // tracked -> re-runs when $stateA changes
31
+ * const snapshot = $config.pick(); // untracked -> does not re-run on changes
32
+ * console.log(tracked, snapshot);
33
+ * });
34
+ *
35
+ * // Async work
36
+ * const fxAsync = effect(async (t) => {
37
+ * const userId = $userId.get(t);
38
+ * const profile = await fetchProfile(userId);
39
+ * renderProfile(profile);
40
+ * });
41
+ * ```
42
+ *
43
+ * @public
44
+ */
45
+ export class FlowEffect {
46
+ private _disposed = false;
47
+ private _dependencies = new Set<FlowSignal>();
48
+ private _apply: (t: FlowTracker) => void | Promise<void>;
49
+ settled: Promise<void>;
50
+
51
+ /**
52
+ * Creates a new effect and runs it once immediately.
53
+ *
54
+ * @param apply - Side-effect function receiving the tracking context (`t`).
55
+ * It can be sync or async (returning `Promise<void>`).
56
+ *
57
+ * @remarks
58
+ * Use `t` to opt-in to reactive tracking:
59
+ * - `observable.get(t)` / `signal.watch(t)` to re-run when they change
60
+ * - `observable.pick()` for reads that should not re-run the effect
61
+ *
62
+ * The effect schedules its first run during construction. Each subsequent
63
+ * change to a tracked dependency queues another run.
64
+ *
65
+ * @public
66
+ */
67
+ constructor(apply: (t: FlowTracker) => void | Promise<void>) {
68
+ this._apply = apply;
69
+ this.settled = FlowGraph.requestRead(() => this._exec());
70
+ }
71
+
72
+ /**
73
+ * Stops the effect and detaches it from all tracked dependencies.
74
+ *
75
+ * @remarks
76
+ * Call this when the effect's work is no longer needed (e.g., component
77
+ * unmount, teardown of a feature). After disposal:
78
+ * - The effect will not re-run.
79
+ * - All dependency links are removed.
80
+ * - Calling `dispose()` again throws an error.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const fx = effect((t) => $signal.watch(t));
85
+ * // ... later
86
+ * fx.dispose();
87
+ * ```
88
+ *
89
+ * @public
90
+ */
91
+ public dispose(): void {
92
+ if (this._disposed) throw new Error("[PicoFlow] Effect is disposed");
93
+ Array.from(this._dependencies).forEach((dependency) => {
94
+ this._unregisterDependency(dependency);
95
+ });
96
+ this._disposed = true;
97
+ }
98
+
99
+ /**
100
+ * Whether the effect has been disposed.
101
+ *
102
+ * @returns `true` once disposal has run; `false` while the effect is active.
103
+ *
104
+ * @public
105
+ */
106
+ public get disposed(): boolean {
107
+ return this._disposed;
108
+ }
109
+
110
+ /** @internal */ async _requestExec(): Promise<void> {
111
+ this.settled = this._exec();
112
+ return this.settled;
113
+ }
114
+
115
+ /** @internal */ _registerDependency(dependency: FlowSignal): void {
116
+ this._dependencies.add(dependency);
117
+ dependency._registerEffect(this);
118
+ }
119
+
120
+ /** @internal */ _unregisterDependency(dependency: FlowSignal): void {
121
+ this._dependencies.delete(dependency);
122
+ dependency._unregisterEffect(this);
123
+ }
124
+
125
+ private async _exec(): Promise<void> {
126
+ if (this._disposed)
127
+ /* v8 ignore next 1 */
128
+ throw new Error("[PicoFlow] Effect is disposed");
129
+
130
+ const result = this._apply(this);
131
+ if (result instanceof Promise) {
132
+ await result;
133
+ }
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Creates a reactive effect that runs now and re-runs when its tracked
139
+ * dependencies change.
140
+ *
141
+ * @param fn - Side-effect function that receives the tracking context (`t`).
142
+ * @returns A new {@link FlowEffect}.
143
+ *
144
+ * @remarks
145
+ * Use an effect when you need to react to state changes with side effects
146
+ * (UI updates, logging, network calls, timers). For computing derived values,
147
+ * prefer derivations; for one-off work, use a normal function.
148
+ *
149
+ * - Track dependencies with `observable.get(t)` or `signal.watch(t)`.
150
+ * - Read without tracking using `observable.pick()`.
151
+ * - Async callbacks are supported; return a promise if you `await` inside.
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * const $count = state(0);
156
+ *
157
+ * const fx = effect((t) => {
158
+ * console.log("Count is:", $count.get(t));
159
+ * });
160
+ *
161
+ * $count.set(1); // Logs "Count is: 1"
162
+ * $count.set(2); // Logs "Count is: 2"
163
+ *
164
+ * fx.dispose(); // Stop reacting
165
+ * ```
166
+ *
167
+ * @public
168
+ */
169
+ export function effect(fn: (t: FlowTracker) => void): FlowEffect {
170
+ return new FlowEffect(fn);
171
+ }