synstate 3.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. package/README.md +13 -8
  2. package/dist/core/authoring.d.mts +7 -0
  3. package/dist/core/authoring.d.mts.map +1 -0
  4. package/dist/core/authoring.mjs +7 -0
  5. package/dist/core/authoring.mjs.map +1 -0
  6. package/dist/core/class/child-observable-class.d.mts +93 -0
  7. package/dist/core/class/child-observable-class.d.mts.map +1 -0
  8. package/dist/core/class/child-observable-class.mjs +248 -0
  9. package/dist/core/class/child-observable-class.mjs.map +1 -0
  10. package/dist/core/class/create-child-observable.d.mts +159 -0
  11. package/dist/core/class/create-child-observable.d.mts.map +1 -0
  12. package/dist/core/class/create-child-observable.mjs +183 -0
  13. package/dist/core/class/create-child-observable.mjs.map +1 -0
  14. package/dist/core/class/create-root-observable.d.mts +37 -0
  15. package/dist/core/class/create-root-observable.d.mts.map +1 -0
  16. package/dist/core/class/create-root-observable.mjs +52 -0
  17. package/dist/core/class/create-root-observable.mjs.map +1 -0
  18. package/dist/core/class/index.d.mts +9 -0
  19. package/dist/core/class/index.d.mts.map +1 -0
  20. package/dist/core/class/index.mjs +9 -0
  21. package/dist/core/class/index.mjs.map +1 -0
  22. package/dist/core/class/manager-observable-class.d.mts +26 -0
  23. package/dist/core/class/manager-observable-class.d.mts.map +1 -0
  24. package/dist/core/class/manager-observable-class.mjs +116 -0
  25. package/dist/core/class/manager-observable-class.mjs.map +1 -0
  26. package/dist/core/class/observable-base-class.d.mts +97 -0
  27. package/dist/core/class/observable-base-class.d.mts.map +1 -0
  28. package/dist/core/class/observable-base-class.mjs +219 -0
  29. package/dist/core/class/observable-base-class.mjs.map +1 -0
  30. package/dist/core/class/observable-node.d.mts +40 -0
  31. package/dist/core/class/observable-node.d.mts.map +1 -0
  32. package/dist/core/class/observable-node.mjs +10 -0
  33. package/dist/core/class/observable-node.mjs.map +1 -0
  34. package/dist/core/class/propagation-state.d.mts +24 -0
  35. package/dist/core/class/propagation-state.d.mts.map +1 -0
  36. package/dist/core/class/propagation-state.mjs +66 -0
  37. package/dist/core/class/propagation-state.mjs.map +1 -0
  38. package/dist/core/class/root-observable-class.d.mts +23 -0
  39. package/dist/core/class/root-observable-class.d.mts.map +1 -0
  40. package/dist/core/class/root-observable-class.mjs +58 -0
  41. package/dist/core/class/root-observable-class.mjs.map +1 -0
  42. package/dist/core/combine/combine.d.mts.map +1 -1
  43. package/dist/core/combine/combine.mjs +25 -14
  44. package/dist/core/combine/combine.mjs.map +1 -1
  45. package/dist/core/combine/merge.d.mts.map +1 -1
  46. package/dist/core/combine/merge.mjs +26 -14
  47. package/dist/core/combine/merge.mjs.map +1 -1
  48. package/dist/core/combine/zip.d.mts.map +1 -1
  49. package/dist/core/combine/zip.mjs +43 -25
  50. package/dist/core/combine/zip.mjs.map +1 -1
  51. package/dist/core/create/counter.d.mts.map +1 -1
  52. package/dist/core/create/counter.mjs +65 -44
  53. package/dist/core/create/counter.mjs.map +1 -1
  54. package/dist/core/create/from-abortable-promise.d.mts.map +1 -1
  55. package/dist/core/create/from-abortable-promise.mjs +26 -18
  56. package/dist/core/create/from-abortable-promise.mjs.map +1 -1
  57. package/dist/core/create/from-promise.d.mts.map +1 -1
  58. package/dist/core/create/from-promise.mjs +30 -21
  59. package/dist/core/create/from-promise.mjs.map +1 -1
  60. package/dist/core/create/from-subscribable.d.mts.map +1 -1
  61. package/dist/core/create/from-subscribable.mjs +37 -24
  62. package/dist/core/create/from-subscribable.mjs.map +1 -1
  63. package/dist/core/create/just.d.mts.map +1 -1
  64. package/dist/core/create/just.mjs +4 -2
  65. package/dist/core/create/just.mjs.map +1 -1
  66. package/dist/core/create/source.d.mts.map +1 -1
  67. package/dist/core/create/source.mjs +27 -17
  68. package/dist/core/create/source.mjs.map +1 -1
  69. package/dist/core/create/timer.d.mts.map +1 -1
  70. package/dist/core/create/timer.mjs +40 -25
  71. package/dist/core/create/timer.mjs.map +1 -1
  72. package/dist/core/index.d.mts +2 -0
  73. package/dist/core/index.d.mts.map +1 -1
  74. package/dist/core/index.mjs +2 -0
  75. package/dist/core/index.mjs.map +1 -1
  76. package/dist/core/operators/audit.d.mts +3 -0
  77. package/dist/core/operators/audit.d.mts.map +1 -1
  78. package/dist/core/operators/audit.mjs +56 -28
  79. package/dist/core/operators/audit.mjs.map +1 -1
  80. package/dist/core/operators/debounce.d.mts +3 -0
  81. package/dist/core/operators/debounce.d.mts.map +1 -1
  82. package/dist/core/operators/debounce.mjs +57 -26
  83. package/dist/core/operators/debounce.mjs.map +1 -1
  84. package/dist/core/operators/filter.d.mts.map +1 -1
  85. package/dist/core/operators/filter.mjs +35 -21
  86. package/dist/core/operators/filter.mjs.map +1 -1
  87. package/dist/core/operators/index.d.mts +1 -0
  88. package/dist/core/operators/index.d.mts.map +1 -1
  89. package/dist/core/operators/index.mjs +1 -0
  90. package/dist/core/operators/index.mjs.map +1 -1
  91. package/dist/core/operators/map.d.mts.map +1 -1
  92. package/dist/core/operators/map.mjs +31 -17
  93. package/dist/core/operators/map.mjs.map +1 -1
  94. package/dist/core/operators/merge-map.d.mts +5 -0
  95. package/dist/core/operators/merge-map.d.mts.map +1 -1
  96. package/dist/core/operators/merge-map.mjs +52 -26
  97. package/dist/core/operators/merge-map.mjs.map +1 -1
  98. package/dist/core/operators/pairwise.d.mts.map +1 -1
  99. package/dist/core/operators/pairwise.mjs +31 -20
  100. package/dist/core/operators/pairwise.mjs.map +1 -1
  101. package/dist/core/operators/scan.d.mts.map +1 -1
  102. package/dist/core/operators/scan.mjs +27 -14
  103. package/dist/core/operators/scan.mjs.map +1 -1
  104. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  105. package/dist/core/operators/skip-if-no-change.mjs +33 -19
  106. package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
  107. package/dist/core/operators/skip-until.d.mts.map +1 -1
  108. package/dist/core/operators/skip-until.mjs +40 -26
  109. package/dist/core/operators/skip-until.mjs.map +1 -1
  110. package/dist/core/operators/skip-while.d.mts.map +1 -1
  111. package/dist/core/operators/skip-while.mjs +35 -20
  112. package/dist/core/operators/skip-while.mjs.map +1 -1
  113. package/dist/core/operators/switch-map.d.mts +5 -0
  114. package/dist/core/operators/switch-map.d.mts.map +1 -1
  115. package/dist/core/operators/switch-map.mjs +56 -25
  116. package/dist/core/operators/switch-map.mjs.map +1 -1
  117. package/dist/core/operators/take-until.d.mts.map +1 -1
  118. package/dist/core/operators/take-until.mjs +36 -29
  119. package/dist/core/operators/take-until.mjs.map +1 -1
  120. package/dist/core/operators/take-while.d.mts.map +1 -1
  121. package/dist/core/operators/take-while.mjs +36 -22
  122. package/dist/core/operators/take-while.mjs.map +1 -1
  123. package/dist/core/{predefined/operators → operators}/take.d.mts +7 -3
  124. package/dist/core/operators/take.d.mts.map +1 -0
  125. package/dist/core/operators/take.mjs +102 -0
  126. package/dist/core/operators/take.mjs.map +1 -0
  127. package/dist/core/operators/throttle.d.mts.map +1 -1
  128. package/dist/core/operators/throttle.mjs +41 -26
  129. package/dist/core/operators/throttle.mjs.map +1 -1
  130. package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
  131. package/dist/core/operators/with-buffered-from.mjs +43 -39
  132. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  133. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  134. package/dist/core/operators/with-current-value-from.mjs +36 -21
  135. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  136. package/dist/core/operators/with-initial-value.d.mts.map +1 -1
  137. package/dist/core/operators/with-initial-value.mjs +24 -12
  138. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  139. package/dist/core/predefined/operators/index.d.mts +0 -1
  140. package/dist/core/predefined/operators/index.d.mts.map +1 -1
  141. package/dist/core/predefined/operators/index.mjs +0 -1
  142. package/dist/core/predefined/operators/index.mjs.map +1 -1
  143. package/dist/core/scope/index.d.mts +2 -0
  144. package/dist/core/scope/index.d.mts.map +1 -0
  145. package/dist/core/scope/index.mjs +2 -0
  146. package/dist/core/scope/index.mjs.map +1 -0
  147. package/dist/core/scope/scope.d.mts +51 -0
  148. package/dist/core/scope/scope.d.mts.map +1 -0
  149. package/dist/core/scope/scope.mjs +194 -0
  150. package/dist/core/scope/scope.mjs.map +1 -0
  151. package/dist/core/types/id.d.mts +0 -2
  152. package/dist/core/types/id.d.mts.map +1 -1
  153. package/dist/core/types/index.d.mts +1 -0
  154. package/dist/core/types/index.d.mts.map +1 -1
  155. package/dist/core/types/index.mjs +1 -0
  156. package/dist/core/types/index.mjs.map +1 -1
  157. package/dist/core/types/observable-family.d.mts +1 -0
  158. package/dist/core/types/observable-family.d.mts.map +1 -1
  159. package/dist/core/types/observable.d.mts +40 -28
  160. package/dist/core/types/observable.d.mts.map +1 -1
  161. package/dist/core/types/observable.mjs +24 -0
  162. package/dist/core/types/observable.mjs.map +1 -1
  163. package/dist/core/types/scope.d.mts +27 -0
  164. package/dist/core/types/scope.d.mts.map +1 -0
  165. package/dist/core/types/scope.mjs +2 -0
  166. package/dist/core/types/scope.mjs.map +1 -0
  167. package/dist/core/utils/current-scope.d.mts +30 -0
  168. package/dist/core/utils/current-scope.d.mts.map +1 -0
  169. package/dist/core/utils/current-scope.mjs +26 -0
  170. package/dist/core/utils/current-scope.mjs.map +1 -0
  171. package/dist/core/utils/id-maker.d.mts +14 -3
  172. package/dist/core/utils/id-maker.d.mts.map +1 -1
  173. package/dist/core/utils/id-maker.mjs +18 -14
  174. package/dist/core/utils/id-maker.mjs.map +1 -1
  175. package/dist/core/utils/index.d.mts +2 -1
  176. package/dist/core/utils/index.d.mts.map +1 -1
  177. package/dist/core/utils/index.mjs +2 -1
  178. package/dist/core/utils/index.mjs.map +1 -1
  179. package/dist/core/utils/max-depth.d.mts +1 -0
  180. package/dist/core/utils/max-depth.d.mts.map +1 -1
  181. package/dist/core/utils/max-depth.mjs +9 -7
  182. package/dist/core/utils/max-depth.mjs.map +1 -1
  183. package/dist/core/utils/observable-brand.d.mts +13 -0
  184. package/dist/core/utils/observable-brand.d.mts.map +1 -0
  185. package/dist/core/utils/observable-brand.mjs +13 -0
  186. package/dist/core/utils/observable-brand.mjs.map +1 -0
  187. package/dist/utils/create-boolean-state.d.mts +2 -2
  188. package/dist/utils/create-reducer.d.mts +7 -7
  189. package/dist/utils/create-reducer.mjs +2 -2
  190. package/dist/utils/create-reducer.mjs.map +1 -1
  191. package/dist/utils/create-state.d.mts +2 -2
  192. package/package.json +16 -16
  193. package/src/core/authoring.mts +19 -0
  194. package/src/core/class/child-observable-class.mts +374 -0
  195. package/src/core/{base → class}/circular-dependency-comparison.test.mts +46 -38
  196. package/src/core/{base → class}/circular-dependency.test.mts +38 -2
  197. package/src/core/class/complete-propagation.test.mts +264 -0
  198. package/src/core/class/create-child-observable.mts +370 -0
  199. package/src/core/class/create-root-observable.mts +107 -0
  200. package/src/core/class/index.mts +8 -0
  201. package/src/core/class/manager-observable-class.mts +166 -0
  202. package/src/core/class/observable-base-class.mts +315 -0
  203. package/src/core/{base → class}/observable-base.test.mts +1 -1
  204. package/src/core/class/observable-node.mts +54 -0
  205. package/src/core/class/propagation-order.test.mts +92 -0
  206. package/src/core/class/propagation-state.mts +80 -0
  207. package/src/core/class/remove-completed.test.mts +369 -0
  208. package/src/core/class/root-observable-class.mts +85 -0
  209. package/src/core/{base → class}/write-during-propagation.test.mts +3 -3
  210. package/src/core/combine/combine.mts +67 -55
  211. package/src/core/combine/merge.mts +33 -23
  212. package/src/core/combine/zip.mts +95 -81
  213. package/src/core/create/counter.mts +95 -65
  214. package/src/core/create/from-abortable-promise.mts +54 -42
  215. package/src/core/create/from-promise.mts +38 -29
  216. package/src/core/create/from-subscribable.mts +62 -45
  217. package/src/core/create/from-subscribable.test.mts +8 -3
  218. package/src/core/create/just.mts +4 -5
  219. package/src/core/create/source.mts +29 -20
  220. package/src/core/create/timer.mts +51 -32
  221. package/src/core/index.mts +2 -0
  222. package/src/core/operators/audit.mts +79 -45
  223. package/src/core/operators/debounce-audit-complete.test.mts +121 -0
  224. package/src/core/operators/debounce.mts +81 -40
  225. package/src/core/operators/filter.mts +48 -31
  226. package/src/core/operators/index.mts +1 -0
  227. package/src/core/operators/inner-ownership.test.mts +280 -0
  228. package/src/core/operators/map.mts +49 -32
  229. package/src/core/operators/merge-map.mts +81 -43
  230. package/src/core/operators/pairwise.mts +40 -30
  231. package/src/core/operators/scan.mts +41 -24
  232. package/src/core/operators/skip-if-no-change.mts +45 -31
  233. package/src/core/operators/skip-until.mts +55 -41
  234. package/src/core/operators/skip-while.mts +49 -31
  235. package/src/core/operators/switch-map.mts +74 -35
  236. package/src/core/operators/take-until-skip-until-teardown.test.mts +18 -12
  237. package/src/core/operators/take-until.mts +45 -41
  238. package/src/core/operators/take-while.mts +50 -33
  239. package/src/core/operators/take.mts +138 -0
  240. package/src/core/operators/throttle.mts +55 -38
  241. package/src/core/operators/with-buffered-from.mts +42 -44
  242. package/src/core/operators/with-buffered-from.test.mts +4 -3
  243. package/src/core/operators/with-current-value-from.mts +39 -25
  244. package/src/core/operators/with-initial-value.mts +30 -21
  245. package/src/core/predefined/operators/index.mts +0 -1
  246. package/src/core/scope/index.mts +1 -0
  247. package/src/core/scope/scope.mts +260 -0
  248. package/src/core/scope/scope.test.mts +462 -0
  249. package/src/core/types/id.mts +0 -4
  250. package/src/core/types/index.mts +1 -0
  251. package/src/core/types/observable-family.mts +2 -0
  252. package/src/core/types/observable.mts +77 -42
  253. package/src/core/types/scope.mts +27 -0
  254. package/src/core/utils/current-scope.mts +61 -0
  255. package/src/core/utils/id-maker.mts +20 -30
  256. package/src/core/utils/index.mts +2 -1
  257. package/src/core/utils/max-depth.mts +12 -9
  258. package/src/core/utils/observable-brand.mts +12 -0
  259. package/src/utils/create-boolean-state.mts +2 -2
  260. package/src/utils/create-boolean-state.test.mts +18 -4
  261. package/src/utils/create-reducer.mts +9 -9
  262. package/src/utils/create-reducer.test.mts +31 -14
  263. package/src/utils/create-state.mts +2 -2
  264. package/src/utils/create-state.test.mts +12 -14
  265. package/dist/core/base/create-child-observable.d.mts +0 -72
  266. package/dist/core/base/create-child-observable.d.mts.map +0 -1
  267. package/dist/core/base/create-child-observable.mjs +0 -285
  268. package/dist/core/base/create-child-observable.mjs.map +0 -1
  269. package/dist/core/base/create-observable-base.d.mts +0 -149
  270. package/dist/core/base/create-observable-base.d.mts.map +0 -1
  271. package/dist/core/base/create-observable-base.mjs +0 -273
  272. package/dist/core/base/create-observable-base.mjs.map +0 -1
  273. package/dist/core/base/create-root-observable.d.mts +0 -46
  274. package/dist/core/base/create-root-observable.d.mts.map +0 -1
  275. package/dist/core/base/create-root-observable.mjs +0 -80
  276. package/dist/core/base/create-root-observable.mjs.map +0 -1
  277. package/dist/core/base/index.d.mts +0 -3
  278. package/dist/core/base/index.d.mts.map +0 -1
  279. package/dist/core/base/index.mjs +0 -3
  280. package/dist/core/base/index.mjs.map +0 -1
  281. package/dist/core/predefined/operators/take.d.mts.map +0 -1
  282. package/dist/core/predefined/operators/take.mjs +0 -44
  283. package/dist/core/predefined/operators/take.mjs.map +0 -1
  284. package/dist/core/utils/utils.d.mts +0 -6
  285. package/dist/core/utils/utils.d.mts.map +0 -1
  286. package/dist/core/utils/utils.mjs +0 -37
  287. package/dist/core/utils/utils.mjs.map +0 -1
  288. package/src/core/base/create-child-observable.mts +0 -485
  289. package/src/core/base/create-observable-base.mts +0 -568
  290. package/src/core/base/create-root-observable.mts +0 -147
  291. package/src/core/base/index.mts +0 -2
  292. package/src/core/predefined/operators/take.mts +0 -48
  293. package/src/core/utils/utils.mts +0 -56
  294. package/src/core/utils/utils.test.mts +0 -88
@@ -1,485 +0,0 @@
1
- import { Arr, type Optional, type Some } from 'ts-data-forge';
2
- import { panic } from 'ts-std-forge';
3
- import type { MutableSet } from 'ts-type-forge';
4
- import {
5
- isChildObservable,
6
- isManagerObservable,
7
- type AsyncChildObservable,
8
- type ChildObservable,
9
- type InitializedSyncChildObservable,
10
- type NonEmptyUnknownList,
11
- type Observable,
12
- type ObservableId,
13
- type SyncChildObservable,
14
- type UpdateToken,
15
- type Wrap,
16
- } from '../types/index.mjs';
17
- import { maxDepth } from '../utils/index.mjs';
18
- import {
19
- assembleObservable,
20
- createManagerObservableParts,
21
- createObservableBaseHandle,
22
- } from './create-observable-base.mjs';
23
-
24
- /**
25
- * The internal tools a sync-child leaf factory (`map`, `filter`, `zip`, …)
26
- * receives — the replacement for the `protected` methods a subclass of
27
- * `SyncChildObservableClass` used to inherit.
28
- */
29
- export type SyncChildObservableTools<A> = Readonly<{
30
- /** Emits `nextValue` under `updateToken` and notifies subscribers. */
31
- setNext: (nextValue: A, updateToken: UpdateToken) => void;
32
-
33
- getSnapshot: () => Optional<A>;
34
-
35
- /** Completes this observable (leaf teardown included). */
36
- complete: () => void;
37
- }>;
38
-
39
- /**
40
- * The internal tools an async-child leaf factory (`debounce`, `switchMap`, …)
41
- * receives. Async children additionally start their own update propagation.
42
- */
43
- export type AsyncChildObservableTools<A> = Readonly<{
44
- /** Emits `nextValue` and propagates the update to all descendants. */
45
- startUpdate: (nextValue: A) => void;
46
- }> &
47
- SyncChildObservableTools<A>;
48
-
49
- type ChildObservableConfigBase<P extends NonEmptyUnknownList> = Readonly<{
50
- parents: Wrap<P>;
51
-
52
- depth?: number;
53
-
54
- /**
55
- * Leaf teardown (clear timers, unsubscribe inner observables, …), run at
56
- * the beginning of `complete()` — the closure replacement for
57
- * `override complete() { teardown(); super.complete(); }`.
58
- */
59
- onComplete?: () => void;
60
-
61
- /**
62
- * Whether this observable can emit nothing more although some parent is
63
- * still live, asked whenever a parent completes. `true` completes it then,
64
- * as all parents completing does (`zip` once a completed source's queue is
65
- * empty).
66
- */
67
- canEmitNoMore?: () => boolean;
68
- }>;
69
-
70
- export type ChildObservableConfig<
71
- A,
72
- P extends NonEmptyUnknownList,
73
- > = ChildObservableConfigBase<P> &
74
- Readonly<{
75
- initialValue: Optional<A>;
76
- }>;
77
-
78
- export type InitializedChildObservableConfig<
79
- A,
80
- P extends NonEmptyUnknownList,
81
- > = ChildObservableConfigBase<P> &
82
- Readonly<{
83
- initialValue: Some<A>;
84
- }>;
85
-
86
- /**
87
- * Creates a sync-child observable — the classless replacement for extending
88
- * `SyncChildObservableClass`.
89
- *
90
- * `createTryUpdate` receives the internal tools and returns the `tryUpdate`
91
- * implementation (the former `override tryUpdate`). It runs during
92
- * construction, before the observable is registered to its parents; leaf
93
- * state can live either in the enclosing factory scope or inside the
94
- * `createTryUpdate` closure.
95
- */
96
- export const createSyncChildObservable = <A, P extends NonEmptyUnknownList>(
97
- {
98
- parents,
99
- depth = 1 + maxDepth(parents),
100
- initialValue,
101
- onComplete,
102
- canEmitNoMore,
103
- }: ChildObservableConfig<A, P>,
104
- createTryUpdate: (
105
- tools: SyncChildObservableTools<A>,
106
- ) => (updateToken: UpdateToken) => void,
107
- ): SyncChildObservable<A, P> => {
108
- const handle = createObservableBaseHandle<A>(initialValue);
109
-
110
- /** Set once assembled; see {@link unregisterChild}. */
111
- let mut_self: ChildObservable<A> | undefined = undefined;
112
-
113
- const autoComplete =
114
- onComplete !== undefined || parents.some((p) => p.autoComplete);
115
-
116
- const dispose = (): void => {
117
- // A completed node is reached again through every parent and child that
118
- // completes after it; running again would repeat the teardown and the
119
- // ancestor walk in `unregisterChild` for each of them.
120
- if (handle.isCompleted()) {
121
- return;
122
- }
123
-
124
- onComplete?.();
125
-
126
- handle.completeBase();
127
-
128
- if (mut_self !== undefined) {
129
- unregisterChild(mut_self, parents);
130
- }
131
- };
132
-
133
- const complete = (): void => {
134
- if (handle.isCompleted()) {
135
- return;
136
- }
137
-
138
- dispose();
139
-
140
- // propagate to parents
141
- for (const par of parents) {
142
- par.tryComplete();
143
- }
144
- };
145
-
146
- const tryComplete = (): void => {
147
- if (handle.isCompleted()) {
148
- return;
149
- }
150
-
151
- tryCompleteChild({
152
- canEmitNoMore,
153
- hasSubscriber: handle.hasSubscriber(),
154
- hasActiveChild: handle.hasActiveChild(),
155
- autoComplete,
156
- parents,
157
- complete,
158
- });
159
- };
160
-
161
- const tryUpdate = createTryUpdate({
162
- setNext: handle.setNext,
163
- getSnapshot: handle.getSnapshot,
164
- complete,
165
- });
166
-
167
- const observable: SyncChildObservable<A, P> = assembleObservable({
168
- kind: 'sync child',
169
- depth,
170
- handle,
171
- tryUpdate,
172
- tryComplete,
173
- complete,
174
- dispose,
175
- autoComplete,
176
- extra: { parents },
177
- });
178
-
179
- // Completed parents never update again, so a child created under them is
180
- // complete from the start; registering it would only leave a reference in
181
- // each parent (#2248).
182
- if (parents.every((p) => p.isCompleted)) {
183
- dispose();
184
- } else {
185
- registerChild(observable, parents);
186
-
187
- mut_self = observable;
188
- }
189
-
190
- return observable;
191
- };
192
-
193
- /**
194
- * Creates a sync-child observable that always holds a value.
195
- *
196
- * Runtime-wise this is exactly `createSyncChildObservable`; only the types of
197
- * `getSnapshot` and `pipe` are narrowed, which is what the
198
- * `InitializedSyncChildObservableClass` overrides existed for.
199
- */
200
- export const createInitializedSyncChildObservable = <
201
- A,
202
- P extends NonEmptyUnknownList,
203
- >(
204
- config: InitializedChildObservableConfig<A, P>,
205
- createTryUpdate: (
206
- tools: SyncChildObservableTools<A>,
207
- ) => (updateToken: UpdateToken) => void,
208
- ): InitializedSyncChildObservable<A, P> => {
209
- const observable = createSyncChildObservable<A, P>(config, createTryUpdate);
210
-
211
- return (
212
- // `getSnapshot` always returns `Some` here: `initialValue` is a `Some` and
213
- // `setNext` only ever stores a `Some`.
214
- // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
215
- observable as InitializedSyncChildObservable<A, P>
216
- );
217
- };
218
-
219
- /**
220
- * Creates an async-child observable — the classless replacement for extending
221
- * `AsyncChildObservableClass`. See {@link createSyncChildObservable} for the
222
- * `createTryUpdate` contract.
223
- */
224
- export const createAsyncChildObservable = <A, P extends NonEmptyUnknownList>(
225
- {
226
- parents,
227
- depth = 1 + maxDepth(parents),
228
- initialValue,
229
- onComplete,
230
- canEmitNoMore,
231
- }: ChildObservableConfig<A, P>,
232
- createTryUpdate: (
233
- tools: AsyncChildObservableTools<A>,
234
- ) => (updateToken: UpdateToken) => void,
235
- ): AsyncChildObservable<A, P> => {
236
- const handle = createObservableBaseHandle<A>(initialValue);
237
-
238
- const { addDescendant, deleteDescendant, startUpdate } =
239
- createManagerObservableParts(handle);
240
-
241
- /** Set once assembled; see {@link unregisterChild}. */
242
- let mut_self: ChildObservable<A> | undefined = undefined;
243
-
244
- const autoComplete =
245
- onComplete !== undefined || parents.some((p) => p.autoComplete);
246
-
247
- const dispose = (): void => {
248
- // A completed node is reached again through every parent and child that
249
- // completes after it; running again would repeat the teardown and the
250
- // ancestor walk in `unregisterChild` for each of them.
251
- if (handle.isCompleted()) {
252
- return;
253
- }
254
-
255
- onComplete?.();
256
-
257
- handle.completeBase();
258
-
259
- if (mut_self !== undefined) {
260
- unregisterChild(mut_self, parents);
261
- }
262
- };
263
-
264
- const complete = (): void => {
265
- if (handle.isCompleted()) {
266
- return;
267
- }
268
-
269
- dispose();
270
-
271
- // propagate to parents
272
- for (const par of parents) {
273
- par.tryComplete();
274
- }
275
- };
276
-
277
- const tryComplete = (): void => {
278
- if (handle.isCompleted()) {
279
- return;
280
- }
281
-
282
- tryCompleteChild({
283
- canEmitNoMore,
284
- hasSubscriber: handle.hasSubscriber(),
285
- hasActiveChild: handle.hasActiveChild(),
286
- autoComplete,
287
- parents,
288
- complete,
289
- });
290
- };
291
-
292
- const tryUpdate = createTryUpdate({
293
- setNext: handle.setNext,
294
- getSnapshot: handle.getSnapshot,
295
- complete,
296
- startUpdate,
297
- });
298
-
299
- const observable: AsyncChildObservable<A, P> = assembleObservable({
300
- kind: 'async child',
301
- depth,
302
- handle,
303
- tryUpdate,
304
- tryComplete,
305
- complete,
306
- dispose,
307
- autoComplete,
308
- extra: { parents, addDescendant, deleteDescendant },
309
- });
310
-
311
- // Completed parents never update again, so a child created under them is
312
- // complete from the start; registering it would only leave a reference in
313
- // each parent (#2248).
314
- if (parents.every((p) => p.isCompleted)) {
315
- dispose();
316
- } else {
317
- registerChild(observable, parents);
318
-
319
- mut_self = observable;
320
- }
321
-
322
- return observable;
323
- };
324
-
325
- /* helpers */
326
-
327
- /**
328
- * Detects circular dependencies by walking the full ancestor chain of the
329
- * given parents and checking whether `child` already appears among them.
330
- *
331
- * @throws {Error} if a circular dependency is detected
332
- */
333
- const hasCircularDependencyFrom = (
334
- node: Observable<unknown>,
335
- mut_visited: MutableSet<ObservableId>,
336
- mut_inPath: MutableSet<ObservableId>,
337
- ): boolean => {
338
- if (mut_inPath.has(node.id)) {
339
- return true;
340
- }
341
-
342
- if (mut_visited.has(node.id)) {
343
- return false;
344
- }
345
-
346
- mut_visited.add(node.id);
347
-
348
- mut_inPath.add(node.id);
349
-
350
- if (isChildObservable(node)) {
351
- for (const parent of node.parents) {
352
- if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {
353
- return true;
354
- }
355
- }
356
- }
357
-
358
- mut_inPath.delete(node.id);
359
-
360
- return false;
361
- };
362
-
363
- const detectCircularDependency = (
364
- child: ChildObservable<unknown>,
365
- parents: readonly Observable<unknown>[],
366
- ): void => {
367
- const mut_visited = new Set<ObservableId>();
368
-
369
- const mut_inPath = new Set<ObservableId>([child.id]);
370
-
371
- for (const parent of parents) {
372
- if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {
373
- // A programming error (invariant violation), not a recoverable
374
- // failure. `panic` marks the error it throws, so the `Result` /
375
- // `AsyncResult` boundaries rethrow it rather than turning an invariant
376
- // violation into an `Err` a caller might handle (Sumi D-48 / D-53).
377
- panic(
378
- 'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
379
- );
380
- }
381
- }
382
- };
383
-
384
- /**
385
- * Registers `child` to its parents and to all reachable manager observables.
386
- * Runs after the child object is fully assembled, so parents only ever hold a
387
- * reference to the final composed object (the class version registered `this`
388
- * from inside the constructor instead).
389
- */
390
- const registerChild = <A,>(
391
- child: ChildObservable<A>,
392
- parents: ChildObservable<A>['parents'],
393
- ): void => {
394
- detectCircularDependency(child, parents);
395
-
396
- for (const p of parents) {
397
- p.addChild(child);
398
- }
399
-
400
- // register child to all reachable ManagerObservables
401
- const mut_rest = Array.from(parents);
402
-
403
- while (Arr.isNonEmpty(mut_rest)) {
404
- const p = mut_rest.pop();
405
-
406
- if (p === undefined) {
407
- break;
408
- }
409
-
410
- if (isManagerObservable(p)) {
411
- p.addDescendant(child);
412
- } else {
413
- // trace back dependency graph
414
- mut_rest.push(...p.parents);
415
- }
416
- }
417
- };
418
-
419
- /**
420
- * Undoes {@link registerChild} for a completed child: its parents forget it, and
421
- * every manager it registered to stops propagating updates to it. Without this
422
- * a completed node stayed reachable for as long as its root lived, and ran its
423
- * `tryUpdate` on every update (#2134).
424
- *
425
- * A completion that happens while the child is still being constructed (from
426
- * a leaf factory's `createTryUpdate`) precedes its registration, so there is
427
- * nothing to undo; `mut_self` is still `undefined` then.
428
- */
429
- const unregisterChild = <A,>(
430
- child: ChildObservable<A>,
431
- parents: ChildObservable<A>['parents'],
432
- ): void => {
433
- for (const p of parents) {
434
- p.deleteChild(child);
435
- }
436
-
437
- const mut_rest = Array.from(parents);
438
-
439
- while (Arr.isNonEmpty(mut_rest)) {
440
- const p = mut_rest.pop();
441
-
442
- if (p === undefined) {
443
- break;
444
- }
445
-
446
- if (isManagerObservable(p)) {
447
- p.deleteDescendant(child);
448
- } else {
449
- mut_rest.push(...p.parents);
450
- }
451
- }
452
- };
453
-
454
- const tryCompleteChild = <A,>({
455
- canEmitNoMore,
456
- hasSubscriber,
457
- hasActiveChild,
458
- autoComplete,
459
- parents,
460
- complete,
461
- }: Readonly<{
462
- canEmitNoMore: (() => boolean) | undefined;
463
- hasSubscriber: boolean;
464
- hasActiveChild: boolean;
465
- autoComplete: boolean;
466
- parents: ChildObservable<A>['parents'];
467
- complete: () => void;
468
- }>): void => {
469
- // If there is no working parent node, or no value can come from the live ones
470
- if (parents.every((r) => r.isCompleted) || canEmitNoMore?.() === true) {
471
- complete();
472
-
473
- return;
474
- }
475
-
476
- // If there are no active child node, and completing releases something
477
- if (autoComplete && !hasSubscriber && !hasActiveChild) {
478
- complete();
479
- }
480
-
481
- // propagate to parents
482
- for (const par of parents) {
483
- par.tryComplete();
484
- }
485
- };