synstate 3.4.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -8
- package/dist/core/authoring.d.mts +7 -0
- package/dist/core/authoring.d.mts.map +1 -0
- package/dist/core/authoring.mjs +7 -0
- package/dist/core/authoring.mjs.map +1 -0
- package/dist/core/class/child-observable-class.d.mts +93 -0
- package/dist/core/class/child-observable-class.d.mts.map +1 -0
- package/dist/core/class/child-observable-class.mjs +248 -0
- package/dist/core/class/child-observable-class.mjs.map +1 -0
- package/dist/core/class/create-child-observable.d.mts +159 -0
- package/dist/core/class/create-child-observable.d.mts.map +1 -0
- package/dist/core/class/create-child-observable.mjs +183 -0
- package/dist/core/class/create-child-observable.mjs.map +1 -0
- package/dist/core/class/create-root-observable.d.mts +37 -0
- package/dist/core/class/create-root-observable.d.mts.map +1 -0
- package/dist/core/class/create-root-observable.mjs +52 -0
- package/dist/core/class/create-root-observable.mjs.map +1 -0
- package/dist/core/class/index.d.mts +9 -0
- package/dist/core/class/index.d.mts.map +1 -0
- package/dist/core/class/index.mjs +9 -0
- package/dist/core/class/index.mjs.map +1 -0
- package/dist/core/class/manager-observable-class.d.mts +26 -0
- package/dist/core/class/manager-observable-class.d.mts.map +1 -0
- package/dist/core/class/manager-observable-class.mjs +116 -0
- package/dist/core/class/manager-observable-class.mjs.map +1 -0
- package/dist/core/class/observable-base-class.d.mts +97 -0
- package/dist/core/class/observable-base-class.d.mts.map +1 -0
- package/dist/core/class/observable-base-class.mjs +219 -0
- package/dist/core/class/observable-base-class.mjs.map +1 -0
- package/dist/core/class/observable-node.d.mts +40 -0
- package/dist/core/class/observable-node.d.mts.map +1 -0
- package/dist/core/class/observable-node.mjs +10 -0
- package/dist/core/class/observable-node.mjs.map +1 -0
- package/dist/core/class/propagation-state.d.mts +24 -0
- package/dist/core/class/propagation-state.d.mts.map +1 -0
- package/dist/core/class/propagation-state.mjs +66 -0
- package/dist/core/class/propagation-state.mjs.map +1 -0
- package/dist/core/class/root-observable-class.d.mts +23 -0
- package/dist/core/class/root-observable-class.d.mts.map +1 -0
- package/dist/core/class/root-observable-class.mjs +58 -0
- package/dist/core/class/root-observable-class.mjs.map +1 -0
- package/dist/core/combine/combine.d.mts.map +1 -1
- package/dist/core/combine/combine.mjs +25 -14
- package/dist/core/combine/combine.mjs.map +1 -1
- package/dist/core/combine/merge.d.mts.map +1 -1
- package/dist/core/combine/merge.mjs +26 -14
- package/dist/core/combine/merge.mjs.map +1 -1
- package/dist/core/combine/zip.d.mts.map +1 -1
- package/dist/core/combine/zip.mjs +43 -25
- package/dist/core/combine/zip.mjs.map +1 -1
- package/dist/core/create/counter.d.mts.map +1 -1
- package/dist/core/create/counter.mjs +65 -44
- package/dist/core/create/counter.mjs.map +1 -1
- package/dist/core/create/empty.d.mts +32 -0
- package/dist/core/create/empty.d.mts.map +1 -0
- package/dist/core/create/empty.mjs +39 -0
- package/dist/core/create/empty.mjs.map +1 -0
- package/dist/core/create/from-abortable-promise.d.mts.map +1 -1
- package/dist/core/create/from-abortable-promise.mjs +26 -18
- package/dist/core/create/from-abortable-promise.mjs.map +1 -1
- package/dist/core/create/from-promise.d.mts.map +1 -1
- package/dist/core/create/from-promise.mjs +30 -21
- package/dist/core/create/from-promise.mjs.map +1 -1
- package/dist/core/create/from-subscribable.d.mts.map +1 -1
- package/dist/core/create/from-subscribable.mjs +37 -24
- package/dist/core/create/from-subscribable.mjs.map +1 -1
- package/dist/core/create/index.d.mts +1 -0
- package/dist/core/create/index.d.mts.map +1 -1
- package/dist/core/create/index.mjs +1 -0
- package/dist/core/create/index.mjs.map +1 -1
- package/dist/core/create/just.d.mts.map +1 -1
- package/dist/core/create/just.mjs +4 -2
- package/dist/core/create/just.mjs.map +1 -1
- package/dist/core/create/source.d.mts.map +1 -1
- package/dist/core/create/source.mjs +27 -17
- package/dist/core/create/source.mjs.map +1 -1
- package/dist/core/create/timer.d.mts.map +1 -1
- package/dist/core/create/timer.mjs +40 -25
- package/dist/core/create/timer.mjs.map +1 -1
- package/dist/core/index.d.mts +2 -0
- package/dist/core/index.d.mts.map +1 -1
- package/dist/core/index.mjs +2 -0
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/operators/audit.d.mts +3 -0
- package/dist/core/operators/audit.d.mts.map +1 -1
- package/dist/core/operators/audit.mjs +56 -28
- package/dist/core/operators/audit.mjs.map +1 -1
- package/dist/core/operators/debounce.d.mts +3 -0
- package/dist/core/operators/debounce.d.mts.map +1 -1
- package/dist/core/operators/debounce.mjs +57 -26
- package/dist/core/operators/debounce.mjs.map +1 -1
- package/dist/core/operators/filter.d.mts.map +1 -1
- package/dist/core/operators/filter.mjs +35 -21
- package/dist/core/operators/filter.mjs.map +1 -1
- package/dist/core/operators/index.d.mts +1 -0
- package/dist/core/operators/index.d.mts.map +1 -1
- package/dist/core/operators/index.mjs +1 -0
- package/dist/core/operators/index.mjs.map +1 -1
- package/dist/core/operators/map.d.mts.map +1 -1
- package/dist/core/operators/map.mjs +31 -17
- package/dist/core/operators/map.mjs.map +1 -1
- package/dist/core/operators/merge-map.d.mts +5 -0
- package/dist/core/operators/merge-map.d.mts.map +1 -1
- package/dist/core/operators/merge-map.mjs +52 -26
- package/dist/core/operators/merge-map.mjs.map +1 -1
- package/dist/core/operators/pairwise.d.mts.map +1 -1
- package/dist/core/operators/pairwise.mjs +31 -20
- package/dist/core/operators/pairwise.mjs.map +1 -1
- package/dist/core/operators/scan.d.mts.map +1 -1
- package/dist/core/operators/scan.mjs +27 -14
- package/dist/core/operators/scan.mjs.map +1 -1
- package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
- package/dist/core/operators/skip-if-no-change.mjs +33 -19
- package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
- package/dist/core/operators/skip-until.d.mts.map +1 -1
- package/dist/core/operators/skip-until.mjs +40 -26
- package/dist/core/operators/skip-until.mjs.map +1 -1
- package/dist/core/operators/skip-while.d.mts.map +1 -1
- package/dist/core/operators/skip-while.mjs +35 -20
- package/dist/core/operators/skip-while.mjs.map +1 -1
- package/dist/core/operators/switch-map.d.mts +5 -0
- package/dist/core/operators/switch-map.d.mts.map +1 -1
- package/dist/core/operators/switch-map.mjs +56 -25
- package/dist/core/operators/switch-map.mjs.map +1 -1
- package/dist/core/operators/take-until.d.mts.map +1 -1
- package/dist/core/operators/take-until.mjs +36 -29
- package/dist/core/operators/take-until.mjs.map +1 -1
- package/dist/core/operators/take-while.d.mts.map +1 -1
- package/dist/core/operators/take-while.mjs +36 -22
- package/dist/core/operators/take-while.mjs.map +1 -1
- package/dist/core/{predefined/operators → operators}/take.d.mts +7 -3
- package/dist/core/operators/take.d.mts.map +1 -0
- package/dist/core/operators/take.mjs +102 -0
- package/dist/core/operators/take.mjs.map +1 -0
- package/dist/core/operators/throttle.d.mts.map +1 -1
- package/dist/core/operators/throttle.mjs +41 -26
- package/dist/core/operators/throttle.mjs.map +1 -1
- package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
- package/dist/core/operators/with-buffered-from.mjs +43 -39
- package/dist/core/operators/with-buffered-from.mjs.map +1 -1
- package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
- package/dist/core/operators/with-current-value-from.mjs +36 -21
- package/dist/core/operators/with-current-value-from.mjs.map +1 -1
- package/dist/core/operators/with-initial-value.d.mts.map +1 -1
- package/dist/core/operators/with-initial-value.mjs +24 -12
- package/dist/core/operators/with-initial-value.mjs.map +1 -1
- package/dist/core/predefined/operators/index.d.mts +0 -1
- package/dist/core/predefined/operators/index.d.mts.map +1 -1
- package/dist/core/predefined/operators/index.mjs +0 -1
- package/dist/core/predefined/operators/index.mjs.map +1 -1
- package/dist/core/scope/index.d.mts +2 -0
- package/dist/core/scope/index.d.mts.map +1 -0
- package/dist/core/scope/index.mjs +2 -0
- package/dist/core/scope/index.mjs.map +1 -0
- package/dist/core/scope/scope.d.mts +51 -0
- package/dist/core/scope/scope.d.mts.map +1 -0
- package/dist/core/scope/scope.mjs +194 -0
- package/dist/core/scope/scope.mjs.map +1 -0
- package/dist/core/types/id.d.mts +0 -2
- package/dist/core/types/id.d.mts.map +1 -1
- package/dist/core/types/index.d.mts +1 -0
- package/dist/core/types/index.d.mts.map +1 -1
- package/dist/core/types/index.mjs +1 -0
- package/dist/core/types/index.mjs.map +1 -1
- package/dist/core/types/observable-family.d.mts +1 -0
- package/dist/core/types/observable-family.d.mts.map +1 -1
- package/dist/core/types/observable.d.mts +40 -28
- package/dist/core/types/observable.d.mts.map +1 -1
- package/dist/core/types/observable.mjs +24 -0
- package/dist/core/types/observable.mjs.map +1 -1
- package/dist/core/types/scope.d.mts +27 -0
- package/dist/core/types/scope.d.mts.map +1 -0
- package/dist/core/types/scope.mjs +2 -0
- package/dist/core/types/scope.mjs.map +1 -0
- package/dist/core/utils/current-scope.d.mts +30 -0
- package/dist/core/utils/current-scope.d.mts.map +1 -0
- package/dist/core/utils/current-scope.mjs +26 -0
- package/dist/core/utils/current-scope.mjs.map +1 -0
- package/dist/core/utils/id-maker.d.mts +14 -3
- package/dist/core/utils/id-maker.d.mts.map +1 -1
- package/dist/core/utils/id-maker.mjs +18 -14
- package/dist/core/utils/id-maker.mjs.map +1 -1
- package/dist/core/utils/index.d.mts +2 -1
- package/dist/core/utils/index.d.mts.map +1 -1
- package/dist/core/utils/index.mjs +2 -1
- package/dist/core/utils/index.mjs.map +1 -1
- package/dist/core/utils/max-depth.d.mts +1 -0
- package/dist/core/utils/max-depth.d.mts.map +1 -1
- package/dist/core/utils/max-depth.mjs +9 -7
- package/dist/core/utils/max-depth.mjs.map +1 -1
- package/dist/core/utils/observable-brand.d.mts +13 -0
- package/dist/core/utils/observable-brand.d.mts.map +1 -0
- package/dist/core/utils/observable-brand.mjs +13 -0
- package/dist/core/utils/observable-brand.mjs.map +1 -0
- package/dist/utils/create-boolean-state.d.mts +2 -2
- package/dist/utils/create-reducer.d.mts +7 -7
- package/dist/utils/create-reducer.mjs +2 -2
- package/dist/utils/create-reducer.mjs.map +1 -1
- package/dist/utils/create-state.d.mts +2 -2
- package/package.json +17 -17
- package/src/core/authoring.mts +19 -0
- package/src/core/class/child-observable-class.mts +374 -0
- package/src/core/{base → class}/circular-dependency-comparison.test.mts +46 -38
- package/src/core/{base → class}/circular-dependency.test.mts +38 -2
- package/src/core/class/complete-propagation.test.mts +264 -0
- package/src/core/class/create-child-observable.mts +370 -0
- package/src/core/class/create-root-observable.mts +107 -0
- package/src/core/class/index.mts +8 -0
- package/src/core/class/manager-observable-class.mts +166 -0
- package/src/core/class/observable-base-class.mts +315 -0
- package/src/core/{base → class}/observable-base.test.mts +1 -1
- package/src/core/class/observable-node.mts +54 -0
- package/src/core/class/propagation-order.test.mts +92 -0
- package/src/core/class/propagation-state.mts +80 -0
- package/src/core/class/remove-completed.test.mts +369 -0
- package/src/core/class/root-observable-class.mts +85 -0
- package/src/core/{base → class}/write-during-propagation.test.mts +3 -3
- package/src/core/combine/combine.mts +67 -55
- package/src/core/combine/merge.mts +33 -23
- package/src/core/combine/zip.mts +95 -81
- package/src/core/create/counter.mts +95 -65
- package/src/core/create/empty.mts +42 -0
- package/src/core/create/empty.test.mts +65 -0
- package/src/core/create/from-abortable-promise.mts +54 -42
- package/src/core/create/from-promise.mts +38 -29
- package/src/core/create/from-subscribable.mts +62 -45
- package/src/core/create/from-subscribable.test.mts +8 -3
- package/src/core/create/index.mts +1 -0
- package/src/core/create/just.mts +4 -5
- package/src/core/create/source.mts +29 -20
- package/src/core/create/timer.mts +51 -32
- package/src/core/index.mts +2 -0
- package/src/core/operators/audit.mts +79 -45
- package/src/core/operators/debounce-audit-complete.test.mts +121 -0
- package/src/core/operators/debounce.mts +81 -40
- package/src/core/operators/filter.mts +48 -31
- package/src/core/operators/index.mts +1 -0
- package/src/core/operators/inner-ownership.test.mts +280 -0
- package/src/core/operators/map.mts +49 -32
- package/src/core/operators/merge-map.mts +81 -43
- package/src/core/operators/pairwise.mts +40 -30
- package/src/core/operators/scan.mts +41 -24
- package/src/core/operators/skip-if-no-change.mts +45 -31
- package/src/core/operators/skip-until.mts +55 -41
- package/src/core/operators/skip-while.mts +49 -31
- package/src/core/operators/switch-map.mts +74 -35
- package/src/core/operators/take-until-skip-until-teardown.test.mts +18 -12
- package/src/core/operators/take-until.mts +45 -41
- package/src/core/operators/take-while.mts +50 -33
- package/src/core/operators/take.mts +138 -0
- package/src/core/operators/throttle.mts +55 -38
- package/src/core/operators/with-buffered-from.mts +42 -44
- package/src/core/operators/with-buffered-from.test.mts +4 -3
- package/src/core/operators/with-current-value-from.mts +39 -25
- package/src/core/operators/with-initial-value.mts +30 -21
- package/src/core/predefined/operators/index.mts +0 -1
- package/src/core/scope/index.mts +1 -0
- package/src/core/scope/scope.mts +260 -0
- package/src/core/scope/scope.test.mts +462 -0
- package/src/core/types/id.mts +0 -4
- package/src/core/types/index.mts +1 -0
- package/src/core/types/observable-family.mts +2 -0
- package/src/core/types/observable.mts +77 -42
- package/src/core/types/scope.mts +27 -0
- package/src/core/utils/current-scope.mts +61 -0
- package/src/core/utils/id-maker.mts +20 -30
- package/src/core/utils/index.mts +2 -1
- package/src/core/utils/max-depth.mts +12 -9
- package/src/core/utils/observable-brand.mts +12 -0
- package/src/utils/create-boolean-state.mts +2 -2
- package/src/utils/create-boolean-state.test.mts +18 -4
- package/src/utils/create-reducer.mts +9 -9
- package/src/utils/create-reducer.test.mts +31 -14
- package/src/utils/create-state.mts +2 -2
- package/src/utils/create-state.test.mts +12 -14
- package/dist/core/base/create-child-observable.d.mts +0 -72
- package/dist/core/base/create-child-observable.d.mts.map +0 -1
- package/dist/core/base/create-child-observable.mjs +0 -285
- package/dist/core/base/create-child-observable.mjs.map +0 -1
- package/dist/core/base/create-observable-base.d.mts +0 -149
- package/dist/core/base/create-observable-base.d.mts.map +0 -1
- package/dist/core/base/create-observable-base.mjs +0 -273
- package/dist/core/base/create-observable-base.mjs.map +0 -1
- package/dist/core/base/create-root-observable.d.mts +0 -46
- package/dist/core/base/create-root-observable.d.mts.map +0 -1
- package/dist/core/base/create-root-observable.mjs +0 -80
- package/dist/core/base/create-root-observable.mjs.map +0 -1
- package/dist/core/base/index.d.mts +0 -3
- package/dist/core/base/index.d.mts.map +0 -1
- package/dist/core/base/index.mjs +0 -3
- package/dist/core/base/index.mjs.map +0 -1
- package/dist/core/predefined/operators/take.d.mts.map +0 -1
- package/dist/core/predefined/operators/take.mjs +0 -44
- package/dist/core/predefined/operators/take.mjs.map +0 -1
- package/dist/core/utils/utils.d.mts +0 -6
- package/dist/core/utils/utils.d.mts.map +0 -1
- package/dist/core/utils/utils.mjs +0 -37
- package/dist/core/utils/utils.mjs.map +0 -1
- package/src/core/base/create-child-observable.mts +0 -485
- package/src/core/base/create-observable-base.mts +0 -568
- package/src/core/base/create-root-observable.mts +0 -147
- package/src/core/base/index.mts +0 -2
- package/src/core/predefined/operators/take.mts +0 -48
- package/src/core/utils/utils.mts +0 -56
- package/src/core/utils/utils.test.mts +0 -88
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
// Sumi bans classes, `this`, method syntax and writes to `this` outside a
|
|
2
|
+
// `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
|
|
3
|
+
// so those rules are waived here (D-63).
|
|
4
|
+
// @sumi-expect-error-file classes/no-class
|
|
5
|
+
// @sumi-expect-error-file banned-syntax/no-this
|
|
6
|
+
// @sumi-expect-error-file functions/prefer-arrow-function
|
|
1
7
|
import { Arr, expectType, Optional } from 'ts-data-forge';
|
|
2
8
|
import type { NonEmptyTuple, Tuple } from 'ts-type-forge';
|
|
3
|
-
import {
|
|
9
|
+
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
4
10
|
import { source } from '../create/index.mjs';
|
|
5
11
|
import { withInitialValue } from '../operators/index.mjs';
|
|
6
12
|
import type {
|
|
@@ -11,6 +17,7 @@ import type {
|
|
|
11
17
|
NonEmptyUnknownList,
|
|
12
18
|
Observable,
|
|
13
19
|
SyncChildObservable,
|
|
20
|
+
UpdateToken,
|
|
14
21
|
Wrap,
|
|
15
22
|
} from '../types/index.mjs';
|
|
16
23
|
|
|
@@ -75,7 +82,9 @@ export const combine = <const OS extends NonEmptyTuple<Observable<unknown>>>(
|
|
|
75
82
|
parents: OS,
|
|
76
83
|
): CombineObservableRefined<OS> =>
|
|
77
84
|
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
78
|
-
|
|
85
|
+
new CombineObservableClass(
|
|
86
|
+
parents,
|
|
87
|
+
) as unknown as CombineObservableRefined<OS>;
|
|
79
88
|
|
|
80
89
|
/**
|
|
81
90
|
* Alias for `combine`.
|
|
@@ -83,61 +92,64 @@ export const combine = <const OS extends NonEmptyTuple<Observable<unknown>>>(
|
|
|
83
92
|
*/
|
|
84
93
|
export const combineLatest = combine;
|
|
85
94
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Optional
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
{
|
|
95
|
+
class CombineObservableClass<const A extends NonEmptyUnknownList>
|
|
96
|
+
extends SyncChildObservableClass<A, A>
|
|
97
|
+
implements CombineObservable<A>
|
|
98
|
+
{
|
|
99
|
+
constructor(parents: Wrap<A>) {
|
|
100
|
+
const parentsValues: Tuple.MapTo<Optional<A[number]>, A> = Arr.map(
|
|
101
|
+
parents,
|
|
102
|
+
(p) => p.getSnapshot(),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// The annotation on `initialValue` is what replaces the type assertion.
|
|
106
|
+
// `Arr.map` carries a single result element type, so it reports `A[number]`
|
|
107
|
+
// at every position, and `Tuple.MapTo<A[number], A>` — the uniform mapping
|
|
108
|
+
// — is *not* assignable back to `A`. The positional
|
|
109
|
+
// `Readonly<{ [K in keyof A]: A[K] }>` is, and TypeScript accepts the
|
|
110
|
+
// uniform result into it because the two mapped types share `keyof A`. So
|
|
111
|
+
// it is the annotation, not an assertion, that records the position-wise
|
|
112
|
+
// fact the checker cannot derive on its own.
|
|
113
|
+
const initialValue: Optional<Readonly<{ [K in keyof A]: A[K] }>> =
|
|
114
|
+
Arr.every(parentsValues, Optional.isSome)
|
|
115
|
+
? Optional.some(Arr.map(parentsValues, (c) => c.value))
|
|
116
|
+
: Optional.none;
|
|
117
|
+
|
|
118
|
+
super({
|
|
111
119
|
parents,
|
|
112
120
|
initialValue,
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
this.attach();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
127
|
+
const parents = this.parents;
|
|
128
|
+
|
|
129
|
+
if (parents.every((o) => o.updateToken !== updateToken)) {
|
|
130
|
+
return; // all parents are skipped
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Same shape-preserving chain as the initial value: `Arr.map` (not the
|
|
134
|
+
// native one, which drops the tuple shape) into the uniform
|
|
135
|
+
// `Tuple.MapTo`, then a positional annotation to get back to `A`.
|
|
136
|
+
const parentValues: Tuple.MapTo<Optional<A[number]>, A> = Arr.map(
|
|
137
|
+
parents,
|
|
138
|
+
(a) => a.getSnapshot(),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (!Arr.every(parentValues, Optional.isSome)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const nextValue: Readonly<{ [K in keyof A]: A[K] }> = Arr.map(
|
|
146
|
+
parentValues,
|
|
147
|
+
(a) => a.value,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
this.setNext(nextValue, updateToken);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
141
153
|
|
|
142
154
|
if (import.meta.vitest !== undefined) {
|
|
143
155
|
test('type test', () => {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
// Sumi bans classes, `this`, method syntax and writes to `this` outside a
|
|
2
|
+
// `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
|
|
3
|
+
// so those rules are waived here (D-63).
|
|
4
|
+
// @sumi-expect-error-file classes/no-class
|
|
5
|
+
// @sumi-expect-error-file banned-syntax/no-this
|
|
6
|
+
// @sumi-expect-error-file functions/prefer-arrow-function
|
|
1
7
|
import { Optional, expectType } from 'ts-data-forge';
|
|
2
8
|
import type { ArrayElement, NonEmptyTuple } from 'ts-type-forge';
|
|
3
|
-
import {
|
|
9
|
+
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
4
10
|
import { source } from '../create/index.mjs';
|
|
5
11
|
import type {
|
|
6
12
|
MergeObservable,
|
|
@@ -8,6 +14,7 @@ import type {
|
|
|
8
14
|
NonEmptyUnknownList,
|
|
9
15
|
Observable,
|
|
10
16
|
SyncChildObservable,
|
|
17
|
+
UpdateToken,
|
|
11
18
|
Wrap,
|
|
12
19
|
} from '../types/index.mjs';
|
|
13
20
|
|
|
@@ -63,34 +70,37 @@ export const merge = <const OS extends NonEmptyTuple<Observable<unknown>>>(
|
|
|
63
70
|
parents: OS,
|
|
64
71
|
): MergeObservableRefined<OS> =>
|
|
65
72
|
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
66
|
-
|
|
73
|
+
new MergeObservableClass(parents) as MergeObservableRefined<OS>;
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
class MergeObservableClass<const P extends NonEmptyUnknownList>
|
|
76
|
+
extends SyncChildObservableClass<ArrayElement<P>, P>
|
|
77
|
+
implements MergeObservable<P>
|
|
78
|
+
{
|
|
79
|
+
constructor(parents: Wrap<P>) {
|
|
80
|
+
super({
|
|
73
81
|
parents,
|
|
74
82
|
initialValue: Optional.none,
|
|
75
|
-
}
|
|
76
|
-
({ setNext }) =>
|
|
77
|
-
(updateToken) => {
|
|
78
|
-
const parentToUse = parents.find(
|
|
79
|
-
(o) =>
|
|
80
|
-
o.updateToken === updateToken && Optional.isSome(o.getSnapshot()),
|
|
81
|
-
);
|
|
83
|
+
});
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
85
|
+
this.attach();
|
|
86
|
+
}
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
89
|
+
const parentToUse = this.parents.find(
|
|
90
|
+
(o) => o.updateToken === updateToken && Optional.isSome(o.getSnapshot()),
|
|
91
|
+
);
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
if (parentToUse === undefined) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const nextValue =
|
|
98
|
+
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
99
|
+
Optional.unwrap(parentToUse.getSnapshot()) as ArrayElement<P>;
|
|
100
|
+
|
|
101
|
+
this.setNext(nextValue, updateToken);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
94
104
|
|
|
95
105
|
if (import.meta.vitest !== undefined) {
|
|
96
106
|
test('type test', () => {
|
package/src/core/combine/zip.mts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
// Sumi bans classes, `this`, method syntax and writes to `this` outside a
|
|
2
|
+
// `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
|
|
3
|
+
// so those rules are waived here (D-63).
|
|
4
|
+
// @sumi-expect-error-file classes/no-class
|
|
5
|
+
// @sumi-expect-error-file banned-syntax/no-this
|
|
6
|
+
// @sumi-expect-error-file functions/prefer-arrow-function
|
|
7
|
+
// @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
|
|
1
8
|
import { Arr, Optional, createQueue, expectType } from 'ts-data-forge';
|
|
2
9
|
import type { NonEmptyTuple, Tuple } from 'ts-type-forge';
|
|
3
|
-
import {
|
|
10
|
+
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
4
11
|
import { source } from '../create/index.mjs';
|
|
5
12
|
import { withInitialValue } from '../operators/index.mjs';
|
|
6
13
|
import type {
|
|
@@ -71,90 +78,97 @@ export const zip = <const OS extends NonEmptyTuple<Observable<unknown>>>(
|
|
|
71
78
|
parents: OS,
|
|
72
79
|
): ZipObservableRefined<OS> =>
|
|
73
80
|
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
81
|
+
new ZipObservableClass(parents) as unknown as ZipObservableRefined<OS>;
|
|
82
|
+
|
|
83
|
+
class ZipObservableClass<const A extends NonEmptyUnknownList>
|
|
84
|
+
extends SyncChildObservableClass<A, A>
|
|
85
|
+
implements ZipObservable<A>
|
|
86
|
+
{
|
|
87
|
+
readonly #queues: TupleToQueueTuple<A>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The token of each parent's latest value that zip has taken in, so that a
|
|
91
|
+
* value a parent emits in the update it completes in — its `tryComplete`
|
|
92
|
+
* reaches zip before its `tryUpdate` does — still counts as queued.
|
|
93
|
+
* Written in place, one slot per parent, on every update that reaches zip.
|
|
94
|
+
*/
|
|
95
|
+
// @sumi-expect-error readonly/require-readonly-type
|
|
96
|
+
readonly #mut_takenTokens: UpdateToken[];
|
|
97
|
+
|
|
98
|
+
constructor(parents: Wrap<A>) {
|
|
99
|
+
const parentsValues: Tuple.MapTo<Optional<A[number]>, A> = Arr.map(
|
|
100
|
+
parents,
|
|
101
|
+
(p) => p.getSnapshot(),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// The annotation on `initialValue` is what replaces the type assertion.
|
|
105
|
+
// `Arr.map` carries a single result element type, so it reports `A[number]`
|
|
106
|
+
// at every position, and `Tuple.MapTo<A[number], A>` — the uniform mapping
|
|
107
|
+
// — is *not* assignable back to `A`. The positional
|
|
108
|
+
// `Readonly<{ [K in keyof A]: A[K] }>` is, and TypeScript accepts the
|
|
109
|
+
// uniform result into it because the two mapped types share `keyof A`. So
|
|
110
|
+
// it is the annotation, not an assertion, that records the position-wise
|
|
111
|
+
// fact the checker cannot derive on its own.
|
|
112
|
+
const initialValue: Optional<Readonly<{ [K in keyof A]: A[K] }>> =
|
|
113
|
+
Arr.every(parentsValues, Optional.isSome)
|
|
114
|
+
? Optional.some(Arr.map(parentsValues, (c) => c.value))
|
|
115
|
+
: Optional.none;
|
|
116
|
+
|
|
117
|
+
super({ parents, initialValue });
|
|
118
|
+
|
|
119
|
+
this.#queues = Arr.map(parents, () => createQueue());
|
|
120
|
+
|
|
121
|
+
this.#mut_takenTokens = Array.from(parents, (p) => p.updateToken);
|
|
122
|
+
|
|
123
|
+
this.attach();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
127
|
+
const queues = this.#queues;
|
|
128
|
+
|
|
129
|
+
for (const [index, par] of this.parents.entries()) {
|
|
130
|
+
const sn = par.getSnapshot();
|
|
131
|
+
|
|
132
|
+
if (!(par.updateToken === updateToken && Optional.isSome(sn))) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
queues[index]?.enqueue(sn.value);
|
|
137
|
+
|
|
138
|
+
this.#mut_takenTokens[index] = updateToken;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (queues.some((list) => list.isEmpty)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const nextValue =
|
|
146
|
+
// `Arr.map` has a single result element type, so it reports
|
|
147
|
+
// `A[number] | undefined` per position rather than `A[P]`.
|
|
148
|
+
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
149
|
+
Arr.map(queues, (q) => Optional.unwrap(q.dequeue())) as A;
|
|
150
|
+
|
|
151
|
+
this.setNext(nextValue, updateToken);
|
|
152
|
+
|
|
153
|
+
if (this.canEmitNoMore()) {
|
|
154
|
+
this.complete();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* No tuple can be completed any more once a completed source has nothing
|
|
160
|
+
* left to pair (#2343).
|
|
161
|
+
*/
|
|
162
|
+
override canEmitNoMore(): boolean {
|
|
163
|
+
return this.parents.some(
|
|
113
164
|
(par, index) =>
|
|
114
165
|
par.isCompleted &&
|
|
115
|
-
queues[index]?.isEmpty === true &&
|
|
116
|
-
(par.updateToken === mut_takenTokens[index] ||
|
|
166
|
+
this.#queues[index]?.isEmpty === true &&
|
|
167
|
+
(par.updateToken === this.#mut_takenTokens[index] ||
|
|
117
168
|
Optional.isNone(par.getSnapshot())),
|
|
118
169
|
);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
{
|
|
122
|
-
parents,
|
|
123
|
-
initialValue,
|
|
124
|
-
canEmitNoMore,
|
|
125
|
-
},
|
|
126
|
-
({ setNext, complete }) =>
|
|
127
|
-
(updateToken) => {
|
|
128
|
-
for (const [index, par] of parents.entries()) {
|
|
129
|
-
const sn = par.getSnapshot();
|
|
130
|
-
|
|
131
|
-
if (!(par.updateToken === updateToken && Optional.isSome(sn))) {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
queues[index]?.enqueue(sn.value);
|
|
136
|
-
|
|
137
|
-
mut_takenTokens[index] = updateToken;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (queues.some((list) => list.isEmpty)) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const nextValue =
|
|
145
|
-
// `Arr.map` has a single result element type, so it reports
|
|
146
|
-
// `A[number] | undefined` per position rather than `A[P]`.
|
|
147
|
-
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
148
|
-
Arr.map(queues, (q) => Optional.unwrap(q.dequeue())) as A;
|
|
149
|
-
|
|
150
|
-
setNext(nextValue, updateToken);
|
|
151
|
-
|
|
152
|
-
if (canEmitNoMore()) {
|
|
153
|
-
complete();
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
);
|
|
157
|
-
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
158
172
|
|
|
159
173
|
if (import.meta.vitest !== undefined) {
|
|
160
174
|
test('type test', () => {
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
// Sumi bans classes, `this`, method syntax and writes to `this` outside a
|
|
2
|
+
// `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
|
|
3
|
+
// so those rules are waived here (D-63).
|
|
4
|
+
// @sumi-expect-error-file classes/no-class
|
|
5
|
+
// @sumi-expect-error-file banned-syntax/no-this
|
|
6
|
+
// @sumi-expect-error-file functions/prefer-arrow-function
|
|
7
|
+
// @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
|
|
1
8
|
import { asSafeUint, Optional, SafeUint } from 'ts-data-forge';
|
|
2
9
|
import type { PositiveSafeIntWithSmallInt } from 'ts-type-forge';
|
|
3
|
-
import {
|
|
10
|
+
import { RootObservableClass } from '../class/index.mjs';
|
|
4
11
|
import type { CounterObservable, TimerId } from '../types/index.mjs';
|
|
5
12
|
|
|
6
13
|
/**
|
|
@@ -65,78 +72,101 @@ export const counter = (
|
|
|
65
72
|
times?: PositiveSafeIntWithSmallInt;
|
|
66
73
|
}>,
|
|
67
74
|
): CounterObservable => {
|
|
68
|
-
|
|
75
|
+
const observable = new CounterObservableClass(
|
|
76
|
+
intervalMilliSeconds,
|
|
77
|
+
options?.emitInitial ?? true,
|
|
78
|
+
options?.times,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (!(options?.startManually ?? false)) {
|
|
82
|
+
observable.start();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return observable;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
class CounterObservableClass
|
|
89
|
+
extends RootObservableClass<SafeUint>
|
|
90
|
+
implements CounterObservable
|
|
91
|
+
{
|
|
92
|
+
readonly #intervalMilliSeconds: number;
|
|
93
|
+
readonly #emitInitial: boolean;
|
|
94
|
+
readonly #times: PositiveSafeIntWithSmallInt | undefined;
|
|
95
|
+
#mut_counter: SafeUint;
|
|
96
|
+
#mut_emittedCount: number;
|
|
97
|
+
#mut_timerId0: TimerId | undefined;
|
|
98
|
+
#mut_timerId: TimerId | undefined;
|
|
99
|
+
#mut_isStarted: boolean;
|
|
69
100
|
|
|
70
|
-
|
|
101
|
+
constructor(
|
|
102
|
+
intervalMilliSeconds: number,
|
|
103
|
+
emitInitial: boolean,
|
|
104
|
+
times: PositiveSafeIntWithSmallInt | undefined,
|
|
105
|
+
) {
|
|
106
|
+
super({ initialValue: Optional.none });
|
|
71
107
|
|
|
72
|
-
|
|
108
|
+
this.#intervalMilliSeconds = intervalMilliSeconds;
|
|
73
109
|
|
|
74
|
-
|
|
110
|
+
this.#emitInitial = emitInitial;
|
|
75
111
|
|
|
76
|
-
|
|
112
|
+
this.#times = times;
|
|
77
113
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
114
|
+
this.#mut_counter = asSafeUint(0);
|
|
115
|
+
|
|
116
|
+
this.#mut_emittedCount = 0;
|
|
117
|
+
|
|
118
|
+
this.#mut_timerId0 = undefined;
|
|
119
|
+
|
|
120
|
+
this.#mut_timerId = undefined;
|
|
121
|
+
|
|
122
|
+
this.#mut_isStarted = false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#emit(value: SafeUint): void {
|
|
126
|
+
this.startUpdate(value);
|
|
127
|
+
|
|
128
|
+
this.#mut_emittedCount += 1;
|
|
129
|
+
|
|
130
|
+
if (this.#times !== undefined && this.#mut_emittedCount >= this.#times) {
|
|
131
|
+
this.complete();
|
|
81
132
|
}
|
|
133
|
+
}
|
|
82
134
|
|
|
83
|
-
|
|
84
|
-
|
|
135
|
+
start(): void {
|
|
136
|
+
if (this.#mut_isStarted) {
|
|
137
|
+
console.warn('cannot start twice');
|
|
138
|
+
|
|
139
|
+
return;
|
|
85
140
|
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const observable = createRootObservable<
|
|
89
|
-
SafeUint,
|
|
90
|
-
Readonly<{ start: () => void }>
|
|
91
|
-
>(
|
|
92
|
-
{ initialValue: Optional.none, onComplete: resetTimer },
|
|
93
|
-
({ startUpdate, isCompleted, complete }) => ({
|
|
94
|
-
start: () => {
|
|
95
|
-
if (mut_isStarted) {
|
|
96
|
-
console.warn('cannot start twice');
|
|
97
|
-
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
mut_isStarted = true;
|
|
102
|
-
|
|
103
|
-
if (isCompleted()) {
|
|
104
|
-
console.warn('cannot restart stopped CounterObservable');
|
|
105
|
-
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const emit = (value: SafeUint): void => {
|
|
110
|
-
startUpdate(value);
|
|
111
|
-
|
|
112
|
-
mut_emittedCount += 1;
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
options?.times !== undefined &&
|
|
116
|
-
mut_emittedCount >= options.times
|
|
117
|
-
) {
|
|
118
|
-
complete();
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
if (options?.emitInitial ?? true) {
|
|
123
|
-
mut_timerId0 = setTimeout(() => {
|
|
124
|
-
emit(mut_counter);
|
|
125
|
-
}, 0);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
mut_timerId = setInterval(() => {
|
|
129
|
-
mut_counter = SafeUint.add(1, mut_counter);
|
|
130
|
-
|
|
131
|
-
emit(mut_counter);
|
|
132
|
-
}, intervalMilliSeconds);
|
|
133
|
-
},
|
|
134
|
-
}),
|
|
135
|
-
);
|
|
136
141
|
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
this.#mut_isStarted = true;
|
|
143
|
+
|
|
144
|
+
if (this.isCompleted) {
|
|
145
|
+
console.warn('cannot restart stopped CounterObservable');
|
|
146
|
+
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (this.#emitInitial) {
|
|
151
|
+
this.#mut_timerId0 = setTimeout(() => {
|
|
152
|
+
this.#emit(this.#mut_counter);
|
|
153
|
+
}, 0);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.#mut_timerId = setInterval(() => {
|
|
157
|
+
this.#mut_counter = SafeUint.add(1, this.#mut_counter);
|
|
158
|
+
|
|
159
|
+
this.#emit(this.#mut_counter);
|
|
160
|
+
}, this.#intervalMilliSeconds);
|
|
139
161
|
}
|
|
140
162
|
|
|
141
|
-
|
|
142
|
-
|
|
163
|
+
protected override teardown(): void {
|
|
164
|
+
if (this.#mut_timerId0 !== undefined) {
|
|
165
|
+
clearTimeout(this.#mut_timerId0);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (this.#mut_timerId !== undefined) {
|
|
169
|
+
clearInterval(this.#mut_timerId);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Optional } from 'ts-data-forge';
|
|
2
|
+
import { RootObservableClass } from '../class/index.mjs';
|
|
3
|
+
import type { RootObservable } from '../types/index.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates an Observable that holds no value and is already completed, so it
|
|
7
|
+
* never passes anything on (almost equivalent to RxJS `EMPTY`). Useful inside
|
|
8
|
+
* `switchMap` for the branch that should follow nothing, where a `source()`
|
|
9
|
+
* would offer a `next` that nothing calls.
|
|
10
|
+
*
|
|
11
|
+
* @template A - The type of the values it would carry
|
|
12
|
+
* @returns An Observable with no value that is already completed
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const nothing$ = empty<number>();
|
|
17
|
+
*
|
|
18
|
+
* const valueHistory: number[] = [];
|
|
19
|
+
*
|
|
20
|
+
* nothing$.subscribe(
|
|
21
|
+
* (value) => {
|
|
22
|
+
* valueHistory.push(value);
|
|
23
|
+
* },
|
|
24
|
+
* () => {
|
|
25
|
+
* // onComplete — called immediately since empty() completes right away
|
|
26
|
+
* },
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* assert.deepStrictEqual(valueHistory, []);
|
|
30
|
+
*
|
|
31
|
+
* assert.strictEqual(nothing$.isCompleted, true);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export const empty = <A,>(): RootObservable<A> => {
|
|
35
|
+
const obs = new RootObservableClass<A>({
|
|
36
|
+
initialValue: Optional.none,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
obs.complete();
|
|
40
|
+
|
|
41
|
+
return obs;
|
|
42
|
+
};
|