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,462 @@
|
|
|
1
|
+
// cspell:ignore unstub
|
|
2
|
+
import { expectType } from 'ts-data-forge';
|
|
3
|
+
import type { Mock } from 'vitest';
|
|
4
|
+
import { toNode } from '../class/index.mjs';
|
|
5
|
+
import { source } from '../create/index.mjs';
|
|
6
|
+
import { map } from '../operators/index.mjs';
|
|
7
|
+
import type { Scope } from '../types/index.mjs';
|
|
8
|
+
import {
|
|
9
|
+
createScope,
|
|
10
|
+
getCurrentScope,
|
|
11
|
+
onScopeDispose,
|
|
12
|
+
untracked,
|
|
13
|
+
} from './scope.mjs';
|
|
14
|
+
|
|
15
|
+
describe(createScope, () => {
|
|
16
|
+
test('types', () => {
|
|
17
|
+
expectType<ReturnType<typeof createScope>, Scope>('=');
|
|
18
|
+
|
|
19
|
+
expectType<ReturnType<typeof getCurrentScope>, Scope | undefined>('=');
|
|
20
|
+
|
|
21
|
+
expectType<ReturnType<typeof untracked<number>>, number>('=');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('run returns what fn returns and makes the scope current only while it runs', () => {
|
|
25
|
+
const scope = createScope();
|
|
26
|
+
|
|
27
|
+
assert.isUndefined(getCurrentScope());
|
|
28
|
+
|
|
29
|
+
const result = scope.run(() => {
|
|
30
|
+
assert.strictEqual(getCurrentScope(), scope);
|
|
31
|
+
|
|
32
|
+
return 42;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
assert.strictEqual(result, 42);
|
|
36
|
+
|
|
37
|
+
assert.isUndefined(getCurrentScope());
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('the outer scope is current again after a nested run, and after fn throws', () => {
|
|
41
|
+
const outer = createScope();
|
|
42
|
+
|
|
43
|
+
const inner = createScope(outer);
|
|
44
|
+
|
|
45
|
+
outer.run(() => {
|
|
46
|
+
inner.run(() => {
|
|
47
|
+
assert.strictEqual(getCurrentScope(), inner);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
assert.strictEqual(getCurrentScope(), outer);
|
|
51
|
+
|
|
52
|
+
assert.throws(() => {
|
|
53
|
+
// throws a SyntaxError
|
|
54
|
+
inner.run(() => JSON.parse('{'));
|
|
55
|
+
}, SyntaxError);
|
|
56
|
+
|
|
57
|
+
assert.strictEqual(getCurrentScope(), outer);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
assert.isUndefined(getCurrentScope());
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('an observable created inside run is disposed with the scope; a shared parent outside it is not', () => {
|
|
64
|
+
const store$ = source<number>();
|
|
65
|
+
|
|
66
|
+
const scope = createScope();
|
|
67
|
+
|
|
68
|
+
const doubled$ = scope.run(() => store$.pipe(map((x) => x * 2)));
|
|
69
|
+
|
|
70
|
+
const mut_history: number[] = [];
|
|
71
|
+
|
|
72
|
+
doubled$.subscribe((x) => {
|
|
73
|
+
mut_history.push(x);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
store$.next(1);
|
|
77
|
+
|
|
78
|
+
assert.deepStrictEqual(mut_history, [2]);
|
|
79
|
+
|
|
80
|
+
scope.dispose();
|
|
81
|
+
|
|
82
|
+
assert.isTrue(doubled$.isCompleted);
|
|
83
|
+
|
|
84
|
+
assert.isFalse(store$.isCompleted);
|
|
85
|
+
|
|
86
|
+
assert.isFalse(toNode(store$).hasChild);
|
|
87
|
+
|
|
88
|
+
store$.next(2);
|
|
89
|
+
|
|
90
|
+
assert.deepStrictEqual(mut_history, [2]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('an observable created outside any scope belongs to nothing', () => {
|
|
94
|
+
const store$ = source<number>();
|
|
95
|
+
|
|
96
|
+
const doubled$ = store$.pipe(map((x) => x * 2));
|
|
97
|
+
|
|
98
|
+
const scope = createScope();
|
|
99
|
+
|
|
100
|
+
scope.run(() => {
|
|
101
|
+
// a derived node created elsewhere is not adopted by being used here
|
|
102
|
+
doubled$.subscribe(() => {});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
scope.dispose();
|
|
106
|
+
|
|
107
|
+
assert.isFalse(doubled$.isCompleted);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('nodes go to the innermost scope', () => {
|
|
111
|
+
const store$ = source<number>();
|
|
112
|
+
|
|
113
|
+
const outer = createScope();
|
|
114
|
+
|
|
115
|
+
const inner = createScope(outer);
|
|
116
|
+
|
|
117
|
+
const [a$, b$] = outer.run(() => {
|
|
118
|
+
const a = store$.pipe(map((x) => x + 1));
|
|
119
|
+
|
|
120
|
+
const b = inner.run(() => store$.pipe(map((x) => x + 2)));
|
|
121
|
+
|
|
122
|
+
return [a, b] as const;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
inner.dispose();
|
|
126
|
+
|
|
127
|
+
assert.isFalse(a$.isCompleted);
|
|
128
|
+
|
|
129
|
+
assert.isTrue(b$.isCompleted);
|
|
130
|
+
|
|
131
|
+
outer.dispose();
|
|
132
|
+
|
|
133
|
+
assert.isTrue(a$.isCompleted);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('dispose runs child scopes, then cleanups in reverse, then owned nodes in reverse creation order', () => {
|
|
137
|
+
const store$ = source<number>();
|
|
138
|
+
|
|
139
|
+
const parent = createScope();
|
|
140
|
+
|
|
141
|
+
const child1 = createScope(parent);
|
|
142
|
+
|
|
143
|
+
const child2 = createScope(parent);
|
|
144
|
+
|
|
145
|
+
const mut_order: string[] = [];
|
|
146
|
+
|
|
147
|
+
child1.run(() => {
|
|
148
|
+
onScopeDispose(() => {
|
|
149
|
+
mut_order.push('child1');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
child2.run(() => {
|
|
154
|
+
onScopeDispose(() => {
|
|
155
|
+
mut_order.push('child2');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
parent.run(() => {
|
|
160
|
+
const first$ = store$.pipe(map((x) => x));
|
|
161
|
+
|
|
162
|
+
first$.subscribe(
|
|
163
|
+
() => {},
|
|
164
|
+
() => {
|
|
165
|
+
mut_order.push('node first');
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
onScopeDispose(() => {
|
|
170
|
+
mut_order.push('cleanup 1');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const second$ = store$.pipe(map((x) => x));
|
|
174
|
+
|
|
175
|
+
second$.subscribe(
|
|
176
|
+
() => {},
|
|
177
|
+
() => {
|
|
178
|
+
mut_order.push('node second');
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
onScopeDispose(() => {
|
|
183
|
+
mut_order.push('cleanup 2');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
parent.dispose();
|
|
188
|
+
|
|
189
|
+
assert.deepStrictEqual(mut_order, [
|
|
190
|
+
'child2',
|
|
191
|
+
'child1',
|
|
192
|
+
'cleanup 2',
|
|
193
|
+
'cleanup 1',
|
|
194
|
+
'node second',
|
|
195
|
+
'node first',
|
|
196
|
+
]);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('dispose is idempotent', () => {
|
|
200
|
+
const scope = createScope();
|
|
201
|
+
|
|
202
|
+
const mut_calls: number[] = [];
|
|
203
|
+
|
|
204
|
+
scope.run(() => {
|
|
205
|
+
onScopeDispose(() => {
|
|
206
|
+
mut_calls.push(1);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
scope.dispose();
|
|
211
|
+
|
|
212
|
+
scope.dispose();
|
|
213
|
+
|
|
214
|
+
assert.deepStrictEqual(mut_calls, [1]);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('a disposed child scope leaves its parent, so the parent does not dispose it again', () => {
|
|
218
|
+
const parent = createScope();
|
|
219
|
+
|
|
220
|
+
const child = createScope(parent);
|
|
221
|
+
|
|
222
|
+
const mut_calls: string[] = [];
|
|
223
|
+
|
|
224
|
+
child.run(() => {
|
|
225
|
+
onScopeDispose(() => {
|
|
226
|
+
mut_calls.push('child');
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
child.dispose();
|
|
231
|
+
|
|
232
|
+
parent.dispose();
|
|
233
|
+
|
|
234
|
+
assert.deepStrictEqual(mut_calls, ['child']);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('a node that completes on its own is released by its scope', () => {
|
|
238
|
+
const store$ = source<number>();
|
|
239
|
+
|
|
240
|
+
const scope = createScope();
|
|
241
|
+
|
|
242
|
+
const mut_completions: number[] = [];
|
|
243
|
+
|
|
244
|
+
const doubled$ = scope.run(() => store$.pipe(map((x) => x * 2)));
|
|
245
|
+
|
|
246
|
+
doubled$.subscribe(
|
|
247
|
+
() => {},
|
|
248
|
+
() => {
|
|
249
|
+
mut_completions.push(1);
|
|
250
|
+
},
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
doubled$.complete();
|
|
254
|
+
|
|
255
|
+
scope.dispose();
|
|
256
|
+
|
|
257
|
+
assert.deepStrictEqual(mut_completions, [1]);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('after many child scopes are disposed on their own, the parent disposes the rest once, newest first', () => {
|
|
261
|
+
const parent = createScope();
|
|
262
|
+
|
|
263
|
+
const mut_order: number[] = [];
|
|
264
|
+
|
|
265
|
+
const children = Array.from({ length: 100 }, (_, i) => {
|
|
266
|
+
const child = createScope(parent);
|
|
267
|
+
|
|
268
|
+
child.run(() => {
|
|
269
|
+
onScopeDispose(() => {
|
|
270
|
+
mut_order.push(i);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return child;
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Every child whose index is not a multiple of 3 goes on its own: 66 of
|
|
278
|
+
// them, enough for the parent to rebuild its list of children.
|
|
279
|
+
for (const [i, child] of children.entries()) {
|
|
280
|
+
if (i % 3 !== 0) {
|
|
281
|
+
child.dispose();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const disposedAlone = mut_order.length;
|
|
286
|
+
|
|
287
|
+
parent.dispose();
|
|
288
|
+
|
|
289
|
+
assert.strictEqual(disposedAlone, 66);
|
|
290
|
+
|
|
291
|
+
assert.deepStrictEqual(
|
|
292
|
+
mut_order.slice(disposedAlone),
|
|
293
|
+
Array.from({ length: 34 }, (_, k) => (33 - k) * 3),
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test('after many owned nodes complete on their own, the scope disposes the rest once, newest first', () => {
|
|
298
|
+
const store$ = source<number>();
|
|
299
|
+
|
|
300
|
+
const scope = createScope();
|
|
301
|
+
|
|
302
|
+
const mut_completions: number[] = [];
|
|
303
|
+
|
|
304
|
+
const nodes = scope.run(() =>
|
|
305
|
+
Array.from({ length: 100 }, (_, i) => {
|
|
306
|
+
const node$ = store$.pipe(map((x) => x + i));
|
|
307
|
+
|
|
308
|
+
node$.subscribe(
|
|
309
|
+
() => {},
|
|
310
|
+
() => {
|
|
311
|
+
mut_completions.push(i);
|
|
312
|
+
},
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
return node$;
|
|
316
|
+
}),
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
for (const [i, node$] of nodes.entries()) {
|
|
320
|
+
if (i % 3 !== 0) {
|
|
321
|
+
node$.complete();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const completedAlone = mut_completions.length;
|
|
326
|
+
|
|
327
|
+
scope.dispose();
|
|
328
|
+
|
|
329
|
+
assert.strictEqual(completedAlone, 66);
|
|
330
|
+
|
|
331
|
+
assert.deepStrictEqual(
|
|
332
|
+
mut_completions.slice(completedAlone),
|
|
333
|
+
Array.from({ length: 34 }, (_, k) => (33 - k) * 3),
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test('nodes created inside untracked belong to no scope', () => {
|
|
338
|
+
const store$ = source<number>();
|
|
339
|
+
|
|
340
|
+
const scope = createScope();
|
|
341
|
+
|
|
342
|
+
const [owned$, free$] = scope.run(() => {
|
|
343
|
+
const owned = store$.pipe(map((x) => x));
|
|
344
|
+
|
|
345
|
+
const free = untracked(() => {
|
|
346
|
+
assert.isUndefined(getCurrentScope());
|
|
347
|
+
|
|
348
|
+
return store$.pipe(map((x) => x));
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
assert.strictEqual(getCurrentScope(), scope);
|
|
352
|
+
|
|
353
|
+
return [owned, free] as const;
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
scope.dispose();
|
|
357
|
+
|
|
358
|
+
assert.isTrue(owned$.isCompleted);
|
|
359
|
+
|
|
360
|
+
assert.isFalse(free$.isCompleted);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test('a node created after await belongs to no scope', async () => {
|
|
364
|
+
const store$ = source<number>();
|
|
365
|
+
|
|
366
|
+
const scope = createScope();
|
|
367
|
+
|
|
368
|
+
const promise = scope.run(async () => {
|
|
369
|
+
await Promise.resolve();
|
|
370
|
+
|
|
371
|
+
assert.isUndefined(getCurrentScope());
|
|
372
|
+
|
|
373
|
+
return store$.pipe(map((x) => x));
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const late$ = await promise;
|
|
377
|
+
|
|
378
|
+
scope.dispose();
|
|
379
|
+
|
|
380
|
+
assert.isFalse(late$.isCompleted);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test('run on a disposed scope warns, still runs fn, and owns nothing it creates', () => {
|
|
384
|
+
const warn = stubConsoleWarn();
|
|
385
|
+
|
|
386
|
+
const store$ = source<number>();
|
|
387
|
+
|
|
388
|
+
const scope = createScope();
|
|
389
|
+
|
|
390
|
+
scope.dispose();
|
|
391
|
+
|
|
392
|
+
const free$ = scope.run(() => {
|
|
393
|
+
assert.isUndefined(getCurrentScope());
|
|
394
|
+
|
|
395
|
+
return store$.pipe(map((x) => x));
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
assert.isFalse(free$.isCompleted);
|
|
399
|
+
|
|
400
|
+
expect(warn).toHaveBeenCalledExactlyOnceWith(
|
|
401
|
+
expect.stringContaining('disposed scope'),
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
vi.unstubAllGlobals();
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test('onScopeDispose outside any scope warns and registers nothing', () => {
|
|
408
|
+
const warn = stubConsoleWarn();
|
|
409
|
+
|
|
410
|
+
onScopeDispose(() => {});
|
|
411
|
+
|
|
412
|
+
expect(warn).toHaveBeenCalledExactlyOnceWith(
|
|
413
|
+
expect.stringContaining('outside any scope'),
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
vi.unstubAllGlobals();
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test('a scope created under a disposed parent is born disposed', () => {
|
|
420
|
+
const warn = stubConsoleWarn();
|
|
421
|
+
|
|
422
|
+
const parent = createScope();
|
|
423
|
+
|
|
424
|
+
parent.dispose();
|
|
425
|
+
|
|
426
|
+
const child = createScope(parent);
|
|
427
|
+
|
|
428
|
+
expect(warn).toHaveBeenCalledExactlyOnceWith(
|
|
429
|
+
expect.stringContaining('disposed parent'),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
child.run(() => {
|
|
433
|
+
assert.isUndefined(getCurrentScope());
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
expect(warn).toHaveBeenCalledTimes(2);
|
|
437
|
+
|
|
438
|
+
vi.unstubAllGlobals();
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test('disposing a parent disposes nodes of its child scopes', () => {
|
|
442
|
+
const store$ = source<number>();
|
|
443
|
+
|
|
444
|
+
const parent = createScope();
|
|
445
|
+
|
|
446
|
+
const child = createScope(parent);
|
|
447
|
+
|
|
448
|
+
const owned$ = child.run(() => store$.pipe(map((x) => x)));
|
|
449
|
+
|
|
450
|
+
parent.dispose();
|
|
451
|
+
|
|
452
|
+
assert.isTrue(owned$.isCompleted);
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
const stubConsoleWarn = (): Mock => {
|
|
457
|
+
const warn = vi.fn();
|
|
458
|
+
|
|
459
|
+
vi.stubGlobal('console', { ...console, warn });
|
|
460
|
+
|
|
461
|
+
return warn;
|
|
462
|
+
};
|
package/src/core/types/id.mts
CHANGED
package/src/core/types/index.mts
CHANGED
|
@@ -210,6 +210,8 @@ export type PairwiseOperatorObservable<A> = SyncChildObservable<
|
|
|
210
210
|
readonly [A]
|
|
211
211
|
>;
|
|
212
212
|
|
|
213
|
+
export type TakeOperatorObservable<A> = SyncChildObservable<A, readonly [A]>;
|
|
214
|
+
|
|
213
215
|
export type TakeWhileOperatorObservable<A> = SyncChildObservable<
|
|
214
216
|
A,
|
|
215
217
|
readonly [A]
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
expectType,
|
|
3
|
+
hasKey,
|
|
4
|
+
isRecord,
|
|
5
|
+
type Optional,
|
|
6
|
+
type Some,
|
|
7
|
+
} from 'ts-data-forge';
|
|
2
8
|
import type { FixedLengthTuple, StrictOmit } from 'ts-type-forge';
|
|
3
|
-
import
|
|
9
|
+
import { observableBrand } from '../utils/index.mjs';
|
|
10
|
+
import type { UpdateToken } from './id.mjs';
|
|
4
11
|
import type { ObservableKind } from './observable-kind.mjs';
|
|
5
12
|
import type { NonEmptyUnknownList, Subscription } from './types.mjs';
|
|
6
13
|
|
|
@@ -19,42 +26,37 @@ import type { NonEmptyUnknownList, Subscription } from './types.mjs';
|
|
|
19
26
|
*/
|
|
20
27
|
|
|
21
28
|
type CreateObservableType<A, Kind extends ObservableKind> = Readonly<{
|
|
22
|
-
|
|
29
|
+
[observableBrand]: true;
|
|
30
|
+
|
|
23
31
|
kind: Kind;
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
/**
|
|
34
|
+
* One more than the deepest observable this one is derived from; 0 for a
|
|
35
|
+
* root. An update reaches observables in ascending depth, so every parent
|
|
36
|
+
* has emitted before its child reads it.
|
|
37
|
+
*/
|
|
26
38
|
depth: Kind extends 'root' ? 0 : number;
|
|
27
|
-
addChild: <B>(child: ChildObservable<B>) => void;
|
|
28
|
-
deleteChild: <B>(child: ChildObservable<B>) => void;
|
|
29
39
|
|
|
30
|
-
// state
|
|
31
40
|
getSnapshot: () => Optional<A>;
|
|
32
41
|
isCompleted: boolean;
|
|
33
|
-
updateToken: UpdateToken;
|
|
34
|
-
hasSubscriber: boolean;
|
|
35
|
-
hasChild: boolean;
|
|
36
|
-
hasActiveChild: () => boolean;
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* `autoComplete`. A `source` or `createState` and everything derived from
|
|
44
|
-
* it alone are never completed this way, so completing a derived observable
|
|
45
|
-
* cannot end a state that something else may still read (#2133).
|
|
44
|
+
* The token of the update in which this observable last emitted. A child's
|
|
45
|
+
* `tryUpdate` receives the token of the update now propagating; a parent
|
|
46
|
+
* whose token differs did not emit in it. The token is opaque: compare it,
|
|
47
|
+
* store it, or pass it on to `setNext`.
|
|
46
48
|
*/
|
|
47
|
-
|
|
49
|
+
updateToken: UpdateToken;
|
|
48
50
|
|
|
49
|
-
tryUpdate: (updateToken: UpdateToken) => void;
|
|
50
|
-
tryComplete: () => void;
|
|
51
51
|
complete: () => void;
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* Completes this observable and takes it out of the graph,
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
54
|
+
* Completes this observable and takes it out of the graph, for whoever owns
|
|
55
|
+
* it (a scope) to release it. Neither this nor `complete()` reaches upstream:
|
|
56
|
+
* a child completing never completes its parents, which may be shared
|
|
57
|
+
* (#2133). Its descendants complete once none of their parents is live.
|
|
58
|
+
* Unlike `complete()`, it passes on nothing the observable still holds: a
|
|
59
|
+
* `debounce` or `audit` drops its pending value instead of emitting it.
|
|
58
60
|
*/
|
|
59
61
|
dispose: () => void;
|
|
60
62
|
subscribe: (onNext: (v: A) => void, onComplete?: () => void) => Subscription;
|
|
@@ -97,15 +99,6 @@ namespace ObservableTypeConverter {
|
|
|
97
99
|
Readonly<{
|
|
98
100
|
parents: Wrap<P>;
|
|
99
101
|
}>;
|
|
100
|
-
|
|
101
|
-
export type ToManager<
|
|
102
|
-
A,
|
|
103
|
-
O extends CreateObservableType<A, 'async child' | 'root'>,
|
|
104
|
-
> = O &
|
|
105
|
-
Readonly<{
|
|
106
|
-
addDescendant: <B>(child: ChildObservable<B>) => void;
|
|
107
|
-
deleteDescendant: <B>(child: ChildObservable<B>) => void;
|
|
108
|
-
}>;
|
|
109
102
|
}
|
|
110
103
|
|
|
111
104
|
export type SyncChildObservable<
|
|
@@ -125,9 +118,10 @@ export type InitializedSyncChildObservable<
|
|
|
125
118
|
export type AsyncChildObservable<
|
|
126
119
|
A,
|
|
127
120
|
P extends NonEmptyUnknownList = NonEmptyUnknownList,
|
|
128
|
-
> = ObservableTypeConverter.
|
|
121
|
+
> = ObservableTypeConverter.ToChild<
|
|
129
122
|
A,
|
|
130
|
-
|
|
123
|
+
CreateObservableType<A, 'async child'>,
|
|
124
|
+
P
|
|
131
125
|
>;
|
|
132
126
|
|
|
133
127
|
export type InitializedAsyncChildObservable<
|
|
@@ -140,10 +134,7 @@ export type ChildObservable<
|
|
|
140
134
|
P extends NonEmptyUnknownList = NonEmptyUnknownList,
|
|
141
135
|
> = AsyncChildObservable<A, P> | SyncChildObservable<A, P>;
|
|
142
136
|
|
|
143
|
-
export type RootObservable<A> =
|
|
144
|
-
A,
|
|
145
|
-
CreateObservableType<A, 'root'>
|
|
146
|
-
>;
|
|
137
|
+
export type RootObservable<A> = CreateObservableType<A, 'root'>;
|
|
147
138
|
|
|
148
139
|
export type InitializedRootObservable<A> =
|
|
149
140
|
ObservableTypeConverter.ToInitialized<A, RootObservable<A>>;
|
|
@@ -180,6 +171,27 @@ export type Operator<A, B> =
|
|
|
180
171
|
| DropInitialValueOperator<A, B>
|
|
181
172
|
| KeepInitialValueOperator<A, B>;
|
|
182
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Tells an observable made by synstate from any other value, by the brand
|
|
176
|
+
* every observable carries. Checking for a `subscribe` method instead would
|
|
177
|
+
* also accept plain values that happen to have one.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
*
|
|
181
|
+
* ```ts
|
|
182
|
+
* const [count$] = createState(0);
|
|
183
|
+
*
|
|
184
|
+
* assert.isTrue(isObservable(count$));
|
|
185
|
+
*
|
|
186
|
+
* // A `subscribe` method alone does not make an observable.
|
|
187
|
+
* assert.isFalse(isObservable({ subscribe: () => {} }));
|
|
188
|
+
*
|
|
189
|
+
* assert.isFalse(isObservable(0));
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
export const isObservable = (value: unknown): value is Observable<unknown> =>
|
|
193
|
+
isRecord(value) && hasKey(value, observableBrand);
|
|
194
|
+
|
|
183
195
|
export const isManagerObservable = <A,>(
|
|
184
196
|
obs: Observable<A>,
|
|
185
197
|
): obs is ManagerObservable<A> => obs.kind !== 'sync child';
|
|
@@ -210,9 +222,32 @@ export type WrapInitialized<A extends readonly unknown[]> = Readonly<{
|
|
|
210
222
|
// type GetDestTypeOfOperator<Op extends Operator<unknown, unknown>> =
|
|
211
223
|
// Op extends Operator<unknown, infer B> ? B : never;
|
|
212
224
|
|
|
213
|
-
|
|
225
|
+
// Only the members meant for users are public; the rest live on the
|
|
226
|
+
// internal node types in `core/class` (#2117).
|
|
227
|
+
expectType<
|
|
228
|
+
keyof Observable<unknown>,
|
|
229
|
+
| typeof observableBrand
|
|
230
|
+
| 'kind'
|
|
231
|
+
| 'depth'
|
|
232
|
+
| 'getSnapshot'
|
|
233
|
+
| 'isCompleted'
|
|
234
|
+
| 'updateToken'
|
|
235
|
+
| 'complete'
|
|
236
|
+
| 'dispose'
|
|
237
|
+
| 'subscribe'
|
|
238
|
+
| 'pipe'
|
|
239
|
+
>('=');
|
|
214
240
|
|
|
215
|
-
expectType<
|
|
241
|
+
expectType<
|
|
242
|
+
keyof ChildObservable<unknown>,
|
|
243
|
+
keyof Observable<unknown> | 'parents'
|
|
244
|
+
>('=');
|
|
245
|
+
|
|
246
|
+
// Everything but the brand is not enough to pass for an observable.
|
|
247
|
+
expectType<
|
|
248
|
+
StrictOmit<RootObservable<unknown>, typeof observableBrand>,
|
|
249
|
+
Observable<unknown>
|
|
250
|
+
>('!<=');
|
|
216
251
|
|
|
217
252
|
expectType<RootObservable<unknown>['depth'], 0>('=');
|
|
218
253
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A unit of ownership: the observables created while {@link Scope.run} is
|
|
3
|
+
* running belong to the scope, and {@link Scope.dispose} releases them all at
|
|
4
|
+
* once. Create one with `createScope`.
|
|
5
|
+
*
|
|
6
|
+
* For authors of libraries built on synstate (a UI binding mounts and unmounts
|
|
7
|
+
* by scope); application code that defines its state at module level does not
|
|
8
|
+
* need it.
|
|
9
|
+
*/
|
|
10
|
+
export type Scope = Readonly<{
|
|
11
|
+
/**
|
|
12
|
+
* Calls `body` with this scope as the current one and returns what it
|
|
13
|
+
* returns. Only what `body` creates synchronously joins the scope: by the time
|
|
14
|
+
* code after an `await` or in a callback runs, `run` has already returned.
|
|
15
|
+
*
|
|
16
|
+
* On a disposed scope it warns, still calls `body`, and nothing created there
|
|
17
|
+
* belongs to any scope.
|
|
18
|
+
*/
|
|
19
|
+
run: <R>(body: () => R) => R;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Disposes the child scopes (newest first), calls the functions registered
|
|
23
|
+
* with `onScopeDispose` (newest first), disposes the owned observables
|
|
24
|
+
* (newest first) and leaves the parent scope. Calling it again does nothing.
|
|
25
|
+
*/
|
|
26
|
+
dispose: () => void;
|
|
27
|
+
}>;
|