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,16 +1,31 @@
1
+ // Sumi bans classes, `this`, method syntax and writes to `this` outside a
2
+ // `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
3
+ // so those rules are waived here (D-63).
4
+ // @sumi-expect-error-file classes/no-class
5
+ // @sumi-expect-error-file banned-syntax/no-this
6
+ // @sumi-expect-error-file functions/prefer-arrow-function
7
+ // @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
1
8
  import { Arr, Optional } from 'ts-data-forge';
2
- import { createAsyncChildObservable } from '../base/index.mjs';
9
+ import { AsyncChildObservableClass } from '../class/index.mjs';
10
+ import { createScope, getCurrentScope } from '../scope/index.mjs';
3
11
  import type {
4
12
  DropInitialValueOperator,
5
13
  MergeMapOperatorObservable,
6
14
  Observable,
15
+ Scope,
7
16
  Subscription,
17
+ UpdateToken,
8
18
  } from '../types/index.mjs';
9
19
 
10
20
  /**
11
21
  * Projects each source value to an observable and merges all inner observables.
12
22
  * Unlike `switchMap`, does not cancel previous inner observables.
13
23
  *
24
+ * Each projection runs in a scope of its own (see `createScope`): what it
25
+ * creates is disposed when `mergeMap` completes. An observable it only
26
+ * returns — one created outside the projection and shared with others — is
27
+ * unsubscribed and left running.
28
+ *
14
29
  * @template A - The type of values from the source
15
30
  * @template B - The type of values from the projected observable
16
31
  * @param mapToObservable - A function that maps each source value to an observable
@@ -98,16 +113,13 @@ import type {
98
113
  * 'C3',
99
114
  * ]);
100
115
  * ```
101
- *
102
- * @note To improve code readability, consider using `createState` instead of `mergeMap`,
103
- * subscribing to `parentObservable` and calling `setState` within it.
104
116
  */
105
117
  export const mergeMap =
106
118
  <A, B>(
107
119
  mapToObservable: (curr: A) => Observable<B>,
108
120
  ): DropInitialValueOperator<A, B> =>
109
121
  (parentObservable) =>
110
- createMergeMapObservable(parentObservable, mapToObservable);
122
+ new MergeMapObservableClass(parentObservable, mapToObservable);
111
123
 
112
124
  /**
113
125
  * Alias for `mergeMap`.
@@ -115,48 +127,71 @@ export const mergeMap =
115
127
  */
116
128
  export const flatMap = mergeMap;
117
129
 
118
- const createMergeMapObservable = <A, B>(
119
- parentObservable: Observable<A>,
120
- mapToObservable: (curr: A) => Observable<B>,
121
- ): MergeMapOperatorObservable<A, B> => {
122
- let mut_observables: readonly Observable<B>[] = [];
130
+ class MergeMapObservableClass<A, B>
131
+ extends AsyncChildObservableClass<B, readonly [A]>
132
+ implements MergeMapOperatorObservable<A, B>
133
+ {
134
+ readonly #mapToObservable: (curr: A) => Observable<B>;
135
+
136
+ /** See `SwitchMapObservableClass`. */
137
+ readonly #ownerScope: Scope | undefined;
123
138
 
124
- let mut_subscriptions: readonly Subscription[] = [];
139
+ #mut_innerScopes: readonly Scope[];
140
+ #mut_subscriptions: readonly Subscription[];
125
141
 
126
- return createAsyncChildObservable(
127
- {
142
+ constructor(
143
+ parentObservable: Observable<A>,
144
+ mapToObservable: (curr: A) => Observable<B>,
145
+ ) {
146
+ super({
128
147
  parents: [parentObservable],
129
148
  initialValue: Optional.none,
130
- onComplete: () => {
131
- for (const s of mut_subscriptions) {
132
- s.unsubscribe();
133
- }
134
-
135
- for (const o of mut_observables) {
136
- o.complete();
137
- }
138
- },
139
- },
140
- ({ startUpdate }) =>
141
- (updateToken) => {
142
- const sn = parentObservable.getSnapshot();
143
-
144
- if (
145
- parentObservable.updateToken !== updateToken ||
146
- Optional.isNone(sn)
147
- ) {
148
- return; // skip update
149
- }
150
-
151
- const observable = mapToObservable(sn.value);
152
-
153
- mut_observables = Arr.toPushed(mut_observables, observable);
154
-
155
- const subscription = observable.subscribe((curr) => {
156
- startUpdate(curr);
157
- });
158
-
159
- mut_subscriptions = Arr.toPushed(mut_subscriptions, subscription);
160
- },
161
- );
162
- };
149
+ });
150
+
151
+ this.#mapToObservable = mapToObservable;
152
+
153
+ this.#ownerScope = getCurrentScope();
154
+
155
+ this.#mut_innerScopes = [];
156
+
157
+ this.#mut_subscriptions = [];
158
+
159
+ this.attach();
160
+ }
161
+
162
+ override tryUpdate(updateToken: UpdateToken): void {
163
+ const par = this.parents[0];
164
+
165
+ const sn = par.getSnapshot();
166
+
167
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
168
+ return; // skip update
169
+ }
170
+
171
+ const innerScope = createScope(this.#ownerScope);
172
+
173
+ this.#mut_innerScopes = Arr.toPushed(this.#mut_innerScopes, innerScope);
174
+
175
+ const observable = innerScope.run(() => this.#mapToObservable(sn.value));
176
+
177
+ const subscription = observable.subscribe((curr) => {
178
+ this.startUpdate(curr);
179
+ });
180
+
181
+ this.#mut_subscriptions = Arr.toPushed(
182
+ this.#mut_subscriptions,
183
+ subscription,
184
+ );
185
+ }
186
+
187
+ /** Releases the inner observables, and only those this operator made. */
188
+ protected override teardown(): void {
189
+ for (const s of this.#mut_subscriptions) {
190
+ s.unsubscribe();
191
+ }
192
+
193
+ for (const scope of this.#mut_innerScopes) {
194
+ scope.dispose();
195
+ }
196
+ }
197
+ }
@@ -1,10 +1,18 @@
1
+ // Sumi bans classes, `this`, method syntax and writes to `this` outside a
2
+ // `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
3
+ // so those rules are waived here (D-63).
4
+ // @sumi-expect-error-file classes/no-class
5
+ // @sumi-expect-error-file banned-syntax/no-this
6
+ // @sumi-expect-error-file functions/prefer-arrow-function
7
+ // @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
1
8
  import { Optional } from 'ts-data-forge';
2
9
  import type { FixedLengthTuple } from 'ts-type-forge';
3
- import { createSyncChildObservable } from '../base/index.mjs';
10
+ import { SyncChildObservableClass } from '../class/index.mjs';
4
11
  import type {
5
12
  DropInitialValueOperator,
6
13
  Observable,
7
14
  PairwiseOperatorObservable,
15
+ UpdateToken,
8
16
  } from '../types/index.mjs';
9
17
 
10
18
  /**
@@ -68,41 +76,43 @@ export const pairwise = <A,>(): DropInitialValueOperator<
68
76
  const f = <A,>(
69
77
  parentObservable: Observable<A>,
70
78
  ): Observable<FixedLengthTuple<2, A>> =>
71
- createPairwiseObservable(parentObservable);
79
+ new PairwiseObservableClass(parentObservable);
72
80
 
73
- const createPairwiseObservable = <A,>(
74
- parentObservable: Observable<A>,
75
- ): PairwiseOperatorObservable<A> =>
76
- createSyncChildObservable<FixedLengthTuple<2, A>, readonly [A]>(
77
- {
81
+ class PairwiseObservableClass<A>
82
+ extends SyncChildObservableClass<FixedLengthTuple<2, A>, readonly [A]>
83
+ implements PairwiseOperatorObservable<A>
84
+ {
85
+ #mut_previousValue: Optional<A>;
86
+
87
+ constructor(parentObservable: Observable<A>) {
88
+ super({
78
89
  parents: [parentObservable],
79
90
  initialValue: Optional.none,
80
- },
81
- ({ setNext }) => {
82
- // parentObservable.snapshot has value
83
- // if parentObservable is InitializedObservable
84
- let mut_previousValue: Optional<A> = parentObservable.getSnapshot();
91
+ });
92
+
93
+ // parentObservable.snapshot has value
94
+ // if parentObservable is InitializedObservable
95
+ this.#mut_previousValue = parentObservable.getSnapshot();
96
+
97
+ this.attach();
98
+ }
85
99
 
86
- return (updateToken) => {
87
- const sn = parentObservable.getSnapshot();
100
+ override tryUpdate(updateToken: UpdateToken): void {
101
+ const par = this.parents[0];
88
102
 
89
- if (
90
- parentObservable.updateToken !== updateToken ||
91
- Optional.isNone(sn)
92
- ) {
93
- return; // skip update
94
- }
103
+ const sn = par.getSnapshot();
95
104
 
96
- const prev = mut_previousValue;
105
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
106
+ return; // skip update
107
+ }
97
108
 
98
- const cond = !Optional.isNone(prev);
109
+ const prev = this.#mut_previousValue;
99
110
 
100
- // NOTE: Must update before setNext, otherwise Optional.isNone(prev) remains true when tryUpdate is called consecutively
101
- mut_previousValue = parentObservable.getSnapshot();
111
+ // NOTE: Must update before setNext, otherwise Optional.isNone(prev) remains true when tryUpdate is called consecutively
112
+ this.#mut_previousValue = sn;
102
113
 
103
- if (cond) {
104
- setNext([prev.value, sn.value], updateToken);
105
- }
106
- };
107
- },
108
- );
114
+ if (Optional.isSome(prev)) {
115
+ this.setNext([prev.value, sn.value], updateToken);
116
+ }
117
+ }
118
+ }
@@ -1,8 +1,16 @@
1
+ // Sumi bans classes, `this`, method syntax and writes to `this` outside a
2
+ // `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
3
+ // so those rules are waived here (D-63).
4
+ // @sumi-expect-error-file classes/no-class
5
+ // @sumi-expect-error-file banned-syntax/no-this
6
+ // @sumi-expect-error-file functions/prefer-arrow-function
7
+ // @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
1
8
  import { Optional } from 'ts-data-forge';
2
- import { createInitializedSyncChildObservable } from '../base/index.mjs';
9
+ import { InitializedSyncChildObservableClass } from '../class/index.mjs';
3
10
  import type {
4
11
  Observable,
5
12
  ScanOperatorObservable,
13
+ UpdateToken,
6
14
  WithInitialValueOperator,
7
15
  } from '../types/index.mjs';
8
16
 
@@ -61,32 +69,41 @@ export const scan =
61
69
  initialValue: B,
62
70
  ): WithInitialValueOperator<A, B> =>
63
71
  (parentObservable) =>
64
- createScanObservable(parentObservable, reducer, initialValue);
72
+ new ScanObservableClass(parentObservable, reducer, initialValue);
65
73
 
66
- const createScanObservable = <A, B>(
67
- parentObservable: Observable<A>,
68
- reducer: (acc: B, curr: A) => B,
69
- initialValue: B,
70
- ): ScanOperatorObservable<A, B> =>
71
- createInitializedSyncChildObservable(
72
- {
74
+ class ScanObservableClass<A, B>
75
+ extends InitializedSyncChildObservableClass<B, readonly [A]>
76
+ implements ScanOperatorObservable<A, B>
77
+ {
78
+ readonly #reducer: (acc: B, curr: A) => B;
79
+
80
+ constructor(
81
+ parentObservable: Observable<A>,
82
+ reducer: (acc: B, curr: A) => B,
83
+ initialValue: B,
84
+ ) {
85
+ super({
73
86
  parents: [parentObservable],
74
87
  initialValue: Optional.some(initialValue),
75
- },
76
- ({ setNext, getSnapshot }) =>
77
- (updateToken) => {
78
- const psn = parentObservable.getSnapshot();
88
+ });
89
+
90
+ this.#reducer = reducer;
91
+
92
+ this.attach();
93
+ }
94
+
95
+ override tryUpdate(updateToken: UpdateToken): void {
96
+ const par = this.parents[0];
79
97
 
80
- const sn = getSnapshot();
98
+ const psn = par.getSnapshot();
81
99
 
82
- if (
83
- parentObservable.updateToken !== updateToken ||
84
- Optional.isNone(psn) ||
85
- Optional.isNone(sn)
86
- ) {
87
- return; // skip update
88
- }
100
+ if (par.updateToken !== updateToken || Optional.isNone(psn)) {
101
+ return; // skip update
102
+ }
89
103
 
90
- setNext(reducer(sn.value, psn.value), updateToken);
91
- },
92
- );
104
+ this.setNext(
105
+ this.#reducer(this.getSnapshot().value, psn.value),
106
+ updateToken,
107
+ );
108
+ }
109
+ }
@@ -1,9 +1,17 @@
1
+ // Sumi bans classes, `this`, method syntax and writes to `this` outside a
2
+ // `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
3
+ // so those rules are waived here (D-63).
4
+ // @sumi-expect-error-file classes/no-class
5
+ // @sumi-expect-error-file banned-syntax/no-this
6
+ // @sumi-expect-error-file functions/prefer-arrow-function
7
+ // @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
1
8
  import { Optional } from 'ts-data-forge';
2
- import { createSyncChildObservable } from '../base/index.mjs';
9
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
10
  import type {
4
11
  KeepInitialValueOperator,
5
12
  Observable,
6
13
  SkipIfNoChangeOperatorObservable,
14
+ UpdateToken,
7
15
  } from '../types/index.mjs';
8
16
 
9
17
  /**
@@ -72,7 +80,7 @@ export const skipIfNoChange = <A,>(
72
80
  ): KeepInitialValueOperator<A, A> =>
73
81
  // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
74
82
  ((parentObservable) =>
75
- createSkipIfNoChangeObservable(
83
+ new SkipIfNoChangeObservableClass(
76
84
  parentObservable,
77
85
  eq,
78
86
  )) as KeepInitialValueOperator<A, A>;
@@ -83,40 +91,46 @@ export const skipIfNoChange = <A,>(
83
91
  */
84
92
  export const distinctUntilChanged = skipIfNoChange;
85
93
 
86
- const createSkipIfNoChangeObservable = <A,>(
87
- parentObservable: Observable<A>,
88
- eq: (x: A, y: A) => boolean,
89
- ): SkipIfNoChangeOperatorObservable<A> =>
90
- createSyncChildObservable(
91
- {
94
+ class SkipIfNoChangeObservableClass<A>
95
+ extends SyncChildObservableClass<A, readonly [A]>
96
+ implements SkipIfNoChangeOperatorObservable<A>
97
+ {
98
+ readonly #eq: (x: A, y: A) => boolean;
99
+ #mut_previousValue: Optional<A>;
100
+
101
+ constructor(parentObservable: Observable<A>, eq: (x: A, y: A) => boolean) {
102
+ super({
92
103
  parents: [parentObservable],
93
104
  initialValue: parentObservable.getSnapshot(),
94
- },
95
- ({ setNext }) => {
96
- // parentObservable.snapshot has value
97
- // if parentObservable is InitializedObservable
98
- let mut_previousValue: Optional<A> = parentObservable.getSnapshot();
105
+ });
106
+
107
+ // parentObservable.snapshot has value
108
+ // if parentObservable is InitializedObservable
109
+ this.#mut_previousValue = parentObservable.getSnapshot();
110
+
111
+ this.#eq = eq;
112
+
113
+ this.attach();
114
+ }
115
+
116
+ override tryUpdate(updateToken: UpdateToken): void {
117
+ const par = this.parents[0];
99
118
 
100
- return (updateToken) => {
101
- const sn = parentObservable.getSnapshot();
119
+ const sn = par.getSnapshot();
102
120
 
103
- if (
104
- parentObservable.updateToken !== updateToken ||
105
- Optional.isNone(sn)
106
- ) {
107
- return; // skip update
108
- }
121
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
122
+ return; // skip update
123
+ }
109
124
 
110
- const prev = mut_previousValue;
125
+ const prev = this.#mut_previousValue;
111
126
 
112
- const cond = Optional.isNone(prev) || !eq(prev.value, sn.value);
127
+ const cond = Optional.isNone(prev) || !this.#eq(prev.value, sn.value);
113
128
 
114
- // NOTE: Must update before setNext, otherwise Optional.isNone(prev) remains true when tryUpdate is called consecutively
115
- mut_previousValue = sn;
129
+ // NOTE: Must update before setNext, otherwise Optional.isNone(prev) remains true when tryUpdate is called consecutively
130
+ this.#mut_previousValue = sn;
116
131
 
117
- if (cond) {
118
- setNext(sn.value, updateToken);
119
- }
120
- };
121
- },
122
- );
132
+ if (cond) {
133
+ this.setNext(sn.value, updateToken);
134
+ }
135
+ }
136
+ }
@@ -1,9 +1,18 @@
1
+ // Sumi bans classes, `this`, method syntax and writes to `this` outside a
2
+ // `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
3
+ // so those rules are waived here (D-63).
4
+ // @sumi-expect-error-file classes/no-class
5
+ // @sumi-expect-error-file banned-syntax/no-this
6
+ // @sumi-expect-error-file functions/prefer-arrow-function
7
+ // @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
1
8
  import { Optional } from 'ts-data-forge';
2
- import { createSyncChildObservable } from '../base/index.mjs';
9
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
10
  import type {
4
11
  DropInitialValueOperator,
5
12
  Observable,
6
13
  SkipUntilOperatorObservable,
14
+ Subscription,
15
+ UpdateToken,
7
16
  } from '../types/index.mjs';
8
17
 
9
18
  /**
@@ -59,43 +68,60 @@ import type {
59
68
  export const skipUntil =
60
69
  <A,>(notifier: Observable<unknown>): DropInitialValueOperator<A, A> =>
61
70
  (parentObservable) =>
62
- createSkipUntilObservable(parentObservable, notifier);
71
+ new SkipUntilObservableClass(parentObservable, notifier);
63
72
 
64
- const createSkipUntilObservable = <A,>(
65
- parentObservable: Observable<A>,
66
- notifier: Observable<unknown>,
67
- ): SkipUntilOperatorObservable<A> => {
68
- let mut_isSkipping = true;
73
+ class SkipUntilObservableClass<A>
74
+ extends SyncChildObservableClass<A, readonly [A]>
75
+ implements SkipUntilOperatorObservable<A>
76
+ {
77
+ #mut_isSkipping: boolean;
78
+ #mut_subscription: Subscription | undefined;
69
79
 
70
- const observable = createSyncChildObservable<A, readonly [A]>(
71
- {
80
+ constructor(parentObservable: Observable<A>, notifier: Observable<unknown>) {
81
+ super({
72
82
  parents: [parentObservable],
73
83
  initialValue: Optional.none,
74
- },
75
- ({ setNext }) =>
76
- (updateToken) => {
77
- const sn = parentObservable.getSnapshot();
84
+ });
78
85
 
79
- if (
80
- mut_isSkipping ||
81
- parentObservable.updateToken !== updateToken ||
82
- Optional.isNone(sn)
83
- ) {
84
- return; // skip update
85
- }
86
+ // A notifier that already holds a value, or has completed, would stop
87
+ // the skipping inside `subscribe`, before the subscription is stored.
88
+ this.#mut_isSkipping =
89
+ Optional.isNone(notifier.getSnapshot()) && !notifier.isCompleted;
86
90
 
87
- setNext(sn.value, updateToken);
88
- },
89
- );
91
+ this.#mut_subscription = undefined;
90
92
 
91
- notifier.subscribe(
92
- () => {
93
- mut_isSkipping = false;
94
- },
95
- () => {
96
- mut_isSkipping = false;
97
- },
98
- );
93
+ if (this.#mut_isSkipping) {
94
+ const stopSkipping = (): void => {
95
+ this.#mut_isSkipping = false;
99
96
 
100
- return observable;
101
- };
97
+ this.#mut_subscription?.unsubscribe();
98
+
99
+ this.#mut_subscription = undefined;
100
+ };
101
+
102
+ this.#mut_subscription = notifier.subscribe(stopSkipping, stopSkipping);
103
+ }
104
+
105
+ this.attach();
106
+ }
107
+
108
+ override tryUpdate(updateToken: UpdateToken): void {
109
+ const par = this.parents[0];
110
+
111
+ const sn = par.getSnapshot();
112
+
113
+ if (
114
+ this.#mut_isSkipping ||
115
+ par.updateToken !== updateToken ||
116
+ Optional.isNone(sn)
117
+ ) {
118
+ return; // skip update
119
+ }
120
+
121
+ this.setNext(sn.value, updateToken);
122
+ }
123
+
124
+ protected override teardown(): void {
125
+ this.#mut_subscription?.unsubscribe();
126
+ }
127
+ }
@@ -1,9 +1,17 @@
1
+ // Sumi bans classes, `this`, method syntax and writes to `this` outside a
2
+ // `mut_` binding; an observable is a class (`ObservableBaseClass` says why),
3
+ // so those rules are waived here (D-63).
4
+ // @sumi-expect-error-file classes/no-class
5
+ // @sumi-expect-error-file banned-syntax/no-this
6
+ // @sumi-expect-error-file functions/prefer-arrow-function
7
+ // @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
1
8
  import { Optional, SafeUint, asSafeUint, pipe } from 'ts-data-forge';
2
- import { createSyncChildObservable } from '../base/index.mjs';
9
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
10
  import type {
4
11
  DropInitialValueOperator,
5
12
  Observable,
6
13
  SkipWhileOperatorObservable,
14
+ UpdateToken,
7
15
  } from '../types/index.mjs';
8
16
 
9
17
  /**
@@ -69,46 +77,56 @@ export const skipWhile =
69
77
  predicate: (value: A, index: SafeUint | -1) => boolean,
70
78
  ): DropInitialValueOperator<A, A> =>
71
79
  (parentObservable) =>
72
- createSkipWhileObservable(parentObservable, predicate);
80
+ new SkipWhileObservableClass(parentObservable, predicate);
73
81
 
74
82
  /* implementation */
75
83
 
76
- const createSkipWhileObservable = <A,>(
77
- parentObservable: Observable<A>,
78
- predicate: (value: A, index: SafeUint | -1) => boolean,
79
- ): SkipWhileOperatorObservable<A> =>
80
- createSyncChildObservable(
81
- {
84
+ class SkipWhileObservableClass<A>
85
+ extends SyncChildObservableClass<A, readonly [A]>
86
+ implements SkipWhileOperatorObservable<A>
87
+ {
88
+ readonly #predicate: (value: A, index: SafeUint | -1) => boolean;
89
+ #mut_index: SafeUint | -1;
90
+ #mut_isSkipping: boolean;
91
+
92
+ constructor(
93
+ parentObservable: Observable<A>,
94
+ predicate: (value: A, index: SafeUint | -1) => boolean,
95
+ ) {
96
+ super({
82
97
  parents: [parentObservable],
83
98
  initialValue: pipe(parentObservable.getSnapshot()).map((sn) =>
84
99
  Optional.isNone(sn) || predicate(sn.value, -1) ? Optional.none : sn,
85
100
  ).value,
86
- },
87
- ({ setNext }) => {
88
- let mut_index: SafeUint | -1 = -1;
101
+ });
102
+
103
+ this.#mut_index = -1;
104
+
105
+ this.#mut_isSkipping = true;
106
+
107
+ this.#predicate = predicate;
108
+
109
+ this.attach();
110
+ }
89
111
 
90
- let mut_skipping = true;
112
+ override tryUpdate(updateToken: UpdateToken): void {
113
+ const par = this.parents[0];
91
114
 
92
- return (updateToken) => {
93
- const sn = parentObservable.getSnapshot();
115
+ const sn = par.getSnapshot();
94
116
 
95
- if (
96
- parentObservable.updateToken !== updateToken ||
97
- Optional.isNone(sn)
98
- ) {
99
- return; // skip update
100
- }
117
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
118
+ return; // skip update
119
+ }
101
120
 
102
- mut_index =
103
- mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, mut_index);
121
+ this.#mut_index =
122
+ this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
104
123
 
105
- if (!predicate(sn.value, mut_index)) {
106
- mut_skipping = false;
107
- }
124
+ if (!this.#predicate(sn.value, this.#mut_index)) {
125
+ this.#mut_isSkipping = false;
126
+ }
108
127
 
109
- if (!mut_skipping) {
110
- setNext(sn.value, updateToken);
111
- }
112
- };
113
- },
114
- );
128
+ if (!this.#mut_isSkipping) {
129
+ this.setNext(sn.value, updateToken);
130
+ }
131
+ }
132
+ }