synstate 3.4.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 +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/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/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 +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/{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/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/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
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { source } from '../create/index.mjs';
|
|
2
|
+
import { createScope } from '../scope/index.mjs';
|
|
3
|
+
import { map } from './map.mjs';
|
|
4
|
+
import { mergeMap } from './merge-map.mjs';
|
|
5
|
+
import { switchMap } from './switch-map.mjs';
|
|
6
|
+
|
|
7
|
+
describe('switchMap releases only the inner observables its projection created (#2335)', () => {
|
|
8
|
+
test('a shared inner survives a switch', () => {
|
|
9
|
+
const user$ = source<'a' | 'b'>();
|
|
10
|
+
|
|
11
|
+
const a$ = source<number>();
|
|
12
|
+
|
|
13
|
+
const b$ = source<number>();
|
|
14
|
+
|
|
15
|
+
const current$ = user$.pipe(switchMap((u) => (u === 'a' ? a$ : b$)));
|
|
16
|
+
|
|
17
|
+
const mut_current: number[] = [];
|
|
18
|
+
|
|
19
|
+
current$.subscribe((v) => {
|
|
20
|
+
mut_current.push(v);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const mut_others: number[] = [];
|
|
24
|
+
|
|
25
|
+
a$.subscribe((v) => {
|
|
26
|
+
mut_others.push(v);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
user$.next('a');
|
|
30
|
+
|
|
31
|
+
a$.next(1);
|
|
32
|
+
|
|
33
|
+
user$.next('b');
|
|
34
|
+
|
|
35
|
+
assert.isFalse(a$.isCompleted);
|
|
36
|
+
|
|
37
|
+
a$.next(2);
|
|
38
|
+
|
|
39
|
+
b$.next(3);
|
|
40
|
+
|
|
41
|
+
assert.deepStrictEqual(mut_others, [1, 2]);
|
|
42
|
+
|
|
43
|
+
assert.deepStrictEqual(mut_current, [1, 3]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('a shared inner survives the completion of switchMap', () => {
|
|
47
|
+
const user$ = source<'a'>();
|
|
48
|
+
|
|
49
|
+
const a$ = source<number>();
|
|
50
|
+
|
|
51
|
+
const current$ = user$.pipe(switchMap(() => a$));
|
|
52
|
+
|
|
53
|
+
current$.subscribe(() => {});
|
|
54
|
+
|
|
55
|
+
user$.next('a');
|
|
56
|
+
|
|
57
|
+
current$.complete();
|
|
58
|
+
|
|
59
|
+
assert.isFalse(a$.isCompleted);
|
|
60
|
+
|
|
61
|
+
const mut_others: number[] = [];
|
|
62
|
+
|
|
63
|
+
a$.subscribe((v) => {
|
|
64
|
+
mut_others.push(v);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
a$.next(1);
|
|
68
|
+
|
|
69
|
+
assert.deepStrictEqual(mut_others, [1]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('a node derived in the projection from a shared observable is released, and the shared one survives', () => {
|
|
73
|
+
const user$ = source<number>();
|
|
74
|
+
|
|
75
|
+
const shared$ = source<number>();
|
|
76
|
+
|
|
77
|
+
const mut_derived: { isCompleted: boolean }[] = [];
|
|
78
|
+
|
|
79
|
+
const current$ = user$.pipe(
|
|
80
|
+
switchMap((u) => {
|
|
81
|
+
const derived$ = shared$.pipe(map((x) => x + u));
|
|
82
|
+
|
|
83
|
+
mut_derived.push(derived$);
|
|
84
|
+
|
|
85
|
+
return derived$;
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
current$.subscribe(() => {});
|
|
90
|
+
|
|
91
|
+
user$.next(1);
|
|
92
|
+
|
|
93
|
+
user$.next(2);
|
|
94
|
+
|
|
95
|
+
assert.deepStrictEqual(
|
|
96
|
+
mut_derived.map((o) => o.isCompleted),
|
|
97
|
+
[true, false],
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
assert.isFalse(shared$.isCompleted);
|
|
101
|
+
|
|
102
|
+
current$.complete();
|
|
103
|
+
|
|
104
|
+
assert.deepStrictEqual(
|
|
105
|
+
mut_derived.map((o) => o.isCompleted),
|
|
106
|
+
[true, true],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
assert.isFalse(shared$.isCompleted);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('every node the projection created is released on a switch, not only the returned one', () => {
|
|
113
|
+
const user$ = source<number>();
|
|
114
|
+
|
|
115
|
+
const mut_created: { isCompleted: boolean }[] = [];
|
|
116
|
+
|
|
117
|
+
const current$ = user$.pipe(
|
|
118
|
+
switchMap((u) => {
|
|
119
|
+
const inner$ = source<number>();
|
|
120
|
+
|
|
121
|
+
const mapped$ = inner$.pipe(map((x) => x + u));
|
|
122
|
+
|
|
123
|
+
mut_created.push(inner$, mapped$);
|
|
124
|
+
|
|
125
|
+
return mapped$;
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
current$.subscribe(() => {});
|
|
130
|
+
|
|
131
|
+
user$.next(1);
|
|
132
|
+
|
|
133
|
+
user$.next(2);
|
|
134
|
+
|
|
135
|
+
assert.deepStrictEqual(
|
|
136
|
+
mut_created.map((o) => o.isCompleted),
|
|
137
|
+
[true, true, false, false],
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
current$.complete();
|
|
141
|
+
|
|
142
|
+
assert.deepStrictEqual(
|
|
143
|
+
mut_created.map((o) => o.isCompleted),
|
|
144
|
+
[true, true, true, true],
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('the scope switchMap and mergeMap were created in (#2335)', () => {
|
|
150
|
+
test('disposing it releases what their projections created, and the shared inners survive', () => {
|
|
151
|
+
const user$ = source<number>();
|
|
152
|
+
|
|
153
|
+
const shared$ = source<number>();
|
|
154
|
+
|
|
155
|
+
const mut_created: { isCompleted: boolean }[] = [];
|
|
156
|
+
|
|
157
|
+
const scope = createScope();
|
|
158
|
+
|
|
159
|
+
scope.run(() => {
|
|
160
|
+
user$
|
|
161
|
+
.pipe(
|
|
162
|
+
switchMap(() => {
|
|
163
|
+
const inner$ = source<number>();
|
|
164
|
+
|
|
165
|
+
mut_created.push(inner$);
|
|
166
|
+
|
|
167
|
+
return inner$;
|
|
168
|
+
}),
|
|
169
|
+
)
|
|
170
|
+
.subscribe(() => {});
|
|
171
|
+
|
|
172
|
+
user$.pipe(mergeMap(() => shared$)).subscribe(() => {});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
user$.next(1);
|
|
176
|
+
|
|
177
|
+
assert.deepStrictEqual(
|
|
178
|
+
mut_created.map((o) => o.isCompleted),
|
|
179
|
+
[false],
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
scope.dispose();
|
|
183
|
+
|
|
184
|
+
assert.deepStrictEqual(
|
|
185
|
+
mut_created.map((o) => o.isCompleted),
|
|
186
|
+
[true],
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
assert.isFalse(shared$.isCompleted);
|
|
190
|
+
|
|
191
|
+
assert.isFalse(user$.isCompleted);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('mergeMap releases only the inner observables its projection created (#2335)', () => {
|
|
196
|
+
test('a shared inner survives the completion of mergeMap', () => {
|
|
197
|
+
const user$ = source<'a' | 'b'>();
|
|
198
|
+
|
|
199
|
+
const a$ = source<number>();
|
|
200
|
+
|
|
201
|
+
const b$ = source<number>();
|
|
202
|
+
|
|
203
|
+
const merged$ = user$.pipe(mergeMap((u) => (u === 'a' ? a$ : b$)));
|
|
204
|
+
|
|
205
|
+
const mut_merged: number[] = [];
|
|
206
|
+
|
|
207
|
+
merged$.subscribe((v) => {
|
|
208
|
+
mut_merged.push(v);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
user$.next('a');
|
|
212
|
+
|
|
213
|
+
user$.next('b');
|
|
214
|
+
|
|
215
|
+
a$.next(1);
|
|
216
|
+
|
|
217
|
+
b$.next(2);
|
|
218
|
+
|
|
219
|
+
assert.deepStrictEqual(mut_merged, [1, 2]);
|
|
220
|
+
|
|
221
|
+
merged$.complete();
|
|
222
|
+
|
|
223
|
+
assert.isFalse(a$.isCompleted);
|
|
224
|
+
|
|
225
|
+
assert.isFalse(b$.isCompleted);
|
|
226
|
+
|
|
227
|
+
const mut_others: number[] = [];
|
|
228
|
+
|
|
229
|
+
a$.subscribe((v) => {
|
|
230
|
+
mut_others.push(v);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
a$.next(3);
|
|
234
|
+
|
|
235
|
+
// A new subscriber receives the current value first.
|
|
236
|
+
assert.deepStrictEqual(mut_others, [1, 3]);
|
|
237
|
+
|
|
238
|
+
assert.deepStrictEqual(mut_merged, [1, 2]);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('every node the projection created is released on completion', () => {
|
|
242
|
+
const user$ = source<number>();
|
|
243
|
+
|
|
244
|
+
const shared$ = source<number>();
|
|
245
|
+
|
|
246
|
+
const mut_created: { isCompleted: boolean }[] = [];
|
|
247
|
+
|
|
248
|
+
const merged$ = user$.pipe(
|
|
249
|
+
mergeMap((u) => {
|
|
250
|
+
const inner$ = source<number>();
|
|
251
|
+
|
|
252
|
+
const derived$ = shared$.pipe(map((x) => x + u));
|
|
253
|
+
|
|
254
|
+
mut_created.push(inner$, derived$);
|
|
255
|
+
|
|
256
|
+
return inner$;
|
|
257
|
+
}),
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
merged$.subscribe(() => {});
|
|
261
|
+
|
|
262
|
+
user$.next(1);
|
|
263
|
+
|
|
264
|
+
user$.next(2);
|
|
265
|
+
|
|
266
|
+
assert.deepStrictEqual(
|
|
267
|
+
mut_created.map((o) => o.isCompleted),
|
|
268
|
+
[false, false, false, false],
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
merged$.complete();
|
|
272
|
+
|
|
273
|
+
assert.deepStrictEqual(
|
|
274
|
+
mut_created.map((o) => o.isCompleted),
|
|
275
|
+
[true, true, true, true],
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
assert.isFalse(shared$.isCompleted);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
@@ -1,11 +1,19 @@
|
|
|
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, SafeUint, asSafeUint, expectType } from 'ts-data-forge';
|
|
2
|
-
import {
|
|
9
|
+
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
3
10
|
import { source } from '../create/index.mjs';
|
|
4
11
|
import type {
|
|
5
12
|
InitializedObservable,
|
|
6
13
|
KeepInitialValueOperator,
|
|
7
14
|
MapOperatorObservable,
|
|
8
15
|
Observable,
|
|
16
|
+
UpdateToken,
|
|
9
17
|
} from '../types/index.mjs';
|
|
10
18
|
import { withInitialValue } from './with-initial-value.mjs';
|
|
11
19
|
|
|
@@ -52,44 +60,53 @@ export const map = <A, B>(
|
|
|
52
60
|
): KeepInitialValueOperator<A, B> =>
|
|
53
61
|
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
54
62
|
((parentObservable) =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
new MapObservableClass(
|
|
64
|
+
parentObservable,
|
|
65
|
+
mapFn,
|
|
66
|
+
)) as KeepInitialValueOperator<A, B>;
|
|
59
67
|
|
|
60
68
|
/* implementation */
|
|
61
69
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
class MapObservableClass<A, B>
|
|
71
|
+
extends SyncChildObservableClass<B, readonly [A]>
|
|
72
|
+
implements MapOperatorObservable<A, B>
|
|
73
|
+
{
|
|
74
|
+
readonly #mapFn: (x: A, index: SafeUint | -1) => B;
|
|
75
|
+
#mut_index: SafeUint | -1;
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
parentObservable: Observable<A>,
|
|
79
|
+
mapFn: (x: A, index: SafeUint | -1) => B,
|
|
80
|
+
) {
|
|
81
|
+
super({
|
|
68
82
|
parents: [parentObservable],
|
|
69
83
|
initialValue: Optional.map(parentObservable.getSnapshot(), (x) =>
|
|
70
84
|
mapFn(x, -1),
|
|
71
85
|
),
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
this.#mut_index = -1;
|
|
89
|
+
|
|
90
|
+
this.#mapFn = mapFn;
|
|
91
|
+
|
|
92
|
+
this.attach();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
96
|
+
const par = this.parents[0];
|
|
97
|
+
|
|
98
|
+
const sn = par.getSnapshot();
|
|
99
|
+
|
|
100
|
+
if (par.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
101
|
+
return; // skip update
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.#mut_index =
|
|
105
|
+
this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
|
|
106
|
+
|
|
107
|
+
this.setNext(this.#mapFn(sn.value, this.#mut_index), updateToken);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
93
110
|
|
|
94
111
|
if (import.meta.vitest !== undefined) {
|
|
95
112
|
test('type test', () => {
|
|
@@ -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 { Arr, 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
|
MergeMapOperatorObservable,
|
|
6
14
|
Observable,
|
|
15
|
+
Scope,
|
|
7
16
|
Subscription,
|
|
17
|
+
UpdateToken,
|
|
8
18
|
} from '../types/index.mjs';
|
|
9
19
|
|
|
10
20
|
/**
|
|
11
21
|
* Projects each source value to an observable and merges all inner observables.
|
|
12
22
|
* Unlike `switchMap`, does not cancel previous inner observables.
|
|
13
23
|
*
|
|
24
|
+
* Each projection runs in a scope of its own (see `createScope`): what it
|
|
25
|
+
* creates is disposed when `mergeMap` completes. An observable it only
|
|
26
|
+
* returns — one created outside the projection and shared with others — is
|
|
27
|
+
* 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
|
|
@@ -104,7 +119,7 @@ export const mergeMap =
|
|
|
104
119
|
mapToObservable: (curr: A) => Observable<B>,
|
|
105
120
|
): DropInitialValueOperator<A, B> =>
|
|
106
121
|
(parentObservable) =>
|
|
107
|
-
|
|
122
|
+
new MergeMapObservableClass(parentObservable, mapToObservable);
|
|
108
123
|
|
|
109
124
|
/**
|
|
110
125
|
* Alias for `mergeMap`.
|
|
@@ -112,48 +127,71 @@ export const mergeMap =
|
|
|
112
127
|
*/
|
|
113
128
|
export const flatMap = mergeMap;
|
|
114
129
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
class MergeMapObservableClass<A, B>
|
|
131
|
+
extends AsyncChildObservableClass<B, readonly [A]>
|
|
132
|
+
implements MergeMapOperatorObservable<A, B>
|
|
133
|
+
{
|
|
134
|
+
readonly #mapToObservable: (curr: A) => Observable<B>;
|
|
135
|
+
|
|
136
|
+
/** See `SwitchMapObservableClass`. */
|
|
137
|
+
readonly #ownerScope: Scope | undefined;
|
|
120
138
|
|
|
121
|
-
|
|
139
|
+
#mut_innerScopes: readonly Scope[];
|
|
140
|
+
#mut_subscriptions: readonly Subscription[];
|
|
122
141
|
|
|
123
|
-
|
|
124
|
-
|
|
142
|
+
constructor(
|
|
143
|
+
parentObservable: Observable<A>,
|
|
144
|
+
mapToObservable: (curr: A) => Observable<B>,
|
|
145
|
+
) {
|
|
146
|
+
super({
|
|
125
147
|
parents: [parentObservable],
|
|
126
148
|
initialValue: Optional.none,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
this.#mapToObservable = mapToObservable;
|
|
152
|
+
|
|
153
|
+
this.#ownerScope = getCurrentScope();
|
|
154
|
+
|
|
155
|
+
this.#mut_innerScopes = [];
|
|
156
|
+
|
|
157
|
+
this.#mut_subscriptions = [];
|
|
158
|
+
|
|
159
|
+
this.attach();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
163
|
+
const par = this.parents[0];
|
|
164
|
+
|
|
165
|
+
const sn = par.getSnapshot();
|
|
166
|
+
|
|
167
|
+
if (par.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
168
|
+
return; // skip update
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const innerScope = createScope(this.#ownerScope);
|
|
172
|
+
|
|
173
|
+
this.#mut_innerScopes = Arr.toPushed(this.#mut_innerScopes, innerScope);
|
|
174
|
+
|
|
175
|
+
const observable = innerScope.run(() => this.#mapToObservable(sn.value));
|
|
176
|
+
|
|
177
|
+
const subscription = observable.subscribe((curr) => {
|
|
178
|
+
this.startUpdate(curr);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
this.#mut_subscriptions = Arr.toPushed(
|
|
182
|
+
this.#mut_subscriptions,
|
|
183
|
+
subscription,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Releases the inner observables, and only those this operator made. */
|
|
188
|
+
protected override teardown(): void {
|
|
189
|
+
for (const s of this.#mut_subscriptions) {
|
|
190
|
+
s.unsubscribe();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const scope of this.#mut_innerScopes) {
|
|
194
|
+
scope.dispose();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -1,10 +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
9
|
import type { FixedLengthTuple } from 'ts-type-forge';
|
|
3
|
-
import {
|
|
10
|
+
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
4
11
|
import type {
|
|
5
12
|
DropInitialValueOperator,
|
|
6
13
|
Observable,
|
|
7
14
|
PairwiseOperatorObservable,
|
|
15
|
+
UpdateToken,
|
|
8
16
|
} from '../types/index.mjs';
|
|
9
17
|
|
|
10
18
|
/**
|
|
@@ -68,41 +76,43 @@ export const pairwise = <A,>(): DropInitialValueOperator<
|
|
|
68
76
|
const f = <A,>(
|
|
69
77
|
parentObservable: Observable<A>,
|
|
70
78
|
): Observable<FixedLengthTuple<2, A>> =>
|
|
71
|
-
|
|
79
|
+
new PairwiseObservableClass(parentObservable);
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
class PairwiseObservableClass<A>
|
|
82
|
+
extends SyncChildObservableClass<FixedLengthTuple<2, A>, readonly [A]>
|
|
83
|
+
implements PairwiseOperatorObservable<A>
|
|
84
|
+
{
|
|
85
|
+
#mut_previousValue: Optional<A>;
|
|
86
|
+
|
|
87
|
+
constructor(parentObservable: Observable<A>) {
|
|
88
|
+
super({
|
|
78
89
|
parents: [parentObservable],
|
|
79
90
|
initialValue: Optional.none,
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// parentObservable.snapshot has value
|
|
94
|
+
// if parentObservable is InitializedObservable
|
|
95
|
+
this.#mut_previousValue = parentObservable.getSnapshot();
|
|
96
|
+
|
|
97
|
+
this.attach();
|
|
98
|
+
}
|
|
85
99
|
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
101
|
+
const par = this.parents[0];
|
|
88
102
|
|
|
89
|
-
|
|
90
|
-
parentObservable.updateToken !== updateToken ||
|
|
91
|
-
Optional.isNone(sn)
|
|
92
|
-
) {
|
|
93
|
-
return; // skip update
|
|
94
|
-
}
|
|
103
|
+
const sn = par.getSnapshot();
|
|
95
104
|
|
|
96
|
-
|
|
105
|
+
if (par.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
106
|
+
return; // skip update
|
|
107
|
+
}
|
|
97
108
|
|
|
98
|
-
|
|
109
|
+
const prev = this.#mut_previousValue;
|
|
99
110
|
|
|
100
|
-
|
|
101
|
-
|
|
111
|
+
// NOTE: Must update before setNext, otherwise Optional.isNone(prev) remains true when tryUpdate is called consecutively
|
|
112
|
+
this.#mut_previousValue = sn;
|
|
102
113
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
);
|
|
114
|
+
if (Optional.isSome(prev)) {
|
|
115
|
+
this.setNext([prev.value, sn.value], updateToken);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|