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