@typed/fx 2.0.0-beta.0 → 2.0.0-beta.10
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/LICENSE +21 -0
- package/README.md +58 -21
- package/dist/Fx/Fx.d.ts +371 -19
- package/dist/Fx/Fx.d.ts.map +1 -1
- package/dist/Fx/Fx.js +37 -6
- package/dist/Fx/TypeId.d.ts +52 -6
- package/dist/Fx/TypeId.d.ts.map +1 -1
- package/dist/Fx/TypeId.js +41 -4
- package/dist/Fx/combinators/additive.d.ts +245 -0
- package/dist/Fx/combinators/additive.d.ts.map +1 -0
- package/dist/Fx/combinators/additive.js +243 -0
- package/dist/Fx/combinators/catch.d.ts +246 -12
- package/dist/Fx/combinators/catch.d.ts.map +1 -1
- package/dist/Fx/combinators/catch.js +236 -12
- package/dist/Fx/combinators/causes.d.ts +25 -2
- package/dist/Fx/combinators/causes.d.ts.map +1 -1
- package/dist/Fx/combinators/causes.js +25 -2
- package/dist/Fx/combinators/changesWithEffect.d.ts +40 -0
- package/dist/Fx/combinators/changesWithEffect.d.ts.map +1 -0
- package/dist/Fx/combinators/changesWithEffect.js +48 -0
- package/dist/Fx/combinators/compact.d.ts +18 -1
- package/dist/Fx/combinators/compact.d.ts.map +1 -1
- package/dist/Fx/combinators/compact.js +18 -1
- package/dist/Fx/combinators/concatMap.d.ts +86 -0
- package/dist/Fx/combinators/concatMap.d.ts.map +1 -0
- package/dist/Fx/combinators/concatMap.js +86 -0
- package/dist/Fx/combinators/continueWith.d.ts +113 -5
- package/dist/Fx/combinators/continueWith.d.ts.map +1 -1
- package/dist/Fx/combinators/continueWith.js +113 -5
- package/dist/Fx/combinators/debounce.d.ts +34 -0
- package/dist/Fx/combinators/debounce.d.ts.map +1 -0
- package/dist/Fx/combinators/debounce.js +35 -0
- package/dist/Fx/combinators/delay.d.ts +36 -0
- package/dist/Fx/combinators/delay.d.ts.map +1 -0
- package/dist/Fx/combinators/delay.js +33 -0
- package/dist/Fx/combinators/dropUntil.d.ts +71 -0
- package/dist/Fx/combinators/dropUntil.d.ts.map +1 -0
- package/dist/Fx/combinators/dropUntil.js +65 -0
- package/dist/Fx/combinators/during.d.ts +38 -0
- package/dist/Fx/combinators/during.d.ts.map +1 -0
- package/dist/Fx/combinators/during.js +85 -0
- package/dist/Fx/combinators/ensuring.d.ts +34 -4
- package/dist/Fx/combinators/ensuring.d.ts.map +1 -1
- package/dist/Fx/combinators/ensuring.js +34 -4
- package/dist/Fx/combinators/exhaustLatestMap.d.ts +41 -5
- package/dist/Fx/combinators/exhaustLatestMap.d.ts.map +1 -1
- package/dist/Fx/combinators/exhaustLatestMap.js +41 -5
- package/dist/Fx/combinators/exhaustLatestMapEffect.d.ts +40 -4
- package/dist/Fx/combinators/exhaustLatestMapEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/exhaustLatestMapEffect.js +40 -4
- package/dist/Fx/combinators/exhaustMap.d.ts +38 -1
- package/dist/Fx/combinators/exhaustMap.d.ts.map +1 -1
- package/dist/Fx/combinators/exhaustMap.js +38 -1
- package/dist/Fx/combinators/exhaustMapEffect.d.ts +33 -1
- package/dist/Fx/combinators/exhaustMapEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/exhaustMapEffect.js +33 -1
- package/dist/Fx/combinators/exit.d.ts +23 -2
- package/dist/Fx/combinators/exit.d.ts.map +1 -1
- package/dist/Fx/combinators/exit.js +23 -2
- package/dist/Fx/combinators/filter.d.ts +17 -1
- package/dist/Fx/combinators/filter.d.ts.map +1 -1
- package/dist/Fx/combinators/filter.js +17 -1
- package/dist/Fx/combinators/filterEffect.d.ts +22 -1
- package/dist/Fx/combinators/filterEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/filterEffect.js +22 -1
- package/dist/Fx/combinators/filterMap.d.ts +20 -1
- package/dist/Fx/combinators/filterMap.d.ts.map +1 -1
- package/dist/Fx/combinators/filterMap.js +20 -1
- package/dist/Fx/combinators/filterMapEffect.d.ts +21 -1
- package/dist/Fx/combinators/filterMapEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/filterMapEffect.js +21 -1
- package/dist/Fx/combinators/filterMapLoop.d.ts +20 -1
- package/dist/Fx/combinators/filterMapLoop.d.ts.map +1 -1
- package/dist/Fx/combinators/filterMapLoop.js +20 -1
- package/dist/Fx/combinators/filterMapLoopCause.d.ts +21 -1
- package/dist/Fx/combinators/filterMapLoopCause.d.ts.map +1 -1
- package/dist/Fx/combinators/filterMapLoopCause.js +21 -1
- package/dist/Fx/combinators/filterMapLoopCauseEffect.d.ts +21 -1
- package/dist/Fx/combinators/filterMapLoopCauseEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/filterMapLoopCauseEffect.js +21 -1
- package/dist/Fx/combinators/filterMapLoopEffect.d.ts +21 -1
- package/dist/Fx/combinators/filterMapLoopEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/filterMapLoopEffect.js +21 -1
- package/dist/Fx/combinators/flatMap.d.ts +50 -5
- package/dist/Fx/combinators/flatMap.d.ts.map +1 -1
- package/dist/Fx/combinators/flatMap.js +36 -3
- package/dist/Fx/combinators/flatMapConcurrently.d.ts +51 -4
- package/dist/Fx/combinators/flatMapConcurrently.d.ts.map +1 -1
- package/dist/Fx/combinators/flatMapConcurrently.js +51 -4
- package/dist/Fx/combinators/flatMapConcurrentlyEffect.d.ts +48 -4
- package/dist/Fx/combinators/flatMapConcurrentlyEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/flatMapConcurrentlyEffect.js +40 -2
- package/dist/Fx/combinators/flatMapEffect.d.ts +46 -4
- package/dist/Fx/combinators/flatMapEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/flatMapEffect.js +32 -2
- package/dist/Fx/combinators/flip.d.ts +34 -2
- package/dist/Fx/combinators/flip.d.ts.map +1 -1
- package/dist/Fx/combinators/flip.js +34 -2
- package/dist/Fx/combinators/gen.d.ts +29 -10
- package/dist/Fx/combinators/gen.d.ts.map +1 -1
- package/dist/Fx/combinators/gen.js +28 -4
- package/dist/Fx/combinators/genScoped.d.ts +33 -10
- package/dist/Fx/combinators/genScoped.d.ts.map +1 -1
- package/dist/Fx/combinators/genScoped.js +31 -3
- package/dist/Fx/combinators/grouped.d.ts +89 -0
- package/dist/Fx/combinators/grouped.d.ts.map +1 -0
- package/dist/Fx/combinators/grouped.js +146 -0
- package/dist/Fx/combinators/index.d.ts +45 -0
- package/dist/Fx/combinators/index.d.ts.map +1 -1
- package/dist/Fx/combinators/index.js +45 -0
- package/dist/Fx/combinators/keyed.d.ts +124 -10
- package/dist/Fx/combinators/keyed.d.ts.map +1 -1
- package/dist/Fx/combinators/keyed.js +69 -11
- package/dist/Fx/combinators/loop.d.ts +17 -1
- package/dist/Fx/combinators/loop.d.ts.map +1 -1
- package/dist/Fx/combinators/loop.js +17 -1
- package/dist/Fx/combinators/loopCause.d.ts +18 -1
- package/dist/Fx/combinators/loopCause.d.ts.map +1 -1
- package/dist/Fx/combinators/loopCause.js +18 -1
- package/dist/Fx/combinators/loopCauseEffect.d.ts +19 -1
- package/dist/Fx/combinators/loopCauseEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/loopCauseEffect.js +19 -1
- package/dist/Fx/combinators/loopEffect.d.ts +24 -1
- package/dist/Fx/combinators/loopEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/loopEffect.js +24 -1
- package/dist/Fx/combinators/map.d.ts +46 -1
- package/dist/Fx/combinators/map.d.ts.map +1 -1
- package/dist/Fx/combinators/map.js +43 -1
- package/dist/Fx/combinators/mapBoth.d.ts +40 -0
- package/dist/Fx/combinators/mapBoth.d.ts.map +1 -0
- package/dist/Fx/combinators/mapBoth.js +33 -0
- package/dist/Fx/combinators/mapEffect.d.ts +25 -1
- package/dist/Fx/combinators/mapEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/mapEffect.js +25 -1
- package/dist/Fx/combinators/mapError.d.ts +33 -0
- package/dist/Fx/combinators/mapError.d.ts.map +1 -0
- package/dist/Fx/combinators/mapError.js +32 -0
- package/dist/Fx/combinators/mergeAll.d.ts +35 -1
- package/dist/Fx/combinators/mergeAll.d.ts.map +1 -1
- package/dist/Fx/combinators/mergeAll.js +35 -1
- package/dist/Fx/combinators/mergeOrdered.d.ts +36 -4
- package/dist/Fx/combinators/mergeOrdered.d.ts.map +1 -1
- package/dist/Fx/combinators/mergeOrdered.js +68 -14
- package/dist/Fx/combinators/onError.d.ts +34 -4
- package/dist/Fx/combinators/onError.d.ts.map +1 -1
- package/dist/Fx/combinators/onError.js +34 -4
- package/dist/Fx/combinators/onExit.d.ts +29 -3
- package/dist/Fx/combinators/onExit.d.ts.map +1 -1
- package/dist/Fx/combinators/onExit.js +29 -3
- package/dist/Fx/combinators/onInterrupt.d.ts +49 -4
- package/dist/Fx/combinators/onInterrupt.d.ts.map +1 -1
- package/dist/Fx/combinators/onInterrupt.js +49 -4
- package/dist/Fx/combinators/pairwise.d.ts +28 -0
- package/dist/Fx/combinators/pairwise.d.ts.map +1 -0
- package/dist/Fx/combinators/pairwise.js +31 -0
- package/dist/Fx/combinators/provide.d.ts +162 -8
- package/dist/Fx/combinators/provide.d.ts.map +1 -1
- package/dist/Fx/combinators/provide.js +154 -6
- package/dist/Fx/combinators/race.d.ts +92 -0
- package/dist/Fx/combinators/race.d.ts.map +1 -0
- package/dist/Fx/combinators/race.js +149 -0
- package/dist/Fx/combinators/repeat.d.ts +41 -0
- package/dist/Fx/combinators/repeat.d.ts.map +1 -0
- package/dist/Fx/combinators/repeat.js +57 -0
- package/dist/Fx/combinators/result.d.ts +45 -0
- package/dist/Fx/combinators/result.d.ts.map +1 -0
- package/dist/Fx/combinators/result.js +46 -0
- package/dist/Fx/combinators/retry.d.ts +53 -0
- package/dist/Fx/combinators/retry.d.ts.map +1 -0
- package/dist/Fx/combinators/retry.js +71 -0
- package/dist/Fx/combinators/sample.d.ts +31 -0
- package/dist/Fx/combinators/sample.d.ts.map +1 -0
- package/dist/Fx/combinators/sample.js +42 -0
- package/dist/Fx/combinators/scan.d.ts +72 -0
- package/dist/Fx/combinators/scan.d.ts.map +1 -0
- package/dist/Fx/combinators/scan.js +77 -0
- package/dist/Fx/combinators/since.d.ts +30 -0
- package/dist/Fx/combinators/since.d.ts.map +1 -0
- package/dist/Fx/combinators/since.js +39 -0
- package/dist/Fx/combinators/skip.d.ts +45 -1
- package/dist/Fx/combinators/skip.d.ts.map +1 -1
- package/dist/Fx/combinators/skip.js +43 -1
- package/dist/Fx/combinators/skipRepeats.d.ts +17 -1
- package/dist/Fx/combinators/skipRepeats.d.ts.map +1 -1
- package/dist/Fx/combinators/skipRepeats.js +17 -1
- package/dist/Fx/combinators/skipRepeatsWith.d.ts +18 -1
- package/dist/Fx/combinators/skipRepeatsWith.d.ts.map +1 -1
- package/dist/Fx/combinators/skipRepeatsWith.js +18 -1
- package/dist/Fx/combinators/skipWhile.d.ts +117 -0
- package/dist/Fx/combinators/skipWhile.d.ts.map +1 -0
- package/dist/Fx/combinators/skipWhile.js +134 -0
- package/dist/Fx/combinators/slice.d.ts +97 -2
- package/dist/Fx/combinators/slice.d.ts.map +1 -1
- package/dist/Fx/combinators/slice.js +45 -1
- package/dist/Fx/combinators/switchMap.d.ts +39 -1
- package/dist/Fx/combinators/switchMap.d.ts.map +1 -1
- package/dist/Fx/combinators/switchMap.js +43 -5
- package/dist/Fx/combinators/switchMapEffect.d.ts +38 -1
- package/dist/Fx/combinators/switchMapEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/switchMapEffect.js +38 -1
- package/dist/Fx/combinators/take.d.ts +46 -1
- package/dist/Fx/combinators/take.d.ts.map +1 -1
- package/dist/Fx/combinators/take.js +44 -1
- package/dist/Fx/combinators/takeUntil.d.ts +62 -2
- package/dist/Fx/combinators/takeUntil.d.ts.map +1 -1
- package/dist/Fx/combinators/takeUntil.js +62 -2
- package/dist/Fx/combinators/takeWhile.d.ts +60 -0
- package/dist/Fx/combinators/takeWhile.d.ts.map +1 -0
- package/dist/Fx/combinators/takeWhile.js +54 -0
- package/dist/Fx/combinators/tapEffect.d.ts +17 -1
- package/dist/Fx/combinators/tapEffect.d.ts.map +1 -1
- package/dist/Fx/combinators/tapEffect.js +17 -1
- package/dist/Fx/combinators/throttle.d.ts +79 -0
- package/dist/Fx/combinators/throttle.d.ts.map +1 -0
- package/dist/Fx/combinators/throttle.js +99 -0
- package/dist/Fx/combinators/timeout.d.ts +76 -0
- package/dist/Fx/combinators/timeout.d.ts.map +1 -0
- package/dist/Fx/combinators/timeout.js +110 -0
- package/dist/Fx/combinators/tuple.d.ts +32 -2
- package/dist/Fx/combinators/tuple.d.ts.map +1 -1
- package/dist/Fx/combinators/tuple.js +36 -5
- package/dist/Fx/combinators/until.d.ts +27 -0
- package/dist/Fx/combinators/until.d.ts.map +1 -0
- package/dist/Fx/combinators/until.js +56 -0
- package/dist/Fx/combinators/unwrap.d.ts +31 -2
- package/dist/Fx/combinators/unwrap.d.ts.map +1 -1
- package/dist/Fx/combinators/unwrap.js +30 -1
- package/dist/Fx/combinators/unwrapScoped.d.ts +34 -1
- package/dist/Fx/combinators/unwrapScoped.d.ts.map +1 -1
- package/dist/Fx/combinators/unwrapScoped.js +34 -1
- package/dist/Fx/combinators/when.d.ts +37 -2
- package/dist/Fx/combinators/when.d.ts.map +1 -1
- package/dist/Fx/combinators/when.js +38 -3
- package/dist/Fx/combinators/withLatestFrom.d.ts +58 -0
- package/dist/Fx/combinators/withLatestFrom.d.ts.map +1 -0
- package/dist/Fx/combinators/withLatestFrom.js +68 -0
- package/dist/Fx/combinators/withSpan.d.ts +35 -1
- package/dist/Fx/combinators/withSpan.d.ts.map +1 -1
- package/dist/Fx/combinators/withSpan.js +34 -3
- package/dist/Fx/combinators/zip.d.ts +135 -0
- package/dist/Fx/combinators/zip.d.ts.map +1 -0
- package/dist/Fx/combinators/zip.js +160 -0
- package/dist/Fx/constructors/at.d.ts +23 -3
- package/dist/Fx/constructors/at.d.ts.map +1 -1
- package/dist/Fx/constructors/at.js +21 -1
- package/dist/Fx/constructors/die.d.ts +22 -1
- package/dist/Fx/constructors/die.d.ts.map +1 -1
- package/dist/Fx/constructors/die.js +22 -1
- package/dist/Fx/constructors/empty.d.ts +19 -1
- package/dist/Fx/constructors/empty.d.ts.map +1 -1
- package/dist/Fx/constructors/empty.js +19 -1
- package/dist/Fx/constructors/fail.d.ts +22 -1
- package/dist/Fx/constructors/fail.d.ts.map +1 -1
- package/dist/Fx/constructors/fail.js +22 -1
- package/dist/Fx/constructors/failCause.d.ts +23 -1
- package/dist/Fx/constructors/failCause.d.ts.map +1 -1
- package/dist/Fx/constructors/failCause.js +23 -1
- package/dist/Fx/constructors/fn.d.ts +116 -16
- package/dist/Fx/constructors/fn.d.ts.map +1 -1
- package/dist/Fx/constructors/fn.js +36 -0
- package/dist/Fx/constructors/fromEffect.d.ts +45 -3
- package/dist/Fx/constructors/fromEffect.d.ts.map +1 -1
- package/dist/Fx/constructors/fromEffect.js +45 -3
- package/dist/Fx/constructors/fromFailures.d.ts +25 -1
- package/dist/Fx/constructors/fromFailures.d.ts.map +1 -1
- package/dist/Fx/constructors/fromFailures.js +25 -1
- package/dist/Fx/constructors/fromIterable.d.ts +23 -1
- package/dist/Fx/constructors/fromIterable.d.ts.map +1 -1
- package/dist/Fx/constructors/fromIterable.js +23 -1
- package/dist/Fx/constructors/fromSchedule.d.ts +26 -1
- package/dist/Fx/constructors/fromSchedule.d.ts.map +1 -1
- package/dist/Fx/constructors/fromSchedule.js +28 -3
- package/dist/Fx/constructors/index.d.ts +2 -1
- package/dist/Fx/constructors/index.d.ts.map +1 -1
- package/dist/Fx/constructors/index.js +2 -1
- package/dist/Fx/constructors/interrupt.d.ts +22 -2
- package/dist/Fx/constructors/interrupt.d.ts.map +1 -1
- package/dist/Fx/constructors/interrupt.js +22 -2
- package/dist/Fx/constructors/make.d.ts +151 -4
- package/dist/Fx/constructors/make.d.ts.map +1 -1
- package/dist/Fx/constructors/make.js +60 -7
- package/dist/Fx/constructors/periodic.d.ts +31 -2
- package/dist/Fx/constructors/periodic.d.ts.map +1 -1
- package/dist/Fx/constructors/periodic.js +30 -1
- package/dist/Fx/constructors/succeed.d.ts +79 -7
- package/dist/Fx/constructors/succeed.d.ts.map +1 -1
- package/dist/Fx/constructors/succeed.js +79 -7
- package/dist/Fx/constructors/suspend.d.ts +28 -0
- package/dist/Fx/constructors/suspend.d.ts.map +1 -1
- package/dist/Fx/constructors/suspend.js +28 -0
- package/dist/Fx/constructors/sync.d.ts +32 -0
- package/dist/Fx/constructors/sync.d.ts.map +1 -0
- package/dist/Fx/constructors/sync.js +32 -0
- package/dist/Fx/internal/DeferredRef.d.ts +259 -7
- package/dist/Fx/internal/DeferredRef.d.ts.map +1 -1
- package/dist/Fx/internal/DeferredRef.js +260 -9
- package/dist/Fx/internal/UnionToTuple.d.ts +22 -0
- package/dist/Fx/internal/UnionToTuple.d.ts.map +1 -1
- package/dist/Fx/internal/diff.d.ts +586 -0
- package/dist/Fx/internal/diff.d.ts.map +1 -1
- package/dist/Fx/internal/diff.js +120 -9
- package/dist/Fx/internal/effectableEvaluateKey.d.ts +30 -0
- package/dist/Fx/internal/effectableEvaluateKey.d.ts.map +1 -0
- package/dist/Fx/internal/effectableEvaluateKey.js +29 -0
- package/dist/Fx/internal/effectableWithOverride.d.ts +55 -0
- package/dist/Fx/internal/effectableWithOverride.d.ts.map +1 -0
- package/dist/Fx/internal/effectableWithOverride.js +46 -0
- package/dist/Fx/internal/equivalence.d.ts +29 -0
- package/dist/Fx/internal/equivalence.d.ts.map +1 -1
- package/dist/Fx/internal/equivalence.js +29 -0
- package/dist/Fx/internal/multicast.d.ts +145 -2
- package/dist/Fx/internal/multicast.d.ts.map +1 -1
- package/dist/Fx/internal/multicast.js +146 -4
- package/dist/Fx/internal/ring-buffer.d.ts +149 -0
- package/dist/Fx/internal/ring-buffer.d.ts.map +1 -1
- package/dist/Fx/internal/ring-buffer.js +155 -0
- package/dist/Fx/internal/scope.d.ts +184 -0
- package/dist/Fx/internal/scope.d.ts.map +1 -1
- package/dist/Fx/internal/scope.js +150 -0
- package/dist/Fx/internal/yieldable.d.ts +117 -2
- package/dist/Fx/internal/yieldable.d.ts.map +1 -1
- package/dist/Fx/internal/yieldable.js +86 -3
- package/dist/Fx/run/collect.d.ts +93 -4
- package/dist/Fx/run/collect.d.ts.map +1 -1
- package/dist/Fx/run/collect.js +94 -5
- package/dist/Fx/run/first.d.ts +23 -1
- package/dist/Fx/run/first.d.ts.map +1 -1
- package/dist/Fx/run/first.js +23 -1
- package/dist/Fx/run/fork.d.ts +52 -2
- package/dist/Fx/run/fork.d.ts.map +1 -1
- package/dist/Fx/run/fork.js +52 -2
- package/dist/Fx/run/observe.d.ts +112 -4
- package/dist/Fx/run/observe.d.ts.map +1 -1
- package/dist/Fx/run/observe.js +137 -16
- package/dist/Fx/run/runPromise.d.ts +45 -2
- package/dist/Fx/run/runPromise.d.ts.map +1 -1
- package/dist/Fx/run/runPromise.js +45 -2
- package/dist/Fx/stream.d.ts +92 -0
- package/dist/Fx/stream.d.ts.map +1 -1
- package/dist/Fx/stream.js +60 -1
- package/dist/Push/Push.d.ts +1286 -98
- package/dist/Push/Push.d.ts.map +1 -1
- package/dist/Push/Push.js +1074 -88
- package/dist/RefArray.d.ts +2 -0
- package/dist/RefArray.d.ts.map +1 -0
- package/dist/RefArray.js +1 -0
- package/dist/RefBigDecimal.d.ts +2 -0
- package/dist/RefBigDecimal.d.ts.map +1 -0
- package/dist/RefBigDecimal.js +1 -0
- package/dist/RefBigInt.d.ts +2 -0
- package/dist/RefBigInt.d.ts.map +1 -0
- package/dist/RefBigInt.js +1 -0
- package/dist/RefBoolean.d.ts +2 -0
- package/dist/RefBoolean.d.ts.map +1 -0
- package/dist/RefBoolean.js +1 -0
- package/dist/RefCause.d.ts +2 -0
- package/dist/RefCause.d.ts.map +1 -0
- package/dist/RefCause.js +1 -0
- package/dist/RefChunk.d.ts +2 -0
- package/dist/RefChunk.d.ts.map +1 -0
- package/dist/RefChunk.js +1 -0
- package/dist/RefDateTime.d.ts +2 -0
- package/dist/RefDateTime.d.ts.map +1 -0
- package/dist/RefDateTime.js +1 -0
- package/dist/RefDuration.d.ts +2 -0
- package/dist/RefDuration.d.ts.map +1 -0
- package/dist/RefDuration.js +1 -0
- package/dist/RefGraph.d.ts +2 -0
- package/dist/RefGraph.d.ts.map +1 -0
- package/dist/RefGraph.js +1 -0
- package/dist/RefHashMap.d.ts +2 -0
- package/dist/RefHashMap.d.ts.map +1 -0
- package/dist/RefHashMap.js +1 -0
- package/dist/RefHashRing.d.ts +2 -0
- package/dist/RefHashRing.d.ts.map +1 -0
- package/dist/RefHashRing.js +1 -0
- package/dist/RefHashSet.d.ts +2 -0
- package/dist/RefHashSet.d.ts.map +1 -0
- package/dist/RefHashSet.js +1 -0
- package/dist/RefIterable.d.ts +2 -0
- package/dist/RefIterable.d.ts.map +1 -0
- package/dist/RefIterable.js +1 -0
- package/dist/RefOption.d.ts +2 -0
- package/dist/RefOption.d.ts.map +1 -0
- package/dist/RefOption.js +1 -0
- package/dist/RefRecord.d.ts +2 -0
- package/dist/RefRecord.d.ts.map +1 -0
- package/dist/RefRecord.js +1 -0
- package/dist/RefResult.d.ts +2 -0
- package/dist/RefResult.d.ts.map +1 -0
- package/dist/RefResult.js +1 -0
- package/dist/RefString.d.ts +2 -0
- package/dist/RefString.d.ts.map +1 -0
- package/dist/RefString.js +1 -0
- package/dist/RefStruct.d.ts +2 -0
- package/dist/RefStruct.d.ts.map +1 -0
- package/dist/RefStruct.js +1 -0
- package/dist/RefSubject/Hydration.d.ts +504 -0
- package/dist/RefSubject/Hydration.d.ts.map +1 -0
- package/dist/RefSubject/Hydration.js +300 -0
- package/dist/RefSubject/RefArray.d.ts +382 -35
- package/dist/RefSubject/RefArray.d.ts.map +1 -1
- package/dist/RefSubject/RefArray.js +374 -40
- package/dist/RefSubject/RefBigDecimal.d.ts +241 -21
- package/dist/RefSubject/RefBigDecimal.d.ts.map +1 -1
- package/dist/RefSubject/RefBigDecimal.js +229 -20
- package/dist/RefSubject/RefBigInt.d.ts +181 -16
- package/dist/RefSubject/RefBigInt.d.ts.map +1 -1
- package/dist/RefSubject/RefBigInt.js +169 -15
- package/dist/RefSubject/RefBoolean.d.ts +181 -16
- package/dist/RefSubject/RefBoolean.d.ts.map +1 -1
- package/dist/RefSubject/RefBoolean.js +169 -15
- package/dist/RefSubject/RefCause.d.ts +133 -12
- package/dist/RefSubject/RefCause.d.ts.map +1 -1
- package/dist/RefSubject/RefCause.js +121 -11
- package/dist/RefSubject/RefChunk.d.ts +481 -76
- package/dist/RefSubject/RefChunk.d.ts.map +1 -1
- package/dist/RefSubject/RefChunk.js +471 -75
- package/dist/RefSubject/RefDateTime.d.ts +137 -16
- package/dist/RefSubject/RefDateTime.d.ts.map +1 -1
- package/dist/RefSubject/RefDateTime.js +122 -12
- package/dist/RefSubject/RefDuration.d.ts +169 -15
- package/dist/RefSubject/RefDuration.d.ts.map +1 -1
- package/dist/RefSubject/RefDuration.js +157 -14
- package/dist/RefSubject/RefGraph.d.ts +349 -35
- package/dist/RefSubject/RefGraph.d.ts.map +1 -1
- package/dist/RefSubject/RefGraph.js +334 -32
- package/dist/RefSubject/RefHashMap.d.ts +323 -28
- package/dist/RefSubject/RefHashMap.d.ts.map +1 -1
- package/dist/RefSubject/RefHashMap.js +317 -29
- package/dist/RefSubject/RefHashRing.d.ts +159 -15
- package/dist/RefSubject/RefHashRing.d.ts.map +1 -1
- package/dist/RefSubject/RefHashRing.js +145 -12
- package/dist/RefSubject/RefHashSet.d.ts +251 -22
- package/dist/RefSubject/RefHashSet.d.ts.map +1 -1
- package/dist/RefSubject/RefHashSet.js +238 -20
- package/dist/RefSubject/RefIterable.d.ts +358 -32
- package/dist/RefSubject/RefIterable.d.ts.map +1 -1
- package/dist/RefSubject/RefIterable.js +350 -30
- package/dist/RefSubject/RefOption.d.ts +157 -14
- package/dist/RefSubject/RefOption.d.ts.map +1 -1
- package/dist/RefSubject/RefOption.js +145 -13
- package/dist/RefSubject/RefRecord.d.ts +402 -64
- package/dist/RefSubject/RefRecord.d.ts.map +1 -1
- package/dist/RefSubject/RefRecord.js +364 -39
- package/dist/RefSubject/RefResult.d.ts +167 -35
- package/dist/RefSubject/RefResult.d.ts.map +1 -1
- package/dist/RefSubject/RefResult.js +133 -12
- package/dist/RefSubject/RefString.d.ts +205 -18
- package/dist/RefSubject/RefString.d.ts.map +1 -1
- package/dist/RefSubject/RefString.js +193 -17
- package/dist/RefSubject/RefStruct.d.ts +169 -14
- package/dist/RefSubject/RefStruct.d.ts.map +1 -1
- package/dist/RefSubject/RefStruct.js +157 -13
- package/dist/RefSubject/RefSubject.d.ts +1242 -43
- package/dist/RefSubject/RefSubject.d.ts.map +1 -1
- package/dist/RefSubject/RefSubject.js +1012 -86
- package/dist/RefSubject/RefTrie.d.ts +323 -37
- package/dist/RefSubject/RefTrie.d.ts.map +1 -1
- package/dist/RefSubject/RefTrie.js +311 -31
- package/dist/RefSubject/RefTuple.d.ts +127 -11
- package/dist/RefSubject/RefTuple.d.ts.map +1 -1
- package/dist/RefSubject/RefTuple.js +114 -9
- package/dist/RefSubject/index.d.ts +1 -0
- package/dist/RefSubject/index.d.ts.map +1 -1
- package/dist/RefSubject/index.js +1 -0
- package/dist/RefSubject.d.ts +2 -1
- package/dist/RefSubject.d.ts.map +1 -1
- package/dist/RefSubject.js +2 -1
- package/dist/RefTrie.d.ts +2 -0
- package/dist/RefTrie.d.ts.map +1 -0
- package/dist/RefTrie.js +1 -0
- package/dist/RefTuple.d.ts +2 -0
- package/dist/RefTuple.d.ts.map +1 -0
- package/dist/RefTuple.js +1 -0
- package/dist/Sink/Sink.d.ts +396 -23
- package/dist/Sink/Sink.d.ts.map +1 -1
- package/dist/Sink/Sink.js +55 -7
- package/dist/Sink/combinators.d.ts +789 -3
- package/dist/Sink/combinators.d.ts.map +1 -1
- package/dist/Sink/combinators.js +812 -7
- package/dist/Subject/Subject.d.ts +1022 -27
- package/dist/Subject/Subject.d.ts.map +1 -1
- package/dist/Subject/Subject.js +909 -43
- package/dist/Versioned/Versioned.d.ts +475 -25
- package/dist/Versioned/Versioned.d.ts.map +1 -1
- package/dist/Versioned/Versioned.js +298 -46
- package/dist/index.d.ts +22 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -3
- package/package.json +125 -11
- package/dist/Fx/constructors/fromYieldable.d.ts +0 -12
- package/dist/Fx/constructors/fromYieldable.d.ts.map +0 -1
- package/dist/Fx/constructors/fromYieldable.js +0 -12
- package/src/Fx/Fx.ts +0 -164
- package/src/Fx/TypeId.ts +0 -25
- package/src/Fx/combinators/catch.ts +0 -165
- package/src/Fx/combinators/causes.ts +0 -23
- package/src/Fx/combinators/compact.ts +0 -15
- package/src/Fx/combinators/continueWith.ts +0 -78
- package/src/Fx/combinators/ensuring.ts +0 -23
- package/src/Fx/combinators/exhaustLatestMap.ts +0 -74
- package/src/Fx/combinators/exhaustLatestMapEffect.ts +0 -25
- package/src/Fx/combinators/exhaustMap.ts +0 -36
- package/src/Fx/combinators/exhaustMapEffect.ts +0 -23
- package/src/Fx/combinators/exit.ts +0 -15
- package/src/Fx/combinators/filter.ts +0 -22
- package/src/Fx/combinators/filterEffect.ts +0 -31
- package/src/Fx/combinators/filterMap.ts +0 -23
- package/src/Fx/combinators/filterMapEffect.ts +0 -32
- package/src/Fx/combinators/filterMapLoop.ts +0 -35
- package/src/Fx/combinators/filterMapLoopCause.ts +0 -36
- package/src/Fx/combinators/filterMapLoopCauseEffect.ts +0 -46
- package/src/Fx/combinators/filterMapLoopEffect.ts +0 -36
- package/src/Fx/combinators/flatMap.ts +0 -51
- package/src/Fx/combinators/flatMapConcurrently.ts +0 -36
- package/src/Fx/combinators/flatMapConcurrentlyEffect.ts +0 -26
- package/src/Fx/combinators/flatMapEffect.ts +0 -42
- package/src/Fx/combinators/flip.ts +0 -14
- package/src/Fx/combinators/gen.ts +0 -24
- package/src/Fx/combinators/genScoped.ts +0 -24
- package/src/Fx/combinators/index.ts +0 -51
- package/src/Fx/combinators/keyed.ts +0 -369
- package/src/Fx/combinators/loop.ts +0 -27
- package/src/Fx/combinators/loopCause.ts +0 -34
- package/src/Fx/combinators/loopCauseEffect.ts +0 -36
- package/src/Fx/combinators/loopEffect.ts +0 -34
- package/src/Fx/combinators/map.ts +0 -22
- package/src/Fx/combinators/mapEffect.ts +0 -32
- package/src/Fx/combinators/mergeAll.ts +0 -22
- package/src/Fx/combinators/mergeOrdered.ts +0 -123
- package/src/Fx/combinators/onError.ts +0 -40
- package/src/Fx/combinators/onExit.ts +0 -82
- package/src/Fx/combinators/onInterrupt.ts +0 -87
- package/src/Fx/combinators/provide.ts +0 -62
- package/src/Fx/combinators/skip.ts +0 -20
- package/src/Fx/combinators/skipRepeats.ts +0 -15
- package/src/Fx/combinators/skipRepeatsWith.ts +0 -40
- package/src/Fx/combinators/slice.ts +0 -32
- package/src/Fx/combinators/switchMap.ts +0 -55
- package/src/Fx/combinators/switchMapEffect.ts +0 -25
- package/src/Fx/combinators/take.ts +0 -17
- package/src/Fx/combinators/takeUntil.ts +0 -50
- package/src/Fx/combinators/tapEffect.ts +0 -39
- package/src/Fx/combinators/tuple.ts +0 -79
- package/src/Fx/combinators/unwrap.ts +0 -21
- package/src/Fx/combinators/unwrapScoped.ts +0 -26
- package/src/Fx/combinators/when.ts +0 -64
- package/src/Fx/combinators/withSpan.ts +0 -24
- package/src/Fx/constructors/at.ts +0 -23
- package/src/Fx/constructors/die.ts +0 -17
- package/src/Fx/constructors/empty.ts +0 -10
- package/src/Fx/constructors/fail.ts +0 -14
- package/src/Fx/constructors/failCause.ts +0 -14
- package/src/Fx/constructors/fn.ts +0 -245
- package/src/Fx/constructors/fromEffect.ts +0 -24
- package/src/Fx/constructors/fromFailures.ts +0 -19
- package/src/Fx/constructors/fromIterable.ts +0 -15
- package/src/Fx/constructors/fromSchedule.ts +0 -18
- package/src/Fx/constructors/fromYieldable.ts +0 -17
- package/src/Fx/constructors/index.ts +0 -15
- package/src/Fx/constructors/interrupt.ts +0 -13
- package/src/Fx/constructors/make.ts +0 -103
- package/src/Fx/constructors/periodic.ts +0 -15
- package/src/Fx/constructors/succeed.ts +0 -37
- package/src/Fx/constructors/suspend.ts +0 -6
- package/src/Fx/index.ts +0 -6
- package/src/Fx/internal/DeferredRef.ts +0 -87
- package/src/Fx/internal/UnionToTuple.ts +0 -12
- package/src/Fx/internal/diff.ts +0 -191
- package/src/Fx/internal/equivalence.ts +0 -12
- package/src/Fx/internal/multicast.ts +0 -51
- package/src/Fx/internal/ring-buffer.ts +0 -60
- package/src/Fx/internal/scope.ts +0 -58
- package/src/Fx/internal/yieldable.ts +0 -31
- package/src/Fx/run/collect.ts +0 -79
- package/src/Fx/run/first.ts +0 -20
- package/src/Fx/run/fork.ts +0 -40
- package/src/Fx/run/index.ts +0 -5
- package/src/Fx/run/observe.ts +0 -114
- package/src/Fx/run/runPromise.ts +0 -32
- package/src/Fx/stream.ts +0 -42
- package/src/Fx.catch.test.ts +0 -61
- package/src/Fx.fn.test.ts +0 -51
- package/src/Fx.lifecycle.test.ts +0 -80
- package/src/Fx.test.ts +0 -71
- package/src/Fx.ts +0 -1
- package/src/Push/Push.ts +0 -903
- package/src/Push/index.ts +0 -1
- package/src/Push.test.ts +0 -26
- package/src/Push.ts +0 -1
- package/src/RefSubject/RefArray.ts +0 -512
- package/src/RefSubject/RefBigDecimal.test.ts +0 -56
- package/src/RefSubject/RefBigDecimal.ts +0 -295
- package/src/RefSubject/RefBigInt.test.ts +0 -56
- package/src/RefSubject/RefBigInt.ts +0 -189
- package/src/RefSubject/RefBoolean.test.ts +0 -57
- package/src/RefSubject/RefBoolean.ts +0 -185
- package/src/RefSubject/RefCause.test.ts +0 -53
- package/src/RefSubject/RefCause.ts +0 -146
- package/src/RefSubject/RefChunk.ts +0 -510
- package/src/RefSubject/RefDateTime.test.ts +0 -43
- package/src/RefSubject/RefDateTime.ts +0 -264
- package/src/RefSubject/RefDuration.test.ts +0 -49
- package/src/RefSubject/RefDuration.ts +0 -188
- package/src/RefSubject/RefGraph.ts +0 -650
- package/src/RefSubject/RefHashMap.ts +0 -465
- package/src/RefSubject/RefHashRing.ts +0 -262
- package/src/RefSubject/RefHashSet.ts +0 -308
- package/src/RefSubject/RefIterable.ts +0 -445
- package/src/RefSubject/RefOption.test.ts +0 -67
- package/src/RefSubject/RefOption.ts +0 -193
- package/src/RefSubject/RefRecord.ts +0 -612
- package/src/RefSubject/RefResult.test.ts +0 -63
- package/src/RefSubject/RefResult.ts +0 -209
- package/src/RefSubject/RefString.test.ts +0 -61
- package/src/RefSubject/RefString.ts +0 -256
- package/src/RefSubject/RefStruct.test.ts +0 -60
- package/src/RefSubject/RefStruct.ts +0 -253
- package/src/RefSubject/RefSubject.ts +0 -2545
- package/src/RefSubject/RefTrie.ts +0 -356
- package/src/RefSubject/RefTuple.test.ts +0 -60
- package/src/RefSubject/RefTuple.ts +0 -231
- package/src/RefSubject/index.ts +0 -21
- package/src/RefSubject.test.ts +0 -65
- package/src/RefSubject.ts +0 -1
- package/src/Sink/Sink.ts +0 -159
- package/src/Sink/combinators.ts +0 -993
- package/src/Sink/index.ts +0 -2
- package/src/Sink.ts +0 -1
- package/src/Subject/Subject.ts +0 -441
- package/src/Subject/index.ts +0 -1
- package/src/Subject.test.ts +0 -47
- package/src/Subject.ts +0 -1
- package/src/Versioned/Versioned.ts +0 -521
- package/src/Versioned/index.ts +0 -1
- package/src/Versioned.test.ts +0 -23
- package/src/Versioned.ts +0 -1
- package/src/index.ts +0 -7
- package/tsconfig.json +0 -6
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
/** @effect-diagnostics missingEffectContext:skip-file */
|
|
3
3
|
import * as Array from "effect/Array";
|
|
4
4
|
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Effectable from "effect/Effectable";
|
|
6
|
+
import * as Semaphore from "effect/Semaphore";
|
|
5
7
|
import { equals } from "effect/Equal";
|
|
6
8
|
import * as Exit from "effect/Exit";
|
|
7
9
|
import * as Fiber from "effect/Fiber";
|
|
@@ -12,17 +14,18 @@ import { sum } from "effect/Number";
|
|
|
12
14
|
import * as Option from "effect/Option";
|
|
13
15
|
import { pipeArguments } from "effect/Pipeable";
|
|
14
16
|
import * as Scope from "effect/Scope";
|
|
15
|
-
import * as
|
|
17
|
+
import * as Context from "effect/Context";
|
|
16
18
|
import * as Stream from "effect/Stream";
|
|
17
19
|
import { compact as fxCompact } from "../Fx/combinators/compact.js";
|
|
18
20
|
import { continueWith } from "../Fx/combinators/continueWith.js";
|
|
19
21
|
import { filterMapEffect as fxFilterMapEffect } from "../Fx/combinators/filterMapEffect.js";
|
|
20
22
|
import { mapEffect as fxMapEffect } from "../Fx/combinators/mapEffect.js";
|
|
23
|
+
import { mergeAll as fxMergeAll } from "../Fx/combinators/mergeAll.js";
|
|
24
|
+
import { scanEffect as fxScanEffect } from "../Fx/combinators/scan.js";
|
|
21
25
|
import { skipRepeats } from "../Fx/combinators/skipRepeats.js";
|
|
22
26
|
import { slice as fxSlice } from "../Fx/combinators/slice.js";
|
|
23
27
|
import { unwrap } from "../Fx/combinators/unwrap.js";
|
|
24
28
|
import { fromEffect as fxFromEffect } from "../Fx/constructors/fromEffect.js";
|
|
25
|
-
import { fromYieldable } from "../Fx/constructors/fromYieldable.js";
|
|
26
29
|
import * as DeferredRef from "../Fx/internal/DeferredRef.js";
|
|
27
30
|
import { getExitEquivalence } from "../Fx/internal/equivalence.js";
|
|
28
31
|
import { YieldableFx } from "../Fx/internal/yieldable.js";
|
|
@@ -30,13 +33,92 @@ import { FxTypeId, isFx } from "../Fx/TypeId.js";
|
|
|
30
33
|
import * as Sink from "../Sink/Sink.js";
|
|
31
34
|
import * as Subject from "../Subject/Subject.js";
|
|
32
35
|
import * as Versioned from "../Versioned/Versioned.js";
|
|
36
|
+
import { hasProperty } from "effect/Predicate";
|
|
37
|
+
/**
|
|
38
|
+
* Runtime symbol identifying writable RefSubject values.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* ## Why
|
|
42
|
+
*
|
|
43
|
+
* Provides stable runtime identity for RefSubject values without relying on classes or a renderer.
|
|
44
|
+
*
|
|
45
|
+
* ## Ownership and lifetime
|
|
46
|
+
*
|
|
47
|
+
* This declaration performs no acquisition and retains no resources. Implementations preserve
|
|
48
|
+
* source errors, services, and lifetime.
|
|
49
|
+
*
|
|
50
|
+
* @since 1.18.0
|
|
51
|
+
* @category Type identity
|
|
52
|
+
*/
|
|
33
53
|
export const RefSubjectTypeId = Symbol.for("@typed/fx/RefSubject");
|
|
54
|
+
/**
|
|
55
|
+
* Runtime symbol identifying read-only Computed values.
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
* ## Why
|
|
59
|
+
*
|
|
60
|
+
* Provides stable runtime identity for Computed values without relying on classes or a renderer.
|
|
61
|
+
*
|
|
62
|
+
* ## Ownership and lifetime
|
|
63
|
+
*
|
|
64
|
+
* This declaration performs no acquisition and retains no resources. Implementations preserve
|
|
65
|
+
* source errors, services, and lifetime.
|
|
66
|
+
*
|
|
67
|
+
* @since 1.18.0
|
|
68
|
+
* @category Type identity
|
|
69
|
+
*/
|
|
34
70
|
export const ComputedTypeId = Symbol.for("@typed/fx/Computed");
|
|
71
|
+
/**
|
|
72
|
+
* Runtime symbol identifying conditionally available Filtered values.
|
|
73
|
+
*
|
|
74
|
+
* @remarks
|
|
75
|
+
* ## Why
|
|
76
|
+
*
|
|
77
|
+
* Provides stable runtime identity for Filtered values without relying on classes or a renderer.
|
|
78
|
+
*
|
|
79
|
+
* ## Ownership and lifetime
|
|
80
|
+
*
|
|
81
|
+
* This declaration performs no acquisition and retains no resources. Implementations preserve
|
|
82
|
+
* source errors, services, and lifetime.
|
|
83
|
+
*
|
|
84
|
+
* @since 1.18.0
|
|
85
|
+
* @category Type identity
|
|
86
|
+
*/
|
|
35
87
|
export const FilteredTypeId = Symbol.for("@typed/fx/Filtered");
|
|
36
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Selects whether a computed Fx observes only its current value or also follows later pushes.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* ## Why
|
|
93
|
+
*
|
|
94
|
+
* `"multiple"` (the default) emits the current value and then follows distinct source pushes.
|
|
95
|
+
* `"one"` emits only the current value. Hydration temporarily uses `"one"` when it must resolve a
|
|
96
|
+
* single server value without leaving a live observation running.
|
|
97
|
+
*
|
|
98
|
+
* ## Ownership and lifetime
|
|
99
|
+
*
|
|
100
|
+
* This is an Effect Context reference with a default value, not mutable global state. Supplying a
|
|
101
|
+
* value changes computed observation only in the provided Effect context; the observing Scope
|
|
102
|
+
* still owns any subscription created by `"multiple"`.
|
|
103
|
+
*
|
|
104
|
+
* @since 1.18.0
|
|
105
|
+
* @category Observation policy
|
|
106
|
+
*/
|
|
107
|
+
export const CurrentComputedBehavior = Context.Reference("@typed/fx/CurrentComputedBehavior", {
|
|
37
108
|
defaultValue: () => "multiple",
|
|
38
109
|
});
|
|
39
|
-
const checkIsMultiple = (ctx) =>
|
|
110
|
+
const checkIsMultiple = (ctx) => Context.getUnsafe(ctx, CurrentComputedBehavior) === "multiple";
|
|
111
|
+
function holdInContext(fx) {
|
|
112
|
+
const contexts = new WeakMap();
|
|
113
|
+
return unwrap(Effect.map(Effect.context(), (context) => {
|
|
114
|
+
let shared = contexts.get(context);
|
|
115
|
+
if (!shared) {
|
|
116
|
+
shared = Subject.hold(fx);
|
|
117
|
+
contexts.set(context, shared);
|
|
118
|
+
}
|
|
119
|
+
return shared;
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
40
122
|
class ComputedImpl extends Versioned.VersionedTransform {
|
|
41
123
|
[ComputedTypeId] = ComputedTypeId;
|
|
42
124
|
_computed;
|
|
@@ -46,20 +128,145 @@ class ComputedImpl extends Versioned.VersionedTransform {
|
|
|
46
128
|
super(input, (fx) => fxMapEffect(fx, f), Effect.flatMap(f));
|
|
47
129
|
this.input = input;
|
|
48
130
|
this.f = f;
|
|
49
|
-
this._computed =
|
|
131
|
+
this._computed = holdInContext(unwrap(Effect.map(Effect.context(), (ctx) => {
|
|
50
132
|
if (checkIsMultiple(ctx)) {
|
|
51
|
-
return
|
|
133
|
+
return fxFromEffect(input).pipe(continueWith(() => input), skipRepeats, fxMapEffect(f));
|
|
52
134
|
}
|
|
53
|
-
return fxFromEffect(Effect.flatMap(input
|
|
135
|
+
return fxFromEffect(Effect.flatMap(input, f));
|
|
54
136
|
})));
|
|
55
137
|
}
|
|
56
138
|
run(sink) {
|
|
57
139
|
return this._computed.run(sink);
|
|
58
140
|
}
|
|
59
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Builds a read-only Computed projection from a Versioned source.
|
|
144
|
+
*
|
|
145
|
+
* @remarks
|
|
146
|
+
* ## Why
|
|
147
|
+
*
|
|
148
|
+
* Applies the same Effectful projection to current reads and pushed versions, preserving all three
|
|
149
|
+
* Versioned channels without granting writes to the source.
|
|
150
|
+
*
|
|
151
|
+
* ## Ownership and lifetime
|
|
152
|
+
*
|
|
153
|
+
* Current reads cache the transformed input by source version within each Effect Context. The
|
|
154
|
+
* pushed channel uses `Subject.hold`: observers with the same Context object share one upstream
|
|
155
|
+
* session and its retained projection; distinct Context objects have separate sessions. The last
|
|
156
|
+
* observer ends its session and clears its held value. The observing Scope owns cleanup.
|
|
157
|
+
*
|
|
158
|
+
* @since 1.18.0
|
|
159
|
+
* @category Derived queries
|
|
160
|
+
*/
|
|
60
161
|
export function makeComputed(input, f) {
|
|
61
162
|
return new ComputedImpl(input, f);
|
|
62
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Stateful scan over a `RefSubject` / `Computed`, producing a `Computed` of the accumulated state.
|
|
166
|
+
*
|
|
167
|
+
* Fx subscriptions follow `Fx.scan` semantics (emit `initial`, then fold each source value).
|
|
168
|
+
* Effect sampling accumulates across source versions via a private state ref (do not mix heavy
|
|
169
|
+
* subscribe + sample on the same scan if you need a single shared accumulator).
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* ## Why
|
|
173
|
+
*
|
|
174
|
+
* Stateful scan over a `RefSubject` / `Computed`, producing a `Computed` of the accumulated state.
|
|
175
|
+
* Fx subscriptions follow `Fx.scan` semantics (emit `initial`, then fold each source value).
|
|
176
|
+
* Effect sampling accumulates across source versions via a private state ref (do not mix heavy
|
|
177
|
+
* subscribe + sample on the same scan if you need a single shared accumulator). The result remains
|
|
178
|
+
* a lazy view rather than a duplicated mutable value.
|
|
179
|
+
*
|
|
180
|
+
* ## Ownership and lifetime
|
|
181
|
+
*
|
|
182
|
+
* Each Effect Context object gets a private current-read accumulator, advanced after a successful
|
|
183
|
+
* fold for a new source version. The pushed channel runs `Fx.scanEffect` from `initial` inside
|
|
184
|
+
* `Subject.hold`: observers using the same Context share that active scan and retained result.
|
|
185
|
+
* The last observer ends its session and clears its held value. The observing Scope owns
|
|
186
|
+
* subscription cleanup. Read accumulators are weakly held by Context and need no finalizer.
|
|
187
|
+
*
|
|
188
|
+
* @since 1.0.0
|
|
189
|
+
* @category Accumulation
|
|
190
|
+
*/
|
|
191
|
+
export const scan = dual(3, function scan(versioned, initial, f) {
|
|
192
|
+
return new ComputedScanImpl(versioned, initial, (s, a) => Effect.succeed(f(s, a)));
|
|
193
|
+
});
|
|
194
|
+
/**
|
|
195
|
+
* Effectful stateful scan over a `RefSubject` / `Computed`, producing a `Computed` of the accumulated state.
|
|
196
|
+
*
|
|
197
|
+
* @remarks
|
|
198
|
+
* ## Why
|
|
199
|
+
*
|
|
200
|
+
* Effectful stateful scan over a `RefSubject` / `Computed`, producing a `Computed` of the
|
|
201
|
+
* accumulated state. The result remains a lazy view rather than a duplicated mutable value.
|
|
202
|
+
*
|
|
203
|
+
* ## Ownership and lifetime
|
|
204
|
+
*
|
|
205
|
+
* Each Effect Context object gets a private current-read accumulator. It advances only after `f`
|
|
206
|
+
* succeeds, so a typed failure leaves the previous state intact. The pushed channel runs
|
|
207
|
+
* `Fx.scanEffect` from `initial` inside `Subject.hold`: observers using the same Context share
|
|
208
|
+
* that active scan and retained result; each fold requires `R3`. The last observer ends its
|
|
209
|
+
* session and clears its held value. The observing Scope owns cleanup. Read accumulators are
|
|
210
|
+
* weakly held by Context and need no finalizer.
|
|
211
|
+
*
|
|
212
|
+
* @since 1.0.0
|
|
213
|
+
* @category Accumulation
|
|
214
|
+
*/
|
|
215
|
+
export const scanEffect = dual(3, function scanEffect(versioned, initial, f) {
|
|
216
|
+
return new ComputedScanImpl(versioned, initial, f);
|
|
217
|
+
});
|
|
218
|
+
class ComputedScanImpl extends Versioned.VersionedTransform {
|
|
219
|
+
[ComputedTypeId] = ComputedTypeId;
|
|
220
|
+
_computed;
|
|
221
|
+
input;
|
|
222
|
+
initial;
|
|
223
|
+
f;
|
|
224
|
+
constructor(input, initial, f) {
|
|
225
|
+
const states = new WeakMap();
|
|
226
|
+
super(input, (fx) => fxScanEffect(fx, initial, f), (effect) => Effect.contextWith((context) => {
|
|
227
|
+
let state = states.get(context);
|
|
228
|
+
if (!state) {
|
|
229
|
+
state = MutableRef.make(initial);
|
|
230
|
+
states.set(context, state);
|
|
231
|
+
}
|
|
232
|
+
const current = state;
|
|
233
|
+
return Effect.flatMap(effect, (a) => Effect.flatMap(f(MutableRef.get(current), a), (next) => Effect.sync(() => {
|
|
234
|
+
MutableRef.set(current, next);
|
|
235
|
+
return next;
|
|
236
|
+
})));
|
|
237
|
+
}));
|
|
238
|
+
this.input = input;
|
|
239
|
+
this.initial = initial;
|
|
240
|
+
this.f = f;
|
|
241
|
+
this._computed = holdInContext(unwrap(Effect.map(Effect.context(), (ctx) => {
|
|
242
|
+
if (checkIsMultiple(ctx)) {
|
|
243
|
+
return fxFromEffect(input).pipe(continueWith(() => input), skipRepeats, fxScanEffect(initial, f));
|
|
244
|
+
}
|
|
245
|
+
return fxFromEffect(Effect.flatMap(input, (a) => f(initial, a)));
|
|
246
|
+
})));
|
|
247
|
+
}
|
|
248
|
+
run(sink) {
|
|
249
|
+
return this._computed.run(sink);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Builds a Filtered view from the three channels of a Versioned source.
|
|
254
|
+
*
|
|
255
|
+
* @remarks
|
|
256
|
+
* ## Why
|
|
257
|
+
*
|
|
258
|
+
* Applies `f` to current reads and later pushes while preserving absence as Filtered semantics:
|
|
259
|
+
* Effect reads fail with `NoSuchElementError`, whereas the Fx channel simply omits absent values.
|
|
260
|
+
*
|
|
261
|
+
* ## Ownership and lifetime
|
|
262
|
+
*
|
|
263
|
+
* Sampled results and pushed sessions are isolated by Effect Context identity. Within one Context,
|
|
264
|
+
* current reads cache by source version and observers share the active held projection. Its
|
|
265
|
+
* Effect read fails with NoSuchElement while absent; the observing Scope owns its subscription.
|
|
266
|
+
*
|
|
267
|
+
* @since 1.18.0
|
|
268
|
+
* @category Optional queries
|
|
269
|
+
*/
|
|
63
270
|
export function makeFiltered(input, f) {
|
|
64
271
|
return new FilteredImpl(input, f);
|
|
65
272
|
}
|
|
@@ -69,14 +276,14 @@ class FilteredImpl extends Versioned.VersionedTransform {
|
|
|
69
276
|
input;
|
|
70
277
|
f;
|
|
71
278
|
constructor(input, f) {
|
|
72
|
-
super(input, (fx) => fxFilterMapEffect(fx, f), (effect) => Effect.flatMap(Effect.flatMap(effect, f),
|
|
279
|
+
super(input, (fx) => fxFilterMapEffect(fx, f), (effect) => Effect.flatMap(Effect.flatMap(effect, f), Effect.fromOption));
|
|
73
280
|
this.input = input;
|
|
74
281
|
this.f = f;
|
|
75
|
-
this._computed =
|
|
282
|
+
this._computed = holdInContext(unwrap(Effect.map(Effect.context(), (ctx) => {
|
|
76
283
|
if (checkIsMultiple(ctx)) {
|
|
77
|
-
return
|
|
284
|
+
return fxFromEffect(input).pipe(continueWith(() => input), skipRepeats, fxFilterMapEffect(f));
|
|
78
285
|
}
|
|
79
|
-
return fxCompact(fxFromEffect(Effect.flatMap(input
|
|
286
|
+
return fxCompact(fxFromEffect(Effect.flatMap(input, f)));
|
|
80
287
|
})));
|
|
81
288
|
}
|
|
82
289
|
run(sink) {
|
|
@@ -86,6 +293,7 @@ class FilteredImpl extends Versioned.VersionedTransform {
|
|
|
86
293
|
return new ComputedImpl(this.input, this.f);
|
|
87
294
|
}
|
|
88
295
|
}
|
|
296
|
+
let nextTransactionOrder = 0;
|
|
89
297
|
class RefSubjectCore {
|
|
90
298
|
initial;
|
|
91
299
|
subject;
|
|
@@ -93,6 +301,7 @@ class RefSubjectCore {
|
|
|
93
301
|
scope;
|
|
94
302
|
deferredRef;
|
|
95
303
|
semaphore;
|
|
304
|
+
transactionOrder = nextTransactionOrder++;
|
|
96
305
|
constructor(initial, subject, services, scope, deferredRef, semaphore) {
|
|
97
306
|
this.initial = initial;
|
|
98
307
|
this.subject = subject;
|
|
@@ -103,10 +312,116 @@ class RefSubjectCore {
|
|
|
103
312
|
}
|
|
104
313
|
_fiber = undefined;
|
|
105
314
|
}
|
|
106
|
-
function
|
|
315
|
+
function combineTransactionCores(accesses) {
|
|
316
|
+
return [...new Set(accesses.flatMap((access) => access.cores))].sort((left, right) => left.transactionOrder - right.transactionOrder);
|
|
317
|
+
}
|
|
318
|
+
function getTupleTransactionAccess(refs) {
|
|
319
|
+
const accessEffects = refs.map(getTransactionAccess);
|
|
320
|
+
if (!accessEffects.every((access) => access !== undefined)) {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
return Effect.map(Effect.all(accessEffects, UNBOUNDED), (accessOptions) => Option.map(Option.all(accessOptions), (accesses) => ({
|
|
324
|
+
cores: combineTransactionCores(accesses),
|
|
325
|
+
getSetDelete: (recordCommit) => {
|
|
326
|
+
const transactions = accesses.map((access) => access.getSetDelete(recordCommit));
|
|
327
|
+
return {
|
|
328
|
+
get: Effect.all(transactions.map((transaction) => transaction.get), UNBOUNDED),
|
|
329
|
+
set: (value) => Effect.all(transactions.map((transaction, index) => transaction.set(value[index])), UNBOUNDED),
|
|
330
|
+
delete: Effect.map(Effect.all(transactions.map((transaction) => transaction.delete), UNBOUNDED), Option.all),
|
|
331
|
+
};
|
|
332
|
+
},
|
|
333
|
+
})));
|
|
334
|
+
}
|
|
335
|
+
function getStructTransactionAccess(refs) {
|
|
336
|
+
const keys = Object.keys(refs);
|
|
337
|
+
const accessEffects = keys.map((key) => getTransactionAccess(refs[key]));
|
|
338
|
+
if (!accessEffects.every((access) => access !== undefined)) {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
return Effect.map(Effect.all(accessEffects, UNBOUNDED), (accessOptions) => Option.map(Option.all(accessOptions), (accesses) => ({
|
|
342
|
+
cores: combineTransactionCores(accesses),
|
|
343
|
+
getSetDelete: (recordCommit) => {
|
|
344
|
+
const transactions = accesses.map((access) => access.getSetDelete(recordCommit));
|
|
345
|
+
return {
|
|
346
|
+
get: Effect.map(Effect.all(transactions.map((transaction) => transaction.get), UNBOUNDED), (values) => Object.fromEntries(keys.map((key, index) => [key, values[index]]))),
|
|
347
|
+
set: (value) => Effect.all(transactions.map((transaction, index) => transaction.set(value[keys[index]])), UNBOUNDED),
|
|
348
|
+
delete: Effect.map(Effect.all(transactions.map((transaction) => transaction.delete), UNBOUNDED), (values) => Option.all(Object.fromEntries(keys.map((key, index) => [key, values[index]])))),
|
|
349
|
+
};
|
|
350
|
+
},
|
|
351
|
+
})));
|
|
352
|
+
}
|
|
353
|
+
function getTransactionAccess(ref) {
|
|
354
|
+
if (ref instanceof RefSubjectImpl) {
|
|
355
|
+
return Effect.succeed(Option.some({
|
|
356
|
+
cores: [ref.core],
|
|
357
|
+
getSetDelete: (recordCommit) => getSetDelete(ref.core, (exit) => recordCommit([ref.core, exit, ref.core.deferredRef.version])),
|
|
358
|
+
}));
|
|
359
|
+
}
|
|
360
|
+
if (ref instanceof RefSubjectSimpleTransform)
|
|
361
|
+
return getTransactionAccess(ref.ref);
|
|
362
|
+
if (ref instanceof RefSubjectTransform) {
|
|
363
|
+
const accessEffect = getTransactionAccess(ref.ref);
|
|
364
|
+
if (!accessEffect)
|
|
365
|
+
return undefined;
|
|
366
|
+
return Effect.map(accessEffect, Option.map((access) => ({
|
|
367
|
+
cores: access.cores,
|
|
368
|
+
getSetDelete: (recordCommit) => {
|
|
369
|
+
const transaction = access.getSetDelete(recordCommit);
|
|
370
|
+
return {
|
|
371
|
+
get: Effect.map(transaction.get, ref.toB),
|
|
372
|
+
set: (value) => Effect.map(transaction.set(ref.toA(value)), ref.toB),
|
|
373
|
+
delete: Effect.map(transaction.delete, Option.map(ref.toB)),
|
|
374
|
+
};
|
|
375
|
+
},
|
|
376
|
+
})));
|
|
377
|
+
}
|
|
378
|
+
if (ref instanceof RefSubjectTuple)
|
|
379
|
+
return getTupleTransactionAccess(ref.refs);
|
|
380
|
+
if (ref instanceof RefSubjectStruct)
|
|
381
|
+
return getStructTransactionAccess(ref.refs);
|
|
382
|
+
if (typeof ref === "function") {
|
|
383
|
+
const service = ref.service;
|
|
384
|
+
if (Effect.isEffect(service)) {
|
|
385
|
+
return Effect.flatMap(service, (providedRef) => {
|
|
386
|
+
const accessEffect = getTransactionAccess(providedRef);
|
|
387
|
+
return accessEffect ?? Effect.succeed(Option.none());
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
function withTransactionLocks(cores, effect) {
|
|
394
|
+
let locked = effect;
|
|
395
|
+
for (let index = cores.length - 1; index >= 0; index--) {
|
|
396
|
+
locked = cores[index].semaphore.withPermits(1)(locked);
|
|
397
|
+
}
|
|
398
|
+
return locked;
|
|
399
|
+
}
|
|
400
|
+
function runTransaction(access, run) {
|
|
401
|
+
return Effect.uninterruptibleMask((restore) => {
|
|
402
|
+
const commits = [];
|
|
403
|
+
const transaction = restore(withTransactionLocks(access.cores, Effect.suspend(() => run(access.getSetDelete((commit) => {
|
|
404
|
+
commits.push(commit);
|
|
405
|
+
})))));
|
|
406
|
+
return Effect.flatMap(Effect.exit(transaction), (exit) => Effect.andThen(Effect.forEach(commits, ([core, commit, version]) => sendCurrentEvent(core, commit, version), {
|
|
407
|
+
discard: true,
|
|
408
|
+
}), exit));
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
function sendCurrentEvent(core, commit, version) {
|
|
412
|
+
return Effect.suspend(() => {
|
|
413
|
+
const current = MutableRef.get(core.deferredRef.current);
|
|
414
|
+
return core.deferredRef.version === version &&
|
|
415
|
+
Option.isSome(current) &&
|
|
416
|
+
core.deferredRef.eq(current.value, commit)
|
|
417
|
+
? sendEvent(core, commit)
|
|
418
|
+
: Effect.void;
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
function getSetDelete(ref, recordCommit) {
|
|
107
422
|
return {
|
|
108
423
|
get: getOrInitializeCore(ref, false),
|
|
109
|
-
set: (a) =>
|
|
424
|
+
set: (a) => bufferSuccessCore(ref, a, recordCommit),
|
|
110
425
|
delete: deleteCore(ref),
|
|
111
426
|
};
|
|
112
427
|
}
|
|
@@ -116,7 +431,6 @@ class RefSubjectImpl extends YieldableFx {
|
|
|
116
431
|
version;
|
|
117
432
|
interrupt;
|
|
118
433
|
subscriberCount;
|
|
119
|
-
getSetDelete;
|
|
120
434
|
core;
|
|
121
435
|
constructor(core) {
|
|
122
436
|
super();
|
|
@@ -124,25 +438,25 @@ class RefSubjectImpl extends YieldableFx {
|
|
|
124
438
|
this.version = Effect.sync(() => core.deferredRef.version);
|
|
125
439
|
this.interrupt = Effect.provide(interruptCore(core), core.services);
|
|
126
440
|
this.subscriberCount = Effect.provide(core.subject.subscriberCount, core.services);
|
|
127
|
-
this.getSetDelete = getSetDelete(core);
|
|
128
441
|
this.updates = this.updates.bind(this);
|
|
129
442
|
this.onSuccess = this.onSuccess.bind(this);
|
|
130
443
|
this.onFailure = this.onFailure.bind(this);
|
|
131
444
|
}
|
|
132
445
|
run(sink) {
|
|
446
|
+
const subscribe = Effect.provide(this.core.subject.run(sink), this.core.services);
|
|
133
447
|
return Effect.matchCauseEffect(getOrInitializeCore(this.core, true), {
|
|
134
|
-
onFailure: (
|
|
135
|
-
onSuccess: () =>
|
|
448
|
+
onFailure: () => subscribe,
|
|
449
|
+
onSuccess: () => subscribe,
|
|
136
450
|
});
|
|
137
451
|
}
|
|
138
452
|
updates(run) {
|
|
139
|
-
return this.core
|
|
453
|
+
return updateCore(this.core, (recordCommit) => run(getSetDelete(this.core, recordCommit)));
|
|
140
454
|
}
|
|
141
455
|
onSuccess(value) {
|
|
142
|
-
return
|
|
456
|
+
return updateCore(this.core, (recordCommit) => bufferSuccessCore(this.core, value, recordCommit));
|
|
143
457
|
}
|
|
144
458
|
onFailure(cause) {
|
|
145
|
-
return
|
|
459
|
+
return updateCore(this.core, (recordCommit) => bufferFailureCore(this.core, cause, recordCommit));
|
|
146
460
|
}
|
|
147
461
|
toEffect() {
|
|
148
462
|
return getOrInitializeCore(this.core, true);
|
|
@@ -151,6 +465,31 @@ class RefSubjectImpl extends YieldableFx {
|
|
|
151
465
|
/**
|
|
152
466
|
* Creates a new `RefSubject` from a value, `Effect`, or `Fx`.
|
|
153
467
|
*
|
|
468
|
+
* @remarks
|
|
469
|
+
* ## Why
|
|
470
|
+
*
|
|
471
|
+
* `RefSubject` keeps current state and pushed changes independent of any UI
|
|
472
|
+
* renderer. Domain transitions can therefore be constructed, composed, and
|
|
473
|
+
* tested with Effect alone; rendering is one optional consumer of the state.
|
|
474
|
+
* Plain values, Effect programs, pull-based Effect `Stream`s, and push-based
|
|
475
|
+
* `Fx` sources all enter through the same state boundary.
|
|
476
|
+
*
|
|
477
|
+
* ## Ownership and lifetime
|
|
478
|
+
*
|
|
479
|
+
* Creation requires an Effect `Scope`. That scope owns source acquisition,
|
|
480
|
+
* subscriptions, and finalization. The returned `RefSubject` exposes the
|
|
481
|
+
* source error type when read or observed, while construction itself cannot
|
|
482
|
+
* fail. Closing the scope interrupts a live source and releases its resources.
|
|
483
|
+
*
|
|
484
|
+
* ## Initialization
|
|
485
|
+
*
|
|
486
|
+
* Calling `make` is lazy because it returns an Effect: nothing is allocated and no source runs
|
|
487
|
+
* until that creation Effect is executed. After creation, a plain value is immediately available.
|
|
488
|
+
* An Effect input runs once on the first current read or observation. Fx and Stream inputs instead
|
|
489
|
+
* start one shared source run when the creation Effect executes; reads wait for their first value
|
|
490
|
+
* or failure and later emissions replace the retained current state. Equality and buffering
|
|
491
|
+
* behavior can be customized with `RefSubjectOptions`.
|
|
492
|
+
*
|
|
154
493
|
* @example
|
|
155
494
|
* ```ts
|
|
156
495
|
* import { Effect } from "effect"
|
|
@@ -184,7 +523,7 @@ class RefSubjectImpl extends YieldableFx {
|
|
|
184
523
|
* ```
|
|
185
524
|
*
|
|
186
525
|
* @since 1.0.0
|
|
187
|
-
* @category
|
|
526
|
+
* @category Constructors
|
|
188
527
|
*/
|
|
189
528
|
export function make(effect, options) {
|
|
190
529
|
if (isFx(effect)) {
|
|
@@ -203,6 +542,19 @@ export function make(effect, options) {
|
|
|
203
542
|
/**
|
|
204
543
|
* Creates a `RefSubject` from an `Effect`.
|
|
205
544
|
*
|
|
545
|
+
* @remarks
|
|
546
|
+
* ## Why
|
|
547
|
+
*
|
|
548
|
+
* Makes effect acquisition explicit while normalizing it into current RefSubject state and
|
|
549
|
+
* subsequent pushed versions.
|
|
550
|
+
*
|
|
551
|
+
* ## Ownership and lifetime
|
|
552
|
+
*
|
|
553
|
+
* The creation Effect requires Scope and the source's services, which it captures for the private
|
|
554
|
+
* initializer Scope. The source Effect itself stays deferred until the first current read or
|
|
555
|
+
* observation and runs at most once. Source failures remain visible on reads and observations;
|
|
556
|
+
* closing the creation Scope interrupts an initializer that is still running.
|
|
557
|
+
*
|
|
206
558
|
* @example
|
|
207
559
|
* ```ts
|
|
208
560
|
* import { Effect } from "effect"
|
|
@@ -219,7 +571,7 @@ export function make(effect, options) {
|
|
|
219
571
|
* ```
|
|
220
572
|
*
|
|
221
573
|
* @since 1.0.0
|
|
222
|
-
* @category
|
|
574
|
+
* @category Source adapters
|
|
223
575
|
*/
|
|
224
576
|
export function fromEffect(effect, options) {
|
|
225
577
|
return Effect.map(makeCore(effect, options), (core) => new RefSubjectImpl(core));
|
|
@@ -227,6 +579,17 @@ export function fromEffect(effect, options) {
|
|
|
227
579
|
/**
|
|
228
580
|
* Creates a `RefSubject` from an `Fx`, tracking the latest emitted value.
|
|
229
581
|
*
|
|
582
|
+
* @remarks
|
|
583
|
+
* ## Why
|
|
584
|
+
*
|
|
585
|
+
* Makes fx acquisition explicit while normalizing it into current RefSubject state and subsequent
|
|
586
|
+
* pushed versions.
|
|
587
|
+
*
|
|
588
|
+
* ## Ownership and lifetime
|
|
589
|
+
*
|
|
590
|
+
* The creation Effect requires Scope. It owns initializer acquisition, live source subscriptions,
|
|
591
|
+
* and cleanup; source failures and services stay on reads and pushes.
|
|
592
|
+
*
|
|
230
593
|
* @example
|
|
231
594
|
* ```ts
|
|
232
595
|
* import { Effect } from "effect"
|
|
@@ -247,35 +610,136 @@ export function fromEffect(effect, options) {
|
|
|
247
610
|
* ```
|
|
248
611
|
*
|
|
249
612
|
* @since 1.0.0
|
|
250
|
-
* @category
|
|
613
|
+
* @category Source adapters
|
|
251
614
|
*/
|
|
252
615
|
export function fromFx(fx, options) {
|
|
253
616
|
return Effect.gen(function* () {
|
|
254
617
|
const core = yield* makeDeferredCore(options);
|
|
255
618
|
const ref = new RefSubjectImpl(core);
|
|
256
|
-
yield* Effect.forkIn(fx.run(Sink.make((cause) =>
|
|
619
|
+
yield* Effect.forkIn(fx.run(Sink.make((cause) => updateCore(core, (recordCommit) => bufferFailureCore(core, cause, recordCommit)), (value) => updateCore(core, (recordCommit) => bufferSuccessCore(core, value, recordCommit)))), core.scope, { startImmediately: true });
|
|
257
620
|
return ref;
|
|
258
621
|
});
|
|
259
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* Creates a RefSubject that tracks the latest value emitted by an Effect Stream.
|
|
625
|
+
*
|
|
626
|
+
* @remarks
|
|
627
|
+
* ## Why
|
|
628
|
+
*
|
|
629
|
+
* Bridges a pull-based Stream into renderer-independent state: each emitted value enters the
|
|
630
|
+
* RefSubject's serialized commit path, while a stream failure becomes the ref's current and pushed
|
|
631
|
+
* failure.
|
|
632
|
+
*
|
|
633
|
+
* ## Ownership and lifetime
|
|
634
|
+
*
|
|
635
|
+
* Construction forks stream consumption immediately into the RefSubject's private Scope and cannot
|
|
636
|
+
* fail. `R` is required while constructing that fiber; closing the outer Scope interrupts it.
|
|
637
|
+
* Before the first value or failure, a current read waits for initialization. Later reads and pushes
|
|
638
|
+
* expose `E`.
|
|
639
|
+
*
|
|
640
|
+
* @since 1.18.0
|
|
641
|
+
* @category Source adapters
|
|
642
|
+
*/
|
|
260
643
|
export function fromStream(stream, options) {
|
|
261
644
|
return Effect.gen(function* () {
|
|
262
645
|
const core = yield* makeDeferredCore(options);
|
|
263
646
|
const ref = new RefSubjectImpl(core);
|
|
264
|
-
yield* Effect.forkIn(stream.pipe(redirectCause(core), Stream.runForEach((value) =>
|
|
647
|
+
yield* Effect.forkIn(stream.pipe(redirectCause(core), Stream.runForEach((value) => updateCore(core, (recordCommit) => bufferSuccessCore(core, value, recordCommit)))), core.scope, { startImmediately: true });
|
|
265
648
|
return ref;
|
|
266
649
|
});
|
|
267
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* Creates a `RefSubject` from an `Option` value.
|
|
653
|
+
*
|
|
654
|
+
* @remarks
|
|
655
|
+
* ## Why
|
|
656
|
+
*
|
|
657
|
+
* Stores the Option itself in writable state. `None` is an ordinary current value and does not add
|
|
658
|
+
* `NoSuchElementError`; callers opt into absence semantics with `compact`, or a fallback with
|
|
659
|
+
* `getOrElse`.
|
|
660
|
+
*
|
|
661
|
+
* ## Ownership and lifetime
|
|
662
|
+
*
|
|
663
|
+
* Construction requires Scope because it delegates to `make`, but the supplied Option needs no
|
|
664
|
+
* services and cannot fail. The returned RefSubject owns and publishes subsequent Option writes.
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```ts
|
|
668
|
+
* import { Effect, Option } from "effect"
|
|
669
|
+
* import * as RefSubject from "@typed/fx/RefSubject"
|
|
670
|
+
*
|
|
671
|
+
* const program = Effect.gen(function* () {
|
|
672
|
+
* const ref = yield* RefSubject.fromOption(Option.some(42))
|
|
673
|
+
* const value = yield* ref
|
|
674
|
+
* console.log(Option.isSome(value)) // true
|
|
675
|
+
* })
|
|
676
|
+
* ```
|
|
677
|
+
*
|
|
678
|
+
* @since 1.0.0
|
|
679
|
+
* @category Source adapters
|
|
680
|
+
*/
|
|
681
|
+
export function fromOption(option, options) {
|
|
682
|
+
return make(option, options);
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Creates a `RefSubject` from a nullable value (null/undefined become `Option.none()`).
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```ts
|
|
689
|
+
* import { Effect, Option } from "effect"
|
|
690
|
+
* import * as RefSubject from "@typed/fx/RefSubject"
|
|
691
|
+
*
|
|
692
|
+
* const program = Effect.gen(function* () {
|
|
693
|
+
* const ref = yield* RefSubject.fromNullable("hello")
|
|
694
|
+
* const value = yield* ref
|
|
695
|
+
* console.log(Option.isSome(value)) // true
|
|
696
|
+
*
|
|
697
|
+
* const empty = yield* RefSubject.fromNullable(null)
|
|
698
|
+
* const none = yield* empty
|
|
699
|
+
* console.log(Option.isNone(none)) // true
|
|
700
|
+
* })
|
|
701
|
+
* ```
|
|
702
|
+
*
|
|
703
|
+
* @since 1.0.0
|
|
704
|
+
* @category Source adapters
|
|
705
|
+
*/
|
|
706
|
+
function optionFromNullable(value) {
|
|
707
|
+
return value === null || value === undefined
|
|
708
|
+
? Option.none()
|
|
709
|
+
: Option.some(value);
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Creates Option-valued RefSubject state from a nullable input.
|
|
713
|
+
*
|
|
714
|
+
* @remarks
|
|
715
|
+
* ## Why
|
|
716
|
+
*
|
|
717
|
+
* Converts `null` and `undefined` to `Option.none` and every other value to `Option.some`, then
|
|
718
|
+
* stores that Option in a writable RefSubject. It does not create Filtered state or add
|
|
719
|
+
* `NoSuchElementError`.
|
|
720
|
+
*
|
|
721
|
+
* ## Ownership and lifetime
|
|
722
|
+
*
|
|
723
|
+
* Construction requires Scope because it delegates to `make`; it needs no services and cannot
|
|
724
|
+
* fail. The returned RefSubject owns and publishes subsequent Option writes.
|
|
725
|
+
*
|
|
726
|
+
* @since 1.18.0
|
|
727
|
+
* @category Source adapters
|
|
728
|
+
*/
|
|
729
|
+
export function fromNullable(value, options) {
|
|
730
|
+
return make(optionFromNullable(value), options);
|
|
731
|
+
}
|
|
268
732
|
function redirectCause(core) {
|
|
269
|
-
return Stream.catchCause((cause) => Stream.unwrap(Effect.as(
|
|
733
|
+
return Stream.catchCause((cause) => Stream.unwrap(Effect.as(updateCore(core, (recordCommit) => bufferFailureCore(core, cause, recordCommit)), Stream.empty)));
|
|
270
734
|
}
|
|
271
735
|
function makeCore(initial, options, deferredRef) {
|
|
272
736
|
return Effect.gen(function* () {
|
|
273
|
-
const services = yield* Effect.
|
|
274
|
-
const scope = yield* Scope.fork(
|
|
737
|
+
const services = yield* Effect.context();
|
|
738
|
+
const scope = yield* Scope.fork(Context.get(services, Scope.Scope));
|
|
275
739
|
const id = yield* Effect.withFiber((fiber) => Effect.succeed(fiber.id));
|
|
276
740
|
const subject = new Subject.HoldSubjectImpl();
|
|
277
741
|
const core = new RefSubjectCore(initial, subject, services, scope, deferredRef ??
|
|
278
|
-
DeferredRef.unsafeMake(id, getExitEquivalence(options?.eq ?? equals), subject.lastValue),
|
|
742
|
+
DeferredRef.unsafeMake(id, getExitEquivalence(options?.eq ?? equals), subject.lastValue), Semaphore.makeUnsafe(1));
|
|
279
743
|
yield* Scope.addFinalizer(scope, core.subject.interrupt);
|
|
280
744
|
return core;
|
|
281
745
|
});
|
|
@@ -283,7 +747,7 @@ function makeCore(initial, options, deferredRef) {
|
|
|
283
747
|
function makeDeferredCore(options) {
|
|
284
748
|
return Effect.gen(function* () {
|
|
285
749
|
const deferredRef = yield* DeferredRef.make(getExitEquivalence(options?.eq ?? equals));
|
|
286
|
-
return yield* makeCore(deferredRef
|
|
750
|
+
return yield* makeCore(deferredRef, options, deferredRef);
|
|
287
751
|
});
|
|
288
752
|
}
|
|
289
753
|
function getOrInitializeCore(core, lockInitialize) {
|
|
@@ -292,30 +756,51 @@ function getOrInitializeCore(core, lockInitialize) {
|
|
|
292
756
|
return initializeCoreAndTap(core, lockInitialize);
|
|
293
757
|
}
|
|
294
758
|
else {
|
|
295
|
-
return core.deferredRef
|
|
759
|
+
return core.deferredRef;
|
|
296
760
|
}
|
|
297
761
|
});
|
|
298
762
|
}
|
|
299
763
|
function initializeCoreEffect(core, lock) {
|
|
764
|
+
let completed = false;
|
|
300
765
|
const initialize = Effect.onExit(Effect.provide(core.initial, core.services), (exit) => Effect.sync(() => {
|
|
766
|
+
completed = true;
|
|
301
767
|
core._fiber = undefined;
|
|
302
768
|
core.deferredRef.done(exit);
|
|
303
769
|
}));
|
|
304
|
-
|
|
770
|
+
const isSourceBacked = core.initial === core.deferredRef;
|
|
771
|
+
return Effect.flatMap(Effect.forkIn(lock && !isSourceBacked ? core.semaphore.withPermits(1)(initialize) : initialize, core.scope,
|
|
772
|
+
// Acquire the initializer's permit before a competing update can hold it while awaiting us.
|
|
773
|
+
{ startImmediately: true }), (fiber) => Effect.sync(() => {
|
|
774
|
+
// A synchronous initializer has already cleared its ownership in onExit.
|
|
775
|
+
if (!completed)
|
|
776
|
+
core._fiber = fiber;
|
|
777
|
+
return fiber;
|
|
778
|
+
}));
|
|
305
779
|
}
|
|
306
780
|
function initializeCoreAndTap(core, lock) {
|
|
307
|
-
return Effect.flatMapEager(initializeCoreEffect(core, lock), () =>
|
|
781
|
+
return Effect.flatMapEager(initializeCoreEffect(core, lock), () =>
|
|
782
|
+
// Kind of weird hack, but this second clause will happen for HydrationRefs that are already hydrated
|
|
783
|
+
core.deferredRef.current === core.subject.lastValue
|
|
784
|
+
? tapEventCore(core, core.deferredRef)
|
|
785
|
+
: core.deferredRef);
|
|
786
|
+
}
|
|
787
|
+
function updateCore(core, run) {
|
|
788
|
+
return Effect.uninterruptibleMask((restore) => {
|
|
789
|
+
const commits = [];
|
|
790
|
+
const transaction = restore(core.semaphore.withPermits(1)(Effect.suspend(() => run((commit) => {
|
|
791
|
+
commits.push(commit);
|
|
792
|
+
}))));
|
|
793
|
+
return Effect.flatMap(Effect.exit(transaction), (exit) => Effect.andThen(Effect.forEach(commits, (commit) => sendEvent(core, commit), { discard: true }), exit));
|
|
794
|
+
});
|
|
308
795
|
}
|
|
309
796
|
function setCore(core, a) {
|
|
310
797
|
return Effect.suspend(() => {
|
|
311
798
|
const exit = Exit.succeed(a);
|
|
312
799
|
if (core.deferredRef.done(exit)) {
|
|
313
|
-
|
|
314
|
-
return Effect.as(sendEvent(core, exit), a);
|
|
800
|
+
return Effect.succeed(Option.some(exit));
|
|
315
801
|
}
|
|
316
802
|
else {
|
|
317
|
-
|
|
318
|
-
return Effect.succeed(a);
|
|
803
|
+
return Effect.succeed(Option.none());
|
|
319
804
|
}
|
|
320
805
|
});
|
|
321
806
|
}
|
|
@@ -323,13 +808,26 @@ function onFailureCore(core, cause) {
|
|
|
323
808
|
const exit = Exit.failCause(cause);
|
|
324
809
|
return Effect.suspend(() => {
|
|
325
810
|
if (core.deferredRef.done(exit)) {
|
|
326
|
-
return
|
|
811
|
+
return Effect.succeed(Option.some(exit));
|
|
327
812
|
}
|
|
328
813
|
else {
|
|
329
|
-
return Effect.
|
|
814
|
+
return Effect.succeed(Option.none());
|
|
330
815
|
}
|
|
331
816
|
});
|
|
332
817
|
}
|
|
818
|
+
function bufferSuccessCore(core, value, recordCommit) {
|
|
819
|
+
return Effect.map(setCore(core, value), (commit) => {
|
|
820
|
+
if (Option.isSome(commit))
|
|
821
|
+
recordCommit(commit.value);
|
|
822
|
+
return value;
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
function bufferFailureCore(core, cause, recordCommit) {
|
|
826
|
+
return Effect.asVoid(Effect.map(onFailureCore(core, cause), (commit) => {
|
|
827
|
+
if (Option.isSome(commit))
|
|
828
|
+
recordCommit(commit.value);
|
|
829
|
+
}));
|
|
830
|
+
}
|
|
333
831
|
function interruptCore(core) {
|
|
334
832
|
return Effect.withFiber((fiber) => {
|
|
335
833
|
core.deferredRef.reset();
|
|
@@ -363,6 +861,17 @@ function sendEvent(core, exit) {
|
|
|
363
861
|
/**
|
|
364
862
|
* Sets the value of a `RefSubject`.
|
|
365
863
|
*
|
|
864
|
+
* @remarks
|
|
865
|
+
* ## Why
|
|
866
|
+
*
|
|
867
|
+
* Sets the value of a `RefSubject`. The transition is serialized at the RefSubject rather than
|
|
868
|
+
* coordinated by callers or UI components.
|
|
869
|
+
*
|
|
870
|
+
* ## Ownership and lifetime
|
|
871
|
+
*
|
|
872
|
+
* Running set performs one serialized subject transition and resolves with its committed value. It
|
|
873
|
+
* acquires no resource; failures and services remain those of the source ref.
|
|
874
|
+
*
|
|
366
875
|
* @example
|
|
367
876
|
* ```ts
|
|
368
877
|
* import { Effect } from "effect"
|
|
@@ -384,7 +893,7 @@ function sendEvent(core, exit) {
|
|
|
384
893
|
* ```
|
|
385
894
|
*
|
|
386
895
|
* @since 1.0.0
|
|
387
|
-
* @category
|
|
896
|
+
* @category State updates
|
|
388
897
|
*/
|
|
389
898
|
export const set = dual(2, function set(ref, a) {
|
|
390
899
|
return ref.updates((ref) => ref.set(a));
|
|
@@ -392,6 +901,17 @@ export const set = dual(2, function set(ref, a) {
|
|
|
392
901
|
/**
|
|
393
902
|
* Resets a `RefSubject` to its initial value, returning the previous value if it existed.
|
|
394
903
|
*
|
|
904
|
+
* @remarks
|
|
905
|
+
* ## Why
|
|
906
|
+
*
|
|
907
|
+
* Resets a `RefSubject` to its initial value, returning the previous value if it existed. The
|
|
908
|
+
* transition is serialized at the RefSubject rather than coordinated by callers or UI components.
|
|
909
|
+
*
|
|
910
|
+
* ## Ownership and lifetime
|
|
911
|
+
*
|
|
912
|
+
* Running reset performs one serialized subject transition and resolves with its committed value.
|
|
913
|
+
* It acquires no resource; failures and services remain those of the source ref.
|
|
914
|
+
*
|
|
395
915
|
* @example
|
|
396
916
|
* ```ts
|
|
397
917
|
* import { Effect, Option } from "effect"
|
|
@@ -415,7 +935,7 @@ export const set = dual(2, function set(ref, a) {
|
|
|
415
935
|
* ```
|
|
416
936
|
*
|
|
417
937
|
* @since 1.0.0
|
|
418
|
-
* @category
|
|
938
|
+
* @category State updates
|
|
419
939
|
*/
|
|
420
940
|
export function reset(ref) {
|
|
421
941
|
return ref.updates((ref) => ref.delete);
|
|
@@ -445,12 +965,23 @@ export {
|
|
|
445
965
|
* ```
|
|
446
966
|
*
|
|
447
967
|
* @since 1.20.0
|
|
448
|
-
* @category
|
|
968
|
+
* @category State updates
|
|
449
969
|
*/
|
|
450
970
|
reset as delete, };
|
|
451
971
|
/**
|
|
452
972
|
* Updates a `RefSubject` using an `Effect`ful function.
|
|
453
973
|
*
|
|
974
|
+
* @remarks
|
|
975
|
+
* ## Why
|
|
976
|
+
*
|
|
977
|
+
* Updates a `RefSubject` using an `Effect`ful function. The transition is serialized at the
|
|
978
|
+
* RefSubject rather than coordinated by callers or UI components.
|
|
979
|
+
*
|
|
980
|
+
* ## Ownership and lifetime
|
|
981
|
+
*
|
|
982
|
+
* Running update effect performs one serialized subject transition and resolves with its committed
|
|
983
|
+
* value. It acquires no resource; failures and services remain those of the source ref.
|
|
984
|
+
*
|
|
454
985
|
* @example
|
|
455
986
|
* ```ts
|
|
456
987
|
* import { Effect } from "effect"
|
|
@@ -470,7 +1001,7 @@ reset as delete, };
|
|
|
470
1001
|
* ```
|
|
471
1002
|
*
|
|
472
1003
|
* @since 1.0.0
|
|
473
|
-
* @category
|
|
1004
|
+
* @category State updates
|
|
474
1005
|
*/
|
|
475
1006
|
export const updateEffect = dual(2, function updateEffect(ref, f) {
|
|
476
1007
|
return ref.updates((ref) => Effect.flatMap(Effect.flatMap(ref.get, f), ref.set));
|
|
@@ -478,6 +1009,26 @@ export const updateEffect = dual(2, function updateEffect(ref, f) {
|
|
|
478
1009
|
/**
|
|
479
1010
|
* Updates a `RefSubject` using a pure function.
|
|
480
1011
|
*
|
|
1012
|
+
* @remarks
|
|
1013
|
+
* ## Why
|
|
1014
|
+
*
|
|
1015
|
+
* `update` expresses a state transition where the state lives, without moving
|
|
1016
|
+
* transition logic into a component or renderer. The same function can run in
|
|
1017
|
+
* application code, tests, workers, event handlers, or a Typed template.
|
|
1018
|
+
*
|
|
1019
|
+
* ## Ownership and lifetime
|
|
1020
|
+
*
|
|
1021
|
+
* The update runs inside the RefSubject's existing synchronized update
|
|
1022
|
+
* boundary: it reads the committed value, derives the next value, commits it,
|
|
1023
|
+
* and publishes that change. It acquires no independent resource and retains
|
|
1024
|
+
* the subject's existing error and service channels.
|
|
1025
|
+
*
|
|
1026
|
+
* ## Concurrency and failure
|
|
1027
|
+
*
|
|
1028
|
+
* Concurrent updates are serialized by the subject core so each function sees
|
|
1029
|
+
* a committed value. A pure updater adds no error channel; failures already
|
|
1030
|
+
* associated with reading or writing the RefSubject remain explicit in `E`.
|
|
1031
|
+
*
|
|
481
1032
|
* @example
|
|
482
1033
|
* ```ts
|
|
483
1034
|
* import { Effect } from "effect"
|
|
@@ -499,7 +1050,7 @@ export const updateEffect = dual(2, function updateEffect(ref, f) {
|
|
|
499
1050
|
* ```
|
|
500
1051
|
*
|
|
501
1052
|
* @since 1.0.0
|
|
502
|
-
* @category
|
|
1053
|
+
* @category State updates
|
|
503
1054
|
*/
|
|
504
1055
|
export const update = dual(2, function update(ref, f) {
|
|
505
1056
|
return updateEffect(ref, (value) => Effect.succeed(f(value)));
|
|
@@ -507,6 +1058,18 @@ export const update = dual(2, function update(ref, f) {
|
|
|
507
1058
|
/**
|
|
508
1059
|
* Modifies a `RefSubject` using an `Effect`ful function that returns both a result and a new value.
|
|
509
1060
|
*
|
|
1061
|
+
* @remarks
|
|
1062
|
+
* ## Why
|
|
1063
|
+
*
|
|
1064
|
+
* Modifies a `RefSubject` using an `Effect`ful function that returns both a result and a new
|
|
1065
|
+
* value. The transition is serialized at the RefSubject rather than coordinated by callers or UI
|
|
1066
|
+
* components.
|
|
1067
|
+
*
|
|
1068
|
+
* ## Ownership and lifetime
|
|
1069
|
+
*
|
|
1070
|
+
* Running modify effect performs one serialized subject transition and resolves with its committed
|
|
1071
|
+
* value. It acquires no resource; failures and services remain those of the source ref.
|
|
1072
|
+
*
|
|
510
1073
|
* @example
|
|
511
1074
|
* ```ts
|
|
512
1075
|
* import { Effect } from "effect"
|
|
@@ -527,14 +1090,25 @@ export const update = dual(2, function update(ref, f) {
|
|
|
527
1090
|
* ```
|
|
528
1091
|
*
|
|
529
1092
|
* @since 1.0.0
|
|
530
|
-
* @category
|
|
1093
|
+
* @category Transactions
|
|
531
1094
|
*/
|
|
532
1095
|
export const modifyEffect = dual(2, function modifyEffect(ref, f) {
|
|
533
|
-
return ref.updates((ref) => Effect.flatMap(ref.get, (value) => Effect.flatMap(f(value), ([b, a]) => Effect.
|
|
1096
|
+
return ref.updates((ref) => Effect.flatMap(ref.get, (value) => Effect.flatMap(f(value), ([b, a]) => Effect.as(ref.set(a), b))));
|
|
534
1097
|
});
|
|
535
1098
|
/**
|
|
536
1099
|
* Modifies a `RefSubject` using a pure function that returns both a result and a new value.
|
|
537
1100
|
*
|
|
1101
|
+
* @remarks
|
|
1102
|
+
* ## Why
|
|
1103
|
+
*
|
|
1104
|
+
* Modifies a `RefSubject` using a pure function that returns both a result and a new value. The
|
|
1105
|
+
* transition is serialized at the RefSubject rather than coordinated by callers or UI components.
|
|
1106
|
+
*
|
|
1107
|
+
* ## Ownership and lifetime
|
|
1108
|
+
*
|
|
1109
|
+
* Running modify performs one serialized subject transition and resolves with its committed value.
|
|
1110
|
+
* It acquires no resource; failures and services remain those of the source ref.
|
|
1111
|
+
*
|
|
538
1112
|
* @example
|
|
539
1113
|
* ```ts
|
|
540
1114
|
* import { Effect } from "effect"
|
|
@@ -553,7 +1127,7 @@ export const modifyEffect = dual(2, function modifyEffect(ref, f) {
|
|
|
553
1127
|
* ```
|
|
554
1128
|
*
|
|
555
1129
|
* @since 1.0.0
|
|
556
|
-
* @category
|
|
1130
|
+
* @category Transactions
|
|
557
1131
|
*/
|
|
558
1132
|
export const modify = dual(2, function modify(ref, f) {
|
|
559
1133
|
return modifyEffect(ref, (value) => Effect.succeed(f(value)));
|
|
@@ -561,6 +1135,16 @@ export const modify = dual(2, function modify(ref, f) {
|
|
|
561
1135
|
/**
|
|
562
1136
|
* Checks if a value is a `RefSubject`.
|
|
563
1137
|
*
|
|
1138
|
+
* @remarks
|
|
1139
|
+
* ## Why
|
|
1140
|
+
*
|
|
1141
|
+
* Checks if a value is a `RefSubject`.
|
|
1142
|
+
*
|
|
1143
|
+
* ## Ownership and lifetime
|
|
1144
|
+
*
|
|
1145
|
+
* The derived ref owns no independent state or subscription. It follows the source lifetime and
|
|
1146
|
+
* routes writes through the source's synchronized update boundary.
|
|
1147
|
+
*
|
|
564
1148
|
* @example
|
|
565
1149
|
* ```ts
|
|
566
1150
|
* import { Effect } from "effect"
|
|
@@ -578,14 +1162,45 @@ export const modify = dual(2, function modify(ref, f) {
|
|
|
578
1162
|
* ```
|
|
579
1163
|
*
|
|
580
1164
|
* @since 1.0.0
|
|
581
|
-
* @category guards
|
|
1165
|
+
* @category Type guards
|
|
582
1166
|
*/
|
|
583
1167
|
export function isRefSubject(value) {
|
|
584
|
-
return value
|
|
1168
|
+
return hasProperty(value, RefSubjectTypeId) && value[RefSubjectTypeId] === RefSubjectTypeId;
|
|
585
1169
|
}
|
|
586
1170
|
const isRefSubjectDataFirst = (args) => isRefSubject(args[0]);
|
|
587
1171
|
/**
|
|
1172
|
+
* Tests whether a value carries the public Computed TypeId.
|
|
1173
|
+
*
|
|
1174
|
+
* @remarks
|
|
1175
|
+
* ## Why
|
|
1176
|
+
*
|
|
1177
|
+
* Uses the TypeId contract rather than a concrete implementation class, so custom Computed values
|
|
1178
|
+
* and service facades participate in the same runtime guard.
|
|
1179
|
+
*
|
|
1180
|
+
* ## Ownership and lifetime
|
|
1181
|
+
*
|
|
1182
|
+
* This synchronous predicate performs no Effect, subscription, or acquisition.
|
|
1183
|
+
*
|
|
1184
|
+
* @since 1.18.0
|
|
1185
|
+
* @category Type guards
|
|
1186
|
+
*/
|
|
1187
|
+
export function isComputed(value) {
|
|
1188
|
+
return hasProperty(value, ComputedTypeId) && value[ComputedTypeId] === ComputedTypeId;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Runs an effect that can modify a `RefSubject` transactionally, with optional interrupt handling.
|
|
1192
|
+
*
|
|
1193
|
+
* @remarks
|
|
1194
|
+
* ## Why
|
|
1195
|
+
*
|
|
588
1196
|
* Runs an effect that can modify a `RefSubject` transactionally, with optional interrupt handling.
|
|
1197
|
+
* The transition is serialized at the RefSubject rather than coordinated by callers or UI
|
|
1198
|
+
* components.
|
|
1199
|
+
*
|
|
1200
|
+
* ## Ownership and lifetime
|
|
1201
|
+
*
|
|
1202
|
+
* Running run updates performs one serialized subject transition and resolves with its committed
|
|
1203
|
+
* value. It acquires no resource; failures and services remain those of the source ref.
|
|
589
1204
|
*
|
|
590
1205
|
* @example
|
|
591
1206
|
* ```ts
|
|
@@ -616,7 +1231,7 @@ const isRefSubjectDataFirst = (args) => isRefSubject(args[0]);
|
|
|
616
1231
|
* ```
|
|
617
1232
|
*
|
|
618
1233
|
* @since 1.0.0
|
|
619
|
-
* @category
|
|
1234
|
+
* @category Transactions
|
|
620
1235
|
*/
|
|
621
1236
|
export const runUpdates = dual(isRefSubjectDataFirst, function runUpdates(ref, f, options) {
|
|
622
1237
|
if (options === undefined) {
|
|
@@ -632,6 +1247,17 @@ export const runUpdates = dual(isRefSubjectDataFirst, function runUpdates(ref, f
|
|
|
632
1247
|
/**
|
|
633
1248
|
* Increments a numeric `RefSubject` by 1.
|
|
634
1249
|
*
|
|
1250
|
+
* @remarks
|
|
1251
|
+
* ## Why
|
|
1252
|
+
*
|
|
1253
|
+
* Increments a numeric `RefSubject` by 1. The transition is serialized at the RefSubject rather
|
|
1254
|
+
* than coordinated by callers or UI components.
|
|
1255
|
+
*
|
|
1256
|
+
* ## Ownership and lifetime
|
|
1257
|
+
*
|
|
1258
|
+
* Running increment performs one serialized subject transition and resolves with its committed
|
|
1259
|
+
* value. It acquires no resource; failures and services remain those of the source ref.
|
|
1260
|
+
*
|
|
635
1261
|
* @example
|
|
636
1262
|
* ```ts
|
|
637
1263
|
* import { Effect } from "effect"
|
|
@@ -651,7 +1277,7 @@ export const runUpdates = dual(isRefSubjectDataFirst, function runUpdates(ref, f
|
|
|
651
1277
|
* ```
|
|
652
1278
|
*
|
|
653
1279
|
* @since 1.0.0
|
|
654
|
-
* @category
|
|
1280
|
+
* @category State updates
|
|
655
1281
|
*/
|
|
656
1282
|
export function increment(ref) {
|
|
657
1283
|
return update(ref, (value) => value + 1);
|
|
@@ -659,6 +1285,17 @@ export function increment(ref) {
|
|
|
659
1285
|
/**
|
|
660
1286
|
* Decrements a numeric `RefSubject` by 1.
|
|
661
1287
|
*
|
|
1288
|
+
* @remarks
|
|
1289
|
+
* ## Why
|
|
1290
|
+
*
|
|
1291
|
+
* Decrements a numeric `RefSubject` by 1. The transition is serialized at the RefSubject rather
|
|
1292
|
+
* than coordinated by callers or UI components.
|
|
1293
|
+
*
|
|
1294
|
+
* ## Ownership and lifetime
|
|
1295
|
+
*
|
|
1296
|
+
* Running decrement performs one serialized subject transition and resolves with its committed
|
|
1297
|
+
* value. It acquires no resource; failures and services remain those of the source ref.
|
|
1298
|
+
*
|
|
662
1299
|
* @example
|
|
663
1300
|
* ```ts
|
|
664
1301
|
* import { Effect } from "effect"
|
|
@@ -678,7 +1315,7 @@ export function increment(ref) {
|
|
|
678
1315
|
* ```
|
|
679
1316
|
*
|
|
680
1317
|
* @since 1.0.0
|
|
681
|
-
* @category
|
|
1318
|
+
* @category State updates
|
|
682
1319
|
*/
|
|
683
1320
|
export function decrement(ref) {
|
|
684
1321
|
return update(ref, (value) => value - 1);
|
|
@@ -688,12 +1325,42 @@ const Variance = {
|
|
|
688
1325
|
_E: identity,
|
|
689
1326
|
_R: identity,
|
|
690
1327
|
};
|
|
1328
|
+
/**
|
|
1329
|
+
* Creates a Context-backed RefSubject service facade and Layer constructors.
|
|
1330
|
+
*
|
|
1331
|
+
* @remarks
|
|
1332
|
+
* ## Why
|
|
1333
|
+
*
|
|
1334
|
+
* The returned class is simultaneously a service tag, a current-value Effect, an Fx source, and a
|
|
1335
|
+
* writable RefSubject facade. This lets consumers read, observe, and update renderer-independent
|
|
1336
|
+
* state without manually retrieving the underlying service from Context.
|
|
1337
|
+
*
|
|
1338
|
+
* ## Ownership and lifetime
|
|
1339
|
+
*
|
|
1340
|
+
* Calling `Service` is pure. `layer` installs an existing scoped RefSubject; `make` constructs one
|
|
1341
|
+
* and installs it in a Layer. The Layer Scope owns the subject and its initializer subscription.
|
|
1342
|
+
* Reads and pushes expose the declared `E` channel; construction requirements remain on the Layer,
|
|
1343
|
+
* while consumers require only the generated `Self` service.
|
|
1344
|
+
*
|
|
1345
|
+
* @since 1.18.0
|
|
1346
|
+
* @category Services
|
|
1347
|
+
*/
|
|
691
1348
|
export function Service() {
|
|
692
1349
|
return (id) => {
|
|
693
|
-
const service =
|
|
1350
|
+
const service = Context.Service(id);
|
|
694
1351
|
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
|
695
1352
|
return class RefSubjectService {
|
|
696
1353
|
/// Service
|
|
1354
|
+
static {
|
|
1355
|
+
// @effect-diagnostics-next-line floatingEffect:off
|
|
1356
|
+
Object.assign(this, service);
|
|
1357
|
+
Object.setPrototypeOf(this, Object.getPrototypeOf(service));
|
|
1358
|
+
// @effect-diagnostics-next-line floatingEffect:off
|
|
1359
|
+
Object.assign(this, Effectable.Prototype({
|
|
1360
|
+
label: "RefSubject.Service",
|
|
1361
|
+
evaluate: () => Effect.flatMap(service, identity),
|
|
1362
|
+
}));
|
|
1363
|
+
}
|
|
697
1364
|
static id = id;
|
|
698
1365
|
static service = service;
|
|
699
1366
|
static layer = (make) => Layer.effect(service, make);
|
|
@@ -703,21 +1370,21 @@ export function Service() {
|
|
|
703
1370
|
};
|
|
704
1371
|
// Fx
|
|
705
1372
|
static [FxTypeId] = Variance;
|
|
706
|
-
static run = (sink) => Effect.flatMap(service
|
|
1373
|
+
static run = (sink) => Effect.flatMap(service, (ref) => ref.run(sink));
|
|
707
1374
|
// Sink
|
|
708
|
-
static onSuccess = (value) => Effect.flatMap(service
|
|
709
|
-
static onFailure = (cause) => Effect.flatMap(service
|
|
1375
|
+
static onSuccess = (value) => Effect.flatMap(service, (ref) => ref.onSuccess(value));
|
|
1376
|
+
static onFailure = (cause) => Effect.flatMap(service, (ref) => ref.onFailure(cause));
|
|
710
1377
|
/// Computed
|
|
711
1378
|
static [ComputedTypeId] = ComputedTypeId;
|
|
712
|
-
static version = Effect.flatMap(service
|
|
1379
|
+
static version = Effect.flatMap(service, (ref) => ref.version);
|
|
713
1380
|
// Subject
|
|
714
|
-
static subscriberCount = Effect.flatMap(service
|
|
715
|
-
static interrupt = Effect.flatMap(service
|
|
1381
|
+
static subscriberCount = Effect.flatMap(service, (ref) => ref.subscriberCount);
|
|
1382
|
+
static interrupt = Effect.flatMap(service, (ref) => ref.interrupt);
|
|
716
1383
|
// RefSubject
|
|
717
1384
|
static [RefSubjectTypeId] = RefSubjectTypeId;
|
|
718
|
-
static updates = (f) => Effect.flatMap(service
|
|
1385
|
+
static updates = (f) => Effect.flatMap(service, (ref) => ref.updates(f));
|
|
719
1386
|
// Yieldable
|
|
720
|
-
static
|
|
1387
|
+
static override = Effect.flatten(service);
|
|
721
1388
|
static [Symbol.iterator] = function* () {
|
|
722
1389
|
const ref = yield* service;
|
|
723
1390
|
return yield* ref;
|
|
@@ -741,6 +1408,19 @@ function getDefaultBounds(options) {
|
|
|
741
1408
|
* Extract all values from an object using a Proxy.
|
|
742
1409
|
* Allows accessing nested properties of a `Computed` or `Filtered` object/array as individual computed values.
|
|
743
1410
|
*
|
|
1411
|
+
* @remarks
|
|
1412
|
+
* ## Why
|
|
1413
|
+
*
|
|
1414
|
+
* Lazily creates one property projection per accessed key and memoizes that projection on the
|
|
1415
|
+
* JavaScript Proxy. A Computed or RefSubject source produces Computed properties; only a Filtered
|
|
1416
|
+
* source produces Filtered properties and preserves conditional absence.
|
|
1417
|
+
*
|
|
1418
|
+
* ## Ownership and lifetime
|
|
1419
|
+
*
|
|
1420
|
+
* The Proxy caches derived view objects, not property values. Computed property reads expose only
|
|
1421
|
+
* the source's `E`; Filtered property reads can additionally fail with `NoSuchElementError` while
|
|
1422
|
+
* their source is absent. Each observed property follows the source and uses the observing Scope.
|
|
1423
|
+
*
|
|
744
1424
|
* @example
|
|
745
1425
|
* ```ts
|
|
746
1426
|
* import { Effect } from "effect"
|
|
@@ -769,7 +1449,7 @@ function getDefaultBounds(options) {
|
|
|
769
1449
|
* ```
|
|
770
1450
|
*
|
|
771
1451
|
* @since 2.0.0
|
|
772
|
-
* @category
|
|
1452
|
+
* @category Derived queries
|
|
773
1453
|
*/
|
|
774
1454
|
export const proxy = (source) => {
|
|
775
1455
|
const target = {};
|
|
@@ -784,6 +1464,20 @@ export const proxy = (source) => {
|
|
|
784
1464
|
/**
|
|
785
1465
|
* Transforms a `RefSubject`, `Computed`, or `Filtered` using an `Effect`ful function.
|
|
786
1466
|
*
|
|
1467
|
+
* @remarks
|
|
1468
|
+
* ## Why
|
|
1469
|
+
*
|
|
1470
|
+
* Applies one Effectful derivation to current reads and pushed versions. RefSubject and Computed
|
|
1471
|
+
* inputs return Computed and add only `E2`; a Filtered input returns Filtered and preserves its
|
|
1472
|
+
* conditional absence in addition to `E2`.
|
|
1473
|
+
*
|
|
1474
|
+
* ## Ownership and lifetime
|
|
1475
|
+
*
|
|
1476
|
+
* No callback runs until the result is read or observed. Computed results cannot become absent and
|
|
1477
|
+
* do not add `NoSuchElementError`; only the Filtered overload can fail a current read for absence.
|
|
1478
|
+
* Callback services and failures remain `R2` and `E2`, and the observing Scope owns subscription
|
|
1479
|
+
* cleanup.
|
|
1480
|
+
*
|
|
787
1481
|
* @example
|
|
788
1482
|
* ```ts
|
|
789
1483
|
* import { Effect } from "effect"
|
|
@@ -810,7 +1504,7 @@ export const proxy = (source) => {
|
|
|
810
1504
|
* ```
|
|
811
1505
|
*
|
|
812
1506
|
* @since 1.0.0
|
|
813
|
-
* @category
|
|
1507
|
+
* @category Derived queries
|
|
814
1508
|
*/
|
|
815
1509
|
export const mapEffect = dual(2, function mapEffect(versioned, f) {
|
|
816
1510
|
return FilteredTypeId in versioned
|
|
@@ -820,6 +1514,25 @@ export const mapEffect = dual(2, function mapEffect(versioned, f) {
|
|
|
820
1514
|
/**
|
|
821
1515
|
* Transforms a `RefSubject`, `Computed`, or `Filtered` using a pure function.
|
|
822
1516
|
*
|
|
1517
|
+
* @remarks
|
|
1518
|
+
* ## Why
|
|
1519
|
+
*
|
|
1520
|
+
* `map` creates derived state instead of copying source state into a component.
|
|
1521
|
+
* Reads and pushed updates remain connected to the original `Versioned` value,
|
|
1522
|
+
* so a projection can be tested without rendering and consumed by any renderer.
|
|
1523
|
+
*
|
|
1524
|
+
* ## Ownership and lifetime
|
|
1525
|
+
*
|
|
1526
|
+
* The returned `Computed` or `Filtered` view does not take ownership of the
|
|
1527
|
+
* source. It follows the source lifetime and preserves its error and service
|
|
1528
|
+
* channels. The pure mapping function adds neither resources nor failures.
|
|
1529
|
+
*
|
|
1530
|
+
* ## Re-computation
|
|
1531
|
+
*
|
|
1532
|
+
* The mapping function runs for the current value and subsequent committed
|
|
1533
|
+
* source versions. Updates still occur on the writable source; the derived view
|
|
1534
|
+
* is read-only unless a more specific bidirectional combinator is used.
|
|
1535
|
+
*
|
|
823
1536
|
* @example
|
|
824
1537
|
* ```ts
|
|
825
1538
|
* import { Effect } from "effect"
|
|
@@ -844,7 +1557,7 @@ export const mapEffect = dual(2, function mapEffect(versioned, f) {
|
|
|
844
1557
|
* ```
|
|
845
1558
|
*
|
|
846
1559
|
* @since 1.0.0
|
|
847
|
-
* @category
|
|
1560
|
+
* @category Derived queries
|
|
848
1561
|
*/
|
|
849
1562
|
export const map = dual(2, function map(versioned, f) {
|
|
850
1563
|
return mapEffect(versioned, (a) => Effect.succeed(f(a)));
|
|
@@ -852,6 +1565,18 @@ export const map = dual(2, function map(versioned, f) {
|
|
|
852
1565
|
/**
|
|
853
1566
|
* Filters and transforms a `RefSubject`, `Computed`, or `Filtered` using an `Effect`ful function that returns an `Option`.
|
|
854
1567
|
*
|
|
1568
|
+
* @remarks
|
|
1569
|
+
* ## Why
|
|
1570
|
+
*
|
|
1571
|
+
* Filters and transforms a `RefSubject`, `Computed`, or `Filtered` using an `Effect`ful function
|
|
1572
|
+
* that returns an `Option`. Current reads and pushed versions use the same derivation, preventing
|
|
1573
|
+
* snapshot and subscription behavior from diverging.
|
|
1574
|
+
*
|
|
1575
|
+
* ## Ownership and lifetime
|
|
1576
|
+
*
|
|
1577
|
+
* The filter map effect view retains no independent value. Its Effect read fails with NoSuchElement
|
|
1578
|
+
* while absent; the observing Scope owns and finalizes its Fx subscription.
|
|
1579
|
+
*
|
|
855
1580
|
* @example
|
|
856
1581
|
* ```ts
|
|
857
1582
|
* import { Effect, Option } from "effect"
|
|
@@ -871,7 +1596,7 @@ export const map = dual(2, function map(versioned, f) {
|
|
|
871
1596
|
* ```
|
|
872
1597
|
*
|
|
873
1598
|
* @since 1.0.0
|
|
874
|
-
* @category
|
|
1599
|
+
* @category Optional queries
|
|
875
1600
|
*/
|
|
876
1601
|
export const filterMapEffect = dual(2, function filterMapEffect(versioned, f) {
|
|
877
1602
|
return new FilteredImpl(versioned, f);
|
|
@@ -879,6 +1604,18 @@ export const filterMapEffect = dual(2, function filterMapEffect(versioned, f) {
|
|
|
879
1604
|
/**
|
|
880
1605
|
* Filters and transforms a `RefSubject`, `Computed`, or `Filtered` using a pure function that returns an `Option`.
|
|
881
1606
|
*
|
|
1607
|
+
* @remarks
|
|
1608
|
+
* ## Why
|
|
1609
|
+
*
|
|
1610
|
+
* Filters and transforms a `RefSubject`, `Computed`, or `Filtered` using a pure function that
|
|
1611
|
+
* returns an `Option`. Current reads and pushed versions use the same derivation, preventing
|
|
1612
|
+
* snapshot and subscription behavior from diverging.
|
|
1613
|
+
*
|
|
1614
|
+
* ## Ownership and lifetime
|
|
1615
|
+
*
|
|
1616
|
+
* The filter map view retains no independent value. Its Effect read fails with NoSuchElement
|
|
1617
|
+
* while absent; the observing Scope owns and finalizes its Fx subscription.
|
|
1618
|
+
*
|
|
882
1619
|
* @example
|
|
883
1620
|
* ```ts
|
|
884
1621
|
* import { Effect, Option } from "effect"
|
|
@@ -898,7 +1635,7 @@ export const filterMapEffect = dual(2, function filterMapEffect(versioned, f) {
|
|
|
898
1635
|
* ```
|
|
899
1636
|
*
|
|
900
1637
|
* @since 1.0.0
|
|
901
|
-
* @category
|
|
1638
|
+
* @category Optional queries
|
|
902
1639
|
*/
|
|
903
1640
|
export const filterMap = dual(2, function filterMap(versioned, f) {
|
|
904
1641
|
return new FilteredImpl(versioned, (a) => Effect.succeed(f(a)));
|
|
@@ -906,6 +1643,18 @@ export const filterMap = dual(2, function filterMap(versioned, f) {
|
|
|
906
1643
|
/**
|
|
907
1644
|
* Converts a `Computed` or `Filtered` of `Option<A>` into a `Filtered<A>`, filtering out `None` values.
|
|
908
1645
|
*
|
|
1646
|
+
* @remarks
|
|
1647
|
+
* ## Why
|
|
1648
|
+
*
|
|
1649
|
+
* Converts a `Computed` or `Filtered` of `Option<A>` into a `Filtered<A>`, filtering out `None`
|
|
1650
|
+
* values. Current reads and pushed versions use the same derivation, preventing snapshot and
|
|
1651
|
+
* subscription behavior from diverging.
|
|
1652
|
+
*
|
|
1653
|
+
* ## Ownership and lifetime
|
|
1654
|
+
*
|
|
1655
|
+
* The compact view retains no independent value. Its Effect read fails with NoSuchElement
|
|
1656
|
+
* while absent; the observing Scope owns and finalizes its Fx subscription.
|
|
1657
|
+
*
|
|
909
1658
|
* @example
|
|
910
1659
|
* ```ts
|
|
911
1660
|
* import { Effect, Option } from "effect"
|
|
@@ -927,11 +1676,52 @@ export const filterMap = dual(2, function filterMap(versioned, f) {
|
|
|
927
1676
|
* ```
|
|
928
1677
|
*
|
|
929
1678
|
* @since 1.0.0
|
|
930
|
-
* @category
|
|
1679
|
+
* @category Optional queries
|
|
931
1680
|
*/
|
|
932
1681
|
export const compact = function compact(versioned) {
|
|
933
1682
|
return new FilteredImpl(versioned, Effect.succeed);
|
|
934
1683
|
};
|
|
1684
|
+
/**
|
|
1685
|
+
* Returns a `Computed` that yields the value inside the `Option`, or the fallback when `None`.
|
|
1686
|
+
* Works with `Computed<Option<A>>` (e.g. from `fromOption` / `fromNullable`) and with `Filtered<A>`.
|
|
1687
|
+
*
|
|
1688
|
+
* @remarks
|
|
1689
|
+
* ## Why
|
|
1690
|
+
*
|
|
1691
|
+
* Returns a `Computed` that yields the value inside the `Option`, or the fallback when `None`.
|
|
1692
|
+
* Works with `Computed<Option<A>>` (e.g. from `fromOption` / `fromNullable`) and with
|
|
1693
|
+
* `Filtered<A>`. Current reads and pushed versions use the same derivation, preventing snapshot
|
|
1694
|
+
* and subscription behavior from diverging.
|
|
1695
|
+
*
|
|
1696
|
+
* ## Ownership and lifetime
|
|
1697
|
+
*
|
|
1698
|
+
* The returned Computed never fails for absence: `None` or an absent Filtered value invokes
|
|
1699
|
+
* `fallback`. It preserves only the input's declared `E` and `R`; the pure fallback adds no failure,
|
|
1700
|
+
* service, subscription, or retained value.
|
|
1701
|
+
*
|
|
1702
|
+
* @example
|
|
1703
|
+
* ```ts
|
|
1704
|
+
* import { Effect, Option } from "effect"
|
|
1705
|
+
* import * as RefSubject from "@typed/fx/RefSubject"
|
|
1706
|
+
*
|
|
1707
|
+
* const program = Effect.gen(function* () {
|
|
1708
|
+
* const ref = yield* RefSubject.fromOption(Option.some(42))
|
|
1709
|
+
* const withDefault = RefSubject.getOrElse(ref, () => 0)
|
|
1710
|
+
* expect(yield* withDefault).toBe(42)
|
|
1711
|
+
*
|
|
1712
|
+
* const empty = yield* RefSubject.fromNullable(null)
|
|
1713
|
+
* const fallback = RefSubject.getOrElse(empty, () => 99)
|
|
1714
|
+
* expect(yield* fallback).toBe(99)
|
|
1715
|
+
* })
|
|
1716
|
+
* ```
|
|
1717
|
+
*
|
|
1718
|
+
* @since 1.0.0
|
|
1719
|
+
* @category Optional queries
|
|
1720
|
+
*/
|
|
1721
|
+
export const getOrElse = dual(2, function getOrElse(ref, fallback) {
|
|
1722
|
+
const computed = FilteredTypeId in ref ? ref.asComputed() : ref;
|
|
1723
|
+
return map(computed, (opt) => Option.getOrElse(opt, fallback));
|
|
1724
|
+
});
|
|
935
1725
|
class RefSubjectSimpleTransform extends YieldableFx {
|
|
936
1726
|
[ComputedTypeId] = ComputedTypeId;
|
|
937
1727
|
[RefSubjectTypeId] = RefSubjectTypeId;
|
|
@@ -956,7 +1746,7 @@ class RefSubjectSimpleTransform extends YieldableFx {
|
|
|
956
1746
|
return this._fx.run(sink);
|
|
957
1747
|
}
|
|
958
1748
|
toEffect() {
|
|
959
|
-
return this.transformEffect(this.ref
|
|
1749
|
+
return this.transformEffect(this.ref);
|
|
960
1750
|
}
|
|
961
1751
|
updates(run) {
|
|
962
1752
|
return this.ref.updates(run);
|
|
@@ -968,6 +1758,25 @@ class RefSubjectSimpleTransform extends YieldableFx {
|
|
|
968
1758
|
return this.ref.onSuccess(value);
|
|
969
1759
|
}
|
|
970
1760
|
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Limits which pushed versions a RefSubject view observes without changing its state.
|
|
1763
|
+
*
|
|
1764
|
+
* @remarks
|
|
1765
|
+
* ## Why
|
|
1766
|
+
*
|
|
1767
|
+
* Applies `skip` and `take` only to the Fx observation channel. Current reads still sample the
|
|
1768
|
+
* underlying RefSubject directly, and writes still use its original serialized update boundary.
|
|
1769
|
+
*
|
|
1770
|
+
* ## Ownership and lifetime
|
|
1771
|
+
*
|
|
1772
|
+
* Creating the view starts nothing and retains no value. Each observation uses its Scope and ends
|
|
1773
|
+
* after `take` selected pushes; ending that observation does not interrupt or delete the source.
|
|
1774
|
+
* Version, interruption, subscriber count, reads, failures, services, and writes delegate to the
|
|
1775
|
+
* source RefSubject.
|
|
1776
|
+
*
|
|
1777
|
+
* @since 1.18.0
|
|
1778
|
+
* @category Observation policy
|
|
1779
|
+
*/
|
|
971
1780
|
export const slice = dual(3, function slice(ref, skip, take) {
|
|
972
1781
|
return new RefSubjectSimpleTransform(ref, (_) => fxSlice(_, { skip, take }), identity);
|
|
973
1782
|
});
|
|
@@ -995,7 +1804,7 @@ class RefSubjectTransform extends YieldableFx {
|
|
|
995
1804
|
return this._fx.run(sink);
|
|
996
1805
|
}
|
|
997
1806
|
toEffect() {
|
|
998
|
-
return Effect.map(this.ref
|
|
1807
|
+
return Effect.map(this.ref, this.toB);
|
|
999
1808
|
}
|
|
1000
1809
|
updates(run) {
|
|
1001
1810
|
return this.ref.updates((innerRef) => {
|
|
@@ -1017,6 +1826,17 @@ class RefSubjectTransform extends YieldableFx {
|
|
|
1017
1826
|
/**
|
|
1018
1827
|
* Transforms a `RefSubject` invariantly using bidirectional mapping functions.
|
|
1019
1828
|
*
|
|
1829
|
+
* @remarks
|
|
1830
|
+
* ## Why
|
|
1831
|
+
*
|
|
1832
|
+
* Creates a bidirectional lens over state: reads map outward and writes map back to the original
|
|
1833
|
+
* RefSubject without allocating a second store.
|
|
1834
|
+
*
|
|
1835
|
+
* ## Ownership and lifetime
|
|
1836
|
+
*
|
|
1837
|
+
* The derived ref owns no independent state or subscription. It follows the source lifetime and
|
|
1838
|
+
* routes writes through the source's synchronized update boundary.
|
|
1839
|
+
*
|
|
1020
1840
|
* @example
|
|
1021
1841
|
* ```ts
|
|
1022
1842
|
* import { Effect } from "effect"
|
|
@@ -1045,7 +1865,7 @@ class RefSubjectTransform extends YieldableFx {
|
|
|
1045
1865
|
* ```
|
|
1046
1866
|
*
|
|
1047
1867
|
* @since 1.0.0
|
|
1048
|
-
* @category
|
|
1868
|
+
* @category Writable views
|
|
1049
1869
|
*/
|
|
1050
1870
|
export const transform = dual(3, function transform(ref, toB, toA) {
|
|
1051
1871
|
return new RefSubjectTransform(ref, toB, toA);
|
|
@@ -1077,6 +1897,17 @@ function getRefKind(refs) {
|
|
|
1077
1897
|
/**
|
|
1078
1898
|
* Combines multiple `RefSubject`, `Computed`, or `Filtered` instances into a single struct.
|
|
1079
1899
|
*
|
|
1900
|
+
* @remarks
|
|
1901
|
+
* ## Why
|
|
1902
|
+
*
|
|
1903
|
+
* Combines several refs into one state value while preserving whether the result is writable,
|
|
1904
|
+
* computed, or filtered from its inputs.
|
|
1905
|
+
*
|
|
1906
|
+
* ## Ownership and lifetime
|
|
1907
|
+
*
|
|
1908
|
+
* This declaration performs no acquisition and retains no resources. Implementations preserve
|
|
1909
|
+
* source errors, services, and lifetime.
|
|
1910
|
+
*
|
|
1080
1911
|
* @example
|
|
1081
1912
|
* ```ts
|
|
1082
1913
|
* import { Effect } from "effect"
|
|
@@ -1107,7 +1938,7 @@ function getRefKind(refs) {
|
|
|
1107
1938
|
* ```
|
|
1108
1939
|
*
|
|
1109
1940
|
* @since 1.0.0
|
|
1110
|
-
* @category
|
|
1941
|
+
* @category State composition
|
|
1111
1942
|
*/
|
|
1112
1943
|
export function struct(refs) {
|
|
1113
1944
|
const kind = getRefKind(Object.values(refs));
|
|
@@ -1123,6 +1954,17 @@ export function struct(refs) {
|
|
|
1123
1954
|
/**
|
|
1124
1955
|
* Combines multiple `RefSubject`, `Computed`, or `Filtered` instances into a single tuple.
|
|
1125
1956
|
*
|
|
1957
|
+
* @remarks
|
|
1958
|
+
* ## Why
|
|
1959
|
+
*
|
|
1960
|
+
* Combines several refs into one state value while preserving whether the result is writable,
|
|
1961
|
+
* computed, or filtered from its inputs.
|
|
1962
|
+
*
|
|
1963
|
+
* ## Ownership and lifetime
|
|
1964
|
+
*
|
|
1965
|
+
* This declaration performs no acquisition and retains no resources. Implementations preserve
|
|
1966
|
+
* source errors, services, and lifetime.
|
|
1967
|
+
*
|
|
1126
1968
|
* @example
|
|
1127
1969
|
* ```ts
|
|
1128
1970
|
* import { Effect } from "effect"
|
|
@@ -1149,7 +1991,7 @@ export function struct(refs) {
|
|
|
1149
1991
|
* ```
|
|
1150
1992
|
*
|
|
1151
1993
|
* @since 1.0.0
|
|
1152
|
-
* @category
|
|
1994
|
+
* @category State composition
|
|
1153
1995
|
*/
|
|
1154
1996
|
export function tuple(refs) {
|
|
1155
1997
|
const kind = getRefKind(refs);
|
|
@@ -1166,6 +2008,38 @@ function makeTupleRef(refs) {
|
|
|
1166
2008
|
return new RefSubjectTuple(refs);
|
|
1167
2009
|
}
|
|
1168
2010
|
const UNBOUNDED = { concurrency: "unbounded" };
|
|
2011
|
+
const sampleRefSubject = (ref) =>
|
|
2012
|
+
// @effect-diagnostics-next-line unnecessaryEffectGen:off
|
|
2013
|
+
Effect.gen(function* () {
|
|
2014
|
+
return yield* ref;
|
|
2015
|
+
});
|
|
2016
|
+
function versionedInContext(make) {
|
|
2017
|
+
// Automatic composites share both pushed sessions and in-flight reads only within one Context.
|
|
2018
|
+
const contexts = new WeakMap();
|
|
2019
|
+
const current = Effect.map(Effect.context(), (context) => {
|
|
2020
|
+
let versioned = contexts.get(context);
|
|
2021
|
+
if (!versioned) {
|
|
2022
|
+
versioned = make();
|
|
2023
|
+
contexts.set(context, versioned);
|
|
2024
|
+
}
|
|
2025
|
+
return versioned;
|
|
2026
|
+
});
|
|
2027
|
+
return new (class extends YieldableFx {
|
|
2028
|
+
version = Effect.flatMap(current, (versioned) => versioned.version);
|
|
2029
|
+
interrupt = Effect.flatMap(current, (versioned) => versioned.interrupt);
|
|
2030
|
+
run(sink) {
|
|
2031
|
+
return Effect.flatMap(current, (versioned) => versioned.run(sink));
|
|
2032
|
+
}
|
|
2033
|
+
toEffect() {
|
|
2034
|
+
return Effect.flatMap(current, identity);
|
|
2035
|
+
}
|
|
2036
|
+
})();
|
|
2037
|
+
}
|
|
2038
|
+
function makeCompositeVersioned(refs, current) {
|
|
2039
|
+
const version = Effect.map(Effect.all(refs.map((ref) => ref.version), UNBOUNDED), (versions) => versions.reduce(sum, 0));
|
|
2040
|
+
const changes = skipRepeats(fxMapEffect(fxMergeAll(...refs), () => current));
|
|
2041
|
+
return versionedInContext(() => Versioned.hold(Versioned.make(version, changes, current)));
|
|
2042
|
+
}
|
|
1169
2043
|
class RefSubjectTuple extends YieldableFx {
|
|
1170
2044
|
[ComputedTypeId] = ComputedTypeId;
|
|
1171
2045
|
[RefSubjectTypeId] = RefSubjectTypeId;
|
|
@@ -1178,12 +2052,14 @@ class RefSubjectTuple extends YieldableFx {
|
|
|
1178
2052
|
constructor(refs) {
|
|
1179
2053
|
super();
|
|
1180
2054
|
this.refs = refs;
|
|
1181
|
-
this.versioned =
|
|
2055
|
+
this.versioned = (refs.length > 0 && getTupleTransactionAccess(refs)
|
|
2056
|
+
? makeCompositeVersioned(refs, Effect.all(refs.map(sampleRefSubject), UNBOUNDED))
|
|
2057
|
+
: versionedInContext(() => Versioned.hold(Versioned.tuple(refs))));
|
|
1182
2058
|
this.version = this.versioned.version;
|
|
1183
2059
|
this.interrupt = Effect.all(refs.map((r) => r.interrupt), UNBOUNDED);
|
|
1184
2060
|
this.subscriberCount = Effect.map(Effect.all(refs.map((r) => r.subscriberCount), UNBOUNDED), Array.reduce(0, sum));
|
|
1185
2061
|
this.getSetDelete = {
|
|
1186
|
-
get: this.versioned
|
|
2062
|
+
get: this.versioned,
|
|
1187
2063
|
set: (a) => Effect.all(refs.map((r, i) => set(r, a[i])), UNBOUNDED),
|
|
1188
2064
|
delete: Effect.map(Effect.all(refs.map((r) => reset(r)), UNBOUNDED), Option.all),
|
|
1189
2065
|
};
|
|
@@ -1195,10 +2071,15 @@ class RefSubjectTuple extends YieldableFx {
|
|
|
1195
2071
|
return this.versioned.run(sink);
|
|
1196
2072
|
}
|
|
1197
2073
|
toEffect() {
|
|
1198
|
-
return this.versioned
|
|
2074
|
+
return this.versioned;
|
|
1199
2075
|
}
|
|
1200
2076
|
updates(run) {
|
|
1201
|
-
|
|
2077
|
+
const accessEffect = getTransactionAccess(this);
|
|
2078
|
+
return accessEffect
|
|
2079
|
+
? Effect.flatMap(accessEffect, (transactionAccess) => Option.isSome(transactionAccess)
|
|
2080
|
+
? runTransaction(transactionAccess.value, run)
|
|
2081
|
+
: Effect.suspend(() => run(this.getSetDelete)))
|
|
2082
|
+
: Effect.suspend(() => run(this.getSetDelete));
|
|
1202
2083
|
}
|
|
1203
2084
|
onFailure(cause) {
|
|
1204
2085
|
return Effect.all(this.refs.map((ref) => ref.onFailure(cause)));
|
|
@@ -1208,10 +2089,10 @@ class RefSubjectTuple extends YieldableFx {
|
|
|
1208
2089
|
}
|
|
1209
2090
|
}
|
|
1210
2091
|
function makeTupleComputed(refs) {
|
|
1211
|
-
return new ComputedImpl(Versioned.tuple(refs), Effect.succeed);
|
|
2092
|
+
return new ComputedImpl(versionedInContext(() => Versioned.tuple(refs)), Effect.succeed);
|
|
1212
2093
|
}
|
|
1213
2094
|
function makeTupleFiltered(refs) {
|
|
1214
|
-
return new FilteredImpl(Versioned.tuple(refs), Effect.succeedSome);
|
|
2095
|
+
return new FilteredImpl(versionedInContext(() => Versioned.tuple(refs)), Effect.succeedSome);
|
|
1215
2096
|
}
|
|
1216
2097
|
function makeStructRef(refs) {
|
|
1217
2098
|
return new RefSubjectStruct(refs);
|
|
@@ -1228,12 +2109,16 @@ class RefSubjectStruct extends YieldableFx {
|
|
|
1228
2109
|
constructor(refs) {
|
|
1229
2110
|
super();
|
|
1230
2111
|
this.refs = refs;
|
|
1231
|
-
|
|
2112
|
+
const keys = Object.keys(refs);
|
|
2113
|
+
const current = Effect.map(Effect.all(keys.map((key) => sampleRefSubject(refs[key])), UNBOUNDED), (values) => Object.fromEntries(keys.map((key, index) => [key, values[index]])));
|
|
2114
|
+
this.versioned = (keys.length > 0 && getStructTransactionAccess(refs)
|
|
2115
|
+
? makeCompositeVersioned(Object.values(refs), current)
|
|
2116
|
+
: versionedInContext(() => Versioned.hold(Versioned.struct(refs))));
|
|
1232
2117
|
this.version = this.versioned.version;
|
|
1233
2118
|
this.interrupt = Effect.all(Object.values(refs).map((r) => r.interrupt), UNBOUNDED);
|
|
1234
2119
|
this.subscriberCount = Effect.map(Effect.all(Object.values(refs).map((r) => r.subscriberCount), UNBOUNDED), Array.reduce(0, sum));
|
|
1235
2120
|
this.getSetDelete = {
|
|
1236
|
-
get: this.versioned
|
|
2121
|
+
get: this.versioned,
|
|
1237
2122
|
set: (a) => Effect.all(Object.keys(refs).map((k) => set(refs[k], a[k])), UNBOUNDED),
|
|
1238
2123
|
delete: Effect.map(Effect.all(Object.values(refs).map((r) => reset(r)), UNBOUNDED), Option.all),
|
|
1239
2124
|
};
|
|
@@ -1245,10 +2130,15 @@ class RefSubjectStruct extends YieldableFx {
|
|
|
1245
2130
|
return this.versioned.run(sink);
|
|
1246
2131
|
}
|
|
1247
2132
|
toEffect() {
|
|
1248
|
-
return this.versioned
|
|
2133
|
+
return this.versioned;
|
|
1249
2134
|
}
|
|
1250
2135
|
updates(run) {
|
|
1251
|
-
|
|
2136
|
+
const accessEffect = getTransactionAccess(this);
|
|
2137
|
+
return accessEffect
|
|
2138
|
+
? Effect.flatMap(accessEffect, (transactionAccess) => Option.isSome(transactionAccess)
|
|
2139
|
+
? runTransaction(transactionAccess.value, run)
|
|
2140
|
+
: Effect.suspend(() => run(this.getSetDelete)))
|
|
2141
|
+
: Effect.suspend(() => run(this.getSetDelete));
|
|
1252
2142
|
}
|
|
1253
2143
|
onFailure(cause) {
|
|
1254
2144
|
return Effect.all(Object.values(this.refs).map((ref) => ref.onFailure(cause)));
|
|
@@ -1258,11 +2148,29 @@ class RefSubjectStruct extends YieldableFx {
|
|
|
1258
2148
|
}
|
|
1259
2149
|
}
|
|
1260
2150
|
function makeStructComputed(refs) {
|
|
1261
|
-
return new ComputedImpl(Versioned.struct(refs), Effect.succeed);
|
|
2151
|
+
return new ComputedImpl(versionedInContext(() => Versioned.struct(refs)), Effect.succeed);
|
|
1262
2152
|
}
|
|
1263
2153
|
function makeStructFiltered(refs) {
|
|
1264
|
-
return new FilteredImpl(Versioned.struct(refs), Effect.succeedSome);
|
|
2154
|
+
return new FilteredImpl(versionedInContext(() => Versioned.struct(refs)), Effect.succeedSome);
|
|
1265
2155
|
}
|
|
2156
|
+
/**
|
|
2157
|
+
* Lifts an Effect-provided Computed into a Computed facade.
|
|
2158
|
+
*
|
|
2159
|
+
* @remarks
|
|
2160
|
+
* ## Why
|
|
2161
|
+
*
|
|
2162
|
+
* Defers service lookup until each version read, current read, interruption, or Fx observation, so
|
|
2163
|
+
* callers can compose a Context-provided Computed without manually flat-mapping the service Effect.
|
|
2164
|
+
*
|
|
2165
|
+
* ## Ownership and lifetime
|
|
2166
|
+
*
|
|
2167
|
+
* The facade caches neither the resolved Computed nor its value. Each operation runs the supplied
|
|
2168
|
+
* Effect, then delegates to the resolved Computed. Service requirements combine as `R | R2`, source
|
|
2169
|
+
* failures remain `E`, and the observing Scope owns the delegated subscription.
|
|
2170
|
+
*
|
|
2171
|
+
* @since 1.18.0
|
|
2172
|
+
* @category Services
|
|
2173
|
+
*/
|
|
1266
2174
|
export function computedFromService(effect) {
|
|
1267
2175
|
return new ComputedFromService(effect);
|
|
1268
2176
|
}
|
|
@@ -1281,9 +2189,26 @@ class ComputedFromService extends YieldableFx {
|
|
|
1281
2189
|
return Effect.flatMap(this.effect, (c) => c.run(sink));
|
|
1282
2190
|
}
|
|
1283
2191
|
toEffect() {
|
|
1284
|
-
return Effect.flatMap(this.effect, (c) => c
|
|
2192
|
+
return Effect.flatMap(this.effect, (c) => c);
|
|
1285
2193
|
}
|
|
1286
2194
|
}
|
|
2195
|
+
/**
|
|
2196
|
+
* Defers retrieval of a Filtered value from Effect Context while preserving Filtered behavior.
|
|
2197
|
+
*
|
|
2198
|
+
* @remarks
|
|
2199
|
+
* ## Why
|
|
2200
|
+
*
|
|
2201
|
+
* Lets a service-provided Filtered participate directly in Effect reads and Fx observation without
|
|
2202
|
+
* manually flat-mapping the service tag at every use.
|
|
2203
|
+
*
|
|
2204
|
+
* ## Ownership and lifetime
|
|
2205
|
+
*
|
|
2206
|
+
* The filtered from service view retains no independent value. Its Effect read fails with NoSuchElement
|
|
2207
|
+
* while absent; the observing Scope owns and finalizes its Fx subscription.
|
|
2208
|
+
*
|
|
2209
|
+
* @since 1.18.0
|
|
2210
|
+
* @category Services
|
|
2211
|
+
*/
|
|
1287
2212
|
export function filteredFromService(effect) {
|
|
1288
2213
|
return new FilteredFromService(effect);
|
|
1289
2214
|
}
|
|
@@ -1302,9 +2227,10 @@ class FilteredFromService extends YieldableFx {
|
|
|
1302
2227
|
return Effect.flatMap(this.effect, (c) => c.run(sink));
|
|
1303
2228
|
}
|
|
1304
2229
|
toEffect() {
|
|
1305
|
-
return Effect.flatMap(this.effect, (c) => c
|
|
2230
|
+
return Effect.flatMap(this.effect, (c) => c);
|
|
1306
2231
|
}
|
|
1307
2232
|
asComputed() {
|
|
1308
2233
|
return computedFromService(Effect.map(this.effect, (c) => c.asComputed()));
|
|
1309
2234
|
}
|
|
1310
2235
|
}
|
|
2236
|
+
export * from "./Hydration.js";
|