@ersbeth/picoflow 1.1.1 → 2.0.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 (383) hide show
  1. package/.vscode/settings.json +3 -3
  2. package/CHANGELOG.md +43 -0
  3. package/README.md +2 -18
  4. package/biome.json +45 -35
  5. package/dist/picoflow.js +857 -1528
  6. package/dist/types/api/base/flowDisposable.d.ts +41 -0
  7. package/dist/types/api/base/flowDisposable.d.ts.map +1 -0
  8. package/dist/types/api/base/flowObservable.d.ts +27 -0
  9. package/dist/types/api/base/flowObservable.d.ts.map +1 -0
  10. package/dist/types/api/base/flowSubscribable.d.ts +79 -0
  11. package/dist/types/api/base/flowSubscribable.d.ts.map +1 -0
  12. package/dist/types/api/base/flowTracker.d.ts +8 -0
  13. package/dist/types/api/base/flowTracker.d.ts.map +1 -0
  14. package/dist/types/api/base/index.d.ts +5 -0
  15. package/dist/types/api/base/index.d.ts.map +1 -0
  16. package/dist/types/api/index.d.ts +3 -0
  17. package/dist/types/api/index.d.ts.map +1 -0
  18. package/dist/types/api/nodes/async/flowConstantAsync.d.ts +31 -0
  19. package/dist/types/api/nodes/async/flowConstantAsync.d.ts.map +1 -0
  20. package/dist/types/api/nodes/async/flowDerivationAsync.d.ts +37 -0
  21. package/dist/types/api/nodes/async/flowDerivationAsync.d.ts.map +1 -0
  22. package/dist/types/api/nodes/async/flowStateAsync.d.ts +41 -0
  23. package/dist/types/api/nodes/async/flowStateAsync.d.ts.map +1 -0
  24. package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts +30 -0
  25. package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts.map +1 -0
  26. package/dist/types/{flow → api}/nodes/async/index.d.ts +1 -2
  27. package/dist/types/api/nodes/async/index.d.ts.map +1 -0
  28. package/dist/types/api/nodes/collections/flowArray.d.ts +134 -0
  29. package/dist/types/api/nodes/collections/flowArray.d.ts.map +1 -0
  30. package/dist/types/api/nodes/collections/flowMap.d.ts +98 -0
  31. package/dist/types/api/nodes/collections/flowMap.d.ts.map +1 -0
  32. package/dist/types/api/nodes/collections/index.d.ts.map +1 -0
  33. package/dist/types/api/nodes/flowEffect.d.ts +28 -0
  34. package/dist/types/api/nodes/flowEffect.d.ts.map +1 -0
  35. package/dist/types/api/nodes/flowSignal.d.ts +25 -0
  36. package/dist/types/api/nodes/flowSignal.d.ts.map +1 -0
  37. package/dist/types/api/nodes/flowValue.d.ts +35 -0
  38. package/dist/types/api/nodes/flowValue.d.ts.map +1 -0
  39. package/dist/types/api/nodes/index.d.ts +8 -0
  40. package/dist/types/api/nodes/index.d.ts.map +1 -0
  41. package/dist/types/api/nodes/sync/flowConstant.d.ts +29 -0
  42. package/dist/types/api/nodes/sync/flowConstant.d.ts.map +1 -0
  43. package/dist/types/api/nodes/sync/flowDerivation.d.ts +36 -0
  44. package/dist/types/api/nodes/sync/flowDerivation.d.ts.map +1 -0
  45. package/dist/types/api/nodes/sync/flowState.d.ts +39 -0
  46. package/dist/types/api/nodes/sync/flowState.d.ts.map +1 -0
  47. package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts +28 -0
  48. package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts.map +1 -0
  49. package/dist/types/{flow → api}/nodes/sync/index.d.ts +1 -2
  50. package/dist/types/api/nodes/sync/index.d.ts.map +1 -0
  51. package/dist/types/api/nodes/utils.d.ts +22 -0
  52. package/dist/types/api/nodes/utils.d.ts.map +1 -0
  53. package/dist/types/base/disposable.d.ts +11 -0
  54. package/dist/types/base/disposable.d.ts.map +1 -0
  55. package/dist/types/base/executionStack.d.ts +14 -0
  56. package/dist/types/base/executionStack.d.ts.map +1 -0
  57. package/dist/types/base/index.d.ts +6 -0
  58. package/dist/types/base/index.d.ts.map +1 -0
  59. package/dist/types/base/node.d.ts +27 -0
  60. package/dist/types/base/node.d.ts.map +1 -0
  61. package/dist/types/base/observable.d.ts +37 -0
  62. package/dist/types/base/observable.d.ts.map +1 -0
  63. package/dist/types/base/observer.d.ts +25 -0
  64. package/dist/types/base/observer.d.ts.map +1 -0
  65. package/dist/types/converters/index.d.ts +2 -0
  66. package/dist/types/converters/index.d.ts.map +1 -0
  67. package/dist/types/converters/solid.d.ts +46 -0
  68. package/dist/types/converters/solid.d.ts.map +1 -0
  69. package/dist/types/index.d.ts +2 -63
  70. package/dist/types/index.d.ts.map +1 -1
  71. package/dist/types/nodes/arrayNode.d.ts +2 -0
  72. package/dist/types/nodes/arrayNode.d.ts.map +1 -0
  73. package/dist/types/nodes/effectNode.d.ts +2 -0
  74. package/dist/types/nodes/effectNode.d.ts.map +1 -0
  75. package/dist/types/nodes/index.d.ts +9 -0
  76. package/dist/types/nodes/index.d.ts.map +1 -0
  77. package/dist/types/nodes/mapNode.d.ts +2 -0
  78. package/dist/types/nodes/mapNode.d.ts.map +1 -0
  79. package/dist/types/nodes/signalNode.d.ts +2 -0
  80. package/dist/types/nodes/signalNode.d.ts.map +1 -0
  81. package/dist/types/nodes/valueAsyncNode.d.ts +2 -0
  82. package/dist/types/nodes/valueAsyncNode.d.ts.map +1 -0
  83. package/dist/types/nodes/valueNode.d.ts +2 -0
  84. package/dist/types/nodes/valueNode.d.ts.map +1 -0
  85. package/dist/types/nodes/valueSyncNode.d.ts +2 -0
  86. package/dist/types/nodes/valueSyncNode.d.ts.map +1 -0
  87. package/dist/types/schedulers/asyncResolver.d.ts +2 -0
  88. package/dist/types/schedulers/asyncResolver.d.ts.map +1 -0
  89. package/dist/types/schedulers/asyncScheduler.d.ts +2 -0
  90. package/dist/types/schedulers/asyncScheduler.d.ts.map +1 -0
  91. package/dist/types/schedulers/index.d.ts +5 -0
  92. package/dist/types/schedulers/index.d.ts.map +1 -0
  93. package/dist/types/schedulers/pendingError.d.ts +2 -0
  94. package/dist/types/schedulers/pendingError.d.ts.map +1 -0
  95. package/dist/types/schedulers/scheduler.d.ts +2 -0
  96. package/dist/types/schedulers/scheduler.d.ts.map +1 -0
  97. package/dist/types/schedulers/syncResolver.d.ts +2 -0
  98. package/dist/types/schedulers/syncResolver.d.ts.map +1 -0
  99. package/dist/types/schedulers/syncScheduler.d.ts +2 -0
  100. package/dist/types/schedulers/syncScheduler.d.ts.map +1 -0
  101. package/docs/.vitepress/config.mts +128 -93
  102. package/docs/api/functions/array.md +14 -37
  103. package/docs/api/functions/constant.md +13 -25
  104. package/docs/api/functions/constantAsync.md +69 -0
  105. package/docs/api/functions/derivation.md +14 -33
  106. package/docs/api/functions/derivationAsync.md +34 -0
  107. package/docs/api/functions/from.md +62 -153
  108. package/docs/api/functions/isDisposable.md +8 -30
  109. package/docs/api/functions/map.md +15 -36
  110. package/docs/api/functions/signal.md +8 -23
  111. package/docs/api/functions/state.md +43 -23
  112. package/docs/api/functions/stateAsync.md +69 -0
  113. package/docs/api/functions/subscribe.md +40 -0
  114. package/docs/api/functions/writableDerivation.md +33 -0
  115. package/docs/api/functions/writableDerivationAsync.md +34 -0
  116. package/docs/api/index.md +45 -102
  117. package/docs/api/interfaces/FlowArray.md +439 -0
  118. package/docs/api/interfaces/FlowConstant.md +220 -0
  119. package/docs/api/interfaces/FlowConstantAsync.md +221 -0
  120. package/docs/api/interfaces/FlowDerivation.md +241 -0
  121. package/docs/api/interfaces/FlowDerivationAsync.md +242 -0
  122. package/docs/api/interfaces/FlowDisposable.md +32 -38
  123. package/docs/api/interfaces/FlowEffect.md +64 -0
  124. package/docs/api/interfaces/FlowMap.md +374 -0
  125. package/docs/api/interfaces/FlowObservable.md +155 -0
  126. package/docs/api/interfaces/FlowSignal.md +156 -0
  127. package/docs/api/interfaces/FlowState.md +269 -0
  128. package/docs/api/interfaces/FlowStateAsync.md +268 -0
  129. package/docs/api/interfaces/FlowSubscribable.md +55 -0
  130. package/docs/api/interfaces/FlowTracker.md +61 -0
  131. package/docs/api/interfaces/FlowValue.md +222 -0
  132. package/docs/api/interfaces/FlowWritableDerivation.md +292 -0
  133. package/docs/api/interfaces/FlowWritableDerivationAsync.md +293 -0
  134. package/docs/api/type-aliases/DerivationFunction.md +28 -0
  135. package/docs/api/type-aliases/DerivationFunctionAsync.md +28 -0
  136. package/docs/api/type-aliases/FlowArrayAction.md +19 -8
  137. package/docs/api/type-aliases/FlowDataTracker.md +33 -0
  138. package/docs/api/type-aliases/FlowMapAction.md +48 -0
  139. package/docs/api/type-aliases/FlowOnDataListener.md +33 -0
  140. package/docs/api/type-aliases/FlowOnErrorListener.md +27 -0
  141. package/docs/api/type-aliases/FlowOnPendingListener.md +21 -0
  142. package/docs/api/type-aliases/FlowReadonly.md +22 -0
  143. package/docs/api/type-aliases/InitFunction.md +21 -0
  144. package/docs/api/type-aliases/InitFunctionAsync.md +21 -0
  145. package/docs/api/type-aliases/NotPromise.md +6 -3
  146. package/docs/api/type-aliases/UpdateFunction.md +27 -0
  147. package/docs/api/type-aliases/UpdateFunctionAsync.md +27 -0
  148. package/docs/api/typedoc-sidebar.json +1 -81
  149. package/docs/examples/examples.md +0 -2
  150. package/docs/guide/advanced/architecture.md +1234 -0
  151. package/docs/guide/advanced/migration-v2.md +204 -0
  152. package/docs/guide/advanced/solidjs.md +2 -88
  153. package/docs/guide/introduction/concepts.md +4 -3
  154. package/docs/guide/introduction/conventions.md +2 -33
  155. package/docs/guide/introduction/getting-started.md +28 -23
  156. package/docs/guide/introduction/lifecycle.md +16 -19
  157. package/docs/guide/primitives/array.md +102 -216
  158. package/docs/guide/primitives/constant.md +39 -212
  159. package/docs/guide/primitives/derivations.md +55 -122
  160. package/docs/guide/primitives/effects.md +155 -241
  161. package/docs/guide/primitives/map.md +64 -186
  162. package/docs/guide/primitives/overview.md +45 -128
  163. package/docs/guide/primitives/signal.md +51 -88
  164. package/docs/guide/primitives/state.md +34 -130
  165. package/package.json +56 -60
  166. package/src/api/base/flowDisposable.ts +44 -0
  167. package/src/api/base/flowObservable.ts +28 -0
  168. package/src/api/base/flowSubscribable.ts +87 -0
  169. package/src/api/base/flowTracker.ts +7 -0
  170. package/src/api/base/index.ts +4 -0
  171. package/src/{flow → api}/index.ts +0 -1
  172. package/src/api/nodes/async/flowConstantAsync.ts +36 -0
  173. package/src/api/nodes/async/flowDerivationAsync.ts +42 -0
  174. package/src/api/nodes/async/flowStateAsync.ts +47 -0
  175. package/src/api/nodes/async/flowWritableDerivationAsync.ts +33 -0
  176. package/src/{flow → api}/nodes/async/index.ts +1 -2
  177. package/src/api/nodes/collections/flowArray.ts +155 -0
  178. package/src/api/nodes/collections/flowMap.ts +115 -0
  179. package/src/api/nodes/flowEffect.ts +42 -0
  180. package/src/api/nodes/flowSignal.ts +28 -0
  181. package/src/api/nodes/flowValue.ts +36 -0
  182. package/src/api/nodes/index.ts +7 -0
  183. package/src/api/nodes/sync/flowConstant.ts +33 -0
  184. package/src/api/nodes/sync/flowDerivation.ts +41 -0
  185. package/src/api/nodes/sync/flowState.ts +45 -0
  186. package/src/api/nodes/sync/flowWritableDerivation.ts +31 -0
  187. package/src/{flow → api}/nodes/sync/index.ts +1 -2
  188. package/src/api/nodes/utils.ts +22 -0
  189. package/src/base/disposable.ts +18 -0
  190. package/src/base/executionStack.ts +42 -0
  191. package/src/base/index.ts +5 -0
  192. package/src/base/node.ts +98 -0
  193. package/src/base/observable.ts +87 -0
  194. package/src/base/observer.ts +51 -0
  195. package/src/converters/index.ts +1 -0
  196. package/src/converters/solid.ts +109 -0
  197. package/src/index.ts +2 -64
  198. package/src/nodes/arrayNode.ts +172 -0
  199. package/src/nodes/effectNode.ts +59 -0
  200. package/src/nodes/index.ts +8 -0
  201. package/src/nodes/mapNode.ts +127 -0
  202. package/src/nodes/signalNode.ts +21 -0
  203. package/src/nodes/valueAsyncNode.ts +88 -0
  204. package/src/nodes/valueNode.ts +144 -0
  205. package/src/nodes/valueSyncNode.ts +128 -0
  206. package/src/schedulers/asyncResolver.ts +78 -0
  207. package/src/schedulers/asyncScheduler.ts +66 -0
  208. package/src/schedulers/index.ts +4 -0
  209. package/src/schedulers/pendingError.ts +13 -0
  210. package/src/schedulers/scheduler.ts +9 -0
  211. package/src/schedulers/syncResolver.ts +69 -0
  212. package/src/schedulers/syncScheduler.ts +55 -0
  213. package/test/base/pendingError.test.ts +67 -0
  214. package/test/converters/solid.derivation.browser.test.tsx +69 -0
  215. package/test/converters/solid.node.test.ts +654 -0
  216. package/test/converters/solid.state.browser.test.tsx +1592 -0
  217. package/test/reactivity/flowSignal.test.ts +226 -0
  218. package/test/reactivity/nodes/async/asyncScheduler/asyncResolver.test.ts +593 -0
  219. package/test/reactivity/nodes/async/asyncScheduler/asyncScheduler.test.ts +317 -0
  220. package/test/reactivity/nodes/async/flowConstantAsync.test.ts +652 -0
  221. package/test/reactivity/nodes/async/flowDerivation.test.ts +898 -0
  222. package/test/reactivity/nodes/async/flowDerivationAsync.test.ts +1716 -0
  223. package/test/reactivity/nodes/async/flowStateAsync.test.ts +708 -0
  224. package/test/reactivity/nodes/async/flowWritableDerivationAsync.test.ts +614 -0
  225. package/test/reactivity/nodes/collections/flowArray.asyncStates.test.ts +1289 -0
  226. package/test/reactivity/nodes/collections/flowArray.scalars.test.ts +961 -0
  227. package/test/reactivity/nodes/collections/flowArray.states.test.ts +1035 -0
  228. package/test/reactivity/nodes/collections/flowMap.asyncStates.test.ts +960 -0
  229. package/test/reactivity/nodes/collections/flowMap.scalars.test.ts +775 -0
  230. package/test/reactivity/nodes/collections/flowMap.states.test.ts +958 -0
  231. package/test/reactivity/nodes/sync/flowConstant.test.ts +377 -0
  232. package/test/reactivity/nodes/sync/flowDerivation.test.ts +896 -0
  233. package/test/reactivity/nodes/sync/flowState.test.ts +341 -0
  234. package/test/reactivity/nodes/sync/flowWritableDerivation.test.ts +603 -0
  235. package/test/vitest.d.ts +10 -0
  236. package/tsconfig.json +31 -20
  237. package/typedoc.json +35 -35
  238. package/vite.config.ts +25 -23
  239. package/vitest.browser.config.ts +21 -0
  240. package/vitest.config.ts +12 -12
  241. package/.cursor/plans/unifier-flowresource-avec-flowderivation-c9506e24.plan.md +0 -372
  242. package/.cursor/plans/update-js-e795d61b.plan.md +0 -567
  243. package/dist/types/flow/base/flowDisposable.d.ts +0 -67
  244. package/dist/types/flow/base/flowDisposable.d.ts.map +0 -1
  245. package/dist/types/flow/base/flowEffect.d.ts +0 -127
  246. package/dist/types/flow/base/flowEffect.d.ts.map +0 -1
  247. package/dist/types/flow/base/flowGraph.d.ts +0 -97
  248. package/dist/types/flow/base/flowGraph.d.ts.map +0 -1
  249. package/dist/types/flow/base/flowSignal.d.ts +0 -134
  250. package/dist/types/flow/base/flowSignal.d.ts.map +0 -1
  251. package/dist/types/flow/base/flowTracker.d.ts +0 -15
  252. package/dist/types/flow/base/flowTracker.d.ts.map +0 -1
  253. package/dist/types/flow/base/index.d.ts +0 -7
  254. package/dist/types/flow/base/index.d.ts.map +0 -1
  255. package/dist/types/flow/base/utils.d.ts +0 -20
  256. package/dist/types/flow/base/utils.d.ts.map +0 -1
  257. package/dist/types/flow/collections/flowArray.d.ts +0 -148
  258. package/dist/types/flow/collections/flowArray.d.ts.map +0 -1
  259. package/dist/types/flow/collections/flowMap.d.ts +0 -224
  260. package/dist/types/flow/collections/flowMap.d.ts.map +0 -1
  261. package/dist/types/flow/collections/index.d.ts.map +0 -1
  262. package/dist/types/flow/index.d.ts +0 -4
  263. package/dist/types/flow/index.d.ts.map +0 -1
  264. package/dist/types/flow/nodes/async/flowConstantAsync.d.ts +0 -137
  265. package/dist/types/flow/nodes/async/flowConstantAsync.d.ts.map +0 -1
  266. package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts +0 -137
  267. package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts.map +0 -1
  268. package/dist/types/flow/nodes/async/flowNodeAsync.d.ts +0 -343
  269. package/dist/types/flow/nodes/async/flowNodeAsync.d.ts.map +0 -1
  270. package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts +0 -81
  271. package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts.map +0 -1
  272. package/dist/types/flow/nodes/async/flowStateAsync.d.ts +0 -111
  273. package/dist/types/flow/nodes/async/flowStateAsync.d.ts.map +0 -1
  274. package/dist/types/flow/nodes/async/index.d.ts.map +0 -1
  275. package/dist/types/flow/nodes/index.d.ts +0 -3
  276. package/dist/types/flow/nodes/index.d.ts.map +0 -1
  277. package/dist/types/flow/nodes/sync/flowConstant.d.ts +0 -108
  278. package/dist/types/flow/nodes/sync/flowConstant.d.ts.map +0 -1
  279. package/dist/types/flow/nodes/sync/flowDerivation.d.ts +0 -100
  280. package/dist/types/flow/nodes/sync/flowDerivation.d.ts.map +0 -1
  281. package/dist/types/flow/nodes/sync/flowNode.d.ts +0 -314
  282. package/dist/types/flow/nodes/sync/flowNode.d.ts.map +0 -1
  283. package/dist/types/flow/nodes/sync/flowReadonly.d.ts +0 -57
  284. package/dist/types/flow/nodes/sync/flowReadonly.d.ts.map +0 -1
  285. package/dist/types/flow/nodes/sync/flowState.d.ts +0 -96
  286. package/dist/types/flow/nodes/sync/flowState.d.ts.map +0 -1
  287. package/dist/types/flow/nodes/sync/index.d.ts.map +0 -1
  288. package/dist/types/solid/converters.d.ts +0 -53
  289. package/dist/types/solid/converters.d.ts.map +0 -1
  290. package/dist/types/solid/index.d.ts +0 -3
  291. package/dist/types/solid/index.d.ts.map +0 -1
  292. package/dist/types/solid/primitives.d.ts +0 -181
  293. package/dist/types/solid/primitives.d.ts.map +0 -1
  294. package/docs/api/classes/FlowArray.md +0 -489
  295. package/docs/api/classes/FlowConstant.md +0 -350
  296. package/docs/api/classes/FlowDerivation.md +0 -334
  297. package/docs/api/classes/FlowEffect.md +0 -100
  298. package/docs/api/classes/FlowMap.md +0 -512
  299. package/docs/api/classes/FlowObservable.md +0 -306
  300. package/docs/api/classes/FlowResource.md +0 -380
  301. package/docs/api/classes/FlowResourceAsync.md +0 -362
  302. package/docs/api/classes/FlowSignal.md +0 -160
  303. package/docs/api/classes/FlowState.md +0 -368
  304. package/docs/api/classes/FlowStream.md +0 -367
  305. package/docs/api/classes/FlowStreamAsync.md +0 -364
  306. package/docs/api/classes/SolidDerivation.md +0 -75
  307. package/docs/api/classes/SolidResource.md +0 -91
  308. package/docs/api/classes/SolidState.md +0 -71
  309. package/docs/api/classes/TrackingContext.md +0 -33
  310. package/docs/api/functions/effect.md +0 -49
  311. package/docs/api/functions/resource.md +0 -52
  312. package/docs/api/functions/resourceAsync.md +0 -50
  313. package/docs/api/functions/stream.md +0 -53
  314. package/docs/api/functions/streamAsync.md +0 -50
  315. package/docs/api/interfaces/SolidObservable.md +0 -19
  316. package/docs/api/type-aliases/FlowStreamDisposer.md +0 -15
  317. package/docs/api/type-aliases/FlowStreamSetter.md +0 -27
  318. package/docs/api/type-aliases/FlowStreamUpdater.md +0 -32
  319. package/docs/api/type-aliases/SolidGetter.md +0 -17
  320. package/docs/guide/primitives/resources.md +0 -858
  321. package/docs/guide/primitives/streams.md +0 -931
  322. package/src/flow/base/flowDisposable.ts +0 -71
  323. package/src/flow/base/flowEffect.ts +0 -171
  324. package/src/flow/base/flowGraph.ts +0 -288
  325. package/src/flow/base/flowSignal.ts +0 -207
  326. package/src/flow/base/flowTracker.ts +0 -17
  327. package/src/flow/base/index.ts +0 -6
  328. package/src/flow/base/utils.ts +0 -19
  329. package/src/flow/collections/flowArray.ts +0 -409
  330. package/src/flow/collections/flowMap.ts +0 -398
  331. package/src/flow/nodes/async/flowConstantAsync.ts +0 -142
  332. package/src/flow/nodes/async/flowDerivationAsync.ts +0 -143
  333. package/src/flow/nodes/async/flowNodeAsync.ts +0 -474
  334. package/src/flow/nodes/async/flowReadonlyAsync.ts +0 -81
  335. package/src/flow/nodes/async/flowStateAsync.ts +0 -116
  336. package/src/flow/nodes/await/advanced/index.ts +0 -5
  337. package/src/flow/nodes/await/advanced/resource.ts +0 -134
  338. package/src/flow/nodes/await/advanced/resourceAsync.ts +0 -109
  339. package/src/flow/nodes/await/advanced/stream.ts +0 -188
  340. package/src/flow/nodes/await/advanced/streamAsync.ts +0 -176
  341. package/src/flow/nodes/await/flowConstantAwait.ts +0 -154
  342. package/src/flow/nodes/await/flowDerivationAwait.ts +0 -154
  343. package/src/flow/nodes/await/flowNodeAwait.ts +0 -508
  344. package/src/flow/nodes/await/flowReadonlyAwait.ts +0 -89
  345. package/src/flow/nodes/await/flowStateAwait.ts +0 -130
  346. package/src/flow/nodes/await/index.ts +0 -5
  347. package/src/flow/nodes/index.ts +0 -3
  348. package/src/flow/nodes/sync/flowConstant.ts +0 -111
  349. package/src/flow/nodes/sync/flowDerivation.ts +0 -105
  350. package/src/flow/nodes/sync/flowNode.ts +0 -439
  351. package/src/flow/nodes/sync/flowReadonly.ts +0 -57
  352. package/src/flow/nodes/sync/flowState.ts +0 -101
  353. package/src/solid/converters.ts +0 -139
  354. package/src/solid/index.ts +0 -2
  355. package/src/solid/primitives.ts +0 -215
  356. package/test/base/flowEffect.test.ts +0 -108
  357. package/test/base/flowGraph.test.ts +0 -485
  358. package/test/base/flowSignal.test.ts +0 -372
  359. package/test/collections/flowArray.asyncStates.test.ts +0 -1553
  360. package/test/collections/flowArray.scalars.test.ts +0 -1129
  361. package/test/collections/flowArray.states.test.ts +0 -1365
  362. package/test/collections/flowMap.asyncStates.test.ts +0 -1105
  363. package/test/collections/flowMap.scalars.test.ts +0 -877
  364. package/test/collections/flowMap.states.test.ts +0 -1097
  365. package/test/nodes/async/flowConstantAsync.test.ts +0 -860
  366. package/test/nodes/async/flowDerivationAsync.test.ts +0 -1517
  367. package/test/nodes/async/flowStateAsync.test.ts +0 -1387
  368. package/test/nodes/await/advanced/resource.test.ts +0 -129
  369. package/test/nodes/await/advanced/resourceAsync.test.ts +0 -108
  370. package/test/nodes/await/advanced/stream.test.ts +0 -198
  371. package/test/nodes/await/advanced/streamAsync.test.ts +0 -196
  372. package/test/nodes/await/flowConstantAwait.test.ts +0 -643
  373. package/test/nodes/await/flowDerivationAwait.test.ts +0 -1583
  374. package/test/nodes/await/flowStateAwait.test.ts +0 -999
  375. package/test/nodes/mixed/derivation.test.ts +0 -1527
  376. package/test/nodes/sync/flowConstant.test.ts +0 -620
  377. package/test/nodes/sync/flowDerivation.test.ts +0 -1373
  378. package/test/nodes/sync/flowState.test.ts +0 -945
  379. package/test/solid/converters.test.ts +0 -721
  380. package/test/solid/primitives.test.ts +0 -1031
  381. /package/dist/types/{flow → api/nodes}/collections/index.d.ts +0 -0
  382. /package/docs/guide/advanced/{upgrading.md → migration-v1.md} +0 -0
  383. /package/src/{flow → api/nodes}/collections/index.ts +0 -0
@@ -1,1527 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import {
3
- derivation,
4
- derivationAsync,
5
- effect,
6
- FlowGraph,
7
- signal,
8
- state,
9
- } from "#package";
10
-
11
- describe("derivationAsync", () => {
12
- beforeEach(() => {
13
- FlowGraph.clear();
14
- });
15
-
16
- describe("unit", () => {
17
- describe("constructor", () => {
18
- it("should create derivation with compute function (lazy evaluation)", () => {
19
- const computeFn = vi.fn(async () => 42);
20
- const $derivation = derivationAsync(computeFn);
21
-
22
- expect(computeFn).not.toHaveBeenCalled();
23
- expect($derivation).toBeDefined();
24
- });
25
-
26
- it("should not execute compute function immediately upon creation", () => {
27
- const computeFn = vi.fn(async () => 42);
28
- derivationAsync(computeFn);
29
-
30
- expect(computeFn).not.toHaveBeenCalled();
31
- });
32
-
33
- it("should execute compute function on first access via pick()", async () => {
34
- const computeFn = vi.fn(async () => 42);
35
- const $derivation = derivationAsync(computeFn);
36
-
37
- expect(computeFn).not.toHaveBeenCalled();
38
- await $derivation.pick();
39
- expect(computeFn).toHaveBeenCalledTimes(1);
40
- });
41
-
42
- it("should execute compute function on first access via get()", async () => {
43
- const computeFn = vi.fn(async () => 42);
44
- const $derivation = derivationAsync(computeFn);
45
- const $tracker = signal();
46
-
47
- expect(computeFn).not.toHaveBeenCalled();
48
- await $derivation.get($tracker);
49
- expect(computeFn).toHaveBeenCalledTimes(1);
50
- });
51
- });
52
-
53
- describe("get()", () => {
54
- it("should return Promise<T> when called with tracker", async () => {
55
- const $state = state(10);
56
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
57
- const $tracker = signal();
58
-
59
- const result = $derivation.get($tracker);
60
- expect(result).toBeInstanceOf(Promise);
61
- expect(await result).toBe(20);
62
- });
63
-
64
- it("should register dependency when called with tracker", async () => {
65
- const $state = state(10);
66
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
67
- const $tracker = signal();
68
-
69
- await $derivation.get($tracker);
70
- expect($derivation.disposed).toBe(false);
71
-
72
- $state.set(20);
73
- expect(await $derivation.pick()).toBe(40);
74
- });
75
-
76
- it("should throw error when called after disposal", async () => {
77
- const $state = state(10);
78
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
79
- const $tracker = signal();
80
-
81
- $derivation.dispose();
82
- await expect($derivation.get($tracker)).rejects.toThrow(
83
- "[PicoFlow] Primitive is disposed",
84
- );
85
- });
86
-
87
- it("should compute value lazily on first get() call", async () => {
88
- const computeFn = vi.fn(async () => 42);
89
- const $derivation = derivationAsync(computeFn);
90
- const $tracker = signal();
91
-
92
- expect(computeFn).not.toHaveBeenCalled();
93
- await $derivation.get($tracker);
94
- expect(computeFn).toHaveBeenCalledTimes(1);
95
- });
96
-
97
- it("should cache computed value until dependencies change", async () => {
98
- const $state = state(10);
99
- const computeFn = vi.fn(async (t) => $state.get(t) * 2);
100
- const $derivation = derivationAsync(computeFn);
101
- const $tracker = signal();
102
-
103
- await $derivation.get($tracker);
104
- expect(computeFn).toHaveBeenCalledTimes(1);
105
-
106
- await $derivation.get($tracker);
107
- expect(computeFn).toHaveBeenCalledTimes(1);
108
-
109
- $state.set(20);
110
- await $derivation.get($tracker);
111
- expect(computeFn).toHaveBeenCalledTimes(2);
112
- });
113
-
114
- it("should recompute when dependencies change", async () => {
115
- const $state = state(10);
116
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
117
- const $tracker = signal();
118
-
119
- expect(await $derivation.get($tracker)).toBe(20);
120
-
121
- $state.set(20);
122
- expect(await $derivation.get($tracker)).toBe(40);
123
- });
124
- });
125
-
126
- describe("pick()", () => {
127
- it("should return Promise<T> without tracking", async () => {
128
- const $state = state(10);
129
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
130
-
131
- const result = $derivation.pick();
132
- expect(result).toBeInstanceOf(Promise);
133
- expect(await result).toBe(20);
134
- });
135
-
136
- it("should not register dependency when called", async () => {
137
- const $state = state(10);
138
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
139
-
140
- await $derivation.pick();
141
- expect(await $derivation.pick()).toBe(20);
142
-
143
- $state.set(20);
144
- expect(await $derivation.pick()).toBe(40);
145
- });
146
-
147
- it("should throw error when called after disposal", async () => {
148
- const $state = state(10);
149
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
150
-
151
- $derivation.dispose();
152
- await expect($derivation.pick()).rejects.toThrow(
153
- "[PicoFlow] Primitive is disposed",
154
- );
155
- });
156
-
157
- it("should compute value lazily on first pick() call", async () => {
158
- const computeFn = vi.fn(async () => 42);
159
- const $derivation = derivationAsync(computeFn);
160
-
161
- expect(computeFn).not.toHaveBeenCalled();
162
- await $derivation.pick();
163
- expect(computeFn).toHaveBeenCalledTimes(1);
164
- });
165
-
166
- it("should use cached value when not dirty", async () => {
167
- const $state = state(10);
168
- const computeFn = vi.fn(async (t) => $state.get(t) * 2);
169
- const $derivation = derivationAsync(computeFn);
170
-
171
- await $derivation.pick();
172
- expect(computeFn).toHaveBeenCalledTimes(1);
173
-
174
- await $derivation.pick();
175
- expect(computeFn).toHaveBeenCalledTimes(1);
176
- });
177
-
178
- it("should recompute when dirty", async () => {
179
- const $state = state(10);
180
- const computeFn = vi.fn(async (t) => $state.get(t) * 2);
181
- const $derivation = derivationAsync(computeFn);
182
-
183
- await $derivation.pick();
184
- expect(computeFn).toHaveBeenCalledTimes(1);
185
-
186
- $state.set(20);
187
- await $derivation.pick();
188
- expect(computeFn).toHaveBeenCalledTimes(2);
189
- });
190
- });
191
-
192
- describe("watch()", () => {
193
- it("should register dependency when called", async () => {
194
- const $state = state(10);
195
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
196
- const $tracker = signal();
197
-
198
- $derivation.watch($tracker);
199
- expect($derivation.disposed).toBe(false);
200
- });
201
-
202
- it("should trigger computation when called", async () => {
203
- const computeFn = vi.fn(async () => 42);
204
- const $derivation = derivationAsync(computeFn);
205
- const $tracker = signal();
206
-
207
- expect(computeFn).not.toHaveBeenCalled();
208
- $derivation.watch($tracker);
209
- expect(computeFn).toHaveBeenCalledTimes(1);
210
- });
211
-
212
- it("should throw error when called after disposal", async () => {
213
- const $derivation = derivationAsync(async () => 42);
214
- const $tracker = signal();
215
-
216
- $derivation.dispose();
217
- await expect($derivation.watch($tracker)).rejects.toThrow(
218
- "[PicoFlow] Primitive is disposed",
219
- );
220
- });
221
-
222
- it("should not recompute if already computed and not dirty", async () => {
223
- const computeFn = vi.fn(async () => 42);
224
- const $derivation = derivationAsync(computeFn);
225
- const $tracker = signal();
226
-
227
- await $derivation.pick();
228
- expect(computeFn).toHaveBeenCalledTimes(1);
229
-
230
- $derivation.watch($tracker);
231
- expect(computeFn).toHaveBeenCalledTimes(1);
232
- });
233
- });
234
-
235
- describe("subscribe()", () => {
236
- it("should call listener immediately with current value", async () => {
237
- const $state = state(10);
238
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
239
- const listener = vi.fn();
240
-
241
- $derivation.subscribe(listener);
242
-
243
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(1));
244
- const value = await listener.mock.calls[0][0];
245
- expect(value).toBe(20);
246
- });
247
-
248
- it("should call listener when value changes", async () => {
249
- const $state = state(10);
250
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
251
- const listener = vi.fn();
252
-
253
- $derivation.subscribe(async (promise) => listener(await promise));
254
-
255
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(1));
256
-
257
- await $state.set(20);
258
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(2));
259
- const value = await listener.mock.calls[1][0];
260
- expect(value).toBe(40);
261
- });
262
-
263
- it("should return disposer function", () => {
264
- const $derivation = derivationAsync(async () => 42);
265
- const listener = vi.fn();
266
-
267
- const disposer = $derivation.subscribe(listener);
268
- expect(typeof disposer).toBe("function");
269
- });
270
-
271
- it("should stop calling listener after disposal", async () => {
272
- const $state = state(10);
273
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
274
- const listener = vi.fn();
275
-
276
- const disposer = $derivation.subscribe(listener);
277
-
278
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(1));
279
-
280
- disposer();
281
-
282
- $state.set(20);
283
- await new Promise((resolve) => setTimeout(resolve, 50));
284
- expect(listener).toHaveBeenCalledTimes(1);
285
- });
286
-
287
- it("should throw error when called after derivation disposal", () => {
288
- const $derivation = derivationAsync(async () => 42);
289
- const listener = vi.fn();
290
-
291
- $derivation.dispose();
292
- expect(() => $derivation.subscribe(listener)).toThrow(
293
- "[PicoFlow] Primitive is disposed",
294
- );
295
- });
296
-
297
- it("should handle async listener functions", async () => {
298
- const $state = state(10);
299
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
300
- const listener = vi.fn(async (valuePromise) => {
301
- const value = await valuePromise;
302
- expect(value).toBe(20);
303
- });
304
-
305
- $derivation.subscribe(listener);
306
-
307
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(1));
308
- });
309
- });
310
-
311
- describe("trigger()", () => {
312
- it("should mark derivation as dirty when triggered", async () => {
313
- const $state = state(10);
314
- const computeFn = vi.fn(async (t) => $state.get(t) * 2);
315
- const $derivation = derivationAsync(computeFn);
316
-
317
- await $derivation.pick();
318
- expect(computeFn).toHaveBeenCalledTimes(1);
319
-
320
- await $derivation.trigger();
321
-
322
- await $derivation.pick();
323
- expect(computeFn).toHaveBeenCalledTimes(2);
324
- });
325
-
326
- it("should notify dependents when triggered", async () => {
327
- const $derivation = derivationAsync(async () => 42);
328
- const listener = vi.fn();
329
-
330
- $derivation.subscribe(async (promise) => listener(await promise));
331
-
332
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(1));
333
-
334
- await $derivation.trigger();
335
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(2));
336
- });
337
-
338
- it("should return Promise when triggered", () => {
339
- const $derivation = derivationAsync(async () => 42);
340
- const result = $derivation.trigger();
341
- expect(result).toBeInstanceOf(Promise);
342
- });
343
-
344
- it("should throw error when triggered after disposal", async () => {
345
- const $derivation = derivationAsync(async () => 42);
346
- $derivation.dispose();
347
-
348
- await expect($derivation.trigger()).rejects.toThrow(
349
- "[PicoFlow] Primitive is disposed",
350
- );
351
- });
352
- });
353
-
354
- describe("disposal", () => {
355
- it("should have disposed property set to false initially", () => {
356
- const $derivation = derivationAsync(async () => 42);
357
- expect($derivation.disposed).toBe(false);
358
- });
359
-
360
- it("should have disposed property set to true after disposal", () => {
361
- const $derivation = derivationAsync(async () => 42);
362
- $derivation.dispose();
363
- expect($derivation.disposed).toBe(true);
364
- });
365
-
366
- it("should throw error when disposed twice", () => {
367
- const $derivation = derivationAsync(async () => 42);
368
- $derivation.dispose();
369
- expect(() => $derivation.dispose()).toThrow(
370
- "[PicoFlow] Primitive is disposed",
371
- );
372
- });
373
-
374
- it("should throw error when get() called after disposal", async () => {
375
- const $derivation = derivationAsync(async () => 42);
376
- const $tracker = signal();
377
- $derivation.dispose();
378
-
379
- await expect($derivation.get($tracker)).rejects.toThrow(
380
- "[PicoFlow] Primitive is disposed",
381
- );
382
- });
383
-
384
- it("should throw error when pick() called after disposal", async () => {
385
- const $derivation = derivationAsync(async () => 42);
386
- $derivation.dispose();
387
-
388
- await expect($derivation.pick()).rejects.toThrow(
389
- "[PicoFlow] Primitive is disposed",
390
- );
391
- });
392
-
393
- it("should throw error when watch() called after disposal", async () => {
394
- const $derivation = derivationAsync(async () => 42);
395
- const $tracker = signal();
396
- $derivation.dispose();
397
-
398
- await expect($derivation.watch($tracker)).rejects.toThrow(
399
- "[PicoFlow] Primitive is disposed",
400
- );
401
- });
402
-
403
- it("should dispose dependents when disposed (default behavior)", async () => {
404
- const $state = state(10);
405
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
406
- const listener = vi.fn();
407
-
408
- $derivation.subscribe(async (promise) => listener(await promise));
409
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(1));
410
-
411
- $derivation.dispose();
412
-
413
- $state.set(20);
414
- await new Promise((resolve) => setTimeout(resolve, 50));
415
- expect(listener).toHaveBeenCalledTimes(1);
416
- });
417
-
418
- it("should not dispose dependents when disposed with self option", async () => {
419
- const $state = state(10);
420
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
421
- const listener = vi.fn();
422
-
423
- $derivation.subscribe(async (promise) => listener(await promise));
424
- await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(1));
425
-
426
- $derivation.dispose({ self: true });
427
- expect($derivation.disposed).toBe(true);
428
-
429
- // When disposed with self option, the derivation is disposed but dependents are not
430
- // However, the derivation can no longer react to changes since it's disposed
431
- // The listener subscription should still be active but won't be called because derivation is disposed
432
- $state.set(20);
433
- await new Promise((resolve) => setTimeout(resolve, 50));
434
- // Listener won't be called again because derivation is disposed
435
- expect(listener).toHaveBeenCalledTimes(1);
436
- });
437
-
438
- it("should unregister from dependencies when disposed", async () => {
439
- const $state = state(10);
440
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
441
- const $tracker = signal();
442
-
443
- await $derivation.get($tracker);
444
- expect(await $derivation.pick()).toBe(20);
445
-
446
- $derivation.dispose();
447
-
448
- // After disposal, the derivation should no longer react to dependency changes
449
- $state.set(20);
450
- await new Promise((resolve) => setTimeout(resolve, 50));
451
- // Cannot call pick() after disposal - it will throw
452
- await expect($derivation.pick()).rejects.toThrow(
453
- "[PicoFlow] Primitive is disposed",
454
- );
455
- });
456
-
457
- it("should throw error when pick() called after state is disposed", async () => {
458
- const $state = state(10);
459
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
460
-
461
- await $derivation.pick();
462
- $state.dispose();
463
-
464
- await expect($derivation.pick()).rejects.toThrow(
465
- "[PicoFlow] Primitive is disposed",
466
- );
467
- });
468
- });
469
-
470
- describe("lazy computation", () => {
471
- it("should not compute until first access", () => {
472
- const computeFn = vi.fn(async () => 42);
473
- derivationAsync(computeFn);
474
-
475
- expect(computeFn).not.toHaveBeenCalled();
476
- });
477
-
478
- it("should compute only once for multiple pick() calls when not dirty", async () => {
479
- const computeFn = vi.fn(async () => 42);
480
- const $derivation = derivationAsync(computeFn);
481
-
482
- await $derivation.pick();
483
- await $derivation.pick();
484
- await $derivation.pick();
485
-
486
- expect(computeFn).toHaveBeenCalledTimes(1);
487
- });
488
-
489
- it("should recompute when marked dirty", async () => {
490
- const $state = state(10);
491
- const computeFn = vi.fn(async (t) => $state.get(t) * 2);
492
- const $derivation = derivationAsync(computeFn);
493
-
494
- await $derivation.pick();
495
- expect(computeFn).toHaveBeenCalledTimes(1);
496
-
497
- $state.set(20);
498
- await $derivation.pick();
499
- expect(computeFn).toHaveBeenCalledTimes(2);
500
- });
501
- });
502
-
503
- describe("dirty tracking", () => {
504
- it("should mark as dirty when dependency changes", async () => {
505
- const $state = state(10);
506
- const computeFn = vi.fn(async (t) => $state.get(t) * 2);
507
- const $derivation = derivationAsync(computeFn);
508
-
509
- await $derivation.pick();
510
- expect(computeFn).toHaveBeenCalledTimes(1);
511
-
512
- $state.set(20);
513
- await $derivation.pick();
514
- expect(computeFn).toHaveBeenCalledTimes(2);
515
- });
516
-
517
- it("should not recompute immediately when marked dirty", async () => {
518
- const $state = state(10);
519
- const computeFn = vi.fn(async (t) => $state.get(t) * 2);
520
- const $derivation = derivationAsync(computeFn);
521
-
522
- await $derivation.pick();
523
- expect(computeFn).toHaveBeenCalledTimes(1);
524
-
525
- $state.set(20);
526
- expect(computeFn).toHaveBeenCalledTimes(1);
527
-
528
- await $derivation.pick();
529
- expect(computeFn).toHaveBeenCalledTimes(2);
530
- });
531
-
532
- it("should recompute on next access when dirty", async () => {
533
- const $state = state(10);
534
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
535
-
536
- expect(await $derivation.pick()).toBe(20);
537
-
538
- $state.set(20);
539
- expect(await $derivation.pick()).toBe(40);
540
- });
541
-
542
- it("should clear dirty flag after recomputation", async () => {
543
- const $state = state(10);
544
- const computeFn = vi.fn(async (t) => $state.get(t) * 2);
545
- const $derivation = derivationAsync(computeFn);
546
-
547
- await $derivation.pick();
548
- $state.set(20);
549
- await $derivation.pick();
550
- expect(computeFn).toHaveBeenCalledTimes(2);
551
-
552
- await $derivation.pick();
553
- expect(computeFn).toHaveBeenCalledTimes(2);
554
- });
555
- });
556
-
557
- describe("dynamic dependencies", () => {
558
- it("should track dependencies dynamically during computation", async () => {
559
- const $state1 = state(10);
560
- const $state2 = state(20);
561
- const $cond = state(true);
562
- const $derivation = derivationAsync(async (t) => {
563
- if ($cond.get(t)) {
564
- return $state1.get(t);
565
- }
566
- return $state2.get(t);
567
- });
568
-
569
- expect(await $derivation.pick()).toBe(10);
570
-
571
- $cond.set(false);
572
- expect(await $derivation.pick()).toBe(20);
573
- });
574
-
575
- it("should remove old dependencies when they are no longer accessed", async () => {
576
- const $state1 = state(10);
577
- const $state2 = state(20);
578
- const $cond = state(true);
579
- const $derivation = derivationAsync(async (t) => {
580
- if ($cond.get(t)) {
581
- return $state1.get(t);
582
- }
583
- return $state2.get(t);
584
- });
585
-
586
- await $derivation.pick();
587
- $cond.set(false);
588
- await $derivation.pick();
589
-
590
- $state1.set(100);
591
- expect(await $derivation.pick()).toBe(20);
592
-
593
- $state2.set(200);
594
- expect(await $derivation.pick()).toBe(200);
595
- });
596
-
597
- it("should add new dependencies when they are accessed", async () => {
598
- const $state1 = state(10);
599
- const $state2 = state(20);
600
- const $cond = state(false);
601
- const $derivation = derivationAsync(async (t) => {
602
- if ($cond.get(t)) {
603
- return $state1.get(t);
604
- }
605
- return $state2.get(t);
606
- });
607
-
608
- expect(await $derivation.pick()).toBe(20);
609
-
610
- $cond.set(true);
611
- expect(await $derivation.pick()).toBe(10);
612
- });
613
-
614
- it("should handle conditional dependencies correctly", async () => {
615
- const $state1 = state(10);
616
- const $state2 = state(20);
617
- const $cond = state(true);
618
- const $derivation = derivationAsync(async (t) => {
619
- if ($cond.get(t)) {
620
- return $state1.get(t) * 2;
621
- }
622
- return $state2.get(t) * 3;
623
- });
624
-
625
- expect(await $derivation.pick()).toBe(20);
626
-
627
- $state1.set(15);
628
- expect(await $derivation.pick()).toBe(30);
629
-
630
- $cond.set(false);
631
- expect(await $derivation.pick()).toBe(60);
632
-
633
- $state2.set(25);
634
- expect(await $derivation.pick()).toBe(75);
635
- });
636
- });
637
-
638
- describe("value caching", () => {
639
- it("should cache Promise until dependencies change", async () => {
640
- const $state = state(10);
641
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
642
- const $tracker = signal();
643
-
644
- const promise1 = $derivation.get($tracker);
645
- const promise2 = $derivation.get($tracker);
646
-
647
- expect(await promise1).toBe(20);
648
- expect(await promise2).toBe(20);
649
- });
650
-
651
- it("should return same Promise for multiple get() calls when not dirty", async () => {
652
- const $state = state(10);
653
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
654
- const $tracker = signal();
655
-
656
- const promise1 = $derivation.get($tracker);
657
- const promise2 = $derivation.get($tracker);
658
- const promise3 = $derivation.get($tracker);
659
-
660
- expect(await promise1).toBe(20);
661
- expect(await promise2).toBe(20);
662
- expect(await promise3).toBe(20);
663
- });
664
-
665
- it("should create new Promise when recomputed", async () => {
666
- const $state = state(10);
667
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
668
- const $tracker = signal();
669
-
670
- const promise1 = $derivation.get($tracker);
671
- await promise1;
672
- await $state.set(20);
673
- const promise2 = $derivation.get($tracker);
674
-
675
- expect(await promise1).toBe(20);
676
- expect(await promise2).toBe(40);
677
- });
678
- });
679
-
680
- describe("from state", () => {
681
- it("should be updated when state changes", async () => {
682
- const $state = state(1);
683
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
684
- expect(await $derivation.pick()).toBe(2);
685
-
686
- $state.set(2);
687
- expect(await $derivation.pick()).toBe(4);
688
- });
689
-
690
- it("should be updated with chained async dependencies", async () => {
691
- const $state = state(1);
692
- const $derivation1 = derivationAsync(async (t) => $state.get(t) * 2);
693
- const $derivation2 = derivationAsync(
694
- async (t) => (await $derivation1.get(t)) * 2,
695
- );
696
-
697
- expect(await $derivation2.pick()).toBe(4);
698
-
699
- $state.set(2);
700
- expect(await $derivation2.pick()).toBe(8);
701
- });
702
-
703
- it("should be updated with chained sync and async dependencies", async () => {
704
- const $state = state(1);
705
- const $derivation1 = derivation((t) => $state.get(t) * 2);
706
- const $derivation2 = derivationAsync(
707
- async (t) => $derivation1.get(t) * 2,
708
- );
709
-
710
- expect(await $derivation2.pick()).toBe(4);
711
-
712
- $state.set(2);
713
- expect(await $derivation2.pick()).toBe(8);
714
- });
715
-
716
- it("should be updated with multiple dependencies", async () => {
717
- const $state1 = state(1);
718
- const $state2 = state(2);
719
- const $derivation = derivationAsync(
720
- async (t) => $state1.get(t) + $state2.get(t),
721
- );
722
-
723
- expect(await $derivation.pick()).toBe(3);
724
-
725
- $state1.set(2);
726
- expect(await $derivation.pick()).toBe(4);
727
-
728
- $state2.set(3);
729
- expect(await $derivation.pick()).toBe(5);
730
- });
731
-
732
- it("should be updated with multiple dependants", async () => {
733
- const $state = state(1);
734
- const $derivation1 = derivationAsync(async (t) => $state.get(t) * 2);
735
- const $derivation2 = derivationAsync(async (t) => $state.get(t) * 3);
736
-
737
- expect(await $derivation1.pick()).toBe(2);
738
- expect(await $derivation2.pick()).toBe(3);
739
-
740
- $state.set(2);
741
- expect(await $derivation1.pick()).toBe(4);
742
- expect(await $derivation2.pick()).toBe(6);
743
-
744
- $state.set(3);
745
- expect(await $derivation1.pick()).toBe(6);
746
- expect(await $derivation2.pick()).toBe(9);
747
- });
748
- });
749
- });
750
-
751
- describe("integration", () => {
752
- describe("with effects - basic", () => {
753
- it("should execute effect when derivation value changes", async () => {
754
- const $state = state(1);
755
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
756
- const effectFn = vi.fn();
757
- effect(async (t) => effectFn(await $derivation.get(t)));
758
-
759
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
760
- expect(effectFn).toHaveBeenLastCalledWith(2);
761
-
762
- $state.set(2);
763
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
764
- expect(effectFn).toHaveBeenLastCalledWith(4);
765
- });
766
-
767
- it("should not execute effect when value is same (===)", async () => {
768
- const $state = state(1);
769
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
770
- const effectFn = vi.fn();
771
- effect(async (t) => effectFn(await $derivation.get(t)));
772
-
773
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
774
- expect(effectFn).toHaveBeenLastCalledWith(2);
775
-
776
- $state.set(1);
777
- await new Promise((resolve) => setTimeout(resolve, 50));
778
- expect(effectFn).toHaveBeenCalledTimes(1);
779
- });
780
-
781
- it("should execute effect multiple times on multiple changes", async () => {
782
- const $state = state(1);
783
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
784
- const effectFn = vi.fn();
785
- effect(async (t) => effectFn(await $derivation.get(t)));
786
-
787
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
788
-
789
- $state.set(2);
790
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
791
-
792
- $state.set(3);
793
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
794
-
795
- $state.set(4);
796
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(4));
797
- });
798
-
799
- it("should stop executing effect after effect disposal", async () => {
800
- const $state = state(1);
801
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
802
- const effectFn = vi.fn();
803
- const $effect = effect(async (t) => effectFn(await $derivation.get(t)));
804
-
805
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
806
- expect(effectFn).toHaveBeenLastCalledWith(2);
807
-
808
- $state.set(2);
809
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
810
- expect(effectFn).toHaveBeenLastCalledWith(4);
811
-
812
- $effect.dispose();
813
-
814
- $state.set(3);
815
- await new Promise((resolve) => setTimeout(resolve, 50));
816
- expect(effectFn).toHaveBeenCalledTimes(2);
817
- expect(effectFn).toHaveBeenLastCalledWith(4);
818
- });
819
-
820
- it("should stop executing effect after derivation disposal", async () => {
821
- const $state = state(1);
822
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
823
- const effectFn = vi.fn();
824
- effect(async (t) => effectFn(await $derivation.get(t)));
825
-
826
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
827
- expect(effectFn).toHaveBeenLastCalledWith(2);
828
-
829
- $state.set(2);
830
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
831
- expect(effectFn).toHaveBeenLastCalledWith(4);
832
-
833
- $derivation.dispose();
834
-
835
- $state.set(3);
836
- await new Promise((resolve) => setTimeout(resolve, 50));
837
- expect(effectFn).toHaveBeenCalledTimes(2);
838
- expect(effectFn).toHaveBeenLastCalledWith(4);
839
- });
840
-
841
- it("should stop executing effect after derivation disposal (watch)", async () => {
842
- const $state = state(1);
843
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
844
- const effectFn = vi.fn();
845
- effect(async (t) => {
846
- $derivation.watch(t);
847
- effectFn();
848
- });
849
-
850
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
851
-
852
- $state.set(2);
853
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
854
-
855
- $derivation.dispose();
856
-
857
- $state.set(3);
858
- await new Promise((resolve) => setTimeout(resolve, 50));
859
- expect(effectFn).toHaveBeenCalledTimes(2);
860
- });
861
- });
862
-
863
- describe("with effects - dependencies", () => {
864
- it("should execute effect when dependency changes", async () => {
865
- const $state = state(1);
866
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
867
- const effectFn = vi.fn();
868
- effect(async (t) => effectFn(await $derivation.get(t)));
869
-
870
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
871
- expect(effectFn).toHaveBeenLastCalledWith(2);
872
-
873
- $state.set(2);
874
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
875
- expect(effectFn).toHaveBeenLastCalledWith(4);
876
- });
877
-
878
- it("should execute effect when multiple dependencies change", async () => {
879
- const $state1 = state(1);
880
- const $state2 = state(2);
881
- const $derivation = derivationAsync(
882
- async (t) => $state1.get(t) + $state2.get(t),
883
- );
884
- const effectFn = vi.fn();
885
- effect(async (t) => effectFn(await $derivation.get(t)));
886
-
887
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
888
- expect(effectFn).toHaveBeenLastCalledWith(3);
889
-
890
- $state1.set(2);
891
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
892
- expect(effectFn).toHaveBeenLastCalledWith(4);
893
-
894
- $state2.set(3);
895
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
896
- expect(effectFn).toHaveBeenLastCalledWith(5);
897
- });
898
-
899
- it("should execute effect when chained derivation dependency changes", async () => {
900
- const $state = state(1);
901
- const $derivation1 = derivation((t) => $state.get(t) * 2);
902
- const $derivation2 = derivationAsync(
903
- async (t) => (await $derivation1.get(t)) * 2,
904
- );
905
- const effectFn = vi.fn();
906
- effect(async (t) => effectFn(await $derivation2.get(t)));
907
-
908
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
909
- expect(effectFn).toHaveBeenLastCalledWith(4);
910
-
911
- $state.set(2);
912
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
913
- expect(effectFn).toHaveBeenLastCalledWith(8);
914
- });
915
-
916
- it("should handle mixed sync and async derivations in effects", async () => {
917
- const $state = state(1);
918
- const $derivation1 = derivation((t) => $state.get(t) * 2);
919
- const $derivation2 = derivationAsync(
920
- async (t) => $derivation1.get(t) * 2,
921
- );
922
- const effectFn = vi.fn();
923
- effect(async (t) => effectFn(await $derivation2.get(t)));
924
-
925
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
926
- expect(effectFn).toHaveBeenLastCalledWith(4);
927
-
928
- $state.set(2);
929
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
930
- expect(effectFn).toHaveBeenLastCalledWith(8);
931
- });
932
- });
933
-
934
- describe("with effects - from signal", () => {
935
- it("should execute effect when signal is triggered", async () => {
936
- const $signal = signal();
937
- const $derivation = derivationAsync(async (t) => {
938
- $signal.watch(t);
939
- });
940
- const effectFn = vi.fn();
941
- effect((t) => {
942
- $derivation.watch(t);
943
- effectFn();
944
- });
945
-
946
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
947
-
948
- $signal.trigger();
949
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
950
-
951
- $signal.trigger();
952
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
953
-
954
- $signal.trigger();
955
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(4));
956
- });
957
-
958
- it("should execute effect when signal is triggered (chained dependencies)", async () => {
959
- const $signal = signal();
960
- const $derivation1 = derivationAsync(async (t) => $signal.watch(t));
961
- const $derivation2 = derivationAsync(async (t) =>
962
- $derivation1.watch(t),
963
- );
964
- const effectFn = vi.fn();
965
- effect((t) => {
966
- $derivation2.watch(t);
967
- effectFn();
968
- });
969
-
970
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
971
-
972
- $signal.trigger();
973
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
974
- });
975
-
976
- it("should execute effect when multiple signals are triggered", async () => {
977
- const $signal1 = signal();
978
- const $signal2 = signal();
979
- const $derivation = derivationAsync(async (t) => {
980
- $signal1.watch(t);
981
- $signal2.watch(t);
982
- });
983
- const effectFn = vi.fn();
984
- effect((t) => {
985
- $derivation.watch(t);
986
- effectFn();
987
- });
988
-
989
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
990
-
991
- $signal1.trigger();
992
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
993
-
994
- $signal2.trigger();
995
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
996
- });
997
-
998
- it("should execute effect when multiple derivations watch same signal", async () => {
999
- const $signal = signal();
1000
- const $derivation1 = derivationAsync(async (t) => $signal.watch(t));
1001
- const $derivation2 = derivationAsync(async (t) => $signal.watch(t));
1002
- const effect1Fn = vi.fn();
1003
- const effect2Fn = vi.fn();
1004
-
1005
- effect(async (t) => effect1Fn(await $derivation1.get(t)));
1006
- effect(async (t) => effect2Fn(await $derivation2.get(t)));
1007
-
1008
- await vi.waitFor(() => expect(effect1Fn).toHaveBeenCalledTimes(1));
1009
- await vi.waitFor(() => expect(effect2Fn).toHaveBeenCalledTimes(1));
1010
-
1011
- $signal.trigger();
1012
- await vi.waitFor(() => expect(effect1Fn).toHaveBeenCalledTimes(2));
1013
- await vi.waitFor(() => expect(effect2Fn).toHaveBeenCalledTimes(2));
1014
-
1015
- $signal.trigger();
1016
- await vi.waitFor(() => expect(effect1Fn).toHaveBeenCalledTimes(3));
1017
- await vi.waitFor(() => expect(effect2Fn).toHaveBeenCalledTimes(3));
1018
- });
1019
-
1020
- it("should execute effect when signal is triggered with state", async () => {
1021
- const $signal = signal();
1022
- const $state = state(1);
1023
- const $derivation = derivationAsync(async (t) => {
1024
- $signal.watch(t);
1025
- return $state.get(t) * 2;
1026
- });
1027
- const effectFn = vi.fn();
1028
- const $effect = effect(async (t) => effectFn(await $derivation.get(t)));
1029
-
1030
- await $effect.settled;
1031
-
1032
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1033
- expect(effectFn).toHaveBeenLastCalledWith(2);
1034
-
1035
- $signal.trigger();
1036
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1037
- expect(effectFn).toHaveBeenLastCalledWith(2);
1038
-
1039
- $signal.trigger();
1040
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1041
- expect(effectFn).toHaveBeenLastCalledWith(2);
1042
-
1043
- $state.set(2);
1044
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(4));
1045
- expect(effectFn).toHaveBeenLastCalledWith(4);
1046
-
1047
- $signal.trigger();
1048
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(5));
1049
- expect(effectFn).toHaveBeenLastCalledWith(4);
1050
- });
1051
- });
1052
-
1053
- describe("with effects - from state", () => {
1054
- it("should execute effect when state is updated", async () => {
1055
- const $state = state(1);
1056
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
1057
- const effectFn = vi.fn();
1058
- effect(async (t) => effectFn(await $derivation.get(t)));
1059
-
1060
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1061
- expect(effectFn).toHaveBeenLastCalledWith(2);
1062
-
1063
- $state.set(2);
1064
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1065
- expect(effectFn).toHaveBeenLastCalledWith(4);
1066
-
1067
- $state.set(3);
1068
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1069
- expect(effectFn).toHaveBeenLastCalledWith(6);
1070
-
1071
- $state.set(3);
1072
- await new Promise((resolve) => setTimeout(resolve, 50));
1073
- expect(effectFn).toHaveBeenCalledTimes(3);
1074
- expect(effectFn).toHaveBeenLastCalledWith(6);
1075
- });
1076
-
1077
- it("should execute effect when derivation is triggered", async () => {
1078
- const $state = state(1);
1079
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
1080
- const effectFn = vi.fn();
1081
- effect(async (t) => effectFn(await $derivation.get(t)));
1082
-
1083
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1084
- expect(effectFn).toHaveBeenLastCalledWith(2);
1085
-
1086
- $state.set(2);
1087
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1088
- expect(effectFn).toHaveBeenLastCalledWith(4);
1089
-
1090
- $derivation.trigger();
1091
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1092
- expect(effectFn).toHaveBeenLastCalledWith(4);
1093
- });
1094
-
1095
- it("should execute effect when triggered from signal", async () => {
1096
- const $state = state(1);
1097
- const $signal = signal();
1098
- const $derivation = derivationAsync(async (t) => {
1099
- $signal.watch(t);
1100
- return $state.get(t) * 2;
1101
- });
1102
-
1103
- const effectFn = vi.fn();
1104
- effect(async (t) => effectFn(await $derivation.get(t)));
1105
-
1106
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1107
- expect(effectFn).toHaveBeenLastCalledWith(2);
1108
-
1109
- $state.set(2);
1110
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1111
- expect(effectFn).toHaveBeenLastCalledWith(4);
1112
-
1113
- $signal.trigger();
1114
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1115
- expect(effectFn).toHaveBeenLastCalledWith(4);
1116
-
1117
- $signal.trigger();
1118
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(4));
1119
- expect(effectFn).toHaveBeenLastCalledWith(4);
1120
- });
1121
-
1122
- it("should execute effect when state is updated (chained dependencies)", async () => {
1123
- const $state = state(1);
1124
- const $derivation1 = derivation((t) => $state.get(t) * 2);
1125
- const $derivation2 = derivationAsync(
1126
- async (t) => (await $derivation1.get(t)) * 2,
1127
- );
1128
- const effectFn = vi.fn();
1129
- effect(async (t) => effectFn(await $derivation2.get(t)));
1130
-
1131
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1132
- expect(effectFn).toHaveBeenLastCalledWith(4);
1133
-
1134
- $state.set(2);
1135
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1136
- expect(effectFn).toHaveBeenLastCalledWith(8);
1137
- });
1138
-
1139
- it("should execute effect when state is updated (multiple dependencies)", async () => {
1140
- const $state1 = state(1);
1141
- const $state2 = state(2);
1142
- const $derivation = derivationAsync(
1143
- async (t) => $state1.get(t) + $state2.get(t),
1144
- );
1145
- const effectFn = vi.fn();
1146
- effect(async (t) => effectFn(await $derivation.get(t)));
1147
-
1148
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1149
- expect(effectFn).toHaveBeenLastCalledWith(3);
1150
-
1151
- $state1.set(2);
1152
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1153
- expect(effectFn).toHaveBeenLastCalledWith(4);
1154
-
1155
- $state2.set(3);
1156
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1157
- expect(effectFn).toHaveBeenLastCalledWith(5);
1158
- });
1159
-
1160
- it("should execute effect when state is updated (multiple dependants)", async () => {
1161
- const $state = state(1);
1162
- const $derivation1 = derivationAsync(async (t) => $state.get(t) * 2);
1163
- const $derivation2 = derivationAsync(async (t) => $state.get(t) * 3);
1164
- const effect1Fn = vi.fn();
1165
- const effect2Fn = vi.fn();
1166
-
1167
- effect(async (t) => effect1Fn(await $derivation1.get(t)));
1168
- effect(async (t) => effect2Fn(await $derivation2.get(t)));
1169
-
1170
- await vi.waitFor(() => expect(effect1Fn).toHaveBeenCalledTimes(1));
1171
- expect(effect1Fn).toHaveBeenLastCalledWith(2);
1172
- await vi.waitFor(() => expect(effect2Fn).toHaveBeenCalledTimes(1));
1173
- expect(effect2Fn).toHaveBeenLastCalledWith(3);
1174
-
1175
- $state.set(2);
1176
- await vi.waitFor(() => expect(effect1Fn).toHaveBeenCalledTimes(2));
1177
- expect(effect1Fn).toHaveBeenLastCalledWith(4);
1178
- await vi.waitFor(() => expect(effect2Fn).toHaveBeenCalledTimes(2));
1179
- expect(effect2Fn).toHaveBeenLastCalledWith(6);
1180
-
1181
- $state.set(3);
1182
- await vi.waitFor(() => expect(effect1Fn).toHaveBeenCalledTimes(3));
1183
- expect(effect1Fn).toHaveBeenLastCalledWith(6);
1184
- await vi.waitFor(() => expect(effect2Fn).toHaveBeenCalledTimes(3));
1185
- expect(effect2Fn).toHaveBeenLastCalledWith(9);
1186
- });
1187
-
1188
- it("should execute effect when state is updated (async)", async () => {
1189
- const $state = state(1);
1190
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
1191
- const effectFn = vi.fn();
1192
- effect(async (t) => effectFn(await $derivation.get(t)));
1193
-
1194
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1195
- expect(await effectFn.mock.calls[0][0]).toEqual(2);
1196
-
1197
- $state.set(2);
1198
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1199
- expect(await effectFn.mock.calls[1][0]).toEqual(4);
1200
- });
1201
-
1202
- it("should execute effect when state is updated (async chain)", async () => {
1203
- const $state = state(1);
1204
- const $derivation1 = derivationAsync(async (t) => $state.get(t) * 2);
1205
- const $derivation2 = derivationAsync(
1206
- async (t) => (await $derivation1.get(t)) * 2,
1207
- );
1208
- const effectFn = vi.fn();
1209
- effect(async (t) => effectFn(await $derivation2.get(t)));
1210
-
1211
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1212
- expect(await effectFn.mock.calls[0][0]).toEqual(4);
1213
-
1214
- $state.set(2);
1215
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1216
- expect(await effectFn.mock.calls[1][0]).toEqual(8);
1217
- });
1218
- });
1219
-
1220
- describe("with effects - complex scenarios", () => {
1221
- it("should handle diamond dependency pattern correctly", async () => {
1222
- const $stateA = state(1);
1223
- const $aMult3 = derivationAsync(async (t) => $stateA.get(t) * 3);
1224
- const $aMult2 = derivationAsync(async (t) => $stateA.get(t) * 2);
1225
- const $addAmult3Amult2 = derivationAsync(
1226
- async (t) => (await $aMult3.get(t)) + (await $aMult2.get(t)),
1227
- );
1228
-
1229
- const effectFn = vi.fn();
1230
- effect(async (t) => effectFn(await $addAmult3Amult2.get(t)));
1231
-
1232
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1233
- expect(effectFn.mock.calls[0][0]).toEqual(5);
1234
-
1235
- $stateA.set(2);
1236
- $stateA.set(3);
1237
-
1238
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1239
- expect(effectFn.mock.calls[1][0]).toEqual(10);
1240
- expect(effectFn.mock.calls[2][0]).toEqual(15);
1241
- });
1242
-
1243
- it("should handle diamond dependency pattern correctly (async)", async () => {
1244
- const $stateA = state(1);
1245
- const $aMult3 = derivationAsync(async (t) => $stateA.get(t) * 3);
1246
- const $aMult2 = derivationAsync(async (t) => $stateA.get(t) * 2);
1247
- const $addAmult3Amult2 = derivationAsync(
1248
- async (t) => (await $aMult3.get(t)) + (await $aMult2.get(t)),
1249
- );
1250
-
1251
- const effectFn = vi.fn();
1252
- effect(async (t) => effectFn(await $addAmult3Amult2.get(t)));
1253
-
1254
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1255
-
1256
- $stateA.set(2);
1257
- $stateA.set(3);
1258
-
1259
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1260
-
1261
- expect(await effectFn.mock.calls[0][0]).toEqual(5);
1262
- expect(await effectFn.mock.calls[1][0]).toEqual(10);
1263
- expect(await effectFn.mock.calls[2][0]).toEqual(15);
1264
- });
1265
-
1266
- it("should handle multiple diamond patterns correctly", async () => {
1267
- const $stateA = state(1);
1268
- const $stateB = state(2);
1269
- const $addAB = derivationAsync(async (t) => {
1270
- const a = $stateA.get(t);
1271
- const b = $stateB.get(t);
1272
- return a + b;
1273
- });
1274
-
1275
- const $multiplyAB = derivationAsync(async (t) => {
1276
- const a = $stateA.get(t);
1277
- const b = $stateB.get(t);
1278
- return a * b;
1279
- });
1280
-
1281
- const $addAndMultiply = derivationAsync(async (t) => {
1282
- const add = await $addAB.get(t);
1283
- const multiply = await $multiplyAB.get(t);
1284
- return add * multiply;
1285
- });
1286
-
1287
- const effectFn = vi.fn();
1288
- effect(async (t) => effectFn(await $addAndMultiply.get(t)));
1289
-
1290
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1291
- expect(effectFn.mock.calls[0][0]).toEqual(6);
1292
-
1293
- $stateA.set(2);
1294
- $stateB.set(3);
1295
-
1296
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1297
- expect(effectFn.mock.calls[1][0]).toEqual(16);
1298
- expect(effectFn.mock.calls[2][0]).toEqual(30);
1299
- });
1300
-
1301
- it("should handle multiple diamond patterns correctly (async - stepped)", async () => {
1302
- const $stateA = state(1);
1303
- const $stateB = state(2);
1304
- const $addAB = derivationAsync(async (t) => {
1305
- const a = $stateA.get(t);
1306
- const b = $stateB.get(t);
1307
- return a + b;
1308
- });
1309
-
1310
- const $multiplyAB = derivationAsync(async (t) => {
1311
- const a = $stateA.get(t);
1312
- const b = $stateB.get(t);
1313
- return a * b;
1314
- });
1315
-
1316
- const $addAndMultiply = derivationAsync(async (t) => {
1317
- const add = await $addAB.get(t);
1318
- const multiply = await $multiplyAB.get(t);
1319
- return add * multiply;
1320
- });
1321
-
1322
- const effectFn = vi.fn();
1323
- effect(async (t) => effectFn(await $addAndMultiply.get(t)));
1324
-
1325
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1326
- expect(await effectFn.mock.calls[0][0]).toEqual(6);
1327
-
1328
- $stateA.set(2);
1329
-
1330
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1331
- expect(await effectFn.mock.calls[1][0]).toEqual(16);
1332
-
1333
- $stateB.set(3);
1334
-
1335
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1336
- expect(await effectFn.mock.calls[2][0]).toEqual(30);
1337
- });
1338
-
1339
- it("should handle multiple diamond patterns correctly (async)", async () => {
1340
- const $stateA = state(1);
1341
- const $stateB = state(2);
1342
- const $addAB = derivationAsync(async (t) => {
1343
- const a = $stateA.get(t);
1344
- const b = $stateB.get(t);
1345
- return a + b;
1346
- });
1347
-
1348
- const $multiplyAB = derivationAsync(async (t) => {
1349
- const a = $stateA.get(t);
1350
- const b = $stateB.get(t);
1351
- return a * b;
1352
- });
1353
-
1354
- const $addAndMultiply = derivationAsync(async (t) => {
1355
- const add = await $addAB.get(t);
1356
- const multiply = await $multiplyAB.get(t);
1357
- return add * multiply;
1358
- });
1359
-
1360
- const effectFn = vi.fn();
1361
- effect(async (t) => effectFn(await $addAndMultiply.get(t)));
1362
-
1363
- $stateA.set(2);
1364
- $stateB.set(3);
1365
-
1366
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1367
-
1368
- expect(await effectFn.mock.calls[0][0]).toEqual(6);
1369
- expect(await effectFn.mock.calls[1][0]).toEqual(16);
1370
- expect(await effectFn.mock.calls[2][0]).toEqual(30);
1371
- });
1372
-
1373
- it("should handle conditional dependencies in effects", async () => {
1374
- const obj = {
1375
- cond: state(false),
1376
- b: state(2),
1377
- };
1378
-
1379
- const $state = state(obj);
1380
- const $derivation = derivationAsync(async (t) => {
1381
- const cond = $state.get(t).cond.get(t);
1382
- if (cond) {
1383
- return $state.get(t).b.get(t) * 2;
1384
- }
1385
- return 0;
1386
- });
1387
-
1388
- const effectFn = vi.fn();
1389
- effect(async (t) => effectFn(await $derivation.get(t)));
1390
-
1391
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1392
- expect(effectFn).toHaveBeenLastCalledWith(0);
1393
-
1394
- (await $state.pick()).cond.set(true);
1395
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(2));
1396
- expect(effectFn).toHaveBeenLastCalledWith(4);
1397
-
1398
- await $state.set({
1399
- cond: state(false),
1400
- b: state(3),
1401
- });
1402
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(3));
1403
- expect(effectFn).toHaveBeenLastCalledWith(0);
1404
-
1405
- (await $state.pick()).cond.set(true);
1406
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(4));
1407
- expect(effectFn).toHaveBeenLastCalledWith(6);
1408
- });
1409
-
1410
- it("should handle nested derivations correctly", async () => {
1411
- const obj1 = {
1412
- cond: state(false),
1413
- num: state(2),
1414
- dispose: (options: { self: boolean }) => {
1415
- obj1.cond.dispose(options);
1416
- obj1.num.dispose(options);
1417
- },
1418
- };
1419
- const obj2 = {
1420
- cond: state(false),
1421
- num: state(4),
1422
- dispose: (options: { self: boolean }) => {
1423
- obj2.cond.dispose(options);
1424
- obj2.num.dispose(options);
1425
- },
1426
- };
1427
- const $state = state(obj1);
1428
- const $derivationCond = derivationAsync(async (t) =>
1429
- $state.get(t).cond.get(t),
1430
- );
1431
- const $derivationNum = derivationAsync(
1432
- async (t) => $state.get(t).num.get(t) * 2,
1433
- );
1434
- const effectCondFn = vi.fn();
1435
- const effectNumFn = vi.fn();
1436
- effect(async (t) => effectCondFn(await $derivationCond.get(t)));
1437
- effect(async (t) => effectNumFn(await $derivationNum.get(t)));
1438
-
1439
- await vi.waitFor(() => expect(effectCondFn).toHaveBeenCalledTimes(1));
1440
- expect(effectCondFn).toHaveBeenLastCalledWith(false);
1441
- await vi.waitFor(() => expect(effectNumFn).toHaveBeenCalledTimes(1));
1442
- expect(effectNumFn).toHaveBeenLastCalledWith(4);
1443
-
1444
- (await $state.pick()).num.set(3);
1445
- await vi.waitFor(() => expect(effectCondFn).toHaveBeenCalledTimes(1));
1446
- expect(effectCondFn).toHaveBeenLastCalledWith(false);
1447
- await vi.waitFor(() => expect(effectNumFn).toHaveBeenCalledTimes(2));
1448
- expect(effectNumFn).toHaveBeenLastCalledWith(6);
1449
-
1450
- (await $state.pick()).cond.set(true);
1451
- await vi.waitFor(() => expect(effectCondFn).toHaveBeenCalledTimes(2));
1452
- expect(effectCondFn).toHaveBeenLastCalledWith(true);
1453
- await vi.waitFor(() => expect(effectNumFn).toHaveBeenCalledTimes(2));
1454
- expect(effectNumFn).toHaveBeenLastCalledWith(6);
1455
-
1456
- $state.set(obj2);
1457
- await vi.waitFor(() => expect(effectCondFn).toHaveBeenCalledTimes(3));
1458
- expect(effectCondFn).toHaveBeenLastCalledWith(false);
1459
- await vi.waitFor(() => expect(effectNumFn).toHaveBeenCalledTimes(3));
1460
- expect(effectNumFn).toHaveBeenLastCalledWith(8);
1461
-
1462
- (await $state.pick()).cond.set(true);
1463
- await vi.waitFor(() => expect(effectCondFn).toHaveBeenCalledTimes(4));
1464
- expect(effectCondFn).toHaveBeenLastCalledWith(true);
1465
- await vi.waitFor(() => expect(effectNumFn).toHaveBeenCalledTimes(3));
1466
- expect(effectNumFn).toHaveBeenLastCalledWith(8);
1467
-
1468
- (await $state.pick()).num.set(5);
1469
- await vi.waitFor(() => expect(effectCondFn).toHaveBeenCalledTimes(4));
1470
- expect(effectCondFn).toHaveBeenLastCalledWith(true);
1471
- await vi.waitFor(() => expect(effectNumFn).toHaveBeenCalledTimes(4));
1472
- expect(effectNumFn).toHaveBeenLastCalledWith(10);
1473
- });
1474
-
1475
- it("should handle basic derivation with multiple states", async () => {
1476
- const $stateA = state(1);
1477
- const $stateB = state(2);
1478
- const $derivation = derivationAsync(async (t) => {
1479
- const a = $stateA.get(t);
1480
- const b = $stateB.get(t);
1481
- return a + b;
1482
- });
1483
- const effectFn = vi.fn();
1484
- effect(async (t) => effectFn(await $derivation.get(t)));
1485
-
1486
- await vi.waitFor(() => expect(effectFn).toHaveBeenCalledTimes(1));
1487
- expect(await effectFn.mock.calls[0][0]).toEqual(3);
1488
- });
1489
- });
1490
-
1491
- describe("with effects - error handling", () => {
1492
- it("should propagate errors from compute function", async () => {
1493
- const $state = state(1);
1494
- const $derivation = derivationAsync(async (t) => {
1495
- const val = $state.get(t);
1496
- if (val > 10) {
1497
- throw new Error("Value too large");
1498
- }
1499
- return val * 2;
1500
- });
1501
-
1502
- expect(await $derivation.pick()).toBe(2);
1503
-
1504
- $state.set(5);
1505
- expect(await $derivation.pick()).toBe(10);
1506
-
1507
- // Force recomputation by changing state to trigger error
1508
- $state.set(15);
1509
- // The error will be thrown when pick() is called and the promise will reject
1510
- await expect($derivation.pick()).rejects.toThrow("Value too large");
1511
- });
1512
-
1513
- it("should handle disposed dependencies correctly", async () => {
1514
- const $state = state(10);
1515
- const $derivation = derivationAsync(async (t) => $state.get(t) * 2);
1516
-
1517
- expect(await $derivation.pick()).toBe(20);
1518
-
1519
- $state.dispose();
1520
-
1521
- await expect($derivation.pick()).rejects.toThrow(
1522
- "[PicoFlow] Primitive is disposed",
1523
- );
1524
- });
1525
- });
1526
- });
1527
- });