synstate 0.1.1 → 1.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 +317 -298
- package/dist/core/class/child-observable-class.d.mts.map +1 -1
- package/dist/core/class/child-observable-class.mjs +43 -10
- package/dist/core/class/child-observable-class.mjs.map +1 -1
- package/dist/core/class/observable-base-class.d.mts +4 -4
- package/dist/core/class/observable-base-class.d.mts.map +1 -1
- package/dist/core/class/observable-base-class.mjs +8 -8
- package/dist/core/class/observable-base-class.mjs.map +1 -1
- package/dist/core/class/root-observable-class.d.mts +1 -1
- package/dist/core/class/root-observable-class.d.mts.map +1 -1
- package/dist/core/class/root-observable-class.mjs +9 -9
- package/dist/core/class/root-observable-class.mjs.map +1 -1
- package/dist/core/combine/combine.d.mts +7 -7
- package/dist/core/combine/combine.mjs +13 -14
- package/dist/core/combine/combine.mjs.map +1 -1
- package/dist/core/combine/merge.d.mts +6 -6
- package/dist/core/combine/merge.mjs +9 -9
- package/dist/core/combine/merge.mjs.map +1 -1
- package/dist/core/combine/zip.d.mts +20 -19
- package/dist/core/combine/zip.d.mts.map +1 -1
- package/dist/core/combine/zip.mjs +22 -21
- package/dist/core/combine/zip.mjs.map +1 -1
- package/dist/core/create/{interval.d.mts → counter.d.mts} +14 -12
- package/dist/core/create/counter.d.mts.map +1 -0
- package/dist/core/create/{interval.mjs → counter.mjs} +21 -23
- package/dist/core/create/counter.mjs.map +1 -0
- package/dist/core/create/from-abortable-promise.d.mts +29 -0
- package/dist/core/create/from-abortable-promise.d.mts.map +1 -0
- package/dist/core/create/from-abortable-promise.mjs +70 -0
- package/dist/core/create/from-abortable-promise.mjs.map +1 -0
- package/dist/core/create/from-promise.d.mts +9 -6
- package/dist/core/create/from-promise.d.mts.map +1 -1
- package/dist/core/create/from-promise.mjs +8 -5
- package/dist/core/create/from-promise.mjs.map +1 -1
- package/dist/core/create/from-subscribable.d.mts +4 -4
- package/dist/core/create/from-subscribable.mjs +4 -4
- package/dist/core/create/index.d.mts +3 -3
- package/dist/core/create/index.d.mts.map +1 -1
- package/dist/core/create/index.mjs +4 -4
- package/dist/core/create/just.d.mts +32 -0
- package/dist/core/create/just.d.mts.map +1 -0
- package/dist/core/create/just.mjs +44 -0
- package/dist/core/create/just.mjs.map +1 -0
- package/dist/core/create/source.d.mts +7 -12
- package/dist/core/create/source.d.mts.map +1 -1
- package/dist/core/create/source.mjs +1 -6
- package/dist/core/create/source.mjs.map +1 -1
- package/dist/core/create/timer.d.mts +6 -4
- package/dist/core/create/timer.d.mts.map +1 -1
- package/dist/core/create/timer.mjs +6 -7
- package/dist/core/create/timer.mjs.map +1 -1
- package/dist/core/index.d.mts +1 -1
- package/dist/core/index.d.mts.map +1 -1
- package/dist/core/index.mjs +21 -14
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/operators/audit.d.mts +97 -0
- package/dist/core/operators/audit.d.mts.map +1 -0
- package/dist/core/operators/audit.mjs +144 -0
- package/dist/core/operators/audit.mjs.map +1 -0
- package/dist/core/operators/debounce.d.mts +88 -0
- package/dist/core/operators/debounce.d.mts.map +1 -0
- package/dist/core/operators/debounce.mjs +130 -0
- package/dist/core/operators/debounce.mjs.map +1 -0
- package/dist/core/operators/filter.d.mts +5 -5
- package/dist/core/operators/filter.mjs +3 -3
- package/dist/core/operators/filter.mjs.map +1 -1
- package/dist/core/operators/index.d.mts +4 -4
- package/dist/core/operators/index.d.mts.map +1 -1
- package/dist/core/operators/index.mjs +6 -6
- package/dist/core/operators/map.d.mts +41 -0
- package/dist/core/operators/map.d.mts.map +1 -0
- package/dist/core/operators/map.mjs +71 -0
- package/dist/core/operators/map.mjs.map +1 -0
- package/dist/core/operators/merge-map.d.mts +57 -30
- package/dist/core/operators/merge-map.d.mts.map +1 -1
- package/dist/core/operators/merge-map.mjs +59 -32
- package/dist/core/operators/merge-map.mjs.map +1 -1
- package/dist/core/operators/pairwise.d.mts +6 -6
- package/dist/core/operators/pairwise.mjs +9 -9
- package/dist/core/operators/pairwise.mjs.map +1 -1
- package/dist/core/operators/scan.d.mts +6 -6
- package/dist/core/operators/scan.mjs +9 -9
- package/dist/core/operators/scan.mjs.map +1 -1
- package/dist/core/operators/skip-if-no-change.d.mts +21 -9
- package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
- package/dist/core/operators/skip-if-no-change.mjs +25 -13
- package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
- package/dist/core/operators/skip-until.d.mts +5 -5
- package/dist/core/operators/skip-until.mjs +8 -8
- package/dist/core/operators/skip-until.mjs.map +1 -1
- package/dist/core/operators/skip-while.d.mts +18 -9
- package/dist/core/operators/skip-while.d.mts.map +1 -1
- package/dist/core/operators/skip-while.mjs +28 -16
- package/dist/core/operators/skip-while.mjs.map +1 -1
- package/dist/core/operators/switch-map.d.mts +57 -26
- package/dist/core/operators/switch-map.d.mts.map +1 -1
- package/dist/core/operators/switch-map.mjs +59 -28
- package/dist/core/operators/switch-map.mjs.map +1 -1
- package/dist/core/operators/take-until.d.mts +5 -5
- package/dist/core/operators/take-until.mjs +8 -8
- package/dist/core/operators/take-until.mjs.map +1 -1
- package/dist/core/operators/take-while.d.mts +15 -8
- package/dist/core/operators/take-while.d.mts.map +1 -1
- package/dist/core/operators/take-while.mjs +19 -13
- package/dist/core/operators/take-while.mjs.map +1 -1
- package/dist/core/operators/throttle.d.mts +81 -0
- package/dist/core/operators/throttle.d.mts.map +1 -0
- package/dist/core/operators/throttle.mjs +126 -0
- package/dist/core/operators/throttle.mjs.map +1 -0
- package/dist/core/operators/with-buffered-from.d.mts +13 -9
- package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
- package/dist/core/operators/with-buffered-from.mjs +17 -13
- package/dist/core/operators/with-buffered-from.mjs.map +1 -1
- package/dist/core/operators/with-current-value-from.d.mts +14 -9
- package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
- package/dist/core/operators/with-current-value-from.mjs +18 -13
- package/dist/core/operators/with-current-value-from.mjs.map +1 -1
- package/dist/core/operators/with-initial-value.d.mts +5 -5
- package/dist/core/operators/with-initial-value.mjs +8 -8
- package/dist/core/operators/with-initial-value.mjs.map +1 -1
- package/dist/core/predefined/index.d.mts +2 -0
- package/dist/core/predefined/index.d.mts.map +1 -0
- package/dist/core/predefined/index.mjs +12 -0
- package/dist/core/predefined/index.mjs.map +1 -0
- package/dist/core/predefined/operators/attach-index.d.mts +57 -0
- package/dist/core/predefined/operators/attach-index.d.mts.map +1 -0
- package/dist/core/predefined/operators/attach-index.mjs +62 -0
- package/dist/core/predefined/operators/attach-index.mjs.map +1 -0
- package/dist/core/predefined/operators/index.d.mts +12 -0
- package/dist/core/predefined/operators/index.d.mts.map +1 -0
- package/dist/core/predefined/operators/index.mjs +12 -0
- package/dist/core/predefined/operators/index.mjs.map +1 -0
- package/dist/core/predefined/operators/map-optional.d.mts +51 -0
- package/dist/core/predefined/operators/map-optional.d.mts.map +1 -0
- package/dist/core/predefined/operators/map-optional.mjs +55 -0
- package/dist/core/predefined/operators/map-optional.mjs.map +1 -0
- package/dist/core/predefined/operators/map-result-err.d.mts +51 -0
- package/dist/core/predefined/operators/map-result-err.d.mts.map +1 -0
- package/dist/core/predefined/operators/map-result-err.mjs +55 -0
- package/dist/core/predefined/operators/map-result-err.mjs.map +1 -0
- package/dist/core/predefined/operators/map-result-ok.d.mts +51 -0
- package/dist/core/predefined/operators/map-result-ok.d.mts.map +1 -0
- package/dist/core/predefined/operators/map-result-ok.mjs +55 -0
- package/dist/core/predefined/operators/map-result-ok.mjs.map +1 -0
- package/dist/core/predefined/operators/map-to.d.mts +43 -0
- package/dist/core/predefined/operators/map-to.d.mts.map +1 -0
- package/dist/core/predefined/operators/map-to.mjs +48 -0
- package/dist/core/predefined/operators/map-to.mjs.map +1 -0
- package/dist/core/predefined/operators/pluck.d.mts +47 -0
- package/dist/core/predefined/operators/pluck.d.mts.map +1 -0
- package/dist/core/predefined/operators/pluck.mjs +52 -0
- package/dist/core/predefined/operators/pluck.mjs.map +1 -0
- package/dist/core/predefined/operators/skip.d.mts +50 -0
- package/dist/core/predefined/operators/skip.d.mts.map +1 -0
- package/dist/core/predefined/operators/skip.mjs +56 -0
- package/dist/core/predefined/operators/skip.mjs.map +1 -0
- package/dist/core/predefined/operators/take.d.mts +44 -0
- package/dist/core/predefined/operators/take.d.mts.map +1 -0
- package/dist/core/predefined/operators/take.mjs +49 -0
- package/dist/core/predefined/operators/take.mjs.map +1 -0
- package/dist/core/predefined/operators/unwrap-optional.d.mts +44 -0
- package/dist/core/predefined/operators/unwrap-optional.d.mts.map +1 -0
- package/dist/core/predefined/operators/unwrap-optional.mjs +50 -0
- package/dist/core/predefined/operators/unwrap-optional.mjs.map +1 -0
- package/dist/core/predefined/operators/unwrap-result-err.d.mts +44 -0
- package/dist/core/predefined/operators/unwrap-result-err.d.mts.map +1 -0
- package/dist/core/predefined/operators/unwrap-result-err.mjs +48 -0
- package/dist/core/predefined/operators/unwrap-result-err.mjs.map +1 -0
- package/dist/core/predefined/operators/unwrap-result-ok.d.mts +44 -0
- package/dist/core/predefined/operators/unwrap-result-ok.d.mts.map +1 -0
- package/dist/core/predefined/operators/unwrap-result-ok.mjs +50 -0
- package/dist/core/predefined/operators/unwrap-result-ok.mjs.map +1 -0
- package/dist/core/types/id.d.mts +1 -1
- 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/observable-family.d.mts +8 -14
- package/dist/core/types/observable-family.d.mts.map +1 -1
- package/dist/core/types/observable.d.mts +3 -3
- package/dist/core/types/observable.d.mts.map +1 -1
- package/dist/core/types/timer.d.mts +2 -0
- package/dist/core/types/timer.d.mts.map +1 -0
- package/dist/core/types/timer.mjs +2 -0
- package/dist/core/types/timer.mjs.map +1 -0
- package/dist/core/utils/id-maker.d.mts +2 -2
- package/dist/core/utils/id-maker.d.mts.map +1 -1
- package/dist/core/utils/id-maker.mjs +3 -3
- package/dist/core/utils/id-maker.mjs.map +1 -1
- package/dist/core/utils/index.mjs +1 -1
- package/dist/entry-point.mjs +24 -15
- package/dist/entry-point.mjs.map +1 -1
- package/dist/globals.d.mts +0 -3
- package/dist/index.mjs +24 -15
- package/dist/index.mjs.map +1 -1
- package/dist/utils/collect-to-array.d.mts +3 -0
- package/dist/utils/collect-to-array.d.mts.map +1 -0
- package/dist/utils/collect-to-array.mjs +11 -0
- package/dist/utils/collect-to-array.mjs.map +1 -0
- package/dist/utils/create-boolean-state.d.mts +40 -0
- package/dist/utils/create-boolean-state.d.mts.map +1 -0
- package/dist/utils/create-boolean-state.mjs +53 -0
- package/dist/utils/create-boolean-state.mjs.map +1 -0
- package/dist/utils/create-event-emitter.d.mts +4 -4
- package/dist/utils/create-event-emitter.mjs +4 -4
- package/dist/utils/create-reducer.d.mts +10 -7
- package/dist/utils/create-reducer.d.mts.map +1 -1
- package/dist/utils/create-reducer.mjs +7 -7
- package/dist/utils/create-reducer.mjs.map +1 -1
- package/dist/utils/create-state.d.mts +8 -48
- package/dist/utils/create-state.d.mts.map +1 -1
- package/dist/utils/create-state.mjs +10 -60
- package/dist/utils/create-state.mjs.map +1 -1
- package/dist/utils/index.d.mts +2 -0
- package/dist/utils/index.d.mts.map +1 -1
- package/dist/utils/index.mjs +3 -1
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +17 -11
- package/src/core/class/child-observable-class.mts +65 -9
- package/src/core/class/circular-dependency-comparison.test.mts +142 -0
- package/src/core/class/circular-dependency.test.mts +251 -0
- package/src/core/class/observable-base-class.mts +9 -9
- package/src/core/class/root-observable-class.mts +14 -10
- package/src/core/combine/combine.mts +15 -15
- package/src/core/combine/merge.mts +13 -14
- package/src/core/combine/zip.mts +26 -25
- package/src/core/create/{interval.mts → counter.mts} +32 -30
- package/src/core/create/from-abortable-promise.mts +83 -0
- package/src/core/create/from-promise.mts +10 -7
- package/src/core/create/from-subscribable.mts +4 -4
- package/src/core/create/index.mts +3 -3
- package/src/core/create/just.mts +43 -0
- package/src/core/create/source.mts +10 -14
- package/src/core/create/timer.mts +12 -11
- package/src/core/index.mts +1 -1
- package/src/core/operators/audit.mts +172 -0
- package/src/core/operators/debounce.mts +154 -0
- package/src/core/operators/filter.mts +9 -9
- package/src/core/operators/index.mts +4 -4
- package/src/core/operators/map.mts +124 -0
- package/src/core/operators/merge-map.mts +60 -33
- package/src/core/operators/pairwise.mts +10 -10
- package/src/core/operators/scan.mts +10 -10
- package/src/core/operators/skip-if-no-change.mts +26 -14
- package/src/core/operators/skip-until.mts +9 -9
- package/src/core/operators/skip-while.mts +30 -28
- package/src/core/operators/switch-map.mts +60 -29
- package/src/core/operators/take-until.mts +9 -9
- package/src/core/operators/take-while.mts +21 -19
- package/src/core/operators/{throttle-time.mts → throttle.mts} +58 -38
- package/src/core/operators/with-buffered-from.mts +18 -14
- package/src/core/operators/with-current-value-from.mts +19 -14
- package/src/core/operators/with-initial-value.mts +9 -9
- package/src/core/predefined/index.mts +1 -0
- package/src/core/predefined/operators/attach-index.mts +62 -0
- package/src/core/predefined/operators/index.mts +11 -0
- package/src/core/predefined/operators/map-optional.mts +55 -0
- package/src/core/predefined/operators/map-result-err.mts +55 -0
- package/src/core/predefined/operators/map-result-ok.mts +55 -0
- package/src/core/predefined/operators/map-to.mts +45 -0
- package/src/core/predefined/operators/pluck.mts +51 -0
- package/src/core/predefined/operators/skip.mts +57 -0
- package/src/core/predefined/operators/take.mts +47 -0
- package/src/core/predefined/operators/unwrap-optional.mts +49 -0
- package/src/core/predefined/operators/unwrap-result-err.mts +48 -0
- package/src/core/predefined/operators/unwrap-result-ok.mts +49 -0
- package/src/core/types/id.mts +1 -1
- package/src/core/types/index.mts +1 -0
- package/src/core/types/observable-family.mts +8 -24
- package/src/core/types/observable.mts +3 -3
- package/src/core/types/timer.mts +2 -0
- package/src/core/utils/id-maker.mts +4 -4
- package/src/globals.d.mts +0 -3
- package/src/utils/collect-to-array.mts +17 -0
- package/src/utils/create-boolean-state.mts +68 -0
- package/src/utils/create-event-emitter.mts +4 -4
- package/src/utils/create-reducer.mts +11 -8
- package/src/utils/create-state.mts +10 -75
- package/src/utils/index.mts +2 -0
- package/dist/core/create/from-array.d.mts +0 -39
- package/dist/core/create/from-array.d.mts.map +0 -1
- package/dist/core/create/from-array.mjs +0 -65
- package/dist/core/create/from-array.mjs.map +0 -1
- package/dist/core/create/interval.d.mts.map +0 -1
- package/dist/core/create/interval.mjs.map +0 -1
- package/dist/core/create/of.d.mts +0 -39
- package/dist/core/create/of.d.mts.map +0 -1
- package/dist/core/create/of.mjs +0 -63
- package/dist/core/create/of.mjs.map +0 -1
- package/dist/core/operators/audit-time.d.mts +0 -62
- package/dist/core/operators/audit-time.d.mts.map +0 -1
- package/dist/core/operators/audit-time.mjs +0 -109
- package/dist/core/operators/audit-time.mjs.map +0 -1
- package/dist/core/operators/debounce-time.d.mts +0 -51
- package/dist/core/operators/debounce-time.d.mts.map +0 -1
- package/dist/core/operators/debounce-time.mjs +0 -93
- package/dist/core/operators/debounce-time.mjs.map +0 -1
- package/dist/core/operators/map-with-index.d.mts +0 -54
- package/dist/core/operators/map-with-index.d.mts.map +0 -1
- package/dist/core/operators/map-with-index.mjs +0 -88
- package/dist/core/operators/map-with-index.mjs.map +0 -1
- package/dist/core/operators/throttle-time.d.mts +0 -62
- package/dist/core/operators/throttle-time.d.mts.map +0 -1
- package/dist/core/operators/throttle-time.mjs +0 -107
- package/dist/core/operators/throttle-time.mjs.map +0 -1
- package/src/core/create/from-array.mts +0 -76
- package/src/core/create/of.mts +0 -73
- package/src/core/operators/audit-time.mts +0 -136
- package/src/core/operators/debounce-time.mts +0 -116
- package/src/core/operators/map-with-index.mts +0 -183
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/* eslint-disable functional/immutable-data */
|
|
2
|
+
/* eslint-disable no-new */
|
|
3
|
+
import { Optional } from 'ts-data-forge';
|
|
4
|
+
import { combine, merge } from '../combine/index.mjs';
|
|
5
|
+
import { source } from '../create/index.mjs';
|
|
6
|
+
import { map } from '../operators/index.mjs';
|
|
7
|
+
import {
|
|
8
|
+
AsyncChildObservableClass,
|
|
9
|
+
SyncChildObservableClass,
|
|
10
|
+
} from './child-observable-class.mjs';
|
|
11
|
+
import { RootObservableClass } from './root-observable-class.mjs';
|
|
12
|
+
|
|
13
|
+
describe('circular dependency detection', () => {
|
|
14
|
+
describe('cycle in ancestor graph', () => {
|
|
15
|
+
test('should throw when parent chain contains a cycle (A -> B -> A)', () => {
|
|
16
|
+
const root = new RootObservableClass({
|
|
17
|
+
initialValue: Optional.some(0),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const childA = new SyncChildObservableClass({
|
|
21
|
+
parents: [root],
|
|
22
|
+
initialValue: Optional.some(0),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const childB = new SyncChildObservableClass({
|
|
26
|
+
parents: [childA],
|
|
27
|
+
initialValue: Optional.some(0),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Mutate childA.parents to create a cycle: childA -> childB -> childA
|
|
31
|
+
Object.defineProperty(childA, 'parents', {
|
|
32
|
+
value: [childB],
|
|
33
|
+
writable: false,
|
|
34
|
+
configurable: true,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(() => {
|
|
38
|
+
new SyncChildObservableClass({
|
|
39
|
+
parents: [childA],
|
|
40
|
+
initialValue: Optional.some(0),
|
|
41
|
+
});
|
|
42
|
+
}).toThrow(
|
|
43
|
+
'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('should throw when AsyncChildObservable parent chain contains a cycle', () => {
|
|
48
|
+
const root = new RootObservableClass({
|
|
49
|
+
initialValue: Optional.some(0),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const childA = new AsyncChildObservableClass({
|
|
53
|
+
parents: [root],
|
|
54
|
+
initialValue: Optional.some(0),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const childB = new SyncChildObservableClass({
|
|
58
|
+
parents: [childA],
|
|
59
|
+
initialValue: Optional.some(0),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Create cycle: childA -> childB -> childA
|
|
63
|
+
Object.defineProperty(childA, 'parents', {
|
|
64
|
+
value: [childB],
|
|
65
|
+
writable: false,
|
|
66
|
+
configurable: true,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(() => {
|
|
70
|
+
new AsyncChildObservableClass({
|
|
71
|
+
parents: [childA],
|
|
72
|
+
initialValue: Optional.some(0),
|
|
73
|
+
});
|
|
74
|
+
}).toThrow(
|
|
75
|
+
'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('should throw when a cycle exists through a chain of 3 observables', () => {
|
|
80
|
+
const root = new RootObservableClass({
|
|
81
|
+
initialValue: Optional.some(0),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const childA = new SyncChildObservableClass({
|
|
85
|
+
parents: [root],
|
|
86
|
+
initialValue: Optional.some(0),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const childB = new SyncChildObservableClass({
|
|
90
|
+
parents: [childA],
|
|
91
|
+
initialValue: Optional.some(0),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const childC = new SyncChildObservableClass({
|
|
95
|
+
parents: [childB],
|
|
96
|
+
initialValue: Optional.some(0),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Create cycle: childA -> childC -> childB -> childA
|
|
100
|
+
Object.defineProperty(childA, 'parents', {
|
|
101
|
+
value: [childC],
|
|
102
|
+
writable: false,
|
|
103
|
+
configurable: true,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(() => {
|
|
107
|
+
new SyncChildObservableClass({
|
|
108
|
+
parents: [childA],
|
|
109
|
+
initialValue: Optional.some(0),
|
|
110
|
+
});
|
|
111
|
+
}).toThrow(
|
|
112
|
+
'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('should throw when the new child itself appears as an ancestor', () => {
|
|
117
|
+
const root = new RootObservableClass({
|
|
118
|
+
initialValue: Optional.some(0),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const childA = new SyncChildObservableClass({
|
|
122
|
+
parents: [root],
|
|
123
|
+
initialValue: Optional.some(0),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Make childA's parents reference childA itself (self-loop)
|
|
127
|
+
Object.defineProperty(childA, 'parents', {
|
|
128
|
+
value: [childA],
|
|
129
|
+
writable: false,
|
|
130
|
+
configurable: true,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(() => {
|
|
134
|
+
new SyncChildObservableClass({
|
|
135
|
+
parents: [childA],
|
|
136
|
+
initialValue: Optional.some(0),
|
|
137
|
+
});
|
|
138
|
+
}).toThrow(
|
|
139
|
+
'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('valid DAG patterns should not throw', () => {
|
|
145
|
+
test('diamond dependency is not a cycle', () => {
|
|
146
|
+
const root = new RootObservableClass({
|
|
147
|
+
initialValue: Optional.some(0),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const left = new SyncChildObservableClass({
|
|
151
|
+
parents: [root],
|
|
152
|
+
initialValue: Optional.some(0),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const right = new SyncChildObservableClass({
|
|
156
|
+
parents: [root],
|
|
157
|
+
initialValue: Optional.some(0),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Diamond: root -> left -> combined, root -> right -> combined
|
|
161
|
+
expect(() => {
|
|
162
|
+
new SyncChildObservableClass({
|
|
163
|
+
parents: [left, right],
|
|
164
|
+
initialValue: Optional.some(0),
|
|
165
|
+
});
|
|
166
|
+
}).not.toThrow();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('linear chain is not a cycle', () => {
|
|
170
|
+
const root = new RootObservableClass({
|
|
171
|
+
initialValue: Optional.some(0),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const a = new SyncChildObservableClass({
|
|
175
|
+
parents: [root],
|
|
176
|
+
initialValue: Optional.some(0),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const b = new SyncChildObservableClass({
|
|
180
|
+
parents: [a],
|
|
181
|
+
initialValue: Optional.some(0),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
expect(() => {
|
|
185
|
+
new SyncChildObservableClass({
|
|
186
|
+
parents: [b],
|
|
187
|
+
initialValue: Optional.some(0),
|
|
188
|
+
});
|
|
189
|
+
}).not.toThrow();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('multiple roots converging is not a cycle', () => {
|
|
193
|
+
const root1 = new RootObservableClass({
|
|
194
|
+
initialValue: Optional.some(1),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const root2 = new RootObservableClass({
|
|
198
|
+
initialValue: Optional.some(2),
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const child1 = new SyncChildObservableClass({
|
|
202
|
+
parents: [root1],
|
|
203
|
+
initialValue: Optional.some(0),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const child2 = new SyncChildObservableClass({
|
|
207
|
+
parents: [root2],
|
|
208
|
+
initialValue: Optional.some(0),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
expect(() => {
|
|
212
|
+
new SyncChildObservableClass({
|
|
213
|
+
parents: [child1, child2],
|
|
214
|
+
initialValue: Optional.some(0),
|
|
215
|
+
});
|
|
216
|
+
}).not.toThrow();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('combine with source observables works', () => {
|
|
220
|
+
const a$ = source<number>();
|
|
221
|
+
|
|
222
|
+
const b$ = source<string>();
|
|
223
|
+
|
|
224
|
+
expect(() => {
|
|
225
|
+
combine([a$, b$]);
|
|
226
|
+
}).not.toThrow();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('combine with derived observables works', () => {
|
|
230
|
+
const a$ = source<number>();
|
|
231
|
+
|
|
232
|
+
const b$ = source<number>();
|
|
233
|
+
|
|
234
|
+
const mapped$ = a$.pipe(map((x) => x * 2));
|
|
235
|
+
|
|
236
|
+
expect(() => {
|
|
237
|
+
combine([mapped$, b$]);
|
|
238
|
+
}).not.toThrow();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('merge with source observables works', () => {
|
|
242
|
+
const a$ = source<number>();
|
|
243
|
+
|
|
244
|
+
const b$ = source<number>();
|
|
245
|
+
|
|
246
|
+
expect(() => {
|
|
247
|
+
merge([a$, b$]);
|
|
248
|
+
}).not.toThrow();
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
type Subscriber,
|
|
9
9
|
type SubscriberId,
|
|
10
10
|
type Subscription,
|
|
11
|
-
type
|
|
11
|
+
type UpdateToken,
|
|
12
12
|
type WithInitialValueOperator,
|
|
13
13
|
} from '../types/index.mjs';
|
|
14
14
|
import {
|
|
15
15
|
issueObservableId,
|
|
16
16
|
issueSubscriberId,
|
|
17
|
-
|
|
17
|
+
issueUpdateToken,
|
|
18
18
|
toSubscriber,
|
|
19
19
|
} from '../utils/index.mjs';
|
|
20
20
|
|
|
@@ -30,7 +30,7 @@ export class ObservableBaseClass<
|
|
|
30
30
|
readonly #subscribers: MutableMap<SubscriberId, Subscriber<A>>;
|
|
31
31
|
#mut_currentValue: ReturnType<ObservableBase<A>['getSnapshot']>;
|
|
32
32
|
#mut_isCompleted: ObservableBase<A>['isCompleted'];
|
|
33
|
-
#
|
|
33
|
+
#mut_updateToken: ObservableBase<A>['updateToken'];
|
|
34
34
|
|
|
35
35
|
constructor({
|
|
36
36
|
kind,
|
|
@@ -55,7 +55,7 @@ export class ObservableBaseClass<
|
|
|
55
55
|
|
|
56
56
|
this.#mut_isCompleted = false;
|
|
57
57
|
|
|
58
|
-
this.#
|
|
58
|
+
this.#mut_updateToken = issueUpdateToken();
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
addChild<B>(child: ChildObservable<B>): void {
|
|
@@ -78,8 +78,8 @@ export class ObservableBaseClass<
|
|
|
78
78
|
return this.#mut_isCompleted;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
get
|
|
82
|
-
return this.#
|
|
81
|
+
get updateToken(): UpdateToken {
|
|
82
|
+
return this.#mut_updateToken;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
get hasSubscriber(): boolean {
|
|
@@ -94,8 +94,8 @@ export class ObservableBaseClass<
|
|
|
94
94
|
return this.#mut_children.some((c) => !c.isCompleted);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
protected setNext(nextValue: A,
|
|
98
|
-
this.#
|
|
97
|
+
protected setNext(nextValue: A, updateToken: UpdateToken): void {
|
|
98
|
+
this.#mut_updateToken = updateToken;
|
|
99
99
|
|
|
100
100
|
this.#mut_currentValue = Optional.some(nextValue);
|
|
101
101
|
|
|
@@ -105,7 +105,7 @@ export class ObservableBaseClass<
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
108
|
-
tryUpdate(
|
|
108
|
+
tryUpdate(_updateToken: UpdateToken): void {
|
|
109
109
|
throw new Error('not implemented');
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -5,20 +5,20 @@ import {
|
|
|
5
5
|
type ObservableId,
|
|
6
6
|
type RootObservable,
|
|
7
7
|
} from '../types/index.mjs';
|
|
8
|
-
import { binarySearch,
|
|
8
|
+
import { binarySearch, issueUpdateToken } from '../utils/index.mjs';
|
|
9
9
|
import { ObservableBaseClass } from './observable-base-class.mjs';
|
|
10
10
|
|
|
11
11
|
export class RootObservableClass<A>
|
|
12
12
|
extends ObservableBaseClass<A, 'root', 0>
|
|
13
13
|
implements RootObservable<A>
|
|
14
14
|
{
|
|
15
|
-
#
|
|
15
|
+
#mut_propagationOrder: readonly ChildObservable<unknown>[];
|
|
16
16
|
protected readonly _descendantsIdSet: MutableSet<ObservableId>;
|
|
17
17
|
|
|
18
18
|
constructor({
|
|
19
19
|
initialValue,
|
|
20
20
|
}: Readonly<{
|
|
21
|
-
initialValue:
|
|
21
|
+
initialValue: Optional<A>;
|
|
22
22
|
}>) {
|
|
23
23
|
super({
|
|
24
24
|
kind: 'root',
|
|
@@ -26,7 +26,7 @@ export class RootObservableClass<A>
|
|
|
26
26
|
initialValue,
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
this.#
|
|
29
|
+
this.#mut_propagationOrder = [];
|
|
30
30
|
|
|
31
31
|
this._descendantsIdSet = new Set<ObservableId>();
|
|
32
32
|
}
|
|
@@ -37,20 +37,24 @@ export class RootObservableClass<A>
|
|
|
37
37
|
this._descendantsIdSet.add(child.id);
|
|
38
38
|
|
|
39
39
|
const insertPos = binarySearch(
|
|
40
|
-
this.#
|
|
40
|
+
this.#mut_propagationOrder.map((a) => a.depth),
|
|
41
41
|
child.depth,
|
|
42
42
|
);
|
|
43
43
|
|
|
44
|
-
this.#
|
|
44
|
+
this.#mut_propagationOrder = Arr.toInserted(
|
|
45
|
+
this.#mut_propagationOrder,
|
|
46
|
+
insertPos,
|
|
47
|
+
child,
|
|
48
|
+
);
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
startUpdate(nextValue: A): void {
|
|
48
|
-
const
|
|
52
|
+
const updateToken = issueUpdateToken();
|
|
49
53
|
|
|
50
|
-
this.setNext(nextValue,
|
|
54
|
+
this.setNext(nextValue, updateToken);
|
|
51
55
|
|
|
52
|
-
for (const p of this.#
|
|
53
|
-
p.tryUpdate(
|
|
56
|
+
for (const p of this.#mut_propagationOrder) {
|
|
57
|
+
p.tryUpdate(updateToken);
|
|
54
58
|
}
|
|
55
59
|
}
|
|
56
60
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Arr, Optional, expectType } from 'ts-data-forge';
|
|
2
2
|
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import { source } from '../create/index.mjs';
|
|
4
4
|
import { withInitialValue } from '../operators/index.mjs';
|
|
5
5
|
import {
|
|
6
6
|
type CombineObservable,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type NonEmptyUnknownList,
|
|
11
11
|
type Observable,
|
|
12
12
|
type SyncChildObservable,
|
|
13
|
-
type
|
|
13
|
+
type UpdateToken,
|
|
14
14
|
type Wrap,
|
|
15
15
|
} from '../types/index.mjs';
|
|
16
16
|
|
|
@@ -41,30 +41,30 @@ import {
|
|
|
41
41
|
*
|
|
42
42
|
* const user$ = combine([name$, age$]);
|
|
43
43
|
*
|
|
44
|
-
* const
|
|
44
|
+
* const userHistory: (readonly [string, number])[] = [];
|
|
45
45
|
*
|
|
46
46
|
* user$.subscribe(([name_, age]) => {
|
|
47
|
-
*
|
|
47
|
+
* userHistory.push([name_, age]);
|
|
48
48
|
* });
|
|
49
49
|
*
|
|
50
50
|
* name$.next('Alice'); // nothing logged (age$ hasn't emitted yet)
|
|
51
51
|
*
|
|
52
|
-
* assert.deepStrictEqual(
|
|
52
|
+
* assert.deepStrictEqual(userHistory, []);
|
|
53
53
|
*
|
|
54
54
|
* age$.next(25); // logs: { name: 'Alice', age: 25 }
|
|
55
55
|
*
|
|
56
|
-
* assert.deepStrictEqual(
|
|
56
|
+
* assert.deepStrictEqual(userHistory, [['Alice', 25]]);
|
|
57
57
|
*
|
|
58
58
|
* name$.next('Bob'); // logs: { name: 'Bob', age: 25 }
|
|
59
59
|
*
|
|
60
|
-
* assert.deepStrictEqual(
|
|
60
|
+
* assert.deepStrictEqual(userHistory, [
|
|
61
61
|
* ['Alice', 25],
|
|
62
62
|
* ['Bob', 25],
|
|
63
63
|
* ]);
|
|
64
64
|
*
|
|
65
65
|
* age$.next(30); // logs: { name: 'Bob', age: 30 }
|
|
66
66
|
*
|
|
67
|
-
* assert.deepStrictEqual(
|
|
67
|
+
* assert.deepStrictEqual(userHistory, [
|
|
68
68
|
* ['Alice', 25],
|
|
69
69
|
* ['Bob', 25],
|
|
70
70
|
* ['Bob', 30],
|
|
@@ -80,10 +80,10 @@ export const combine = <const OS extends NonEmptyArray<Observable<unknown>>>(
|
|
|
80
80
|
) as unknown as CombineObservableRefined<OS>;
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
* Alias for `combine
|
|
83
|
+
* Alias for `combine`.
|
|
84
84
|
* @see combine
|
|
85
85
|
*/
|
|
86
|
-
export const combineLatest = combine;
|
|
86
|
+
export const combineLatest = combine;
|
|
87
87
|
|
|
88
88
|
class CombineObservableClass<const A extends NonEmptyUnknownList>
|
|
89
89
|
extends SyncChildObservableClass<A, A>
|
|
@@ -103,8 +103,8 @@ class CombineObservableClass<const A extends NonEmptyUnknownList>
|
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
override tryUpdate(
|
|
107
|
-
if (this.parents.every((o) => o.
|
|
106
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
107
|
+
if (this.parents.every((o) => o.updateToken !== updateToken)) return; // all parents are skipped
|
|
108
108
|
|
|
109
109
|
const parentValues = this.parents.map((a) => a.getSnapshot());
|
|
110
110
|
|
|
@@ -113,7 +113,7 @@ class CombineObservableClass<const A extends NonEmptyUnknownList>
|
|
|
113
113
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
114
114
|
Arr.map(parentValues, (a) => a.value) as A;
|
|
115
115
|
|
|
116
|
-
this.setNext(nextValue,
|
|
116
|
+
this.setNext(nextValue, updateToken);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -154,9 +154,9 @@ class CombineObservableClass<const A extends NonEmptyUnknownList>
|
|
|
154
154
|
expectType<typeof _d, InitializedObservable<readonly [1, 2]>>('<=');
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
const r1 =
|
|
157
|
+
const r1 = source(1);
|
|
158
158
|
|
|
159
|
-
const r2 =
|
|
159
|
+
const r2 = source('a');
|
|
160
160
|
|
|
161
161
|
const _c = combine([r1, r2]);
|
|
162
162
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Optional, expectType } from 'ts-data-forge';
|
|
2
2
|
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import { source } from '../create/index.mjs';
|
|
4
4
|
import {
|
|
5
5
|
type MergeObservable,
|
|
6
6
|
type MergeObservableRefined,
|
|
7
7
|
type NonEmptyUnknownList,
|
|
8
8
|
type Observable,
|
|
9
9
|
type SyncChildObservable,
|
|
10
|
-
type
|
|
10
|
+
type UpdateToken,
|
|
11
11
|
type Wrap,
|
|
12
12
|
} from '../types/index.mjs';
|
|
13
13
|
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
* ```ts
|
|
24
24
|
* // Timeline:
|
|
25
25
|
* //
|
|
26
|
-
* // clicks$ c1
|
|
26
|
+
* // clicks$ c1 c2 c3
|
|
27
27
|
* // keys$ k1 k2 k3
|
|
28
28
|
* // events$ c1 k1 c2 k2 c3 k3
|
|
29
29
|
* //
|
|
@@ -38,25 +38,25 @@ import {
|
|
|
38
38
|
*
|
|
39
39
|
* const events$ = merge([clicks$, keys$]);
|
|
40
40
|
*
|
|
41
|
-
* const
|
|
41
|
+
* const valueHistory: string[] = [];
|
|
42
42
|
*
|
|
43
43
|
* events$.subscribe((event_) => {
|
|
44
|
-
*
|
|
44
|
+
* valueHistory.push(event_);
|
|
45
45
|
* });
|
|
46
46
|
*
|
|
47
47
|
* clicks$.next('c1');
|
|
48
48
|
*
|
|
49
|
-
* assert.deepStrictEqual(
|
|
49
|
+
* assert.deepStrictEqual(valueHistory, ['c1']);
|
|
50
50
|
*
|
|
51
51
|
* keys$.next('k1');
|
|
52
52
|
*
|
|
53
|
-
* assert.deepStrictEqual(
|
|
53
|
+
* assert.deepStrictEqual(valueHistory, ['c1', 'k1']);
|
|
54
54
|
*
|
|
55
55
|
* clicks$.next('c2');
|
|
56
56
|
*
|
|
57
57
|
* keys$.next('k2');
|
|
58
58
|
*
|
|
59
|
-
* assert.deepStrictEqual(
|
|
59
|
+
* assert.deepStrictEqual(valueHistory, ['c1', 'k1', 'c2', 'k2']);
|
|
60
60
|
* ```
|
|
61
61
|
*
|
|
62
62
|
* @note To improve code readability, consider using `createState` instead of `merge`,
|
|
@@ -79,10 +79,9 @@ class MergeObservableClass<const P extends NonEmptyUnknownList>
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
override tryUpdate(
|
|
82
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
83
83
|
const parentToUse = this.parents.find(
|
|
84
|
-
(o) =>
|
|
85
|
-
o.updaterSymbol === updaterSymbol && Optional.isSome(o.getSnapshot()),
|
|
84
|
+
(o) => o.updateToken === updateToken && Optional.isSome(o.getSnapshot()),
|
|
86
85
|
);
|
|
87
86
|
|
|
88
87
|
if (parentToUse === undefined) return;
|
|
@@ -91,7 +90,7 @@ class MergeObservableClass<const P extends NonEmptyUnknownList>
|
|
|
91
90
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
92
91
|
Optional.unwrap(parentToUse.getSnapshot()) as ArrayElement<P>;
|
|
93
92
|
|
|
94
|
-
this.setNext(nextValue,
|
|
93
|
+
this.setNext(nextValue, updateToken);
|
|
95
94
|
}
|
|
96
95
|
}
|
|
97
96
|
|
|
@@ -100,9 +99,9 @@ if (import.meta.vitest !== undefined) {
|
|
|
100
99
|
expect(1).toBe(1); // dummy
|
|
101
100
|
});
|
|
102
101
|
|
|
103
|
-
const r1 =
|
|
102
|
+
const r1 = source(1);
|
|
104
103
|
|
|
105
|
-
const r2 =
|
|
104
|
+
const r2 = source('a');
|
|
106
105
|
|
|
107
106
|
const _m = merge([r1, r2] as const);
|
|
108
107
|
|
package/src/core/combine/zip.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Arr, Optional, createQueue, expectType } from 'ts-data-forge';
|
|
2
2
|
import { SyncChildObservableClass } from '../class/index.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import { source } from '../create/index.mjs';
|
|
4
4
|
import { withInitialValue } from '../operators/index.mjs';
|
|
5
5
|
import {
|
|
6
6
|
type InitializedObservable,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type Observable,
|
|
10
10
|
type SyncChildObservable,
|
|
11
11
|
type TupleToQueueTuple,
|
|
12
|
-
type
|
|
12
|
+
type UpdateToken,
|
|
13
13
|
type Wrap,
|
|
14
14
|
type ZipObservable,
|
|
15
15
|
type ZipObservableRefined,
|
|
@@ -28,38 +28,39 @@ import {
|
|
|
28
28
|
* ```ts
|
|
29
29
|
* // Timeline:
|
|
30
30
|
* //
|
|
31
|
-
* // letters$ '
|
|
31
|
+
* // letters$ 'A' 'B' 'C'
|
|
32
32
|
* // numbers$ 1 2 3
|
|
33
|
-
* // zipped$ ['
|
|
33
|
+
* // zipped$ ['A',1] ['B',2] ['C',3]
|
|
34
34
|
* //
|
|
35
35
|
* // Explanation:
|
|
36
36
|
* // - zip pairs values by their index from multiple sources
|
|
37
37
|
* // - Waits for all sources to emit at the same index
|
|
38
38
|
* // - Completes when any source completes
|
|
39
39
|
*
|
|
40
|
-
* const letters
|
|
40
|
+
* const [letters$, setLetter] = createState<string>('A');
|
|
41
41
|
*
|
|
42
|
-
* const numbers
|
|
42
|
+
* const [numbers$, setNumber] = createState<number>(1);
|
|
43
43
|
*
|
|
44
44
|
* const zipped$ = zip([letters$, numbers$]);
|
|
45
45
|
*
|
|
46
|
-
* const
|
|
46
|
+
* const valueHistory: (readonly [string, number])[] = [];
|
|
47
47
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* ([letter, num]) => {
|
|
51
|
-
* mut_history.push([letter, num]);
|
|
52
|
-
* },
|
|
53
|
-
* () => {
|
|
54
|
-
* resolve();
|
|
55
|
-
* },
|
|
56
|
-
* );
|
|
48
|
+
* zipped$.subscribe(([letter, num]) => {
|
|
49
|
+
* valueHistory.push([letter, num]);
|
|
57
50
|
* });
|
|
58
51
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
52
|
+
* for (const letter of ['B', 'C']) {
|
|
53
|
+
* setLetter(letter);
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* for (const num of [2, 3]) {
|
|
57
|
+
* setNumber(num);
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* assert.deepStrictEqual(valueHistory, [
|
|
61
|
+
* ['A', 1],
|
|
62
|
+
* ['B', 2],
|
|
63
|
+
* ['C', 3],
|
|
63
64
|
* ]);
|
|
64
65
|
* ```
|
|
65
66
|
*/
|
|
@@ -93,13 +94,13 @@ class ZipObservableClass<const A extends NonEmptyUnknownList>
|
|
|
93
94
|
) satisfies TupleToQueueTuple<A>;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
override tryUpdate(
|
|
97
|
+
override tryUpdate(updateToken: UpdateToken): void {
|
|
97
98
|
const queues = this.#queues;
|
|
98
99
|
|
|
99
100
|
for (const [index, par] of this.parents.entries()) {
|
|
100
101
|
const sn = par.getSnapshot();
|
|
101
102
|
|
|
102
|
-
if (par.
|
|
103
|
+
if (par.updateToken === updateToken && Optional.isSome(sn)) {
|
|
103
104
|
queues[index]?.enqueue(sn.value);
|
|
104
105
|
}
|
|
105
106
|
}
|
|
@@ -109,7 +110,7 @@ class ZipObservableClass<const A extends NonEmptyUnknownList>
|
|
|
109
110
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
110
111
|
Arr.map(queues, (q) => Optional.unwrap(q.dequeue())) as A;
|
|
111
112
|
|
|
112
|
-
this.setNext(nextValue,
|
|
113
|
+
this.setNext(nextValue, updateToken);
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
}
|
|
@@ -154,9 +155,9 @@ if (import.meta.vitest !== undefined) {
|
|
|
154
155
|
expectType<typeof _d, InitializedObservable<readonly [1, 2]>>('<=');
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
const r1 =
|
|
158
|
+
const r1 = source(1);
|
|
158
159
|
|
|
159
|
-
const r2 =
|
|
160
|
+
const r2 = source('a');
|
|
160
161
|
|
|
161
162
|
const _z = zip([r1, r2] as const);
|
|
162
163
|
|