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,27 @@
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 { createAsyncChildObservable } from '../base/index.mjs';
9
+ import { AsyncChildObservableClass } from '../class/index.mjs';
3
10
  import type {
4
11
  DebounceOperatorObservable,
5
12
  KeepInitialValueOperator,
6
13
  Observable,
7
14
  TimerId,
15
+ UpdateToken,
8
16
  } from '../types/index.mjs';
9
17
 
10
18
  /**
11
19
  * Delays emissions from the source observable until a specified time has passed without another emission.
12
20
  * Useful for handling user input events like typing or scrolling.
13
21
  *
22
+ * Completing it, or its source completing, passes on the value its timer is
23
+ * still holding before the completion; `dispose()` drops that value.
24
+ *
14
25
  * @template A - The type of values from the source
15
26
  * @param milliSeconds - The debounce duration in milliseconds
16
27
  * @returns An operator that debounces the observable
@@ -97,50 +108,80 @@ export const debounce = <A,>(
97
108
  ): KeepInitialValueOperator<A, A> =>
98
109
  // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
99
110
  ((parentObservable) =>
100
- createDebounceObservable(
111
+ new DebounceObservableClass(
101
112
  parentObservable,
102
113
  milliSeconds,
103
114
  )) as KeepInitialValueOperator<A, A>;
104
115
 
105
- const createDebounceObservable = <A,>(
106
- parentObservable: Observable<A>,
107
- milliSeconds: number,
108
- ): DebounceOperatorObservable<A> => {
109
- let mut_timerId: TimerId | undefined = undefined;
116
+ class DebounceObservableClass<A>
117
+ extends AsyncChildObservableClass<A, readonly [A]>
118
+ implements DebounceOperatorObservable<A>
119
+ {
120
+ readonly #milliSeconds: number;
121
+ #mut_timerId: TimerId | undefined;
110
122
 
111
- const resetTimer = (): void => {
112
- if (mut_timerId !== undefined) {
113
- clearTimeout(mut_timerId);
114
- }
115
- };
123
+ /** The value the running timer will pass on. */
124
+ #mut_pending: Optional<A>;
116
125
 
117
- return createAsyncChildObservable(
118
- {
126
+ constructor(parentObservable: Observable<A>, milliSeconds: number) {
127
+ super({
119
128
  parents: [parentObservable],
120
129
  initialValue: parentObservable.getSnapshot(),
121
- onComplete: resetTimer,
122
- },
123
- ({ startUpdate }) =>
124
- (updateToken) => {
125
- const sn = parentObservable.getSnapshot();
126
-
127
- if (
128
- parentObservable.updateToken !== updateToken ||
129
- Optional.isNone(sn)
130
- ) {
131
- return; // skip update
132
- }
133
-
134
- resetTimer();
135
-
136
- // set timer
137
- mut_timerId = setTimeout(() => {
138
- if (Optional.isNone(sn)) {
139
- return;
140
- }
141
-
142
- startUpdate(sn.value);
143
- }, milliSeconds);
144
- },
145
- );
146
- };
130
+ });
131
+
132
+ this.#mut_timerId = undefined;
133
+
134
+ this.#mut_pending = Optional.none;
135
+
136
+ this.#milliSeconds = milliSeconds;
137
+
138
+ this.attach();
139
+ }
140
+
141
+ #resetTimer(): void {
142
+ if (this.#mut_timerId !== undefined) {
143
+ clearTimeout(this.#mut_timerId);
144
+
145
+ this.#mut_timerId = undefined;
146
+ }
147
+
148
+ this.#mut_pending = Optional.none;
149
+ }
150
+
151
+ override tryUpdate(updateToken: UpdateToken): void {
152
+ const par = this.parents[0];
153
+
154
+ const sn = par.getSnapshot();
155
+
156
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
157
+ return; // skip update
158
+ }
159
+
160
+ this.#resetTimer();
161
+
162
+ this.#mut_pending = sn;
163
+
164
+ // set timer
165
+ this.#mut_timerId = setTimeout(() => {
166
+ this.#mut_timerId = undefined;
167
+
168
+ this.#mut_pending = Optional.none;
169
+
170
+ this.startUpdate(sn.value);
171
+ }, this.#milliSeconds);
172
+ }
173
+
174
+ protected override flush(): void {
175
+ const pending = this.#mut_pending;
176
+
177
+ this.#resetTimer();
178
+
179
+ if (Optional.isSome(pending)) {
180
+ this.startUpdate(pending.value);
181
+ }
182
+ }
183
+
184
+ protected override teardown(): void {
185
+ this.#resetTimer();
186
+ }
187
+ }
@@ -1,3 +1,10 @@
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 {
2
9
  asSafeUint,
3
10
  expectType,
@@ -7,13 +14,14 @@ import {
7
14
  pipe,
8
15
  SafeUint,
9
16
  } from 'ts-data-forge';
10
- import { createSyncChildObservable } from '../base/index.mjs';
17
+ import { SyncChildObservableClass } from '../class/index.mjs';
11
18
  import { source } from '../create/index.mjs';
12
19
  import type {
13
20
  DropInitialValueOperator,
14
21
  FilterOperatorObservable,
15
22
  InitializedObservable,
16
23
  Observable,
24
+ UpdateToken,
17
25
  } from '../types/index.mjs';
18
26
  import { withInitialValue } from './with-initial-value.mjs';
19
27
 
@@ -83,15 +91,21 @@ export function filter<A>(
83
91
  predicate: (value: A, index: SafeUint | -1) => boolean,
84
92
  ): DropInitialValueOperator<A, A> {
85
93
  return (parentObservable) =>
86
- createFilterObservable(parentObservable, predicate);
94
+ new FilterObservableClass(parentObservable, predicate);
87
95
  }
88
96
 
89
- const createFilterObservable = <A,>(
90
- parentObservable: Observable<A>,
91
- predicate: (x: A, index: SafeUint | -1) => boolean,
92
- ): FilterOperatorObservable<A> =>
93
- createSyncChildObservable(
94
- {
97
+ class FilterObservableClass<A>
98
+ extends SyncChildObservableClass<A, readonly [A]>
99
+ implements FilterOperatorObservable<A>
100
+ {
101
+ readonly #predicate: (x: A, index: SafeUint | -1) => boolean;
102
+ #mut_index: SafeUint | -1;
103
+
104
+ constructor(
105
+ parentObservable: Observable<A>,
106
+ predicate: (x: A, index: SafeUint | -1) => boolean,
107
+ ) {
108
+ super({
95
109
  parents: [parentObservable],
96
110
  initialValue: pipe(parentObservable.getSnapshot()).map((sn) =>
97
111
  Optional.isNone(sn)
@@ -100,29 +114,32 @@ const createFilterObservable = <A,>(
100
114
  ? sn
101
115
  : Optional.none,
102
116
  ).value,
103
- },
104
- ({ setNext }) => {
105
- let mut_index: SafeUint | -1 = -1;
106
-
107
- return (updateToken) => {
108
- const sn = parentObservable.getSnapshot();
109
-
110
- if (
111
- parentObservable.updateToken !== updateToken ||
112
- Optional.isNone(sn)
113
- ) {
114
- return; // skip update
115
- }
116
-
117
- mut_index =
118
- mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, mut_index);
119
-
120
- if (predicate(sn.value, mut_index)) {
121
- setNext(sn.value, updateToken);
122
- }
123
- };
124
- },
125
- );
117
+ });
118
+
119
+ this.#mut_index = -1;
120
+
121
+ this.#predicate = predicate;
122
+
123
+ this.attach();
124
+ }
125
+
126
+ override tryUpdate(updateToken: UpdateToken): void {
127
+ const par = this.parents[0];
128
+
129
+ const sn = par.getSnapshot();
130
+
131
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
132
+ return; // skip update
133
+ }
134
+
135
+ this.#mut_index =
136
+ this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
137
+
138
+ if (this.#predicate(sn.value, this.#mut_index)) {
139
+ this.setNext(sn.value, updateToken);
140
+ }
141
+ }
142
+ }
126
143
 
127
144
  if (import.meta.vitest !== undefined) {
128
145
  test('type test', () => {
@@ -11,6 +11,7 @@ export * from './skip-while.mjs';
11
11
  export * from './switch-map.mjs';
12
12
  export * from './take-until.mjs';
13
13
  export * from './take-while.mjs';
14
+ export * from './take.mjs';
14
15
  export * from './throttle.mjs';
15
16
  export * from './with-buffered-from.mjs';
16
17
  export * from './with-current-value-from.mjs';
@@ -0,0 +1,280 @@
1
+ import { source } from '../create/index.mjs';
2
+ import { createScope } from '../scope/index.mjs';
3
+ import { map } from './map.mjs';
4
+ import { mergeMap } from './merge-map.mjs';
5
+ import { switchMap } from './switch-map.mjs';
6
+
7
+ describe('switchMap releases only the inner observables its projection created (#2335)', () => {
8
+ test('a shared inner survives a switch', () => {
9
+ const user$ = source<'a' | 'b'>();
10
+
11
+ const a$ = source<number>();
12
+
13
+ const b$ = source<number>();
14
+
15
+ const current$ = user$.pipe(switchMap((u) => (u === 'a' ? a$ : b$)));
16
+
17
+ const mut_current: number[] = [];
18
+
19
+ current$.subscribe((v) => {
20
+ mut_current.push(v);
21
+ });
22
+
23
+ const mut_others: number[] = [];
24
+
25
+ a$.subscribe((v) => {
26
+ mut_others.push(v);
27
+ });
28
+
29
+ user$.next('a');
30
+
31
+ a$.next(1);
32
+
33
+ user$.next('b');
34
+
35
+ assert.isFalse(a$.isCompleted);
36
+
37
+ a$.next(2);
38
+
39
+ b$.next(3);
40
+
41
+ assert.deepStrictEqual(mut_others, [1, 2]);
42
+
43
+ assert.deepStrictEqual(mut_current, [1, 3]);
44
+ });
45
+
46
+ test('a shared inner survives the completion of switchMap', () => {
47
+ const user$ = source<'a'>();
48
+
49
+ const a$ = source<number>();
50
+
51
+ const current$ = user$.pipe(switchMap(() => a$));
52
+
53
+ current$.subscribe(() => {});
54
+
55
+ user$.next('a');
56
+
57
+ current$.complete();
58
+
59
+ assert.isFalse(a$.isCompleted);
60
+
61
+ const mut_others: number[] = [];
62
+
63
+ a$.subscribe((v) => {
64
+ mut_others.push(v);
65
+ });
66
+
67
+ a$.next(1);
68
+
69
+ assert.deepStrictEqual(mut_others, [1]);
70
+ });
71
+
72
+ test('a node derived in the projection from a shared observable is released, and the shared one survives', () => {
73
+ const user$ = source<number>();
74
+
75
+ const shared$ = source<number>();
76
+
77
+ const mut_derived: { isCompleted: boolean }[] = [];
78
+
79
+ const current$ = user$.pipe(
80
+ switchMap((u) => {
81
+ const derived$ = shared$.pipe(map((x) => x + u));
82
+
83
+ mut_derived.push(derived$);
84
+
85
+ return derived$;
86
+ }),
87
+ );
88
+
89
+ current$.subscribe(() => {});
90
+
91
+ user$.next(1);
92
+
93
+ user$.next(2);
94
+
95
+ assert.deepStrictEqual(
96
+ mut_derived.map((o) => o.isCompleted),
97
+ [true, false],
98
+ );
99
+
100
+ assert.isFalse(shared$.isCompleted);
101
+
102
+ current$.complete();
103
+
104
+ assert.deepStrictEqual(
105
+ mut_derived.map((o) => o.isCompleted),
106
+ [true, true],
107
+ );
108
+
109
+ assert.isFalse(shared$.isCompleted);
110
+ });
111
+
112
+ test('every node the projection created is released on a switch, not only the returned one', () => {
113
+ const user$ = source<number>();
114
+
115
+ const mut_created: { isCompleted: boolean }[] = [];
116
+
117
+ const current$ = user$.pipe(
118
+ switchMap((u) => {
119
+ const inner$ = source<number>();
120
+
121
+ const mapped$ = inner$.pipe(map((x) => x + u));
122
+
123
+ mut_created.push(inner$, mapped$);
124
+
125
+ return mapped$;
126
+ }),
127
+ );
128
+
129
+ current$.subscribe(() => {});
130
+
131
+ user$.next(1);
132
+
133
+ user$.next(2);
134
+
135
+ assert.deepStrictEqual(
136
+ mut_created.map((o) => o.isCompleted),
137
+ [true, true, false, false],
138
+ );
139
+
140
+ current$.complete();
141
+
142
+ assert.deepStrictEqual(
143
+ mut_created.map((o) => o.isCompleted),
144
+ [true, true, true, true],
145
+ );
146
+ });
147
+ });
148
+
149
+ describe('the scope switchMap and mergeMap were created in (#2335)', () => {
150
+ test('disposing it releases what their projections created, and the shared inners survive', () => {
151
+ const user$ = source<number>();
152
+
153
+ const shared$ = source<number>();
154
+
155
+ const mut_created: { isCompleted: boolean }[] = [];
156
+
157
+ const scope = createScope();
158
+
159
+ scope.run(() => {
160
+ user$
161
+ .pipe(
162
+ switchMap(() => {
163
+ const inner$ = source<number>();
164
+
165
+ mut_created.push(inner$);
166
+
167
+ return inner$;
168
+ }),
169
+ )
170
+ .subscribe(() => {});
171
+
172
+ user$.pipe(mergeMap(() => shared$)).subscribe(() => {});
173
+ });
174
+
175
+ user$.next(1);
176
+
177
+ assert.deepStrictEqual(
178
+ mut_created.map((o) => o.isCompleted),
179
+ [false],
180
+ );
181
+
182
+ scope.dispose();
183
+
184
+ assert.deepStrictEqual(
185
+ mut_created.map((o) => o.isCompleted),
186
+ [true],
187
+ );
188
+
189
+ assert.isFalse(shared$.isCompleted);
190
+
191
+ assert.isFalse(user$.isCompleted);
192
+ });
193
+ });
194
+
195
+ describe('mergeMap releases only the inner observables its projection created (#2335)', () => {
196
+ test('a shared inner survives the completion of mergeMap', () => {
197
+ const user$ = source<'a' | 'b'>();
198
+
199
+ const a$ = source<number>();
200
+
201
+ const b$ = source<number>();
202
+
203
+ const merged$ = user$.pipe(mergeMap((u) => (u === 'a' ? a$ : b$)));
204
+
205
+ const mut_merged: number[] = [];
206
+
207
+ merged$.subscribe((v) => {
208
+ mut_merged.push(v);
209
+ });
210
+
211
+ user$.next('a');
212
+
213
+ user$.next('b');
214
+
215
+ a$.next(1);
216
+
217
+ b$.next(2);
218
+
219
+ assert.deepStrictEqual(mut_merged, [1, 2]);
220
+
221
+ merged$.complete();
222
+
223
+ assert.isFalse(a$.isCompleted);
224
+
225
+ assert.isFalse(b$.isCompleted);
226
+
227
+ const mut_others: number[] = [];
228
+
229
+ a$.subscribe((v) => {
230
+ mut_others.push(v);
231
+ });
232
+
233
+ a$.next(3);
234
+
235
+ // A new subscriber receives the current value first.
236
+ assert.deepStrictEqual(mut_others, [1, 3]);
237
+
238
+ assert.deepStrictEqual(mut_merged, [1, 2]);
239
+ });
240
+
241
+ test('every node the projection created is released on completion', () => {
242
+ const user$ = source<number>();
243
+
244
+ const shared$ = source<number>();
245
+
246
+ const mut_created: { isCompleted: boolean }[] = [];
247
+
248
+ const merged$ = user$.pipe(
249
+ mergeMap((u) => {
250
+ const inner$ = source<number>();
251
+
252
+ const derived$ = shared$.pipe(map((x) => x + u));
253
+
254
+ mut_created.push(inner$, derived$);
255
+
256
+ return inner$;
257
+ }),
258
+ );
259
+
260
+ merged$.subscribe(() => {});
261
+
262
+ user$.next(1);
263
+
264
+ user$.next(2);
265
+
266
+ assert.deepStrictEqual(
267
+ mut_created.map((o) => o.isCompleted),
268
+ [false, false, false, false],
269
+ );
270
+
271
+ merged$.complete();
272
+
273
+ assert.deepStrictEqual(
274
+ mut_created.map((o) => o.isCompleted),
275
+ [true, true, true, true],
276
+ );
277
+
278
+ assert.isFalse(shared$.isCompleted);
279
+ });
280
+ });
@@ -1,11 +1,19 @@
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, expectType } from 'ts-data-forge';
2
- import { createSyncChildObservable } from '../base/index.mjs';
9
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
10
  import { source } from '../create/index.mjs';
4
11
  import type {
5
12
  InitializedObservable,
6
13
  KeepInitialValueOperator,
7
14
  MapOperatorObservable,
8
15
  Observable,
16
+ UpdateToken,
9
17
  } from '../types/index.mjs';
10
18
  import { withInitialValue } from './with-initial-value.mjs';
11
19
 
@@ -52,44 +60,53 @@ export const map = <A, B>(
52
60
  ): KeepInitialValueOperator<A, B> =>
53
61
  // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
54
62
  ((parentObservable) =>
55
- createMapObservable(parentObservable, mapFn)) as KeepInitialValueOperator<
56
- A,
57
- B
58
- >;
63
+ new MapObservableClass(
64
+ parentObservable,
65
+ mapFn,
66
+ )) as KeepInitialValueOperator<A, B>;
59
67
 
60
68
  /* implementation */
61
69
 
62
- const createMapObservable = <A, B>(
63
- parentObservable: Observable<A>,
64
- mapFn: (x: A, index: SafeUint | -1) => B,
65
- ): MapOperatorObservable<A, B> =>
66
- createSyncChildObservable(
67
- {
70
+ class MapObservableClass<A, B>
71
+ extends SyncChildObservableClass<B, readonly [A]>
72
+ implements MapOperatorObservable<A, B>
73
+ {
74
+ readonly #mapFn: (x: A, index: SafeUint | -1) => B;
75
+ #mut_index: SafeUint | -1;
76
+
77
+ constructor(
78
+ parentObservable: Observable<A>,
79
+ mapFn: (x: A, index: SafeUint | -1) => B,
80
+ ) {
81
+ super({
68
82
  parents: [parentObservable],
69
83
  initialValue: Optional.map(parentObservable.getSnapshot(), (x) =>
70
84
  mapFn(x, -1),
71
85
  ),
72
- },
73
- ({ setNext }) => {
74
- let mut_index: SafeUint | -1 = -1;
75
-
76
- return (updateToken) => {
77
- const sn = parentObservable.getSnapshot();
78
-
79
- if (
80
- parentObservable.updateToken !== updateToken ||
81
- Optional.isNone(sn)
82
- ) {
83
- return; // skip update
84
- }
85
-
86
- mut_index =
87
- mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, mut_index);
88
-
89
- setNext(mapFn(sn.value, mut_index), updateToken);
90
- };
91
- },
92
- );
86
+ });
87
+
88
+ this.#mut_index = -1;
89
+
90
+ this.#mapFn = mapFn;
91
+
92
+ this.attach();
93
+ }
94
+
95
+ override tryUpdate(updateToken: UpdateToken): void {
96
+ const par = this.parents[0];
97
+
98
+ const sn = par.getSnapshot();
99
+
100
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
101
+ return; // skip update
102
+ }
103
+
104
+ this.#mut_index =
105
+ this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
106
+
107
+ this.setNext(this.#mapFn(sn.value, this.#mut_index), updateToken);
108
+ }
109
+ }
93
110
 
94
111
  if (import.meta.vitest !== undefined) {
95
112
  test('type test', () => {