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