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
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { Optional } from 'ts-data-forge';
|
|
2
|
+
import { createState } from '../../utils/index.mjs';
|
|
3
|
+
import { combine } from '../combine/index.mjs';
|
|
4
|
+
import { counter, source } from '../create/index.mjs';
|
|
5
|
+
import { debounce, map } from '../operators/index.mjs';
|
|
6
|
+
import { toNode } from './observable-node.mjs';
|
|
7
|
+
|
|
8
|
+
describe('completing a derived observable', () => {
|
|
9
|
+
test('does not complete an upstream state (#2133)', () => {
|
|
10
|
+
const [count$, setCount] = createState(0);
|
|
11
|
+
|
|
12
|
+
const doubled$ = count$.pipe(map((x) => x * 2));
|
|
13
|
+
|
|
14
|
+
doubled$.complete();
|
|
15
|
+
|
|
16
|
+
assert.isFalse(count$.isCompleted);
|
|
17
|
+
|
|
18
|
+
setCount(5);
|
|
19
|
+
|
|
20
|
+
assert.deepStrictEqual(count$.getSnapshot(), Optional.some(5));
|
|
21
|
+
|
|
22
|
+
const mut_got: number[] = [];
|
|
23
|
+
|
|
24
|
+
count$.subscribe((v) => {
|
|
25
|
+
mut_got.push(v);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
setCount(6);
|
|
29
|
+
|
|
30
|
+
assert.deepStrictEqual(mut_got, [5, 6]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('does not complete a shared derived observable', () => {
|
|
34
|
+
const s = source<number>(0);
|
|
35
|
+
|
|
36
|
+
const shared$ = s.pipe(map((x) => x + 1));
|
|
37
|
+
|
|
38
|
+
const local$ = shared$.pipe(map((x) => x * 2));
|
|
39
|
+
|
|
40
|
+
local$.complete();
|
|
41
|
+
|
|
42
|
+
assert.isFalse(shared$.isCompleted);
|
|
43
|
+
|
|
44
|
+
assert.isFalse(s.isCompleted);
|
|
45
|
+
|
|
46
|
+
const mut_got: number[] = [];
|
|
47
|
+
|
|
48
|
+
shared$.subscribe((v) => {
|
|
49
|
+
mut_got.push(v);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
s.next(1);
|
|
53
|
+
|
|
54
|
+
assert.deepStrictEqual(mut_got, [1, 2]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('does not complete an unused root with a teardown', () => {
|
|
58
|
+
const counter$ = counter(10, { startManually: true });
|
|
59
|
+
|
|
60
|
+
const derived$ = counter$.pipe(map((x) => x * 2));
|
|
61
|
+
|
|
62
|
+
const leaf$ = derived$.pipe(map((x) => x + 1));
|
|
63
|
+
|
|
64
|
+
leaf$.complete();
|
|
65
|
+
|
|
66
|
+
assert.isFalse(derived$.isCompleted);
|
|
67
|
+
|
|
68
|
+
assert.isFalse(counter$.isCompleted);
|
|
69
|
+
|
|
70
|
+
counter$.complete();
|
|
71
|
+
|
|
72
|
+
assert.isTrue(derived$.isCompleted);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('does not complete an unused operator with a teardown', () => {
|
|
76
|
+
const s = source<number>(0);
|
|
77
|
+
|
|
78
|
+
const debounced$ = s.pipe(debounce(10));
|
|
79
|
+
|
|
80
|
+
const leaf$ = debounced$.pipe(map((x) => x));
|
|
81
|
+
|
|
82
|
+
leaf$.complete();
|
|
83
|
+
|
|
84
|
+
assert.isFalse(debounced$.isCompleted);
|
|
85
|
+
|
|
86
|
+
assert.isFalse(s.isCompleted);
|
|
87
|
+
|
|
88
|
+
debounced$.complete();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('does not stop a root with a teardown that is still used', () => {
|
|
92
|
+
const counter$ = counter(10, { startManually: true });
|
|
93
|
+
|
|
94
|
+
counter$.subscribe(() => {});
|
|
95
|
+
|
|
96
|
+
counter$.pipe(map((x) => x)).complete();
|
|
97
|
+
|
|
98
|
+
assert.isFalse(counter$.isCompleted);
|
|
99
|
+
|
|
100
|
+
counter$.complete();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('dispose', () => {
|
|
105
|
+
test('completes the observable without completing its parents', () => {
|
|
106
|
+
const counter$ = counter(10, { startManually: true });
|
|
107
|
+
|
|
108
|
+
const derived$ = counter$.pipe(map((x) => x * 2));
|
|
109
|
+
|
|
110
|
+
derived$.dispose();
|
|
111
|
+
|
|
112
|
+
assert.isTrue(derived$.isCompleted);
|
|
113
|
+
|
|
114
|
+
assert.isFalse(counter$.isCompleted);
|
|
115
|
+
|
|
116
|
+
counter$.complete();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('takes the observable out of the propagation graph', () => {
|
|
120
|
+
const s = source<number>(0);
|
|
121
|
+
|
|
122
|
+
s.subscribe(() => {});
|
|
123
|
+
|
|
124
|
+
let mut_calls = 0;
|
|
125
|
+
|
|
126
|
+
const derived$ = s.pipe(
|
|
127
|
+
map((x) => {
|
|
128
|
+
mut_calls += 1;
|
|
129
|
+
|
|
130
|
+
return x;
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
derived$.dispose();
|
|
135
|
+
|
|
136
|
+
assert.isFalse(toNode(s).hasChild);
|
|
137
|
+
|
|
138
|
+
mut_calls = 0;
|
|
139
|
+
|
|
140
|
+
s.next(1);
|
|
141
|
+
|
|
142
|
+
expect(mut_calls).toBe(0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('completes the descendants that have no other live parent', () => {
|
|
146
|
+
const s = source<number>(0);
|
|
147
|
+
|
|
148
|
+
const t = source<number>(0);
|
|
149
|
+
|
|
150
|
+
const derived$ = s.pipe(map((x) => x));
|
|
151
|
+
|
|
152
|
+
const onlyChild$ = derived$.pipe(map((x) => x));
|
|
153
|
+
|
|
154
|
+
const s2 = t.pipe(map((x) => x));
|
|
155
|
+
|
|
156
|
+
const mut_got: string[] = [];
|
|
157
|
+
|
|
158
|
+
onlyChild$.subscribe(
|
|
159
|
+
() => {},
|
|
160
|
+
() => {
|
|
161
|
+
mut_got.push('onlyChild completed');
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
derived$.dispose();
|
|
166
|
+
|
|
167
|
+
assert.isTrue(onlyChild$.isCompleted);
|
|
168
|
+
|
|
169
|
+
assert.isFalse(s2.isCompleted);
|
|
170
|
+
|
|
171
|
+
assert.deepStrictEqual(mut_got, ['onlyChild completed']);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('runs the teardown of the observable it is called on', async () => {
|
|
175
|
+
const s = source<number>(0);
|
|
176
|
+
|
|
177
|
+
const debounced$ = s.pipe(debounce(10));
|
|
178
|
+
|
|
179
|
+
const mut_got: number[] = [];
|
|
180
|
+
|
|
181
|
+
debounced$.subscribe((v) => {
|
|
182
|
+
mut_got.push(v);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
s.next(1);
|
|
186
|
+
|
|
187
|
+
debounced$.dispose();
|
|
188
|
+
|
|
189
|
+
await new Promise((resolve) => {
|
|
190
|
+
setTimeout(resolve, 50);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Only the initial value: the pending 1 was cancelled.
|
|
194
|
+
assert.deepStrictEqual(mut_got, [0]);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('a child created under completed parents (#2248)', () => {
|
|
199
|
+
test('completes at creation and keeps the value computed from the final parent value', () => {
|
|
200
|
+
const s = source<number>(0);
|
|
201
|
+
|
|
202
|
+
s.next(5);
|
|
203
|
+
|
|
204
|
+
s.complete();
|
|
205
|
+
|
|
206
|
+
const d = s.pipe(map((x) => x * 100));
|
|
207
|
+
|
|
208
|
+
assert.isTrue(d.isCompleted);
|
|
209
|
+
|
|
210
|
+
assert.deepStrictEqual(d.getSnapshot(), Optional.some(500));
|
|
211
|
+
|
|
212
|
+
const mut_got: string[] = [];
|
|
213
|
+
|
|
214
|
+
d.subscribe(
|
|
215
|
+
(v) => {
|
|
216
|
+
mut_got.push(`next ${v}`);
|
|
217
|
+
},
|
|
218
|
+
() => {
|
|
219
|
+
mut_got.push('complete');
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// A completed observable emits its final value, then completes.
|
|
224
|
+
assert.deepStrictEqual(mut_got, ['next 500', 'complete']);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('is not registered to the completed parent', () => {
|
|
228
|
+
const s = source<number>(0);
|
|
229
|
+
|
|
230
|
+
s.complete();
|
|
231
|
+
|
|
232
|
+
s.pipe(map((x) => x + 1));
|
|
233
|
+
|
|
234
|
+
assert.isFalse(toNode(s).hasChild);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('completes an async child at creation', () => {
|
|
238
|
+
const s = source<number>(0);
|
|
239
|
+
|
|
240
|
+
s.complete();
|
|
241
|
+
|
|
242
|
+
const d = s.pipe(debounce(10));
|
|
243
|
+
|
|
244
|
+
assert.isTrue(d.isCompleted);
|
|
245
|
+
|
|
246
|
+
assert.isFalse(toNode(s).hasChild);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test('does not complete a child that still has a live parent', () => {
|
|
250
|
+
const live$ = source<number>(1);
|
|
251
|
+
|
|
252
|
+
const completed$ = source<number>(2);
|
|
253
|
+
|
|
254
|
+
completed$.complete();
|
|
255
|
+
|
|
256
|
+
const sum$ = combine([live$, completed$]).pipe(map(([a, b]) => a + b));
|
|
257
|
+
|
|
258
|
+
assert.isFalse(sum$.isCompleted);
|
|
259
|
+
|
|
260
|
+
live$.next(10);
|
|
261
|
+
|
|
262
|
+
assert.deepStrictEqual(sum$.getSnapshot(), Optional.some(12));
|
|
263
|
+
});
|
|
264
|
+
});
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
// @sumi-expect-error-file classes/no-class
|
|
2
|
+
// @sumi-expect-error-file banned-syntax/no-this
|
|
3
|
+
// @sumi-expect-error-file functions/prefer-arrow-function
|
|
4
|
+
// @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
|
|
5
|
+
import { Arr, type Optional, type Some } from 'ts-data-forge';
|
|
6
|
+
import { unreachable } from 'ts-std-forge';
|
|
7
|
+
import type {
|
|
8
|
+
AsyncChildObservable,
|
|
9
|
+
InitializedSyncChildObservable,
|
|
10
|
+
NonEmptyUnknownList,
|
|
11
|
+
SyncChildObservable,
|
|
12
|
+
UpdateToken,
|
|
13
|
+
Wrap,
|
|
14
|
+
} from '../types/index.mjs';
|
|
15
|
+
import {
|
|
16
|
+
AsyncChildObservableClass,
|
|
17
|
+
SyncChildObservableClass,
|
|
18
|
+
} from './child-observable-class.mjs';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The tools a sync-child factory (`map`, `filter`, `zip`, …) receives: what
|
|
22
|
+
* only the observable being created may do to itself.
|
|
23
|
+
*/
|
|
24
|
+
export type SyncChildObservableTools<A> = Readonly<{
|
|
25
|
+
/** Emits `nextValue` under `updateToken` and notifies subscribers. */
|
|
26
|
+
setNext: (nextValue: A, updateToken: UpdateToken) => void;
|
|
27
|
+
|
|
28
|
+
getSnapshot: () => Optional<A>;
|
|
29
|
+
|
|
30
|
+
/** Completes this observable (leaf teardown included). */
|
|
31
|
+
complete: () => void;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Completes this observable once the update now propagating has reached
|
|
35
|
+
* every descendant, so that they see the value just passed on first.
|
|
36
|
+
*/
|
|
37
|
+
completeAfterPropagation: () => void;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Registers a teardown (clear timers, unsubscribe inner observables, …) to
|
|
41
|
+
* run when this observable completes or is disposed, in the order
|
|
42
|
+
* registered. Called from `init`, it lets the teardown close over what
|
|
43
|
+
* `init` creates. Registered once the observable has completed, the
|
|
44
|
+
* teardown runs at once.
|
|
45
|
+
*/
|
|
46
|
+
onComplete: (teardown: () => void) => void;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The tools an async-child factory (`debounce`, `switchMap`, …) receives.
|
|
51
|
+
* Async children additionally start their own update propagation.
|
|
52
|
+
*/
|
|
53
|
+
export type AsyncChildObservableTools<A> = Readonly<{
|
|
54
|
+
/** Emits `nextValue` and propagates the update to all descendants. */
|
|
55
|
+
startUpdate: (nextValue: A) => void;
|
|
56
|
+
}> &
|
|
57
|
+
SyncChildObservableTools<A>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* What a sync child's `init` may return instead of `tryUpdate` alone: the
|
|
61
|
+
* hooks the factory calls, which read the state `init` creates.
|
|
62
|
+
*/
|
|
63
|
+
export type SyncChildObservableHooks = Readonly<{
|
|
64
|
+
/** Called with the token of every update that reaches this observable. */
|
|
65
|
+
tryUpdate: (updateToken: UpdateToken) => void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whether this observable can emit nothing more although some parent is
|
|
69
|
+
* still live, asked whenever a parent completes. `true` completes it then,
|
|
70
|
+
* as all parents completing does (`zip` once a completed source's queue is
|
|
71
|
+
* empty).
|
|
72
|
+
*/
|
|
73
|
+
canEmitNoMore?: () => boolean;
|
|
74
|
+
}>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* What an async child's `init` may return instead of `tryUpdate` alone. An
|
|
78
|
+
* async child can also hold a value it has not passed on yet (`debounce`'s
|
|
79
|
+
* pending value), which completing should not lose.
|
|
80
|
+
*/
|
|
81
|
+
export type AsyncChildObservableHooks = SyncChildObservableHooks &
|
|
82
|
+
Readonly<{
|
|
83
|
+
/**
|
|
84
|
+
* Run at the beginning of `complete()`, before the teardowns, and never
|
|
85
|
+
* by `dispose()`: passes on what this observable still holds through
|
|
86
|
+
* `startUpdate`, so that its subscribers see it before they see the
|
|
87
|
+
* completion. A disposed observable has no one left to tell.
|
|
88
|
+
*/
|
|
89
|
+
flush?: () => void;
|
|
90
|
+
}>;
|
|
91
|
+
|
|
92
|
+
type ChildObservableConfigBase<P extends NonEmptyUnknownList> = Readonly<{
|
|
93
|
+
parents: Wrap<P>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Defaults to one more than the deepest parent. An update reaches
|
|
97
|
+
* observables in ascending depth, so an observable `tryUpdate` reads with
|
|
98
|
+
* `getSnapshot` without being its child (`withCurrentValueFrom`'s second
|
|
99
|
+
* source) has to be shallower than this one too, or this one reads its value
|
|
100
|
+
* from before the update: pass `1 + Math.max(...)` over both then.
|
|
101
|
+
*/
|
|
102
|
+
depth?: number;
|
|
103
|
+
}>;
|
|
104
|
+
|
|
105
|
+
export type ChildObservableConfig<
|
|
106
|
+
A,
|
|
107
|
+
P extends NonEmptyUnknownList,
|
|
108
|
+
> = ChildObservableConfigBase<P> &
|
|
109
|
+
Readonly<{
|
|
110
|
+
initialValue: Optional<A>;
|
|
111
|
+
}>;
|
|
112
|
+
|
|
113
|
+
export type InitializedChildObservableConfig<
|
|
114
|
+
A,
|
|
115
|
+
P extends NonEmptyUnknownList,
|
|
116
|
+
> = ChildObservableConfigBase<P> &
|
|
117
|
+
Readonly<{
|
|
118
|
+
initialValue: Some<A>;
|
|
119
|
+
}>;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Creates a sync-child observable: one derived from `parents` that emits, if
|
|
123
|
+
* at all, in the same update as they do. `map`, `filter`, `combine` and the
|
|
124
|
+
* other synchronous operators are built with it.
|
|
125
|
+
*
|
|
126
|
+
* `init` runs during construction and receives the tools. It returns
|
|
127
|
+
* `tryUpdate`, which is called with the token of every update that reaches
|
|
128
|
+
* this observable — a parent that did not emit in that update has a different
|
|
129
|
+
* `updateToken`; to emit, call `setNext` with the token received — or an
|
|
130
|
+
* object holding `tryUpdate` and the other hooks
|
|
131
|
+
* ({@link SyncChildObservableHooks}). State the hooks share can live inside
|
|
132
|
+
* `init`, next to the teardown it registers with the `onComplete` tool.
|
|
133
|
+
* `init` may complete the observable; it is then never registered to its
|
|
134
|
+
* parents.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
*
|
|
138
|
+
* ```ts
|
|
139
|
+
* // A custom operator, written as `map((x) => x * 2)` is.
|
|
140
|
+
* const double = (src: Observable<number>): Observable<number> =>
|
|
141
|
+
* createSyncChildObservable(
|
|
142
|
+
* { parents: [src], initialValue: Optional.none },
|
|
143
|
+
* ({ setNext }) =>
|
|
144
|
+
* (updateToken) => {
|
|
145
|
+
* const sn = src.getSnapshot();
|
|
146
|
+
*
|
|
147
|
+
* if (src.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
148
|
+
* return; // `src` did not emit in this update
|
|
149
|
+
* }
|
|
150
|
+
*
|
|
151
|
+
* setNext(sn.value * 2, updateToken);
|
|
152
|
+
* },
|
|
153
|
+
* );
|
|
154
|
+
*
|
|
155
|
+
* const num$ = source<number>();
|
|
156
|
+
*
|
|
157
|
+
* const pair$ = combine([num$, double(num$)]);
|
|
158
|
+
*
|
|
159
|
+
* const valueHistory: FixedLengthTuple<2, number>[] = [];
|
|
160
|
+
*
|
|
161
|
+
* pair$.subscribe((pair) => {
|
|
162
|
+
* valueHistory.push(pair);
|
|
163
|
+
* });
|
|
164
|
+
*
|
|
165
|
+
* num$.next(1);
|
|
166
|
+
*
|
|
167
|
+
* num$.next(2);
|
|
168
|
+
*
|
|
169
|
+
* // `double` joins the same update as `num$`, so `combine` sees no [2, 2].
|
|
170
|
+
* assert.deepStrictEqual(valueHistory, [
|
|
171
|
+
* [1, 2],
|
|
172
|
+
* [2, 4],
|
|
173
|
+
* ]);
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export const createSyncChildObservable = <A, P extends NonEmptyUnknownList>(
|
|
177
|
+
config: ChildObservableConfig<A, P>,
|
|
178
|
+
init: (
|
|
179
|
+
tools: SyncChildObservableTools<A>,
|
|
180
|
+
) => ((updateToken: UpdateToken) => void) | SyncChildObservableHooks,
|
|
181
|
+
): SyncChildObservable<A, P> =>
|
|
182
|
+
new CustomSyncChildObservableClass(config, init);
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Creates a sync-child observable that always holds a value.
|
|
186
|
+
*
|
|
187
|
+
* Runtime-wise this is exactly `createSyncChildObservable`; only the types of
|
|
188
|
+
* `getSnapshot` and `pipe` are narrowed.
|
|
189
|
+
*/
|
|
190
|
+
export const createInitializedSyncChildObservable = <
|
|
191
|
+
A,
|
|
192
|
+
P extends NonEmptyUnknownList,
|
|
193
|
+
>(
|
|
194
|
+
config: InitializedChildObservableConfig<A, P>,
|
|
195
|
+
init: (
|
|
196
|
+
tools: SyncChildObservableTools<A>,
|
|
197
|
+
) => ((updateToken: UpdateToken) => void) | SyncChildObservableHooks,
|
|
198
|
+
): InitializedSyncChildObservable<A, P> =>
|
|
199
|
+
// `getSnapshot` always returns `Some` here: `initialValue` is a `Some` and
|
|
200
|
+
// `setNext` only ever stores a `Some`.
|
|
201
|
+
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
202
|
+
new CustomSyncChildObservableClass<A, P>(
|
|
203
|
+
config,
|
|
204
|
+
init,
|
|
205
|
+
) as unknown as InitializedSyncChildObservable<A, P>;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Creates an async-child observable: one derived from `parents` that may emit
|
|
209
|
+
* later, in an update of its own, through `startUpdate` (`debounce`,
|
|
210
|
+
* `switchMap`). It may also emit synchronously with `setNext`, as a sync child
|
|
211
|
+
* does. See {@link createSyncChildObservable} for the `init` contract; an
|
|
212
|
+
* async child's `init` may also return a `flush` hook
|
|
213
|
+
* ({@link AsyncChildObservableHooks}).
|
|
214
|
+
*/
|
|
215
|
+
export const createAsyncChildObservable = <A, P extends NonEmptyUnknownList>(
|
|
216
|
+
config: ChildObservableConfig<A, P>,
|
|
217
|
+
init: (
|
|
218
|
+
tools: AsyncChildObservableTools<A>,
|
|
219
|
+
) => ((updateToken: UpdateToken) => void) | AsyncChildObservableHooks,
|
|
220
|
+
): AsyncChildObservable<A, P> =>
|
|
221
|
+
new CustomAsyncChildObservableClass(config, init);
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* An `init` for a child that never receives a parent update; reaching its
|
|
225
|
+
* `tryUpdate` is the invariant breaking (Sumi D-48).
|
|
226
|
+
*/
|
|
227
|
+
export const createTryUpdateNotImplemented = (): ((
|
|
228
|
+
updateToken: UpdateToken,
|
|
229
|
+
) => void) => tryUpdateNotImplemented;
|
|
230
|
+
|
|
231
|
+
const tryUpdateNotImplemented = (): void => {
|
|
232
|
+
unreachable();
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/* implementation */
|
|
236
|
+
|
|
237
|
+
class CustomSyncChildObservableClass<
|
|
238
|
+
A,
|
|
239
|
+
const P extends NonEmptyUnknownList,
|
|
240
|
+
> extends SyncChildObservableClass<A, P> {
|
|
241
|
+
#mut_teardowns: readonly (() => void)[] | undefined;
|
|
242
|
+
readonly #hooks: SyncChildObservableHooks;
|
|
243
|
+
|
|
244
|
+
constructor(
|
|
245
|
+
config: ChildObservableConfig<A, P>,
|
|
246
|
+
init: (
|
|
247
|
+
tools: SyncChildObservableTools<A>,
|
|
248
|
+
) => ((updateToken: UpdateToken) => void) | SyncChildObservableHooks,
|
|
249
|
+
) {
|
|
250
|
+
super(config);
|
|
251
|
+
|
|
252
|
+
this.#mut_teardowns = undefined;
|
|
253
|
+
|
|
254
|
+
this.#hooks = toHooks(
|
|
255
|
+
init({
|
|
256
|
+
setNext: (nextValue, updateToken) => {
|
|
257
|
+
this.setNext(nextValue, updateToken);
|
|
258
|
+
},
|
|
259
|
+
getSnapshot: () => this.getSnapshot(),
|
|
260
|
+
complete: () => {
|
|
261
|
+
this.complete();
|
|
262
|
+
},
|
|
263
|
+
completeAfterPropagation: () => {
|
|
264
|
+
this.completeAfterPropagation();
|
|
265
|
+
},
|
|
266
|
+
onComplete: (teardown) => {
|
|
267
|
+
if (this.isCompleted) {
|
|
268
|
+
teardown();
|
|
269
|
+
} else {
|
|
270
|
+
this.#mut_teardowns = Arr.toPushed(
|
|
271
|
+
this.#mut_teardowns ?? [],
|
|
272
|
+
teardown,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
}),
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
this.attach();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
283
|
+
this.#hooks.tryUpdate(updateToken);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
override canEmitNoMore(): boolean {
|
|
287
|
+
return this.#hooks.canEmitNoMore?.() === true;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
protected override teardown(): void {
|
|
291
|
+
for (const teardown of this.#mut_teardowns ?? []) {
|
|
292
|
+
teardown();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
class CustomAsyncChildObservableClass<
|
|
298
|
+
A,
|
|
299
|
+
const P extends NonEmptyUnknownList,
|
|
300
|
+
> extends AsyncChildObservableClass<A, P> {
|
|
301
|
+
#mut_teardowns: readonly (() => void)[] | undefined;
|
|
302
|
+
readonly #hooks: AsyncChildObservableHooks;
|
|
303
|
+
|
|
304
|
+
constructor(
|
|
305
|
+
config: ChildObservableConfig<A, P>,
|
|
306
|
+
init: (
|
|
307
|
+
tools: AsyncChildObservableTools<A>,
|
|
308
|
+
) => ((updateToken: UpdateToken) => void) | AsyncChildObservableHooks,
|
|
309
|
+
) {
|
|
310
|
+
super(config);
|
|
311
|
+
|
|
312
|
+
this.#mut_teardowns = undefined;
|
|
313
|
+
|
|
314
|
+
this.#hooks = toHooks(
|
|
315
|
+
init({
|
|
316
|
+
setNext: (nextValue, updateToken) => {
|
|
317
|
+
this.setNext(nextValue, updateToken);
|
|
318
|
+
},
|
|
319
|
+
startUpdate: (nextValue) => {
|
|
320
|
+
this.startUpdate(nextValue);
|
|
321
|
+
},
|
|
322
|
+
getSnapshot: () => this.getSnapshot(),
|
|
323
|
+
complete: () => {
|
|
324
|
+
this.complete();
|
|
325
|
+
},
|
|
326
|
+
completeAfterPropagation: () => {
|
|
327
|
+
this.completeAfterPropagation();
|
|
328
|
+
},
|
|
329
|
+
onComplete: (teardown) => {
|
|
330
|
+
if (this.isCompleted) {
|
|
331
|
+
teardown();
|
|
332
|
+
} else {
|
|
333
|
+
this.#mut_teardowns = Arr.toPushed(
|
|
334
|
+
this.#mut_teardowns ?? [],
|
|
335
|
+
teardown,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
}),
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
this.attach();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
346
|
+
this.#hooks.tryUpdate(updateToken);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
override canEmitNoMore(): boolean {
|
|
350
|
+
return this.#hooks.canEmitNoMore?.() === true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
protected override flush(): void {
|
|
354
|
+
this.#hooks.flush?.();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
protected override teardown(): void {
|
|
358
|
+
for (const teardown of this.#mut_teardowns ?? []) {
|
|
359
|
+
teardown();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** `init` may return `tryUpdate` alone, or it with the other hooks. */
|
|
365
|
+
const toHooks = (
|
|
366
|
+
returned: ((updateToken: UpdateToken) => void) | AsyncChildObservableHooks,
|
|
367
|
+
): AsyncChildObservableHooks =>
|
|
368
|
+
typeof returned === 'function'
|
|
369
|
+
? ({ tryUpdate: returned } as const)
|
|
370
|
+
: returned;
|