synstate 0.1.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 (268) hide show
  1. package/README.md +878 -0
  2. package/dist/core/class/child-observable-class.d.mts +37 -0
  3. package/dist/core/class/child-observable-class.d.mts.map +1 -0
  4. package/dist/core/class/child-observable-class.mjs +134 -0
  5. package/dist/core/class/child-observable-class.mjs.map +1 -0
  6. package/dist/core/class/index.d.mts +4 -0
  7. package/dist/core/class/index.d.mts.map +1 -0
  8. package/dist/core/class/index.mjs +4 -0
  9. package/dist/core/class/index.mjs.map +1 -0
  10. package/dist/core/class/observable-base-class.d.mts +28 -0
  11. package/dist/core/class/observable-base-class.d.mts.map +1 -0
  12. package/dist/core/class/observable-base-class.mjs +116 -0
  13. package/dist/core/class/observable-base-class.mjs.map +1 -0
  14. package/dist/core/class/root-observable-class.d.mts +12 -0
  15. package/dist/core/class/root-observable-class.d.mts.map +1 -0
  16. package/dist/core/class/root-observable-class.mjs +35 -0
  17. package/dist/core/class/root-observable-class.mjs.map +1 -0
  18. package/dist/core/combine/combine.d.mts +35 -0
  19. package/dist/core/combine/combine.d.mts.map +1 -0
  20. package/dist/core/combine/combine.mjs +94 -0
  21. package/dist/core/combine/combine.mjs.map +1 -0
  22. package/dist/core/combine/index.d.mts +4 -0
  23. package/dist/core/combine/index.d.mts.map +1 -0
  24. package/dist/core/combine/index.mjs +4 -0
  25. package/dist/core/combine/index.mjs.map +1 -0
  26. package/dist/core/combine/merge.d.mts +28 -0
  27. package/dist/core/combine/merge.d.mts.map +1 -0
  28. package/dist/core/combine/merge.mjs +52 -0
  29. package/dist/core/combine/merge.mjs.map +1 -0
  30. package/dist/core/combine/zip.d.mts +26 -0
  31. package/dist/core/combine/zip.d.mts.map +1 -0
  32. package/dist/core/combine/zip.mjs +63 -0
  33. package/dist/core/combine/zip.mjs.map +1 -0
  34. package/dist/core/create/from-array.d.mts +21 -0
  35. package/dist/core/create/from-array.d.mts.map +1 -0
  36. package/dist/core/create/from-array.mjs +47 -0
  37. package/dist/core/create/from-array.mjs.map +1 -0
  38. package/dist/core/create/from-promise.d.mts +25 -0
  39. package/dist/core/create/from-promise.d.mts.map +1 -0
  40. package/dist/core/create/from-promise.mjs +51 -0
  41. package/dist/core/create/from-promise.mjs.map +1 -0
  42. package/dist/core/create/from-subscribable.d.mts +3 -0
  43. package/dist/core/create/from-subscribable.d.mts.map +1 -0
  44. package/dist/core/create/from-subscribable.mjs +22 -0
  45. package/dist/core/create/from-subscribable.mjs.map +1 -0
  46. package/dist/core/create/index.d.mts +8 -0
  47. package/dist/core/create/index.d.mts.map +1 -0
  48. package/dist/core/create/index.mjs +8 -0
  49. package/dist/core/create/index.mjs.map +1 -0
  50. package/dist/core/create/interval.d.mts +21 -0
  51. package/dist/core/create/interval.d.mts.map +1 -0
  52. package/dist/core/create/interval.mjs +74 -0
  53. package/dist/core/create/interval.mjs.map +1 -0
  54. package/dist/core/create/of.d.mts +20 -0
  55. package/dist/core/create/of.d.mts.map +1 -0
  56. package/dist/core/create/of.mjs +44 -0
  57. package/dist/core/create/of.mjs.map +1 -0
  58. package/dist/core/create/source.d.mts +29 -0
  59. package/dist/core/create/source.d.mts.map +1 -0
  60. package/dist/core/create/source.mjs +29 -0
  61. package/dist/core/create/source.mjs.map +1 -0
  62. package/dist/core/create/timer.d.mts +20 -0
  63. package/dist/core/create/timer.d.mts.map +1 -0
  64. package/dist/core/create/timer.mjs +64 -0
  65. package/dist/core/create/timer.mjs.map +1 -0
  66. package/dist/core/index.d.mts +7 -0
  67. package/dist/core/index.d.mts.map +1 -0
  68. package/dist/core/index.mjs +37 -0
  69. package/dist/core/index.mjs.map +1 -0
  70. package/dist/core/operators/audit-time.d.mts +3 -0
  71. package/dist/core/operators/audit-time.d.mts.map +1 -0
  72. package/dist/core/operators/audit-time.mjs +50 -0
  73. package/dist/core/operators/audit-time.mjs.map +1 -0
  74. package/dist/core/operators/debounce-time.d.mts +31 -0
  75. package/dist/core/operators/debounce-time.d.mts.map +1 -0
  76. package/dist/core/operators/debounce-time.mjs +73 -0
  77. package/dist/core/operators/debounce-time.mjs.map +1 -0
  78. package/dist/core/operators/filter.d.mts +28 -0
  79. package/dist/core/operators/filter.d.mts.map +1 -0
  80. package/dist/core/operators/filter.mjs +38 -0
  81. package/dist/core/operators/filter.mjs.map +1 -0
  82. package/dist/core/operators/index.d.mts +18 -0
  83. package/dist/core/operators/index.d.mts.map +1 -0
  84. package/dist/core/operators/index.mjs +18 -0
  85. package/dist/core/operators/index.mjs.map +1 -0
  86. package/dist/core/operators/map-with-index.d.mts +39 -0
  87. package/dist/core/operators/map-with-index.d.mts.map +1 -0
  88. package/dist/core/operators/map-with-index.mjs +73 -0
  89. package/dist/core/operators/map-with-index.mjs.map +1 -0
  90. package/dist/core/operators/merge-map.d.mts +34 -0
  91. package/dist/core/operators/merge-map.d.mts.map +1 -0
  92. package/dist/core/operators/merge-map.mjs +75 -0
  93. package/dist/core/operators/merge-map.mjs.map +1 -0
  94. package/dist/core/operators/pairwise.d.mts +27 -0
  95. package/dist/core/operators/pairwise.d.mts.map +1 -0
  96. package/dist/core/operators/pairwise.mjs +59 -0
  97. package/dist/core/operators/pairwise.mjs.map +1 -0
  98. package/dist/core/operators/scan.d.mts +30 -0
  99. package/dist/core/operators/scan.d.mts.map +1 -0
  100. package/dist/core/operators/scan.mjs +56 -0
  101. package/dist/core/operators/scan.mjs.map +1 -0
  102. package/dist/core/operators/skip-if-no-change.d.mts +33 -0
  103. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -0
  104. package/dist/core/operators/skip-if-no-change.mjs +68 -0
  105. package/dist/core/operators/skip-if-no-change.mjs.map +1 -0
  106. package/dist/core/operators/skip-until.d.mts +3 -0
  107. package/dist/core/operators/skip-until.d.mts.map +1 -0
  108. package/dist/core/operators/skip-until.mjs +33 -0
  109. package/dist/core/operators/skip-until.mjs.map +1 -0
  110. package/dist/core/operators/skip-while.d.mts +4 -0
  111. package/dist/core/operators/skip-while.d.mts.map +1 -0
  112. package/dist/core/operators/skip-while.mjs +40 -0
  113. package/dist/core/operators/skip-while.mjs.map +1 -0
  114. package/dist/core/operators/switch-map.d.mts +31 -0
  115. package/dist/core/operators/switch-map.d.mts.map +1 -0
  116. package/dist/core/operators/switch-map.mjs +70 -0
  117. package/dist/core/operators/switch-map.mjs.map +1 -0
  118. package/dist/core/operators/take-until.d.mts +32 -0
  119. package/dist/core/operators/take-until.d.mts.map +1 -0
  120. package/dist/core/operators/take-until.mjs +60 -0
  121. package/dist/core/operators/take-until.mjs.map +1 -0
  122. package/dist/core/operators/take-while.d.mts +4 -0
  123. package/dist/core/operators/take-while.d.mts.map +1 -0
  124. package/dist/core/operators/take-while.mjs +42 -0
  125. package/dist/core/operators/take-while.mjs.map +1 -0
  126. package/dist/core/operators/throttle-time.d.mts +23 -0
  127. package/dist/core/operators/throttle-time.d.mts.map +1 -0
  128. package/dist/core/operators/throttle-time.mjs +68 -0
  129. package/dist/core/operators/throttle-time.mjs.map +1 -0
  130. package/dist/core/operators/with-buffered-from.d.mts +4 -0
  131. package/dist/core/operators/with-buffered-from.d.mts.map +1 -0
  132. package/dist/core/operators/with-buffered-from.mjs +45 -0
  133. package/dist/core/operators/with-buffered-from.mjs.map +1 -0
  134. package/dist/core/operators/with-current-value-from.d.mts +4 -0
  135. package/dist/core/operators/with-current-value-from.d.mts.map +1 -0
  136. package/dist/core/operators/with-current-value-from.mjs +37 -0
  137. package/dist/core/operators/with-current-value-from.mjs.map +1 -0
  138. package/dist/core/operators/with-initial-value.d.mts +26 -0
  139. package/dist/core/operators/with-initial-value.d.mts.map +1 -0
  140. package/dist/core/operators/with-initial-value.mjs +47 -0
  141. package/dist/core/operators/with-initial-value.mjs.map +1 -0
  142. package/dist/core/types/id.d.mts +4 -0
  143. package/dist/core/types/id.d.mts.map +1 -0
  144. package/dist/core/types/id.mjs +2 -0
  145. package/dist/core/types/id.mjs.map +1 -0
  146. package/dist/core/types/index.d.mts +6 -0
  147. package/dist/core/types/index.d.mts.map +1 -0
  148. package/dist/core/types/index.mjs +3 -0
  149. package/dist/core/types/index.mjs.map +1 -0
  150. package/dist/core/types/observable-family.d.mts +68 -0
  151. package/dist/core/types/observable-family.d.mts.map +1 -0
  152. package/dist/core/types/observable-family.mjs +2 -0
  153. package/dist/core/types/observable-family.mjs.map +1 -0
  154. package/dist/core/types/observable-kind.d.mts +4 -0
  155. package/dist/core/types/observable-kind.d.mts.map +1 -0
  156. package/dist/core/types/observable-kind.mjs +2 -0
  157. package/dist/core/types/observable-kind.mjs.map +1 -0
  158. package/dist/core/types/observable.d.mts +83 -0
  159. package/dist/core/types/observable.d.mts.map +1 -0
  160. package/dist/core/types/observable.mjs +10 -0
  161. package/dist/core/types/observable.mjs.map +1 -0
  162. package/dist/core/types/types.d.mts +16 -0
  163. package/dist/core/types/types.d.mts.map +1 -0
  164. package/dist/core/types/types.mjs +2 -0
  165. package/dist/core/types/types.mjs.map +1 -0
  166. package/dist/core/utils/id-maker.d.mts +5 -0
  167. package/dist/core/utils/id-maker.d.mts.map +1 -0
  168. package/dist/core/utils/id-maker.mjs +17 -0
  169. package/dist/core/utils/id-maker.mjs.map +1 -0
  170. package/dist/core/utils/index.d.mts +5 -0
  171. package/dist/core/utils/index.d.mts.map +1 -0
  172. package/dist/core/utils/index.mjs +5 -0
  173. package/dist/core/utils/index.mjs.map +1 -0
  174. package/dist/core/utils/max-depth.d.mts +3 -0
  175. package/dist/core/utils/max-depth.d.mts.map +1 -0
  176. package/dist/core/utils/max-depth.mjs +8 -0
  177. package/dist/core/utils/max-depth.mjs.map +1 -0
  178. package/dist/core/utils/observable-utils.d.mts +3 -0
  179. package/dist/core/utils/observable-utils.d.mts.map +1 -0
  180. package/dist/core/utils/observable-utils.mjs +7 -0
  181. package/dist/core/utils/observable-utils.mjs.map +1 -0
  182. package/dist/core/utils/utils.d.mts +4 -0
  183. package/dist/core/utils/utils.d.mts.map +1 -0
  184. package/dist/core/utils/utils.mjs +38 -0
  185. package/dist/core/utils/utils.mjs.map +1 -0
  186. package/dist/entry-point.d.mts +2 -0
  187. package/dist/entry-point.d.mts.map +1 -0
  188. package/dist/entry-point.mjs +40 -0
  189. package/dist/entry-point.mjs.map +1 -0
  190. package/dist/globals.d.mts +4 -0
  191. package/dist/index.d.mts +3 -0
  192. package/dist/index.d.mts.map +1 -0
  193. package/dist/index.mjs +40 -0
  194. package/dist/index.mjs.map +1 -0
  195. package/dist/tsconfig.json +1 -0
  196. package/dist/types.d.mts +2 -0
  197. package/dist/utils/create-event-emitter.d.mts +39 -0
  198. package/dist/utils/create-event-emitter.d.mts.map +1 -0
  199. package/dist/utils/create-event-emitter.mjs +57 -0
  200. package/dist/utils/create-event-emitter.mjs.map +1 -0
  201. package/dist/utils/create-reducer.d.mts +34 -0
  202. package/dist/utils/create-reducer.d.mts.map +1 -0
  203. package/dist/utils/create-reducer.mjs +49 -0
  204. package/dist/utils/create-reducer.mjs.map +1 -0
  205. package/dist/utils/create-state.d.mts +61 -0
  206. package/dist/utils/create-state.d.mts.map +1 -0
  207. package/dist/utils/create-state.mjs +92 -0
  208. package/dist/utils/create-state.mjs.map +1 -0
  209. package/dist/utils/index.d.mts +4 -0
  210. package/dist/utils/index.d.mts.map +1 -0
  211. package/dist/utils/index.mjs +4 -0
  212. package/dist/utils/index.mjs.map +1 -0
  213. package/package.json +71 -0
  214. package/src/core/class/child-observable-class.mts +232 -0
  215. package/src/core/class/index.mts +3 -0
  216. package/src/core/class/observable-base-class.mts +186 -0
  217. package/src/core/class/observable.class.test.mts +89 -0
  218. package/src/core/class/root-observable-class.mts +68 -0
  219. package/src/core/combine/combine.mts +144 -0
  220. package/src/core/combine/index.mts +3 -0
  221. package/src/core/combine/merge.mts +84 -0
  222. package/src/core/combine/zip.mts +149 -0
  223. package/src/core/create/from-array.mts +58 -0
  224. package/src/core/create/from-promise.mts +58 -0
  225. package/src/core/create/from-subscribable.mts +37 -0
  226. package/src/core/create/index.mts +7 -0
  227. package/src/core/create/interval.mts +99 -0
  228. package/src/core/create/of.mts +54 -0
  229. package/src/core/create/source.mts +59 -0
  230. package/src/core/create/timer.mts +84 -0
  231. package/src/core/index.mts +6 -0
  232. package/src/core/operators/audit-time.mts +77 -0
  233. package/src/core/operators/debounce-time.mts +96 -0
  234. package/src/core/operators/filter.mts +125 -0
  235. package/src/core/operators/index.mts +17 -0
  236. package/src/core/operators/map-with-index.mts +168 -0
  237. package/src/core/operators/merge-map.mts +108 -0
  238. package/src/core/operators/pairwise.mts +77 -0
  239. package/src/core/operators/scan.mts +81 -0
  240. package/src/core/operators/skip-if-no-change.mts +91 -0
  241. package/src/core/operators/skip-until.mts +54 -0
  242. package/src/core/operators/skip-while.mts +77 -0
  243. package/src/core/operators/switch-map.mts +101 -0
  244. package/src/core/operators/take-until.mts +80 -0
  245. package/src/core/operators/take-while.mts +103 -0
  246. package/src/core/operators/throttle-time.mts +95 -0
  247. package/src/core/operators/with-buffered-from.mts +68 -0
  248. package/src/core/operators/with-current-value-from.mts +58 -0
  249. package/src/core/operators/with-initial-value.mts +76 -0
  250. package/src/core/types/id.mts +5 -0
  251. package/src/core/types/index.mts +5 -0
  252. package/src/core/types/observable-family.mts +259 -0
  253. package/src/core/types/observable-kind.mts +5 -0
  254. package/src/core/types/observable.mts +218 -0
  255. package/src/core/types/types.mts +40 -0
  256. package/src/core/utils/id-maker.mts +31 -0
  257. package/src/core/utils/index.mts +4 -0
  258. package/src/core/utils/max-depth.mts +7 -0
  259. package/src/core/utils/observable-utils.mts +10 -0
  260. package/src/core/utils/utils.mts +51 -0
  261. package/src/core/utils/utils.test.mts +88 -0
  262. package/src/entry-point.mts +1 -0
  263. package/src/globals.d.mts +4 -0
  264. package/src/index.mts +2 -0
  265. package/src/utils/create-event-emitter.mts +62 -0
  266. package/src/utils/create-reducer.mts +55 -0
  267. package/src/utils/create-state.mts +138 -0
  268. package/src/utils/index.mts +3 -0
@@ -0,0 +1,101 @@
1
+ import { Optional } from 'ts-data-forge';
2
+ import { AsyncChildObservableClass } from '../class/index.mjs';
3
+ import {
4
+ type DropInitialValueOperator,
5
+ type Observable,
6
+ type Subscription,
7
+ type SwitchMapOperatorObservable,
8
+ type UpdaterSymbol,
9
+ } from '../types/index.mjs';
10
+
11
+ /**
12
+ * Projects each source value to an observable, subscribes to it, and emits its values.
13
+ * When a new value arrives from the source, the previous inner observable is unsubscribed.
14
+ *
15
+ * @template A - The type of values from the source
16
+ * @template B - The type of values from the projected observable
17
+ * @param mapToObservable - A function that maps each source value to an observable
18
+ * @returns An operator that switches to new observables
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const searchQuery$ = source<string>();
23
+ *
24
+ * const results$ = searchQuery$.pipe(
25
+ * switchMap((query) => fromPromise(fetchResults(query))),
26
+ * );
27
+ *
28
+ * results$.subscribe((results) => {
29
+ * console.log(results);
30
+ * });
31
+ * // Only the latest search results are emitted, previous searches are cancelled
32
+ *
33
+ * const fetchResults = async (_query: string): Promise<readonly unknown[]> => [];
34
+ * ```
35
+ *
36
+ * @note To improve code readability, consider using `createState` instead of `switchMap`,
37
+ * subscribe to `parentObservable` and call `setState` within it.
38
+ */
39
+ export const switchMap =
40
+ <A, B>(
41
+ mapToObservable: (curr: A) => Observable<B>,
42
+ ): DropInitialValueOperator<A, B> =>
43
+ (parentObservable) =>
44
+ new SwitchMapObservableClass(parentObservable, mapToObservable);
45
+
46
+ class SwitchMapObservableClass<A, B>
47
+ extends AsyncChildObservableClass<B, readonly [A]>
48
+ implements SwitchMapOperatorObservable<A, B>
49
+ {
50
+ readonly #mapToObservable: (curr: A) => Observable<B>;
51
+ #mut_observable: Observable<B> | undefined;
52
+ #mut_subscription: Subscription | undefined;
53
+
54
+ constructor(
55
+ parentObservable: Observable<A>,
56
+ mapToObservable: (curr: A) => Observable<B>,
57
+ ) {
58
+ super({
59
+ parents: [parentObservable],
60
+ initialValue: Optional.none,
61
+ });
62
+
63
+ this.#mapToObservable = mapToObservable;
64
+
65
+ this.#mut_observable = undefined;
66
+
67
+ this.#mut_subscription = undefined;
68
+ }
69
+
70
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
71
+ const par = this.parents[0];
72
+
73
+ const sn = par.getSnapshot();
74
+
75
+ if (par.updaterSymbol !== updaterSymbol || Optional.isNone(sn)) {
76
+ return; // skip update
77
+ }
78
+
79
+ this.#mut_observable?.complete();
80
+
81
+ this.#mut_subscription?.unsubscribe();
82
+
83
+ const observable = this.#mapToObservable(sn.value);
84
+
85
+ this.#mut_observable = observable;
86
+
87
+ const subscription = observable.subscribe((curr) => {
88
+ this.startUpdate(curr);
89
+ });
90
+
91
+ this.#mut_subscription = subscription;
92
+ }
93
+
94
+ override complete(): void {
95
+ this.#mut_subscription?.unsubscribe();
96
+
97
+ this.#mut_observable?.complete();
98
+
99
+ super.complete();
100
+ }
101
+ }
@@ -0,0 +1,80 @@
1
+ import { Optional } from 'ts-data-forge';
2
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
+ import {
4
+ type KeepInitialValueOperator,
5
+ type Observable,
6
+ type TakeUntilOperatorObservable,
7
+ type UpdaterSymbol,
8
+ } from '../types/index.mjs';
9
+
10
+ /**
11
+ * Emits values from the source until the notifier observable emits.
12
+ * When the notifier emits, this observable completes.
13
+ *
14
+ * @template A - The type of values from the source
15
+ * @param notifier - An observable that signals when to complete
16
+ * @returns An operator that takes values until notifier emits
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const num$ = source<number>();
21
+ *
22
+ * const [stopNotifier, stop_] = createEventEmitter();
23
+ *
24
+ * const limited$ = num$.pipe(takeUntil(stopNotifier));
25
+ *
26
+ * limited$.subscribe((x) => {
27
+ * console.log(x);
28
+ * });
29
+ *
30
+ * num$.next(1); // logs: 1
31
+ *
32
+ * num$.next(2); // logs: 2
33
+ *
34
+ * stop_();
35
+ *
36
+ * num$.next(3); // nothing logged (completed)
37
+ * ```
38
+ */
39
+ export const takeUntil = <A,>(
40
+ notifier: Observable<unknown>,
41
+ ): KeepInitialValueOperator<A, A> =>
42
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
43
+ ((parentObservable) =>
44
+ new TakeUntilObservableClass(
45
+ parentObservable,
46
+ notifier,
47
+ )) as KeepInitialValueOperator<A, A>;
48
+
49
+ class TakeUntilObservableClass<A>
50
+ extends SyncChildObservableClass<A, readonly [A]>
51
+ implements TakeUntilOperatorObservable<A>
52
+ {
53
+ constructor(parentObservable: Observable<A>, notifier: Observable<unknown>) {
54
+ super({
55
+ parents: [parentObservable],
56
+ initialValue: parentObservable.getSnapshot(),
57
+ });
58
+
59
+ notifier.subscribe(
60
+ () => {
61
+ this.complete();
62
+ },
63
+ () => {
64
+ this.complete();
65
+ },
66
+ );
67
+ }
68
+
69
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
70
+ const par = this.parents[0];
71
+
72
+ const sn = par.getSnapshot();
73
+
74
+ if (par.updaterSymbol !== updaterSymbol || Optional.isNone(sn)) {
75
+ return; // skip update
76
+ }
77
+
78
+ this.setNext(sn.value, updaterSymbol);
79
+ }
80
+ }
@@ -0,0 +1,103 @@
1
+ import {
2
+ Optional,
3
+ SafeUint,
4
+ asSafeUint,
5
+ expectType,
6
+ pipe,
7
+ } from 'ts-data-forge';
8
+ import { SyncChildObservableClass } from '../class/index.mjs';
9
+ import { source } from '../create/index.mjs';
10
+ import {
11
+ type DropInitialValueOperator,
12
+ type InitializedObservable,
13
+ type Observable,
14
+ type TakeWhileOperatorObservable,
15
+ type UpdaterSymbol,
16
+ } from '../types/index.mjs';
17
+ import { withInitialValue } from './with-initial-value.mjs';
18
+
19
+ export const takeWhile =
20
+ <A,>(
21
+ predicate: (value: A, index: SafeUint | -1) => boolean,
22
+ ): DropInitialValueOperator<A, A> =>
23
+ (parentObservable) =>
24
+ new TakeWhileObservableClass(parentObservable, predicate);
25
+
26
+ /* Specialized operators */
27
+
28
+ export const take = <A,>(
29
+ n: PositiveSafeIntWithSmallInt,
30
+ ): DropInitialValueOperator<A, A> => takeWhile((_, index) => index + 1 <= n);
31
+
32
+ /* implementation */
33
+
34
+ class TakeWhileObservableClass<A>
35
+ extends SyncChildObservableClass<A, readonly [A]>
36
+ implements TakeWhileOperatorObservable<A>
37
+ {
38
+ readonly #predicate: (value: A, index: SafeUint | -1) => boolean;
39
+ #mut_index: SafeUint | -1;
40
+
41
+ constructor(
42
+ parentObservable: Observable<A>,
43
+ predicate: (value: A, index: SafeUint | -1) => boolean,
44
+ ) {
45
+ super({
46
+ parents: [parentObservable],
47
+ initialValue: pipe(parentObservable.getSnapshot()).map((par) =>
48
+ Optional.isNone(par)
49
+ ? Optional.none
50
+ : predicate(par.value, -1)
51
+ ? par
52
+ : Optional.none,
53
+ ).value,
54
+ });
55
+
56
+ this.#mut_index = -1;
57
+
58
+ this.#predicate = predicate;
59
+ }
60
+
61
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
62
+ const par = this.parents[0];
63
+
64
+ const sn = par.getSnapshot();
65
+
66
+ if (par.updaterSymbol !== updaterSymbol || Optional.isNone(sn)) {
67
+ return; // skip update
68
+ }
69
+
70
+ this.#mut_index =
71
+ this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
72
+
73
+ if (this.#predicate(sn.value, this.#mut_index)) {
74
+ this.setNext(sn.value, updaterSymbol);
75
+ } else {
76
+ this.complete();
77
+ }
78
+ }
79
+ }
80
+
81
+ if (import.meta.vitest !== undefined) {
82
+ test('type test', () => {
83
+ expect(1).toBe(1); // dummy
84
+ });
85
+
86
+ {
87
+ const s: Observable<number> = source<number>();
88
+
89
+ const _d1 = s.pipe(take(3));
90
+
91
+ expectType<typeof _d1, Observable<number>>('=');
92
+ }
93
+
94
+ {
95
+ const s = source<number>();
96
+
97
+ const m: InitializedObservable<number> = s.pipe(withInitialValue(0));
98
+
99
+ const _d = m.pipe(take(3));
100
+
101
+ expectType<typeof _d, Observable<number>>('=');
102
+ }
103
+ }
@@ -0,0 +1,95 @@
1
+ import { Optional } from 'ts-data-forge';
2
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
+ import {
4
+ type KeepInitialValueOperator,
5
+ type Observable,
6
+ type ThrottleTimeOperatorObservable,
7
+ type UpdaterSymbol,
8
+ } from '../types/index.mjs';
9
+
10
+ /**
11
+ * Emits the first value, then ignores subsequent values for a specified duration.
12
+ * After the duration, the next emission is allowed through.
13
+ *
14
+ * @template A - The type of values from the source
15
+ * @param milliSeconds - The throttle duration in milliseconds
16
+ * @returns An operator that throttles emissions
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const scroll$ = source<Event>();
21
+ *
22
+ * const throttled$ = scroll$.pipe(throttleTime(1000));
23
+ *
24
+ * throttled$.subscribe((event_) => {
25
+ * console.log(event_);
26
+ * });
27
+ * // Emits at most once per second
28
+ * ```
29
+ */
30
+ export const throttleTime = <A,>(
31
+ milliSeconds: number,
32
+ ): KeepInitialValueOperator<A, A> =>
33
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
34
+ ((parentObservable) =>
35
+ new ThrottleTimeObservableClass(
36
+ parentObservable,
37
+ milliSeconds,
38
+ )) as KeepInitialValueOperator<A, A>;
39
+
40
+ class ThrottleTimeObservableClass<A>
41
+ extends SyncChildObservableClass<A, readonly [A]>
42
+ implements ThrottleTimeOperatorObservable<A>
43
+ {
44
+ readonly #milliSeconds: number;
45
+ #mut_timerId: TimerId | undefined;
46
+ #mut_isSkipping: boolean;
47
+
48
+ constructor(parentObservable: Observable<A>, milliSeconds: number) {
49
+ super({
50
+ parents: [parentObservable],
51
+ initialValue: parentObservable.getSnapshot(),
52
+ });
53
+
54
+ this.#mut_timerId = undefined;
55
+
56
+ this.#mut_isSkipping = false;
57
+
58
+ this.#milliSeconds = milliSeconds;
59
+ }
60
+
61
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
62
+ const par = this.parents[0];
63
+
64
+ const sn = par.getSnapshot();
65
+
66
+ if (
67
+ par.updaterSymbol !== updaterSymbol ||
68
+ Optional.isNone(sn) ||
69
+ this.#mut_isSkipping
70
+ ) {
71
+ return; // skip update
72
+ }
73
+
74
+ this.setNext(sn.value, updaterSymbol);
75
+
76
+ this.#mut_isSkipping = true;
77
+
78
+ // set timer
79
+ this.#mut_timerId = setTimeout(() => {
80
+ this.#mut_isSkipping = false;
81
+ }, this.#milliSeconds);
82
+ }
83
+
84
+ #resetTimer(): void {
85
+ if (this.#mut_timerId !== undefined) {
86
+ clearTimeout(this.#mut_timerId);
87
+ }
88
+ }
89
+
90
+ override complete(): void {
91
+ this.#resetTimer();
92
+
93
+ super.complete();
94
+ }
95
+ }
@@ -0,0 +1,68 @@
1
+ import { Arr, Optional, pipe } from 'ts-data-forge';
2
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
+ import {
4
+ type KeepInitialValueOperator,
5
+ type Observable,
6
+ type UpdaterSymbol,
7
+ type WithBufferedFromOperatorObservable,
8
+ } from '../types/index.mjs';
9
+ import { maxDepth } from '../utils/index.mjs';
10
+
11
+ export const withBufferedFrom = <A, B>(
12
+ observable: Observable<B>,
13
+ ): KeepInitialValueOperator<A, readonly [A, readonly B[]]> =>
14
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
15
+ ((parentObservable) =>
16
+ new WithBufferedFromObservableClass(
17
+ parentObservable,
18
+ observable,
19
+ )) as KeepInitialValueOperator<A, readonly [A, readonly B[]]>;
20
+
21
+ export const withBuffered = withBufferedFrom; // alias
22
+
23
+ class WithBufferedFromObservableClass<A, B>
24
+ extends SyncChildObservableClass<readonly [A, readonly B[]], readonly [A]>
25
+ implements WithBufferedFromOperatorObservable<A, B>
26
+ {
27
+ #mut_bufferedValues: readonly B[] = [];
28
+
29
+ constructor(parentObservable: Observable<A>, observable: Observable<B>) {
30
+ super({
31
+ parents: [parentObservable],
32
+ depth: 1 + maxDepth([parentObservable, observable]),
33
+ initialValue: pipe({
34
+ par: parentObservable.getSnapshot(),
35
+ me: observable.getSnapshot(),
36
+ }).map(({ par, me }) =>
37
+ Optional.isNone(par)
38
+ ? Optional.none
39
+ : Optional.some([
40
+ par.value,
41
+ Optional.isNone(me) ? [] : [me.value],
42
+ ] as const),
43
+ ).value,
44
+ });
45
+
46
+ observable.subscribe((value) => {
47
+ this.#mut_bufferedValues = Arr.toPushed(this.#mut_bufferedValues, value);
48
+ });
49
+ }
50
+
51
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
52
+ const par = this.parents[0];
53
+
54
+ const sn = par.getSnapshot();
55
+
56
+ if (par.updaterSymbol !== updaterSymbol || Optional.isNone(sn)) {
57
+ return; // skip update
58
+ }
59
+
60
+ this.setNext([sn.value, this.#mut_bufferedValues], updaterSymbol);
61
+
62
+ this.#clearBuffer();
63
+ }
64
+
65
+ #clearBuffer(): void {
66
+ this.#mut_bufferedValues = [];
67
+ }
68
+ }
@@ -0,0 +1,58 @@
1
+ import { Optional, pipe } from 'ts-data-forge';
2
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
+ import {
4
+ type DropInitialValueOperator,
5
+ type Observable,
6
+ type UpdaterSymbol,
7
+ type WithCurrentValueFromOperatorObservable,
8
+ } from '../types/index.mjs';
9
+ import { maxDepth } from '../utils/index.mjs';
10
+
11
+ export const withCurrentValueFrom =
12
+ <A, B>(
13
+ observable: Observable<B>,
14
+ ): DropInitialValueOperator<A, readonly [A, B]> =>
15
+ (parentObservable) =>
16
+ new WithCurrentValueFromObservableClass(parentObservable, observable);
17
+
18
+ export const withLatestFrom = withCurrentValueFrom; // alias
19
+
20
+ class WithCurrentValueFromObservableClass<A, B>
21
+ extends SyncChildObservableClass<readonly [A, B], readonly [A]>
22
+ implements WithCurrentValueFromOperatorObservable<A, B>
23
+ {
24
+ readonly #observable: Observable<B>;
25
+
26
+ constructor(parentObservable: Observable<A>, observable: Observable<B>) {
27
+ super({
28
+ parents: [parentObservable],
29
+ depth: 1 + maxDepth([parentObservable, observable]),
30
+ initialValue: pipe({
31
+ par: parentObservable.getSnapshot(),
32
+ me: observable.getSnapshot(),
33
+ }).map(({ me, par }) =>
34
+ Optional.isNone(par) || Optional.isNone(me)
35
+ ? Optional.none
36
+ : Optional.some([par.value, me.value] as const),
37
+ ).value,
38
+ });
39
+
40
+ this.#observable = observable;
41
+ }
42
+
43
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
44
+ const par = this.parents[0];
45
+
46
+ const ps = par.getSnapshot();
47
+
48
+ if (par.updaterSymbol !== updaterSymbol || Optional.isNone(ps)) {
49
+ return; // skip update
50
+ }
51
+
52
+ const curr = this.#observable.getSnapshot();
53
+
54
+ if (Optional.isNone(curr)) return; // skip update
55
+
56
+ this.setNext([ps.value, curr.value], updaterSymbol);
57
+ }
58
+ }
@@ -0,0 +1,76 @@
1
+ import { expectType, Optional } from 'ts-data-forge';
2
+ import { InitializedSyncChildObservableClass } from '../class/index.mjs';
3
+ import { source } from '../create/index.mjs';
4
+ import {
5
+ type InitializedObservable,
6
+ type Observable,
7
+ type UpdaterSymbol,
8
+ type WithInitialValueOperator,
9
+ type WithInitialValueOperatorObservable,
10
+ } from '../types/index.mjs';
11
+
12
+ /**
13
+ * Provides an initial value for an observable that doesn't have one.
14
+ * The resulting observable will immediately emit the initial value upon subscription,
15
+ * and then emit all subsequent values from the source.
16
+ *
17
+ * @template A - The type of values from the source
18
+ * @template I - The type of the initial value (defaults to A)
19
+ * @param initialValue - The initial value to emit
20
+ * @returns An operator that sets the initial value
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const num$ = source<number>();
25
+ *
26
+ * const initialized$ = num$.pipe(withInitialValue(0));
27
+ *
28
+ * initialized$.subscribe((x) => {
29
+ * console.log(x);
30
+ * }); // immediately logs: 0
31
+ *
32
+ * num$.next(1); // logs: 1
33
+ * ```
34
+ */
35
+ export const withInitialValue =
36
+ <A, I = A>(initialValue: I): WithInitialValueOperator<A, A | I> =>
37
+ (parentObservable) =>
38
+ new WithInitialValueObservableClass(parentObservable, initialValue);
39
+
40
+ class WithInitialValueObservableClass<A, I>
41
+ extends InitializedSyncChildObservableClass<A | I, readonly [A]>
42
+ implements WithInitialValueOperatorObservable<A, I>
43
+ {
44
+ constructor(parentObservable: Observable<A>, initialValue: I) {
45
+ super({
46
+ parents: [parentObservable],
47
+ initialValue: Optional.some(initialValue),
48
+ });
49
+ }
50
+
51
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
52
+ const par = this.parents[0];
53
+
54
+ const sn = par.getSnapshot();
55
+
56
+ if (par.updaterSymbol !== updaterSymbol || Optional.isNone(sn)) {
57
+ return; // skip update
58
+ }
59
+
60
+ this.setNext(sn.value, updaterSymbol);
61
+ }
62
+ }
63
+
64
+ if (import.meta.vitest !== undefined) {
65
+ test('type test', () => {
66
+ expect(1).toBe(1); // dummy
67
+ });
68
+
69
+ {
70
+ const s = source<number>();
71
+
72
+ const _d: InitializedObservable<number> = s.pipe(withInitialValue(0));
73
+
74
+ expectType<typeof _d, InitializedObservable<number>>('=');
75
+ }
76
+ }
@@ -0,0 +1,5 @@
1
+ export type ObservableId = Brand<symbol, 'observable-id'>;
2
+
3
+ export type SubscriberId = Brand<symbol, 'subscriber-id'>;
4
+
5
+ export type UpdaterSymbol = Brand<symbol, 'updater-symbol'>;
@@ -0,0 +1,5 @@
1
+ export * from './id.mjs';
2
+ export * from './observable-family.mjs';
3
+ export * from './observable-kind.mjs';
4
+ export * from './observable.mjs';
5
+ export * from './types.mjs';