@ersbeth/picoflow 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +856 -1530
- 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 +28 -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 +35 -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 +22 -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 +37 -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 +2 -0
- package/dist/types/nodes/arrayNode.d.ts.map +1 -0
- package/dist/types/nodes/effectNode.d.ts +2 -0
- package/dist/types/nodes/effectNode.d.ts.map +1 -0
- package/dist/types/nodes/index.d.ts +9 -0
- package/dist/types/nodes/index.d.ts.map +1 -0
- package/dist/types/nodes/mapNode.d.ts +2 -0
- package/dist/types/nodes/mapNode.d.ts.map +1 -0
- package/dist/types/nodes/signalNode.d.ts +2 -0
- package/dist/types/nodes/signalNode.d.ts.map +1 -0
- package/dist/types/nodes/valueAsyncNode.d.ts +2 -0
- package/dist/types/nodes/valueAsyncNode.d.ts.map +1 -0
- package/dist/types/nodes/valueNode.d.ts +2 -0
- package/dist/types/nodes/valueNode.d.ts.map +1 -0
- package/dist/types/nodes/valueSyncNode.d.ts +2 -0
- package/dist/types/nodes/valueSyncNode.d.ts.map +1 -0
- package/dist/types/schedulers/asyncResolver.d.ts +2 -0
- package/dist/types/schedulers/asyncResolver.d.ts.map +1 -0
- package/dist/types/schedulers/asyncScheduler.d.ts +2 -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 +2 -0
- package/dist/types/schedulers/pendingError.d.ts.map +1 -0
- package/dist/types/schedulers/scheduler.d.ts +2 -0
- package/dist/types/schedulers/scheduler.d.ts.map +1 -0
- package/dist/types/schedulers/syncResolver.d.ts +2 -0
- package/dist/types/schedulers/syncResolver.d.ts.map +1 -0
- package/dist/types/schedulers/syncScheduler.d.ts +2 -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 +1 -81
- 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 +36 -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 +22 -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 +87 -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 +172 -0
- package/src/nodes/effectNode.ts +59 -0
- package/src/nodes/index.ts +8 -0
- package/src/nodes/mapNode.ts +127 -0
- package/src/nodes/signalNode.ts +21 -0
- package/src/nodes/valueAsyncNode.ts +88 -0
- package/src/nodes/valueNode.ts +144 -0
- package/src/nodes/valueSyncNode.ts +128 -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,976 @@
|
|
|
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
|
-
static
|
|
9
|
-
static
|
|
10
|
-
static
|
|
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
|
|
7
|
+
class ExecutionStack {
|
|
8
|
+
static _pendingQueue = [];
|
|
9
|
+
static _effectQueue = [];
|
|
10
|
+
static _executionScheduled;
|
|
11
|
+
static pushPending(node) {
|
|
12
|
+
ExecutionStack._scheduleExecution();
|
|
13
|
+
ExecutionStack._pendingQueue.push(node);
|
|
14
|
+
}
|
|
15
|
+
static pushEffect(effect) {
|
|
16
|
+
ExecutionStack._scheduleExecution();
|
|
17
|
+
ExecutionStack._effectQueue.push(effect);
|
|
18
|
+
}
|
|
19
|
+
static _scheduleExecution() {
|
|
20
|
+
if (ExecutionStack._executionScheduled) return;
|
|
21
|
+
ExecutionStack._executionScheduled = new Promise((resolve) => {
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
ExecutionStack._executionScheduled = void 0;
|
|
24
|
+
ExecutionStack._execute();
|
|
25
|
+
resolve();
|
|
26
|
+
}, 0);
|
|
52
27
|
});
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
28
|
+
}
|
|
29
|
+
static _execute() {
|
|
30
|
+
ExecutionStack._pendingQueue.forEach((node) => {
|
|
31
|
+
node.execute();
|
|
75
32
|
});
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
33
|
+
ExecutionStack._pendingQueue.length = 0;
|
|
34
|
+
ExecutionStack._effectQueue.forEach((effect) => {
|
|
35
|
+
effect.execute();
|
|
102
36
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Queues effects for execution after the current operation completes.
|
|
108
|
-
*
|
|
109
|
-
* @param effects - Array of effects to queue for execution.
|
|
110
|
-
*
|
|
111
|
-
* @remarks
|
|
112
|
-
* This method is used internally by reactive primitives to schedule effect
|
|
113
|
-
* execution. Effects are executed after the current read/write/trigger
|
|
114
|
-
* operation completes, ensuring proper ordering of reactive updates.
|
|
115
|
-
*
|
|
116
|
-
* @public
|
|
117
|
-
*/
|
|
118
|
-
static pushEffects(effects) {
|
|
119
|
-
FlowGraph._effectsQueue.push(...effects);
|
|
120
|
-
}
|
|
121
|
-
/** @internal */
|
|
122
|
-
static _processActionQueue = async () => {
|
|
123
|
-
if (FlowGraph._processingActionQueue) return;
|
|
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);
|
|
141
|
-
}
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
if (actionRequest.type === "write") {
|
|
145
|
-
try {
|
|
146
|
-
const updateResult = actionRequest.update();
|
|
147
|
-
let updated = false;
|
|
148
|
-
if (updateResult instanceof Promise) {
|
|
149
|
-
updated = await updateResult;
|
|
150
|
-
} else {
|
|
151
|
-
updated = updateResult;
|
|
152
|
-
}
|
|
153
|
-
if (!updated) {
|
|
154
|
-
actionRequest.resolve();
|
|
155
|
-
continue;
|
|
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
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (actionRequest.type === "trigger") {
|
|
165
|
-
actionRequest.notify();
|
|
166
|
-
}
|
|
167
|
-
let effectError = null;
|
|
168
|
-
for (const effect of FlowGraph._effectsQueue) {
|
|
169
|
-
try {
|
|
170
|
-
await effect._requestExec();
|
|
171
|
-
} catch (error) {
|
|
172
|
-
effectError = error instanceof Error ? error : new Error(String(error));
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
FlowGraph._effectsQueue = [];
|
|
177
|
-
if (effectError) {
|
|
178
|
-
actionRequest.reject(effectError);
|
|
179
|
-
} else {
|
|
180
|
-
actionRequest.resolve();
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
FlowGraph._processingActionQueue = false;
|
|
184
|
-
};
|
|
37
|
+
ExecutionStack._effectQueue.length = 0;
|
|
38
|
+
}
|
|
185
39
|
}
|
|
186
40
|
|
|
187
|
-
class
|
|
41
|
+
class Disposable {
|
|
188
42
|
_disposed = false;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Creates a new effect and runs it once immediately.
|
|
194
|
-
*
|
|
195
|
-
* @param apply - Side-effect function receiving the tracking context (`t`).
|
|
196
|
-
* It can be sync or async (returning `Promise<void>`).
|
|
197
|
-
*
|
|
198
|
-
* @remarks
|
|
199
|
-
* Use `t` to opt-in to reactive tracking:
|
|
200
|
-
* - `observable.get(t)` / `signal.watch(t)` to re-run when they change
|
|
201
|
-
* - `observable.pick()` for reads that should not re-run the effect
|
|
202
|
-
*
|
|
203
|
-
* The effect schedules its first run during construction. Each subsequent
|
|
204
|
-
* change to a tracked dependency queues another run.
|
|
205
|
-
*
|
|
206
|
-
* @public
|
|
207
|
-
*/
|
|
208
|
-
constructor(apply) {
|
|
209
|
-
this._apply = apply;
|
|
210
|
-
this.settled = FlowGraph.requestRead(() => this._exec());
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Stops the effect and detaches it from all tracked dependencies.
|
|
214
|
-
*
|
|
215
|
-
* @remarks
|
|
216
|
-
* Call this when the effect's work is no longer needed (e.g., component
|
|
217
|
-
* unmount, teardown of a feature). After disposal:
|
|
218
|
-
* - The effect will not re-run.
|
|
219
|
-
* - All dependency links are removed.
|
|
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
|
-
*/
|
|
43
|
+
get disposed() {
|
|
44
|
+
return this._disposed;
|
|
45
|
+
}
|
|
231
46
|
dispose() {
|
|
232
|
-
if (this._disposed) throw new Error("[PicoFlow]
|
|
233
|
-
Array.from(this._dependencies).forEach((dependency) => {
|
|
234
|
-
this._unregisterDependency(dependency);
|
|
235
|
-
});
|
|
47
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
236
48
|
this._disposed = true;
|
|
237
49
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return this.
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class Node extends Disposable {
|
|
53
|
+
_dependencies = /* @__PURE__ */ new Set();
|
|
54
|
+
_dependents = /* @__PURE__ */ new Set();
|
|
55
|
+
_status = "resolved";
|
|
56
|
+
get status() {
|
|
57
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
58
|
+
return this._status;
|
|
247
59
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this.
|
|
251
|
-
return this.settled;
|
|
60
|
+
set status(status) {
|
|
61
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
62
|
+
this._status = status;
|
|
252
63
|
}
|
|
253
|
-
|
|
254
|
-
|
|
64
|
+
registerDependency(dependency) {
|
|
65
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
255
66
|
this._dependencies.add(dependency);
|
|
256
|
-
dependency.
|
|
67
|
+
dependency.registerDependent(this);
|
|
257
68
|
}
|
|
258
|
-
|
|
259
|
-
|
|
69
|
+
unregisterDependency(dependency) {
|
|
70
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
260
71
|
this._dependencies.delete(dependency);
|
|
261
|
-
dependency.
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (this.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
72
|
+
dependency.unregisterDependent(this);
|
|
73
|
+
}
|
|
74
|
+
clearDependencies() {
|
|
75
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
76
|
+
this._dependencies.forEach((dependency) => {
|
|
77
|
+
dependency.unregisterDependent(this);
|
|
78
|
+
});
|
|
79
|
+
this._dependencies.clear();
|
|
80
|
+
}
|
|
81
|
+
registerDependent(dependent) {
|
|
82
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
83
|
+
this._dependents.add(dependent);
|
|
84
|
+
}
|
|
85
|
+
unregisterDependent(dependent) {
|
|
86
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
87
|
+
this._dependents.delete(dependent);
|
|
88
|
+
}
|
|
89
|
+
notifyDependents() {
|
|
90
|
+
if (this.disposed) return;
|
|
91
|
+
this._dependents.forEach((dependent) => {
|
|
92
|
+
dependent.notify();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
watch(tracker) {
|
|
96
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
97
|
+
tracker.registerDependency(this);
|
|
98
|
+
}
|
|
99
|
+
trigger() {
|
|
100
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
101
|
+
this.notifyDependents();
|
|
102
|
+
}
|
|
103
|
+
notify() {
|
|
104
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
105
|
+
if (this.status === "dirty") return;
|
|
106
|
+
this.status = "dirty";
|
|
107
|
+
this.notifyDependents();
|
|
108
|
+
}
|
|
109
|
+
dispose() {
|
|
110
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
111
|
+
Array.from(this._dependents).forEach((dependant) => {
|
|
112
|
+
dependant.unregisterDependency(this);
|
|
113
|
+
this.unregisterDependent(dependant);
|
|
114
|
+
});
|
|
115
|
+
Array.from(this._dependencies).forEach((dependency) => {
|
|
116
|
+
this.unregisterDependency(dependency);
|
|
117
|
+
});
|
|
118
|
+
this._disposed = true;
|
|
270
119
|
}
|
|
271
120
|
}
|
|
272
|
-
|
|
273
|
-
|
|
121
|
+
|
|
122
|
+
class Observable extends Disposable {
|
|
123
|
+
_dependents = /* @__PURE__ */ new Set();
|
|
124
|
+
_status = "resolved";
|
|
125
|
+
get status() {
|
|
126
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
127
|
+
return this._status;
|
|
128
|
+
}
|
|
129
|
+
set status(status) {
|
|
130
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
131
|
+
this._status = status;
|
|
132
|
+
}
|
|
133
|
+
registerDependent(dependent) {
|
|
134
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
135
|
+
this._dependents.add(dependent);
|
|
136
|
+
}
|
|
137
|
+
unregisterDependent(dependent) {
|
|
138
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
139
|
+
this._dependents.delete(dependent);
|
|
140
|
+
}
|
|
141
|
+
notifyDependents() {
|
|
142
|
+
if (this.disposed) return;
|
|
143
|
+
this._dependents.forEach((dependent) => {
|
|
144
|
+
dependent.notify();
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
watch(tracker) {
|
|
148
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
149
|
+
tracker.registerDependency(this);
|
|
150
|
+
}
|
|
151
|
+
trigger() {
|
|
152
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
153
|
+
this.notifyDependents();
|
|
154
|
+
}
|
|
155
|
+
dispose() {
|
|
156
|
+
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
157
|
+
Array.from(this._dependents).forEach((dependant) => {
|
|
158
|
+
dependant.unregisterDependency(this);
|
|
159
|
+
this.unregisterDependent(dependant);
|
|
160
|
+
});
|
|
161
|
+
this._disposed = true;
|
|
162
|
+
}
|
|
274
163
|
}
|
|
275
164
|
|
|
276
|
-
class
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
*
|
|
280
|
-
* @remarks
|
|
281
|
-
* Any effect/derivation that called `watch(t)` on this signal will re-run.
|
|
282
|
-
* Returns a promise that settles after notifications complete.
|
|
283
|
-
*
|
|
284
|
-
* @throws Error if the signal is disposed.
|
|
285
|
-
*
|
|
286
|
-
* @example
|
|
287
|
-
* ```typescript
|
|
288
|
-
* const $tick = signal();
|
|
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() {
|
|
165
|
+
class Observer extends Disposable {
|
|
166
|
+
_dependencies = /* @__PURE__ */ new Set();
|
|
167
|
+
registerDependency(dependency) {
|
|
301
168
|
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) {
|
|
169
|
+
this._dependencies.add(dependency);
|
|
170
|
+
dependency.registerDependent(this);
|
|
171
|
+
}
|
|
172
|
+
unregisterDependency(dependency) {
|
|
331
173
|
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
* @throws Error if the signal is already disposed.
|
|
344
|
-
*
|
|
345
|
-
* @example
|
|
346
|
-
* ```typescript
|
|
347
|
-
* const $refresh = signal();
|
|
348
|
-
* // ... use it
|
|
349
|
-
* $refresh.dispose();
|
|
350
|
-
* ```
|
|
351
|
-
*
|
|
352
|
-
* @public
|
|
353
|
-
*/
|
|
354
|
-
dispose(options) {
|
|
174
|
+
this._dependencies.delete(dependency);
|
|
175
|
+
dependency.unregisterDependent(this);
|
|
176
|
+
}
|
|
177
|
+
clearDependencies() {
|
|
178
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
179
|
+
this._dependencies.forEach((dependency) => {
|
|
180
|
+
dependency.unregisterDependent(this);
|
|
181
|
+
});
|
|
182
|
+
this._dependencies.clear();
|
|
183
|
+
}
|
|
184
|
+
dispose() {
|
|
355
185
|
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
356
|
-
if (options?.self) {
|
|
357
|
-
Array.from(this._effects).forEach((effect) => {
|
|
358
|
-
effect._unregisterDependency(this);
|
|
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
|
-
}
|
|
371
186
|
Array.from(this._dependencies).forEach((dependency) => {
|
|
372
|
-
this.
|
|
187
|
+
this.unregisterDependency(dependency);
|
|
373
188
|
});
|
|
374
189
|
this._disposed = true;
|
|
375
190
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
*/
|
|
385
|
-
get disposed() {
|
|
386
|
-
return this._disposed;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class PendingError extends Error {
|
|
194
|
+
pendingPromise;
|
|
195
|
+
constructor(promise) {
|
|
196
|
+
super("[PicoFlow] PendingResolver pending");
|
|
197
|
+
this.name = "PendingError";
|
|
198
|
+
this.pendingPromise = promise;
|
|
387
199
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
class AsyncResolver {
|
|
203
|
+
_compute;
|
|
204
|
+
_computed = Promise.withResolvers();
|
|
205
|
+
_iteration = 0;
|
|
206
|
+
_aborted = false;
|
|
207
|
+
_finished = false;
|
|
208
|
+
constructor(compute) {
|
|
209
|
+
this._compute = compute;
|
|
210
|
+
this._computed.promise.catch(() => {
|
|
211
|
+
}).finally(() => {
|
|
212
|
+
this._finished = true;
|
|
397
213
|
});
|
|
398
214
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
215
|
+
get computed() {
|
|
216
|
+
return this._computed.promise;
|
|
217
|
+
}
|
|
218
|
+
get aborted() {
|
|
219
|
+
return this._aborted;
|
|
220
|
+
}
|
|
221
|
+
get finished() {
|
|
222
|
+
return this._finished;
|
|
223
|
+
}
|
|
224
|
+
compute() {
|
|
225
|
+
if (this._finished) throw new Error("[Picoflow] PendingResolver: Can't restart a settled resolver");
|
|
226
|
+
if (this._aborted) throw new Error("[Picoflow] PendingResolver: Can't restart an aborted resolver");
|
|
227
|
+
this._iteration++;
|
|
228
|
+
const currentIteration = this._iteration;
|
|
229
|
+
this._compute().then((value) => {
|
|
230
|
+
if (this._iteration === currentIteration && !this._aborted) this._computed.resolve(value);
|
|
231
|
+
}).catch((error) => {
|
|
232
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
233
|
+
if (!(error instanceof PendingError)) {
|
|
234
|
+
this._computed.reject(error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
403
238
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
this.
|
|
407
|
-
|
|
239
|
+
overwrite(promise) {
|
|
240
|
+
if (this._finished) throw new Error("[Picoflow] PendingResolver: Can't overwrite a settled resolver");
|
|
241
|
+
if (this._aborted) throw new Error("[Picoflow] PendingResolver: Can't overwrite an aborted resolver");
|
|
242
|
+
this._iteration++;
|
|
243
|
+
const currentIteration = this._iteration;
|
|
244
|
+
promise.then((value) => {
|
|
245
|
+
if (this._iteration === currentIteration && !this._aborted) this._computed.resolve(value);
|
|
246
|
+
}).catch((error) => {
|
|
247
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
248
|
+
this._computed.reject(error);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
408
251
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
this.
|
|
252
|
+
abort() {
|
|
253
|
+
this._iteration++;
|
|
254
|
+
this._aborted = true;
|
|
412
255
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
class AsyncScheduler {
|
|
259
|
+
_compute;
|
|
260
|
+
_resolver;
|
|
261
|
+
_onResolve;
|
|
262
|
+
_onReject;
|
|
263
|
+
_settled = Promise.withResolvers();
|
|
264
|
+
_disposed = false;
|
|
265
|
+
constructor(compute, onResolve, onReject) {
|
|
266
|
+
this._compute = compute;
|
|
267
|
+
this._onResolve = onResolve;
|
|
268
|
+
this._onReject = onReject;
|
|
269
|
+
this._resolver = new AsyncResolver(compute);
|
|
270
|
+
this._resolver.computed.then(this._onResolve).catch(this._onReject).finally(() => this._settled.resolve());
|
|
416
271
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
this.
|
|
272
|
+
get settled() {
|
|
273
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
274
|
+
return this._settled.promise;
|
|
420
275
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
276
|
+
get disposed() {
|
|
277
|
+
return this._disposed;
|
|
278
|
+
}
|
|
279
|
+
overwrite(promise) {
|
|
280
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
281
|
+
if (this._resolver.finished) {
|
|
282
|
+
this._resolver = new AsyncResolver(this._compute);
|
|
283
|
+
this._settled = Promise.withResolvers();
|
|
284
|
+
this._resolver.computed.then(this._onResolve).catch(this._onReject).finally(() => this._settled.resolve());
|
|
285
|
+
}
|
|
286
|
+
this._resolver.overwrite(promise);
|
|
287
|
+
}
|
|
288
|
+
compute() {
|
|
289
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
290
|
+
if (this._resolver.finished) {
|
|
291
|
+
this._resolver = new AsyncResolver(this._compute);
|
|
292
|
+
this._settled = Promise.withResolvers();
|
|
293
|
+
this._resolver.computed.then(this._onResolve).catch(this._onReject).finally(() => this._settled.resolve());
|
|
294
|
+
}
|
|
295
|
+
this._resolver.compute();
|
|
296
|
+
}
|
|
297
|
+
dispose() {
|
|
298
|
+
this._resolver.abort();
|
|
299
|
+
this._disposed = true;
|
|
424
300
|
}
|
|
425
|
-
}
|
|
426
|
-
function signal() {
|
|
427
|
-
return new FlowSignal();
|
|
428
301
|
}
|
|
429
302
|
|
|
430
|
-
class
|
|
431
|
-
_promise;
|
|
432
|
-
_dirty = true;
|
|
303
|
+
class SyncResolver {
|
|
433
304
|
_compute;
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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;
|
|
305
|
+
_iteration = 0;
|
|
306
|
+
_aborted = false;
|
|
307
|
+
_finished = false;
|
|
308
|
+
_onValue;
|
|
309
|
+
_onError;
|
|
310
|
+
constructor(compute, onValue, onError) {
|
|
311
|
+
this._compute = compute;
|
|
312
|
+
this._onValue = onValue;
|
|
313
|
+
this._onError = onError;
|
|
314
|
+
}
|
|
315
|
+
get aborted() {
|
|
316
|
+
return this._aborted;
|
|
317
|
+
}
|
|
318
|
+
get finished() {
|
|
319
|
+
return this._finished;
|
|
320
|
+
}
|
|
321
|
+
compute() {
|
|
322
|
+
if (this._finished) throw new Error("[Picoflow] PendingResolver: Can't restart a settled resolver");
|
|
323
|
+
if (this._aborted) throw new Error("[Picoflow] PendingResolver: Can't restart an aborted resolver");
|
|
324
|
+
this._iteration++;
|
|
325
|
+
const currentIteration = this._iteration;
|
|
326
|
+
try {
|
|
327
|
+
const value = this._compute();
|
|
328
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
329
|
+
this._finished = true;
|
|
330
|
+
this._onValue(value);
|
|
331
|
+
}
|
|
332
|
+
} catch (error) {
|
|
333
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
334
|
+
if (!(error instanceof PendingError)) {
|
|
335
|
+
this._finished = true;
|
|
336
|
+
this._onError(error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
481
339
|
}
|
|
482
340
|
}
|
|
483
|
-
|
|
484
|
-
|
|
341
|
+
overwrite(value) {
|
|
342
|
+
if (this._finished) throw new Error("[Picoflow] PendingResolver: Can't overwrite a settled resolver");
|
|
343
|
+
if (this._aborted) throw new Error("[Picoflow] PendingResolver: Can't overwrite an aborted resolver");
|
|
344
|
+
this._iteration++;
|
|
345
|
+
const currentIteration = this._iteration;
|
|
346
|
+
if (this._iteration === currentIteration && !this._aborted) {
|
|
347
|
+
this._finished = true;
|
|
348
|
+
this._onValue(value);
|
|
349
|
+
}
|
|
485
350
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
*
|
|
505
|
-
* **Important:** This method returns a `Promise<T>`, not `T`. You must await the Promise to
|
|
506
|
-
* get the actual value. The Promise is cached until dependencies change, ensuring efficient
|
|
507
|
-
* access patterns.
|
|
508
|
-
*
|
|
509
|
-
* To read a value without creating a dependency, use `pick()` instead.
|
|
510
|
-
*
|
|
511
|
-
* @example
|
|
512
|
-
* ```typescript
|
|
513
|
-
* effect(async (t) => {
|
|
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;
|
|
351
|
+
abort() {
|
|
352
|
+
this._iteration++;
|
|
353
|
+
this._aborted = true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
class SyncScheduler {
|
|
358
|
+
_compute;
|
|
359
|
+
_resolver;
|
|
360
|
+
_onResolve;
|
|
361
|
+
_onReject;
|
|
362
|
+
_settled = Promise.withResolvers();
|
|
363
|
+
_disposed = false;
|
|
364
|
+
constructor(compute, onResolve, onReject) {
|
|
365
|
+
this._compute = compute;
|
|
366
|
+
this._onResolve = (value) => {
|
|
367
|
+
onResolve(value);
|
|
368
|
+
this._settled.resolve();
|
|
654
369
|
};
|
|
655
|
-
|
|
656
|
-
|
|
370
|
+
this._onReject = (error) => {
|
|
371
|
+
onReject(error);
|
|
372
|
+
this._settled.resolve();
|
|
657
373
|
};
|
|
658
|
-
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
|
|
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();
|
|
374
|
+
this._resolver = new SyncResolver(compute, this._onResolve, this._onReject);
|
|
375
|
+
}
|
|
376
|
+
get settled() {
|
|
377
|
+
return this._settled.promise;
|
|
752
378
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
379
|
+
overwrite(value) {
|
|
380
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
381
|
+
if (this._resolver.finished) {
|
|
382
|
+
this._settled = Promise.withResolvers();
|
|
383
|
+
this._resolver = new SyncResolver(this._compute, this._onResolve, this._onReject);
|
|
758
384
|
}
|
|
759
|
-
this.
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
if (
|
|
764
|
-
|
|
765
|
-
this.
|
|
766
|
-
return;
|
|
385
|
+
this._resolver.overwrite(value);
|
|
386
|
+
}
|
|
387
|
+
compute() {
|
|
388
|
+
if (this._disposed) throw new Error("[PicoFlow] ComputationScheduler is disposed");
|
|
389
|
+
if (this._resolver.finished) {
|
|
390
|
+
this._settled = Promise.withResolvers();
|
|
391
|
+
this._resolver = new SyncResolver(this._compute, this._onResolve, this._onReject);
|
|
767
392
|
}
|
|
768
|
-
this.
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
this.
|
|
772
|
-
|
|
773
|
-
const dependenciesToRemove = dependencies.filter(
|
|
774
|
-
(dependency) => !this._dependencies.has(dependency)
|
|
775
|
-
);
|
|
776
|
-
dependenciesToRemove.forEach((dependency) => {
|
|
777
|
-
dependency._unregisterDependency(this);
|
|
778
|
-
});
|
|
393
|
+
this._resolver.compute();
|
|
394
|
+
}
|
|
395
|
+
dispose() {
|
|
396
|
+
this._resolver.abort();
|
|
397
|
+
this._disposed = true;
|
|
779
398
|
}
|
|
780
399
|
}
|
|
781
400
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
function stateAsync(value) {
|
|
791
|
-
return new FlowNodeAsync(value);
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
class FlowNode extends FlowSignal {
|
|
795
|
-
_value;
|
|
796
|
-
_dirty = true;
|
|
797
|
-
_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) {
|
|
401
|
+
class EffectNode extends Observer {
|
|
402
|
+
_data;
|
|
403
|
+
_onData;
|
|
404
|
+
_onError;
|
|
405
|
+
_onPending;
|
|
406
|
+
constructor(data, onData, onError, onPending) {
|
|
834
407
|
super();
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
408
|
+
this._data = data;
|
|
409
|
+
this._onData = onData;
|
|
410
|
+
this._onError = onError;
|
|
411
|
+
this._onPending = onPending;
|
|
412
|
+
this.execute();
|
|
413
|
+
}
|
|
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
|
+
}
|
|
840
434
|
}
|
|
841
435
|
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
class ValueNode extends Node {
|
|
439
|
+
_value;
|
|
440
|
+
_error;
|
|
842
441
|
watch(tracker) {
|
|
843
|
-
if (this.
|
|
844
|
-
this._computeValue();
|
|
442
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
845
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
|
+
}
|
|
466
|
+
}
|
|
846
467
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
* @remarks
|
|
856
|
-
* Use `get(t)` within effects and derivations to create reactive dependencies. The tracker
|
|
857
|
-
* parameter must be provided from the reactive context (typically the `t` parameter in effect
|
|
858
|
-
* or derivation callbacks).
|
|
859
|
-
*
|
|
860
|
-
* To read a value without creating a dependency, use `pick()` instead.
|
|
861
|
-
*
|
|
862
|
-
* @example
|
|
863
|
-
* ```typescript
|
|
864
|
-
* effect((t) => {
|
|
865
|
-
* const tracked = $state.get(t); // Dependency registered
|
|
866
|
-
* const untracked = await $other.pick(); // No dependency
|
|
867
|
-
* });
|
|
868
|
-
* ```
|
|
869
|
-
*
|
|
870
|
-
* @throws Error if the node has been disposed.
|
|
871
|
-
*
|
|
872
|
-
* @public
|
|
873
|
-
*/
|
|
874
|
-
get(tracker) {
|
|
875
|
-
if (this._disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
876
|
-
this._computeValue();
|
|
877
|
-
if (tracker) super.watch(tracker);
|
|
878
|
-
return this._value;
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* Updates the node with a new value.
|
|
882
|
-
*
|
|
883
|
-
* @param value - A new value or a callback function that computes a new value based on the current value.
|
|
884
|
-
*
|
|
885
|
-
* @returns A promise that resolves after the update is processed and all dependent effects have been notified.
|
|
886
|
-
*
|
|
887
|
-
* @remarks
|
|
888
|
-
* This method can be used in two ways:
|
|
889
|
-
*
|
|
890
|
-
* **For mutable state nodes** (constructed with a constant value):
|
|
891
|
-
* - Updates the stored value directly
|
|
892
|
-
* - All dependents are notified of the change
|
|
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();
|
|
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();
|
|
1093
476
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
477
|
+
dispose() {
|
|
478
|
+
super.dispose();
|
|
479
|
+
this._scheduler.dispose();
|
|
480
|
+
}
|
|
481
|
+
get(tracker) {
|
|
482
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
483
|
+
super.watch(tracker);
|
|
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
|
+
}
|
|
1099
507
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
508
|
+
}
|
|
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
|
+
}
|
|
1108
540
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
this.
|
|
1112
|
-
this.
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
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;
|
|
1119
552
|
}
|
|
1120
553
|
}
|
|
1121
554
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
555
|
+
class ValueSyncNode extends ValueNode {
|
|
556
|
+
_scheduler;
|
|
557
|
+
_compute;
|
|
558
|
+
constructor(valueOrCompute) {
|
|
559
|
+
super();
|
|
560
|
+
if (typeof valueOrCompute === "function") {
|
|
561
|
+
this._compute = () => valueOrCompute(this, this._value);
|
|
562
|
+
} else {
|
|
563
|
+
this._compute = () => valueOrCompute;
|
|
564
|
+
}
|
|
565
|
+
this.status = "dirty";
|
|
566
|
+
this._scheduler = new SyncScheduler(
|
|
567
|
+
() => this._compute(),
|
|
568
|
+
(value) => this._onResolve(value),
|
|
569
|
+
(error) => this._onReject(error)
|
|
570
|
+
);
|
|
571
|
+
if (typeof valueOrCompute !== "function") {
|
|
572
|
+
this._scheduler.compute();
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
_onResolve(value) {
|
|
576
|
+
this.status = "resolved";
|
|
577
|
+
this._value = value;
|
|
578
|
+
this._error = void 0;
|
|
579
|
+
}
|
|
580
|
+
_onReject(error) {
|
|
581
|
+
this.status = "error";
|
|
582
|
+
this._error = error;
|
|
583
|
+
}
|
|
584
|
+
set(valueOrUpdater) {
|
|
585
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
586
|
+
if (typeof valueOrUpdater === "function") {
|
|
587
|
+
const updater = valueOrUpdater;
|
|
588
|
+
switch (this.status) {
|
|
589
|
+
case "resolved": {
|
|
590
|
+
const nextValue = updater(this._value);
|
|
591
|
+
if (nextValue === this._value) return;
|
|
592
|
+
this.status = "pending";
|
|
593
|
+
this._scheduler.overwrite(nextValue);
|
|
594
|
+
this.notifyDependents();
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
case "pending":
|
|
598
|
+
case "error":
|
|
599
|
+
case "dirty": {
|
|
600
|
+
const nextValue = updater(this._value);
|
|
601
|
+
this.status = "pending";
|
|
602
|
+
this._scheduler.overwrite(nextValue);
|
|
603
|
+
this.notifyDependents();
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
} else {
|
|
608
|
+
const nextValue = valueOrUpdater;
|
|
609
|
+
switch (this.status) {
|
|
610
|
+
case "resolved": {
|
|
611
|
+
if (nextValue === this._value) return;
|
|
612
|
+
this.status = "pending";
|
|
613
|
+
this._scheduler.overwrite(nextValue);
|
|
614
|
+
this.notifyDependents();
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
case "pending":
|
|
618
|
+
case "error":
|
|
619
|
+
case "dirty": {
|
|
620
|
+
this.status = "pending";
|
|
621
|
+
this._scheduler.overwrite(nextValue);
|
|
622
|
+
this.notifyDependents();
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
refresh() {
|
|
629
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
630
|
+
const currentValue = this._value;
|
|
631
|
+
const currentStatus = this.status;
|
|
632
|
+
this.execute();
|
|
633
|
+
switch (this.status) {
|
|
634
|
+
case "resolved": {
|
|
635
|
+
const nextValue = this._value;
|
|
636
|
+
if (nextValue === currentValue && currentStatus === this.status) return;
|
|
637
|
+
this.notifyDependents();
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
case "error":
|
|
641
|
+
case "pending":
|
|
642
|
+
case "dirty": {
|
|
643
|
+
this.notifyDependents();
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
1132
648
|
}
|
|
1133
649
|
|
|
1134
|
-
class
|
|
1135
|
-
/**
|
|
1136
|
-
* Last action performed on the FlowArray.
|
|
1137
|
-
* @public
|
|
1138
|
-
*/
|
|
650
|
+
class ArrayNode extends ValueSyncNode {
|
|
1139
651
|
$lastAction;
|
|
1140
|
-
/**
|
|
1141
|
-
* Creates an instance of FlowArray.
|
|
1142
|
-
* @param value - Initial array value.
|
|
1143
|
-
* @public
|
|
1144
|
-
*/
|
|
1145
652
|
constructor(value = []) {
|
|
1146
653
|
super(value);
|
|
1147
|
-
this.$lastAction =
|
|
654
|
+
this.$lastAction = new ValueSyncNode({
|
|
1148
655
|
type: "set",
|
|
1149
|
-
|
|
656
|
+
setItems: value,
|
|
657
|
+
clearedItems: []
|
|
1150
658
|
});
|
|
1151
659
|
}
|
|
1152
|
-
/**
|
|
1153
|
-
* Gets the current length of the array.
|
|
1154
|
-
* @returns The length of the array.
|
|
1155
|
-
* @public
|
|
1156
|
-
*/
|
|
1157
660
|
get length() {
|
|
1158
|
-
if (this.
|
|
661
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
1159
662
|
return this._value.length;
|
|
1160
663
|
}
|
|
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);
|
|
664
|
+
set(itemsOrUpdater) {
|
|
665
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
666
|
+
const previousValue = this._value;
|
|
667
|
+
super.set(itemsOrUpdater);
|
|
668
|
+
this.$lastAction.set({
|
|
669
|
+
type: "set",
|
|
670
|
+
setItems: this._value,
|
|
671
|
+
clearedItems: previousValue
|
|
672
|
+
});
|
|
673
|
+
return previousValue;
|
|
1235
674
|
}
|
|
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
|
-
|
|
675
|
+
update(index, item) {
|
|
676
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
677
|
+
if (index < 0 || index >= this._value.length) {
|
|
678
|
+
throw new Error("[PicoFlow] Index out of bounds");
|
|
679
|
+
}
|
|
680
|
+
const previousValue = this._value[index];
|
|
681
|
+
this._value[index] = item;
|
|
682
|
+
this.notifyDependents();
|
|
683
|
+
this.$lastAction.set({
|
|
684
|
+
type: "update",
|
|
685
|
+
index,
|
|
686
|
+
setItem: item,
|
|
687
|
+
clearedItem: previousValue
|
|
688
|
+
});
|
|
689
|
+
return previousValue;
|
|
690
|
+
}
|
|
691
|
+
push(item) {
|
|
692
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
693
|
+
this._value.push(item);
|
|
694
|
+
this.notifyDependents();
|
|
695
|
+
this.$lastAction.set({ type: "push", addedItem: item });
|
|
696
|
+
}
|
|
697
|
+
pop() {
|
|
698
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
699
|
+
const item = this._value.pop();
|
|
700
|
+
this.notifyDependents();
|
|
701
|
+
this.$lastAction.set({ type: "pop", removedItem: item });
|
|
702
|
+
return item;
|
|
703
|
+
}
|
|
704
|
+
unshift(item) {
|
|
705
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
706
|
+
this._value.unshift(item);
|
|
707
|
+
this.notifyDependents();
|
|
708
|
+
this.$lastAction.set({ type: "unshift", addedItem: item });
|
|
709
|
+
}
|
|
710
|
+
shift() {
|
|
711
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
712
|
+
const item = this._value.shift();
|
|
713
|
+
this.notifyDependents();
|
|
714
|
+
this.$lastAction.set({ type: "shift", removedItem: item });
|
|
715
|
+
return item;
|
|
716
|
+
}
|
|
717
|
+
splice(start, deleteCount, ...newItems) {
|
|
718
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
719
|
+
const items = this._value.splice(start, deleteCount, ...newItems);
|
|
720
|
+
this.notifyDependents();
|
|
721
|
+
this.$lastAction.set({
|
|
722
|
+
type: "splice",
|
|
723
|
+
start,
|
|
724
|
+
deleteCount,
|
|
725
|
+
addedItems: newItems,
|
|
726
|
+
removedItems: items
|
|
727
|
+
});
|
|
728
|
+
return items;
|
|
1274
729
|
}
|
|
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);
|
|
730
|
+
clear() {
|
|
731
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
732
|
+
const previousValue = [...this._value];
|
|
733
|
+
this._value = [];
|
|
734
|
+
this.notifyDependents();
|
|
735
|
+
this.$lastAction.set({ type: "clear", clearedItems: previousValue });
|
|
736
|
+
return previousValue;
|
|
1323
737
|
}
|
|
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);
|
|
738
|
+
dispose() {
|
|
739
|
+
super.dispose();
|
|
1351
740
|
this._value.forEach((item) => {
|
|
1352
|
-
if (
|
|
741
|
+
if (item instanceof Disposable) item.dispose();
|
|
1353
742
|
});
|
|
1354
743
|
this._value = [];
|
|
1355
744
|
}
|
|
1356
|
-
/* INTERNAL */
|
|
1357
|
-
}
|
|
1358
|
-
function array(initial) {
|
|
1359
|
-
return new FlowArray(initial);
|
|
1360
745
|
}
|
|
1361
746
|
|
|
1362
|
-
class
|
|
1363
|
-
/**
|
|
1364
|
-
* Last action performed on the FlowMap.
|
|
1365
|
-
* @public
|
|
1366
|
-
*/
|
|
747
|
+
class MapNode extends ValueSyncNode {
|
|
1367
748
|
$lastAction;
|
|
1368
|
-
/**
|
|
1369
|
-
* Creates an instance of FlowMap.
|
|
1370
|
-
* @param value - Initial map value.
|
|
1371
|
-
* @public
|
|
1372
|
-
*/
|
|
1373
749
|
constructor(value = /* @__PURE__ */ new Map()) {
|
|
1374
750
|
super(value);
|
|
1375
|
-
this.$lastAction =
|
|
751
|
+
this.$lastAction = new ValueSyncNode({
|
|
1376
752
|
type: "set",
|
|
1377
|
-
|
|
753
|
+
setMap: value,
|
|
754
|
+
clearedMap: /* @__PURE__ */ new Map()
|
|
1378
755
|
});
|
|
1379
756
|
}
|
|
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);
|
|
757
|
+
add(key, value) {
|
|
758
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
759
|
+
const previousValue = this._value.get(key);
|
|
760
|
+
if (previousValue) {
|
|
761
|
+
throw new Error("[PicoFlow] Key already exists");
|
|
762
|
+
}
|
|
763
|
+
this._value.set(key, value);
|
|
764
|
+
this.notifyDependents();
|
|
765
|
+
this.$lastAction.set({ type: "add", key, addedValue: value });
|
|
766
|
+
}
|
|
767
|
+
update(key, value) {
|
|
768
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
769
|
+
const previousValue = this._value.get(key);
|
|
770
|
+
if (!previousValue) throw new Error("[PicoFlow] Key does not exist");
|
|
771
|
+
this._value.set(key, value);
|
|
772
|
+
this.notifyDependents();
|
|
773
|
+
this.$lastAction.set({
|
|
774
|
+
type: "update",
|
|
775
|
+
key,
|
|
776
|
+
setValue: value,
|
|
777
|
+
clearedValue: previousValue
|
|
778
|
+
});
|
|
779
|
+
return previousValue;
|
|
780
|
+
}
|
|
781
|
+
delete(key) {
|
|
782
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
783
|
+
const value = this._value.get(key);
|
|
784
|
+
if (value === void 0) throw new Error("[PicoFlow] Key does not exist");
|
|
785
|
+
this._value.delete(key);
|
|
786
|
+
this.notifyDependents();
|
|
787
|
+
this.$lastAction.set({ type: "delete", key, removedValue: value });
|
|
788
|
+
return value;
|
|
789
|
+
}
|
|
790
|
+
set(mapOrUpdater) {
|
|
791
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
792
|
+
const previousValue = this._value;
|
|
793
|
+
super.set(mapOrUpdater);
|
|
794
|
+
this.$lastAction.set({
|
|
795
|
+
type: "set",
|
|
796
|
+
setMap: this._value,
|
|
797
|
+
clearedMap: previousValue
|
|
798
|
+
});
|
|
799
|
+
return previousValue;
|
|
800
|
+
}
|
|
801
|
+
clear() {
|
|
802
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
803
|
+
const previousValue = this._value;
|
|
804
|
+
this._value = /* @__PURE__ */ new Map();
|
|
805
|
+
this.notifyDependents();
|
|
806
|
+
this.$lastAction.set({ type: "clear", clearedMap: previousValue });
|
|
807
|
+
return previousValue;
|
|
808
|
+
}
|
|
809
|
+
dispose() {
|
|
810
|
+
super.dispose();
|
|
1535
811
|
this._value.forEach((item) => {
|
|
1536
|
-
if (
|
|
812
|
+
if (item instanceof Disposable) item.dispose();
|
|
1537
813
|
});
|
|
1538
814
|
this._value.clear();
|
|
1539
815
|
}
|
|
1540
816
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
817
|
+
|
|
818
|
+
class SignalNode extends Observable {
|
|
819
|
+
subscribe(onTrigger, onError, onPending) {
|
|
820
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
821
|
+
const effect = new EffectNode((t) => this.watch(t), onTrigger, onError, onPending);
|
|
822
|
+
return effect;
|
|
1544
823
|
}
|
|
1545
|
-
return new FlowMap(
|
|
1546
|
-
new Map(initial ? Object.entries(initial) : [])
|
|
1547
|
-
);
|
|
1548
824
|
}
|
|
1549
825
|
|
|
1550
|
-
class
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
this.set = set;
|
|
826
|
+
class ValueAsyncNode extends ValueNode {
|
|
827
|
+
_scheduler;
|
|
828
|
+
_compute;
|
|
829
|
+
constructor(promiseOrCompute) {
|
|
830
|
+
super();
|
|
831
|
+
if (typeof promiseOrCompute === "function") {
|
|
832
|
+
this._compute = () => promiseOrCompute(this, this._value);
|
|
833
|
+
} else {
|
|
834
|
+
this._compute = () => promiseOrCompute;
|
|
835
|
+
}
|
|
836
|
+
this.status = "dirty";
|
|
837
|
+
this._scheduler = new AsyncScheduler(
|
|
838
|
+
() => this._compute(),
|
|
839
|
+
(value) => this._onResolve(value),
|
|
840
|
+
(error) => this._onReject(error)
|
|
841
|
+
);
|
|
1567
842
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
this.
|
|
843
|
+
_onResolve(value) {
|
|
844
|
+
this.status = "resolved";
|
|
845
|
+
this._value = value;
|
|
846
|
+
this._error = void 0;
|
|
847
|
+
this.notifyDependents();
|
|
848
|
+
}
|
|
849
|
+
_onReject(error) {
|
|
850
|
+
this.status = "error";
|
|
851
|
+
this._error = error;
|
|
852
|
+
this.notifyDependents();
|
|
853
|
+
}
|
|
854
|
+
set(promiseOrUpdater) {
|
|
855
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
856
|
+
if (typeof promiseOrUpdater === "function") {
|
|
857
|
+
const updater = promiseOrUpdater;
|
|
858
|
+
switch (this.status) {
|
|
859
|
+
case "resolved": {
|
|
860
|
+
this.status = "pending";
|
|
861
|
+
this._scheduler.overwrite(updater(this._value));
|
|
862
|
+
this.notifyDependents();
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
case "pending":
|
|
866
|
+
case "error":
|
|
867
|
+
case "dirty": {
|
|
868
|
+
this.status = "pending";
|
|
869
|
+
this._scheduler.overwrite(updater(this._value));
|
|
870
|
+
this.notifyDependents();
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
} else {
|
|
875
|
+
this.status = "pending";
|
|
876
|
+
const promise = promiseOrUpdater;
|
|
877
|
+
this._scheduler.overwrite(promise);
|
|
878
|
+
this.notifyDependents();
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
refresh() {
|
|
882
|
+
if (this.disposed) throw new Error("[PicoFlow] Primitive is disposed");
|
|
883
|
+
this.execute();
|
|
1581
884
|
}
|
|
1582
885
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
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);
|
|
886
|
+
|
|
887
|
+
function constantAsync(valueOrInitializer) {
|
|
888
|
+
return new ValueAsyncNode(valueOrInitializer);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function derivationAsync(compute) {
|
|
892
|
+
return new ValueAsyncNode(compute);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function stateAsync(valueOrInitializer) {
|
|
896
|
+
return new ValueAsyncNode(valueOrInitializer);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function writableDerivationAsync(compute) {
|
|
900
|
+
return new ValueAsyncNode(compute);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function array(initial) {
|
|
904
|
+
return new ArrayNode(initial);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
function map(initial) {
|
|
908
|
+
if (initial instanceof Map) {
|
|
909
|
+
return new MapNode(initial);
|
|
1612
910
|
}
|
|
911
|
+
return new MapNode(new Map(initial ? Object.entries(initial) : []));
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function subscribe(data, onData, onError, onPending) {
|
|
915
|
+
return new EffectNode(data, onData, onError, onPending);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
function signal() {
|
|
919
|
+
return new SignalNode();
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function constant(initializer) {
|
|
923
|
+
return new ValueSyncNode(initializer);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function derivation(compute) {
|
|
927
|
+
return new ValueSyncNode(compute);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function state(valueOrInitializer) {
|
|
931
|
+
return new ValueSyncNode(valueOrInitializer);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function writableDerivation(compute) {
|
|
935
|
+
return new ValueSyncNode(compute);
|
|
1613
936
|
}
|
|
1614
937
|
|
|
1615
938
|
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
|
-
});
|
|
939
|
+
const [resource, { refetch }] = createResource(() => node.pick());
|
|
1627
940
|
let fx;
|
|
1628
941
|
onMount(() => {
|
|
1629
|
-
fx =
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
942
|
+
fx = subscribe(
|
|
943
|
+
// data
|
|
944
|
+
(t) => node.get(t),
|
|
945
|
+
// onData
|
|
946
|
+
() => {
|
|
947
|
+
refetch();
|
|
948
|
+
},
|
|
949
|
+
// onError
|
|
950
|
+
() => {
|
|
951
|
+
refetch();
|
|
952
|
+
},
|
|
953
|
+
// onPending
|
|
954
|
+
() => {
|
|
955
|
+
refetch();
|
|
1633
956
|
}
|
|
1634
|
-
|
|
1635
|
-
|
|
957
|
+
);
|
|
958
|
+
});
|
|
959
|
+
createEffect(() => {
|
|
960
|
+
resource.error ?? resource();
|
|
961
|
+
resetErrorBoundaries();
|
|
1636
962
|
});
|
|
1637
963
|
onCleanup(() => fx.dispose());
|
|
1638
|
-
return
|
|
964
|
+
return resource;
|
|
1639
965
|
}
|
|
1640
966
|
function fromGetter(getter) {
|
|
1641
|
-
const derivation = new
|
|
967
|
+
const derivation = new ValueSyncNode((t) => {
|
|
1642
968
|
return getter(t);
|
|
1643
969
|
});
|
|
1644
970
|
return fromNode(derivation);
|
|
1645
971
|
}
|
|
1646
972
|
function from(flow) {
|
|
1647
|
-
if (flow instanceof
|
|
973
|
+
if (flow instanceof ValueAsyncNode || flow instanceof ValueSyncNode) {
|
|
1648
974
|
return fromNode(flow);
|
|
1649
975
|
}
|
|
1650
976
|
if (typeof flow === "function") {
|
|
@@ -1653,4 +979,4 @@ function from(flow) {
|
|
|
1653
979
|
throw new Error("Invalid flow type");
|
|
1654
980
|
}
|
|
1655
981
|
|
|
1656
|
-
export {
|
|
982
|
+
export { array, constant, constantAsync, derivation, derivationAsync, from, isDisposable, map, signal, state, stateAsync, subscribe, writableDerivation, writableDerivationAsync };
|