synstate 3.3.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- 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 +0 -3
- package/dist/core/combine/merge.d.mts.map +1 -1
- package/dist/core/combine/merge.mjs +24 -15
- package/dist/core/combine/merge.mjs.map +1 -1
- package/dist/core/combine/zip.d.mts +4 -2
- package/dist/core/combine/zip.d.mts.map +1 -1
- package/dist/core/combine/zip.mjs +54 -18
- package/dist/core/combine/zip.mjs.map +1 -1
- package/dist/core/create/counter.d.mts +9 -3
- package/dist/core/create/counter.d.mts.map +1 -1
- package/dist/core/create/counter.mjs +69 -35
- package/dist/core/create/counter.mjs.map +1 -1
- 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 +48 -12
- package/dist/core/create/from-subscribable.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 +7 -0
- package/dist/core/create/source.d.mts.map +1 -1
- package/dist/core/create/source.mjs +27 -13
- 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 -3
- package/dist/core/operators/merge-map.d.mts.map +1 -1
- package/dist/core/operators/merge-map.mjs +49 -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 +43 -20
- 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 -3
- package/dist/core/operators/switch-map.d.mts.map +1 -1
- package/dist/core/operators/switch-map.mjs +53 -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 +38 -19
- 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 +44 -31
- 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 +6 -1
- package/dist/core/types/observable-family.d.mts.map +1 -1
- package/dist/core/types/observable-family.mjs.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-event-emitter.d.mts +2 -2
- package/dist/utils/create-reducer.d.mts +17 -8
- package/dist/utils/create-reducer.d.mts.map +1 -1
- package/dist/utils/create-reducer.mjs +14 -6
- package/dist/utils/create-reducer.mjs.map +1 -1
- package/dist/utils/create-state.d.mts +9 -2
- package/dist/utils/create-state.d.mts.map +1 -1
- package/dist/utils/create-state.mjs +7 -0
- package/dist/utils/create-state.mjs.map +1 -1
- package/package.json +16 -16
- 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/class/write-during-propagation.test.mts +234 -0
- package/src/core/combine/combine.mts +67 -55
- package/src/core/combine/merge.mts +33 -26
- package/src/core/combine/zip.mts +103 -58
- package/src/core/create/counter.mts +105 -54
- package/src/core/create/counter.test.mts +82 -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 +68 -23
- package/src/core/create/from-subscribable.test.mts +134 -0
- package/src/core/create/just.mts +4 -5
- package/src/core/create/source.mts +37 -13
- package/src/core/create/source.test.mts +41 -0
- 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 -46
- 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 +59 -33
- package/src/core/operators/skip-while.mts +49 -31
- package/src/core/operators/switch-map.mts +74 -38
- package/src/core/operators/take-until-skip-until-teardown.test.mts +133 -0
- package/src/core/operators/take-until.mts +49 -31
- 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 +45 -35
- package/src/core/operators/with-buffered-from.test.mts +57 -0
- 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 +7 -1
- 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-event-emitter.mts +2 -2
- package/src/utils/create-reducer.mts +22 -16
- package/src/utils/create-reducer.test.mts +43 -14
- package/src/utils/create-state.mts +9 -2
- package/src/utils/create-state.test.mts +32 -14
- package/dist/core/base/create-child-observable.d.mts +0 -65
- package/dist/core/base/create-child-observable.d.mts.map +0 -1
- package/dist/core/base/create-child-observable.mjs +0 -267
- package/dist/core/base/create-child-observable.mjs.map +0 -1
- package/dist/core/base/create-observable-base.d.mts +0 -147
- package/dist/core/base/create-observable-base.d.mts.map +0 -1
- package/dist/core/base/create-observable-base.mjs +0 -255
- package/dist/core/base/create-observable-base.mjs.map +0 -1
- package/dist/core/base/create-root-observable.d.mts +0 -37
- package/dist/core/base/create-root-observable.d.mts.map +0 -1
- package/dist/core/base/create-root-observable.mjs +0 -57
- 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 -457
- package/src/core/base/create-observable-base.mts +0 -548
- package/src/core/base/create-root-observable.mts +0 -113
- 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,16 +1,31 @@
|
|
|
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 { Optional } from 'ts-data-forge';
|
|
2
|
-
import {
|
|
9
|
+
import { AsyncChildObservableClass } from '../class/index.mjs';
|
|
10
|
+
import { createScope, getCurrentScope } from '../scope/index.mjs';
|
|
3
11
|
import type {
|
|
4
12
|
DropInitialValueOperator,
|
|
5
13
|
Observable,
|
|
14
|
+
Scope,
|
|
6
15
|
Subscription,
|
|
7
16
|
SwitchMapOperatorObservable,
|
|
17
|
+
UpdateToken,
|
|
8
18
|
} from '../types/index.mjs';
|
|
9
19
|
|
|
10
20
|
/**
|
|
11
21
|
* Projects each source value to an observable, subscribes to it, and emits its values.
|
|
12
22
|
* When a new value arrives from the source, the previous inner observable is unsubscribed.
|
|
13
23
|
*
|
|
24
|
+
* The projection runs in a scope of its own (see `createScope`): what it
|
|
25
|
+
* creates is disposed when the next value switches away from it and when
|
|
26
|
+
* `switchMap` completes. An observable it only returns — one created outside
|
|
27
|
+
* the projection and shared with others — is unsubscribed and left running.
|
|
28
|
+
*
|
|
14
29
|
* @template A - The type of values from the source
|
|
15
30
|
* @template B - The type of values from the projected observable
|
|
16
31
|
* @param mapToObservable - A function that maps each source value to an observable
|
|
@@ -96,59 +111,80 @@ import type {
|
|
|
96
111
|
* 'C3',
|
|
97
112
|
* ]);
|
|
98
113
|
* ```
|
|
99
|
-
*
|
|
100
|
-
* @note To improve code readability, consider using `createState` instead of `switchMap`,
|
|
101
|
-
* subscribe to `parentObservable` and call `setState` within it.
|
|
102
114
|
*/
|
|
103
115
|
export const switchMap =
|
|
104
116
|
<A, B>(
|
|
105
117
|
mapToObservable: (curr: A) => Observable<B>,
|
|
106
118
|
): DropInitialValueOperator<A, B> =>
|
|
107
119
|
(parentObservable) =>
|
|
108
|
-
|
|
120
|
+
new SwitchMapObservableClass(parentObservable, mapToObservable);
|
|
121
|
+
|
|
122
|
+
class SwitchMapObservableClass<A, B>
|
|
123
|
+
extends AsyncChildObservableClass<B, readonly [A]>
|
|
124
|
+
implements SwitchMapOperatorObservable<A, B>
|
|
125
|
+
{
|
|
126
|
+
readonly #mapToObservable: (curr: A) => Observable<B>;
|
|
109
127
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
/**
|
|
129
|
+
* The scope the operator was created in, so that each inner scope is
|
|
130
|
+
* disposed with it; the projection itself runs during propagation, when the
|
|
131
|
+
* current scope is whatever happens to be running.
|
|
132
|
+
*/
|
|
133
|
+
readonly #ownerScope: Scope | undefined;
|
|
115
134
|
|
|
116
|
-
|
|
135
|
+
#mut_innerScope: Scope | undefined;
|
|
136
|
+
#mut_subscription: Subscription | undefined;
|
|
117
137
|
|
|
118
|
-
|
|
119
|
-
|
|
138
|
+
constructor(
|
|
139
|
+
parentObservable: Observable<A>,
|
|
140
|
+
mapToObservable: (curr: A) => Observable<B>,
|
|
141
|
+
) {
|
|
142
|
+
super({
|
|
120
143
|
parents: [parentObservable],
|
|
121
144
|
initialValue: Optional.none,
|
|
122
|
-
|
|
123
|
-
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
this.#mapToObservable = mapToObservable;
|
|
148
|
+
|
|
149
|
+
this.#ownerScope = getCurrentScope();
|
|
150
|
+
|
|
151
|
+
this.#mut_innerScope = undefined;
|
|
152
|
+
|
|
153
|
+
this.#mut_subscription = undefined;
|
|
154
|
+
|
|
155
|
+
this.attach();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Releases the inner observable, and only the one this operator made. */
|
|
159
|
+
#releaseInner(): void {
|
|
160
|
+
this.#mut_subscription?.unsubscribe();
|
|
161
|
+
|
|
162
|
+
this.#mut_innerScope?.dispose();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
166
|
+
const par = this.parents[0];
|
|
124
167
|
|
|
125
|
-
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
({ startUpdate }) =>
|
|
129
|
-
(updateToken) => {
|
|
130
|
-
const sn = parentObservable.getSnapshot();
|
|
168
|
+
const sn = par.getSnapshot();
|
|
131
169
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
) {
|
|
136
|
-
return; // skip update
|
|
137
|
-
}
|
|
170
|
+
if (par.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
171
|
+
return; // skip update
|
|
172
|
+
}
|
|
138
173
|
|
|
139
|
-
|
|
174
|
+
this.#releaseInner();
|
|
140
175
|
|
|
141
|
-
|
|
176
|
+
const innerScope = createScope(this.#ownerScope);
|
|
142
177
|
|
|
143
|
-
|
|
178
|
+
this.#mut_innerScope = innerScope;
|
|
144
179
|
|
|
145
|
-
|
|
180
|
+
const observable = innerScope.run(() => this.#mapToObservable(sn.value));
|
|
146
181
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
182
|
+
this.#mut_subscription = observable.subscribe((curr) => {
|
|
183
|
+
this.startUpdate(curr);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
150
186
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
187
|
+
protected override teardown(): void {
|
|
188
|
+
this.#releaseInner();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { toNode } from '../class/index.mjs';
|
|
2
|
+
import { source } from '../create/index.mjs';
|
|
3
|
+
import { skipUntil } from './skip-until.mjs';
|
|
4
|
+
import { takeUntil } from './take-until.mjs';
|
|
5
|
+
import { take } from './take.mjs';
|
|
6
|
+
|
|
7
|
+
describe('takeUntil releases the notifier', () => {
|
|
8
|
+
test('when the result is completed before the notifier emits', () => {
|
|
9
|
+
const notifier$ = source<undefined>();
|
|
10
|
+
|
|
11
|
+
const result$ = source<number>().pipe(takeUntil(notifier$));
|
|
12
|
+
|
|
13
|
+
assert.isTrue(toNode(notifier$).hasSubscriber);
|
|
14
|
+
|
|
15
|
+
result$.complete();
|
|
16
|
+
|
|
17
|
+
assert.isFalse(toNode(notifier$).hasSubscriber);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('when the parent completes', () => {
|
|
21
|
+
const notifier$ = source<undefined>();
|
|
22
|
+
|
|
23
|
+
const num$ = source<number>();
|
|
24
|
+
|
|
25
|
+
num$.pipe(takeUntil(notifier$));
|
|
26
|
+
|
|
27
|
+
num$.complete();
|
|
28
|
+
|
|
29
|
+
assert.isFalse(toNode(notifier$).hasSubscriber);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('not when the downstream completes', () => {
|
|
33
|
+
const notifier$ = source<undefined>();
|
|
34
|
+
|
|
35
|
+
const num$ = source<number>();
|
|
36
|
+
|
|
37
|
+
const result$ = num$.pipe(takeUntil(notifier$));
|
|
38
|
+
|
|
39
|
+
const taken$ = result$.pipe(take(1));
|
|
40
|
+
|
|
41
|
+
taken$.subscribe(() => {});
|
|
42
|
+
|
|
43
|
+
// `take(n)` completes on the emission after its `n`th.
|
|
44
|
+
num$.next(1);
|
|
45
|
+
|
|
46
|
+
num$.next(2);
|
|
47
|
+
|
|
48
|
+
assert.isTrue(taken$.isCompleted);
|
|
49
|
+
|
|
50
|
+
// Completion flows only downstream, so `takeUntil` is still running.
|
|
51
|
+
assert.isFalse(result$.isCompleted);
|
|
52
|
+
|
|
53
|
+
assert.isTrue(toNode(notifier$).hasSubscriber);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('when the notifier emits', () => {
|
|
57
|
+
const notifier$ = source<undefined>();
|
|
58
|
+
|
|
59
|
+
const result$ = source<number>().pipe(takeUntil(notifier$));
|
|
60
|
+
|
|
61
|
+
notifier$.next(undefined);
|
|
62
|
+
|
|
63
|
+
assert.isTrue(result$.isCompleted);
|
|
64
|
+
|
|
65
|
+
assert.isFalse(toNode(notifier$).hasSubscriber);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('when the notifier has already emitted', () => {
|
|
69
|
+
const notifier$ = source<number>(0);
|
|
70
|
+
|
|
71
|
+
const result$ = source<number>().pipe(takeUntil(notifier$));
|
|
72
|
+
|
|
73
|
+
assert.isTrue(result$.isCompleted);
|
|
74
|
+
|
|
75
|
+
assert.isFalse(toNode(notifier$).hasSubscriber);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('skipUntil releases the notifier', () => {
|
|
80
|
+
test('when the notifier emits', () => {
|
|
81
|
+
const notifier$ = source<undefined>();
|
|
82
|
+
|
|
83
|
+
const num$ = source<number>();
|
|
84
|
+
|
|
85
|
+
const result$ = num$.pipe(skipUntil(notifier$));
|
|
86
|
+
|
|
87
|
+
const mut_history: number[] = [];
|
|
88
|
+
|
|
89
|
+
result$.subscribe((x) => {
|
|
90
|
+
mut_history.push(x);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
num$.next(1);
|
|
94
|
+
|
|
95
|
+
notifier$.next(undefined);
|
|
96
|
+
|
|
97
|
+
assert.isFalse(toNode(notifier$).hasSubscriber);
|
|
98
|
+
|
|
99
|
+
num$.next(2);
|
|
100
|
+
|
|
101
|
+
assert.deepStrictEqual(mut_history, [2]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('when the notifier has already emitted', () => {
|
|
105
|
+
const notifier$ = source<number>(0);
|
|
106
|
+
|
|
107
|
+
const num$ = source<number>();
|
|
108
|
+
|
|
109
|
+
const result$ = num$.pipe(skipUntil(notifier$));
|
|
110
|
+
|
|
111
|
+
const mut_history: number[] = [];
|
|
112
|
+
|
|
113
|
+
result$.subscribe((x) => {
|
|
114
|
+
mut_history.push(x);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
assert.isFalse(toNode(notifier$).hasSubscriber);
|
|
118
|
+
|
|
119
|
+
num$.next(1);
|
|
120
|
+
|
|
121
|
+
assert.deepStrictEqual(mut_history, [1]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('when the result is completed before the notifier emits', () => {
|
|
125
|
+
const notifier$ = source<undefined>();
|
|
126
|
+
|
|
127
|
+
const result$ = source<number>().pipe(skipUntil(notifier$));
|
|
128
|
+
|
|
129
|
+
result$.complete();
|
|
130
|
+
|
|
131
|
+
assert.isFalse(toNode(notifier$).hasSubscriber);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -1,9 +1,18 @@
|
|
|
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 { Optional } from 'ts-data-forge';
|
|
2
|
-
import {
|
|
9
|
+
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
3
10
|
import type {
|
|
4
11
|
KeepInitialValueOperator,
|
|
5
12
|
Observable,
|
|
13
|
+
Subscription,
|
|
6
14
|
TakeUntilOperatorObservable,
|
|
15
|
+
UpdateToken,
|
|
7
16
|
} from '../types/index.mjs';
|
|
8
17
|
|
|
9
18
|
/**
|
|
@@ -59,43 +68,52 @@ export const takeUntil = <A,>(
|
|
|
59
68
|
): KeepInitialValueOperator<A, A> =>
|
|
60
69
|
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
61
70
|
((parentObservable) =>
|
|
62
|
-
|
|
71
|
+
new TakeUntilObservableClass(
|
|
63
72
|
parentObservable,
|
|
64
73
|
notifier,
|
|
65
74
|
)) as KeepInitialValueOperator<A, A>;
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
class TakeUntilObservableClass<A>
|
|
77
|
+
extends SyncChildObservableClass<A, readonly [A]>
|
|
78
|
+
implements TakeUntilOperatorObservable<A>
|
|
79
|
+
{
|
|
80
|
+
/** Unset only while the `subscribe` in the constructor runs. */
|
|
81
|
+
readonly #subscription: Subscription | undefined;
|
|
82
|
+
|
|
83
|
+
constructor(parentObservable: Observable<A>, notifier: Observable<unknown>) {
|
|
84
|
+
super({
|
|
73
85
|
parents: [parentObservable],
|
|
74
86
|
initialValue: parentObservable.getSnapshot(),
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const complete = (): void => {
|
|
90
|
+
this.complete();
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// A notifier that already holds a value completes this observable inside
|
|
94
|
+
// `subscribe`, before the subscription is stored.
|
|
95
|
+
this.#subscription = notifier.subscribe(complete, complete);
|
|
96
|
+
|
|
97
|
+
if (this.isCompleted) {
|
|
98
|
+
this.#subscription.unsubscribe();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.attach();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
105
|
+
const par = this.parents[0];
|
|
79
106
|
|
|
80
|
-
|
|
81
|
-
parentObservable.updateToken !== updateToken ||
|
|
82
|
-
Optional.isNone(sn)
|
|
83
|
-
) {
|
|
84
|
-
return; // skip update
|
|
85
|
-
}
|
|
107
|
+
const sn = par.getSnapshot();
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
109
|
+
if (par.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
110
|
+
return; // skip update
|
|
111
|
+
}
|
|
90
112
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
observable.complete();
|
|
94
|
-
},
|
|
95
|
-
() => {
|
|
96
|
-
observable.complete();
|
|
97
|
-
},
|
|
98
|
-
);
|
|
113
|
+
this.setNext(sn.value, updateToken);
|
|
114
|
+
}
|
|
99
115
|
|
|
100
|
-
|
|
101
|
-
|
|
116
|
+
protected override teardown(): void {
|
|
117
|
+
this.#subscription?.unsubscribe();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
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 {
|
|
2
9
|
Optional,
|
|
3
10
|
SafeUint,
|
|
@@ -5,13 +12,14 @@ import {
|
|
|
5
12
|
expectType,
|
|
6
13
|
pipe,
|
|
7
14
|
} from 'ts-data-forge';
|
|
8
|
-
import {
|
|
15
|
+
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
9
16
|
import { source } from '../create/index.mjs';
|
|
10
17
|
import type {
|
|
11
18
|
DropInitialValueOperator,
|
|
12
19
|
InitializedObservable,
|
|
13
20
|
Observable,
|
|
14
21
|
TakeWhileOperatorObservable,
|
|
22
|
+
UpdateToken,
|
|
15
23
|
} from '../types/index.mjs';
|
|
16
24
|
import { withInitialValue } from './with-initial-value.mjs';
|
|
17
25
|
|
|
@@ -75,16 +83,22 @@ export const takeWhile =
|
|
|
75
83
|
predicate: (value: A, index: SafeUint | -1) => boolean,
|
|
76
84
|
): DropInitialValueOperator<A, A> =>
|
|
77
85
|
(parentObservable) =>
|
|
78
|
-
|
|
86
|
+
new TakeWhileObservableClass(parentObservable, predicate);
|
|
79
87
|
|
|
80
88
|
/* implementation */
|
|
81
89
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
class TakeWhileObservableClass<A>
|
|
91
|
+
extends SyncChildObservableClass<A, readonly [A]>
|
|
92
|
+
implements TakeWhileOperatorObservable<A>
|
|
93
|
+
{
|
|
94
|
+
readonly #predicate: (value: A, index: SafeUint | -1) => boolean;
|
|
95
|
+
#mut_index: SafeUint | -1;
|
|
96
|
+
|
|
97
|
+
constructor(
|
|
98
|
+
parentObservable: Observable<A>,
|
|
99
|
+
predicate: (value: A, index: SafeUint | -1) => boolean,
|
|
100
|
+
) {
|
|
101
|
+
super({
|
|
88
102
|
parents: [parentObservable],
|
|
89
103
|
initialValue: pipe(parentObservable.getSnapshot()).map((par) =>
|
|
90
104
|
Optional.isNone(par)
|
|
@@ -93,31 +107,34 @@ const createTakeWhileObservable = <A,>(
|
|
|
93
107
|
? par
|
|
94
108
|
: Optional.none,
|
|
95
109
|
).value,
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
this.#mut_index = -1;
|
|
113
|
+
|
|
114
|
+
this.#predicate = predicate;
|
|
115
|
+
|
|
116
|
+
this.attach();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
120
|
+
const par = this.parents[0];
|
|
121
|
+
|
|
122
|
+
const sn = par.getSnapshot();
|
|
123
|
+
|
|
124
|
+
if (par.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
125
|
+
return; // skip update
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.#mut_index =
|
|
129
|
+
this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
|
|
130
|
+
|
|
131
|
+
if (this.#predicate(sn.value, this.#mut_index)) {
|
|
132
|
+
this.setNext(sn.value, updateToken);
|
|
133
|
+
} else {
|
|
134
|
+
this.complete();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
121
138
|
|
|
122
139
|
if (import.meta.vitest !== undefined) {
|
|
123
140
|
test('type test', () => {
|
|
@@ -0,0 +1,138 @@
|
|
|
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
|
|
8
|
+
import { Optional, expectType } from 'ts-data-forge';
|
|
9
|
+
import type { PositiveSafeIntWithSmallInt } from 'ts-type-forge';
|
|
10
|
+
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
11
|
+
import { source } from '../create/index.mjs';
|
|
12
|
+
import type {
|
|
13
|
+
DropInitialValueOperator,
|
|
14
|
+
InitializedObservable,
|
|
15
|
+
Observable,
|
|
16
|
+
TakeOperatorObservable,
|
|
17
|
+
UpdateToken,
|
|
18
|
+
} from '../types/index.mjs';
|
|
19
|
+
import { withInitialValue } from './with-initial-value.mjs';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Takes only the first `n` emissions from the source observable, then completes.
|
|
23
|
+
* It completes in the same update as the `n`th emission, after passing that
|
|
24
|
+
* value on. The initial value is passed on and not counted.
|
|
25
|
+
*
|
|
26
|
+
* @template A - The type of values from the source
|
|
27
|
+
* @param n - The number of values to take
|
|
28
|
+
* @returns An operator that takes the first n emissions
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* // Timeline:
|
|
33
|
+
* //
|
|
34
|
+
* // num$ 1 2 3 4 (ignored)
|
|
35
|
+
* // taken$ 1 2 3|(completes)
|
|
36
|
+
* //
|
|
37
|
+
* // Explanation:
|
|
38
|
+
* // - take emits only the first n values, completing with the nth
|
|
39
|
+
* // - Subsequent emissions from the source are ignored
|
|
40
|
+
*
|
|
41
|
+
* const num$ = source<number>();
|
|
42
|
+
*
|
|
43
|
+
* const taken$ = num$.pipe(take(3));
|
|
44
|
+
*
|
|
45
|
+
* const valueHistory: number[] = [];
|
|
46
|
+
*
|
|
47
|
+
* taken$.subscribe((x) => {
|
|
48
|
+
* valueHistory.push(x);
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* num$.next(1);
|
|
52
|
+
*
|
|
53
|
+
* num$.next(2);
|
|
54
|
+
*
|
|
55
|
+
* num$.next(3);
|
|
56
|
+
*
|
|
57
|
+
* assert.deepStrictEqual(valueHistory, [1, 2, 3]);
|
|
58
|
+
*
|
|
59
|
+
* assert.isTrue(taken$.isCompleted);
|
|
60
|
+
*
|
|
61
|
+
* num$.next(4); // ignored (already completed)
|
|
62
|
+
*
|
|
63
|
+
* assert.deepStrictEqual(valueHistory, [1, 2, 3]);
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export const take =
|
|
67
|
+
<A,>(n: PositiveSafeIntWithSmallInt): DropInitialValueOperator<A, A> =>
|
|
68
|
+
(parentObservable) =>
|
|
69
|
+
new TakeObservableClass(parentObservable, n);
|
|
70
|
+
|
|
71
|
+
/* implementation */
|
|
72
|
+
|
|
73
|
+
class TakeObservableClass<A>
|
|
74
|
+
extends SyncChildObservableClass<A, readonly [A]>
|
|
75
|
+
implements TakeOperatorObservable<A>
|
|
76
|
+
{
|
|
77
|
+
readonly #n: PositiveSafeIntWithSmallInt;
|
|
78
|
+
#mut_count: number;
|
|
79
|
+
|
|
80
|
+
constructor(parentObservable: Observable<A>, n: PositiveSafeIntWithSmallInt) {
|
|
81
|
+
super({
|
|
82
|
+
parents: [parentObservable],
|
|
83
|
+
initialValue: parentObservable.getSnapshot(),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
this.#mut_count = 0;
|
|
87
|
+
|
|
88
|
+
this.#n = n;
|
|
89
|
+
|
|
90
|
+
this.attach();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
94
|
+
const par = this.parents[0];
|
|
95
|
+
|
|
96
|
+
const sn = par.getSnapshot();
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
this.#mut_count >= this.#n ||
|
|
100
|
+
par.updateToken !== updateToken ||
|
|
101
|
+
Optional.isNone(sn)
|
|
102
|
+
) {
|
|
103
|
+
return; // skip update
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.#mut_count += 1;
|
|
107
|
+
|
|
108
|
+
this.setNext(sn.value, updateToken);
|
|
109
|
+
|
|
110
|
+
if (this.#mut_count >= this.#n) {
|
|
111
|
+
this.completeAfterPropagation();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (import.meta.vitest !== undefined) {
|
|
117
|
+
test('type test', () => {
|
|
118
|
+
expect(1).toBe(1); // dummy
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
{
|
|
122
|
+
const s: Observable<number> = source<number>();
|
|
123
|
+
|
|
124
|
+
const _d1 = s.pipe(take(3));
|
|
125
|
+
|
|
126
|
+
expectType<typeof _d1, Observable<number>>('=');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
{
|
|
130
|
+
const s = source<number>();
|
|
131
|
+
|
|
132
|
+
const m: InitializedObservable<number> = s.pipe(withInitialValue(0));
|
|
133
|
+
|
|
134
|
+
const _d = m.pipe(take(3));
|
|
135
|
+
|
|
136
|
+
expectType<typeof _d, Observable<number>>('=');
|
|
137
|
+
}
|
|
138
|
+
}
|