@ersbeth/picoflow 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/.vscode/settings.json +3 -3
  2. package/CHANGELOG.md +43 -0
  3. package/README.md +2 -18
  4. package/biome.json +45 -35
  5. package/dist/picoflow.js +857 -1528
  6. package/dist/types/api/base/flowDisposable.d.ts +41 -0
  7. package/dist/types/api/base/flowDisposable.d.ts.map +1 -0
  8. package/dist/types/api/base/flowObservable.d.ts +27 -0
  9. package/dist/types/api/base/flowObservable.d.ts.map +1 -0
  10. package/dist/types/api/base/flowSubscribable.d.ts +79 -0
  11. package/dist/types/api/base/flowSubscribable.d.ts.map +1 -0
  12. package/dist/types/api/base/flowTracker.d.ts +8 -0
  13. package/dist/types/api/base/flowTracker.d.ts.map +1 -0
  14. package/dist/types/api/base/index.d.ts +5 -0
  15. package/dist/types/api/base/index.d.ts.map +1 -0
  16. package/dist/types/api/index.d.ts +3 -0
  17. package/dist/types/api/index.d.ts.map +1 -0
  18. package/dist/types/api/nodes/async/flowConstantAsync.d.ts +31 -0
  19. package/dist/types/api/nodes/async/flowConstantAsync.d.ts.map +1 -0
  20. package/dist/types/api/nodes/async/flowDerivationAsync.d.ts +37 -0
  21. package/dist/types/api/nodes/async/flowDerivationAsync.d.ts.map +1 -0
  22. package/dist/types/api/nodes/async/flowStateAsync.d.ts +41 -0
  23. package/dist/types/api/nodes/async/flowStateAsync.d.ts.map +1 -0
  24. package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts +30 -0
  25. package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts.map +1 -0
  26. package/dist/types/{flow → api}/nodes/async/index.d.ts +1 -2
  27. package/dist/types/api/nodes/async/index.d.ts.map +1 -0
  28. package/dist/types/api/nodes/collections/flowArray.d.ts +134 -0
  29. package/dist/types/api/nodes/collections/flowArray.d.ts.map +1 -0
  30. package/dist/types/api/nodes/collections/flowMap.d.ts +98 -0
  31. package/dist/types/api/nodes/collections/flowMap.d.ts.map +1 -0
  32. package/dist/types/api/nodes/collections/index.d.ts.map +1 -0
  33. package/dist/types/api/nodes/flowEffect.d.ts +28 -0
  34. package/dist/types/api/nodes/flowEffect.d.ts.map +1 -0
  35. package/dist/types/api/nodes/flowSignal.d.ts +25 -0
  36. package/dist/types/api/nodes/flowSignal.d.ts.map +1 -0
  37. package/dist/types/api/nodes/flowValue.d.ts +35 -0
  38. package/dist/types/api/nodes/flowValue.d.ts.map +1 -0
  39. package/dist/types/api/nodes/index.d.ts +8 -0
  40. package/dist/types/api/nodes/index.d.ts.map +1 -0
  41. package/dist/types/api/nodes/sync/flowConstant.d.ts +29 -0
  42. package/dist/types/api/nodes/sync/flowConstant.d.ts.map +1 -0
  43. package/dist/types/api/nodes/sync/flowDerivation.d.ts +36 -0
  44. package/dist/types/api/nodes/sync/flowDerivation.d.ts.map +1 -0
  45. package/dist/types/api/nodes/sync/flowState.d.ts +39 -0
  46. package/dist/types/api/nodes/sync/flowState.d.ts.map +1 -0
  47. package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts +28 -0
  48. package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts.map +1 -0
  49. package/dist/types/{flow → api}/nodes/sync/index.d.ts +1 -2
  50. package/dist/types/api/nodes/sync/index.d.ts.map +1 -0
  51. package/dist/types/api/nodes/utils.d.ts +22 -0
  52. package/dist/types/api/nodes/utils.d.ts.map +1 -0
  53. package/dist/types/base/disposable.d.ts +11 -0
  54. package/dist/types/base/disposable.d.ts.map +1 -0
  55. package/dist/types/base/executionStack.d.ts +14 -0
  56. package/dist/types/base/executionStack.d.ts.map +1 -0
  57. package/dist/types/base/index.d.ts +6 -0
  58. package/dist/types/base/index.d.ts.map +1 -0
  59. package/dist/types/base/node.d.ts +27 -0
  60. package/dist/types/base/node.d.ts.map +1 -0
  61. package/dist/types/base/observable.d.ts +37 -0
  62. package/dist/types/base/observable.d.ts.map +1 -0
  63. package/dist/types/base/observer.d.ts +25 -0
  64. package/dist/types/base/observer.d.ts.map +1 -0
  65. package/dist/types/converters/index.d.ts +2 -0
  66. package/dist/types/converters/index.d.ts.map +1 -0
  67. package/dist/types/converters/solid.d.ts +46 -0
  68. package/dist/types/converters/solid.d.ts.map +1 -0
  69. package/dist/types/index.d.ts +2 -63
  70. package/dist/types/index.d.ts.map +1 -1
  71. package/dist/types/nodes/arrayNode.d.ts +2 -0
  72. package/dist/types/nodes/arrayNode.d.ts.map +1 -0
  73. package/dist/types/nodes/effectNode.d.ts +2 -0
  74. package/dist/types/nodes/effectNode.d.ts.map +1 -0
  75. package/dist/types/nodes/index.d.ts +9 -0
  76. package/dist/types/nodes/index.d.ts.map +1 -0
  77. package/dist/types/nodes/mapNode.d.ts +2 -0
  78. package/dist/types/nodes/mapNode.d.ts.map +1 -0
  79. package/dist/types/nodes/signalNode.d.ts +2 -0
  80. package/dist/types/nodes/signalNode.d.ts.map +1 -0
  81. package/dist/types/nodes/valueAsyncNode.d.ts +2 -0
  82. package/dist/types/nodes/valueAsyncNode.d.ts.map +1 -0
  83. package/dist/types/nodes/valueNode.d.ts +2 -0
  84. package/dist/types/nodes/valueNode.d.ts.map +1 -0
  85. package/dist/types/nodes/valueSyncNode.d.ts +2 -0
  86. package/dist/types/nodes/valueSyncNode.d.ts.map +1 -0
  87. package/dist/types/schedulers/asyncResolver.d.ts +2 -0
  88. package/dist/types/schedulers/asyncResolver.d.ts.map +1 -0
  89. package/dist/types/schedulers/asyncScheduler.d.ts +2 -0
  90. package/dist/types/schedulers/asyncScheduler.d.ts.map +1 -0
  91. package/dist/types/schedulers/index.d.ts +5 -0
  92. package/dist/types/schedulers/index.d.ts.map +1 -0
  93. package/dist/types/schedulers/pendingError.d.ts +2 -0
  94. package/dist/types/schedulers/pendingError.d.ts.map +1 -0
  95. package/dist/types/schedulers/scheduler.d.ts +2 -0
  96. package/dist/types/schedulers/scheduler.d.ts.map +1 -0
  97. package/dist/types/schedulers/syncResolver.d.ts +2 -0
  98. package/dist/types/schedulers/syncResolver.d.ts.map +1 -0
  99. package/dist/types/schedulers/syncScheduler.d.ts +2 -0
  100. package/dist/types/schedulers/syncScheduler.d.ts.map +1 -0
  101. package/docs/.vitepress/config.mts +128 -93
  102. package/docs/api/functions/array.md +14 -37
  103. package/docs/api/functions/constant.md +13 -25
  104. package/docs/api/functions/constantAsync.md +69 -0
  105. package/docs/api/functions/derivation.md +14 -33
  106. package/docs/api/functions/derivationAsync.md +34 -0
  107. package/docs/api/functions/from.md +62 -153
  108. package/docs/api/functions/isDisposable.md +8 -30
  109. package/docs/api/functions/map.md +15 -36
  110. package/docs/api/functions/signal.md +8 -23
  111. package/docs/api/functions/state.md +43 -23
  112. package/docs/api/functions/stateAsync.md +69 -0
  113. package/docs/api/functions/subscribe.md +40 -0
  114. package/docs/api/functions/writableDerivation.md +33 -0
  115. package/docs/api/functions/writableDerivationAsync.md +34 -0
  116. package/docs/api/index.md +45 -102
  117. package/docs/api/interfaces/FlowArray.md +439 -0
  118. package/docs/api/interfaces/FlowConstant.md +220 -0
  119. package/docs/api/interfaces/FlowConstantAsync.md +221 -0
  120. package/docs/api/interfaces/FlowDerivation.md +241 -0
  121. package/docs/api/interfaces/FlowDerivationAsync.md +242 -0
  122. package/docs/api/interfaces/FlowDisposable.md +32 -38
  123. package/docs/api/interfaces/FlowEffect.md +64 -0
  124. package/docs/api/interfaces/FlowMap.md +374 -0
  125. package/docs/api/interfaces/FlowObservable.md +155 -0
  126. package/docs/api/interfaces/FlowSignal.md +156 -0
  127. package/docs/api/interfaces/FlowState.md +269 -0
  128. package/docs/api/interfaces/FlowStateAsync.md +268 -0
  129. package/docs/api/interfaces/FlowSubscribable.md +55 -0
  130. package/docs/api/interfaces/FlowTracker.md +61 -0
  131. package/docs/api/interfaces/FlowValue.md +222 -0
  132. package/docs/api/interfaces/FlowWritableDerivation.md +292 -0
  133. package/docs/api/interfaces/FlowWritableDerivationAsync.md +293 -0
  134. package/docs/api/type-aliases/DerivationFunction.md +28 -0
  135. package/docs/api/type-aliases/DerivationFunctionAsync.md +28 -0
  136. package/docs/api/type-aliases/FlowArrayAction.md +19 -8
  137. package/docs/api/type-aliases/FlowDataTracker.md +33 -0
  138. package/docs/api/type-aliases/FlowMapAction.md +48 -0
  139. package/docs/api/type-aliases/FlowOnDataListener.md +33 -0
  140. package/docs/api/type-aliases/FlowOnErrorListener.md +27 -0
  141. package/docs/api/type-aliases/FlowOnPendingListener.md +21 -0
  142. package/docs/api/type-aliases/FlowReadonly.md +22 -0
  143. package/docs/api/type-aliases/InitFunction.md +21 -0
  144. package/docs/api/type-aliases/InitFunctionAsync.md +21 -0
  145. package/docs/api/type-aliases/NotPromise.md +6 -3
  146. package/docs/api/type-aliases/UpdateFunction.md +27 -0
  147. package/docs/api/type-aliases/UpdateFunctionAsync.md +27 -0
  148. package/docs/api/typedoc-sidebar.json +1 -81
  149. package/docs/examples/examples.md +0 -2
  150. package/docs/guide/advanced/architecture.md +1234 -0
  151. package/docs/guide/advanced/migration-v2.md +204 -0
  152. package/docs/guide/advanced/solidjs.md +2 -88
  153. package/docs/guide/introduction/concepts.md +4 -3
  154. package/docs/guide/introduction/conventions.md +2 -33
  155. package/docs/guide/introduction/getting-started.md +28 -23
  156. package/docs/guide/introduction/lifecycle.md +16 -19
  157. package/docs/guide/primitives/array.md +102 -216
  158. package/docs/guide/primitives/constant.md +39 -212
  159. package/docs/guide/primitives/derivations.md +55 -122
  160. package/docs/guide/primitives/effects.md +155 -241
  161. package/docs/guide/primitives/map.md +64 -186
  162. package/docs/guide/primitives/overview.md +45 -128
  163. package/docs/guide/primitives/signal.md +51 -88
  164. package/docs/guide/primitives/state.md +34 -130
  165. package/package.json +56 -60
  166. package/src/api/base/flowDisposable.ts +44 -0
  167. package/src/api/base/flowObservable.ts +28 -0
  168. package/src/api/base/flowSubscribable.ts +87 -0
  169. package/src/api/base/flowTracker.ts +7 -0
  170. package/src/api/base/index.ts +4 -0
  171. package/src/{flow → api}/index.ts +0 -1
  172. package/src/api/nodes/async/flowConstantAsync.ts +36 -0
  173. package/src/api/nodes/async/flowDerivationAsync.ts +42 -0
  174. package/src/api/nodes/async/flowStateAsync.ts +47 -0
  175. package/src/api/nodes/async/flowWritableDerivationAsync.ts +33 -0
  176. package/src/{flow → api}/nodes/async/index.ts +1 -2
  177. package/src/api/nodes/collections/flowArray.ts +155 -0
  178. package/src/api/nodes/collections/flowMap.ts +115 -0
  179. package/src/api/nodes/flowEffect.ts +42 -0
  180. package/src/api/nodes/flowSignal.ts +28 -0
  181. package/src/api/nodes/flowValue.ts +36 -0
  182. package/src/api/nodes/index.ts +7 -0
  183. package/src/api/nodes/sync/flowConstant.ts +33 -0
  184. package/src/api/nodes/sync/flowDerivation.ts +41 -0
  185. package/src/api/nodes/sync/flowState.ts +45 -0
  186. package/src/api/nodes/sync/flowWritableDerivation.ts +31 -0
  187. package/src/{flow → api}/nodes/sync/index.ts +1 -2
  188. package/src/api/nodes/utils.ts +22 -0
  189. package/src/base/disposable.ts +18 -0
  190. package/src/base/executionStack.ts +42 -0
  191. package/src/base/index.ts +5 -0
  192. package/src/base/node.ts +98 -0
  193. package/src/base/observable.ts +87 -0
  194. package/src/base/observer.ts +51 -0
  195. package/src/converters/index.ts +1 -0
  196. package/src/converters/solid.ts +109 -0
  197. package/src/index.ts +2 -64
  198. package/src/nodes/arrayNode.ts +172 -0
  199. package/src/nodes/effectNode.ts +59 -0
  200. package/src/nodes/index.ts +8 -0
  201. package/src/nodes/mapNode.ts +127 -0
  202. package/src/nodes/signalNode.ts +21 -0
  203. package/src/nodes/valueAsyncNode.ts +88 -0
  204. package/src/nodes/valueNode.ts +144 -0
  205. package/src/nodes/valueSyncNode.ts +128 -0
  206. package/src/schedulers/asyncResolver.ts +78 -0
  207. package/src/schedulers/asyncScheduler.ts +66 -0
  208. package/src/schedulers/index.ts +4 -0
  209. package/src/schedulers/pendingError.ts +13 -0
  210. package/src/schedulers/scheduler.ts +9 -0
  211. package/src/schedulers/syncResolver.ts +69 -0
  212. package/src/schedulers/syncScheduler.ts +55 -0
  213. package/test/base/pendingError.test.ts +67 -0
  214. package/test/converters/solid.derivation.browser.test.tsx +69 -0
  215. package/test/converters/solid.node.test.ts +654 -0
  216. package/test/converters/solid.state.browser.test.tsx +1592 -0
  217. package/test/reactivity/flowSignal.test.ts +226 -0
  218. package/test/reactivity/nodes/async/asyncScheduler/asyncResolver.test.ts +593 -0
  219. package/test/reactivity/nodes/async/asyncScheduler/asyncScheduler.test.ts +317 -0
  220. package/test/reactivity/nodes/async/flowConstantAsync.test.ts +652 -0
  221. package/test/reactivity/nodes/async/flowDerivation.test.ts +898 -0
  222. package/test/reactivity/nodes/async/flowDerivationAsync.test.ts +1716 -0
  223. package/test/reactivity/nodes/async/flowStateAsync.test.ts +708 -0
  224. package/test/reactivity/nodes/async/flowWritableDerivationAsync.test.ts +614 -0
  225. package/test/reactivity/nodes/collections/flowArray.asyncStates.test.ts +1289 -0
  226. package/test/reactivity/nodes/collections/flowArray.scalars.test.ts +961 -0
  227. package/test/reactivity/nodes/collections/flowArray.states.test.ts +1035 -0
  228. package/test/reactivity/nodes/collections/flowMap.asyncStates.test.ts +960 -0
  229. package/test/reactivity/nodes/collections/flowMap.scalars.test.ts +775 -0
  230. package/test/reactivity/nodes/collections/flowMap.states.test.ts +958 -0
  231. package/test/reactivity/nodes/sync/flowConstant.test.ts +377 -0
  232. package/test/reactivity/nodes/sync/flowDerivation.test.ts +896 -0
  233. package/test/reactivity/nodes/sync/flowState.test.ts +341 -0
  234. package/test/reactivity/nodes/sync/flowWritableDerivation.test.ts +603 -0
  235. package/test/vitest.d.ts +10 -0
  236. package/tsconfig.json +31 -20
  237. package/typedoc.json +35 -35
  238. package/vite.config.ts +25 -23
  239. package/vitest.browser.config.ts +21 -0
  240. package/vitest.config.ts +12 -12
  241. package/.cursor/plans/unifier-flowresource-avec-flowderivation-c9506e24.plan.md +0 -372
  242. package/.cursor/plans/update-js-e795d61b.plan.md +0 -567
  243. package/dist/types/flow/base/flowDisposable.d.ts +0 -67
  244. package/dist/types/flow/base/flowDisposable.d.ts.map +0 -1
  245. package/dist/types/flow/base/flowEffect.d.ts +0 -127
  246. package/dist/types/flow/base/flowEffect.d.ts.map +0 -1
  247. package/dist/types/flow/base/flowGraph.d.ts +0 -97
  248. package/dist/types/flow/base/flowGraph.d.ts.map +0 -1
  249. package/dist/types/flow/base/flowSignal.d.ts +0 -134
  250. package/dist/types/flow/base/flowSignal.d.ts.map +0 -1
  251. package/dist/types/flow/base/flowTracker.d.ts +0 -15
  252. package/dist/types/flow/base/flowTracker.d.ts.map +0 -1
  253. package/dist/types/flow/base/index.d.ts +0 -7
  254. package/dist/types/flow/base/index.d.ts.map +0 -1
  255. package/dist/types/flow/base/utils.d.ts +0 -20
  256. package/dist/types/flow/base/utils.d.ts.map +0 -1
  257. package/dist/types/flow/collections/flowArray.d.ts +0 -148
  258. package/dist/types/flow/collections/flowArray.d.ts.map +0 -1
  259. package/dist/types/flow/collections/flowMap.d.ts +0 -224
  260. package/dist/types/flow/collections/flowMap.d.ts.map +0 -1
  261. package/dist/types/flow/collections/index.d.ts.map +0 -1
  262. package/dist/types/flow/index.d.ts +0 -4
  263. package/dist/types/flow/index.d.ts.map +0 -1
  264. package/dist/types/flow/nodes/async/flowConstantAsync.d.ts +0 -137
  265. package/dist/types/flow/nodes/async/flowConstantAsync.d.ts.map +0 -1
  266. package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts +0 -137
  267. package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts.map +0 -1
  268. package/dist/types/flow/nodes/async/flowNodeAsync.d.ts +0 -343
  269. package/dist/types/flow/nodes/async/flowNodeAsync.d.ts.map +0 -1
  270. package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts +0 -81
  271. package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts.map +0 -1
  272. package/dist/types/flow/nodes/async/flowStateAsync.d.ts +0 -111
  273. package/dist/types/flow/nodes/async/flowStateAsync.d.ts.map +0 -1
  274. package/dist/types/flow/nodes/async/index.d.ts.map +0 -1
  275. package/dist/types/flow/nodes/index.d.ts +0 -3
  276. package/dist/types/flow/nodes/index.d.ts.map +0 -1
  277. package/dist/types/flow/nodes/sync/flowConstant.d.ts +0 -108
  278. package/dist/types/flow/nodes/sync/flowConstant.d.ts.map +0 -1
  279. package/dist/types/flow/nodes/sync/flowDerivation.d.ts +0 -100
  280. package/dist/types/flow/nodes/sync/flowDerivation.d.ts.map +0 -1
  281. package/dist/types/flow/nodes/sync/flowNode.d.ts +0 -314
  282. package/dist/types/flow/nodes/sync/flowNode.d.ts.map +0 -1
  283. package/dist/types/flow/nodes/sync/flowReadonly.d.ts +0 -57
  284. package/dist/types/flow/nodes/sync/flowReadonly.d.ts.map +0 -1
  285. package/dist/types/flow/nodes/sync/flowState.d.ts +0 -96
  286. package/dist/types/flow/nodes/sync/flowState.d.ts.map +0 -1
  287. package/dist/types/flow/nodes/sync/index.d.ts.map +0 -1
  288. package/dist/types/solid/converters.d.ts +0 -53
  289. package/dist/types/solid/converters.d.ts.map +0 -1
  290. package/dist/types/solid/index.d.ts +0 -3
  291. package/dist/types/solid/index.d.ts.map +0 -1
  292. package/dist/types/solid/primitives.d.ts +0 -181
  293. package/dist/types/solid/primitives.d.ts.map +0 -1
  294. package/docs/api/classes/FlowArray.md +0 -489
  295. package/docs/api/classes/FlowConstant.md +0 -350
  296. package/docs/api/classes/FlowDerivation.md +0 -334
  297. package/docs/api/classes/FlowEffect.md +0 -100
  298. package/docs/api/classes/FlowMap.md +0 -512
  299. package/docs/api/classes/FlowObservable.md +0 -306
  300. package/docs/api/classes/FlowResource.md +0 -380
  301. package/docs/api/classes/FlowResourceAsync.md +0 -362
  302. package/docs/api/classes/FlowSignal.md +0 -160
  303. package/docs/api/classes/FlowState.md +0 -368
  304. package/docs/api/classes/FlowStream.md +0 -367
  305. package/docs/api/classes/FlowStreamAsync.md +0 -364
  306. package/docs/api/classes/SolidDerivation.md +0 -75
  307. package/docs/api/classes/SolidResource.md +0 -91
  308. package/docs/api/classes/SolidState.md +0 -71
  309. package/docs/api/classes/TrackingContext.md +0 -33
  310. package/docs/api/functions/effect.md +0 -49
  311. package/docs/api/functions/resource.md +0 -52
  312. package/docs/api/functions/resourceAsync.md +0 -50
  313. package/docs/api/functions/stream.md +0 -53
  314. package/docs/api/functions/streamAsync.md +0 -50
  315. package/docs/api/interfaces/SolidObservable.md +0 -19
  316. package/docs/api/type-aliases/FlowStreamDisposer.md +0 -15
  317. package/docs/api/type-aliases/FlowStreamSetter.md +0 -27
  318. package/docs/api/type-aliases/FlowStreamUpdater.md +0 -32
  319. package/docs/api/type-aliases/SolidGetter.md +0 -17
  320. package/docs/guide/primitives/resources.md +0 -858
  321. package/docs/guide/primitives/streams.md +0 -931
  322. package/src/flow/base/flowDisposable.ts +0 -71
  323. package/src/flow/base/flowEffect.ts +0 -171
  324. package/src/flow/base/flowGraph.ts +0 -288
  325. package/src/flow/base/flowSignal.ts +0 -207
  326. package/src/flow/base/flowTracker.ts +0 -17
  327. package/src/flow/base/index.ts +0 -6
  328. package/src/flow/base/utils.ts +0 -19
  329. package/src/flow/collections/flowArray.ts +0 -409
  330. package/src/flow/collections/flowMap.ts +0 -398
  331. package/src/flow/nodes/async/flowConstantAsync.ts +0 -142
  332. package/src/flow/nodes/async/flowDerivationAsync.ts +0 -143
  333. package/src/flow/nodes/async/flowNodeAsync.ts +0 -474
  334. package/src/flow/nodes/async/flowReadonlyAsync.ts +0 -81
  335. package/src/flow/nodes/async/flowStateAsync.ts +0 -116
  336. package/src/flow/nodes/await/advanced/index.ts +0 -5
  337. package/src/flow/nodes/await/advanced/resource.ts +0 -134
  338. package/src/flow/nodes/await/advanced/resourceAsync.ts +0 -109
  339. package/src/flow/nodes/await/advanced/stream.ts +0 -188
  340. package/src/flow/nodes/await/advanced/streamAsync.ts +0 -176
  341. package/src/flow/nodes/await/flowConstantAwait.ts +0 -154
  342. package/src/flow/nodes/await/flowDerivationAwait.ts +0 -154
  343. package/src/flow/nodes/await/flowNodeAwait.ts +0 -508
  344. package/src/flow/nodes/await/flowReadonlyAwait.ts +0 -89
  345. package/src/flow/nodes/await/flowStateAwait.ts +0 -130
  346. package/src/flow/nodes/await/index.ts +0 -5
  347. package/src/flow/nodes/index.ts +0 -3
  348. package/src/flow/nodes/sync/flowConstant.ts +0 -111
  349. package/src/flow/nodes/sync/flowDerivation.ts +0 -105
  350. package/src/flow/nodes/sync/flowNode.ts +0 -439
  351. package/src/flow/nodes/sync/flowReadonly.ts +0 -57
  352. package/src/flow/nodes/sync/flowState.ts +0 -101
  353. package/src/solid/converters.ts +0 -139
  354. package/src/solid/index.ts +0 -2
  355. package/src/solid/primitives.ts +0 -215
  356. package/test/base/flowEffect.test.ts +0 -108
  357. package/test/base/flowGraph.test.ts +0 -485
  358. package/test/base/flowSignal.test.ts +0 -372
  359. package/test/collections/flowArray.asyncStates.test.ts +0 -1553
  360. package/test/collections/flowArray.scalars.test.ts +0 -1129
  361. package/test/collections/flowArray.states.test.ts +0 -1365
  362. package/test/collections/flowMap.asyncStates.test.ts +0 -1105
  363. package/test/collections/flowMap.scalars.test.ts +0 -877
  364. package/test/collections/flowMap.states.test.ts +0 -1097
  365. package/test/nodes/async/flowConstantAsync.test.ts +0 -860
  366. package/test/nodes/async/flowDerivationAsync.test.ts +0 -1517
  367. package/test/nodes/async/flowStateAsync.test.ts +0 -1387
  368. package/test/nodes/await/advanced/resource.test.ts +0 -129
  369. package/test/nodes/await/advanced/resourceAsync.test.ts +0 -108
  370. package/test/nodes/await/advanced/stream.test.ts +0 -198
  371. package/test/nodes/await/advanced/streamAsync.test.ts +0 -196
  372. package/test/nodes/await/flowConstantAwait.test.ts +0 -643
  373. package/test/nodes/await/flowDerivationAwait.test.ts +0 -1583
  374. package/test/nodes/await/flowStateAwait.test.ts +0 -999
  375. package/test/nodes/mixed/derivation.test.ts +0 -1527
  376. package/test/nodes/sync/flowConstant.test.ts +0 -620
  377. package/test/nodes/sync/flowDerivation.test.ts +0 -1373
  378. package/test/nodes/sync/flowState.test.ts +0 -945
  379. package/test/solid/converters.test.ts +0 -721
  380. package/test/solid/primitives.test.ts +0 -1031
  381. /package/dist/types/{flow → api/nodes}/collections/index.d.ts +0 -0
  382. /package/docs/guide/advanced/{upgrading.md → migration-v1.md} +0 -0
  383. /package/src/{flow → api/nodes}/collections/index.ts +0 -0
@@ -1,931 +0,0 @@
1
- # Streams
2
-
3
- Streams are reactive primitives for handling continuous, event-driven data. Unlike resources that you fetch on demand, streams **push** updates to you whenever new data arrives.
4
-
5
- ## Understanding Streams
6
-
7
- Think of a stream like a **river of data** - it flows continuously, and you dip your cup in whenever you want the latest value. Or like a **news feed** - stories arrive as they happen, and you're notified of each new one.
8
-
9
- ### Streams vs Resources vs State
10
-
11
- | Feature | State | Resource | Stream |
12
- |---------|-------|----------|--------|
13
- | Data flow | You set it | You pull (fetch) | It pushes to you |
14
- | Updates | Manual (`.set()`) | Manual (`.fetch()`) | Automatic (events) |
15
- | Use case | User input, toggles | API calls, file loading | WebSockets, events, timers |
16
- | Initial value | Required | Optional | Optional (undefined until first update) |
17
- | Cleanup | None needed | None needed | **Must return disposer** |
18
-
19
- ```mermaid
20
- flowchart LR
21
- A[State] -->|You control| B[Value]
22
- C[Resource] -->|You request| D[Fetch] -->|Returns| B
23
- E[External Event] -->|Pushes| F[Stream] -->|Updates| B
24
-
25
- style A fill:#FFE6E6
26
- style C fill:#E6F3FF
27
- style F fill:#E6FFE6
28
- ```
29
-
30
- ## When to Use Streams
31
-
32
- Use streams when:
33
- - ✅ Data comes from external events (WebSockets, DOM events)
34
- - ✅ Updates happen continuously (timers, intervals)
35
- - ✅ You don't control when data arrives
36
- - ✅ You want push-based reactivity
37
-
38
- Use resources when:
39
- - ✅ You initiate the data fetch
40
- - ✅ Data is pulled on demand
41
- - ✅ You control when to refetch
42
-
43
- Use state when:
44
- - ✅ You set the value directly
45
- - ✅ Updates come from user actions
46
- - ✅ Data is local to your application
47
-
48
- ## Creating Streams
49
-
50
- The basic syntax:
51
-
52
- ```typescript
53
- import { stream } from '@ersbeth/picoflow'
54
-
55
- const $stream = stream(
56
- (set) => {
57
- // Setup: Subscribe to events, start timers, etc.
58
- // Call set(value) whenever you have new data
59
-
60
- // Return cleanup function
61
- return () => {
62
- // Cleanup: Unsubscribe, clear timers, close connections
63
- }
64
- },
65
- initialValue // Optional initial value
66
- )
67
- ```
68
-
69
- ### Anatomy of a Stream
70
-
71
- ```mermaid
72
- flowchart TD
73
- A[Create stream] --> B[Execute updater function]
74
- B --> C[Setup subscriptions/timers]
75
- C --> D[Return disposer function]
76
- D --> E[Wait for events]
77
- E --> F{Event occurs}
78
- F -->|Yes| G[Call set new value]
79
- G --> H[Notify dependents]
80
- H --> E
81
- F -->|dispose called| I[Run disposer]
82
- I --> J[Cleanup complete]
83
- ```
84
-
85
- ### The `set` Callback
86
-
87
- Inside your updater function, you receive a `set` callback. Call it with a new value whenever you want to update the stream:
88
-
89
- ```typescript
90
- const $ticker = stream((set) => {
91
- // Call set() to update the value
92
- const interval = setInterval(() => {
93
- set(Date.now())
94
- }, 1000)
95
-
96
- return () => clearInterval(interval)
97
- }, Date.now())
98
- ```
99
-
100
- ### The Disposer
101
-
102
- The function you return is crucial - it cleans up resources when the stream is disposed:
103
-
104
- ```typescript
105
- const $messages = stream((set) => {
106
- const ws = new WebSocket('ws://example.com')
107
-
108
- ws.onmessage = (event) => {
109
- set(event.data) // Update stream with each message
110
- }
111
-
112
- // IMPORTANT: Return cleanup function!
113
- return () => {
114
- ws.close() // Clean up WebSocket connection
115
- }
116
- })
117
-
118
- // Later, cleanup
119
- $messages.dispose() // Calls the disposer, closes WebSocket
120
- ```
121
-
122
- ## Basic Examples
123
-
124
- ### Example 1: Timer
125
-
126
- ```typescript
127
- import { stream, effect } from '@ersbeth/picoflow'
128
-
129
- const $timer = stream((set) => {
130
- let count = 0
131
- const interval = setInterval(() => {
132
- set(++count)
133
- }, 1000)
134
-
135
- return () => clearInterval(interval)
136
- }, 0)
137
-
138
- effect((t) => {
139
- console.log('Timer:', $timer.get(t))
140
- })
141
- // Logs: "Timer: 0", "Timer: 1", "Timer: 2", ...
142
- ```
143
-
144
- ### Example 2: Current Time
145
-
146
- ```typescript
147
- const $currentTime = stream((set) => {
148
- // Update every second
149
- const interval = setInterval(() => {
150
- set(new Date())
151
- }, 1000)
152
-
153
- return () => clearInterval(interval)
154
- }, new Date())
155
-
156
- effect((t) => {
157
- const time = $currentTime.get(t)
158
- document.getElementById('clock').textContent = time.toLocaleTimeString()
159
- })
160
- ```
161
-
162
- ### Example 3: Mouse Position
163
-
164
- ```typescript
165
- interface Point {
166
- x: number
167
- y: number
168
- }
169
-
170
- const $mousePosition = stream<Point>((set) => {
171
- function handleMouseMove(event: MouseEvent) {
172
- set({ x: event.clientX, y: event.clientY })
173
- }
174
-
175
- window.addEventListener('mousemove', handleMouseMove)
176
-
177
- return () => {
178
- window.removeEventListener('mousemove', handleMouseMove)
179
- }
180
- }, { x: 0, y: 0 })
181
-
182
- effect((t) => {
183
- const pos = $mousePosition.get(t)
184
- console.log(`Mouse at (${pos.x}, ${pos.y})`)
185
- })
186
- ```
187
-
188
- ## Asynchronous Streams
189
-
190
- Async streams handle Promise-based values:
191
-
192
- ```typescript
193
- import { streamAsync } from '@ersbeth/picoflow'
194
-
195
- const $asyncData = streamAsync((set) => {
196
- async function fetchData() {
197
- const response = await fetch('/api/data')
198
- const data = await response.json()
199
- set(data)
200
- }
201
-
202
- const interval = setInterval(fetchData, 5000)
203
- fetchData() // Initial fetch
204
-
205
- return () => clearInterval(interval)
206
- })
207
- ```
208
-
209
- Use with `await` in effects:
210
-
211
- ```typescript
212
- effect(async (t) => {
213
- const data = await $asyncData.get(t)
214
- console.log('Data:', data)
215
- })
216
- ```
217
-
218
- ## Stream Lifecycle
219
-
220
- ```mermaid
221
- stateDiagram-v2
222
- [*] --> Created: stream((set) => ...)
223
- Created --> Active: Execute updater
224
- Active --> Active: set() called → update value
225
- Active --> Disposing: .dispose() called
226
- Disposing --> Disposed: Execute disposer function
227
- Disposed --> [*]
228
-
229
- note right of Active: Event handlers listening<br/>Timers running<br/>Connections open
230
- note right of Disposed: All cleanup complete<br/>No more updates
231
- ```
232
-
233
- ## Push-Based Data Flow
234
-
235
- Here's how streams push data compared to pull-based resources:
236
-
237
- ```mermaid
238
- sequenceDiagram
239
- participant Event as External Event
240
- participant Stream as Stream
241
- participant Effect as Effect
242
-
243
- Note over Stream: Stream created
244
- Stream->>Stream: Setup subscriptions
245
-
246
- Note over Event,Effect: Data flows automatically
247
- Event->>Stream: Event occurs
248
- Stream->>Stream: Call set(value)
249
- Stream->>Effect: Notify of change
250
- Effect->>Stream: .get(t)
251
- Stream->>Effect: Return latest value
252
-
253
- Note over Event,Effect: More events...
254
- Event->>Stream: Another event
255
- Stream->>Stream: Call set(value)
256
- Stream->>Effect: Notify again
257
- ```
258
-
259
- Compare to pull-based resources:
260
-
261
- ```mermaid
262
- sequenceDiagram
263
- participant You as Your Code
264
- participant Resource as Resource
265
- participant API as External API
266
-
267
- Note over You,API: You control when to fetch
268
- You->>Resource: .fetch()
269
- Resource->>API: Request data
270
- API->>Resource: Return data
271
- Resource->>Resource: Update value
272
- Resource->>You: Done
273
- ```
274
-
275
- ## Practical Examples
276
-
277
- ### Example 1: WebSocket Connection
278
-
279
- ```typescript
280
- interface Message {
281
- type: string
282
- content: string
283
- timestamp: number
284
- }
285
-
286
- const $messages = stream<Message>((set) => {
287
- const ws = new WebSocket('wss://example.com/socket')
288
-
289
- ws.onopen = () => {
290
- console.log('WebSocket connected')
291
- }
292
-
293
- ws.onmessage = (event) => {
294
- const message = JSON.parse(event.data)
295
- set(message)
296
- }
297
-
298
- ws.onerror = (error) => {
299
- console.error('WebSocket error:', error)
300
- }
301
-
302
- ws.onclose = () => {
303
- console.log('WebSocket closed')
304
- }
305
-
306
- // Cleanup
307
- return () => {
308
- if (ws.readyState === WebSocket.OPEN) {
309
- ws.close()
310
- }
311
- }
312
- })
313
-
314
- // Display messages
315
- effect((t) => {
316
- const message = $messages.get(t)
317
- if (message) {
318
- displayMessage(message)
319
- }
320
- })
321
- ```
322
-
323
- ### Example 2: Keyboard Events
324
-
325
- ```typescript
326
- const $keyPressed = stream<string>((set) => {
327
- function handleKeyDown(event: KeyboardEvent) {
328
- set(event.key)
329
- }
330
-
331
- window.addEventListener('keydown', handleKeyDown)
332
-
333
- return () => {
334
- window.removeEventListener('keydown', handleKeyDown)
335
- }
336
- }, '')
337
-
338
- effect((t) => {
339
- const key = $keyPressed.get(t)
340
- console.log('Key pressed:', key)
341
- })
342
- ```
343
-
344
- ### Example 3: Countdown Timer
345
-
346
- ```typescript
347
- const $countdown = stream((set) => {
348
- let remaining = 60 // 60 seconds
349
- set(remaining)
350
-
351
- const interval = setInterval(() => {
352
- remaining--
353
- set(remaining)
354
-
355
- if (remaining <= 0) {
356
- clearInterval(interval)
357
- }
358
- }, 1000)
359
-
360
- return () => clearInterval(interval)
361
- }, 60)
362
-
363
- effect((t) => {
364
- const time = $countdown.get(t)
365
- document.getElementById('countdown').textContent = `${time}s`
366
-
367
- if (time === 0) {
368
- alert('Time is up!')
369
- }
370
- })
371
- ```
372
-
373
- ### Example 4: Real-time Notifications
374
-
375
- ```typescript
376
- interface Notification {
377
- id: string
378
- title: string
379
- message: string
380
- }
381
-
382
- const $notifications = stream<Notification[]>((set) => {
383
- const notifications: Notification[] = []
384
-
385
- // Server-Sent Events
386
- const eventSource = new EventSource('/api/notifications/stream')
387
-
388
- eventSource.onmessage = (event) => {
389
- const notification = JSON.parse(event.data)
390
- notifications.push(notification)
391
- set([...notifications]) // Update with new array
392
- }
393
-
394
- eventSource.onerror = (error) => {
395
- console.error('SSE error:', error)
396
- }
397
-
398
- return () => {
399
- eventSource.close()
400
- }
401
- }, [])
402
-
403
- // Show notifications
404
- effect((t) => {
405
- const notifications = $notifications.get(t)
406
- displayNotifications(notifications)
407
- })
408
- ```
409
-
410
- ### Example 5: Window Resize
411
-
412
- ```typescript
413
- interface WindowSize {
414
- width: number
415
- height: number
416
- }
417
-
418
- const $windowSize = stream<WindowSize>((set) => {
419
- function updateSize() {
420
- set({
421
- width: window.innerWidth,
422
- height: window.innerHeight
423
- })
424
- }
425
-
426
- // Initial size
427
- updateSize()
428
-
429
- // Listen for changes
430
- window.addEventListener('resize', updateSize)
431
-
432
- return () => {
433
- window.removeEventListener('resize', updateSize)
434
- }
435
- }, { width: window.innerWidth, height: window.innerHeight })
436
-
437
- // Respond to size changes
438
- effect((t) => {
439
- const size = $windowSize.get(t)
440
- console.log(`Window: ${size.width}x${size.height}`)
441
-
442
- if (size.width < 768) {
443
- enableMobileLayout()
444
- } else {
445
- enableDesktopLayout()
446
- }
447
- })
448
- ```
449
-
450
- ## Stream Patterns
451
-
452
- ### Pattern 1: Filtered Stream
453
-
454
- ```typescript
455
- // Only emit values that pass a condition
456
- function filteredStream<T>(
457
- setup: (set: (value: T) => void) => () => void,
458
- predicate: (value: T) => boolean,
459
- initial?: T
460
- ) {
461
- return stream<T>((set) => {
462
- return setup((value) => {
463
- if (predicate(value)) {
464
- set(value)
465
- }
466
- })
467
- }, initial)
468
- }
469
-
470
- // Usage
471
- const $significantMoves = filteredStream(
472
- (set) => {
473
- let lastX = 0
474
- function handleMove(event: MouseEvent) {
475
- // Only emit if mouse moved > 10px
476
- if (Math.abs(event.clientX - lastX) > 10) {
477
- lastX = event.clientX
478
- set(event.clientX)
479
- }
480
- }
481
- window.addEventListener('mousemove', handleMove)
482
- return () => window.removeEventListener('mousemove', handleMove)
483
- },
484
- (x) => true,
485
- 0
486
- )
487
- ```
488
-
489
- ### Pattern 2: Debounced Stream
490
-
491
- ```typescript
492
- function debouncedStream<T>(
493
- setup: (set: (value: T) => void) => () => void,
494
- delay: number,
495
- initial?: T
496
- ) {
497
- return stream<T>((set) => {
498
- let timeout: number | null = null
499
-
500
- const disposer = setup((value) => {
501
- if (timeout) clearTimeout(timeout)
502
- timeout = setTimeout(() => set(value), delay)
503
- })
504
-
505
- return () => {
506
- if (timeout) clearTimeout(timeout)
507
- disposer()
508
- }
509
- }, initial)
510
- }
511
-
512
- // Usage: Debounce input changes
513
- const $searchQuery = state('')
514
- const $debouncedQuery = debouncedStream((set) => {
515
- return effect((t) => {
516
- set($searchQuery.get(t))
517
- }).dispose
518
- }, 300)
519
- ```
520
-
521
- ### Pattern 3: Combined Streams
522
-
523
- ```typescript
524
- // Merge multiple streams
525
- function mergeStreams<T>(...streams: FlowStream<T>[]) {
526
- return stream<T>((set) => {
527
- const disposers = streams.map(stream => {
528
- return effect((t) => {
529
- set(stream.get(t))
530
- })
531
- })
532
-
533
- return () => {
534
- disposers.forEach(d => d.dispose())
535
- }
536
- })
537
- }
538
- ```
539
-
540
- ### Pattern 4: Stream from Observable
541
-
542
- ```typescript
543
- import { Observable } from 'rxjs'
544
-
545
- function fromObservable<T>(observable: Observable<T>, initial?: T) {
546
- return stream<T>((set) => {
547
- const subscription = observable.subscribe({
548
- next: (value) => set(value),
549
- error: (error) => console.error('Observable error:', error)
550
- })
551
-
552
- return () => subscription.unsubscribe()
553
- }, initial)
554
- }
555
-
556
- // Usage
557
- import { interval } from 'rxjs'
558
- const $rxTimer = fromObservable(interval(1000), 0)
559
- ```
560
-
561
- ## Cleanup and Disposal
562
-
563
- **The most important rule:** Always return a disposer function!
564
-
565
- ### Why Cleanup Matters
566
-
567
- ```mermaid
568
- flowchart TD
569
- A[Stream Created] --> B[Subscriptions Active]
570
- B --> C[Timers Running]
571
- C --> D[Connections Open]
572
- D --> E{Stream Disposed?}
573
- E -->|No| F[⚠️ MEMORY LEAK]
574
- E -->|Yes| G[Run Disposer]
575
- G --> H[Clear Subscriptions]
576
- H --> I[Stop Timers]
577
- I --> J[Close Connections]
578
- J --> K[✅ Clean]
579
-
580
- style F fill:#FFE6E6
581
- style K fill:#E6FFE6
582
- ```
583
-
584
- ### Good Cleanup Examples
585
-
586
- ```typescript
587
- // ✅ Timer cleanup
588
- const $timer = stream((set) => {
589
- const id = setInterval(() => set(Date.now()), 1000)
590
- return () => clearInterval(id) // Cleanup!
591
- }, Date.now())
592
-
593
- // ✅ Event listener cleanup
594
- const $clicks = stream((set) => {
595
- const handler = () => set(Date.now())
596
- window.addEventListener('click', handler)
597
- return () => window.removeEventListener('click', handler) // Cleanup!
598
- })
599
-
600
- // ✅ WebSocket cleanup
601
- const $ws = stream((set) => {
602
- const socket = new WebSocket('ws://example.com')
603
- socket.onmessage = (e) => set(e.data)
604
- return () => socket.close() // Cleanup!
605
- })
606
-
607
- // ✅ Multiple cleanups
608
- const $multi = stream((set) => {
609
- const interval = setInterval(() => set('tick'), 1000)
610
- const handler = () => set('click')
611
- window.addEventListener('click', handler)
612
-
613
- return () => {
614
- clearInterval(interval)
615
- window.removeEventListener('click', handler)
616
- } // Cleanup both!
617
- })
618
- ```
619
-
620
- ### Bad Cleanup Examples
621
-
622
- ```typescript
623
- // ❌ No cleanup - memory leak!
624
- const $bad = stream((set) => {
625
- setInterval(() => set(Date.now()), 1000)
626
- // Missing: return () => clearInterval(...)
627
- })
628
-
629
- // ❌ Cleanup doesn't work
630
- const $broken = stream((set) => {
631
- setInterval(() => set(Date.now()), 1000)
632
- return () => {
633
- clearInterval(123) // Wrong ID!
634
- }
635
- })
636
- ```
637
-
638
- ## Error Handling
639
-
640
- ### Basic Error Handling
641
-
642
- ```typescript
643
- const $data = streamAsync((set) => {
644
- const ws = new WebSocket('wss://example.com')
645
-
646
- ws.onmessage = async (event) => {
647
- try {
648
- const data = JSON.parse(event.data)
649
- set(data)
650
- } catch (error) {
651
- console.error('Failed to parse message:', error)
652
- }
653
- }
654
-
655
- ws.onerror = (error) => {
656
- console.error('WebSocket error:', error)
657
- }
658
-
659
- return () => ws.close()
660
- })
661
- ```
662
-
663
- ### With Error State
664
-
665
- ```typescript
666
- const $wsData = stream<any>((set) => {
667
- const ws = new WebSocket('wss://example.com')
668
-
669
- ws.onopen = () => $wsStatus.set('connected')
670
- ws.onclose = () => $wsStatus.set('disconnected')
671
- ws.onerror = () => $wsStatus.set('error')
672
- ws.onmessage = (e) => set(JSON.parse(e.data))
673
-
674
- return () => ws.close()
675
- })
676
-
677
- const $wsStatus = state<'connected' | 'disconnected' | 'error'>('disconnected')
678
-
679
- effect((t) => {
680
- const status = $wsStatus.get(t)
681
-
682
- switch (status) {
683
- case 'connected':
684
- showConnected()
685
- break
686
- case 'error':
687
- showError('Connection failed')
688
- break
689
- case 'disconnected':
690
- showDisconnected()
691
- break
692
- }
693
- })
694
- ```
695
-
696
- ## Best Practices
697
-
698
- ### 1. Always Return a Disposer
699
-
700
- ```typescript
701
- // ✅ Good
702
- const $stream = stream((set) => {
703
- const id = setInterval(() => set(Date.now()), 1000)
704
- return () => clearInterval(id)
705
- }, Date.now())
706
-
707
- // ❌ Bad - no cleanup
708
- const $stream = stream((set) => {
709
- setInterval(() => set(Date.now()), 1000)
710
- // Missing return!
711
- }, Date.now())
712
- ```
713
-
714
- ### 2. Provide Initial Values
715
-
716
- ```typescript
717
- // ✅ Good - immediate value available
718
- const $time = stream((set) => {
719
- const id = setInterval(() => set(Date.now()), 1000)
720
- return () => clearInterval(id)
721
- }, Date.now()) // Initial value
722
-
723
- // ⚠️ Okay but requires undefined check
724
- const $time = stream((set) => {
725
- const id = setInterval(() => set(Date.now()), 1000)
726
- return () => clearInterval(id)
727
- }) // undefined initially
728
- ```
729
-
730
- ### 3. Handle Connection Errors
731
-
732
- ```typescript
733
- const $ws = stream((set) => {
734
- const ws = new WebSocket('wss://example.com')
735
-
736
- ws.onerror = (error) => {
737
- console.error('WebSocket error:', error)
738
- // Maybe set error state or reconnect
739
- }
740
-
741
- ws.onmessage = (e) => set(e.data)
742
-
743
- return () => ws.close()
744
- })
745
- ```
746
-
747
- ### 4. Avoid Duplicate Subscriptions
748
-
749
- ```typescript
750
- // ❌ Bad - creates new stream on every render
751
- function MyComponent() {
752
- const $stream = stream((set) => { /* ... */ })
753
- // New stream created each time!
754
- }
755
-
756
- // ✅ Good - create stream once
757
- const $stream = stream((set) => { /* ... */ })
758
-
759
- function MyComponent() {
760
- // Use existing stream
761
- }
762
- ```
763
-
764
- ### 5. Clean Up in Components
765
-
766
- ```typescript
767
- // React example
768
- function useStream<T>(stream: FlowStream<T>) {
769
- useEffect(() => {
770
- return () => {
771
- stream.dispose() // Cleanup when component unmounts
772
- }
773
- }, [stream])
774
-
775
- // ... use stream
776
- }
777
- ```
778
-
779
- ## Comparing: Stream vs Resource
780
-
781
- ```mermaid
782
- flowchart TD
783
- A{Who initiates updates?} -->|You do pull| B[Use Resource]
784
- A -->|External pushes| C[Use Stream]
785
- B --> D[.fetch to get data]
786
- C --> E[.set called by events]
787
- D --> F[HTTP APIs<br/>File reads<br/>Database queries]
788
- E --> G[WebSockets<br/>DOM events<br/>Timers<br/>Server-Sent Events]
789
- ```
790
-
791
- ## Common Pitfalls
792
-
793
- ### Pitfall 1: Forgetting Cleanup
794
-
795
- ```typescript
796
- // ❌ Memory leak!
797
- const $bad = stream((set) => {
798
- setInterval(() => set(Date.now()), 1000)
799
- })
800
-
801
- // ✅ Proper cleanup
802
- const $good = stream((set) => {
803
- const id = setInterval(() => set(Date.now()), 1000)
804
- return () => clearInterval(id)
805
- }, Date.now())
806
- ```
807
-
808
- ### Pitfall 2: Wrong Cleanup Reference
809
-
810
- ```typescript
811
- // ❌ Loses reference to interval ID
812
- const $bad = stream((set) => {
813
- setInterval(() => set(Date.now()), 1000)
814
- return () => clearInterval(999) // Wrong ID!
815
- }, Date.now())
816
-
817
- // ✅ Correct reference
818
- const $good = stream((set) => {
819
- const id = setInterval(() => set(Date.now()), 1000)
820
- return () => clearInterval(id) // Correct ID
821
- }, Date.now())
822
- ```
823
-
824
- ### Pitfall 3: Not Handling Errors
825
-
826
- ```typescript
827
- // ❌ Unhandled errors crash app
828
- const $bad = stream((set) => {
829
- const ws = new WebSocket('wss://example.com')
830
- ws.onmessage = (e) => set(JSON.parse(e.data)) // Might throw!
831
- return () => ws.close()
832
- })
833
-
834
- // ✅ Error handling
835
- const $good = stream((set) => {
836
- const ws = new WebSocket('wss://example.com')
837
- ws.onmessage = (e) => {
838
- try {
839
- set(JSON.parse(e.data))
840
- } catch (error) {
841
- console.error('Parse error:', error)
842
- }
843
- }
844
- ws.onerror = (error) => console.error('WS error:', error)
845
- return () => ws.close()
846
- })
847
- ```
848
-
849
- ## Complete Example: Live Chat
850
-
851
- ```typescript
852
- import { stream, state, derivation, effect } from '@ersbeth/picoflow'
853
-
854
- interface ChatMessage {
855
- id: string
856
- user: string
857
- text: string
858
- timestamp: number
859
- }
860
-
861
- // WebSocket stream for incoming messages
862
- const $incomingMessages = stream<ChatMessage>((set) => {
863
- const ws = new WebSocket('wss://chat.example.com')
864
-
865
- ws.onopen = () => {
866
- console.log('Chat connected')
867
- $connected.set(true)
868
- }
869
-
870
- ws.onmessage = (event) => {
871
- const message = JSON.parse(event.data)
872
- set(message)
873
- }
874
-
875
- ws.onerror = (error) => {
876
- console.error('Chat error:', error)
877
- $connected.set(false)
878
- }
879
-
880
- ws.onclose = () => {
881
- console.log('Chat disconnected')
882
- $connected.set(false)
883
- }
884
-
885
- return () => {
886
- ws.close()
887
- }
888
- })
889
-
890
- // Connection state
891
- const $connected = state(false)
892
-
893
- // All messages
894
- const $messages = state<ChatMessage[]>([])
895
-
896
- // Add incoming messages to list
897
- effect((t) => {
898
- const newMessage = $incomingMessages.get(t)
899
- if (newMessage) {
900
- $messages.set(messages => [...messages, newMessage])
901
- }
902
- })
903
-
904
- // Unread count
905
- const $lastReadTime = state(Date.now())
906
- const $unreadCount = derivation((t) => {
907
- const messages = $messages.get(t)
908
- const lastRead = $lastReadTime.get(t)
909
- return messages.filter(m => m.timestamp > lastRead).length
910
- })
911
-
912
- // Display messages
913
- effect((t) => {
914
- const messages = $messages.get(t)
915
- const connected = $connected.get(t)
916
-
917
- renderChatUI(messages, connected)
918
- })
919
-
920
- // Update unread badge
921
- effect((t) => {
922
- const unread = $unreadCount.get(t)
923
- document.getElementById('unread-badge').textContent = String(unread)
924
- document.getElementById('unread-badge').hidden = unread === 0
925
- })
926
-
927
- // Mark as read
928
- function markAllRead() {
929
- $lastReadTime.set(Date.now())
930
- }
931
- ```