synstate 2.0.1 → 3.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 +2 -2
- package/dist/core/base/create-child-observable.d.mts +65 -0
- package/dist/core/base/create-child-observable.d.mts.map +1 -0
- package/dist/core/base/create-child-observable.mjs +157 -0
- package/dist/core/base/create-child-observable.mjs.map +1 -0
- package/dist/core/base/create-observable-base.d.mts +82 -0
- package/dist/core/base/create-observable-base.d.mts.map +1 -0
- package/dist/core/base/create-observable-base.mjs +160 -0
- package/dist/core/base/create-observable-base.mjs.map +1 -0
- package/dist/core/base/create-root-observable.d.mts +37 -0
- package/dist/core/base/create-root-observable.d.mts.map +1 -0
- package/dist/core/base/create-root-observable.mjs +36 -0
- package/dist/core/base/create-root-observable.mjs.map +1 -0
- package/dist/core/base/index.d.mts +3 -0
- package/dist/core/base/index.d.mts.map +1 -0
- package/dist/core/base/index.mjs +3 -0
- package/dist/core/{class → base}/index.mjs.map +1 -1
- package/dist/core/combine/combine.d.mts.map +1 -1
- package/dist/core/combine/combine.mjs +28 -27
- 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 +11 -13
- 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 +32 -31
- 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 +39 -50
- 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 +33 -32
- 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 +12 -12
- 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 +10 -10
- 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 +5 -4
- package/dist/core/create/just.mjs.map +1 -1
- package/dist/core/create/source.mjs +11 -12
- 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 +32 -38
- package/dist/core/create/timer.mjs.map +1 -1
- package/dist/core/index.d.mts +0 -1
- package/dist/core/index.d.mts.map +1 -1
- package/dist/core/index.mjs +0 -3
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/operators/audit.d.mts.map +1 -1
- package/dist/core/operators/audit.mjs +28 -36
- package/dist/core/operators/audit.mjs.map +1 -1
- package/dist/core/operators/debounce.d.mts.map +1 -1
- package/dist/core/operators/debounce.mjs +26 -33
- 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 +23 -27
- package/dist/core/operators/filter.mjs.map +1 -1
- package/dist/core/operators/map.d.mts.map +1 -1
- package/dist/core/operators/map.mjs +21 -28
- package/dist/core/operators/map.mjs.map +1 -1
- package/dist/core/operators/merge-map.d.mts.map +1 -1
- package/dist/core/operators/merge-map.mjs +30 -39
- 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 +22 -24
- 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 +13 -18
- 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 +22 -26
- 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 +23 -24
- 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 +27 -32
- package/dist/core/operators/skip-while.mjs.map +1 -1
- package/dist/core/operators/switch-map.d.mts.map +1 -1
- package/dist/core/operators/switch-map.mjs +28 -34
- 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 +22 -22
- 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 +25 -29
- package/dist/core/operators/take-while.mjs.map +1 -1
- package/dist/core/operators/throttle.d.mts.map +1 -1
- package/dist/core/operators/throttle.mjs +27 -35
- 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 +24 -24
- 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 +20 -25
- 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 +12 -15
- package/dist/core/operators/with-initial-value.mjs.map +1 -1
- package/dist/entry-point.mjs +0 -3
- package/dist/entry-point.mjs.map +1 -1
- package/dist/index.mjs +0 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +25 -18
- package/src/core/{class → base}/circular-dependency-comparison.test.mts +28 -18
- package/src/core/base/circular-dependency.test.mts +322 -0
- package/src/core/base/create-child-observable.mts +340 -0
- package/src/core/base/create-observable-base.mts +358 -0
- package/src/core/base/create-root-observable.mts +102 -0
- package/src/core/base/index.mts +2 -0
- package/src/core/{class/observable.class.test.mts → base/observable-base.test.mts} +25 -17
- package/src/core/combine/combine.mts +51 -53
- package/src/core/combine/merge.mts +21 -23
- package/src/core/combine/zip.mts +54 -58
- package/src/core/create/counter.mts +42 -59
- package/src/core/create/from-abortable-promise.mts +38 -45
- package/src/core/create/from-promise.mts +25 -28
- package/src/core/create/from-subscribable.mts +23 -27
- package/src/core/create/just.mts +5 -4
- package/src/core/create/source.mts +11 -17
- package/src/core/create/timer.mts +32 -47
- package/src/core/index.mts +0 -1
- package/src/core/operators/audit.mts +43 -57
- package/src/core/operators/debounce.mts +35 -45
- package/src/core/operators/filter.mts +31 -39
- package/src/core/operators/map.mts +32 -40
- package/src/core/operators/merge-map.mts +44 -59
- package/src/core/operators/pairwise.mts +30 -33
- package/src/core/operators/scan.mts +24 -35
- package/src/core/operators/skip-if-no-change.mts +31 -36
- package/src/core/operators/skip-until.mts +32 -35
- package/src/core/operators/skip-while.mts +31 -40
- package/src/core/operators/switch-map.mts +36 -48
- package/src/core/operators/take-until.mts +31 -29
- package/src/core/operators/take-while.mts +33 -41
- package/src/core/operators/throttle.mts +37 -51
- package/src/core/operators/with-buffered-from.mts +35 -30
- package/src/core/operators/with-current-value-from.mts +23 -28
- package/src/core/operators/with-initial-value.mts +21 -22
- package/dist/core/class/child-observable-class.d.mts +0 -39
- package/dist/core/class/child-observable-class.d.mts.map +0 -1
- package/dist/core/class/child-observable-class.mjs +0 -173
- package/dist/core/class/child-observable-class.mjs.map +0 -1
- package/dist/core/class/index.d.mts +0 -4
- package/dist/core/class/index.d.mts.map +0 -1
- package/dist/core/class/index.mjs +0 -4
- package/dist/core/class/observable-base-class.d.mts +0 -28
- package/dist/core/class/observable-base-class.d.mts.map +0 -1
- package/dist/core/class/observable-base-class.mjs +0 -120
- package/dist/core/class/observable-base-class.mjs.map +0 -1
- package/dist/core/class/root-observable-class.d.mts +0 -14
- package/dist/core/class/root-observable-class.d.mts.map +0 -1
- package/dist/core/class/root-observable-class.mjs +0 -43
- package/dist/core/class/root-observable-class.mjs.map +0 -1
- package/src/core/class/child-observable-class.mts +0 -286
- package/src/core/class/circular-dependency.test.mts +0 -251
- package/src/core/class/index.mts +0 -3
- package/src/core/class/observable-base-class.mts +0 -187
- package/src/core/class/root-observable-class.mts +0 -73
|
@@ -1,251 +0,0 @@
|
|
|
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
|
-
});
|
package/src/core/class/index.mts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { Arr, Optional } from 'ts-data-forge';
|
|
2
|
-
import { type MutableMap } from 'ts-type-forge';
|
|
3
|
-
import {
|
|
4
|
-
type ChildObservable,
|
|
5
|
-
type InitializedObservable,
|
|
6
|
-
type Observable,
|
|
7
|
-
type ObservableBase,
|
|
8
|
-
type Operator,
|
|
9
|
-
type Subscriber,
|
|
10
|
-
type SubscriberId,
|
|
11
|
-
type Subscription,
|
|
12
|
-
type UpdateToken,
|
|
13
|
-
type WithInitialValueOperator,
|
|
14
|
-
} from '../types/index.mjs';
|
|
15
|
-
import {
|
|
16
|
-
issueObservableId,
|
|
17
|
-
issueSubscriberId,
|
|
18
|
-
issueUpdateToken,
|
|
19
|
-
toSubscriber,
|
|
20
|
-
} from '../utils/index.mjs';
|
|
21
|
-
|
|
22
|
-
export class ObservableBaseClass<
|
|
23
|
-
A,
|
|
24
|
-
Kind extends ObservableBase<A>['kind'],
|
|
25
|
-
Depth extends ObservableBase<A>['depth'],
|
|
26
|
-
> implements ObservableBase<A> {
|
|
27
|
-
#mut_children: readonly ChildObservable<unknown>[];
|
|
28
|
-
readonly #subscribers: MutableMap<SubscriberId, Subscriber<A>>;
|
|
29
|
-
#mut_currentValue: ReturnType<ObservableBase<A>['getSnapshot']>;
|
|
30
|
-
#mut_isCompleted: ObservableBase<A>['isCompleted'];
|
|
31
|
-
#mut_updateToken: ObservableBase<A>['updateToken'];
|
|
32
|
-
readonly id;
|
|
33
|
-
readonly kind: Kind;
|
|
34
|
-
readonly depth: Depth;
|
|
35
|
-
|
|
36
|
-
constructor({
|
|
37
|
-
kind,
|
|
38
|
-
depth,
|
|
39
|
-
initialValue,
|
|
40
|
-
}: Readonly<{
|
|
41
|
-
kind: Kind;
|
|
42
|
-
depth: Depth;
|
|
43
|
-
initialValue: ReturnType<ObservableBase<A>['getSnapshot']>;
|
|
44
|
-
}>) {
|
|
45
|
-
this.kind = kind;
|
|
46
|
-
|
|
47
|
-
this.depth = depth;
|
|
48
|
-
|
|
49
|
-
this.id = issueObservableId();
|
|
50
|
-
|
|
51
|
-
this.#mut_currentValue = initialValue;
|
|
52
|
-
|
|
53
|
-
this.#mut_children = [];
|
|
54
|
-
|
|
55
|
-
this.#subscribers = new Map<SubscriberId, Subscriber<A>>();
|
|
56
|
-
|
|
57
|
-
this.#mut_isCompleted = false;
|
|
58
|
-
|
|
59
|
-
this.#mut_updateToken = issueUpdateToken();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
#addSubscriber(s: Subscriber<A>): SubscriberId {
|
|
63
|
-
// return the id of added subscriber
|
|
64
|
-
const id = issueSubscriberId();
|
|
65
|
-
|
|
66
|
-
this.#subscribers.set(id, s);
|
|
67
|
-
|
|
68
|
-
return id;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
#removeSubscriber(id: SubscriberId): void {
|
|
72
|
-
this.#subscribers.delete(id);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
addChild<B>(child: ChildObservable<B>): void {
|
|
76
|
-
this.#mut_children = Arr.toPushed(
|
|
77
|
-
this.#mut_children,
|
|
78
|
-
|
|
79
|
-
child as ChildObservable<unknown>,
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
getSnapshot(): ReturnType<ObservableBase<A>['getSnapshot']> {
|
|
84
|
-
return this.#mut_currentValue;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
protected getCurrentValue(): ReturnType<ObservableBase<A>['getSnapshot']> {
|
|
88
|
-
return this.#mut_currentValue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
get isCompleted(): boolean {
|
|
92
|
-
return this.#mut_isCompleted;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
get updateToken(): UpdateToken {
|
|
96
|
-
return this.#mut_updateToken;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
get hasSubscriber(): boolean {
|
|
100
|
-
return this.#subscribers.size > 0;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
get hasChild(): boolean {
|
|
104
|
-
return Arr.isNonEmpty(this.#mut_children);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
hasActiveChild(): boolean {
|
|
108
|
-
return this.#mut_children.some((c) => !c.isCompleted);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
protected setNext(nextValue: A, updateToken: UpdateToken): void {
|
|
112
|
-
this.#mut_updateToken = updateToken;
|
|
113
|
-
|
|
114
|
-
this.#mut_currentValue = Optional.some(nextValue);
|
|
115
|
-
|
|
116
|
-
for (const s of this.#subscribers.values()) {
|
|
117
|
-
s.onNext(nextValue);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
122
|
-
tryUpdate(_updateToken: UpdateToken): void {
|
|
123
|
-
throw new Error('not implemented');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
tryComplete(): void {
|
|
127
|
-
if (!this.hasSubscriber && !this.hasActiveChild()) {
|
|
128
|
-
this.complete();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
complete(): void {
|
|
133
|
-
if (this.isCompleted) return; // terminate only once
|
|
134
|
-
|
|
135
|
-
// change state
|
|
136
|
-
this.#mut_isCompleted = true;
|
|
137
|
-
|
|
138
|
-
// run subscribers for the current value
|
|
139
|
-
for (const s of this.#subscribers.values()) {
|
|
140
|
-
s.onComplete();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// remove all subscribers
|
|
144
|
-
this.#subscribers.clear();
|
|
145
|
-
|
|
146
|
-
// propagate to children
|
|
147
|
-
for (const o of this.#mut_children) {
|
|
148
|
-
o.tryComplete();
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
pipe<B>(operator: WithInitialValueOperator<A, B>): InitializedObservable<B>;
|
|
153
|
-
pipe<B>(operator: Operator<A, B>): Observable<B>;
|
|
154
|
-
pipe<B>(operator: Operator<A, B>): Observable<B> {
|
|
155
|
-
return operator(
|
|
156
|
-
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
157
|
-
this as unknown as InitializedObservable<A>,
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
subscribe(onNext: (v: A) => void, onComplete?: () => void): Subscription {
|
|
162
|
-
// first emit
|
|
163
|
-
const curr = this.getSnapshot();
|
|
164
|
-
|
|
165
|
-
if (Optional.isSome(curr)) {
|
|
166
|
-
onNext(curr.value);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (this.isCompleted) {
|
|
170
|
-
if (onComplete !== undefined) {
|
|
171
|
-
onComplete();
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return { unsubscribe: () => {} };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const id: SubscriberId = this.#addSubscriber(
|
|
178
|
-
toSubscriber(onNext, onComplete),
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
unsubscribe: () => {
|
|
183
|
-
this.#removeSubscriber(id);
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Arr, Optional } from 'ts-data-forge';
|
|
2
|
-
import { type MutableSet } from 'ts-type-forge';
|
|
3
|
-
import {
|
|
4
|
-
isRootObservable,
|
|
5
|
-
type ChildObservable,
|
|
6
|
-
type ObservableId,
|
|
7
|
-
type RootObservable,
|
|
8
|
-
} from '../types/index.mjs';
|
|
9
|
-
import { binarySearch, issueUpdateToken } from '../utils/index.mjs';
|
|
10
|
-
import { ObservableBaseClass } from './observable-base-class.mjs';
|
|
11
|
-
|
|
12
|
-
export class RootObservableClass<A>
|
|
13
|
-
extends ObservableBaseClass<A, 'root', 0>
|
|
14
|
-
implements RootObservable<A>
|
|
15
|
-
{
|
|
16
|
-
#mut_propagationOrder: readonly ChildObservable<unknown>[];
|
|
17
|
-
protected readonly _descendantsIdSet: MutableSet<ObservableId>;
|
|
18
|
-
|
|
19
|
-
constructor({
|
|
20
|
-
initialValue,
|
|
21
|
-
}: Readonly<{
|
|
22
|
-
initialValue: Optional<A>;
|
|
23
|
-
}>) {
|
|
24
|
-
super({
|
|
25
|
-
kind: 'root',
|
|
26
|
-
depth: 0,
|
|
27
|
-
initialValue,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
this.#mut_propagationOrder = [];
|
|
31
|
-
|
|
32
|
-
this._descendantsIdSet = new Set<ObservableId>();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
addDescendant<B>(child: ChildObservable<B>): void {
|
|
36
|
-
if (this._descendantsIdSet.has(child.id)) return;
|
|
37
|
-
|
|
38
|
-
this._descendantsIdSet.add(child.id);
|
|
39
|
-
|
|
40
|
-
const insertPos = binarySearch(
|
|
41
|
-
this.#mut_propagationOrder.map((a) => a.depth),
|
|
42
|
-
child.depth,
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
this.#mut_propagationOrder = Arr.toInserted(
|
|
46
|
-
this.#mut_propagationOrder,
|
|
47
|
-
insertPos,
|
|
48
|
-
child,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
startUpdate(nextValue: A): void {
|
|
53
|
-
const updateToken = issueUpdateToken();
|
|
54
|
-
|
|
55
|
-
this.setNext(nextValue, updateToken);
|
|
56
|
-
|
|
57
|
-
for (const p of this.#mut_propagationOrder) {
|
|
58
|
-
p.tryUpdate(updateToken);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (import.meta.vitest !== undefined) {
|
|
64
|
-
test('isRootObservable', () => {
|
|
65
|
-
assert.isTrue(
|
|
66
|
-
isRootObservable(
|
|
67
|
-
new RootObservableClass({
|
|
68
|
-
initialValue: Optional.some(0),
|
|
69
|
-
}),
|
|
70
|
-
),
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
}
|