@ersbeth/picoflow 1.1.2 → 2.0.1
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.
- package/.vscode/settings.json +3 -3
- package/CHANGELOG.md +43 -0
- package/README.md +2 -18
- package/biome.json +45 -35
- package/dist/picoflow.js +854 -1520
- package/dist/types/api/base/flowDisposable.d.ts +41 -0
- package/dist/types/api/base/flowDisposable.d.ts.map +1 -0
- package/dist/types/api/base/flowObservable.d.ts +27 -0
- package/dist/types/api/base/flowObservable.d.ts.map +1 -0
- package/dist/types/api/base/flowSubscribable.d.ts +79 -0
- package/dist/types/api/base/flowSubscribable.d.ts.map +1 -0
- package/dist/types/api/base/flowTracker.d.ts +8 -0
- package/dist/types/api/base/flowTracker.d.ts.map +1 -0
- package/dist/types/api/base/index.d.ts +5 -0
- package/dist/types/api/base/index.d.ts.map +1 -0
- package/dist/types/api/index.d.ts +3 -0
- package/dist/types/api/index.d.ts.map +1 -0
- package/dist/types/api/nodes/async/flowConstantAsync.d.ts +31 -0
- package/dist/types/api/nodes/async/flowConstantAsync.d.ts.map +1 -0
- package/dist/types/api/nodes/async/flowDerivationAsync.d.ts +37 -0
- package/dist/types/api/nodes/async/flowDerivationAsync.d.ts.map +1 -0
- package/dist/types/api/nodes/async/flowStateAsync.d.ts +41 -0
- package/dist/types/api/nodes/async/flowStateAsync.d.ts.map +1 -0
- package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts +30 -0
- package/dist/types/api/nodes/async/flowWritableDerivationAsync.d.ts.map +1 -0
- package/dist/types/{flow → api}/nodes/async/index.d.ts +1 -2
- package/dist/types/api/nodes/async/index.d.ts.map +1 -0
- package/dist/types/api/nodes/collections/flowArray.d.ts +134 -0
- package/dist/types/api/nodes/collections/flowArray.d.ts.map +1 -0
- package/dist/types/api/nodes/collections/flowMap.d.ts +98 -0
- package/dist/types/api/nodes/collections/flowMap.d.ts.map +1 -0
- package/dist/types/api/nodes/collections/index.d.ts.map +1 -0
- package/dist/types/api/nodes/flowEffect.d.ts +29 -0
- package/dist/types/api/nodes/flowEffect.d.ts.map +1 -0
- package/dist/types/api/nodes/flowSignal.d.ts +25 -0
- package/dist/types/api/nodes/flowSignal.d.ts.map +1 -0
- package/dist/types/api/nodes/flowValue.d.ts +36 -0
- package/dist/types/api/nodes/flowValue.d.ts.map +1 -0
- package/dist/types/api/nodes/index.d.ts +8 -0
- package/dist/types/api/nodes/index.d.ts.map +1 -0
- package/dist/types/api/nodes/sync/flowConstant.d.ts +29 -0
- package/dist/types/api/nodes/sync/flowConstant.d.ts.map +1 -0
- package/dist/types/api/nodes/sync/flowDerivation.d.ts +36 -0
- package/dist/types/api/nodes/sync/flowDerivation.d.ts.map +1 -0
- package/dist/types/api/nodes/sync/flowState.d.ts +39 -0
- package/dist/types/api/nodes/sync/flowState.d.ts.map +1 -0
- package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts +28 -0
- package/dist/types/api/nodes/sync/flowWritableDerivation.d.ts.map +1 -0
- package/dist/types/{flow → api}/nodes/sync/index.d.ts +1 -2
- package/dist/types/api/nodes/sync/index.d.ts.map +1 -0
- package/dist/types/api/nodes/utils.d.ts +23 -0
- package/dist/types/api/nodes/utils.d.ts.map +1 -0
- package/dist/types/base/disposable.d.ts +11 -0
- package/dist/types/base/disposable.d.ts.map +1 -0
- package/dist/types/base/executionStack.d.ts +14 -0
- package/dist/types/base/executionStack.d.ts.map +1 -0
- package/dist/types/base/index.d.ts +6 -0
- package/dist/types/base/index.d.ts.map +1 -0
- package/dist/types/base/node.d.ts +27 -0
- package/dist/types/base/node.d.ts.map +1 -0
- package/dist/types/base/observable.d.ts +35 -0
- package/dist/types/base/observable.d.ts.map +1 -0
- package/dist/types/base/observer.d.ts +25 -0
- package/dist/types/base/observer.d.ts.map +1 -0
- package/dist/types/converters/index.d.ts +2 -0
- package/dist/types/converters/index.d.ts.map +1 -0
- package/dist/types/converters/solid.d.ts +46 -0
- package/dist/types/converters/solid.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -63
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/nodes/arrayNode.d.ts +25 -0
- package/dist/types/nodes/arrayNode.d.ts.map +1 -0
- package/dist/types/nodes/effectNode.d.ts +16 -0
- package/dist/types/nodes/effectNode.d.ts.map +1 -0
- package/dist/types/nodes/index.d.ts +8 -0
- package/dist/types/nodes/index.d.ts.map +1 -0
- package/dist/types/nodes/mapNode.d.ts +19 -0
- package/dist/types/nodes/mapNode.d.ts.map +1 -0
- package/dist/types/nodes/signalNode.d.ts +10 -0
- package/dist/types/nodes/signalNode.d.ts.map +1 -0
- package/dist/types/nodes/valueAsyncNode.d.ts +23 -0
- package/dist/types/nodes/valueAsyncNode.d.ts.map +1 -0
- package/dist/types/nodes/valueNode.d.ts +20 -0
- package/dist/types/nodes/valueNode.d.ts.map +1 -0
- package/dist/types/nodes/valueSyncNode.d.ts +23 -0
- package/dist/types/nodes/valueSyncNode.d.ts.map +1 -0
- package/dist/types/schedulers/asyncResolver.d.ts +19 -0
- package/dist/types/schedulers/asyncResolver.d.ts.map +1 -0
- package/dist/types/schedulers/asyncScheduler.d.ts +20 -0
- package/dist/types/schedulers/asyncScheduler.d.ts.map +1 -0
- package/dist/types/schedulers/index.d.ts +5 -0
- package/dist/types/schedulers/index.d.ts.map +1 -0
- package/dist/types/schedulers/pendingError.d.ts +9 -0
- package/dist/types/schedulers/pendingError.d.ts.map +1 -0
- package/dist/types/schedulers/scheduler.d.ts +10 -0
- package/dist/types/schedulers/scheduler.d.ts.map +1 -0
- package/dist/types/schedulers/syncResolver.d.ts +19 -0
- package/dist/types/schedulers/syncResolver.d.ts.map +1 -0
- package/dist/types/schedulers/syncScheduler.d.ts +19 -0
- package/dist/types/schedulers/syncScheduler.d.ts.map +1 -0
- package/docs/.vitepress/config.mts +128 -93
- package/docs/api/functions/array.md +14 -37
- package/docs/api/functions/constant.md +13 -25
- package/docs/api/functions/constantAsync.md +69 -0
- package/docs/api/functions/derivation.md +14 -33
- package/docs/api/functions/derivationAsync.md +34 -0
- package/docs/api/functions/from.md +62 -153
- package/docs/api/functions/isDisposable.md +8 -30
- package/docs/api/functions/map.md +15 -36
- package/docs/api/functions/signal.md +8 -23
- package/docs/api/functions/state.md +43 -23
- package/docs/api/functions/stateAsync.md +69 -0
- package/docs/api/functions/subscribe.md +40 -0
- package/docs/api/functions/writableDerivation.md +33 -0
- package/docs/api/functions/writableDerivationAsync.md +34 -0
- package/docs/api/index.md +45 -102
- package/docs/api/interfaces/FlowArray.md +439 -0
- package/docs/api/interfaces/FlowConstant.md +220 -0
- package/docs/api/interfaces/FlowConstantAsync.md +221 -0
- package/docs/api/interfaces/FlowDerivation.md +241 -0
- package/docs/api/interfaces/FlowDerivationAsync.md +242 -0
- package/docs/api/interfaces/FlowDisposable.md +32 -38
- package/docs/api/interfaces/FlowEffect.md +64 -0
- package/docs/api/interfaces/FlowMap.md +374 -0
- package/docs/api/interfaces/FlowObservable.md +155 -0
- package/docs/api/interfaces/FlowSignal.md +156 -0
- package/docs/api/interfaces/FlowState.md +269 -0
- package/docs/api/interfaces/FlowStateAsync.md +268 -0
- package/docs/api/interfaces/FlowSubscribable.md +55 -0
- package/docs/api/interfaces/FlowTracker.md +61 -0
- package/docs/api/interfaces/FlowValue.md +222 -0
- package/docs/api/interfaces/FlowWritableDerivation.md +292 -0
- package/docs/api/interfaces/FlowWritableDerivationAsync.md +293 -0
- package/docs/api/type-aliases/DerivationFunction.md +28 -0
- package/docs/api/type-aliases/DerivationFunctionAsync.md +28 -0
- package/docs/api/type-aliases/FlowArrayAction.md +19 -8
- package/docs/api/type-aliases/FlowDataTracker.md +33 -0
- package/docs/api/type-aliases/FlowMapAction.md +48 -0
- package/docs/api/type-aliases/FlowOnDataListener.md +33 -0
- package/docs/api/type-aliases/FlowOnErrorListener.md +27 -0
- package/docs/api/type-aliases/FlowOnPendingListener.md +21 -0
- package/docs/api/type-aliases/FlowReadonly.md +22 -0
- package/docs/api/type-aliases/InitFunction.md +21 -0
- package/docs/api/type-aliases/InitFunctionAsync.md +21 -0
- package/docs/api/type-aliases/NotPromise.md +6 -3
- package/docs/api/type-aliases/UpdateFunction.md +27 -0
- package/docs/api/type-aliases/UpdateFunctionAsync.md +27 -0
- package/docs/api/typedoc-sidebar.json +63 -79
- package/docs/examples/examples.md +0 -2
- package/docs/guide/advanced/architecture.md +1234 -0
- package/docs/guide/advanced/migration-v2.md +204 -0
- package/docs/guide/advanced/solidjs.md +2 -88
- package/docs/guide/introduction/concepts.md +4 -3
- package/docs/guide/introduction/conventions.md +2 -33
- package/docs/guide/introduction/getting-started.md +28 -23
- package/docs/guide/introduction/lifecycle.md +16 -19
- package/docs/guide/primitives/array.md +102 -216
- package/docs/guide/primitives/constant.md +39 -212
- package/docs/guide/primitives/derivations.md +55 -122
- package/docs/guide/primitives/effects.md +155 -241
- package/docs/guide/primitives/map.md +64 -186
- package/docs/guide/primitives/overview.md +45 -128
- package/docs/guide/primitives/signal.md +51 -88
- package/docs/guide/primitives/state.md +34 -130
- package/package.json +56 -60
- package/src/api/base/flowDisposable.ts +44 -0
- package/src/api/base/flowObservable.ts +28 -0
- package/src/api/base/flowSubscribable.ts +87 -0
- package/src/api/base/flowTracker.ts +7 -0
- package/src/api/base/index.ts +4 -0
- package/src/{flow → api}/index.ts +0 -1
- package/src/api/nodes/async/flowConstantAsync.ts +36 -0
- package/src/api/nodes/async/flowDerivationAsync.ts +42 -0
- package/src/api/nodes/async/flowStateAsync.ts +47 -0
- package/src/api/nodes/async/flowWritableDerivationAsync.ts +33 -0
- package/src/{flow → api}/nodes/async/index.ts +1 -2
- package/src/api/nodes/collections/flowArray.ts +155 -0
- package/src/api/nodes/collections/flowMap.ts +115 -0
- package/src/api/nodes/flowEffect.ts +42 -0
- package/src/api/nodes/flowSignal.ts +28 -0
- package/src/api/nodes/flowValue.ts +37 -0
- package/src/api/nodes/index.ts +7 -0
- package/src/api/nodes/sync/flowConstant.ts +33 -0
- package/src/api/nodes/sync/flowDerivation.ts +41 -0
- package/src/api/nodes/sync/flowState.ts +45 -0
- package/src/api/nodes/sync/flowWritableDerivation.ts +31 -0
- package/src/{flow → api}/nodes/sync/index.ts +1 -2
- package/src/api/nodes/utils.ts +24 -0
- package/src/base/disposable.ts +18 -0
- package/src/base/executionStack.ts +42 -0
- package/src/base/index.ts +5 -0
- package/src/base/node.ts +98 -0
- package/src/base/observable.ts +92 -0
- package/src/base/observer.ts +51 -0
- package/src/converters/index.ts +1 -0
- package/src/converters/solid.ts +109 -0
- package/src/index.ts +2 -64
- package/src/nodes/arrayNode.ts +180 -0
- package/src/nodes/effectNode.ts +58 -0
- package/src/nodes/index.ts +7 -0
- package/src/nodes/mapNode.ts +125 -0
- package/src/nodes/signalNode.ts +19 -0
- package/src/nodes/valueAsyncNode.ts +85 -0
- package/src/nodes/valueNode.ts +148 -0
- package/src/nodes/valueSyncNode.ts +125 -0
- package/src/schedulers/asyncResolver.ts +78 -0
- package/src/schedulers/asyncScheduler.ts +66 -0
- package/src/schedulers/index.ts +4 -0
- package/src/schedulers/pendingError.ts +13 -0
- package/src/schedulers/scheduler.ts +9 -0
- package/src/schedulers/syncResolver.ts +69 -0
- package/src/schedulers/syncScheduler.ts +55 -0
- package/test/base/pendingError.test.ts +67 -0
- package/test/converters/solid.derivation.browser.test.tsx +69 -0
- package/test/converters/solid.node.test.ts +654 -0
- package/test/converters/solid.state.browser.test.tsx +1592 -0
- package/test/reactivity/flowSignal.test.ts +226 -0
- package/test/reactivity/nodes/async/asyncScheduler/asyncResolver.test.ts +593 -0
- package/test/reactivity/nodes/async/asyncScheduler/asyncScheduler.test.ts +317 -0
- package/test/reactivity/nodes/async/flowConstantAsync.test.ts +652 -0
- package/test/reactivity/nodes/async/flowDerivation.test.ts +898 -0
- package/test/reactivity/nodes/async/flowDerivationAsync.test.ts +1716 -0
- package/test/reactivity/nodes/async/flowStateAsync.test.ts +708 -0
- package/test/reactivity/nodes/async/flowWritableDerivationAsync.test.ts +614 -0
- package/test/reactivity/nodes/collections/flowArray.asyncStates.test.ts +1289 -0
- package/test/reactivity/nodes/collections/flowArray.scalars.test.ts +961 -0
- package/test/reactivity/nodes/collections/flowArray.states.test.ts +1035 -0
- package/test/reactivity/nodes/collections/flowMap.asyncStates.test.ts +960 -0
- package/test/reactivity/nodes/collections/flowMap.scalars.test.ts +775 -0
- package/test/reactivity/nodes/collections/flowMap.states.test.ts +958 -0
- package/test/reactivity/nodes/sync/flowConstant.test.ts +377 -0
- package/test/reactivity/nodes/sync/flowDerivation.test.ts +896 -0
- package/test/reactivity/nodes/sync/flowState.test.ts +341 -0
- package/test/reactivity/nodes/sync/flowWritableDerivation.test.ts +603 -0
- package/test/vitest.d.ts +10 -0
- package/tsconfig.json +31 -20
- package/typedoc.json +35 -35
- package/vite.config.ts +25 -23
- package/vitest.browser.config.ts +21 -0
- package/vitest.config.ts +12 -12
- package/.cursor/plans/unifier-flowresource-avec-flowderivation-c9506e24.plan.md +0 -372
- package/.cursor/plans/update-js-e795d61b.plan.md +0 -567
- package/dist/types/flow/base/flowDisposable.d.ts +0 -67
- package/dist/types/flow/base/flowDisposable.d.ts.map +0 -1
- package/dist/types/flow/base/flowEffect.d.ts +0 -127
- package/dist/types/flow/base/flowEffect.d.ts.map +0 -1
- package/dist/types/flow/base/flowGraph.d.ts +0 -97
- package/dist/types/flow/base/flowGraph.d.ts.map +0 -1
- package/dist/types/flow/base/flowSignal.d.ts +0 -134
- package/dist/types/flow/base/flowSignal.d.ts.map +0 -1
- package/dist/types/flow/base/flowTracker.d.ts +0 -15
- package/dist/types/flow/base/flowTracker.d.ts.map +0 -1
- package/dist/types/flow/base/index.d.ts +0 -7
- package/dist/types/flow/base/index.d.ts.map +0 -1
- package/dist/types/flow/base/utils.d.ts +0 -20
- package/dist/types/flow/base/utils.d.ts.map +0 -1
- package/dist/types/flow/collections/flowArray.d.ts +0 -148
- package/dist/types/flow/collections/flowArray.d.ts.map +0 -1
- package/dist/types/flow/collections/flowMap.d.ts +0 -224
- package/dist/types/flow/collections/flowMap.d.ts.map +0 -1
- package/dist/types/flow/collections/index.d.ts.map +0 -1
- package/dist/types/flow/index.d.ts +0 -4
- package/dist/types/flow/index.d.ts.map +0 -1
- package/dist/types/flow/nodes/async/flowConstantAsync.d.ts +0 -137
- package/dist/types/flow/nodes/async/flowConstantAsync.d.ts.map +0 -1
- package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts +0 -137
- package/dist/types/flow/nodes/async/flowDerivationAsync.d.ts.map +0 -1
- package/dist/types/flow/nodes/async/flowNodeAsync.d.ts +0 -343
- package/dist/types/flow/nodes/async/flowNodeAsync.d.ts.map +0 -1
- package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts +0 -81
- package/dist/types/flow/nodes/async/flowReadonlyAsync.d.ts.map +0 -1
- package/dist/types/flow/nodes/async/flowStateAsync.d.ts +0 -111
- package/dist/types/flow/nodes/async/flowStateAsync.d.ts.map +0 -1
- package/dist/types/flow/nodes/async/index.d.ts.map +0 -1
- package/dist/types/flow/nodes/index.d.ts +0 -3
- package/dist/types/flow/nodes/index.d.ts.map +0 -1
- package/dist/types/flow/nodes/sync/flowConstant.d.ts +0 -108
- package/dist/types/flow/nodes/sync/flowConstant.d.ts.map +0 -1
- package/dist/types/flow/nodes/sync/flowDerivation.d.ts +0 -100
- package/dist/types/flow/nodes/sync/flowDerivation.d.ts.map +0 -1
- package/dist/types/flow/nodes/sync/flowNode.d.ts +0 -314
- package/dist/types/flow/nodes/sync/flowNode.d.ts.map +0 -1
- package/dist/types/flow/nodes/sync/flowReadonly.d.ts +0 -57
- package/dist/types/flow/nodes/sync/flowReadonly.d.ts.map +0 -1
- package/dist/types/flow/nodes/sync/flowState.d.ts +0 -96
- package/dist/types/flow/nodes/sync/flowState.d.ts.map +0 -1
- package/dist/types/flow/nodes/sync/index.d.ts.map +0 -1
- package/dist/types/solid/converters.d.ts +0 -57
- package/dist/types/solid/converters.d.ts.map +0 -1
- package/dist/types/solid/index.d.ts +0 -3
- package/dist/types/solid/index.d.ts.map +0 -1
- package/dist/types/solid/primitives.d.ts +0 -181
- package/dist/types/solid/primitives.d.ts.map +0 -1
- package/docs/api/classes/FlowArray.md +0 -489
- package/docs/api/classes/FlowConstant.md +0 -350
- package/docs/api/classes/FlowDerivation.md +0 -334
- package/docs/api/classes/FlowEffect.md +0 -100
- package/docs/api/classes/FlowMap.md +0 -512
- package/docs/api/classes/FlowObservable.md +0 -306
- package/docs/api/classes/FlowResource.md +0 -380
- package/docs/api/classes/FlowResourceAsync.md +0 -362
- package/docs/api/classes/FlowSignal.md +0 -160
- package/docs/api/classes/FlowState.md +0 -368
- package/docs/api/classes/FlowStream.md +0 -367
- package/docs/api/classes/FlowStreamAsync.md +0 -364
- package/docs/api/classes/SolidDerivation.md +0 -75
- package/docs/api/classes/SolidResource.md +0 -91
- package/docs/api/classes/SolidState.md +0 -71
- package/docs/api/classes/TrackingContext.md +0 -33
- package/docs/api/functions/effect.md +0 -49
- package/docs/api/functions/resource.md +0 -52
- package/docs/api/functions/resourceAsync.md +0 -50
- package/docs/api/functions/stream.md +0 -53
- package/docs/api/functions/streamAsync.md +0 -50
- package/docs/api/interfaces/SolidObservable.md +0 -19
- package/docs/api/type-aliases/FlowStreamDisposer.md +0 -15
- package/docs/api/type-aliases/FlowStreamSetter.md +0 -27
- package/docs/api/type-aliases/FlowStreamUpdater.md +0 -32
- package/docs/api/type-aliases/SolidGetter.md +0 -17
- package/docs/guide/primitives/resources.md +0 -858
- package/docs/guide/primitives/streams.md +0 -931
- package/src/flow/base/flowDisposable.ts +0 -71
- package/src/flow/base/flowEffect.ts +0 -171
- package/src/flow/base/flowGraph.ts +0 -288
- package/src/flow/base/flowSignal.ts +0 -207
- package/src/flow/base/flowTracker.ts +0 -17
- package/src/flow/base/index.ts +0 -6
- package/src/flow/base/utils.ts +0 -19
- package/src/flow/collections/flowArray.ts +0 -409
- package/src/flow/collections/flowMap.ts +0 -398
- package/src/flow/nodes/async/flowConstantAsync.ts +0 -142
- package/src/flow/nodes/async/flowDerivationAsync.ts +0 -143
- package/src/flow/nodes/async/flowNodeAsync.ts +0 -474
- package/src/flow/nodes/async/flowReadonlyAsync.ts +0 -81
- package/src/flow/nodes/async/flowStateAsync.ts +0 -116
- package/src/flow/nodes/await/advanced/index.ts +0 -5
- package/src/flow/nodes/await/advanced/resource.ts +0 -134
- package/src/flow/nodes/await/advanced/resourceAsync.ts +0 -109
- package/src/flow/nodes/await/advanced/stream.ts +0 -188
- package/src/flow/nodes/await/advanced/streamAsync.ts +0 -176
- package/src/flow/nodes/await/flowConstantAwait.ts +0 -154
- package/src/flow/nodes/await/flowDerivationAwait.ts +0 -154
- package/src/flow/nodes/await/flowNodeAwait.ts +0 -508
- package/src/flow/nodes/await/flowReadonlyAwait.ts +0 -89
- package/src/flow/nodes/await/flowStateAwait.ts +0 -130
- package/src/flow/nodes/await/index.ts +0 -5
- package/src/flow/nodes/index.ts +0 -3
- package/src/flow/nodes/sync/flowConstant.ts +0 -111
- package/src/flow/nodes/sync/flowDerivation.ts +0 -105
- package/src/flow/nodes/sync/flowNode.ts +0 -439
- package/src/flow/nodes/sync/flowReadonly.ts +0 -57
- package/src/flow/nodes/sync/flowState.ts +0 -101
- package/src/solid/converters.ts +0 -148
- package/src/solid/index.ts +0 -2
- package/src/solid/primitives.ts +0 -215
- package/test/base/flowEffect.test.ts +0 -108
- package/test/base/flowGraph.test.ts +0 -485
- package/test/base/flowSignal.test.ts +0 -372
- package/test/collections/flowArray.asyncStates.test.ts +0 -1553
- package/test/collections/flowArray.scalars.test.ts +0 -1129
- package/test/collections/flowArray.states.test.ts +0 -1365
- package/test/collections/flowMap.asyncStates.test.ts +0 -1105
- package/test/collections/flowMap.scalars.test.ts +0 -877
- package/test/collections/flowMap.states.test.ts +0 -1097
- package/test/nodes/async/flowConstantAsync.test.ts +0 -860
- package/test/nodes/async/flowDerivationAsync.test.ts +0 -1517
- package/test/nodes/async/flowStateAsync.test.ts +0 -1387
- package/test/nodes/await/advanced/resource.test.ts +0 -129
- package/test/nodes/await/advanced/resourceAsync.test.ts +0 -108
- package/test/nodes/await/advanced/stream.test.ts +0 -198
- package/test/nodes/await/advanced/streamAsync.test.ts +0 -196
- package/test/nodes/await/flowConstantAwait.test.ts +0 -643
- package/test/nodes/await/flowDerivationAwait.test.ts +0 -1583
- package/test/nodes/await/flowStateAwait.test.ts +0 -999
- package/test/nodes/mixed/derivation.test.ts +0 -1527
- package/test/nodes/sync/flowConstant.test.ts +0 -620
- package/test/nodes/sync/flowDerivation.test.ts +0 -1373
- package/test/nodes/sync/flowState.test.ts +0 -945
- package/test/solid/converters.test.ts +0 -721
- package/test/solid/primitives.test.ts +0 -1031
- /package/dist/types/{flow → api/nodes}/collections/index.d.ts +0 -0
- /package/docs/guide/advanced/{upgrading.md → migration-v1.md} +0 -0
- /package/src/{flow → api/nodes}/collections/index.ts +0 -0
package/dist/picoflow.js
CHANGED
|
@@ -1,1650 +1,984 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createResource, onMount, createEffect, resetErrorBoundaries, onCleanup } from 'solid-js';
|
|
2
2
|
|
|
3
3
|
function isDisposable(obj) {
|
|
4
4
|
return obj !== null && obj !== void 0 && typeof obj.dispose === "function";
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
class
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*/
|
|
28
|
-
static clear() {
|
|
29
|
-
FlowGraph._effectsQueue = [];
|
|
30
|
-
FlowGraph._actionQueue = [];
|
|
31
|
-
FlowGraph._processingActionQueue = false;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Queues a trigger notification for processing.
|
|
35
|
-
*
|
|
36
|
-
* @param notify - Function to call when the trigger is processed.
|
|
37
|
-
* @returns A promise that resolves after the trigger is processed.
|
|
38
|
-
*
|
|
39
|
-
* @remarks
|
|
40
|
-
* This method is used internally by reactive primitives to coordinate
|
|
41
|
-
* signal triggers. The promise resolves after all associated effects
|
|
42
|
-
* have been executed.
|
|
43
|
-
*
|
|
44
|
-
* @public
|
|
45
|
-
*/
|
|
46
|
-
static requestTrigger(notify) {
|
|
47
|
-
const promiseWithResolvers = Promise.withResolvers();
|
|
48
|
-
FlowGraph._actionQueue.push({
|
|
49
|
-
...promiseWithResolvers,
|
|
50
|
-
type: "trigger",
|
|
51
|
-
notify
|
|
52
|
-
});
|
|
53
|
-
FlowGraph._processActionQueue();
|
|
54
|
-
return promiseWithResolvers.promise;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Queues a read operation for processing.
|
|
58
|
-
*
|
|
59
|
-
* @param read - Function that performs the read operation.
|
|
60
|
-
* @returns A promise that resolves with the read value.
|
|
61
|
-
*
|
|
62
|
-
* @remarks
|
|
63
|
-
* This method is used internally by reactive primitives to coordinate
|
|
64
|
-
* read operations. The read function is executed and its result (or promise)
|
|
65
|
-
* is returned.
|
|
66
|
-
*
|
|
67
|
-
* @public
|
|
68
|
-
*/
|
|
69
|
-
static requestRead(read) {
|
|
70
|
-
const promiseWithResolvers = Promise.withResolvers();
|
|
71
|
-
FlowGraph._actionQueue.push({
|
|
72
|
-
...promiseWithResolvers,
|
|
73
|
-
type: "read",
|
|
74
|
-
read
|
|
75
|
-
});
|
|
76
|
-
FlowGraph._processActionQueue();
|
|
77
|
-
return promiseWithResolvers.promise;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Queues a write operation with update logic for processing.
|
|
81
|
-
*
|
|
82
|
-
* @param notify - Function to call if the update succeeds.
|
|
83
|
-
* @param update - Function that performs the update and returns whether
|
|
84
|
-
* it changed the value.
|
|
85
|
-
* @returns A promise that resolves after the write is processed.
|
|
86
|
-
*
|
|
87
|
-
* @remarks
|
|
88
|
-
* This method is used internally by reactive primitives to coordinate
|
|
89
|
-
* write operations. The update function is executed, and if it returns true
|
|
90
|
-
* (or a promise resolving to true), the notify function is called to
|
|
91
|
-
* trigger dependent effects.
|
|
92
|
-
*
|
|
93
|
-
* @public
|
|
94
|
-
*/
|
|
95
|
-
static requestWrite(notify, update) {
|
|
96
|
-
const promiseWithResolvers = Promise.withResolvers();
|
|
97
|
-
FlowGraph._actionQueue.push({
|
|
98
|
-
...promiseWithResolvers,
|
|
99
|
-
type: "write",
|
|
100
|
-
notify,
|
|
101
|
-
update
|
|
7
|
+
class PendingError extends Error {
|
|
8
|
+
pendingPromise;
|
|
9
|
+
constructor(promise) {
|
|
10
|
+
super("[PicoFlow] PendingResolver pending");
|
|
11
|
+
this.name = "PendingError";
|
|
12
|
+
this.pendingPromise = promise;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class AsyncResolver {
|
|
17
|
+
_compute;
|
|
18
|
+
_computed = Promise.withResolvers();
|
|
19
|
+
_iteration = 0;
|
|
20
|
+
_aborted = false;
|
|
21
|
+
_finished = false;
|
|
22
|
+
constructor(compute) {
|
|
23
|
+
this._compute = compute;
|
|
24
|
+
this._computed.promise.catch(() => {
|
|
25
|
+
}).finally(() => {
|
|
26
|
+
this._finished = true;
|
|
102
27
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
FlowGraph._processingActionQueue = true;
|
|
125
|
-
while (FlowGraph._actionQueue.length > 0) {
|
|
126
|
-
const actionRequest = FlowGraph._actionQueue.shift();
|
|
127
|
-
if (!actionRequest) break;
|
|
128
|
-
if (actionRequest.type === "read") {
|
|
129
|
-
try {
|
|
130
|
-
const read = actionRequest.read();
|
|
131
|
-
let value;
|
|
132
|
-
if (read instanceof Promise) {
|
|
133
|
-
value = await read;
|
|
134
|
-
} else {
|
|
135
|
-
value = read;
|
|
136
|
-
}
|
|
137
|
-
actionRequest.resolve(value);
|
|
138
|
-
} catch (error) {
|
|
139
|
-
const safeError = error instanceof Error ? error : new Error(String(error));
|
|
140
|
-
actionRequest.reject(safeError);
|
|
28
|
+
}
|
|
29
|
+
get computed() {
|
|
30
|
+
return this._computed.promise;
|
|
31
|
+
}
|
|
32
|
+
get aborted() {
|
|
33
|
+
return this._aborted;
|
|
34
|
+
}
|
|
35
|
+
get finished() {
|
|
36
|
+
return this._finished;
|
|
37
|
+
}
|
|
38
|
+
compute() {
|
|
39
|
+
if (this._finished) throw new Error("[Picoflow] PendingResolver: Can't restart a settled resolver");
|
|
40
|
+
if (this._aborted) throw new Error("[Picoflow] PendingResolver: Can't restart an aborted resolver");
|
|
41
|
+
this._iteration++;
|
|
42
|
+
const currentIteration = this._iteration;
|
|
43
|
+
this._compute().then((value) => {
|
|
44
|
+
if (this._iteration === currentIteration && !this._aborted) this._computed.resolve(value);
|
|
45
|
+
}).catch((error) => {
|
|
46
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
47
|
+
if (!(error instanceof PendingError)) {
|
|
48
|
+
this._computed.reject(error);
|
|
141
49
|
}
|
|
142
|
-
continue;
|
|
143
50
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
actionRequest.notify();
|
|
158
|
-
} catch (error) {
|
|
159
|
-
const safeError = error instanceof Error ? error : new Error(String(error));
|
|
160
|
-
actionRequest.reject(safeError);
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
overwrite(promise) {
|
|
54
|
+
if (this._finished) throw new Error("[Picoflow] PendingResolver: Can't overwrite a settled resolver");
|
|
55
|
+
if (this._aborted) throw new Error("[Picoflow] PendingResolver: Can't overwrite an aborted resolver");
|
|
56
|
+
this._iteration++;
|
|
57
|
+
const currentIteration = this._iteration;
|
|
58
|
+
promise.then((value) => {
|
|
59
|
+
if (this._iteration === currentIteration && !this._aborted) this._computed.resolve(value);
|
|
60
|
+
}).catch((error) => {
|
|
61
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
62
|
+
this._computed.reject(error);
|
|
163
63
|
}
|
|
164
|
-
|
|
165
|
-
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
abort() {
|
|
67
|
+
this._iteration++;
|
|
68
|
+
this._aborted = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
class AsyncScheduler {
|
|
73
|
+
_compute;
|
|
74
|
+
_resolver;
|
|
75
|
+
_onResolve;
|
|
76
|
+
_onReject;
|
|
77
|
+
_settled = Promise.withResolvers();
|
|
78
|
+
_disposed = false;
|
|
79
|
+
constructor(compute, onResolve, onReject) {
|
|
80
|
+
this._compute = compute;
|
|
81
|
+
this._onResolve = onResolve;
|
|
82
|
+
this._onReject = onReject;
|
|
83
|
+
this._resolver = new AsyncResolver(compute);
|
|
84
|
+
this._resolver.computed.then(this._onResolve).catch(this._onReject).finally(() => this._settled.resolve());
|
|
85
|
+
}
|
|
86
|
+
get settled() {
|
|
87
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
88
|
+
return this._settled.promise;
|
|
89
|
+
}
|
|
90
|
+
get disposed() {
|
|
91
|
+
return this._disposed;
|
|
92
|
+
}
|
|
93
|
+
overwrite(promise) {
|
|
94
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
95
|
+
if (this._resolver.finished) {
|
|
96
|
+
this._resolver = new AsyncResolver(this._compute);
|
|
97
|
+
this._settled = Promise.withResolvers();
|
|
98
|
+
this._resolver.computed.then(this._onResolve).catch(this._onReject).finally(() => this._settled.resolve());
|
|
99
|
+
}
|
|
100
|
+
this._resolver.overwrite(promise);
|
|
101
|
+
}
|
|
102
|
+
compute() {
|
|
103
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
104
|
+
if (this._resolver.finished) {
|
|
105
|
+
this._resolver = new AsyncResolver(this._compute);
|
|
106
|
+
this._settled = Promise.withResolvers();
|
|
107
|
+
this._resolver.computed.then(this._onResolve).catch(this._onReject).finally(() => this._settled.resolve());
|
|
108
|
+
}
|
|
109
|
+
this._resolver.compute();
|
|
110
|
+
}
|
|
111
|
+
dispose() {
|
|
112
|
+
this._resolver.abort();
|
|
113
|
+
this._disposed = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
class SyncResolver {
|
|
118
|
+
_compute;
|
|
119
|
+
_iteration = 0;
|
|
120
|
+
_aborted = false;
|
|
121
|
+
_finished = false;
|
|
122
|
+
_onValue;
|
|
123
|
+
_onError;
|
|
124
|
+
constructor(compute, onValue, onError) {
|
|
125
|
+
this._compute = compute;
|
|
126
|
+
this._onValue = onValue;
|
|
127
|
+
this._onError = onError;
|
|
128
|
+
}
|
|
129
|
+
get aborted() {
|
|
130
|
+
return this._aborted;
|
|
131
|
+
}
|
|
132
|
+
get finished() {
|
|
133
|
+
return this._finished;
|
|
134
|
+
}
|
|
135
|
+
compute() {
|
|
136
|
+
if (this._finished) throw new Error("[Picoflow] PendingResolver: Can't restart a settled resolver");
|
|
137
|
+
if (this._aborted) throw new Error("[Picoflow] PendingResolver: Can't restart an aborted resolver");
|
|
138
|
+
this._iteration++;
|
|
139
|
+
const currentIteration = this._iteration;
|
|
140
|
+
try {
|
|
141
|
+
const value = this._compute();
|
|
142
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
143
|
+
this._finished = true;
|
|
144
|
+
this._onValue(value);
|
|
166
145
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
effectError = error instanceof Error ? error : new Error(String(error));
|
|
173
|
-
break;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
148
|
+
if (!(error instanceof PendingError)) {
|
|
149
|
+
this._finished = true;
|
|
150
|
+
this._onError(error);
|
|
174
151
|
}
|
|
175
152
|
}
|
|
176
|
-
FlowGraph._effectsQueue = [];
|
|
177
|
-
if (effectError) {
|
|
178
|
-
actionRequest.reject(effectError);
|
|
179
|
-
} else {
|
|
180
|
-
actionRequest.resolve();
|
|
181
|
-
}
|
|
182
153
|
}
|
|
183
|
-
|
|
184
|
-
|
|
154
|
+
}
|
|
155
|
+
overwrite(value) {
|
|
156
|
+
if (this._finished) throw new Error("[Picoflow] PendingResolver: Can't overwrite a settled resolver");
|
|
157
|
+
if (this._aborted) throw new Error("[Picoflow] PendingResolver: Can't overwrite an aborted resolver");
|
|
158
|
+
this._iteration++;
|
|
159
|
+
const currentIteration = this._iteration;
|
|
160
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
161
|
+
this._finished = true;
|
|
162
|
+
this._onValue(value);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
abort() {
|
|
166
|
+
this._iteration++;
|
|
167
|
+
this._aborted = true;
|
|
168
|
+
}
|
|
185
169
|
}
|
|
186
170
|
|
|
187
|
-
class
|
|
171
|
+
class SyncScheduler {
|
|
172
|
+
_compute;
|
|
173
|
+
_resolver;
|
|
174
|
+
_onResolve;
|
|
175
|
+
_onReject;
|
|
176
|
+
_settled = Promise.withResolvers();
|
|
188
177
|
_disposed = false;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
* - Calling `dispose()` again throws an error.
|
|
221
|
-
*
|
|
222
|
-
* @example
|
|
223
|
-
* ```typescript
|
|
224
|
-
* const fx = effect((t) => $signal.watch(t));
|
|
225
|
-
* // ... later
|
|
226
|
-
* fx.dispose();
|
|
227
|
-
* ```
|
|
228
|
-
*
|
|
229
|
-
* @public
|
|
230
|
-
*/
|
|
178
|
+
constructor(compute, onResolve, onReject) {
|
|
179
|
+
this._compute = compute;
|
|
180
|
+
this._onResolve = (value) => {
|
|
181
|
+
onResolve(value);
|
|
182
|
+
this._settled.resolve();
|
|
183
|
+
};
|
|
184
|
+
this._onReject = (error) => {
|
|
185
|
+
onReject(error);
|
|
186
|
+
this._settled.resolve();
|
|
187
|
+
};
|
|
188
|
+
this._resolver = new SyncResolver(compute, this._onResolve, this._onReject);
|
|
189
|
+
}
|
|
190
|
+
get settled() {
|
|
191
|
+
return this._settled.promise;
|
|
192
|
+
}
|
|
193
|
+
overwrite(value) {
|
|
194
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
195
|
+
if (this._resolver.finished) {
|
|
196
|
+
this._settled = Promise.withResolvers();
|
|
197
|
+
this._resolver = new SyncResolver(this._compute, this._onResolve, this._onReject);
|
|
198
|
+
}
|
|
199
|
+
this._resolver.overwrite(value);
|
|
200
|
+
}
|
|
201
|
+
compute() {
|
|
202
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
203
|
+
if (this._resolver.finished) {
|
|
204
|
+
this._settled = Promise.withResolvers();
|
|
205
|
+
this._resolver = new SyncResolver(this._compute, this._onResolve, this._onReject);
|
|
206
|
+
}
|
|
207
|
+
this._resolver.compute();
|
|
208
|
+
}
|
|
231
209
|
dispose() {
|
|
232
|
-
|
|
233
|
-
Array.from(this._dependencies).forEach((dependency) => {
|
|
234
|
-
this._unregisterDependency(dependency);
|
|
235
|
-
});
|
|
210
|
+
this._resolver.abort();
|
|
236
211
|
this._disposed = true;
|
|
237
212
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
*
|
|
243
|
-
* @public
|
|
244
|
-
*/
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
class Disposable {
|
|
216
|
+
_disposed = false;
|
|
245
217
|
get disposed() {
|
|
246
218
|
return this._disposed;
|
|
247
219
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this.
|
|
251
|
-
return this.settled;
|
|
220
|
+
dispose() {
|
|
221
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
222
|
+
this._disposed = true;
|
|
252
223
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
class ExecutionStack {
|
|
227
|
+
static _pendingQueue = [];
|
|
228
|
+
static _effectQueue = [];
|
|
229
|
+
static _executionScheduled;
|
|
230
|
+
static pushPending(node) {
|
|
231
|
+
ExecutionStack._scheduleExecution();
|
|
232
|
+
ExecutionStack._pendingQueue.push(node);
|
|
233
|
+
}
|
|
234
|
+
static pushEffect(effect) {
|
|
235
|
+
ExecutionStack._scheduleExecution();
|
|
236
|
+
ExecutionStack._effectQueue.push(effect);
|
|
237
|
+
}
|
|
238
|
+
static _scheduleExecution() {
|
|
239
|
+
if (ExecutionStack._executionScheduled) return;
|
|
240
|
+
ExecutionStack._executionScheduled = new Promise((resolve) => {
|
|
241
|
+
setTimeout(() => {
|
|
242
|
+
ExecutionStack._executionScheduled = void 0;
|
|
243
|
+
ExecutionStack._execute();
|
|
244
|
+
resolve();
|
|
245
|
+
}, 0);
|
|
246
|
+
});
|
|
257
247
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (result instanceof Promise) {
|
|
268
|
-
await result;
|
|
269
|
-
}
|
|
248
|
+
static _execute() {
|
|
249
|
+
ExecutionStack._pendingQueue.forEach((node) => {
|
|
250
|
+
node.execute();
|
|
251
|
+
});
|
|
252
|
+
ExecutionStack._pendingQueue.length = 0;
|
|
253
|
+
ExecutionStack._effectQueue.forEach((effect) => {
|
|
254
|
+
effect.execute();
|
|
255
|
+
});
|
|
256
|
+
ExecutionStack._effectQueue.length = 0;
|
|
270
257
|
}
|
|
271
258
|
}
|
|
272
|
-
function effect(fn) {
|
|
273
|
-
return new FlowEffect(fn);
|
|
274
|
-
}
|
|
275
259
|
|
|
276
|
-
class
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
*
|
|
290
|
-
* effect((t) => {
|
|
291
|
-
* $tick.watch(t);
|
|
292
|
-
* console.log("tick");
|
|
293
|
-
* });
|
|
294
|
-
*
|
|
295
|
-
* $tick.trigger(); // logs "tick"
|
|
296
|
-
* ```
|
|
297
|
-
*
|
|
298
|
-
* @public
|
|
299
|
-
*/
|
|
300
|
-
async trigger() {
|
|
260
|
+
class Node extends Disposable {
|
|
261
|
+
_dependencies = /* @__PURE__ */ new Set();
|
|
262
|
+
_dependents = /* @__PURE__ */ new Set();
|
|
263
|
+
_status = "resolved";
|
|
264
|
+
get status() {
|
|
265
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
266
|
+
return this._status;
|
|
267
|
+
}
|
|
268
|
+
set status(status) {
|
|
269
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
270
|
+
this._status = status;
|
|
271
|
+
}
|
|
272
|
+
registerDependency(dependency) {
|
|
301
273
|
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
*
|
|
307
|
-
* @param context - The tracking context (`t`) provided to effects/derivations.
|
|
308
|
-
*
|
|
309
|
-
* @remarks
|
|
310
|
-
* Signals have no value to read; calling `watch(t)` simply means “re-run me
|
|
311
|
-
* when this signal is triggered.” Call this inside an effect/derivation
|
|
312
|
-
* callback where a tracking context is available.
|
|
313
|
-
*
|
|
314
|
-
* @throws Error if the signal has been disposed.
|
|
315
|
-
*
|
|
316
|
-
* @example
|
|
317
|
-
* ```typescript
|
|
318
|
-
* const $refresh = signal();
|
|
319
|
-
*
|
|
320
|
-
* effect((t) => {
|
|
321
|
-
* $refresh.watch(t);
|
|
322
|
-
* console.log("refresh triggered");
|
|
323
|
-
* });
|
|
324
|
-
*
|
|
325
|
-
* $refresh.trigger(); // logs "refresh triggered"
|
|
326
|
-
* ```
|
|
327
|
-
*
|
|
328
|
-
* @public
|
|
329
|
-
*/
|
|
330
|
-
watch(caller) {
|
|
274
|
+
this._dependencies.add(dependency);
|
|
275
|
+
dependency.registerDependent(this);
|
|
276
|
+
}
|
|
277
|
+
unregisterDependency(dependency) {
|
|
331
278
|
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
279
|
+
this._dependencies.delete(dependency);
|
|
280
|
+
dependency.unregisterDependent(this);
|
|
281
|
+
}
|
|
282
|
+
clearDependencies() {
|
|
283
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
284
|
+
this._dependencies.forEach((dependency) => {
|
|
285
|
+
dependency.unregisterDependent(this);
|
|
286
|
+
});
|
|
287
|
+
this._dependencies.clear();
|
|
288
|
+
}
|
|
289
|
+
registerDependent(dependent) {
|
|
290
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
291
|
+
this._dependents.add(dependent);
|
|
292
|
+
}
|
|
293
|
+
unregisterDependent(dependent) {
|
|
294
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
295
|
+
this._dependents.delete(dependent);
|
|
296
|
+
}
|
|
297
|
+
notifyDependents() {
|
|
298
|
+
if (this.disposed) return;
|
|
299
|
+
this._dependents.forEach((dependent) => {
|
|
300
|
+
dependent.notify();
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
watch(tracker) {
|
|
304
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
305
|
+
tracker.registerDependency(this);
|
|
306
|
+
}
|
|
307
|
+
trigger() {
|
|
308
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
309
|
+
this.notifyDependents();
|
|
310
|
+
}
|
|
311
|
+
notify() {
|
|
312
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
313
|
+
if (this.status === "dirty") return;
|
|
314
|
+
this.status = "dirty";
|
|
315
|
+
this.notifyDependents();
|
|
316
|
+
}
|
|
317
|
+
dispose() {
|
|
355
318
|
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
Array.from(this._listeners).forEach((listener) => {
|
|
361
|
-
listener._unregisterDependency(this);
|
|
362
|
-
});
|
|
363
|
-
} else {
|
|
364
|
-
Array.from(this._effects).forEach((effect) => {
|
|
365
|
-
effect.dispose();
|
|
366
|
-
});
|
|
367
|
-
Array.from(this._listeners).forEach((listener) => {
|
|
368
|
-
listener.dispose();
|
|
369
|
-
});
|
|
370
|
-
}
|
|
319
|
+
Array.from(this._dependents).forEach((dependant) => {
|
|
320
|
+
dependant.unregisterDependency(this);
|
|
321
|
+
this.unregisterDependent(dependant);
|
|
322
|
+
});
|
|
371
323
|
Array.from(this._dependencies).forEach((dependency) => {
|
|
372
|
-
this.
|
|
324
|
+
this.unregisterDependency(dependency);
|
|
373
325
|
});
|
|
374
326
|
this._disposed = true;
|
|
375
327
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
class Observable extends Disposable {
|
|
331
|
+
_dependents = /* @__PURE__ */ new Set();
|
|
332
|
+
_status = "resolved";
|
|
333
|
+
get status() {
|
|
334
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
335
|
+
return this._status;
|
|
336
|
+
}
|
|
337
|
+
set status(status) {
|
|
338
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
339
|
+
this._status = status;
|
|
340
|
+
}
|
|
341
|
+
registerDependent(dependent) {
|
|
342
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
343
|
+
this._dependents.add(dependent);
|
|
344
|
+
}
|
|
345
|
+
unregisterDependent(dependent) {
|
|
346
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
347
|
+
this._dependents.delete(dependent);
|
|
348
|
+
}
|
|
349
|
+
notifyDependents() {
|
|
350
|
+
if (this.disposed) return;
|
|
351
|
+
this._dependents.forEach((dependent) => {
|
|
352
|
+
dependent.notify();
|
|
353
|
+
});
|
|
387
354
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
355
|
+
watch(tracker) {
|
|
356
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
357
|
+
tracker.registerDependency(this);
|
|
358
|
+
}
|
|
359
|
+
trigger() {
|
|
360
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
361
|
+
this.notifyDependents();
|
|
362
|
+
}
|
|
363
|
+
dispose() {
|
|
364
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
365
|
+
Array.from(this._dependents).forEach((dependant) => {
|
|
366
|
+
dependant.unregisterDependency(this);
|
|
367
|
+
this.unregisterDependent(dependant);
|
|
397
368
|
});
|
|
369
|
+
this._disposed = true;
|
|
398
370
|
}
|
|
399
|
-
|
|
400
|
-
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
class Observer extends Disposable {
|
|
374
|
+
_dependencies = /* @__PURE__ */ new Set();
|
|
375
|
+
registerDependency(dependency) {
|
|
376
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
401
377
|
this._dependencies.add(dependency);
|
|
402
|
-
dependency.
|
|
378
|
+
dependency.registerDependent(this);
|
|
403
379
|
}
|
|
404
|
-
|
|
405
|
-
|
|
380
|
+
unregisterDependency(dependency) {
|
|
381
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
406
382
|
this._dependencies.delete(dependency);
|
|
407
|
-
dependency.
|
|
383
|
+
dependency.unregisterDependent(this);
|
|
408
384
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
this.
|
|
385
|
+
clearDependencies() {
|
|
386
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
387
|
+
this._dependencies.forEach((dependency) => {
|
|
388
|
+
dependency.unregisterDependent(this);
|
|
389
|
+
});
|
|
390
|
+
this._dependencies.clear();
|
|
412
391
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
this.
|
|
392
|
+
dispose() {
|
|
393
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
394
|
+
Array.from(this._dependencies).forEach((dependency) => {
|
|
395
|
+
this.unregisterDependency(dependency);
|
|
396
|
+
});
|
|
397
|
+
this._disposed = true;
|
|
416
398
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
class EffectNode extends Observer {
|
|
402
|
+
_data;
|
|
403
|
+
_onData;
|
|
404
|
+
_onError;
|
|
405
|
+
_onPending;
|
|
406
|
+
constructor(data, onData, onError, onPending) {
|
|
407
|
+
super();
|
|
408
|
+
this._data = data;
|
|
409
|
+
this._onData = onData;
|
|
410
|
+
this._onError = onError;
|
|
411
|
+
this._onPending = onPending;
|
|
412
|
+
this.execute();
|
|
420
413
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
414
|
+
notify() {
|
|
415
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
416
|
+
ExecutionStack.pushEffect(this);
|
|
417
|
+
}
|
|
418
|
+
execute() {
|
|
419
|
+
try {
|
|
420
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
421
|
+
this.clearDependencies();
|
|
422
|
+
const data = this._data(this);
|
|
423
|
+
this._onData(data);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (error instanceof PendingError) {
|
|
426
|
+
this._onPending?.();
|
|
427
|
+
} else {
|
|
428
|
+
if (this._onError) {
|
|
429
|
+
this._onError(error instanceof Error ? error : new Error(String(error)));
|
|
430
|
+
} else {
|
|
431
|
+
throw error;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
424
435
|
}
|
|
425
436
|
}
|
|
426
|
-
function signal() {
|
|
427
|
-
return new FlowSignal();
|
|
428
|
-
}
|
|
429
437
|
|
|
430
|
-
class
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
*
|
|
459
|
-
* // Computed derivation with async function
|
|
460
|
-
* const $a = new FlowNodeAsync(Promise.resolve(10));
|
|
461
|
-
* const $b = new FlowNodeAsync(Promise.resolve(20));
|
|
462
|
-
* const $sum = new FlowNodeAsync(async (t) => {
|
|
463
|
-
* const a = await $a.get(t);
|
|
464
|
-
* const b = await $b.get(t);
|
|
465
|
-
* return a + b;
|
|
466
|
-
* });
|
|
467
|
-
*
|
|
468
|
-
* // Lazy evaluation - compute function hasn't run yet
|
|
469
|
-
* console.log(await $sum.pick()); // Now it computes: 30
|
|
470
|
-
* ```
|
|
471
|
-
*
|
|
472
|
-
* @public
|
|
473
|
-
*/
|
|
474
|
-
constructor(compute) {
|
|
475
|
-
super();
|
|
476
|
-
if (typeof compute === "function") {
|
|
477
|
-
this._compute = compute;
|
|
478
|
-
} else {
|
|
479
|
-
this._promise = compute;
|
|
480
|
-
this._dirty = false;
|
|
438
|
+
class ValueNode extends Node {
|
|
439
|
+
_value;
|
|
440
|
+
_error;
|
|
441
|
+
watch(tracker) {
|
|
442
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
443
|
+
super.watch(tracker);
|
|
444
|
+
switch (this.status) {
|
|
445
|
+
case "resolved":
|
|
446
|
+
return;
|
|
447
|
+
case "error":
|
|
448
|
+
throw this._error;
|
|
449
|
+
case "pending": {
|
|
450
|
+
throw new PendingError(this._scheduler.settled);
|
|
451
|
+
}
|
|
452
|
+
case "dirty": {
|
|
453
|
+
this.execute();
|
|
454
|
+
switch (this.status) {
|
|
455
|
+
case "pending":
|
|
456
|
+
throw new PendingError(this._scheduler.settled);
|
|
457
|
+
case "resolved":
|
|
458
|
+
return;
|
|
459
|
+
case "error":
|
|
460
|
+
throw this._error;
|
|
461
|
+
case "dirty": {
|
|
462
|
+
throw new Error("[PicoFlow] Internal error");
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
481
466
|
}
|
|
482
467
|
}
|
|
483
|
-
|
|
484
|
-
|
|
468
|
+
notify() {
|
|
469
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
470
|
+
if (this.status === "dirty") return;
|
|
471
|
+
if (this.status === "pending") {
|
|
472
|
+
ExecutionStack.pushPending(this);
|
|
473
|
+
}
|
|
474
|
+
this.status = "dirty";
|
|
475
|
+
this.notifyDependents();
|
|
485
476
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
477
|
+
dispose() {
|
|
478
|
+
super.dispose();
|
|
479
|
+
this._scheduler.dispose();
|
|
480
|
+
}
|
|
481
|
+
get(tracker) {
|
|
482
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
489
483
|
super.watch(tracker);
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
* const tracked = await $state.get(t); // Dependency registered, await the Promise
|
|
515
|
-
* const untracked = await $other.pick(); // No dependency
|
|
516
|
-
* });
|
|
517
|
-
* ```
|
|
518
|
-
*
|
|
519
|
-
* @throws Error if the node has been disposed.
|
|
520
|
-
*
|
|
521
|
-
* @public
|
|
522
|
-
*/
|
|
523
|
-
async get(tracker) {
|
|
524
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
525
|
-
await this._computeValue();
|
|
526
|
-
if (tracker) super.watch(tracker);
|
|
527
|
-
return this._promise;
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Updates the node with a new value.
|
|
531
|
-
*
|
|
532
|
-
* @param value - A new Promise or a callback function that computes a new Promise based on the current value.
|
|
533
|
-
*
|
|
534
|
-
* @returns A promise that resolves after the update is processed and all dependent effects have been notified.
|
|
535
|
-
*
|
|
536
|
-
* @remarks
|
|
537
|
-
* This method can be used in two ways:
|
|
538
|
-
*
|
|
539
|
-
* **For mutable state nodes** (constructed with a constant Promise):
|
|
540
|
-
* - Updates the stored Promise directly
|
|
541
|
-
* - All dependents are notified of the change
|
|
542
|
-
*
|
|
543
|
-
* **For computed nodes** (constructed with a compute function):
|
|
544
|
-
* - Temporarily overrides the computed value
|
|
545
|
-
* - The override persists until the next recomputation, which occurs when:
|
|
546
|
-
* - A tracked dependency changes, or
|
|
547
|
-
* - The node is refreshed
|
|
548
|
-
* - This allows temporarily overriding computed values for testing or manual control
|
|
549
|
-
*
|
|
550
|
-
* **Value Comparison:**
|
|
551
|
-
* The Promises are resolved and their values are compared. If the resolved new value is strictly
|
|
552
|
-
* equal (`===`) to the current resolved value, no update occurs and subscribers are not notified.
|
|
553
|
-
* This prevents unnecessary re-renders and effect executions.
|
|
554
|
-
*
|
|
555
|
-
* **Asynchronous Processing:**
|
|
556
|
-
* The update is processed asynchronously through the reactive graph, ensuring proper
|
|
557
|
-
* ordering of updates and effect execution. The method returns a Promise that resolves
|
|
558
|
-
* after the update is complete.
|
|
559
|
-
*
|
|
560
|
-
* @throws Error if the node has been disposed.
|
|
561
|
-
*
|
|
562
|
-
* @example
|
|
563
|
-
* ```typescript
|
|
564
|
-
* // Mutable state usage
|
|
565
|
-
* const $count = new FlowNodeAsync(Promise.resolve(0));
|
|
566
|
-
* await $count.set(Promise.resolve(5));
|
|
567
|
-
* await $count.set(async (current) => Promise.resolve(current + 1)); // 6
|
|
568
|
-
*
|
|
569
|
-
* // Temporary override of computed value
|
|
570
|
-
* const $source = new FlowNodeAsync(Promise.resolve(10));
|
|
571
|
-
* const $doubled = new FlowNodeAsync(async (t) => {
|
|
572
|
-
* const val = await $source.get(t);
|
|
573
|
-
* return val * 2;
|
|
574
|
-
* });
|
|
575
|
-
*
|
|
576
|
-
* console.log(await $doubled.pick()); // 20
|
|
577
|
-
*
|
|
578
|
-
* // Temporarily override
|
|
579
|
-
* await $doubled.set(Promise.resolve(50));
|
|
580
|
-
* console.log(await $doubled.pick()); // 50 (override active)
|
|
581
|
-
*
|
|
582
|
-
* // Dependency change clears override
|
|
583
|
-
* await $source.set(Promise.resolve(15));
|
|
584
|
-
* console.log(await $doubled.pick()); // 30 (recomputed, override cleared)
|
|
585
|
-
* ```
|
|
586
|
-
*
|
|
587
|
-
* @public
|
|
588
|
-
*/
|
|
589
|
-
async set(value) {
|
|
590
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
591
|
-
const update = async () => {
|
|
592
|
-
await this._computeValue();
|
|
593
|
-
const currentValue = await this._promise;
|
|
594
|
-
const nextPromise = typeof value === "function" ? value(currentValue) : value;
|
|
595
|
-
const nextValue = await nextPromise;
|
|
596
|
-
this._promise = nextPromise;
|
|
597
|
-
return nextValue !== currentValue;
|
|
598
|
-
};
|
|
599
|
-
const notify = () => {
|
|
600
|
-
super._notify();
|
|
601
|
-
};
|
|
602
|
-
return FlowGraph.requestWrite(notify, update);
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* Forces recomputation of the value, even if it's not marked as dirty.
|
|
606
|
-
*
|
|
607
|
-
* @returns A promise that resolves after the recomputation is complete and all
|
|
608
|
-
* dependent effects have been notified.
|
|
609
|
-
*
|
|
610
|
-
* @remarks
|
|
611
|
-
* This method is useful when you need to force a recomputation of a computed value,
|
|
612
|
-
* for example when the computation depends on external data that has changed outside
|
|
613
|
-
* the reactive system.
|
|
614
|
-
*
|
|
615
|
-
* **Behavior:**
|
|
616
|
-
* - For nodes with a compute function: Forces the compute function to run again,
|
|
617
|
-
* even if no dependencies have changed. This effectively clears any temporary
|
|
618
|
-
* override that was set using `set()`.
|
|
619
|
-
* - For nodes without a compute function (mutable state): Recomputes the current
|
|
620
|
-
* value (which is just the stored value), useful for consistency but typically
|
|
621
|
-
* not necessary.
|
|
622
|
-
*
|
|
623
|
-
* The recomputation happens asynchronously through the reactive graph, ensuring
|
|
624
|
-
* proper ordering of updates and effect execution.
|
|
625
|
-
*
|
|
626
|
-
* @throws Error if the node has been disposed.
|
|
627
|
-
*
|
|
628
|
-
* @example
|
|
629
|
-
* ```typescript
|
|
630
|
-
* const $externalData = new FlowNode(() => fetchExternalData());
|
|
631
|
-
*
|
|
632
|
-
* // Some external event occurs that changes the data source
|
|
633
|
-
* externalDataChanged();
|
|
634
|
-
*
|
|
635
|
-
* // Force recomputation to get the new value
|
|
636
|
-
* await $externalData.refresh();
|
|
637
|
-
*
|
|
638
|
-
* // For computed nodes with temporary overrides
|
|
639
|
-
* const $computed = new FlowNode((t) => $source.get(t) * 2);
|
|
640
|
-
* $computed.set(100); // Temporary override
|
|
641
|
-
* await $computed.refresh(); // Clears override, recomputes from source
|
|
642
|
-
* ```
|
|
643
|
-
*
|
|
644
|
-
* @public
|
|
645
|
-
*/
|
|
646
|
-
async refresh() {
|
|
647
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
648
|
-
const update = async () => {
|
|
649
|
-
await this._computeValue();
|
|
650
|
-
const currentValue = await this._promise;
|
|
651
|
-
await this._computeValue({ force: true });
|
|
652
|
-
const nextValue = await this._promise;
|
|
653
|
-
return nextValue !== currentValue;
|
|
654
|
-
};
|
|
655
|
-
const notify = () => {
|
|
656
|
-
super._notify();
|
|
657
|
-
};
|
|
658
|
-
return await FlowGraph.requestWrite(notify, update);
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* Gets the current value without any dependency tracking.
|
|
662
|
-
*
|
|
663
|
-
* @returns A promise that resolves with the current value of type T.
|
|
664
|
-
*
|
|
665
|
-
* @remarks
|
|
666
|
-
* This method reads the value asynchronously through the reactive graph, ensuring proper ordering of
|
|
667
|
-
* read operations. Unlike `get(t)`, this method does not create a reactive dependency.
|
|
668
|
-
*
|
|
669
|
-
* **Important:** This is an async method that returns `Promise<T>`. Always use `await` when calling it.
|
|
670
|
-
*
|
|
671
|
-
* Use `pick()` when you want to read a snapshot of the current value without creating a reactive
|
|
672
|
-
* dependency. This is useful for:
|
|
673
|
-
* - Reading initial values outside reactive contexts
|
|
674
|
-
* - Accessing configuration that shouldn't trigger updates
|
|
675
|
-
* - Mixing tracked and untracked reads in the same effect
|
|
676
|
-
*
|
|
677
|
-
* @example
|
|
678
|
-
* ```typescript
|
|
679
|
-
* // Read a snapshot outside reactive context
|
|
680
|
-
* const currentValue = await $state.pick();
|
|
681
|
-
*
|
|
682
|
-
* // Mix tracked and untracked reads
|
|
683
|
-
* effect(async (t) => {
|
|
684
|
-
* const tracked = await $reactive.get(t); // Triggers re-runs
|
|
685
|
-
* const snapshot = await $config.pick(); // Doesn't trigger re-runs
|
|
686
|
-
* processData(tracked, snapshot);
|
|
687
|
-
* });
|
|
688
|
-
* ```
|
|
689
|
-
*
|
|
690
|
-
* @throws Error if the node has been disposed.
|
|
691
|
-
*
|
|
692
|
-
* @public
|
|
693
|
-
*/
|
|
694
|
-
async pick() {
|
|
695
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
696
|
-
await FlowGraph.requestRead(() => this._computeValue());
|
|
697
|
-
return this._promise;
|
|
698
|
-
}
|
|
699
|
-
/**
|
|
700
|
-
* Subscribes a listener function to changes of this node.
|
|
701
|
-
*
|
|
702
|
-
* @param listener - A callback function that receives the new value whenever it changes.
|
|
703
|
-
*
|
|
704
|
-
* @returns A disposer function that cancels the subscription when called.
|
|
705
|
-
*
|
|
706
|
-
* @remarks
|
|
707
|
-
* This method creates a reactive subscription that automatically tracks this node as a dependency.
|
|
708
|
-
* The listener is executed:
|
|
709
|
-
* - Immediately with the current value when the subscription is created
|
|
710
|
-
* - Automatically whenever the value changes
|
|
711
|
-
*
|
|
712
|
-
* **Important:** The listener receives a `Promise<T>`, not `T`. You must await the Promise
|
|
713
|
-
* within the listener to access the actual value. The Promise is the same one returned by `get()`,
|
|
714
|
-
* so it's cached until dependencies change.
|
|
715
|
-
*
|
|
716
|
-
* The subscription uses a {@link FlowEffect} internally to manage the reactive tracking and
|
|
717
|
-
* automatic re-execution. When the value changes, the listener is called with the new Promise
|
|
718
|
-
* after the update is processed through the reactive graph.
|
|
719
|
-
*
|
|
720
|
-
* **Cleanup:**
|
|
721
|
-
* Always call the returned disposer function when you no longer need the subscription to
|
|
722
|
-
* prevent memory leaks and unnecessary computations.
|
|
723
|
-
*
|
|
724
|
-
* @example
|
|
725
|
-
* ```typescript
|
|
726
|
-
* const $count = new FlowNodeAsync(Promise.resolve(0));
|
|
727
|
-
*
|
|
728
|
-
* // Subscribe to changes
|
|
729
|
-
* const unsubscribe = $count.subscribe(async (valuePromise) => {
|
|
730
|
-
* const value = await valuePromise;
|
|
731
|
-
* console.log(`Count is now: ${value}`);
|
|
732
|
-
* });
|
|
733
|
-
* // Logs immediately: "Count is now: 0"
|
|
734
|
-
*
|
|
735
|
-
* await $count.set(Promise.resolve(5));
|
|
736
|
-
* // Logs: "Count is now: 5"
|
|
737
|
-
*
|
|
738
|
-
* // Clean up when done
|
|
739
|
-
* unsubscribe();
|
|
740
|
-
* ```
|
|
741
|
-
*
|
|
742
|
-
* @throws Error if the node has been disposed.
|
|
743
|
-
*
|
|
744
|
-
* @public
|
|
745
|
-
*/
|
|
746
|
-
subscribe(listener) {
|
|
747
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
748
|
-
const effect = new FlowEffect((t) => {
|
|
749
|
-
listener(this.get(t));
|
|
750
|
-
});
|
|
751
|
-
return () => effect.dispose();
|
|
484
|
+
switch (this.status) {
|
|
485
|
+
case "resolved":
|
|
486
|
+
return this._value;
|
|
487
|
+
case "error":
|
|
488
|
+
throw this._error;
|
|
489
|
+
case "pending": {
|
|
490
|
+
throw new PendingError(this._scheduler.settled);
|
|
491
|
+
}
|
|
492
|
+
case "dirty": {
|
|
493
|
+
this.execute();
|
|
494
|
+
switch (this.status) {
|
|
495
|
+
case "resolved":
|
|
496
|
+
return this._value;
|
|
497
|
+
case "error":
|
|
498
|
+
throw this._error;
|
|
499
|
+
case "pending": {
|
|
500
|
+
throw new PendingError(this._scheduler.settled);
|
|
501
|
+
}
|
|
502
|
+
case "dirty": {
|
|
503
|
+
throw new Error("[PicoFlow] Internal error");
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
752
508
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
509
|
+
async pick() {
|
|
510
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
511
|
+
switch (this.status) {
|
|
512
|
+
case "resolved":
|
|
513
|
+
return this._value;
|
|
514
|
+
case "pending": {
|
|
515
|
+
await this._scheduler.settled;
|
|
516
|
+
if (this.status === "resolved") return this._value;
|
|
517
|
+
if (this.status === "error") throw this._error;
|
|
518
|
+
throw new Error("[PicoFlow] Internal error");
|
|
519
|
+
}
|
|
520
|
+
case "error":
|
|
521
|
+
throw this._error;
|
|
522
|
+
case "dirty": {
|
|
523
|
+
this.execute();
|
|
524
|
+
switch (this.status) {
|
|
525
|
+
case "resolved":
|
|
526
|
+
return this._value;
|
|
527
|
+
case "pending": {
|
|
528
|
+
await this._scheduler.settled;
|
|
529
|
+
if (this.status === "resolved") return this._value;
|
|
530
|
+
if (this.status === "error") throw this._error;
|
|
531
|
+
throw new Error("[PicoFlow] Internal error");
|
|
532
|
+
}
|
|
533
|
+
case "error":
|
|
534
|
+
throw this._error;
|
|
535
|
+
case "dirty": {
|
|
536
|
+
throw new Error("[PicoFlow] Internal error");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
758
540
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
541
|
+
}
|
|
542
|
+
execute() {
|
|
543
|
+
if (this.disposed) return;
|
|
544
|
+
this.clearDependencies();
|
|
545
|
+
this.status = "pending";
|
|
546
|
+
this._scheduler.compute();
|
|
547
|
+
}
|
|
548
|
+
subscribe(onValue, onError, onPending) {
|
|
549
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
550
|
+
const effect = new EffectNode((t) => this.get(t), onValue, onError, onPending);
|
|
551
|
+
return effect;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
class ValueAsyncNode extends ValueNode {
|
|
556
|
+
_scheduler;
|
|
557
|
+
_compute;
|
|
558
|
+
constructor(promiseOrCompute) {
|
|
559
|
+
super();
|
|
560
|
+
if (typeof promiseOrCompute === "function") {
|
|
561
|
+
this._compute = () => promiseOrCompute(this, this._value);
|
|
562
|
+
} else {
|
|
563
|
+
this._compute = () => promiseOrCompute;
|
|
767
564
|
}
|
|
768
|
-
this.
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const dependenciesToRemove = dependencies.filter(
|
|
774
|
-
(dependency) => !this._dependencies.has(dependency)
|
|
565
|
+
this.status = "dirty";
|
|
566
|
+
this._scheduler = new AsyncScheduler(
|
|
567
|
+
() => this._compute(),
|
|
568
|
+
(value) => this._onResolve(value),
|
|
569
|
+
(error) => this._onReject(error)
|
|
775
570
|
);
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
571
|
+
}
|
|
572
|
+
_onResolve(value) {
|
|
573
|
+
this.status = "resolved";
|
|
574
|
+
this._value = value;
|
|
575
|
+
this._error = void 0;
|
|
576
|
+
this.notifyDependents();
|
|
577
|
+
}
|
|
578
|
+
_onReject(error) {
|
|
579
|
+
this.status = "error";
|
|
580
|
+
this._error = error;
|
|
581
|
+
this.notifyDependents();
|
|
582
|
+
}
|
|
583
|
+
set(promiseOrUpdater) {
|
|
584
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
585
|
+
if (typeof promiseOrUpdater === "function") {
|
|
586
|
+
const updater = promiseOrUpdater;
|
|
587
|
+
switch (this.status) {
|
|
588
|
+
case "resolved": {
|
|
589
|
+
this.status = "pending";
|
|
590
|
+
this._scheduler.overwrite(updater(this._value));
|
|
591
|
+
this.notifyDependents();
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
case "pending":
|
|
595
|
+
case "error":
|
|
596
|
+
case "dirty": {
|
|
597
|
+
this.status = "pending";
|
|
598
|
+
this._scheduler.overwrite(updater(this._value));
|
|
599
|
+
this.notifyDependents();
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
this.status = "pending";
|
|
605
|
+
const promise = promiseOrUpdater;
|
|
606
|
+
this._scheduler.overwrite(promise);
|
|
607
|
+
this.notifyDependents();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
refresh() {
|
|
611
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
612
|
+
this.execute();
|
|
779
613
|
}
|
|
780
614
|
}
|
|
781
615
|
|
|
782
|
-
function constantAsync(
|
|
783
|
-
return new
|
|
616
|
+
function constantAsync(valueOrInitializer) {
|
|
617
|
+
return new ValueAsyncNode(valueOrInitializer);
|
|
784
618
|
}
|
|
785
619
|
|
|
786
|
-
function derivationAsync(
|
|
787
|
-
return new
|
|
620
|
+
function derivationAsync(compute) {
|
|
621
|
+
return new ValueAsyncNode(compute);
|
|
788
622
|
}
|
|
789
623
|
|
|
790
|
-
function stateAsync(
|
|
791
|
-
return new
|
|
624
|
+
function stateAsync(valueOrInitializer) {
|
|
625
|
+
return new ValueAsyncNode(valueOrInitializer);
|
|
792
626
|
}
|
|
793
627
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
628
|
+
function writableDerivationAsync(compute) {
|
|
629
|
+
return new ValueAsyncNode(compute);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
class ValueSyncNode extends ValueNode {
|
|
633
|
+
_scheduler;
|
|
797
634
|
_compute;
|
|
798
|
-
|
|
799
|
-
* Creates a new FlowNode.
|
|
800
|
-
*
|
|
801
|
-
* @param compute - Either a constant value or a compute function that derives the value.
|
|
802
|
-
*
|
|
803
|
-
* @remarks
|
|
804
|
-
* The constructor accepts two different initialization modes:
|
|
805
|
-
*
|
|
806
|
-
* - **Constant value**: Creates a mutable reactive node that can be updated via `set()`.
|
|
807
|
-
* The value is stored immediately and can be changed at any time.
|
|
808
|
-
*
|
|
809
|
-
* - **Compute function**: Creates a computed reactive node that automatically tracks dependencies
|
|
810
|
-
* and recomputes when they change. The compute function receives a tracking context (`t`)
|
|
811
|
-
* that should be used to access dependencies via `.get(t)`. The function is not executed
|
|
812
|
-
* immediately; it runs lazily on first access. The computed value can be temporarily
|
|
813
|
-
* overridden using `set()`, but the override is cleared on the next recomputation
|
|
814
|
-
* (triggered by dependency changes or `refresh()`).
|
|
815
|
-
*
|
|
816
|
-
* @example
|
|
817
|
-
* ```typescript
|
|
818
|
-
* // Mutable state with constant value
|
|
819
|
-
* const $count = new FlowNode(0);
|
|
820
|
-
* $count.set(5);
|
|
821
|
-
*
|
|
822
|
-
* // Computed derivation with function
|
|
823
|
-
* const $a = new FlowNode(10);
|
|
824
|
-
* const $b = new FlowNode(20);
|
|
825
|
-
* const $sum = new FlowNode((t) => $a.get(t) + $b.get(t));
|
|
826
|
-
*
|
|
827
|
-
* // Lazy evaluation - compute function hasn't run yet
|
|
828
|
-
* console.log(await $sum.pick()); // Now it computes: 30
|
|
829
|
-
* ```
|
|
830
|
-
*
|
|
831
|
-
* @public
|
|
832
|
-
*/
|
|
833
|
-
constructor(compute) {
|
|
635
|
+
constructor(valueOrCompute) {
|
|
834
636
|
super();
|
|
835
|
-
if (typeof
|
|
836
|
-
this._compute =
|
|
637
|
+
if (typeof valueOrCompute === "function") {
|
|
638
|
+
this._compute = () => valueOrCompute(this, this._value);
|
|
837
639
|
} else {
|
|
838
|
-
this.
|
|
839
|
-
|
|
640
|
+
this._compute = () => valueOrCompute;
|
|
641
|
+
}
|
|
642
|
+
this.status = "dirty";
|
|
643
|
+
this._scheduler = new SyncScheduler(
|
|
644
|
+
() => this._compute(),
|
|
645
|
+
(value) => this._onResolve(value),
|
|
646
|
+
(error) => this._onReject(error)
|
|
647
|
+
);
|
|
648
|
+
if (typeof valueOrCompute !== "function") {
|
|
649
|
+
this._scheduler.compute();
|
|
840
650
|
}
|
|
841
651
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
this.
|
|
845
|
-
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
*
|
|
894
|
-
* **For computed nodes** (constructed with a compute function):
|
|
895
|
-
* - Temporarily overrides the computed value
|
|
896
|
-
* - The override persists until the next recomputation, which occurs when:
|
|
897
|
-
* - A tracked dependency changes, or
|
|
898
|
-
* - `refresh()` is called
|
|
899
|
-
* - This allows temporarily overriding computed values for testing or manual control
|
|
900
|
-
*
|
|
901
|
-
* **Value Comparison:**
|
|
902
|
-
* If the new value is strictly equal (`===`) to the current value, no update occurs
|
|
903
|
-
* and subscribers are not notified. This prevents unnecessary re-renders and effect
|
|
904
|
-
* executions.
|
|
905
|
-
*
|
|
906
|
-
* **Asynchronous Processing:**
|
|
907
|
-
* The update is processed asynchronously through the reactive graph, ensuring proper
|
|
908
|
-
* ordering of updates and effect execution.
|
|
909
|
-
*
|
|
910
|
-
* @throws Error if the node has been disposed.
|
|
911
|
-
*
|
|
912
|
-
* @example
|
|
913
|
-
* ```typescript
|
|
914
|
-
* // Mutable state usage
|
|
915
|
-
* const $count = new FlowNode(0);
|
|
916
|
-
* await $count.set(5);
|
|
917
|
-
* await $count.set(current => current + 1); // 6
|
|
918
|
-
*
|
|
919
|
-
* // Temporary override of computed value
|
|
920
|
-
* const $source = new FlowNode(10);
|
|
921
|
-
* const $doubled = new FlowNode((t) => $source.get(t) * 2);
|
|
922
|
-
*
|
|
923
|
-
* console.log(await $doubled.pick()); // 20
|
|
924
|
-
*
|
|
925
|
-
* // Temporarily override
|
|
926
|
-
* await $doubled.set(50);
|
|
927
|
-
* console.log(await $doubled.pick()); // 50 (override active)
|
|
928
|
-
*
|
|
929
|
-
* // Dependency change clears override
|
|
930
|
-
* await $source.set(15);
|
|
931
|
-
* console.log(await $doubled.pick()); // 30 (recomputed, override cleared)
|
|
932
|
-
* ```
|
|
933
|
-
*
|
|
934
|
-
* @public
|
|
935
|
-
*/
|
|
936
|
-
set(value) {
|
|
937
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
938
|
-
const update = () => {
|
|
939
|
-
this._computeValue();
|
|
940
|
-
const currentValue = this._value;
|
|
941
|
-
const next = typeof value === "function" ? value(currentValue) : value;
|
|
942
|
-
this._value = next;
|
|
943
|
-
return next !== currentValue;
|
|
944
|
-
};
|
|
945
|
-
const notify = () => {
|
|
946
|
-
super._notify();
|
|
947
|
-
};
|
|
948
|
-
return FlowGraph.requestWrite(notify, update);
|
|
949
|
-
}
|
|
950
|
-
/**
|
|
951
|
-
* Forces recomputation of the value, even if it's not marked as dirty.
|
|
952
|
-
*
|
|
953
|
-
* @returns A promise that resolves after the recomputation is complete and all
|
|
954
|
-
* dependent effects have been notified.
|
|
955
|
-
*
|
|
956
|
-
* @remarks
|
|
957
|
-
* This method is useful when you need to force a recomputation of a computed value,
|
|
958
|
-
* for example when the computation depends on external data that has changed outside
|
|
959
|
-
* the reactive system.
|
|
960
|
-
*
|
|
961
|
-
* **Behavior:**
|
|
962
|
-
* - For nodes with a compute function: Forces the compute function to run again,
|
|
963
|
-
* even if no dependencies have changed. This effectively clears any temporary
|
|
964
|
-
* override that was set using `set()`.
|
|
965
|
-
* - For nodes without a compute function (mutable state): Recomputes the current
|
|
966
|
-
* value (which is just the stored value), useful for consistency but typically
|
|
967
|
-
* not necessary.
|
|
968
|
-
*
|
|
969
|
-
* The recomputation happens asynchronously through the reactive graph, ensuring
|
|
970
|
-
* proper ordering of updates and effect execution.
|
|
971
|
-
*
|
|
972
|
-
* @throws Error if the node has been disposed.
|
|
973
|
-
*
|
|
974
|
-
* @example
|
|
975
|
-
* ```typescript
|
|
976
|
-
* const $externalData = new FlowNode(() => fetchExternalData());
|
|
977
|
-
*
|
|
978
|
-
* // Some external event occurs that changes the data source
|
|
979
|
-
* externalDataChanged();
|
|
980
|
-
*
|
|
981
|
-
* // Force recomputation to get the new value
|
|
982
|
-
* await $externalData.refresh();
|
|
983
|
-
*
|
|
984
|
-
* // For computed nodes with temporary overrides
|
|
985
|
-
* const $computed = new FlowNode((t) => $source.get(t) * 2);
|
|
986
|
-
* $computed.set(100); // Temporary override
|
|
987
|
-
* await $computed.refresh(); // Clears override, recomputes from source
|
|
988
|
-
* ```
|
|
989
|
-
*
|
|
990
|
-
* @public
|
|
991
|
-
*/
|
|
992
|
-
async refresh() {
|
|
993
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
994
|
-
const update = () => {
|
|
995
|
-
this._computeValue();
|
|
996
|
-
const currentValue = this._value;
|
|
997
|
-
this._computeValue({ force: true });
|
|
998
|
-
const nextValue = this._value;
|
|
999
|
-
return nextValue !== currentValue;
|
|
1000
|
-
};
|
|
1001
|
-
const notify = () => {
|
|
1002
|
-
super._notify();
|
|
1003
|
-
};
|
|
1004
|
-
return await FlowGraph.requestWrite(notify, update);
|
|
1005
|
-
}
|
|
1006
|
-
/**
|
|
1007
|
-
* Gets the current value without any dependency tracking.
|
|
1008
|
-
*
|
|
1009
|
-
* @returns A promise that resolves with the current value of type T.
|
|
1010
|
-
*
|
|
1011
|
-
* @remarks
|
|
1012
|
-
* This method reads the value asynchronously through the reactive graph, ensuring proper ordering of
|
|
1013
|
-
* read operations. Unlike `get(t)`, this method does not create a reactive dependency.
|
|
1014
|
-
*
|
|
1015
|
-
* Use `pick()` when you want to read a snapshot of the current value without creating a reactive
|
|
1016
|
-
* dependency. This is useful for:
|
|
1017
|
-
* - Reading initial values outside reactive contexts
|
|
1018
|
-
* - Accessing configuration that shouldn't trigger updates
|
|
1019
|
-
* - Mixing tracked and untracked reads in the same effect
|
|
1020
|
-
*
|
|
1021
|
-
* **Note:** This is an async method. Always use `await` when calling it.
|
|
1022
|
-
*
|
|
1023
|
-
* @example
|
|
1024
|
-
* ```typescript
|
|
1025
|
-
* // Read a snapshot outside reactive context
|
|
1026
|
-
* const currentValue = await $state.pick();
|
|
1027
|
-
*
|
|
1028
|
-
* // Mix tracked and untracked reads
|
|
1029
|
-
* effect(async (t) => {
|
|
1030
|
-
* const tracked = $reactive.get(t); // Triggers re-runs
|
|
1031
|
-
* const snapshot = await $config.pick(); // Doesn't trigger re-runs
|
|
1032
|
-
* processData(tracked, snapshot);
|
|
1033
|
-
* });
|
|
1034
|
-
* ```
|
|
1035
|
-
*
|
|
1036
|
-
* @throws Error if the node has been disposed.
|
|
1037
|
-
*
|
|
1038
|
-
* @public
|
|
1039
|
-
*/
|
|
1040
|
-
async pick() {
|
|
1041
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1042
|
-
await FlowGraph.requestRead(() => this._computeValue());
|
|
1043
|
-
return this._value;
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Subscribes a listener function to changes of this node.
|
|
1047
|
-
*
|
|
1048
|
-
* @param listener - A callback function that receives the new value whenever it changes.
|
|
1049
|
-
*
|
|
1050
|
-
* @returns A disposer function that cancels the subscription when called.
|
|
1051
|
-
*
|
|
1052
|
-
* @remarks
|
|
1053
|
-
* This method creates a reactive subscription that automatically tracks this node as a dependency.
|
|
1054
|
-
* The listener is executed:
|
|
1055
|
-
* - Immediately with the current value when the subscription is created
|
|
1056
|
-
* - Automatically whenever the value changes
|
|
1057
|
-
*
|
|
1058
|
-
* The subscription uses a {@link FlowEffect} internally to manage the reactive tracking and
|
|
1059
|
-
* automatic re-execution. When the value changes, the listener is called with the new value
|
|
1060
|
-
* after the update is processed through the reactive graph.
|
|
1061
|
-
*
|
|
1062
|
-
* **Cleanup:**
|
|
1063
|
-
* Always call the returned disposer function when you no longer need the subscription to
|
|
1064
|
-
* prevent memory leaks and unnecessary computations.
|
|
1065
|
-
*
|
|
1066
|
-
* @example
|
|
1067
|
-
* ```typescript
|
|
1068
|
-
* const $count = new FlowNode(0);
|
|
1069
|
-
*
|
|
1070
|
-
* // Subscribe to changes
|
|
1071
|
-
* const unsubscribe = $count.subscribe((value) => {
|
|
1072
|
-
* console.log(`Count is now: ${value}`);
|
|
1073
|
-
* });
|
|
1074
|
-
* // Logs immediately: "Count is now: 0"
|
|
1075
|
-
*
|
|
1076
|
-
* await $count.set(5);
|
|
1077
|
-
* // Logs: "Count is now: 5"
|
|
1078
|
-
*
|
|
1079
|
-
* // Clean up when done
|
|
1080
|
-
* unsubscribe();
|
|
1081
|
-
* ```
|
|
1082
|
-
*
|
|
1083
|
-
* @throws Error if the node has been disposed.
|
|
1084
|
-
*
|
|
1085
|
-
* @public
|
|
1086
|
-
*/
|
|
1087
|
-
subscribe(listener) {
|
|
1088
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1089
|
-
const effect = new FlowEffect((t) => {
|
|
1090
|
-
listener(this.get(t));
|
|
1091
|
-
});
|
|
1092
|
-
return () => effect.dispose();
|
|
1093
|
-
}
|
|
1094
|
-
/* INTERNAL --------------------------------------------------------- */
|
|
1095
|
-
/* @internal */
|
|
1096
|
-
_notify() {
|
|
1097
|
-
if (this._dirty) {
|
|
1098
|
-
return;
|
|
652
|
+
_onResolve(value) {
|
|
653
|
+
this.status = "resolved";
|
|
654
|
+
this._value = value;
|
|
655
|
+
this._error = void 0;
|
|
656
|
+
}
|
|
657
|
+
_onReject(error) {
|
|
658
|
+
this.status = "error";
|
|
659
|
+
this._error = error;
|
|
660
|
+
}
|
|
661
|
+
set(valueOrUpdater) {
|
|
662
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
663
|
+
if (typeof valueOrUpdater === "function") {
|
|
664
|
+
const updater = valueOrUpdater;
|
|
665
|
+
switch (this.status) {
|
|
666
|
+
case "resolved": {
|
|
667
|
+
const nextValue = updater(this._value);
|
|
668
|
+
if (nextValue === this._value) return;
|
|
669
|
+
this.status = "pending";
|
|
670
|
+
this._scheduler.overwrite(nextValue);
|
|
671
|
+
this.notifyDependents();
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
case "pending":
|
|
675
|
+
case "error":
|
|
676
|
+
case "dirty": {
|
|
677
|
+
const nextValue = updater(this._value);
|
|
678
|
+
this.status = "pending";
|
|
679
|
+
this._scheduler.overwrite(nextValue);
|
|
680
|
+
this.notifyDependents();
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
} else {
|
|
685
|
+
const nextValue = valueOrUpdater;
|
|
686
|
+
switch (this.status) {
|
|
687
|
+
case "resolved": {
|
|
688
|
+
if (nextValue === this._value) return;
|
|
689
|
+
this.status = "pending";
|
|
690
|
+
this._scheduler.overwrite(nextValue);
|
|
691
|
+
this.notifyDependents();
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
case "pending":
|
|
695
|
+
case "error":
|
|
696
|
+
case "dirty": {
|
|
697
|
+
this.status = "pending";
|
|
698
|
+
this._scheduler.overwrite(nextValue);
|
|
699
|
+
this.notifyDependents();
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
1099
703
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
704
|
+
}
|
|
705
|
+
refresh() {
|
|
706
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
707
|
+
const currentValue = this._value;
|
|
708
|
+
const currentStatus = this.status;
|
|
709
|
+
this.execute();
|
|
710
|
+
switch (this.status) {
|
|
711
|
+
case "resolved": {
|
|
712
|
+
const nextValue = this._value;
|
|
713
|
+
if (nextValue === currentValue && currentStatus === this.status) return;
|
|
714
|
+
this.notifyDependents();
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
case "error":
|
|
718
|
+
case "pending":
|
|
719
|
+
case "dirty": {
|
|
720
|
+
this.notifyDependents();
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
1108
723
|
}
|
|
1109
|
-
this._dirty = false;
|
|
1110
|
-
const dependencies = [...this._dependencies];
|
|
1111
|
-
this._dependencies.clear();
|
|
1112
|
-
this._value = this._compute(this);
|
|
1113
|
-
const dependenciesToRemove = dependencies.filter(
|
|
1114
|
-
(dependency) => !this._dependencies.has(dependency)
|
|
1115
|
-
);
|
|
1116
|
-
dependenciesToRemove.forEach((dependency) => {
|
|
1117
|
-
dependency._unregisterDependency(this);
|
|
1118
|
-
});
|
|
1119
724
|
}
|
|
1120
725
|
}
|
|
1121
726
|
|
|
1122
|
-
|
|
1123
|
-
return new FlowNode(value);
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
function derivation(fn) {
|
|
1127
|
-
return new FlowNode(fn);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
function state(value) {
|
|
1131
|
-
return new FlowNode(value);
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
class FlowArray extends FlowNode {
|
|
1135
|
-
/**
|
|
1136
|
-
* Last action performed on the FlowArray.
|
|
1137
|
-
* @public
|
|
1138
|
-
*/
|
|
727
|
+
class ArrayNode extends ValueSyncNode {
|
|
1139
728
|
$lastAction;
|
|
1140
|
-
/**
|
|
1141
|
-
* Creates an instance of FlowArray.
|
|
1142
|
-
* @param value - Initial array value.
|
|
1143
|
-
* @public
|
|
1144
|
-
*/
|
|
1145
729
|
constructor(value = []) {
|
|
1146
730
|
super(value);
|
|
1147
|
-
this.$lastAction =
|
|
731
|
+
this.$lastAction = new ValueSyncNode({
|
|
1148
732
|
type: "set",
|
|
1149
|
-
|
|
733
|
+
setItems: value,
|
|
734
|
+
clearedItems: []
|
|
1150
735
|
});
|
|
1151
736
|
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
737
|
+
get(tracker) {
|
|
738
|
+
const values = super.get(tracker);
|
|
739
|
+
return [...values];
|
|
740
|
+
}
|
|
741
|
+
async pick() {
|
|
742
|
+
const values = await super.pick();
|
|
743
|
+
return [...values];
|
|
744
|
+
}
|
|
1157
745
|
get length() {
|
|
1158
|
-
if (this.
|
|
746
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1159
747
|
return this._value.length;
|
|
1160
748
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
* @param items - The new array of items.
|
|
1172
|
-
* @public
|
|
1173
|
-
*/
|
|
1174
|
-
async set(items) {
|
|
1175
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1176
|
-
const update = () => {
|
|
1177
|
-
const currentValue = this._value;
|
|
1178
|
-
if (currentValue !== items) {
|
|
1179
|
-
currentValue.forEach((item) => {
|
|
1180
|
-
if (isDisposable(item)) item.dispose({ self: true });
|
|
1181
|
-
});
|
|
1182
|
-
this._value = items;
|
|
1183
|
-
this.$lastAction.set({ type: "set", items });
|
|
1184
|
-
}
|
|
1185
|
-
return currentValue !== items;
|
|
1186
|
-
};
|
|
1187
|
-
const notify = () => {
|
|
1188
|
-
super._notify();
|
|
1189
|
-
};
|
|
1190
|
-
return FlowGraph.requestWrite(notify, update);
|
|
1191
|
-
}
|
|
1192
|
-
/**
|
|
1193
|
-
* Replaces an item at a specific index.
|
|
1194
|
-
* @param index - The index of the item to replace.
|
|
1195
|
-
* @param item - The new item.
|
|
1196
|
-
* @public
|
|
1197
|
-
*/
|
|
1198
|
-
async update(index, item) {
|
|
1199
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1200
|
-
const update = () => {
|
|
1201
|
-
if (index < 0 || index >= this._value.length) {
|
|
1202
|
-
throw new Error("[PicoFlow] Index out of bounds");
|
|
1203
|
-
}
|
|
1204
|
-
const currentValue = this._value[index];
|
|
1205
|
-
if (currentValue !== item) {
|
|
1206
|
-
if (isDisposable(currentValue)) {
|
|
1207
|
-
currentValue.dispose({ self: true });
|
|
1208
|
-
}
|
|
1209
|
-
this._value[index] = item;
|
|
1210
|
-
this.$lastAction.set({ type: "update", index, item });
|
|
1211
|
-
}
|
|
1212
|
-
return currentValue !== item;
|
|
1213
|
-
};
|
|
1214
|
-
const notify = () => {
|
|
1215
|
-
super._notify();
|
|
1216
|
-
};
|
|
1217
|
-
return FlowGraph.requestWrite(notify, update);
|
|
1218
|
-
}
|
|
1219
|
-
/**
|
|
1220
|
-
* Appends an item to the end of the array.
|
|
1221
|
-
* @param item - The item to append.
|
|
1222
|
-
* @public
|
|
1223
|
-
*/
|
|
1224
|
-
async push(item) {
|
|
1225
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1226
|
-
const update = () => {
|
|
1227
|
-
this._value.push(item);
|
|
1228
|
-
this.$lastAction.set({ type: "push", item });
|
|
1229
|
-
return true;
|
|
1230
|
-
};
|
|
1231
|
-
const notify = () => {
|
|
1232
|
-
super._notify();
|
|
1233
|
-
};
|
|
1234
|
-
return FlowGraph.requestWrite(notify, update);
|
|
749
|
+
set(itemsOrUpdater) {
|
|
750
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
751
|
+
const previousValue = this._value;
|
|
752
|
+
super.set(itemsOrUpdater);
|
|
753
|
+
this.$lastAction.set({
|
|
754
|
+
type: "set",
|
|
755
|
+
setItems: this._value,
|
|
756
|
+
clearedItems: previousValue
|
|
757
|
+
});
|
|
758
|
+
return previousValue;
|
|
1235
759
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
};
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
760
|
+
update(index, item) {
|
|
761
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
762
|
+
if (index < 0 || index >= this._value.length) {
|
|
763
|
+
throw new Error("[PicoFlow] Index out of bounds");
|
|
764
|
+
}
|
|
765
|
+
const previousValue = this._value[index];
|
|
766
|
+
this._value[index] = item;
|
|
767
|
+
this.notifyDependents();
|
|
768
|
+
this.$lastAction.set({
|
|
769
|
+
type: "update",
|
|
770
|
+
index,
|
|
771
|
+
setItem: item,
|
|
772
|
+
clearedItem: previousValue
|
|
773
|
+
});
|
|
774
|
+
return previousValue;
|
|
775
|
+
}
|
|
776
|
+
push(item) {
|
|
777
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
778
|
+
this._value.push(item);
|
|
779
|
+
this.notifyDependents();
|
|
780
|
+
this.$lastAction.set({ type: "push", addedItem: item });
|
|
781
|
+
}
|
|
782
|
+
pop() {
|
|
783
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
784
|
+
const item = this._value.pop();
|
|
785
|
+
this.notifyDependents();
|
|
786
|
+
this.$lastAction.set({ type: "pop", removedItem: item });
|
|
787
|
+
return item;
|
|
788
|
+
}
|
|
789
|
+
unshift(item) {
|
|
790
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
791
|
+
this._value.unshift(item);
|
|
792
|
+
this.notifyDependents();
|
|
793
|
+
this.$lastAction.set({ type: "unshift", addedItem: item });
|
|
794
|
+
}
|
|
795
|
+
shift() {
|
|
796
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
797
|
+
const item = this._value.shift();
|
|
798
|
+
this.notifyDependents();
|
|
799
|
+
this.$lastAction.set({ type: "shift", removedItem: item });
|
|
800
|
+
return item;
|
|
801
|
+
}
|
|
802
|
+
splice(start, deleteCount, ...newItems) {
|
|
803
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
804
|
+
const items = this._value.splice(start, deleteCount, ...newItems);
|
|
805
|
+
this.notifyDependents();
|
|
806
|
+
this.$lastAction.set({
|
|
807
|
+
type: "splice",
|
|
808
|
+
start,
|
|
809
|
+
deleteCount,
|
|
810
|
+
addedItems: newItems,
|
|
811
|
+
removedItems: items
|
|
812
|
+
});
|
|
813
|
+
return items;
|
|
1274
814
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
const item = this._value.shift();
|
|
1283
|
-
if (item !== void 0) {
|
|
1284
|
-
if (isDisposable(item)) {
|
|
1285
|
-
item.dispose({ self: true });
|
|
1286
|
-
}
|
|
1287
|
-
this.$lastAction.set({ type: "shift" });
|
|
1288
|
-
return true;
|
|
1289
|
-
}
|
|
1290
|
-
return false;
|
|
1291
|
-
};
|
|
1292
|
-
const notify = () => {
|
|
1293
|
-
super._notify();
|
|
1294
|
-
};
|
|
1295
|
-
return FlowGraph.requestWrite(notify, update);
|
|
1296
|
-
}
|
|
1297
|
-
/**
|
|
1298
|
-
* Changes the content of the array.
|
|
1299
|
-
* @param start - The starting index.
|
|
1300
|
-
* @param deleteCount - Number of items to remove.
|
|
1301
|
-
* @param newItems - New items to add.
|
|
1302
|
-
* @public
|
|
1303
|
-
*/
|
|
1304
|
-
async splice(start, deleteCount, ...newItems) {
|
|
1305
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1306
|
-
const update = () => {
|
|
1307
|
-
const items = this._value.splice(start, deleteCount, ...newItems);
|
|
1308
|
-
items.forEach((item) => {
|
|
1309
|
-
if (isDisposable(item)) item.dispose({ self: true });
|
|
1310
|
-
});
|
|
1311
|
-
this.$lastAction.set({
|
|
1312
|
-
type: "splice",
|
|
1313
|
-
start,
|
|
1314
|
-
deleteCount,
|
|
1315
|
-
items: newItems
|
|
1316
|
-
});
|
|
1317
|
-
return true;
|
|
1318
|
-
};
|
|
1319
|
-
const notify = () => {
|
|
1320
|
-
super._notify();
|
|
1321
|
-
};
|
|
1322
|
-
return FlowGraph.requestWrite(notify, update);
|
|
815
|
+
clear() {
|
|
816
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
817
|
+
const previousValue = [...this._value];
|
|
818
|
+
this._value = [];
|
|
819
|
+
this.notifyDependents();
|
|
820
|
+
this.$lastAction.set({ type: "clear", clearedItems: previousValue });
|
|
821
|
+
return previousValue;
|
|
1323
822
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
* @public
|
|
1327
|
-
*/
|
|
1328
|
-
async clear() {
|
|
1329
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1330
|
-
const update = () => {
|
|
1331
|
-
const items = [...this._value];
|
|
1332
|
-
items.forEach((item) => {
|
|
1333
|
-
if (isDisposable(item)) item.dispose({ self: true });
|
|
1334
|
-
});
|
|
1335
|
-
this._value = [];
|
|
1336
|
-
this.$lastAction.set({ type: "clear" });
|
|
1337
|
-
return true;
|
|
1338
|
-
};
|
|
1339
|
-
const notify = () => {
|
|
1340
|
-
super._notify();
|
|
1341
|
-
};
|
|
1342
|
-
return FlowGraph.requestWrite(notify, update);
|
|
1343
|
-
}
|
|
1344
|
-
/**
|
|
1345
|
-
* Disposes the FlowArray and its items.
|
|
1346
|
-
* @param options - Disposal options.
|
|
1347
|
-
* @public
|
|
1348
|
-
*/
|
|
1349
|
-
dispose(options) {
|
|
1350
|
-
super.dispose(options);
|
|
823
|
+
dispose() {
|
|
824
|
+
super.dispose();
|
|
1351
825
|
this._value.forEach((item) => {
|
|
1352
|
-
if (
|
|
826
|
+
if (item instanceof Disposable) item.dispose();
|
|
1353
827
|
});
|
|
1354
828
|
this._value = [];
|
|
1355
829
|
}
|
|
1356
|
-
/* INTERNAL */
|
|
1357
830
|
}
|
|
831
|
+
|
|
1358
832
|
function array(initial) {
|
|
1359
|
-
return new
|
|
833
|
+
return new ArrayNode(initial);
|
|
1360
834
|
}
|
|
1361
835
|
|
|
1362
|
-
class
|
|
1363
|
-
/**
|
|
1364
|
-
* Last action performed on the FlowMap.
|
|
1365
|
-
* @public
|
|
1366
|
-
*/
|
|
836
|
+
class MapNode extends ValueSyncNode {
|
|
1367
837
|
$lastAction;
|
|
1368
|
-
/**
|
|
1369
|
-
* Creates an instance of FlowMap.
|
|
1370
|
-
* @param value - Initial map value.
|
|
1371
|
-
* @public
|
|
1372
|
-
*/
|
|
1373
838
|
constructor(value = /* @__PURE__ */ new Map()) {
|
|
1374
839
|
super(value);
|
|
1375
|
-
this.$lastAction =
|
|
840
|
+
this.$lastAction = new ValueSyncNode({
|
|
1376
841
|
type: "set",
|
|
1377
|
-
|
|
842
|
+
setMap: value,
|
|
843
|
+
clearedMap: /* @__PURE__ */ new Map()
|
|
1378
844
|
});
|
|
1379
845
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
if (this.
|
|
1426
|
-
const
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
}
|
|
1435
|
-
return false;
|
|
1436
|
-
};
|
|
1437
|
-
const notify = () => {
|
|
1438
|
-
super._notify();
|
|
1439
|
-
};
|
|
1440
|
-
return FlowGraph.requestWrite(notify, update);
|
|
1441
|
-
}
|
|
1442
|
-
/**
|
|
1443
|
-
* Deletes the value at the specified key from the underlying map.
|
|
1444
|
-
*
|
|
1445
|
-
* @param key - The key to delete.
|
|
1446
|
-
* @throws If the FlowMap instance is disposed.
|
|
1447
|
-
*
|
|
1448
|
-
* @remarks
|
|
1449
|
-
* Removes the key from the internal map, emits the deleted key and its value via {@link FlowMap.$lastAction},
|
|
1450
|
-
* and notifies all subscribers of the change.
|
|
1451
|
-
*
|
|
1452
|
-
* @public
|
|
1453
|
-
*/
|
|
1454
|
-
async delete(key) {
|
|
1455
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1456
|
-
const update = () => {
|
|
1457
|
-
const value = this._value.get(key);
|
|
1458
|
-
if (value === void 0) throw new Error("[PicoFlow] Key does not exist");
|
|
1459
|
-
if (isDisposable(value)) value.dispose({ self: true });
|
|
1460
|
-
this._value.delete(key);
|
|
1461
|
-
this.$lastAction.set({ type: "delete", key, value });
|
|
1462
|
-
return true;
|
|
1463
|
-
};
|
|
1464
|
-
const notify = () => {
|
|
1465
|
-
super._notify();
|
|
1466
|
-
};
|
|
1467
|
-
return FlowGraph.requestWrite(notify, update);
|
|
1468
|
-
}
|
|
1469
|
-
/**
|
|
1470
|
-
* Replaces the entire map with new entries.
|
|
1471
|
-
*
|
|
1472
|
-
* @param map - The new map of entries.
|
|
1473
|
-
* @throws If the FlowMap instance is disposed.
|
|
1474
|
-
*
|
|
1475
|
-
* @remarks
|
|
1476
|
-
* Replaces all entries in the internal map, disposes the old values (if they are disposable),
|
|
1477
|
-
* emits the new map via {@link FlowMap.$lastAction}, and notifies all subscribers of the change.
|
|
1478
|
-
*
|
|
1479
|
-
* @public
|
|
1480
|
-
*/
|
|
1481
|
-
async set(map2) {
|
|
1482
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1483
|
-
const update = () => {
|
|
1484
|
-
const currentValue = this._value;
|
|
1485
|
-
if (currentValue !== map2) {
|
|
1486
|
-
currentValue.forEach((item) => {
|
|
1487
|
-
if (isDisposable(item)) item.dispose({ self: true });
|
|
1488
|
-
});
|
|
1489
|
-
}
|
|
1490
|
-
this._value = map2;
|
|
1491
|
-
if (currentValue !== map2) {
|
|
1492
|
-
this.$lastAction.set({ type: "set", map: map2 });
|
|
1493
|
-
}
|
|
1494
|
-
return currentValue !== map2;
|
|
1495
|
-
};
|
|
1496
|
-
const notify = () => {
|
|
1497
|
-
super._notify();
|
|
1498
|
-
};
|
|
1499
|
-
return FlowGraph.requestWrite(notify, update);
|
|
1500
|
-
}
|
|
1501
|
-
/**
|
|
1502
|
-
* Clears all entries from the map.
|
|
1503
|
-
*
|
|
1504
|
-
* @throws If the FlowMap instance is disposed.
|
|
1505
|
-
*
|
|
1506
|
-
* @remarks
|
|
1507
|
-
* Removes all entries from the internal map, disposes the removed values (if they are disposable),
|
|
1508
|
-
* emits a clear action via {@link FlowMap.$lastAction}, and notifies all subscribers of the change.
|
|
1509
|
-
*
|
|
1510
|
-
* @public
|
|
1511
|
-
*/
|
|
1512
|
-
async clear() {
|
|
1513
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1514
|
-
const update = () => {
|
|
1515
|
-
const items = [...this._value.values()];
|
|
1516
|
-
items.forEach((item) => {
|
|
1517
|
-
if (isDisposable(item)) item.dispose({ self: true });
|
|
1518
|
-
});
|
|
1519
|
-
this._value.clear();
|
|
1520
|
-
this.$lastAction.set({ type: "clear" });
|
|
1521
|
-
return true;
|
|
1522
|
-
};
|
|
1523
|
-
const notify = () => {
|
|
1524
|
-
super._notify();
|
|
1525
|
-
};
|
|
1526
|
-
return FlowGraph.requestWrite(notify, update);
|
|
1527
|
-
}
|
|
1528
|
-
/**
|
|
1529
|
-
* Disposes the FlowMap and its values.
|
|
1530
|
-
* @param options - Disposal options.
|
|
1531
|
-
* @public
|
|
1532
|
-
*/
|
|
1533
|
-
dispose(options) {
|
|
1534
|
-
super.dispose(options);
|
|
846
|
+
add(key, value) {
|
|
847
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
848
|
+
const previousValue = this._value.get(key);
|
|
849
|
+
if (previousValue) {
|
|
850
|
+
throw new Error("[PicoFlow] Key already exists");
|
|
851
|
+
}
|
|
852
|
+
this._value.set(key, value);
|
|
853
|
+
this.notifyDependents();
|
|
854
|
+
this.$lastAction.set({ type: "add", key, addedValue: value });
|
|
855
|
+
}
|
|
856
|
+
update(key, value) {
|
|
857
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
858
|
+
const previousValue = this._value.get(key);
|
|
859
|
+
if (!previousValue) throw new Error("[PicoFlow] Key does not exist");
|
|
860
|
+
this._value.set(key, value);
|
|
861
|
+
this.notifyDependents();
|
|
862
|
+
this.$lastAction.set({
|
|
863
|
+
type: "update",
|
|
864
|
+
key,
|
|
865
|
+
setValue: value,
|
|
866
|
+
clearedValue: previousValue
|
|
867
|
+
});
|
|
868
|
+
return previousValue;
|
|
869
|
+
}
|
|
870
|
+
delete(key) {
|
|
871
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
872
|
+
const value = this._value.get(key);
|
|
873
|
+
if (value === void 0) throw new Error("[PicoFlow] Key does not exist");
|
|
874
|
+
this._value.delete(key);
|
|
875
|
+
this.notifyDependents();
|
|
876
|
+
this.$lastAction.set({ type: "delete", key, removedValue: value });
|
|
877
|
+
return value;
|
|
878
|
+
}
|
|
879
|
+
set(mapOrUpdater) {
|
|
880
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
881
|
+
const previousValue = this._value;
|
|
882
|
+
super.set(mapOrUpdater);
|
|
883
|
+
this.$lastAction.set({
|
|
884
|
+
type: "set",
|
|
885
|
+
setMap: this._value,
|
|
886
|
+
clearedMap: previousValue
|
|
887
|
+
});
|
|
888
|
+
return previousValue;
|
|
889
|
+
}
|
|
890
|
+
clear() {
|
|
891
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
892
|
+
const previousValue = this._value;
|
|
893
|
+
this._value = /* @__PURE__ */ new Map();
|
|
894
|
+
this.notifyDependents();
|
|
895
|
+
this.$lastAction.set({ type: "clear", clearedMap: previousValue });
|
|
896
|
+
return previousValue;
|
|
897
|
+
}
|
|
898
|
+
dispose() {
|
|
899
|
+
super.dispose();
|
|
1535
900
|
this._value.forEach((item) => {
|
|
1536
|
-
if (
|
|
901
|
+
if (item instanceof Disposable) item.dispose();
|
|
1537
902
|
});
|
|
1538
903
|
this._value.clear();
|
|
1539
904
|
}
|
|
1540
905
|
}
|
|
906
|
+
|
|
1541
907
|
function map(initial) {
|
|
1542
908
|
if (initial instanceof Map) {
|
|
1543
|
-
return new
|
|
909
|
+
return new MapNode(initial);
|
|
1544
910
|
}
|
|
1545
|
-
return new
|
|
1546
|
-
new Map(initial ? Object.entries(initial) : [])
|
|
1547
|
-
);
|
|
911
|
+
return new MapNode(new Map(initial ? Object.entries(initial) : []));
|
|
1548
912
|
}
|
|
1549
913
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
* Returns the current value.
|
|
1553
|
-
*/
|
|
1554
|
-
get;
|
|
1555
|
-
/**
|
|
1556
|
-
* Sets the value or updates it using a getter function.
|
|
1557
|
-
*/
|
|
1558
|
-
set;
|
|
1559
|
-
/**
|
|
1560
|
-
* Creates a new SolidState with the given initial value.
|
|
1561
|
-
* @param initialValue - The initial value.
|
|
1562
|
-
*/
|
|
1563
|
-
constructor(initialValue) {
|
|
1564
|
-
const [get, set] = createSignal(initialValue);
|
|
1565
|
-
this.get = get;
|
|
1566
|
-
this.set = set;
|
|
1567
|
-
}
|
|
914
|
+
function subscribe(data, onData, onError, onPending) {
|
|
915
|
+
return new EffectNode(data, onData, onError, onPending);
|
|
1568
916
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
* Creates a new SolidDerivation from a getter function or value.
|
|
1576
|
-
* @param calculator - The getter function or value.
|
|
1577
|
-
*/
|
|
1578
|
-
constructor(calculator) {
|
|
1579
|
-
const get = createMemo(calculator);
|
|
1580
|
-
this.get = get;
|
|
917
|
+
|
|
918
|
+
class SignalNode extends Observable {
|
|
919
|
+
subscribe(onTrigger, onError, onPending) {
|
|
920
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
921
|
+
const effect = new EffectNode((t) => this.watch(t), onTrigger, onError, onPending);
|
|
922
|
+
return effect;
|
|
1581
923
|
}
|
|
1582
924
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
* Creates a new SolidResource from a fetcher function.
|
|
1603
|
-
* @param fetcher - The async fetcher function.
|
|
1604
|
-
*/
|
|
1605
|
-
constructor(fetcher) {
|
|
1606
|
-
const [get, set] = createResource(fetcher);
|
|
1607
|
-
this.get = get;
|
|
1608
|
-
this.state = () => get.state;
|
|
1609
|
-
this.latest = () => get.latest;
|
|
1610
|
-
this.refetch = () => set.refetch();
|
|
1611
|
-
this.set = (value) => set.mutate(value);
|
|
1612
|
-
}
|
|
925
|
+
|
|
926
|
+
function signal() {
|
|
927
|
+
return new SignalNode();
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function constant(initializer) {
|
|
931
|
+
return new ValueSyncNode(initializer);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function derivation(compute) {
|
|
935
|
+
return new ValueSyncNode(compute);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function state(valueOrInitializer) {
|
|
939
|
+
return new ValueSyncNode(valueOrInitializer);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function writableDerivation(compute) {
|
|
943
|
+
return new ValueSyncNode(compute);
|
|
1613
944
|
}
|
|
1614
945
|
|
|
1615
946
|
function fromNode(node) {
|
|
1616
|
-
|
|
1617
|
-
const solidResource = new SolidResource(async () => {
|
|
1618
|
-
let value;
|
|
1619
|
-
if (initialized) {
|
|
1620
|
-
value = await node.get(null);
|
|
1621
|
-
} else {
|
|
1622
|
-
value = await node.pick();
|
|
1623
|
-
initialized = true;
|
|
1624
|
-
}
|
|
1625
|
-
return value;
|
|
1626
|
-
});
|
|
947
|
+
const [resource, { refetch }] = createResource(() => node.pick());
|
|
1627
948
|
let fx;
|
|
1628
949
|
onMount(() => {
|
|
1629
|
-
fx =
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
950
|
+
fx = subscribe(
|
|
951
|
+
// data
|
|
952
|
+
(t) => node.get(t),
|
|
953
|
+
// onData
|
|
954
|
+
() => {
|
|
955
|
+
refetch();
|
|
956
|
+
},
|
|
957
|
+
// onError
|
|
958
|
+
() => {
|
|
959
|
+
refetch();
|
|
960
|
+
},
|
|
961
|
+
// onPending
|
|
962
|
+
() => {
|
|
963
|
+
refetch();
|
|
1633
964
|
}
|
|
1634
|
-
|
|
1635
|
-
|
|
965
|
+
);
|
|
966
|
+
});
|
|
967
|
+
createEffect(() => {
|
|
968
|
+
resource.error ?? resource();
|
|
969
|
+
resetErrorBoundaries();
|
|
1636
970
|
});
|
|
1637
971
|
onCleanup(() => fx.dispose());
|
|
1638
|
-
return
|
|
972
|
+
return resource;
|
|
1639
973
|
}
|
|
1640
974
|
function fromGetter(getter) {
|
|
1641
|
-
const derivation = new
|
|
975
|
+
const derivation = new ValueSyncNode((t) => {
|
|
1642
976
|
return getter(t);
|
|
1643
977
|
});
|
|
1644
978
|
return fromNode(derivation);
|
|
1645
979
|
}
|
|
1646
980
|
function from(flow) {
|
|
1647
|
-
if (flow instanceof
|
|
981
|
+
if (flow instanceof ValueAsyncNode || flow instanceof ValueSyncNode) {
|
|
1648
982
|
return fromNode(flow);
|
|
1649
983
|
}
|
|
1650
984
|
if (typeof flow === "function") {
|
|
@@ -1653,4 +987,4 @@ function from(flow) {
|
|
|
1653
987
|
throw new Error("Invalid flow type");
|
|
1654
988
|
}
|
|
1655
989
|
|
|
1656
|
-
export {
|
|
990
|
+
export { array, constant, constantAsync, derivation, derivationAsync, from, isDisposable, map, signal, state, stateAsync, subscribe, writableDerivation, writableDerivationAsync };
|