synstate 3.3.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/dist/core/authoring.d.mts +7 -0
- package/dist/core/authoring.d.mts.map +1 -0
- package/dist/core/authoring.mjs +7 -0
- package/dist/core/authoring.mjs.map +1 -0
- package/dist/core/class/child-observable-class.d.mts +93 -0
- package/dist/core/class/child-observable-class.d.mts.map +1 -0
- package/dist/core/class/child-observable-class.mjs +248 -0
- package/dist/core/class/child-observable-class.mjs.map +1 -0
- package/dist/core/class/create-child-observable.d.mts +159 -0
- package/dist/core/class/create-child-observable.d.mts.map +1 -0
- package/dist/core/class/create-child-observable.mjs +183 -0
- package/dist/core/class/create-child-observable.mjs.map +1 -0
- package/dist/core/class/create-root-observable.d.mts +37 -0
- package/dist/core/class/create-root-observable.d.mts.map +1 -0
- package/dist/core/class/create-root-observable.mjs +52 -0
- package/dist/core/class/create-root-observable.mjs.map +1 -0
- package/dist/core/class/index.d.mts +9 -0
- package/dist/core/class/index.d.mts.map +1 -0
- package/dist/core/class/index.mjs +9 -0
- package/dist/core/class/index.mjs.map +1 -0
- package/dist/core/class/manager-observable-class.d.mts +26 -0
- package/dist/core/class/manager-observable-class.d.mts.map +1 -0
- package/dist/core/class/manager-observable-class.mjs +116 -0
- package/dist/core/class/manager-observable-class.mjs.map +1 -0
- package/dist/core/class/observable-base-class.d.mts +97 -0
- package/dist/core/class/observable-base-class.d.mts.map +1 -0
- package/dist/core/class/observable-base-class.mjs +219 -0
- package/dist/core/class/observable-base-class.mjs.map +1 -0
- package/dist/core/class/observable-node.d.mts +40 -0
- package/dist/core/class/observable-node.d.mts.map +1 -0
- package/dist/core/class/observable-node.mjs +10 -0
- package/dist/core/class/observable-node.mjs.map +1 -0
- package/dist/core/class/propagation-state.d.mts +24 -0
- package/dist/core/class/propagation-state.d.mts.map +1 -0
- package/dist/core/class/propagation-state.mjs +66 -0
- package/dist/core/class/propagation-state.mjs.map +1 -0
- package/dist/core/class/root-observable-class.d.mts +23 -0
- package/dist/core/class/root-observable-class.d.mts.map +1 -0
- package/dist/core/class/root-observable-class.mjs +58 -0
- package/dist/core/class/root-observable-class.mjs.map +1 -0
- package/dist/core/combine/combine.d.mts.map +1 -1
- package/dist/core/combine/combine.mjs +25 -14
- package/dist/core/combine/combine.mjs.map +1 -1
- package/dist/core/combine/merge.d.mts +0 -3
- package/dist/core/combine/merge.d.mts.map +1 -1
- package/dist/core/combine/merge.mjs +24 -15
- package/dist/core/combine/merge.mjs.map +1 -1
- package/dist/core/combine/zip.d.mts +4 -2
- package/dist/core/combine/zip.d.mts.map +1 -1
- package/dist/core/combine/zip.mjs +54 -18
- package/dist/core/combine/zip.mjs.map +1 -1
- package/dist/core/create/counter.d.mts +9 -3
- package/dist/core/create/counter.d.mts.map +1 -1
- package/dist/core/create/counter.mjs +69 -35
- package/dist/core/create/counter.mjs.map +1 -1
- package/dist/core/create/from-abortable-promise.d.mts.map +1 -1
- package/dist/core/create/from-abortable-promise.mjs +26 -18
- package/dist/core/create/from-abortable-promise.mjs.map +1 -1
- package/dist/core/create/from-promise.d.mts.map +1 -1
- package/dist/core/create/from-promise.mjs +30 -21
- package/dist/core/create/from-promise.mjs.map +1 -1
- package/dist/core/create/from-subscribable.d.mts.map +1 -1
- package/dist/core/create/from-subscribable.mjs +48 -12
- package/dist/core/create/from-subscribable.mjs.map +1 -1
- package/dist/core/create/just.d.mts.map +1 -1
- package/dist/core/create/just.mjs +4 -2
- package/dist/core/create/just.mjs.map +1 -1
- package/dist/core/create/source.d.mts +7 -0
- package/dist/core/create/source.d.mts.map +1 -1
- package/dist/core/create/source.mjs +27 -13
- package/dist/core/create/source.mjs.map +1 -1
- package/dist/core/create/timer.d.mts.map +1 -1
- package/dist/core/create/timer.mjs +40 -25
- package/dist/core/create/timer.mjs.map +1 -1
- package/dist/core/index.d.mts +2 -0
- package/dist/core/index.d.mts.map +1 -1
- package/dist/core/index.mjs +2 -0
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/operators/audit.d.mts +3 -0
- package/dist/core/operators/audit.d.mts.map +1 -1
- package/dist/core/operators/audit.mjs +56 -28
- package/dist/core/operators/audit.mjs.map +1 -1
- package/dist/core/operators/debounce.d.mts +3 -0
- package/dist/core/operators/debounce.d.mts.map +1 -1
- package/dist/core/operators/debounce.mjs +57 -26
- package/dist/core/operators/debounce.mjs.map +1 -1
- package/dist/core/operators/filter.d.mts.map +1 -1
- package/dist/core/operators/filter.mjs +35 -21
- package/dist/core/operators/filter.mjs.map +1 -1
- package/dist/core/operators/index.d.mts +1 -0
- package/dist/core/operators/index.d.mts.map +1 -1
- package/dist/core/operators/index.mjs +1 -0
- package/dist/core/operators/index.mjs.map +1 -1
- package/dist/core/operators/map.d.mts.map +1 -1
- package/dist/core/operators/map.mjs +31 -17
- package/dist/core/operators/map.mjs.map +1 -1
- package/dist/core/operators/merge-map.d.mts +5 -3
- package/dist/core/operators/merge-map.d.mts.map +1 -1
- package/dist/core/operators/merge-map.mjs +49 -26
- package/dist/core/operators/merge-map.mjs.map +1 -1
- package/dist/core/operators/pairwise.d.mts.map +1 -1
- package/dist/core/operators/pairwise.mjs +31 -20
- package/dist/core/operators/pairwise.mjs.map +1 -1
- package/dist/core/operators/scan.d.mts.map +1 -1
- package/dist/core/operators/scan.mjs +27 -14
- package/dist/core/operators/scan.mjs.map +1 -1
- package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
- package/dist/core/operators/skip-if-no-change.mjs +33 -19
- package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
- package/dist/core/operators/skip-until.d.mts.map +1 -1
- package/dist/core/operators/skip-until.mjs +43 -20
- package/dist/core/operators/skip-until.mjs.map +1 -1
- package/dist/core/operators/skip-while.d.mts.map +1 -1
- package/dist/core/operators/skip-while.mjs +35 -20
- package/dist/core/operators/skip-while.mjs.map +1 -1
- package/dist/core/operators/switch-map.d.mts +5 -3
- package/dist/core/operators/switch-map.d.mts.map +1 -1
- package/dist/core/operators/switch-map.mjs +53 -25
- package/dist/core/operators/switch-map.mjs.map +1 -1
- package/dist/core/operators/take-until.d.mts.map +1 -1
- package/dist/core/operators/take-until.mjs +38 -19
- package/dist/core/operators/take-until.mjs.map +1 -1
- package/dist/core/operators/take-while.d.mts.map +1 -1
- package/dist/core/operators/take-while.mjs +36 -22
- package/dist/core/operators/take-while.mjs.map +1 -1
- package/dist/core/{predefined/operators → operators}/take.d.mts +7 -3
- package/dist/core/operators/take.d.mts.map +1 -0
- package/dist/core/operators/take.mjs +102 -0
- package/dist/core/operators/take.mjs.map +1 -0
- package/dist/core/operators/throttle.d.mts.map +1 -1
- package/dist/core/operators/throttle.mjs +41 -26
- package/dist/core/operators/throttle.mjs.map +1 -1
- package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
- package/dist/core/operators/with-buffered-from.mjs +44 -31
- package/dist/core/operators/with-buffered-from.mjs.map +1 -1
- package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
- package/dist/core/operators/with-current-value-from.mjs +36 -21
- package/dist/core/operators/with-current-value-from.mjs.map +1 -1
- package/dist/core/operators/with-initial-value.d.mts.map +1 -1
- package/dist/core/operators/with-initial-value.mjs +24 -12
- package/dist/core/operators/with-initial-value.mjs.map +1 -1
- package/dist/core/predefined/operators/index.d.mts +0 -1
- package/dist/core/predefined/operators/index.d.mts.map +1 -1
- package/dist/core/predefined/operators/index.mjs +0 -1
- package/dist/core/predefined/operators/index.mjs.map +1 -1
- package/dist/core/scope/index.d.mts +2 -0
- package/dist/core/scope/index.d.mts.map +1 -0
- package/dist/core/scope/index.mjs +2 -0
- package/dist/core/scope/index.mjs.map +1 -0
- package/dist/core/scope/scope.d.mts +51 -0
- package/dist/core/scope/scope.d.mts.map +1 -0
- package/dist/core/scope/scope.mjs +194 -0
- package/dist/core/scope/scope.mjs.map +1 -0
- package/dist/core/types/id.d.mts +0 -2
- package/dist/core/types/id.d.mts.map +1 -1
- package/dist/core/types/index.d.mts +1 -0
- package/dist/core/types/index.d.mts.map +1 -1
- package/dist/core/types/index.mjs +1 -0
- package/dist/core/types/index.mjs.map +1 -1
- package/dist/core/types/observable-family.d.mts +6 -1
- package/dist/core/types/observable-family.d.mts.map +1 -1
- package/dist/core/types/observable-family.mjs.map +1 -1
- package/dist/core/types/observable.d.mts +40 -28
- package/dist/core/types/observable.d.mts.map +1 -1
- package/dist/core/types/observable.mjs +24 -0
- package/dist/core/types/observable.mjs.map +1 -1
- package/dist/core/types/scope.d.mts +27 -0
- package/dist/core/types/scope.d.mts.map +1 -0
- package/dist/core/types/scope.mjs +2 -0
- package/dist/core/types/scope.mjs.map +1 -0
- package/dist/core/utils/current-scope.d.mts +30 -0
- package/dist/core/utils/current-scope.d.mts.map +1 -0
- package/dist/core/utils/current-scope.mjs +26 -0
- package/dist/core/utils/current-scope.mjs.map +1 -0
- package/dist/core/utils/id-maker.d.mts +14 -3
- package/dist/core/utils/id-maker.d.mts.map +1 -1
- package/dist/core/utils/id-maker.mjs +18 -14
- package/dist/core/utils/id-maker.mjs.map +1 -1
- package/dist/core/utils/index.d.mts +2 -1
- package/dist/core/utils/index.d.mts.map +1 -1
- package/dist/core/utils/index.mjs +2 -1
- package/dist/core/utils/index.mjs.map +1 -1
- package/dist/core/utils/max-depth.d.mts +1 -0
- package/dist/core/utils/max-depth.d.mts.map +1 -1
- package/dist/core/utils/max-depth.mjs +9 -7
- package/dist/core/utils/max-depth.mjs.map +1 -1
- package/dist/core/utils/observable-brand.d.mts +13 -0
- package/dist/core/utils/observable-brand.d.mts.map +1 -0
- package/dist/core/utils/observable-brand.mjs +13 -0
- package/dist/core/utils/observable-brand.mjs.map +1 -0
- package/dist/utils/create-boolean-state.d.mts +2 -2
- package/dist/utils/create-event-emitter.d.mts +2 -2
- package/dist/utils/create-reducer.d.mts +17 -8
- package/dist/utils/create-reducer.d.mts.map +1 -1
- package/dist/utils/create-reducer.mjs +14 -6
- package/dist/utils/create-reducer.mjs.map +1 -1
- package/dist/utils/create-state.d.mts +9 -2
- package/dist/utils/create-state.d.mts.map +1 -1
- package/dist/utils/create-state.mjs +7 -0
- package/dist/utils/create-state.mjs.map +1 -1
- package/package.json +16 -16
- package/src/core/authoring.mts +19 -0
- package/src/core/class/child-observable-class.mts +374 -0
- package/src/core/{base → class}/circular-dependency-comparison.test.mts +46 -38
- package/src/core/{base → class}/circular-dependency.test.mts +38 -2
- package/src/core/class/complete-propagation.test.mts +264 -0
- package/src/core/class/create-child-observable.mts +370 -0
- package/src/core/class/create-root-observable.mts +107 -0
- package/src/core/class/index.mts +8 -0
- package/src/core/class/manager-observable-class.mts +166 -0
- package/src/core/class/observable-base-class.mts +315 -0
- package/src/core/{base → class}/observable-base.test.mts +1 -1
- package/src/core/class/observable-node.mts +54 -0
- package/src/core/class/propagation-order.test.mts +92 -0
- package/src/core/class/propagation-state.mts +80 -0
- package/src/core/class/remove-completed.test.mts +369 -0
- package/src/core/class/root-observable-class.mts +85 -0
- package/src/core/class/write-during-propagation.test.mts +234 -0
- package/src/core/combine/combine.mts +67 -55
- package/src/core/combine/merge.mts +33 -26
- package/src/core/combine/zip.mts +103 -58
- package/src/core/create/counter.mts +105 -54
- package/src/core/create/counter.test.mts +82 -0
- package/src/core/create/from-abortable-promise.mts +54 -42
- package/src/core/create/from-promise.mts +38 -29
- package/src/core/create/from-subscribable.mts +68 -23
- package/src/core/create/from-subscribable.test.mts +134 -0
- package/src/core/create/just.mts +4 -5
- package/src/core/create/source.mts +37 -13
- package/src/core/create/source.test.mts +41 -0
- package/src/core/create/timer.mts +51 -32
- package/src/core/index.mts +2 -0
- package/src/core/operators/audit.mts +79 -45
- package/src/core/operators/debounce-audit-complete.test.mts +121 -0
- package/src/core/operators/debounce.mts +81 -40
- package/src/core/operators/filter.mts +48 -31
- package/src/core/operators/index.mts +1 -0
- package/src/core/operators/inner-ownership.test.mts +280 -0
- package/src/core/operators/map.mts +49 -32
- package/src/core/operators/merge-map.mts +81 -46
- package/src/core/operators/pairwise.mts +40 -30
- package/src/core/operators/scan.mts +41 -24
- package/src/core/operators/skip-if-no-change.mts +45 -31
- package/src/core/operators/skip-until.mts +59 -33
- package/src/core/operators/skip-while.mts +49 -31
- package/src/core/operators/switch-map.mts +74 -38
- package/src/core/operators/take-until-skip-until-teardown.test.mts +133 -0
- package/src/core/operators/take-until.mts +49 -31
- package/src/core/operators/take-while.mts +50 -33
- package/src/core/operators/take.mts +138 -0
- package/src/core/operators/throttle.mts +55 -38
- package/src/core/operators/with-buffered-from.mts +45 -35
- package/src/core/operators/with-buffered-from.test.mts +57 -0
- package/src/core/operators/with-current-value-from.mts +39 -25
- package/src/core/operators/with-initial-value.mts +30 -21
- package/src/core/predefined/operators/index.mts +0 -1
- package/src/core/scope/index.mts +1 -0
- package/src/core/scope/scope.mts +260 -0
- package/src/core/scope/scope.test.mts +462 -0
- package/src/core/types/id.mts +0 -4
- package/src/core/types/index.mts +1 -0
- package/src/core/types/observable-family.mts +7 -1
- package/src/core/types/observable.mts +77 -42
- package/src/core/types/scope.mts +27 -0
- package/src/core/utils/current-scope.mts +61 -0
- package/src/core/utils/id-maker.mts +20 -30
- package/src/core/utils/index.mts +2 -1
- package/src/core/utils/max-depth.mts +12 -9
- package/src/core/utils/observable-brand.mts +12 -0
- package/src/utils/create-boolean-state.mts +2 -2
- package/src/utils/create-boolean-state.test.mts +18 -4
- package/src/utils/create-event-emitter.mts +2 -2
- package/src/utils/create-reducer.mts +22 -16
- package/src/utils/create-reducer.test.mts +43 -14
- package/src/utils/create-state.mts +9 -2
- package/src/utils/create-state.test.mts +32 -14
- package/dist/core/base/create-child-observable.d.mts +0 -65
- package/dist/core/base/create-child-observable.d.mts.map +0 -1
- package/dist/core/base/create-child-observable.mjs +0 -267
- package/dist/core/base/create-child-observable.mjs.map +0 -1
- package/dist/core/base/create-observable-base.d.mts +0 -147
- package/dist/core/base/create-observable-base.d.mts.map +0 -1
- package/dist/core/base/create-observable-base.mjs +0 -255
- package/dist/core/base/create-observable-base.mjs.map +0 -1
- package/dist/core/base/create-root-observable.d.mts +0 -37
- package/dist/core/base/create-root-observable.d.mts.map +0 -1
- package/dist/core/base/create-root-observable.mjs +0 -57
- package/dist/core/base/create-root-observable.mjs.map +0 -1
- package/dist/core/base/index.d.mts +0 -3
- package/dist/core/base/index.d.mts.map +0 -1
- package/dist/core/base/index.mjs +0 -3
- package/dist/core/base/index.mjs.map +0 -1
- package/dist/core/predefined/operators/take.d.mts.map +0 -1
- package/dist/core/predefined/operators/take.mjs +0 -44
- package/dist/core/predefined/operators/take.mjs.map +0 -1
- package/dist/core/utils/utils.d.mts +0 -6
- package/dist/core/utils/utils.d.mts.map +0 -1
- package/dist/core/utils/utils.mjs +0 -37
- package/dist/core/utils/utils.mjs.map +0 -1
- package/src/core/base/create-child-observable.mts +0 -457
- package/src/core/base/create-observable-base.mts +0 -548
- package/src/core/base/create-root-observable.mts +0 -113
- package/src/core/base/index.mts +0 -2
- package/src/core/predefined/operators/take.mts +0 -48
- package/src/core/utils/utils.mts +0 -56
- package/src/core/utils/utils.test.mts +0 -88
|
@@ -1,548 +0,0 @@
|
|
|
1
|
-
import { Arr, Optional } from 'ts-data-forge';
|
|
2
|
-
import { unreachable } from 'ts-std-forge';
|
|
3
|
-
import type { MutableMap, MutableSet, ReadonlyRecord } from 'ts-type-forge';
|
|
4
|
-
import type {
|
|
5
|
-
ChildObservable,
|
|
6
|
-
InitializedObservable,
|
|
7
|
-
Observable,
|
|
8
|
-
ObservableBase,
|
|
9
|
-
ObservableId,
|
|
10
|
-
ObservableKind,
|
|
11
|
-
Operator,
|
|
12
|
-
Subscriber,
|
|
13
|
-
SubscriberId,
|
|
14
|
-
Subscription,
|
|
15
|
-
UpdateToken,
|
|
16
|
-
WithInitialValueOperator,
|
|
17
|
-
} from '../types/index.mjs';
|
|
18
|
-
import {
|
|
19
|
-
binarySearch,
|
|
20
|
-
issueObservableId,
|
|
21
|
-
issueSubscriberId,
|
|
22
|
-
issueUpdateToken,
|
|
23
|
-
toSubscriber,
|
|
24
|
-
} from '../utils/index.mjs';
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The mutable core shared by every observable, expressed as closures instead of
|
|
28
|
-
* a base class.
|
|
29
|
-
*
|
|
30
|
-
* The returned handle is the "protected" surface of the former
|
|
31
|
-
* `ObservableBaseClass`: the layer factories (`createRootObservable`,
|
|
32
|
-
* `createSyncChildObservable`, `createAsyncChildObservable`) build the public
|
|
33
|
-
* object from it via {@link assembleObservable}, and leaf factories receive
|
|
34
|
-
* only the narrow tools they need. Neither the handle nor its type is exported
|
|
35
|
-
* from the package (`core/base/**` is excluded from index generation), so
|
|
36
|
-
* `setNext` and friends stay internal, as `protected` kept them before.
|
|
37
|
-
*/
|
|
38
|
-
export type ObservableBaseHandle<A> = Readonly<{
|
|
39
|
-
id: ObservableId;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Registers the public object {@link assembleObservable} built from this
|
|
43
|
-
* handle, so that its four state flags (`isCompleted`, `updateToken`,
|
|
44
|
-
* `hasSubscriber`, `hasChild`) can be mirrored onto it as plain data
|
|
45
|
-
* properties.
|
|
46
|
-
*
|
|
47
|
-
* They used to be getters delegating to this handle's closures, and that
|
|
48
|
-
* costs more than it looks. Reading one was an accessor call plus a closure
|
|
49
|
-
* call where a field read would do — `updateToken` is read once per child
|
|
50
|
-
* per update, so the propagation path paid it on every step — and an object
|
|
51
|
-
* literal carrying accessors is far more expensive to create than one
|
|
52
|
-
* carrying data, which is most of what building a graph costs. Measured on
|
|
53
|
-
* one machine with `pnpm run benchmark`: the derived chain's 100,000 updates
|
|
54
|
-
* 16.7 ms -> 13.6 ms, and the cascaded diamond at N=20, whose measurement is
|
|
55
|
-
* dominated by graph construction, 587 ms -> 362 ms.
|
|
56
|
-
*
|
|
57
|
-
* Every write below therefore has to keep the mirror in step; that is the
|
|
58
|
-
* price of not reading through a closure on the hot path. It is paid in
|
|
59
|
-
* clarity too, and on purpose: `hasSubscriber` and `hasChild` are derived
|
|
60
|
-
* from `mut_subscribers` and `mut_children`, which a getter stated once and
|
|
61
|
-
* declaratively, whereas the mirror restates it at every write.
|
|
62
|
-
*
|
|
63
|
-
* Nor can the rarely read flags alone go back to getters while `updateToken`
|
|
64
|
-
* stays data. A single accessor in the literal is enough to lose nearly all
|
|
65
|
-
* of the gain: measured on Node 26 with the `...extra` spread followed by
|
|
66
|
-
* these properties, one getter among data properties made the literal about
|
|
67
|
-
* 10x slower to create than all data (four getters: about 15x) and gave
|
|
68
|
-
* every instance its own hidden class (`%HaveSameMap` false). Keep all four
|
|
69
|
-
* as data, or measure again before changing that.
|
|
70
|
-
*/
|
|
71
|
-
attach: (writers: ObservableStateWriters) => void;
|
|
72
|
-
|
|
73
|
-
addChild: <B>(child: ChildObservable<B>) => void;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Forgets `child`, which a completed child calls on each of its parents so
|
|
77
|
-
* that nothing keeps a reference to it (#2134).
|
|
78
|
-
*/
|
|
79
|
-
deleteChild: <B>(child: ChildObservable<B>) => void;
|
|
80
|
-
|
|
81
|
-
getSnapshot: () => Optional<A>;
|
|
82
|
-
|
|
83
|
-
isCompleted: () => boolean;
|
|
84
|
-
|
|
85
|
-
updateToken: () => UpdateToken;
|
|
86
|
-
|
|
87
|
-
hasSubscriber: () => boolean;
|
|
88
|
-
|
|
89
|
-
hasActiveChild: () => boolean;
|
|
90
|
-
|
|
91
|
-
subscribe: (onNext: (v: A) => void, onComplete?: () => void) => Subscription;
|
|
92
|
-
|
|
93
|
-
/** Stores `nextValue` as the current value and notifies subscribers. */
|
|
94
|
-
setNext: (nextValue: A, updateToken: UpdateToken) => void;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* The innermost part of `complete()` (what `ObservableBaseClass.complete`
|
|
98
|
-
* used to do): marks this observable completed exactly once, notifies and
|
|
99
|
-
* removes all subscribers, and propagates `tryComplete` to children. Layer
|
|
100
|
-
* factories wrap this with leaf teardown and propagation to parents.
|
|
101
|
-
*/
|
|
102
|
-
completeBase: () => void;
|
|
103
|
-
}>;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* How the handle writes the four state flags onto the public object. The
|
|
107
|
-
* writers are closures over that object rather than the object itself, because
|
|
108
|
-
* a parameter this side could mutate has to be a mutable type, which
|
|
109
|
-
* `@typescript-eslint/prefer-readonly-parameter-types` rejects — and the object
|
|
110
|
-
* is `Readonly` to everyone else.
|
|
111
|
-
*/
|
|
112
|
-
type ObservableStateWriters = Readonly<{
|
|
113
|
-
setIsCompleted: (value: boolean) => void;
|
|
114
|
-
setUpdateToken: (value: UpdateToken) => void;
|
|
115
|
-
setHasSubscriber: (value: boolean) => void;
|
|
116
|
-
setHasChild: (value: boolean) => void;
|
|
117
|
-
}>;
|
|
118
|
-
|
|
119
|
-
export const createObservableBaseHandle = <A,>(
|
|
120
|
-
initialValue: Optional<A>,
|
|
121
|
-
): ObservableBaseHandle<A> => {
|
|
122
|
-
const id = issueObservableId();
|
|
123
|
-
|
|
124
|
-
// A set, not an array: a completed child removes itself, and that has to be
|
|
125
|
-
// O(1) for a UI that creates and completes derived nodes on every mount.
|
|
126
|
-
const mut_children: MutableSet<ChildObservable<unknown>> = new Set();
|
|
127
|
-
|
|
128
|
-
const mut_subscribers: MutableMap<SubscriberId, Subscriber<A>> = new Map();
|
|
129
|
-
|
|
130
|
-
let mut_currentValue: Optional<A> = initialValue;
|
|
131
|
-
|
|
132
|
-
let mut_isCompleted = false;
|
|
133
|
-
|
|
134
|
-
let mut_updateToken: UpdateToken = issueUpdateToken();
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Set once {@link assembleObservable} has built the public object. It does
|
|
138
|
-
* not exist while a leaf factory's `init` callback runs — that callback may
|
|
139
|
-
* already subscribe or complete — so every write below is optional, and
|
|
140
|
-
* `attach` copies the state as it stands rather than assuming defaults.
|
|
141
|
-
*/
|
|
142
|
-
let mut_writers: ObservableStateWriters | undefined;
|
|
143
|
-
|
|
144
|
-
const attach = (writers: ObservableStateWriters): void => {
|
|
145
|
-
mut_writers = writers;
|
|
146
|
-
|
|
147
|
-
writers.setIsCompleted(mut_isCompleted);
|
|
148
|
-
|
|
149
|
-
writers.setUpdateToken(mut_updateToken);
|
|
150
|
-
|
|
151
|
-
writers.setHasSubscriber(mut_subscribers.size > 0);
|
|
152
|
-
|
|
153
|
-
writers.setHasChild(mut_children.size > 0);
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const addSubscriber = (s: Subscriber<A>): SubscriberId => {
|
|
157
|
-
// return the id of added subscriber
|
|
158
|
-
const subscriberId = issueSubscriberId();
|
|
159
|
-
|
|
160
|
-
mut_subscribers.set(subscriberId, s);
|
|
161
|
-
|
|
162
|
-
mut_writers?.setHasSubscriber(true);
|
|
163
|
-
|
|
164
|
-
return subscriberId;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const removeSubscriber = (subscriberId: SubscriberId): void => {
|
|
168
|
-
mut_subscribers.delete(subscriberId);
|
|
169
|
-
|
|
170
|
-
mut_writers?.setHasSubscriber(mut_subscribers.size > 0);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const addChild = <B,>(child: ChildObservable<B>): void => {
|
|
174
|
-
mut_children.add(child);
|
|
175
|
-
|
|
176
|
-
mut_writers?.setHasChild(true);
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const deleteChild = <B,>(child: ChildObservable<B>): void => {
|
|
180
|
-
mut_children.delete(child);
|
|
181
|
-
|
|
182
|
-
mut_writers?.setHasChild(mut_children.size > 0);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const getSnapshot = (): Optional<A> => mut_currentValue;
|
|
186
|
-
|
|
187
|
-
const isCompleted = (): boolean => mut_isCompleted;
|
|
188
|
-
|
|
189
|
-
const updateToken = (): UpdateToken => mut_updateToken;
|
|
190
|
-
|
|
191
|
-
const hasSubscriber = (): boolean => mut_subscribers.size > 0;
|
|
192
|
-
|
|
193
|
-
const hasActiveChild = (): boolean => {
|
|
194
|
-
for (const c of mut_children) {
|
|
195
|
-
if (!c.isCompleted) {
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return false;
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const setNext = (nextValue: A, nextUpdateToken: UpdateToken): void => {
|
|
204
|
-
mut_updateToken = nextUpdateToken;
|
|
205
|
-
|
|
206
|
-
mut_writers?.setUpdateToken(nextUpdateToken);
|
|
207
|
-
|
|
208
|
-
mut_currentValue = Optional.some(nextValue);
|
|
209
|
-
|
|
210
|
-
for (const s of mut_subscribers.values()) {
|
|
211
|
-
s.onNext(nextValue);
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const completeBase = (): void => {
|
|
216
|
-
if (mut_isCompleted) {
|
|
217
|
-
return; // terminate only once
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// change state
|
|
221
|
-
mut_isCompleted = true;
|
|
222
|
-
|
|
223
|
-
mut_writers?.setIsCompleted(true);
|
|
224
|
-
|
|
225
|
-
// run subscribers for the current value
|
|
226
|
-
for (const s of mut_subscribers.values()) {
|
|
227
|
-
s.onComplete();
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// remove all subscribers
|
|
231
|
-
mut_subscribers.clear();
|
|
232
|
-
|
|
233
|
-
mut_writers?.setHasSubscriber(false);
|
|
234
|
-
|
|
235
|
-
// Propagate to children. A child that completes removes itself from
|
|
236
|
-
// `mut_children` during this loop, which iterating a `Set` tolerates.
|
|
237
|
-
for (const o of mut_children) {
|
|
238
|
-
o.tryComplete();
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const subscribe = (
|
|
243
|
-
onNext: (v: A) => void,
|
|
244
|
-
onComplete?: () => void,
|
|
245
|
-
): Subscription => {
|
|
246
|
-
// first emit
|
|
247
|
-
const curr = getSnapshot();
|
|
248
|
-
|
|
249
|
-
if (Optional.isSome(curr)) {
|
|
250
|
-
onNext(curr.value);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (mut_isCompleted) {
|
|
254
|
-
if (onComplete !== undefined) {
|
|
255
|
-
onComplete();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return { unsubscribe: () => {} };
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const subscriberId: SubscriberId = addSubscriber(
|
|
262
|
-
toSubscriber(onNext, onComplete),
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
unsubscribe: () => {
|
|
267
|
-
removeSubscriber(subscriberId);
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
return {
|
|
273
|
-
id,
|
|
274
|
-
attach,
|
|
275
|
-
addChild,
|
|
276
|
-
deleteChild,
|
|
277
|
-
getSnapshot,
|
|
278
|
-
isCompleted,
|
|
279
|
-
updateToken,
|
|
280
|
-
hasSubscriber,
|
|
281
|
-
hasActiveChild,
|
|
282
|
-
subscribe,
|
|
283
|
-
setNext,
|
|
284
|
-
completeBase,
|
|
285
|
-
};
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* The extra members every manager observable — a root or an async child — owns:
|
|
290
|
-
* the depth-ordered descendant list and the update propagation that walks it.
|
|
291
|
-
*/
|
|
292
|
-
export type ManagerObservableParts<A> = Readonly<{
|
|
293
|
-
addDescendant: <B>(child: ChildObservable<B>) => void;
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Takes `child` out of the propagation order, which a completed child calls
|
|
297
|
-
* on every manager it registered to (#2134).
|
|
298
|
-
*/
|
|
299
|
-
deleteDescendant: <B>(child: ChildObservable<B>) => void;
|
|
300
|
-
|
|
301
|
-
/** Emits `nextValue` and propagates the update to all descendants. */
|
|
302
|
-
startUpdate: (nextValue: A) => void;
|
|
303
|
-
}>;
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Builds the manager half of a root or async-child observable. Both start their
|
|
307
|
-
* own update propagation, so both keep a depth-ordered propagation list; this
|
|
308
|
-
* is the single implementation of it (the class version duplicated it across
|
|
309
|
-
* `RootObservableClass` and `AsyncChildObservableClass`).
|
|
310
|
-
*
|
|
311
|
-
* The descendants are kept in one insertion-ordered set per depth, so adding
|
|
312
|
-
* and removing one is O(1) apart from the first node of a new depth. The flat
|
|
313
|
-
* array `startUpdate` walks is rebuilt only when an update finds it stale: it
|
|
314
|
-
* used to be copied on every `addDescendant`, which made creating n nodes under
|
|
315
|
-
* one root O(n²). Rebuilding at the start of an update also keeps the walk a
|
|
316
|
-
* snapshot, as the copy-on-write array was — a node created or completed while
|
|
317
|
-
* an update is propagating does not change which nodes that update visits.
|
|
318
|
-
*/
|
|
319
|
-
export const createManagerObservableParts = <A,>(
|
|
320
|
-
handle: ObservableBaseHandle<A>,
|
|
321
|
-
): ManagerObservableParts<A> => {
|
|
322
|
-
const mut_buckets: MutableMap<
|
|
323
|
-
number,
|
|
324
|
-
MutableSet<ChildObservable<unknown>>
|
|
325
|
-
> = new Map();
|
|
326
|
-
|
|
327
|
-
/** The keys of `mut_buckets`, ascending. */
|
|
328
|
-
let mut_depths: readonly number[] = [];
|
|
329
|
-
|
|
330
|
-
/** `undefined` while stale. */
|
|
331
|
-
let mut_propagationOrder: readonly ChildObservable<unknown>[] | undefined =
|
|
332
|
-
[];
|
|
333
|
-
|
|
334
|
-
const addDescendant = <B,>(child: ChildObservable<B>): void => {
|
|
335
|
-
const mut_bucket = mut_buckets.get(child.depth);
|
|
336
|
-
|
|
337
|
-
if (mut_bucket === undefined) {
|
|
338
|
-
mut_buckets.set(child.depth, new Set<ChildObservable<unknown>>([child]));
|
|
339
|
-
|
|
340
|
-
mut_depths = Arr.toInserted(
|
|
341
|
-
mut_depths,
|
|
342
|
-
binarySearch(mut_depths, child.depth),
|
|
343
|
-
child.depth,
|
|
344
|
-
);
|
|
345
|
-
} else {
|
|
346
|
-
if (mut_bucket.has(child)) {
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
mut_bucket.add(child);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
mut_propagationOrder = undefined;
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
const deleteDescendant = <B,>(child: ChildObservable<B>): void => {
|
|
357
|
-
// An emptied bucket stays, so that its depth is not inserted again.
|
|
358
|
-
const mut_bucket = mut_buckets.get(child.depth);
|
|
359
|
-
|
|
360
|
-
if (mut_bucket?.delete(child) === true) {
|
|
361
|
-
mut_propagationOrder = undefined;
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
const getPropagationOrder = (): readonly ChildObservable<unknown>[] => {
|
|
366
|
-
if (mut_propagationOrder !== undefined) {
|
|
367
|
-
return mut_propagationOrder;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const mut_order: ChildObservable<unknown>[] = [];
|
|
371
|
-
|
|
372
|
-
for (const depth of mut_depths) {
|
|
373
|
-
const bucket = mut_buckets.get(depth);
|
|
374
|
-
|
|
375
|
-
if (bucket !== undefined) {
|
|
376
|
-
// Not `push(...bucket)`: a spread passes every element as an argument,
|
|
377
|
-
// which overflows the stack for a bucket of a few hundred thousand.
|
|
378
|
-
for (const c of bucket) {
|
|
379
|
-
mut_order.push(c);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
mut_propagationOrder = mut_order;
|
|
385
|
-
|
|
386
|
-
return mut_order;
|
|
387
|
-
};
|
|
388
|
-
|
|
389
|
-
const startUpdate = (nextValue: A): void => {
|
|
390
|
-
const updateToken = issueUpdateToken();
|
|
391
|
-
|
|
392
|
-
handle.setNext(nextValue, updateToken);
|
|
393
|
-
|
|
394
|
-
for (const p of getPropagationOrder()) {
|
|
395
|
-
p.tryUpdate(updateToken);
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
return { addDescendant, deleteDescendant, startUpdate };
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
/** Default `tryUpdate` for observables that never receive parent updates. */
|
|
403
|
-
export const createTryUpdateNotImplemented = (): ((
|
|
404
|
-
_updateToken: UpdateToken,
|
|
405
|
-
) => void) => tryUpdateNotImplemented;
|
|
406
|
-
|
|
407
|
-
const tryUpdateNotImplemented = (_updateToken: UpdateToken): void => {
|
|
408
|
-
// Not `todo()`: nothing here is waiting to be written. This is the base
|
|
409
|
-
// default of what was a base-class method, and every observable that can
|
|
410
|
-
// receive a parent update supplies its own `tryUpdate`; an observable with
|
|
411
|
-
// no parents never gets one. Reaching this is the invariant breaking, which
|
|
412
|
-
// is what `unreachable` says (Sumi D-48). There is no `never` value to
|
|
413
|
-
// hand it — the fact of the call is the error.
|
|
414
|
-
unreachable();
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
type AssembleObservableArgs<
|
|
418
|
-
A,
|
|
419
|
-
Kind extends ObservableKind,
|
|
420
|
-
Extra extends ReadonlyRecord<string, unknown>,
|
|
421
|
-
> = Readonly<{
|
|
422
|
-
kind: Kind;
|
|
423
|
-
depth: Kind extends 'root' ? 0 : number;
|
|
424
|
-
handle: ObservableBaseHandle<A>;
|
|
425
|
-
tryUpdate: (updateToken: UpdateToken) => void;
|
|
426
|
-
tryComplete: () => void;
|
|
427
|
-
complete: () => void;
|
|
428
|
-
dispose: () => void;
|
|
429
|
-
autoComplete: boolean;
|
|
430
|
-
/**
|
|
431
|
-
* Kind-specific public members (`parents`, `addDescendant`) and leaf
|
|
432
|
-
* extensions (`next`, `start`). They are merged here, in the one place the
|
|
433
|
-
* final object literal is created, because the handle keeps the state flags
|
|
434
|
-
* up to date on that object alone: a copy made by spreading it afterwards
|
|
435
|
-
* would freeze their current values.
|
|
436
|
-
*/
|
|
437
|
-
extra: Extra;
|
|
438
|
-
}>;
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Builds the public observable object from a base handle. This replaces the
|
|
442
|
-
* implicit assembly a `class` performs: state stays in the handle's closures,
|
|
443
|
-
* `isCompleted` / `updateToken` / `hasSubscriber` / `hasChild` become data
|
|
444
|
-
* properties the handle keeps in step through `attach`, and the kind-specific
|
|
445
|
-
* behavior (`tryUpdate`, `tryComplete`, `complete`) is passed in already
|
|
446
|
-
* composed.
|
|
447
|
-
*/
|
|
448
|
-
export const assembleObservable = <
|
|
449
|
-
A,
|
|
450
|
-
Kind extends ObservableKind,
|
|
451
|
-
Extra extends ReadonlyRecord<string, unknown>,
|
|
452
|
-
>({
|
|
453
|
-
kind,
|
|
454
|
-
depth,
|
|
455
|
-
handle,
|
|
456
|
-
tryUpdate,
|
|
457
|
-
tryComplete,
|
|
458
|
-
complete,
|
|
459
|
-
dispose,
|
|
460
|
-
autoComplete,
|
|
461
|
-
extra,
|
|
462
|
-
}: AssembleObservableArgs<A, Kind, Extra>): Extra &
|
|
463
|
-
ObservableBase<A> &
|
|
464
|
-
Readonly<{
|
|
465
|
-
kind: Kind;
|
|
466
|
-
depth: AssembleObservableArgs<A, Kind, Extra>['depth'];
|
|
467
|
-
}> => {
|
|
468
|
-
function pipe<B>(
|
|
469
|
-
operator: WithInitialValueOperator<A, B>,
|
|
470
|
-
): InitializedObservable<B>;
|
|
471
|
-
|
|
472
|
-
// A `WithInitialValueOperator` is also an `Operator`: nothing at run time
|
|
473
|
-
// tells the two apart, so this set only refines the return type (#1952).
|
|
474
|
-
// @sumi-expect-error functions/no-refinement-overload
|
|
475
|
-
function pipe<B>(operator: Operator<A, B>): Observable<B>;
|
|
476
|
-
|
|
477
|
-
function pipe<B>(operator: Operator<A, B>): Observable<B> {
|
|
478
|
-
return operator(
|
|
479
|
-
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
480
|
-
mut_observable as unknown as InitializedObservable<A>,
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// The four state flags are plain data properties kept in step by the handle,
|
|
485
|
-
// not getters delegating to it — see `ObservableBaseHandle.attach` for the
|
|
486
|
-
// measurements that decided this. `updateToken` in particular is read once
|
|
487
|
-
// per child per update, and `tryUpdate` reading a field rather than calling
|
|
488
|
-
// through an accessor is most of what the change buys.
|
|
489
|
-
const mut_observable = {
|
|
490
|
-
...extra,
|
|
491
|
-
|
|
492
|
-
id: handle.id,
|
|
493
|
-
|
|
494
|
-
kind,
|
|
495
|
-
|
|
496
|
-
depth,
|
|
497
|
-
|
|
498
|
-
addChild: handle.addChild,
|
|
499
|
-
|
|
500
|
-
deleteChild: handle.deleteChild,
|
|
501
|
-
|
|
502
|
-
getSnapshot: handle.getSnapshot,
|
|
503
|
-
|
|
504
|
-
isCompleted: false,
|
|
505
|
-
|
|
506
|
-
updateToken: handle.updateToken(),
|
|
507
|
-
|
|
508
|
-
hasSubscriber: false,
|
|
509
|
-
|
|
510
|
-
hasChild: false,
|
|
511
|
-
|
|
512
|
-
hasActiveChild: handle.hasActiveChild,
|
|
513
|
-
|
|
514
|
-
autoComplete,
|
|
515
|
-
|
|
516
|
-
tryUpdate,
|
|
517
|
-
|
|
518
|
-
tryComplete,
|
|
519
|
-
|
|
520
|
-
complete,
|
|
521
|
-
|
|
522
|
-
dispose,
|
|
523
|
-
|
|
524
|
-
subscribe: handle.subscribe,
|
|
525
|
-
|
|
526
|
-
pipe,
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
handle.attach({
|
|
530
|
-
setIsCompleted: (value) => {
|
|
531
|
-
mut_observable.isCompleted = value;
|
|
532
|
-
},
|
|
533
|
-
|
|
534
|
-
setUpdateToken: (value) => {
|
|
535
|
-
mut_observable.updateToken = value;
|
|
536
|
-
},
|
|
537
|
-
|
|
538
|
-
setHasSubscriber: (value) => {
|
|
539
|
-
mut_observable.hasSubscriber = value;
|
|
540
|
-
},
|
|
541
|
-
|
|
542
|
-
setHasChild: (value) => {
|
|
543
|
-
mut_observable.hasChild = value;
|
|
544
|
-
},
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
return mut_observable;
|
|
548
|
-
};
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { Optional } from 'ts-data-forge';
|
|
2
|
-
import type { ReadonlyRecord } from 'ts-type-forge';
|
|
3
|
-
import { isRootObservable, type RootObservable } from '../types/index.mjs';
|
|
4
|
-
import {
|
|
5
|
-
assembleObservable,
|
|
6
|
-
createManagerObservableParts,
|
|
7
|
-
createObservableBaseHandle,
|
|
8
|
-
createTryUpdateNotImplemented,
|
|
9
|
-
} from './create-observable-base.mjs';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The internal tools a root leaf factory (`source`, `timer`, `fromPromise`, …)
|
|
13
|
-
* receives — the replacement for the `protected` methods a subclass of
|
|
14
|
-
* `RootObservableClass` used to inherit.
|
|
15
|
-
*/
|
|
16
|
-
export type RootObservableTools<A> = Readonly<{
|
|
17
|
-
/** Emits `nextValue` and propagates the update to all descendants. */
|
|
18
|
-
startUpdate: (nextValue: A) => void;
|
|
19
|
-
|
|
20
|
-
isCompleted: () => boolean;
|
|
21
|
-
|
|
22
|
-
/** Completes this observable (leaf teardown included). */
|
|
23
|
-
complete: () => void;
|
|
24
|
-
}>;
|
|
25
|
-
|
|
26
|
-
export type RootObservableConfig<A> = Readonly<{
|
|
27
|
-
initialValue: Optional<A>;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Leaf teardown (clear timers, abort requests, …), run at the beginning of
|
|
31
|
-
* `complete()` — the closure replacement for
|
|
32
|
-
* `override complete() { teardown(); super.complete(); }`.
|
|
33
|
-
*/
|
|
34
|
-
onComplete?: () => void;
|
|
35
|
-
}>;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Creates a root observable — the classless replacement for extending
|
|
39
|
-
* `RootObservableClass`.
|
|
40
|
-
*
|
|
41
|
-
* The optional `init` callback receives the internal tools and returns extra
|
|
42
|
-
* public members to merge into the observable (e.g. `next` for `source`,
|
|
43
|
-
* `start` for `timer`). `init` runs during construction; it may register
|
|
44
|
-
* asynchronous callbacks that use the tools, and may call `complete`
|
|
45
|
-
* synchronously, but the observable object itself does not exist yet at that
|
|
46
|
-
* point.
|
|
47
|
-
*/
|
|
48
|
-
export const createRootObservable = <
|
|
49
|
-
A,
|
|
50
|
-
// The default is deliberately empty: a root leaf without `init` adds no members.
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/no-generated-empty-object-type
|
|
52
|
-
Extension extends ReadonlyRecord<string, unknown> = ReadonlyRecord<
|
|
53
|
-
never,
|
|
54
|
-
never
|
|
55
|
-
>,
|
|
56
|
-
>(
|
|
57
|
-
{ initialValue, onComplete }: RootObservableConfig<A>,
|
|
58
|
-
init: (tools: RootObservableTools<A>) => Extension,
|
|
59
|
-
): Extension & RootObservable<A> => {
|
|
60
|
-
const handle = createObservableBaseHandle<A>(initialValue);
|
|
61
|
-
|
|
62
|
-
const { addDescendant, deleteDescendant, startUpdate } =
|
|
63
|
-
createManagerObservableParts(handle);
|
|
64
|
-
|
|
65
|
-
const complete = (): void => {
|
|
66
|
-
onComplete?.();
|
|
67
|
-
|
|
68
|
-
handle.completeBase();
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// A root without a teardown (`source`, `createState`, `just`) releases
|
|
72
|
-
// nothing by completing, so a child completing never completes it (#2133).
|
|
73
|
-
const autoComplete = onComplete !== undefined;
|
|
74
|
-
|
|
75
|
-
const tryComplete = (): void => {
|
|
76
|
-
if (autoComplete && !handle.hasSubscriber() && !handle.hasActiveChild()) {
|
|
77
|
-
complete();
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const extension: Extension = init({
|
|
82
|
-
startUpdate,
|
|
83
|
-
isCompleted: handle.isCompleted,
|
|
84
|
-
complete,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return assembleObservable({
|
|
88
|
-
kind: 'root',
|
|
89
|
-
depth: 0,
|
|
90
|
-
handle,
|
|
91
|
-
tryUpdate: createTryUpdateNotImplemented,
|
|
92
|
-
tryComplete,
|
|
93
|
-
complete,
|
|
94
|
-
// A root has no parents to keep out of it.
|
|
95
|
-
dispose: complete,
|
|
96
|
-
autoComplete,
|
|
97
|
-
extra: {
|
|
98
|
-
...extension,
|
|
99
|
-
addDescendant,
|
|
100
|
-
deleteDescendant,
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
if (import.meta.vitest !== undefined) {
|
|
106
|
-
test('isRootObservable', () => {
|
|
107
|
-
assert.isTrue(
|
|
108
|
-
isRootObservable(
|
|
109
|
-
createRootObservable({ initialValue: Optional.some(0) }, () => ({})),
|
|
110
|
-
),
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
}
|
package/src/core/base/index.mts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { PositiveSafeIntWithSmallInt } from 'ts-type-forge';
|
|
2
|
-
import { takeWhile } from '../../operators/index.mjs';
|
|
3
|
-
import type { DropInitialValueOperator } from '../../types/index.mjs';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Takes only the first `n` emissions from the source observable, then completes.
|
|
7
|
-
*
|
|
8
|
-
* @template A - The type of values from the source
|
|
9
|
-
* @param n - The number of values to take
|
|
10
|
-
* @returns An operator that takes the first n emissions
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* // Timeline:
|
|
15
|
-
* //
|
|
16
|
-
* // num$ 1 2 3 4 (ignored)
|
|
17
|
-
* // taken$ 1 2 3 | (completes)
|
|
18
|
-
* //
|
|
19
|
-
* // Explanation:
|
|
20
|
-
* // - take emits only the first n values, then completes
|
|
21
|
-
* // - Subsequent emissions from the source are ignored
|
|
22
|
-
*
|
|
23
|
-
* const num$ = source<number>();
|
|
24
|
-
*
|
|
25
|
-
* const taken$ = num$.pipe(take(3));
|
|
26
|
-
*
|
|
27
|
-
* const valueHistory: number[] = [];
|
|
28
|
-
*
|
|
29
|
-
* taken$.subscribe((x) => {
|
|
30
|
-
* valueHistory.push(x);
|
|
31
|
-
* });
|
|
32
|
-
*
|
|
33
|
-
* num$.next(1);
|
|
34
|
-
*
|
|
35
|
-
* num$.next(2);
|
|
36
|
-
*
|
|
37
|
-
* num$.next(3);
|
|
38
|
-
*
|
|
39
|
-
* assert.deepStrictEqual(valueHistory, [1, 2, 3]);
|
|
40
|
-
*
|
|
41
|
-
* num$.next(4); // ignored (already completed)
|
|
42
|
-
*
|
|
43
|
-
* assert.deepStrictEqual(valueHistory, [1, 2, 3]);
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
export const take = <A,>(
|
|
47
|
-
n: PositiveSafeIntWithSmallInt,
|
|
48
|
-
): DropInitialValueOperator<A, A> => takeWhile((_, index) => index + 1 <= n);
|