synstate 3.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. package/README.md +13 -8
  2. package/dist/core/authoring.d.mts +7 -0
  3. package/dist/core/authoring.d.mts.map +1 -0
  4. package/dist/core/authoring.mjs +7 -0
  5. package/dist/core/authoring.mjs.map +1 -0
  6. package/dist/core/class/child-observable-class.d.mts +93 -0
  7. package/dist/core/class/child-observable-class.d.mts.map +1 -0
  8. package/dist/core/class/child-observable-class.mjs +248 -0
  9. package/dist/core/class/child-observable-class.mjs.map +1 -0
  10. package/dist/core/class/create-child-observable.d.mts +159 -0
  11. package/dist/core/class/create-child-observable.d.mts.map +1 -0
  12. package/dist/core/class/create-child-observable.mjs +183 -0
  13. package/dist/core/class/create-child-observable.mjs.map +1 -0
  14. package/dist/core/class/create-root-observable.d.mts +37 -0
  15. package/dist/core/class/create-root-observable.d.mts.map +1 -0
  16. package/dist/core/class/create-root-observable.mjs +52 -0
  17. package/dist/core/class/create-root-observable.mjs.map +1 -0
  18. package/dist/core/class/index.d.mts +9 -0
  19. package/dist/core/class/index.d.mts.map +1 -0
  20. package/dist/core/class/index.mjs +9 -0
  21. package/dist/core/class/index.mjs.map +1 -0
  22. package/dist/core/class/manager-observable-class.d.mts +26 -0
  23. package/dist/core/class/manager-observable-class.d.mts.map +1 -0
  24. package/dist/core/class/manager-observable-class.mjs +116 -0
  25. package/dist/core/class/manager-observable-class.mjs.map +1 -0
  26. package/dist/core/class/observable-base-class.d.mts +97 -0
  27. package/dist/core/class/observable-base-class.d.mts.map +1 -0
  28. package/dist/core/class/observable-base-class.mjs +219 -0
  29. package/dist/core/class/observable-base-class.mjs.map +1 -0
  30. package/dist/core/class/observable-node.d.mts +40 -0
  31. package/dist/core/class/observable-node.d.mts.map +1 -0
  32. package/dist/core/class/observable-node.mjs +10 -0
  33. package/dist/core/class/observable-node.mjs.map +1 -0
  34. package/dist/core/class/propagation-state.d.mts +24 -0
  35. package/dist/core/class/propagation-state.d.mts.map +1 -0
  36. package/dist/core/class/propagation-state.mjs +66 -0
  37. package/dist/core/class/propagation-state.mjs.map +1 -0
  38. package/dist/core/class/root-observable-class.d.mts +23 -0
  39. package/dist/core/class/root-observable-class.d.mts.map +1 -0
  40. package/dist/core/class/root-observable-class.mjs +58 -0
  41. package/dist/core/class/root-observable-class.mjs.map +1 -0
  42. package/dist/core/combine/combine.d.mts.map +1 -1
  43. package/dist/core/combine/combine.mjs +25 -14
  44. package/dist/core/combine/combine.mjs.map +1 -1
  45. package/dist/core/combine/merge.d.mts.map +1 -1
  46. package/dist/core/combine/merge.mjs +26 -14
  47. package/dist/core/combine/merge.mjs.map +1 -1
  48. package/dist/core/combine/zip.d.mts.map +1 -1
  49. package/dist/core/combine/zip.mjs +43 -25
  50. package/dist/core/combine/zip.mjs.map +1 -1
  51. package/dist/core/create/counter.d.mts.map +1 -1
  52. package/dist/core/create/counter.mjs +65 -44
  53. package/dist/core/create/counter.mjs.map +1 -1
  54. package/dist/core/create/from-abortable-promise.d.mts.map +1 -1
  55. package/dist/core/create/from-abortable-promise.mjs +26 -18
  56. package/dist/core/create/from-abortable-promise.mjs.map +1 -1
  57. package/dist/core/create/from-promise.d.mts.map +1 -1
  58. package/dist/core/create/from-promise.mjs +30 -21
  59. package/dist/core/create/from-promise.mjs.map +1 -1
  60. package/dist/core/create/from-subscribable.d.mts.map +1 -1
  61. package/dist/core/create/from-subscribable.mjs +37 -24
  62. package/dist/core/create/from-subscribable.mjs.map +1 -1
  63. package/dist/core/create/just.d.mts.map +1 -1
  64. package/dist/core/create/just.mjs +4 -2
  65. package/dist/core/create/just.mjs.map +1 -1
  66. package/dist/core/create/source.d.mts.map +1 -1
  67. package/dist/core/create/source.mjs +27 -17
  68. package/dist/core/create/source.mjs.map +1 -1
  69. package/dist/core/create/timer.d.mts.map +1 -1
  70. package/dist/core/create/timer.mjs +40 -25
  71. package/dist/core/create/timer.mjs.map +1 -1
  72. package/dist/core/index.d.mts +2 -0
  73. package/dist/core/index.d.mts.map +1 -1
  74. package/dist/core/index.mjs +2 -0
  75. package/dist/core/index.mjs.map +1 -1
  76. package/dist/core/operators/audit.d.mts +3 -0
  77. package/dist/core/operators/audit.d.mts.map +1 -1
  78. package/dist/core/operators/audit.mjs +56 -28
  79. package/dist/core/operators/audit.mjs.map +1 -1
  80. package/dist/core/operators/debounce.d.mts +3 -0
  81. package/dist/core/operators/debounce.d.mts.map +1 -1
  82. package/dist/core/operators/debounce.mjs +57 -26
  83. package/dist/core/operators/debounce.mjs.map +1 -1
  84. package/dist/core/operators/filter.d.mts.map +1 -1
  85. package/dist/core/operators/filter.mjs +35 -21
  86. package/dist/core/operators/filter.mjs.map +1 -1
  87. package/dist/core/operators/index.d.mts +1 -0
  88. package/dist/core/operators/index.d.mts.map +1 -1
  89. package/dist/core/operators/index.mjs +1 -0
  90. package/dist/core/operators/index.mjs.map +1 -1
  91. package/dist/core/operators/map.d.mts.map +1 -1
  92. package/dist/core/operators/map.mjs +31 -17
  93. package/dist/core/operators/map.mjs.map +1 -1
  94. package/dist/core/operators/merge-map.d.mts +5 -0
  95. package/dist/core/operators/merge-map.d.mts.map +1 -1
  96. package/dist/core/operators/merge-map.mjs +52 -26
  97. package/dist/core/operators/merge-map.mjs.map +1 -1
  98. package/dist/core/operators/pairwise.d.mts.map +1 -1
  99. package/dist/core/operators/pairwise.mjs +31 -20
  100. package/dist/core/operators/pairwise.mjs.map +1 -1
  101. package/dist/core/operators/scan.d.mts.map +1 -1
  102. package/dist/core/operators/scan.mjs +27 -14
  103. package/dist/core/operators/scan.mjs.map +1 -1
  104. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  105. package/dist/core/operators/skip-if-no-change.mjs +33 -19
  106. package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
  107. package/dist/core/operators/skip-until.d.mts.map +1 -1
  108. package/dist/core/operators/skip-until.mjs +40 -26
  109. package/dist/core/operators/skip-until.mjs.map +1 -1
  110. package/dist/core/operators/skip-while.d.mts.map +1 -1
  111. package/dist/core/operators/skip-while.mjs +35 -20
  112. package/dist/core/operators/skip-while.mjs.map +1 -1
  113. package/dist/core/operators/switch-map.d.mts +5 -0
  114. package/dist/core/operators/switch-map.d.mts.map +1 -1
  115. package/dist/core/operators/switch-map.mjs +56 -25
  116. package/dist/core/operators/switch-map.mjs.map +1 -1
  117. package/dist/core/operators/take-until.d.mts.map +1 -1
  118. package/dist/core/operators/take-until.mjs +36 -29
  119. package/dist/core/operators/take-until.mjs.map +1 -1
  120. package/dist/core/operators/take-while.d.mts.map +1 -1
  121. package/dist/core/operators/take-while.mjs +36 -22
  122. package/dist/core/operators/take-while.mjs.map +1 -1
  123. package/dist/core/{predefined/operators → operators}/take.d.mts +7 -3
  124. package/dist/core/operators/take.d.mts.map +1 -0
  125. package/dist/core/operators/take.mjs +102 -0
  126. package/dist/core/operators/take.mjs.map +1 -0
  127. package/dist/core/operators/throttle.d.mts.map +1 -1
  128. package/dist/core/operators/throttle.mjs +41 -26
  129. package/dist/core/operators/throttle.mjs.map +1 -1
  130. package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
  131. package/dist/core/operators/with-buffered-from.mjs +43 -39
  132. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  133. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  134. package/dist/core/operators/with-current-value-from.mjs +36 -21
  135. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  136. package/dist/core/operators/with-initial-value.d.mts.map +1 -1
  137. package/dist/core/operators/with-initial-value.mjs +24 -12
  138. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  139. package/dist/core/predefined/operators/index.d.mts +0 -1
  140. package/dist/core/predefined/operators/index.d.mts.map +1 -1
  141. package/dist/core/predefined/operators/index.mjs +0 -1
  142. package/dist/core/predefined/operators/index.mjs.map +1 -1
  143. package/dist/core/scope/index.d.mts +2 -0
  144. package/dist/core/scope/index.d.mts.map +1 -0
  145. package/dist/core/scope/index.mjs +2 -0
  146. package/dist/core/scope/index.mjs.map +1 -0
  147. package/dist/core/scope/scope.d.mts +51 -0
  148. package/dist/core/scope/scope.d.mts.map +1 -0
  149. package/dist/core/scope/scope.mjs +194 -0
  150. package/dist/core/scope/scope.mjs.map +1 -0
  151. package/dist/core/types/id.d.mts +0 -2
  152. package/dist/core/types/id.d.mts.map +1 -1
  153. package/dist/core/types/index.d.mts +1 -0
  154. package/dist/core/types/index.d.mts.map +1 -1
  155. package/dist/core/types/index.mjs +1 -0
  156. package/dist/core/types/index.mjs.map +1 -1
  157. package/dist/core/types/observable-family.d.mts +1 -0
  158. package/dist/core/types/observable-family.d.mts.map +1 -1
  159. package/dist/core/types/observable.d.mts +40 -28
  160. package/dist/core/types/observable.d.mts.map +1 -1
  161. package/dist/core/types/observable.mjs +24 -0
  162. package/dist/core/types/observable.mjs.map +1 -1
  163. package/dist/core/types/scope.d.mts +27 -0
  164. package/dist/core/types/scope.d.mts.map +1 -0
  165. package/dist/core/types/scope.mjs +2 -0
  166. package/dist/core/types/scope.mjs.map +1 -0
  167. package/dist/core/utils/current-scope.d.mts +30 -0
  168. package/dist/core/utils/current-scope.d.mts.map +1 -0
  169. package/dist/core/utils/current-scope.mjs +26 -0
  170. package/dist/core/utils/current-scope.mjs.map +1 -0
  171. package/dist/core/utils/id-maker.d.mts +14 -3
  172. package/dist/core/utils/id-maker.d.mts.map +1 -1
  173. package/dist/core/utils/id-maker.mjs +18 -14
  174. package/dist/core/utils/id-maker.mjs.map +1 -1
  175. package/dist/core/utils/index.d.mts +2 -1
  176. package/dist/core/utils/index.d.mts.map +1 -1
  177. package/dist/core/utils/index.mjs +2 -1
  178. package/dist/core/utils/index.mjs.map +1 -1
  179. package/dist/core/utils/max-depth.d.mts +1 -0
  180. package/dist/core/utils/max-depth.d.mts.map +1 -1
  181. package/dist/core/utils/max-depth.mjs +9 -7
  182. package/dist/core/utils/max-depth.mjs.map +1 -1
  183. package/dist/core/utils/observable-brand.d.mts +13 -0
  184. package/dist/core/utils/observable-brand.d.mts.map +1 -0
  185. package/dist/core/utils/observable-brand.mjs +13 -0
  186. package/dist/core/utils/observable-brand.mjs.map +1 -0
  187. package/dist/utils/create-boolean-state.d.mts +2 -2
  188. package/dist/utils/create-reducer.d.mts +7 -7
  189. package/dist/utils/create-reducer.mjs +2 -2
  190. package/dist/utils/create-reducer.mjs.map +1 -1
  191. package/dist/utils/create-state.d.mts +2 -2
  192. package/package.json +16 -16
  193. package/src/core/authoring.mts +19 -0
  194. package/src/core/class/child-observable-class.mts +374 -0
  195. package/src/core/{base → class}/circular-dependency-comparison.test.mts +46 -38
  196. package/src/core/{base → class}/circular-dependency.test.mts +38 -2
  197. package/src/core/class/complete-propagation.test.mts +264 -0
  198. package/src/core/class/create-child-observable.mts +370 -0
  199. package/src/core/class/create-root-observable.mts +107 -0
  200. package/src/core/class/index.mts +8 -0
  201. package/src/core/class/manager-observable-class.mts +166 -0
  202. package/src/core/class/observable-base-class.mts +315 -0
  203. package/src/core/{base → class}/observable-base.test.mts +1 -1
  204. package/src/core/class/observable-node.mts +54 -0
  205. package/src/core/class/propagation-order.test.mts +92 -0
  206. package/src/core/class/propagation-state.mts +80 -0
  207. package/src/core/class/remove-completed.test.mts +369 -0
  208. package/src/core/class/root-observable-class.mts +85 -0
  209. package/src/core/{base → class}/write-during-propagation.test.mts +3 -3
  210. package/src/core/combine/combine.mts +67 -55
  211. package/src/core/combine/merge.mts +33 -23
  212. package/src/core/combine/zip.mts +95 -81
  213. package/src/core/create/counter.mts +95 -65
  214. package/src/core/create/from-abortable-promise.mts +54 -42
  215. package/src/core/create/from-promise.mts +38 -29
  216. package/src/core/create/from-subscribable.mts +62 -45
  217. package/src/core/create/from-subscribable.test.mts +8 -3
  218. package/src/core/create/just.mts +4 -5
  219. package/src/core/create/source.mts +29 -20
  220. package/src/core/create/timer.mts +51 -32
  221. package/src/core/index.mts +2 -0
  222. package/src/core/operators/audit.mts +79 -45
  223. package/src/core/operators/debounce-audit-complete.test.mts +121 -0
  224. package/src/core/operators/debounce.mts +81 -40
  225. package/src/core/operators/filter.mts +48 -31
  226. package/src/core/operators/index.mts +1 -0
  227. package/src/core/operators/inner-ownership.test.mts +280 -0
  228. package/src/core/operators/map.mts +49 -32
  229. package/src/core/operators/merge-map.mts +81 -43
  230. package/src/core/operators/pairwise.mts +40 -30
  231. package/src/core/operators/scan.mts +41 -24
  232. package/src/core/operators/skip-if-no-change.mts +45 -31
  233. package/src/core/operators/skip-until.mts +55 -41
  234. package/src/core/operators/skip-while.mts +49 -31
  235. package/src/core/operators/switch-map.mts +74 -35
  236. package/src/core/operators/take-until-skip-until-teardown.test.mts +18 -12
  237. package/src/core/operators/take-until.mts +45 -41
  238. package/src/core/operators/take-while.mts +50 -33
  239. package/src/core/operators/take.mts +138 -0
  240. package/src/core/operators/throttle.mts +55 -38
  241. package/src/core/operators/with-buffered-from.mts +42 -44
  242. package/src/core/operators/with-buffered-from.test.mts +4 -3
  243. package/src/core/operators/with-current-value-from.mts +39 -25
  244. package/src/core/operators/with-initial-value.mts +30 -21
  245. package/src/core/predefined/operators/index.mts +0 -1
  246. package/src/core/scope/index.mts +1 -0
  247. package/src/core/scope/scope.mts +260 -0
  248. package/src/core/scope/scope.test.mts +462 -0
  249. package/src/core/types/id.mts +0 -4
  250. package/src/core/types/index.mts +1 -0
  251. package/src/core/types/observable-family.mts +2 -0
  252. package/src/core/types/observable.mts +77 -42
  253. package/src/core/types/scope.mts +27 -0
  254. package/src/core/utils/current-scope.mts +61 -0
  255. package/src/core/utils/id-maker.mts +20 -30
  256. package/src/core/utils/index.mts +2 -1
  257. package/src/core/utils/max-depth.mts +12 -9
  258. package/src/core/utils/observable-brand.mts +12 -0
  259. package/src/utils/create-boolean-state.mts +2 -2
  260. package/src/utils/create-boolean-state.test.mts +18 -4
  261. package/src/utils/create-reducer.mts +9 -9
  262. package/src/utils/create-reducer.test.mts +31 -14
  263. package/src/utils/create-state.mts +2 -2
  264. package/src/utils/create-state.test.mts +12 -14
  265. package/dist/core/base/create-child-observable.d.mts +0 -72
  266. package/dist/core/base/create-child-observable.d.mts.map +0 -1
  267. package/dist/core/base/create-child-observable.mjs +0 -285
  268. package/dist/core/base/create-child-observable.mjs.map +0 -1
  269. package/dist/core/base/create-observable-base.d.mts +0 -149
  270. package/dist/core/base/create-observable-base.d.mts.map +0 -1
  271. package/dist/core/base/create-observable-base.mjs +0 -273
  272. package/dist/core/base/create-observable-base.mjs.map +0 -1
  273. package/dist/core/base/create-root-observable.d.mts +0 -46
  274. package/dist/core/base/create-root-observable.d.mts.map +0 -1
  275. package/dist/core/base/create-root-observable.mjs +0 -80
  276. package/dist/core/base/create-root-observable.mjs.map +0 -1
  277. package/dist/core/base/index.d.mts +0 -3
  278. package/dist/core/base/index.d.mts.map +0 -1
  279. package/dist/core/base/index.mjs +0 -3
  280. package/dist/core/base/index.mjs.map +0 -1
  281. package/dist/core/predefined/operators/take.d.mts.map +0 -1
  282. package/dist/core/predefined/operators/take.mjs +0 -44
  283. package/dist/core/predefined/operators/take.mjs.map +0 -1
  284. package/dist/core/utils/utils.d.mts +0 -6
  285. package/dist/core/utils/utils.d.mts.map +0 -1
  286. package/dist/core/utils/utils.mjs +0 -37
  287. package/dist/core/utils/utils.mjs.map +0 -1
  288. package/src/core/base/create-child-observable.mts +0 -485
  289. package/src/core/base/create-observable-base.mts +0 -568
  290. package/src/core/base/create-root-observable.mts +0 -147
  291. package/src/core/base/index.mts +0 -2
  292. package/src/core/predefined/operators/take.mts +0 -48
  293. package/src/core/utils/utils.mts +0 -56
  294. package/src/core/utils/utils.test.mts +0 -88
@@ -1,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
- 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
  Subscription,
7
14
  TakeUntilOperatorObservable,
15
+ UpdateToken,
8
16
  } from '../types/index.mjs';
9
17
 
10
18
  /**
@@ -60,56 +68,52 @@ export const takeUntil = <A,>(
60
68
  ): KeepInitialValueOperator<A, A> =>
61
69
  // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
62
70
  ((parentObservable) =>
63
- createTakeUntilObservable(
71
+ new TakeUntilObservableClass(
64
72
  parentObservable,
65
73
  notifier,
66
74
  )) as KeepInitialValueOperator<A, A>;
67
75
 
68
- const createTakeUntilObservable = <A,>(
69
- parentObservable: Observable<A>,
70
- notifier: Observable<unknown>,
71
- ): TakeUntilOperatorObservable<A> => {
72
- let mut_subscription: Subscription | undefined = undefined;
76
+ class TakeUntilObservableClass<A>
77
+ extends SyncChildObservableClass<A, readonly [A]>
78
+ implements TakeUntilOperatorObservable<A>
79
+ {
80
+ /** Unset only while the `subscribe` in the constructor runs. */
81
+ readonly #subscription: Subscription | undefined;
73
82
 
74
- const observable = createSyncChildObservable<A, readonly [A]>(
75
- {
83
+ constructor(parentObservable: Observable<A>, notifier: Observable<unknown>) {
84
+ super({
76
85
  parents: [parentObservable],
77
86
  initialValue: parentObservable.getSnapshot(),
78
- onComplete: () => {
79
- mut_subscription?.unsubscribe();
80
- },
81
- },
82
- ({ setNext }) =>
83
- (updateToken) => {
84
- const sn = parentObservable.getSnapshot();
87
+ });
85
88
 
86
- if (
87
- parentObservable.updateToken !== updateToken ||
88
- Optional.isNone(sn)
89
- ) {
90
- return; // skip update
91
- }
89
+ const complete = (): void => {
90
+ this.complete();
91
+ };
92
92
 
93
- setNext(sn.value, updateToken);
94
- },
95
- );
93
+ // A notifier that already holds a value completes this observable inside
94
+ // `subscribe`, before the subscription is stored.
95
+ this.#subscription = notifier.subscribe(complete, complete);
96
96
 
97
- const subscription = notifier.subscribe(
98
- () => {
99
- observable.complete();
100
- },
101
- () => {
102
- observable.complete();
103
- },
104
- );
97
+ if (this.isCompleted) {
98
+ this.#subscription.unsubscribe();
99
+ }
105
100
 
106
- // A notifier that already holds a value completes `observable` inside
107
- // `subscribe`, before `onComplete` can see the subscription.
108
- if (observable.isCompleted) {
109
- subscription.unsubscribe();
110
- } else {
111
- mut_subscription = subscription;
101
+ this.attach();
112
102
  }
113
103
 
114
- return observable;
115
- };
104
+ override tryUpdate(updateToken: UpdateToken): void {
105
+ const par = this.parents[0];
106
+
107
+ const sn = par.getSnapshot();
108
+
109
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
110
+ return; // skip update
111
+ }
112
+
113
+ this.setNext(sn.value, updateToken);
114
+ }
115
+
116
+ protected override teardown(): void {
117
+ this.#subscription?.unsubscribe();
118
+ }
119
+ }
@@ -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
  Optional,
3
10
  SafeUint,
@@ -5,13 +12,14 @@ import {
5
12
  expectType,
6
13
  pipe,
7
14
  } from 'ts-data-forge';
8
- import { createSyncChildObservable } from '../base/index.mjs';
15
+ import { SyncChildObservableClass } from '../class/index.mjs';
9
16
  import { source } from '../create/index.mjs';
10
17
  import type {
11
18
  DropInitialValueOperator,
12
19
  InitializedObservable,
13
20
  Observable,
14
21
  TakeWhileOperatorObservable,
22
+ UpdateToken,
15
23
  } from '../types/index.mjs';
16
24
  import { withInitialValue } from './with-initial-value.mjs';
17
25
 
@@ -75,16 +83,22 @@ export const takeWhile =
75
83
  predicate: (value: A, index: SafeUint | -1) => boolean,
76
84
  ): DropInitialValueOperator<A, A> =>
77
85
  (parentObservable) =>
78
- createTakeWhileObservable(parentObservable, predicate);
86
+ new TakeWhileObservableClass(parentObservable, predicate);
79
87
 
80
88
  /* implementation */
81
89
 
82
- const createTakeWhileObservable = <A,>(
83
- parentObservable: Observable<A>,
84
- predicate: (value: A, index: SafeUint | -1) => boolean,
85
- ): TakeWhileOperatorObservable<A> =>
86
- createSyncChildObservable(
87
- {
90
+ class TakeWhileObservableClass<A>
91
+ extends SyncChildObservableClass<A, readonly [A]>
92
+ implements TakeWhileOperatorObservable<A>
93
+ {
94
+ readonly #predicate: (value: A, index: SafeUint | -1) => boolean;
95
+ #mut_index: SafeUint | -1;
96
+
97
+ constructor(
98
+ parentObservable: Observable<A>,
99
+ predicate: (value: A, index: SafeUint | -1) => boolean,
100
+ ) {
101
+ super({
88
102
  parents: [parentObservable],
89
103
  initialValue: pipe(parentObservable.getSnapshot()).map((par) =>
90
104
  Optional.isNone(par)
@@ -93,31 +107,34 @@ const createTakeWhileObservable = <A,>(
93
107
  ? par
94
108
  : Optional.none,
95
109
  ).value,
96
- },
97
- ({ setNext, complete }) => {
98
- let mut_index: SafeUint | -1 = -1;
99
-
100
- return (updateToken) => {
101
- const sn = parentObservable.getSnapshot();
102
-
103
- if (
104
- parentObservable.updateToken !== updateToken ||
105
- Optional.isNone(sn)
106
- ) {
107
- return; // skip update
108
- }
109
-
110
- mut_index =
111
- mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, mut_index);
112
-
113
- if (predicate(sn.value, mut_index)) {
114
- setNext(sn.value, updateToken);
115
- } else {
116
- complete();
117
- }
118
- };
119
- },
120
- );
110
+ });
111
+
112
+ this.#mut_index = -1;
113
+
114
+ this.#predicate = predicate;
115
+
116
+ this.attach();
117
+ }
118
+
119
+ override tryUpdate(updateToken: UpdateToken): void {
120
+ const par = this.parents[0];
121
+
122
+ const sn = par.getSnapshot();
123
+
124
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
125
+ return; // skip update
126
+ }
127
+
128
+ this.#mut_index =
129
+ this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
130
+
131
+ if (this.#predicate(sn.value, this.#mut_index)) {
132
+ this.setNext(sn.value, updateToken);
133
+ } else {
134
+ this.complete();
135
+ }
136
+ }
137
+ }
121
138
 
122
139
  if (import.meta.vitest !== undefined) {
123
140
  test('type test', () => {
@@ -0,0 +1,138 @@
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
8
+ import { Optional, expectType } from 'ts-data-forge';
9
+ import type { PositiveSafeIntWithSmallInt } from 'ts-type-forge';
10
+ import { SyncChildObservableClass } from '../class/index.mjs';
11
+ import { source } from '../create/index.mjs';
12
+ import type {
13
+ DropInitialValueOperator,
14
+ InitializedObservable,
15
+ Observable,
16
+ TakeOperatorObservable,
17
+ UpdateToken,
18
+ } from '../types/index.mjs';
19
+ import { withInitialValue } from './with-initial-value.mjs';
20
+
21
+ /**
22
+ * Takes only the first `n` emissions from the source observable, then completes.
23
+ * It completes in the same update as the `n`th emission, after passing that
24
+ * value on. The initial value is passed on and not counted.
25
+ *
26
+ * @template A - The type of values from the source
27
+ * @param n - The number of values to take
28
+ * @returns An operator that takes the first n emissions
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * // Timeline:
33
+ * //
34
+ * // num$ 1 2 3 4 (ignored)
35
+ * // taken$ 1 2 3|(completes)
36
+ * //
37
+ * // Explanation:
38
+ * // - take emits only the first n values, completing with the nth
39
+ * // - Subsequent emissions from the source are ignored
40
+ *
41
+ * const num$ = source<number>();
42
+ *
43
+ * const taken$ = num$.pipe(take(3));
44
+ *
45
+ * const valueHistory: number[] = [];
46
+ *
47
+ * taken$.subscribe((x) => {
48
+ * valueHistory.push(x);
49
+ * });
50
+ *
51
+ * num$.next(1);
52
+ *
53
+ * num$.next(2);
54
+ *
55
+ * num$.next(3);
56
+ *
57
+ * assert.deepStrictEqual(valueHistory, [1, 2, 3]);
58
+ *
59
+ * assert.isTrue(taken$.isCompleted);
60
+ *
61
+ * num$.next(4); // ignored (already completed)
62
+ *
63
+ * assert.deepStrictEqual(valueHistory, [1, 2, 3]);
64
+ * ```
65
+ */
66
+ export const take =
67
+ <A,>(n: PositiveSafeIntWithSmallInt): DropInitialValueOperator<A, A> =>
68
+ (parentObservable) =>
69
+ new TakeObservableClass(parentObservable, n);
70
+
71
+ /* implementation */
72
+
73
+ class TakeObservableClass<A>
74
+ extends SyncChildObservableClass<A, readonly [A]>
75
+ implements TakeOperatorObservable<A>
76
+ {
77
+ readonly #n: PositiveSafeIntWithSmallInt;
78
+ #mut_count: number;
79
+
80
+ constructor(parentObservable: Observable<A>, n: PositiveSafeIntWithSmallInt) {
81
+ super({
82
+ parents: [parentObservable],
83
+ initialValue: parentObservable.getSnapshot(),
84
+ });
85
+
86
+ this.#mut_count = 0;
87
+
88
+ this.#n = n;
89
+
90
+ this.attach();
91
+ }
92
+
93
+ override tryUpdate(updateToken: UpdateToken): void {
94
+ const par = this.parents[0];
95
+
96
+ const sn = par.getSnapshot();
97
+
98
+ if (
99
+ this.#mut_count >= this.#n ||
100
+ par.updateToken !== updateToken ||
101
+ Optional.isNone(sn)
102
+ ) {
103
+ return; // skip update
104
+ }
105
+
106
+ this.#mut_count += 1;
107
+
108
+ this.setNext(sn.value, updateToken);
109
+
110
+ if (this.#mut_count >= this.#n) {
111
+ this.completeAfterPropagation();
112
+ }
113
+ }
114
+ }
115
+
116
+ if (import.meta.vitest !== undefined) {
117
+ test('type test', () => {
118
+ expect(1).toBe(1); // dummy
119
+ });
120
+
121
+ {
122
+ const s: Observable<number> = source<number>();
123
+
124
+ const _d1 = s.pipe(take(3));
125
+
126
+ expectType<typeof _d1, Observable<number>>('=');
127
+ }
128
+
129
+ {
130
+ const s = source<number>();
131
+
132
+ const m: InitializedObservable<number> = s.pipe(withInitialValue(0));
133
+
134
+ const _d = m.pipe(take(3));
135
+
136
+ expectType<typeof _d, Observable<number>>('=');
137
+ }
138
+ }
@@ -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
- 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
  ThrottleOperatorObservable,
7
14
  TimerId,
15
+ UpdateToken,
8
16
  } from '../types/index.mjs';
9
17
 
10
18
  /**
@@ -90,51 +98,60 @@ export const throttle = <A,>(
90
98
  ): KeepInitialValueOperator<A, A> =>
91
99
  // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
92
100
  ((parentObservable) =>
93
- createThrottleObservable(
101
+ new ThrottleObservableClass(
94
102
  parentObservable,
95
103
  milliSeconds,
96
104
  )) as KeepInitialValueOperator<A, A>;
97
105
 
98
- const createThrottleObservable = <A,>(
99
- parentObservable: Observable<A>,
100
- milliSeconds: number,
101
- ): ThrottleOperatorObservable<A> => {
102
- let mut_timerId: TimerId | undefined = undefined;
103
-
104
- let mut_isSkipping = false;
105
-
106
- const resetTimer = (): void => {
107
- if (mut_timerId !== undefined) {
108
- clearTimeout(mut_timerId);
109
- }
110
- };
106
+ class ThrottleObservableClass<A>
107
+ extends SyncChildObservableClass<A, readonly [A]>
108
+ implements ThrottleOperatorObservable<A>
109
+ {
110
+ readonly #milliSeconds: number;
111
+ #mut_timerId: TimerId | undefined;
112
+ #mut_isSkipping: boolean;
111
113
 
112
- return createSyncChildObservable(
113
- {
114
+ constructor(parentObservable: Observable<A>, milliSeconds: number) {
115
+ super({
114
116
  parents: [parentObservable],
115
117
  initialValue: parentObservable.getSnapshot(),
116
- onComplete: resetTimer,
117
- },
118
- ({ setNext }) =>
119
- (updateToken) => {
120
- const sn = parentObservable.getSnapshot();
118
+ });
119
+
120
+ this.#mut_timerId = undefined;
121
+
122
+ this.#mut_isSkipping = false;
123
+
124
+ this.#milliSeconds = milliSeconds;
121
125
 
122
- if (
123
- mut_isSkipping ||
124
- parentObservable.updateToken !== updateToken ||
125
- Optional.isNone(sn)
126
- ) {
127
- return; // skip update
128
- }
126
+ this.attach();
127
+ }
129
128
 
130
- setNext(sn.value, updateToken);
129
+ override tryUpdate(updateToken: UpdateToken): void {
130
+ const par = this.parents[0];
131
131
 
132
- mut_isSkipping = true;
132
+ const sn = par.getSnapshot();
133
133
 
134
- // set timer
135
- mut_timerId = setTimeout(() => {
136
- mut_isSkipping = false;
137
- }, milliSeconds);
138
- },
139
- );
140
- };
134
+ if (
135
+ this.#mut_isSkipping ||
136
+ par.updateToken !== updateToken ||
137
+ Optional.isNone(sn)
138
+ ) {
139
+ return; // skip update
140
+ }
141
+
142
+ this.setNext(sn.value, updateToken);
143
+
144
+ this.#mut_isSkipping = true;
145
+
146
+ // set timer
147
+ this.#mut_timerId = setTimeout(() => {
148
+ this.#mut_isSkipping = false;
149
+ }, this.#milliSeconds);
150
+ }
151
+
152
+ protected override teardown(): void {
153
+ if (this.#mut_timerId !== undefined) {
154
+ clearTimeout(this.#mut_timerId);
155
+ }
156
+ }
157
+ }
@@ -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 { Arr, Optional, 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
  KeepInitialValueOperator,
5
12
  Observable,
6
13
  Subscription,
14
+ UpdateToken,
7
15
  WithBufferedFromOperatorObservable,
8
16
  } from '../types/index.mjs';
9
17
  import { maxDepth } from '../utils/index.mjs';
@@ -66,7 +74,7 @@ export const withBufferedFrom = <A, B>(
66
74
  ): KeepInitialValueOperator<A, readonly [A, readonly B[]]> =>
67
75
  // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
68
76
  ((parentObservable) =>
69
- createWithBufferedFromObservable(
77
+ new WithBufferedFromObservableClass(
70
78
  parentObservable,
71
79
  observable,
72
80
  )) as KeepInitialValueOperator<A, readonly [A, readonly B[]]>;
@@ -77,23 +85,15 @@ export const withBufferedFrom = <A, B>(
77
85
  */
78
86
  export const withBuffered = withBufferedFrom;
79
87
 
80
- const createWithBufferedFromObservable = <A, B>(
81
- parentObservable: Observable<A>,
82
- observable: Observable<B>,
83
- ): WithBufferedFromOperatorObservable<A, B> => {
84
- let mut_bufferedValues: readonly B[] = [];
85
-
86
- const clearBuffer = (): void => {
87
- mut_bufferedValues = [];
88
- };
89
-
90
- let mut_subscription: Subscription | undefined = undefined;
88
+ class WithBufferedFromObservableClass<A, B>
89
+ extends SyncChildObservableClass<readonly [A, readonly B[]], readonly [A]>
90
+ implements WithBufferedFromOperatorObservable<A, B>
91
+ {
92
+ readonly #subscription: Subscription;
93
+ #mut_bufferedValues: readonly B[];
91
94
 
92
- const result = createSyncChildObservable<
93
- readonly [A, readonly B[]],
94
- readonly [A]
95
- >(
96
- {
95
+ constructor(parentObservable: Observable<A>, observable: Observable<B>) {
96
+ super({
97
97
  parents: [parentObservable],
98
98
  depth: 1 + maxDepth([parentObservable, observable]),
99
99
  initialValue: pipe({
@@ -107,36 +107,34 @@ const createWithBufferedFromObservable = <A, B>(
107
107
  Optional.isNone(me) ? [] : [me.value],
108
108
  ] as const),
109
109
  ).value,
110
- onComplete: () => {
111
- mut_subscription?.unsubscribe();
110
+ });
111
+
112
+ this.#mut_bufferedValues = [];
113
+
114
+ this.#subscription = observable.subscribe((value) => {
115
+ this.#mut_bufferedValues = Arr.toPushed(this.#mut_bufferedValues, value);
116
+ });
112
117
 
113
- clearBuffer();
114
- },
115
- },
116
- ({ setNext }) =>
117
- (updateToken) => {
118
- const sn = parentObservable.getSnapshot();
118
+ this.attach();
119
+ }
119
120
 
120
- if (
121
- parentObservable.updateToken !== updateToken ||
122
- Optional.isNone(sn)
123
- ) {
124
- return; // skip update
125
- }
121
+ override tryUpdate(updateToken: UpdateToken): void {
122
+ const par = this.parents[0];
126
123
 
127
- setNext([sn.value, mut_bufferedValues], updateToken);
124
+ const sn = par.getSnapshot();
128
125
 
129
- clearBuffer();
130
- },
131
- );
126
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
127
+ return; // skip update
128
+ }
132
129
 
133
- // A child completed at creation has already run `onComplete`; subscribing
134
- // after it would leave a subscription nothing releases.
135
- if (!result.isCompleted) {
136
- mut_subscription = observable.subscribe((value) => {
137
- mut_bufferedValues = Arr.toPushed(mut_bufferedValues, value);
138
- });
130
+ this.setNext([sn.value, this.#mut_bufferedValues], updateToken);
131
+
132
+ this.#mut_bufferedValues = [];
139
133
  }
140
134
 
141
- return result;
142
- };
135
+ protected override teardown(): void {
136
+ this.#subscription.unsubscribe();
137
+
138
+ this.#mut_bufferedValues = [];
139
+ }
140
+ }
@@ -1,3 +1,4 @@
1
+ import { toNode } from '../class/index.mjs';
1
2
  import { source } from '../create/index.mjs';
2
3
  import { withBufferedFrom } from './with-buffered-from.mjs';
3
4
 
@@ -9,13 +10,13 @@ describe('withBufferedFrom teardown', () => {
9
10
 
10
11
  const result$ = trigger$.pipe(withBufferedFrom(data$));
11
12
 
12
- assert.isTrue(data$.hasSubscriber);
13
+ assert.isTrue(toNode(data$).hasSubscriber);
13
14
 
14
15
  result$.complete();
15
16
 
16
17
  assert.isTrue(result$.isCompleted);
17
18
 
18
- assert.isFalse(data$.hasSubscriber);
19
+ assert.isFalse(toNode(data$).hasSubscriber);
19
20
  });
20
21
 
21
22
  test('unsubscribes from the buffered observable when the parent completes', () => {
@@ -29,7 +30,7 @@ describe('withBufferedFrom teardown', () => {
29
30
 
30
31
  assert.isTrue(result$.isCompleted);
31
32
 
32
- assert.isFalse(data$.hasSubscriber);
33
+ assert.isFalse(toNode(data$).hasSubscriber);
33
34
  });
34
35
 
35
36
  test('keeps buffering while subscribed', () => {