synstate 2.0.2 → 3.0.1

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.
Files changed (230) hide show
  1. package/README.md +2 -2
  2. package/dist/core/base/create-child-observable.d.mts +65 -0
  3. package/dist/core/base/create-child-observable.d.mts.map +1 -0
  4. package/dist/core/base/create-child-observable.mjs +181 -0
  5. package/dist/core/base/create-child-observable.mjs.map +1 -0
  6. package/dist/core/base/create-observable-base.d.mts +82 -0
  7. package/dist/core/base/create-observable-base.d.mts.map +1 -0
  8. package/dist/core/base/create-observable-base.mjs +156 -0
  9. package/dist/core/base/create-observable-base.mjs.map +1 -0
  10. package/dist/core/base/create-root-observable.d.mts +37 -0
  11. package/dist/core/base/create-root-observable.d.mts.map +1 -0
  12. package/dist/core/base/create-root-observable.mjs +50 -0
  13. package/dist/core/base/create-root-observable.mjs.map +1 -0
  14. package/dist/core/base/index.d.mts +3 -0
  15. package/dist/core/base/index.d.mts.map +1 -0
  16. package/dist/core/base/index.mjs +3 -0
  17. package/dist/core/base/index.mjs.map +1 -0
  18. package/dist/core/combine/combine.d.mts.map +1 -1
  19. package/dist/core/combine/combine.mjs +132 -39
  20. package/dist/core/combine/combine.mjs.map +1 -1
  21. package/dist/core/combine/index.mjs +4 -4
  22. package/dist/core/combine/index.mjs.map +1 -1
  23. package/dist/core/combine/merge.d.mts.map +1 -1
  24. package/dist/core/combine/merge.mjs +78 -29
  25. package/dist/core/combine/merge.mjs.map +1 -1
  26. package/dist/core/combine/zip.d.mts.map +1 -1
  27. package/dist/core/combine/zip.mjs +124 -42
  28. package/dist/core/combine/zip.mjs.map +1 -1
  29. package/dist/core/create/counter.d.mts.map +1 -1
  30. package/dist/core/create/counter.mjs +82 -57
  31. package/dist/core/create/counter.mjs.map +1 -1
  32. package/dist/core/create/from-abortable-promise.d.mts.map +1 -1
  33. package/dist/core/create/from-abortable-promise.mjs +62 -36
  34. package/dist/core/create/from-abortable-promise.mjs.map +1 -1
  35. package/dist/core/create/from-promise.d.mts.map +1 -1
  36. package/dist/core/create/from-promise.mjs +67 -25
  37. package/dist/core/create/from-promise.mjs.map +1 -1
  38. package/dist/core/create/from-subscribable.d.mts.map +1 -1
  39. package/dist/core/create/from-subscribable.mjs +72 -28
  40. package/dist/core/create/from-subscribable.mjs.map +1 -1
  41. package/dist/core/create/index.mjs +8 -8
  42. package/dist/core/create/index.mjs.map +1 -1
  43. package/dist/core/create/just.d.mts.map +1 -1
  44. package/dist/core/create/just.mjs +36 -12
  45. package/dist/core/create/just.mjs.map +1 -1
  46. package/dist/core/create/source.mjs +15 -20
  47. package/dist/core/create/source.mjs.map +1 -1
  48. package/dist/core/create/timer.d.mts.map +1 -1
  49. package/dist/core/create/timer.mjs +67 -44
  50. package/dist/core/create/timer.mjs.map +1 -1
  51. package/dist/core/index.d.mts +0 -1
  52. package/dist/core/index.d.mts.map +1 -1
  53. package/dist/core/index.mjs +6 -44
  54. package/dist/core/index.mjs.map +1 -1
  55. package/dist/core/operators/audit.d.mts.map +1 -1
  56. package/dist/core/operators/audit.mjs +127 -48
  57. package/dist/core/operators/audit.mjs.map +1 -1
  58. package/dist/core/operators/debounce.d.mts.map +1 -1
  59. package/dist/core/operators/debounce.mjs +115 -45
  60. package/dist/core/operators/debounce.mjs.map +1 -1
  61. package/dist/core/operators/filter.d.mts.map +1 -1
  62. package/dist/core/operators/filter.mjs +43 -32
  63. package/dist/core/operators/filter.mjs.map +1 -1
  64. package/dist/core/operators/index.mjs +18 -18
  65. package/dist/core/operators/index.mjs.map +1 -1
  66. package/dist/core/operators/map.d.mts.map +1 -1
  67. package/dist/core/operators/map.mjs +79 -38
  68. package/dist/core/operators/map.mjs.map +1 -1
  69. package/dist/core/operators/merge-map.d.mts.map +1 -1
  70. package/dist/core/operators/merge-map.mjs +131 -47
  71. package/dist/core/operators/merge-map.mjs.map +1 -1
  72. package/dist/core/operators/pairwise.d.mts.map +1 -1
  73. package/dist/core/operators/pairwise.mjs +79 -31
  74. package/dist/core/operators/pairwise.mjs.map +1 -1
  75. package/dist/core/operators/scan.d.mts.map +1 -1
  76. package/dist/core/operators/scan.mjs +64 -25
  77. package/dist/core/operators/scan.mjs.map +1 -1
  78. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  79. package/dist/core/operators/skip-if-no-change.mjs +93 -39
  80. package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
  81. package/dist/core/operators/skip-until.d.mts.map +1 -1
  82. package/dist/core/operators/skip-until.mjs +73 -32
  83. package/dist/core/operators/skip-until.mjs.map +1 -1
  84. package/dist/core/operators/skip-while.d.mts.map +1 -1
  85. package/dist/core/operators/skip-while.mjs +85 -38
  86. package/dist/core/operators/skip-while.mjs.map +1 -1
  87. package/dist/core/operators/switch-map.d.mts.map +1 -1
  88. package/dist/core/operators/switch-map.mjs +121 -40
  89. package/dist/core/operators/switch-map.mjs.map +1 -1
  90. package/dist/core/operators/take-until.d.mts.map +1 -1
  91. package/dist/core/operators/take-until.mjs +71 -36
  92. package/dist/core/operators/take-until.mjs.map +1 -1
  93. package/dist/core/operators/take-while.d.mts.map +1 -1
  94. package/dist/core/operators/take-while.mjs +103 -35
  95. package/dist/core/operators/take-while.mjs.map +1 -1
  96. package/dist/core/operators/throttle.d.mts.map +1 -1
  97. package/dist/core/operators/throttle.mjs +109 -47
  98. package/dist/core/operators/throttle.mjs.map +1 -1
  99. package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
  100. package/dist/core/operators/with-buffered-from.mjs +93 -46
  101. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  102. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  103. package/dist/core/operators/with-current-value-from.mjs +84 -34
  104. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  105. package/dist/core/operators/with-initial-value.d.mts.map +1 -1
  106. package/dist/core/operators/with-initial-value.mjs +68 -21
  107. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  108. package/dist/core/predefined/index.mjs +2 -12
  109. package/dist/core/predefined/index.mjs.map +1 -1
  110. package/dist/core/predefined/operators/attach-index.mjs +55 -7
  111. package/dist/core/predefined/operators/attach-index.mjs.map +1 -1
  112. package/dist/core/predefined/operators/index.mjs +12 -12
  113. package/dist/core/predefined/operators/index.mjs.map +1 -1
  114. package/dist/core/predefined/operators/map-optional.mjs +48 -5
  115. package/dist/core/predefined/operators/map-optional.mjs.map +1 -1
  116. package/dist/core/predefined/operators/map-result-err.mjs +48 -5
  117. package/dist/core/predefined/operators/map-result-err.mjs.map +1 -1
  118. package/dist/core/predefined/operators/map-result-ok.mjs +48 -5
  119. package/dist/core/predefined/operators/map-result-ok.mjs.map +1 -1
  120. package/dist/core/predefined/operators/map-to.mjs +41 -6
  121. package/dist/core/predefined/operators/map-to.mjs.map +1 -1
  122. package/dist/core/predefined/operators/pluck.mjs +45 -7
  123. package/dist/core/predefined/operators/pluck.mjs.map +1 -1
  124. package/dist/core/predefined/operators/skip.mjs +49 -6
  125. package/dist/core/predefined/operators/skip.mjs.map +1 -1
  126. package/dist/core/predefined/operators/take.mjs +42 -6
  127. package/dist/core/predefined/operators/take.mjs.map +1 -1
  128. package/dist/core/predefined/operators/unwrap-optional.mjs +43 -8
  129. package/dist/core/predefined/operators/unwrap-optional.mjs.map +1 -1
  130. package/dist/core/predefined/operators/unwrap-result-err.mjs +41 -5
  131. package/dist/core/predefined/operators/unwrap-result-err.mjs.map +1 -1
  132. package/dist/core/predefined/operators/unwrap-result-ok.mjs +43 -8
  133. package/dist/core/predefined/operators/unwrap-result-ok.mjs.map +1 -1
  134. package/dist/core/types/id.mjs +2 -2
  135. package/dist/core/types/id.mjs.map +1 -1
  136. package/dist/core/types/index.mjs +7 -3
  137. package/dist/core/types/index.mjs.map +1 -1
  138. package/dist/core/types/observable-family.mjs +14 -2
  139. package/dist/core/types/observable-family.mjs.map +1 -1
  140. package/dist/core/types/observable-kind.mjs +2 -2
  141. package/dist/core/types/observable-kind.mjs.map +1 -1
  142. package/dist/core/types/observable.mjs +14 -6
  143. package/dist/core/types/observable.mjs.map +1 -1
  144. package/dist/core/types/timer.mjs +2 -2
  145. package/dist/core/types/timer.mjs.map +1 -1
  146. package/dist/core/types/types.mjs +9 -2
  147. package/dist/core/types/types.mjs.map +1 -1
  148. package/dist/core/utils/id-maker.mjs +10 -11
  149. package/dist/core/utils/id-maker.mjs.map +1 -1
  150. package/dist/core/utils/index.mjs +5 -5
  151. package/dist/core/utils/index.mjs.map +1 -1
  152. package/dist/core/utils/max-depth.mjs +7 -4
  153. package/dist/core/utils/max-depth.mjs.map +1 -1
  154. package/dist/core/utils/observable-utils.mjs +4 -7
  155. package/dist/core/utils/observable-utils.mjs.map +1 -1
  156. package/dist/core/utils/utils.mjs +32 -28
  157. package/dist/core/utils/utils.mjs.map +1 -1
  158. package/dist/entry-point.mjs +2 -49
  159. package/dist/entry-point.mjs.map +1 -1
  160. package/dist/index.mjs +3 -49
  161. package/dist/index.mjs.map +1 -1
  162. package/dist/utils/collect-to-array.mjs +8 -13
  163. package/dist/utils/collect-to-array.mjs.map +1 -1
  164. package/dist/utils/create-boolean-state.mjs +47 -22
  165. package/dist/utils/create-boolean-state.mjs.map +1 -1
  166. package/dist/utils/create-event-emitter.mjs +67 -19
  167. package/dist/utils/create-event-emitter.mjs.map +1 -1
  168. package/dist/utils/create-reducer.mjs +54 -16
  169. package/dist/utils/create-reducer.mjs.map +1 -1
  170. package/dist/utils/create-state.mjs +54 -27
  171. package/dist/utils/create-state.mjs.map +1 -1
  172. package/dist/utils/index.mjs +6 -6
  173. package/dist/utils/index.mjs.map +1 -1
  174. package/package.json +17 -19
  175. package/src/core/{class → base}/circular-dependency-comparison.test.mts +28 -18
  176. package/src/core/base/circular-dependency.test.mts +322 -0
  177. package/src/core/base/create-child-observable.mts +340 -0
  178. package/src/core/base/create-observable-base.mts +358 -0
  179. package/src/core/base/create-root-observable.mts +102 -0
  180. package/src/core/base/index.mts +2 -0
  181. package/src/core/{class/observable.class.test.mts → base/observable-base.test.mts} +25 -17
  182. package/src/core/combine/combine.mts +51 -53
  183. package/src/core/combine/merge.mts +21 -23
  184. package/src/core/combine/zip.mts +54 -58
  185. package/src/core/create/counter.mts +42 -59
  186. package/src/core/create/from-abortable-promise.mts +38 -45
  187. package/src/core/create/from-promise.mts +25 -28
  188. package/src/core/create/from-subscribable.mts +23 -27
  189. package/src/core/create/just.mts +5 -4
  190. package/src/core/create/source.mts +11 -17
  191. package/src/core/create/timer.mts +32 -47
  192. package/src/core/index.mts +0 -1
  193. package/src/core/operators/audit.mts +43 -57
  194. package/src/core/operators/debounce.mts +35 -45
  195. package/src/core/operators/filter.mts +31 -39
  196. package/src/core/operators/map.mts +32 -40
  197. package/src/core/operators/merge-map.mts +44 -59
  198. package/src/core/operators/pairwise.mts +30 -33
  199. package/src/core/operators/scan.mts +24 -35
  200. package/src/core/operators/skip-if-no-change.mts +31 -36
  201. package/src/core/operators/skip-until.mts +32 -35
  202. package/src/core/operators/skip-while.mts +31 -40
  203. package/src/core/operators/switch-map.mts +36 -48
  204. package/src/core/operators/take-until.mts +31 -29
  205. package/src/core/operators/take-while.mts +33 -41
  206. package/src/core/operators/throttle.mts +37 -51
  207. package/src/core/operators/with-buffered-from.mts +35 -30
  208. package/src/core/operators/with-current-value-from.mts +23 -28
  209. package/src/core/operators/with-initial-value.mts +21 -22
  210. package/dist/core/class/child-observable-class.d.mts +0 -39
  211. package/dist/core/class/child-observable-class.d.mts.map +0 -1
  212. package/dist/core/class/child-observable-class.mjs +0 -173
  213. package/dist/core/class/child-observable-class.mjs.map +0 -1
  214. package/dist/core/class/index.d.mts +0 -4
  215. package/dist/core/class/index.d.mts.map +0 -1
  216. package/dist/core/class/index.mjs +0 -4
  217. package/dist/core/class/index.mjs.map +0 -1
  218. package/dist/core/class/observable-base-class.d.mts +0 -28
  219. package/dist/core/class/observable-base-class.d.mts.map +0 -1
  220. package/dist/core/class/observable-base-class.mjs +0 -120
  221. package/dist/core/class/observable-base-class.mjs.map +0 -1
  222. package/dist/core/class/root-observable-class.d.mts +0 -14
  223. package/dist/core/class/root-observable-class.d.mts.map +0 -1
  224. package/dist/core/class/root-observable-class.mjs +0 -43
  225. package/dist/core/class/root-observable-class.mjs.map +0 -1
  226. package/src/core/class/child-observable-class.mts +0 -286
  227. package/src/core/class/circular-dependency.test.mts +0 -251
  228. package/src/core/class/index.mts +0 -3
  229. package/src/core/class/observable-base-class.mts +0 -187
  230. 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
- });
@@ -1,3 +0,0 @@
1
- export * from './child-observable-class.mjs';
2
- export * from './observable-base-class.mjs';
3
- export * from './root-observable-class.mjs';
@@ -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
- }