synstate 3.3.0 → 4.0.0

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