synstate 0.1.0 → 0.1.2

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 (235) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +272 -532
  3. package/assets/synstate-icon.png +0 -0
  4. package/dist/core/combine/combine.d.mts +33 -3
  5. package/dist/core/combine/combine.d.mts.map +1 -1
  6. package/dist/core/combine/combine.mjs +34 -4
  7. package/dist/core/combine/combine.mjs.map +1 -1
  8. package/dist/core/combine/merge.d.mts +30 -4
  9. package/dist/core/combine/merge.d.mts.map +1 -1
  10. package/dist/core/combine/merge.mjs +30 -4
  11. package/dist/core/combine/merge.mjs.map +1 -1
  12. package/dist/core/combine/zip.d.mts +28 -3
  13. package/dist/core/combine/zip.d.mts.map +1 -1
  14. package/dist/core/combine/zip.mjs +28 -3
  15. package/dist/core/combine/zip.mjs.map +1 -1
  16. package/dist/core/create/from-array.d.mts +21 -3
  17. package/dist/core/create/from-array.d.mts.map +1 -1
  18. package/dist/core/create/from-array.mjs +21 -3
  19. package/dist/core/create/from-array.mjs.map +1 -1
  20. package/dist/core/create/from-promise.d.mts +29 -7
  21. package/dist/core/create/from-promise.d.mts.map +1 -1
  22. package/dist/core/create/from-promise.mjs +29 -7
  23. package/dist/core/create/from-promise.mjs.map +1 -1
  24. package/dist/core/create/from-subscribable.d.mts +58 -0
  25. package/dist/core/create/from-subscribable.d.mts.map +1 -1
  26. package/dist/core/create/from-subscribable.mjs +58 -0
  27. package/dist/core/create/from-subscribable.mjs.map +1 -1
  28. package/dist/core/create/interval.d.mts +29 -4
  29. package/dist/core/create/interval.d.mts.map +1 -1
  30. package/dist/core/create/interval.mjs +29 -4
  31. package/dist/core/create/interval.mjs.map +1 -1
  32. package/dist/core/create/of.d.mts +22 -3
  33. package/dist/core/create/of.d.mts.map +1 -1
  34. package/dist/core/create/of.mjs +22 -3
  35. package/dist/core/create/of.mjs.map +1 -1
  36. package/dist/core/create/source.d.mts +21 -2
  37. package/dist/core/create/source.d.mts.map +1 -1
  38. package/dist/core/create/source.mjs +2 -2
  39. package/dist/core/create/source.mjs.map +1 -1
  40. package/dist/core/create/timer.d.mts +23 -4
  41. package/dist/core/create/timer.d.mts.map +1 -1
  42. package/dist/core/create/timer.mjs +23 -4
  43. package/dist/core/create/timer.mjs.map +1 -1
  44. package/dist/core/index.d.mts +1 -0
  45. package/dist/core/index.d.mts.map +1 -1
  46. package/dist/core/index.mjs +15 -3
  47. package/dist/core/index.mjs.map +1 -1
  48. package/dist/core/operators/audit-time.d.mts +59 -0
  49. package/dist/core/operators/audit-time.d.mts.map +1 -1
  50. package/dist/core/operators/audit-time.mjs +59 -0
  51. package/dist/core/operators/audit-time.mjs.map +1 -1
  52. package/dist/core/operators/debounce-time.d.mts +22 -2
  53. package/dist/core/operators/debounce-time.d.mts.map +1 -1
  54. package/dist/core/operators/debounce-time.mjs +22 -2
  55. package/dist/core/operators/debounce-time.mjs.map +1 -1
  56. package/dist/core/operators/filter.d.mts +26 -1
  57. package/dist/core/operators/filter.d.mts.map +1 -1
  58. package/dist/core/operators/filter.mjs.map +1 -1
  59. package/dist/core/operators/index.mjs +3 -3
  60. package/dist/core/operators/map-with-index.d.mts +19 -17
  61. package/dist/core/operators/map-with-index.d.mts.map +1 -1
  62. package/dist/core/operators/map-with-index.mjs +21 -23
  63. package/dist/core/operators/map-with-index.mjs.map +1 -1
  64. package/dist/core/operators/merge-map.d.mts +48 -6
  65. package/dist/core/operators/merge-map.d.mts.map +1 -1
  66. package/dist/core/operators/merge-map.mjs +48 -6
  67. package/dist/core/operators/merge-map.mjs.map +1 -1
  68. package/dist/core/operators/pairwise.d.mts +30 -1
  69. package/dist/core/operators/pairwise.d.mts.map +1 -1
  70. package/dist/core/operators/pairwise.mjs +30 -1
  71. package/dist/core/operators/pairwise.mjs.map +1 -1
  72. package/dist/core/operators/scan.d.mts +23 -1
  73. package/dist/core/operators/scan.d.mts.map +1 -1
  74. package/dist/core/operators/scan.mjs +23 -1
  75. package/dist/core/operators/scan.mjs.map +1 -1
  76. package/dist/core/operators/skip-if-no-change.d.mts +26 -2
  77. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  78. package/dist/core/operators/skip-if-no-change.mjs +27 -3
  79. package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
  80. package/dist/core/operators/skip-until.d.mts +50 -0
  81. package/dist/core/operators/skip-until.d.mts.map +1 -1
  82. package/dist/core/operators/skip-until.mjs +50 -0
  83. package/dist/core/operators/skip-until.mjs.map +1 -1
  84. package/dist/core/operators/skip-while.d.mts +48 -1
  85. package/dist/core/operators/skip-while.d.mts.map +1 -1
  86. package/dist/core/operators/skip-while.mjs +50 -5
  87. package/dist/core/operators/skip-while.mjs.map +1 -1
  88. package/dist/core/operators/switch-map.d.mts +39 -5
  89. package/dist/core/operators/switch-map.d.mts.map +1 -1
  90. package/dist/core/operators/switch-map.mjs +39 -5
  91. package/dist/core/operators/switch-map.mjs.map +1 -1
  92. package/dist/core/operators/take-until.d.mts +20 -1
  93. package/dist/core/operators/take-until.d.mts.map +1 -1
  94. package/dist/core/operators/take-until.mjs +20 -1
  95. package/dist/core/operators/take-until.mjs.map +1 -1
  96. package/dist/core/operators/take-while.d.mts +47 -1
  97. package/dist/core/operators/take-while.d.mts.map +1 -1
  98. package/dist/core/operators/take-while.mjs +48 -3
  99. package/dist/core/operators/take-while.mjs.map +1 -1
  100. package/dist/core/operators/throttle-time.d.mts +44 -5
  101. package/dist/core/operators/throttle-time.d.mts.map +1 -1
  102. package/dist/core/operators/throttle-time.mjs +44 -5
  103. package/dist/core/operators/throttle-time.mjs.map +1 -1
  104. package/dist/core/operators/with-buffered-from.d.mts +57 -0
  105. package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
  106. package/dist/core/operators/with-buffered-from.mjs +58 -1
  107. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  108. package/dist/core/operators/with-current-value-from.d.mts +59 -0
  109. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  110. package/dist/core/operators/with-current-value-from.mjs +60 -1
  111. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  112. package/dist/core/operators/with-initial-value.d.mts +24 -2
  113. package/dist/core/operators/with-initial-value.d.mts.map +1 -1
  114. package/dist/core/operators/with-initial-value.mjs +24 -2
  115. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  116. package/dist/core/predefined/index.d.mts +2 -0
  117. package/dist/core/predefined/index.d.mts.map +1 -0
  118. package/dist/core/predefined/index.mjs +13 -0
  119. package/dist/core/predefined/index.mjs.map +1 -0
  120. package/dist/core/predefined/operators/attach-index.d.mts +8 -0
  121. package/dist/core/predefined/operators/attach-index.d.mts.map +1 -0
  122. package/dist/core/predefined/operators/attach-index.mjs +13 -0
  123. package/dist/core/predefined/operators/attach-index.mjs.map +1 -0
  124. package/dist/core/predefined/operators/index.d.mts +13 -0
  125. package/dist/core/predefined/operators/index.d.mts.map +1 -0
  126. package/dist/core/predefined/operators/index.mjs +13 -0
  127. package/dist/core/predefined/operators/index.mjs.map +1 -0
  128. package/dist/core/predefined/operators/map-optional.d.mts +4 -0
  129. package/dist/core/predefined/operators/map-optional.d.mts.map +1 -0
  130. package/dist/core/predefined/operators/map-optional.mjs +7 -0
  131. package/dist/core/predefined/operators/map-optional.mjs.map +1 -0
  132. package/dist/core/predefined/operators/map-result-err.d.mts +4 -0
  133. package/dist/core/predefined/operators/map-result-err.d.mts.map +1 -0
  134. package/dist/core/predefined/operators/map-result-err.mjs +7 -0
  135. package/dist/core/predefined/operators/map-result-err.mjs.map +1 -0
  136. package/dist/core/predefined/operators/map-result-ok.d.mts +4 -0
  137. package/dist/core/predefined/operators/map-result-ok.d.mts.map +1 -0
  138. package/dist/core/predefined/operators/map-result-ok.mjs +7 -0
  139. package/dist/core/predefined/operators/map-result-ok.mjs.map +1 -0
  140. package/dist/core/predefined/operators/map-to.d.mts +3 -0
  141. package/dist/core/predefined/operators/map-to.d.mts.map +1 -0
  142. package/dist/core/predefined/operators/map-to.mjs +6 -0
  143. package/dist/core/predefined/operators/map-to.mjs.map +1 -0
  144. package/dist/core/predefined/operators/map.d.mts +3 -0
  145. package/dist/core/predefined/operators/map.d.mts.map +1 -0
  146. package/dist/core/predefined/operators/map.mjs +8 -0
  147. package/dist/core/predefined/operators/map.mjs.map +1 -0
  148. package/dist/core/predefined/operators/pluck.d.mts +8 -0
  149. package/dist/core/predefined/operators/pluck.d.mts.map +1 -0
  150. package/dist/core/predefined/operators/pluck.mjs +11 -0
  151. package/dist/core/predefined/operators/pluck.mjs.map +1 -0
  152. package/dist/core/predefined/operators/skip.d.mts +3 -0
  153. package/dist/core/predefined/operators/skip.d.mts.map +1 -0
  154. package/dist/core/predefined/operators/skip.mjs +9 -0
  155. package/dist/core/predefined/operators/skip.mjs.map +1 -0
  156. package/dist/core/predefined/operators/take.d.mts +3 -0
  157. package/dist/core/predefined/operators/take.d.mts.map +1 -0
  158. package/dist/core/predefined/operators/take.mjs +8 -0
  159. package/dist/core/predefined/operators/take.mjs.map +1 -0
  160. package/dist/core/predefined/operators/unwrap-optional.d.mts +4 -0
  161. package/dist/core/predefined/operators/unwrap-optional.d.mts.map +1 -0
  162. package/dist/core/predefined/operators/unwrap-optional.mjs +9 -0
  163. package/dist/core/predefined/operators/unwrap-optional.mjs.map +1 -0
  164. package/dist/core/predefined/operators/unwrap-result-err.d.mts +4 -0
  165. package/dist/core/predefined/operators/unwrap-result-err.d.mts.map +1 -0
  166. package/dist/core/predefined/operators/unwrap-result-err.mjs +7 -0
  167. package/dist/core/predefined/operators/unwrap-result-err.mjs.map +1 -0
  168. package/dist/core/predefined/operators/unwrap-result-ok.d.mts +4 -0
  169. package/dist/core/predefined/operators/unwrap-result-ok.d.mts.map +1 -0
  170. package/dist/core/predefined/operators/unwrap-result-ok.mjs +9 -0
  171. package/dist/core/predefined/operators/unwrap-result-ok.mjs.map +1 -0
  172. package/dist/core/types/observable-family.d.mts +7 -7
  173. package/dist/entry-point.mjs +15 -3
  174. package/dist/entry-point.mjs.map +1 -1
  175. package/dist/index.mjs +15 -3
  176. package/dist/index.mjs.map +1 -1
  177. package/dist/utils/create-event-emitter.d.mts +20 -2
  178. package/dist/utils/create-event-emitter.d.mts.map +1 -1
  179. package/dist/utils/create-event-emitter.mjs +20 -2
  180. package/dist/utils/create-event-emitter.mjs.map +1 -1
  181. package/dist/utils/create-reducer.d.mts +13 -1
  182. package/dist/utils/create-reducer.d.mts.map +1 -1
  183. package/dist/utils/create-reducer.mjs +13 -1
  184. package/dist/utils/create-reducer.mjs.map +1 -1
  185. package/dist/utils/create-state.d.mts +24 -4
  186. package/dist/utils/create-state.d.mts.map +1 -1
  187. package/dist/utils/create-state.mjs +24 -4
  188. package/dist/utils/create-state.mjs.map +1 -1
  189. package/package.json +13 -12
  190. package/src/core/combine/combine.mts +34 -4
  191. package/src/core/combine/merge.mts +30 -4
  192. package/src/core/combine/zip.mts +28 -3
  193. package/src/core/create/from-array.mts +21 -3
  194. package/src/core/create/from-promise.mts +29 -7
  195. package/src/core/create/from-subscribable.mts +58 -0
  196. package/src/core/create/interval.mts +29 -4
  197. package/src/core/create/of.mts +22 -3
  198. package/src/core/create/source.mts +22 -3
  199. package/src/core/create/timer.mts +23 -4
  200. package/src/core/index.mts +1 -0
  201. package/src/core/operators/audit-time.mts +59 -0
  202. package/src/core/operators/debounce-time.mts +22 -2
  203. package/src/core/operators/filter.mts +26 -1
  204. package/src/core/operators/map-with-index.mts +22 -66
  205. package/src/core/operators/merge-map.mts +48 -6
  206. package/src/core/operators/pairwise.mts +30 -1
  207. package/src/core/operators/scan.mts +23 -1
  208. package/src/core/operators/skip-if-no-change.mts +27 -3
  209. package/src/core/operators/skip-until.mts +50 -0
  210. package/src/core/operators/skip-while.mts +49 -16
  211. package/src/core/operators/switch-map.mts +39 -5
  212. package/src/core/operators/take-until.mts +20 -1
  213. package/src/core/operators/take-while.mts +49 -8
  214. package/src/core/operators/throttle-time.mts +44 -5
  215. package/src/core/operators/with-buffered-from.mts +58 -1
  216. package/src/core/operators/with-current-value-from.mts +60 -1
  217. package/src/core/operators/with-initial-value.mts +24 -2
  218. package/src/core/predefined/index.mts +1 -0
  219. package/src/core/predefined/operators/attach-index.mts +13 -0
  220. package/src/core/predefined/operators/index.mts +12 -0
  221. package/src/core/predefined/operators/map-optional.mts +8 -0
  222. package/src/core/predefined/operators/map-result-err.mts +8 -0
  223. package/src/core/predefined/operators/map-result-ok.mts +8 -0
  224. package/src/core/predefined/operators/map-to.mts +5 -0
  225. package/src/core/predefined/operators/map.mts +5 -0
  226. package/src/core/predefined/operators/pluck.mts +12 -0
  227. package/src/core/predefined/operators/skip.mts +10 -0
  228. package/src/core/predefined/operators/take.mts +6 -0
  229. package/src/core/predefined/operators/unwrap-optional.mts +9 -0
  230. package/src/core/predefined/operators/unwrap-result-err.mts +8 -0
  231. package/src/core/predefined/operators/unwrap-result-ok.mts +9 -0
  232. package/src/core/types/observable-family.mts +7 -7
  233. package/src/utils/create-event-emitter.mts +20 -2
  234. package/src/utils/create-reducer.mts +13 -1
  235. package/src/utils/create-state.mts +24 -4
@@ -7,6 +7,65 @@ import {
7
7
  type UpdaterSymbol,
8
8
  } from '../types/index.mjs';
9
9
 
10
+ /**
11
+ * Emits the last value from the source observable after a specified time window has passed.
12
+ * Unlike throttleTime which emits the first value, auditTime emits the last value.
13
+ *
14
+ * @template A - The type of values from the source
15
+ * @param milliSeconds - The audit time window in milliseconds
16
+ * @returns An operator that audits emissions from the observable
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // Timeline (1000ms audit):
21
+ * //
22
+ * // Time(ms) 0 100 200 300 400 ... 1000 1100
23
+ * // input$ e1 e2 e3 e4 e5
24
+ * // audited$ e5 (emitted at end of window)
25
+ * // |-------1000ms window------> ^
26
+ * //
27
+ * // Explanation:
28
+ * // - auditTime emits the LAST value received during each time window
29
+ * // - Unlike throttleTime (which emits the FIRST value), audit emits the LAST
30
+ * // - At 0-1000ms: e1-e5 are received
31
+ * // - At 1000ms: e5 (the last value in the window) is emitted
32
+ * // - Useful when you want the most recent value after a burst of events
33
+ *
34
+ * const input$ = source<number>();
35
+ *
36
+ * const audited$ = input$.pipe(auditTime(200));
37
+ *
38
+ * const mut_history: number[] = [];
39
+ *
40
+ * audited$.subscribe((value) => {
41
+ * mut_history.push(value);
42
+ * });
43
+ *
44
+ * input$.next(1);
45
+ *
46
+ * input$.next(2);
47
+ *
48
+ * input$.next(3);
49
+ *
50
+ * assert.deepStrictEqual(mut_history, []);
51
+ *
52
+ * await new Promise((resolve) => {
53
+ * setTimeout(resolve, 250);
54
+ * });
55
+ *
56
+ * assert.deepStrictEqual(mut_history, [3]);
57
+ *
58
+ * input$.next(4);
59
+ *
60
+ * input$.next(5);
61
+ *
62
+ * await new Promise((resolve) => {
63
+ * setTimeout(resolve, 250);
64
+ * });
65
+ *
66
+ * assert.deepStrictEqual(mut_history, [3, 5]);
67
+ * ```
68
+ */
10
69
  export const auditTime = <A,>(
11
70
  milliSeconds: number,
12
71
  ): KeepInitialValueOperator<A, A> =>
@@ -17,12 +17,27 @@ import {
17
17
  *
18
18
  * @example
19
19
  * ```ts
20
+ * // Timeline (300ms debounce):
21
+ * //
22
+ * // Time(ms) 0 100 200 300 400 500 600 ... 900 1000
23
+ * // input$ 'h' 'he' 'hel' 'hello'
24
+ * // debounced$ 'hello' (emitted after 300ms silence)
25
+ * //
26
+ * // Explanation:
27
+ * // - At 0ms: 'h' is emitted, timer starts
28
+ * // - At 100ms: 'he' is emitted, timer resets
29
+ * // - At 200ms: 'hel' is emitted, timer resets
30
+ * // - At 300ms: 'hello' is emitted, timer resets
31
+ * // - At 600ms: No new emission for 300ms, 'hello' is finally emitted
32
+ *
20
33
  * const input$ = source<string>();
21
34
  *
22
35
  * const debounced$ = input$.pipe(debounceTime(300));
23
36
  *
37
+ * const mut_history: string[] = [];
38
+ *
24
39
  * debounced$.subscribe((value) => {
25
- * console.log(value);
40
+ * mut_history.push(value);
26
41
  * });
27
42
  *
28
43
  * input$.next('h');
@@ -32,7 +47,12 @@ import {
32
47
  * input$.next('hel');
33
48
  *
34
49
  * input$.next('hello');
35
- * // After 300ms of silence, logs: hello
50
+ *
51
+ * await new Promise((resolve) => {
52
+ * setTimeout(resolve, 400);
53
+ * });
54
+ *
55
+ * assert.deepStrictEqual(mut_history, ['hello']);
36
56
  * ```
37
57
  */
38
58
  export const debounceTime = <A,>(
@@ -27,17 +27,42 @@ import { withInitialValue } from './with-initial-value.mjs';
27
27
  *
28
28
  * @example
29
29
  * ```ts
30
+ * // Timeline:
31
+ * //
32
+ * // num$ 1 2 3 4 5 6
33
+ * // even$ 2 4 6
34
+ * //
35
+ * // Explanation:
36
+ * // - filter passes through only values that satisfy the predicate
37
+ * // - Only even numbers (2, 4, 6) are emitted
38
+ *
30
39
  * const num$ = source<number>();
31
40
  *
32
41
  * const even$ = num$.pipe(filter((x) => x % 2 === 0));
33
42
  *
43
+ * const mut_history: number[] = [];
44
+ *
34
45
  * even$.subscribe((x) => {
35
- * console.log(x);
46
+ * mut_history.push(x);
36
47
  * });
37
48
  *
38
49
  * num$.next(1); // nothing logged
39
50
  *
40
51
  * num$.next(2); // logs: 2
52
+ *
53
+ * assert.deepStrictEqual(mut_history, [2]);
54
+ *
55
+ * num$.next(3); // nothing logged
56
+ *
57
+ * num$.next(4); // logs: 4
58
+ *
59
+ * assert.deepStrictEqual(mut_history, [2, 4]);
60
+ *
61
+ * num$.next(5);
62
+ *
63
+ * num$.next(6);
64
+ *
65
+ * assert.deepStrictEqual(mut_history, [2, 4, 6]);
41
66
  * ```
42
67
  */
43
68
  export function filter<A, B extends A>(
@@ -1,10 +1,4 @@
1
- import {
2
- Optional,
3
- Result,
4
- SafeUint,
5
- asSafeUint,
6
- expectType,
7
- } from 'ts-data-forge';
1
+ import { Optional, SafeUint, asSafeUint, expectType } from 'ts-data-forge';
8
2
  import { SyncChildObservableClass } from '../class/index.mjs';
9
3
  import { source } from '../create/index.mjs';
10
4
  import {
@@ -26,17 +20,32 @@ import { withInitialValue } from './with-initial-value.mjs';
26
20
  *
27
21
  * @example
28
22
  * ```ts
29
- * const num$ = source<number>();
23
+ * // Timeline:
24
+ * //
25
+ * // num$ "a" "b" "c"
26
+ * // indexed$ "0: a" "1: b" "2: c"
27
+ * //
28
+ * // Explanation:
29
+ * // - mapWithIndex transforms each value along with its index
30
+ * // - Index starts at 0 and increments with each emission
31
+ *
32
+ * const num$ = source<string>();
30
33
  *
31
34
  * const indexed$ = num$.pipe(mapWithIndex((x, i) => `${i}: ${x}`));
32
35
  *
36
+ * const mut_history: string[] = [];
37
+ *
33
38
  * indexed$.subscribe((s) => {
34
- * console.log(s);
39
+ * mut_history.push(s);
35
40
  * });
36
41
  *
37
- * num$.next(10); // logs: 0: 10
42
+ * num$.next('a'); // 0: a
43
+ *
44
+ * num$.next('b'); // 1: b
45
+ *
46
+ * num$.next('c'); // 2: c
38
47
  *
39
- * num$.next(20); // logs: 1: 20
48
+ * assert.deepStrictEqual(mut_history, ['0: a', '1: b', '2: c']);
40
49
  * ```
41
50
  */
42
51
  export const mapWithIndex = <A, B>(
@@ -49,59 +58,6 @@ export const mapWithIndex = <A, B>(
49
58
  mapFn,
50
59
  )) as KeepInitialValueOperator<A, B>;
51
60
 
52
- /* Specialized operators */
53
-
54
- export const map = <A, B>(mapFn: (x: A) => B): KeepInitialValueOperator<A, B> =>
55
- mapWithIndex(mapFn);
56
-
57
- export const mapTo = <A, B>(value: B): KeepInitialValueOperator<A, B> =>
58
- map(() => value);
59
-
60
- export const pluck = <A, K extends keyof A>(
61
- key: K,
62
- ): KeepInitialValueOperator<A, A[K]> => map((a) => a[key]);
63
-
64
- export const getKey = pluck; // alias
65
-
66
- export const attachIndex = <A,>(): KeepInitialValueOperator<
67
- A,
68
- readonly [SafeUint | -1, A]
69
- > => mapWithIndex((a, i) => [i, a] as const);
70
-
71
- export const withIndex = attachIndex; // alias
72
-
73
- export const unwrapOptional = <
74
- O extends UnknownOptional,
75
- >(): KeepInitialValueOperator<O, Optional.Unwrap<O> | undefined> =>
76
- // eslint-disable-next-line total-functions/no-unsafe-type-assertion
77
- map(Optional.unwrap as Fn<O, Optional.Unwrap<O> | undefined>);
78
-
79
- export const unwrapResultOk = <
80
- R extends UnknownResult,
81
- >(): KeepInitialValueOperator<R, Result.UnwrapOk<R> | undefined> =>
82
- // eslint-disable-next-line total-functions/no-unsafe-type-assertion
83
- map(Result.unwrapOk as Fn<R, Result.UnwrapOk<R> | undefined>);
84
-
85
- export const unwrapResultErr = <
86
- R extends UnknownResult,
87
- >(): KeepInitialValueOperator<R, Result.UnwrapErr<R> | undefined> =>
88
- map(Result.unwrapErr as Fn<R, Result.UnwrapErr<R> | undefined>);
89
-
90
- export const mapOptional = <O extends UnknownOptional, B>(
91
- mapFn: (x: Optional.Unwrap<O>) => B,
92
- ): KeepInitialValueOperator<O, Optional<B>> =>
93
- map((a) => Optional.map(a, mapFn));
94
-
95
- export const mapResultOk = <R extends UnknownResult, S2>(
96
- mapFn: (x: Result.UnwrapOk<R>) => S2,
97
- ): KeepInitialValueOperator<R, Result<S2, Result.UnwrapErr<R>>> =>
98
- map((a) => Result.map(a, mapFn));
99
-
100
- export const mapResultErr = <R extends UnknownResult, E2>(
101
- mapFn: (x: Result.UnwrapErr<R>) => E2,
102
- ): KeepInitialValueOperator<R, Result<Result.UnwrapOk<R>, E2>> =>
103
- map((a) => Result.mapErr(a, mapFn));
104
-
105
61
  /* implementation */
106
62
 
107
63
  class MapWithIndexObservableClass<A, B>
@@ -151,7 +107,7 @@ if (import.meta.vitest !== undefined) {
151
107
  {
152
108
  const s: Observable<number> = source<number>();
153
109
 
154
- const _d1 = s.pipe(map((x) => x + 1));
110
+ const _d1 = s.pipe(mapWithIndex((x, i) => x + i));
155
111
 
156
112
  expectType<typeof _d1, Observable<number>>('=');
157
113
  }
@@ -161,7 +117,7 @@ if (import.meta.vitest !== undefined) {
161
117
 
162
118
  const m: InitializedObservable<number> = s.pipe(withInitialValue(0));
163
119
 
164
- const _d = m.pipe(map((x) => x + 1));
120
+ const _d = m.pipe(mapWithIndex((x, i) => x + i));
165
121
 
166
122
  expectType<typeof _d, InitializedObservable<number>>('=');
167
123
  }
@@ -19,16 +19,58 @@ import {
19
19
  *
20
20
  * @example
21
21
  * ```ts
22
+ * // Timeline:
23
+ * //
24
+ * // ids$ 1 2 3
25
+ * // requests fetch(1) fetch(2) fetch(3)
26
+ * // users$ result1 result2 result3
27
+ * // (parallel) (parallel) (parallel)
28
+ * //
29
+ * // Explanation:
30
+ * // - mergeMap runs all inner observables in parallel
31
+ * // - Results are emitted as they arrive (may be out of order)
32
+ * // - Does NOT cancel previous requests
33
+ * // - All requests run concurrently and all results are emitted
34
+ *
22
35
  * const ids$ = source<number>();
23
36
  *
24
- * const users$ = ids$.pipe(mergeMap((id) => fromPromise(fetchUser(id))));
37
+ * const users$ = ids$.pipe(
38
+ * mergeMap((id) => {
39
+ * const result$ = source<{ id: number }>();
40
+ *
41
+ * setTimeout(() => {
42
+ * result$.next({ id });
43
+ *
44
+ * result$.complete();
45
+ * }, 10);
46
+ *
47
+ * return result$;
48
+ * }),
49
+ * );
25
50
  *
26
- * users$.subscribe((user) => {
27
- * console.log(user);
51
+ * const mut_history: { id: number }[] = [];
52
+ *
53
+ * users$.subscribe((value) => {
54
+ * mut_history.push(value);
28
55
  * });
29
- * // All requests run in parallel, results merged as they arrive
30
56
  *
31
- * const fetchUser = async (id: number): Promise<unknown> => ({ id });
57
+ * ids$.next(1);
58
+ *
59
+ * ids$.next(2);
60
+ *
61
+ * ids$.next(3);
62
+ *
63
+ * await new Promise((resolve) => {
64
+ * setTimeout(resolve, 200);
65
+ * });
66
+ *
67
+ * assert.deepStrictEqual(mut_history.length, 3);
68
+ *
69
+ * assert.isTrue(mut_history.some((u) => u.id === 1));
70
+ *
71
+ * assert.isTrue(mut_history.some((u) => u.id === 2));
72
+ *
73
+ * assert.isTrue(mut_history.some((u) => u.id === 3));
32
74
  * ```
33
75
  *
34
76
  * @note To improve code readability, consider using `createState` instead of `mergeMap`,
@@ -42,7 +84,7 @@ export const mergeMap =
42
84
  new MergeMapObservableClass(parentObservable, mapToObservable);
43
85
 
44
86
  /**
45
- * Alias for `mergeMap()`.
87
+ * Alias for `mergeMap`.
46
88
  * @see mergeMap
47
89
  */
48
90
  export const flatMap = mergeMap;
@@ -16,19 +16,48 @@ import {
16
16
  *
17
17
  * @example
18
18
  * ```ts
19
+ * // Timeline:
20
+ * //
21
+ * // num$ 1 2 3 4
22
+ * // pairs$ [1,2] [2,3] [3,4]
23
+ * //
24
+ * // Explanation:
25
+ * // - pairwise emits the current and previous values as a tuple
26
+ * // - Nothing is emitted for the first value (no previous value yet)
27
+ * // - Useful for tracking changes between consecutive values
28
+ *
19
29
  * const num$ = source<number>();
20
30
  *
21
31
  * const pairs$ = num$.pipe(pairwise());
22
32
  *
33
+ * const mut_history: (readonly [number, number])[] = [];
34
+ *
23
35
  * pairs$.subscribe(([prev, curr]) => {
24
- * console.log(prev, curr);
36
+ * mut_history.push([prev, curr]);
25
37
  * });
26
38
  *
27
39
  * num$.next(1); // nothing logged
28
40
  *
41
+ * assert.deepStrictEqual(mut_history, []);
42
+ *
29
43
  * num$.next(2); // logs: 1, 2
30
44
  *
45
+ * assert.deepStrictEqual(mut_history, [[1, 2]]);
46
+ *
31
47
  * num$.next(3); // logs: 2, 3
48
+ *
49
+ * assert.deepStrictEqual(mut_history, [
50
+ * [1, 2],
51
+ * [2, 3],
52
+ * ]);
53
+ *
54
+ * num$.next(4); // logs: 3, 4
55
+ *
56
+ * assert.deepStrictEqual(mut_history, [
57
+ * [1, 2],
58
+ * [2, 3],
59
+ * [3, 4],
60
+ * ]);
32
61
  * ```
33
62
  */
34
63
  export const pairwise = <A,>(): DropInitialValueOperator<A, readonly [A, A]> =>
@@ -19,19 +19,41 @@ import {
19
19
  *
20
20
  * @example
21
21
  * ```ts
22
+ * // Timeline (accumulating sum):
23
+ * //
24
+ * // num$ 1 2 3 4 5
25
+ * // sum$ 1 3 6 10 15
26
+ * // | | | | |
27
+ * // 0+1 1+2 3+3 6+4 10+5
28
+ * //
29
+ * // Explanation:
30
+ * // - scan accumulates values over time using a reducer function
31
+ * // - Starting with seed value 0, each emission adds to the accumulator
32
+ * // - Similar to Array.reduce, but for streams
33
+ *
22
34
  * const num$ = source<number>();
23
35
  *
24
36
  * const sum$ = num$.pipe(scan((acc, curr) => acc + curr, 0));
25
37
  *
38
+ * const mut_history: number[] = [];
39
+ *
26
40
  * sum$.subscribe((x) => {
27
- * console.log(x);
41
+ * mut_history.push(x);
28
42
  * });
29
43
  *
44
+ * assert.deepStrictEqual(mut_history, [0]);
45
+ *
30
46
  * num$.next(1); // logs: 1
31
47
  *
48
+ * assert.deepStrictEqual(mut_history, [0, 1]);
49
+ *
32
50
  * num$.next(2); // logs: 3
33
51
  *
52
+ * assert.deepStrictEqual(mut_history, [0, 1, 3]);
53
+ *
34
54
  * num$.next(3); // logs: 6
55
+ *
56
+ * assert.deepStrictEqual(mut_history, [0, 1, 3, 6]);
35
57
  * ```
36
58
  */
37
59
  export const scan =
@@ -17,19 +17,43 @@ import {
17
17
  *
18
18
  * @example
19
19
  * ```ts
20
+ * // Timeline:
21
+ * //
22
+ * // num$ 1 1 2 2 2 3
23
+ * // distinct$ 1 2 3
24
+ * //
25
+ * // Explanation:
26
+ * // - skipIfNoChange filters out consecutive duplicate values
27
+ * // - Uses strict equality (===) for comparison
28
+ * // - Only emits when the value actually changes
29
+ *
20
30
  * const num$ = source<number>();
21
31
  *
22
32
  * const distinct$ = num$.pipe(skipIfNoChange());
23
33
  *
34
+ * const mut_history: number[] = [];
35
+ *
24
36
  * distinct$.subscribe((x) => {
25
- * console.log(x);
37
+ * mut_history.push(x);
26
38
  * });
27
39
  *
28
40
  * num$.next(1); // logs: 1
29
41
  *
42
+ * assert.deepStrictEqual(mut_history, [1]);
43
+ *
30
44
  * num$.next(1); // nothing logged
31
45
  *
46
+ * assert.deepStrictEqual(mut_history, [1]);
47
+ *
32
48
  * num$.next(2); // logs: 2
49
+ *
50
+ * assert.deepStrictEqual(mut_history, [1, 2]);
51
+ *
52
+ * num$.next(2); // nothing logged
53
+ *
54
+ * num$.next(3); // logs: 3
55
+ *
56
+ * assert.deepStrictEqual(mut_history, [1, 2, 3]);
33
57
  * ```
34
58
  */
35
59
  export const skipIfNoChange = <A,>(
@@ -43,10 +67,10 @@ export const skipIfNoChange = <A,>(
43
67
  )) as KeepInitialValueOperator<A, A>;
44
68
 
45
69
  /**
46
- * Alias for `skipIfNoChange()`.
70
+ * Alias for `skipIfNoChange`.
47
71
  * @see skipIfNoChange
48
72
  */
49
- export const distinctUntilChanged = skipIfNoChange; // alias
73
+ export const distinctUntilChanged = skipIfNoChange;
50
74
 
51
75
  class SkipIfNoChangeObservableClass<A>
52
76
  extends SyncChildObservableClass<A, readonly [A]>
@@ -7,6 +7,56 @@ import {
7
7
  type UpdaterSymbol,
8
8
  } from '../types/index.mjs';
9
9
 
10
+ /**
11
+ * Skips all values from the source observable until the notifier observable emits.
12
+ *
13
+ * @template A - The type of values from the source
14
+ * @param notifier - An observable that signals when to start emitting
15
+ * @returns An operator that skips values until the notifier emits
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * // Timeline:
20
+ * //
21
+ * // num$ 1 2 3 start 4 5 6
22
+ * // startNotifier X
23
+ * // skipped$ 4 5 6
24
+ * // |------ skipped -------|
25
+ * //
26
+ * // Explanation:
27
+ * // - skipUntil ignores all values until the notifier emits
28
+ * // - After the notifier emits, all subsequent values are passed through
29
+ * // - Opposite of takeUntil (which completes when notifier emits)
30
+ *
31
+ * const num$ = source<number>();
32
+ *
33
+ * const [startNotifier, start_] = createEventEmitter();
34
+ *
35
+ * const skipped$ = num$.pipe(skipUntil(startNotifier));
36
+ *
37
+ * const mut_history: number[] = [];
38
+ *
39
+ * skipped$.subscribe((x) => {
40
+ * mut_history.push(x);
41
+ * });
42
+ *
43
+ * num$.next(1); // nothing logged
44
+ *
45
+ * num$.next(2); // nothing logged
46
+ *
47
+ * assert.deepStrictEqual(mut_history, []);
48
+ *
49
+ * start_();
50
+ *
51
+ * num$.next(4); // logs: 4
52
+ *
53
+ * assert.deepStrictEqual(mut_history, [4]);
54
+ *
55
+ * num$.next(5); // logs: 5
56
+ *
57
+ * assert.deepStrictEqual(mut_history, [4, 5]);
58
+ * ```
59
+ */
10
60
  export const skipUntil =
11
61
  <A,>(notifier: Observable<unknown>): DropInitialValueOperator<A, A> =>
12
62
  (parentObservable) =>
@@ -1,10 +1,4 @@
1
- import {
2
- Optional,
3
- PositiveSafeInt,
4
- SafeUint,
5
- asSafeUint,
6
- pipe,
7
- } from 'ts-data-forge';
1
+ import { Optional, SafeUint, asSafeUint, pipe } from 'ts-data-forge';
8
2
  import { SyncChildObservableClass } from '../class/index.mjs';
9
3
  import {
10
4
  type DropInitialValueOperator,
@@ -13,6 +7,54 @@ import {
13
7
  type UpdaterSymbol,
14
8
  } from '../types/index.mjs';
15
9
 
10
+ /**
11
+ * Skips values from the source observable while the predicate returns true.
12
+ * Once the predicate returns false, all subsequent values pass through.
13
+ *
14
+ * @template A - The type of values from the source
15
+ * @param predicate - Function to test each value
16
+ * @returns An operator that skips values while the predicate is true
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // Timeline:
21
+ * //
22
+ * // num$ 1 2 3 4 5 6 7
23
+ * // skipped$ 5 6 7
24
+ * // |---- skip -----|
25
+ * //
26
+ * // Explanation:
27
+ * // - skipWhile skips values while the predicate returns true
28
+ * // - Once the predicate returns false, all subsequent values pass through
29
+ * // - Unlike filter, the predicate is never checked again after the first false
30
+ *
31
+ * const num$ = source<number>();
32
+ *
33
+ * const skipped$ = num$.pipe(skipWhile((x) => x < 5));
34
+ *
35
+ * const mut_history: number[] = [];
36
+ *
37
+ * skipped$.subscribe((x) => {
38
+ * mut_history.push(x);
39
+ * });
40
+ *
41
+ * num$.next(1); // nothing logged
42
+ *
43
+ * num$.next(2); // nothing logged
44
+ *
45
+ * num$.next(5); // logs: 5
46
+ *
47
+ * assert.deepStrictEqual(mut_history, [5]);
48
+ *
49
+ * num$.next(6); // logs: 6
50
+ *
51
+ * assert.deepStrictEqual(mut_history, [5, 6]);
52
+ *
53
+ * num$.next(7); // logs: 7
54
+ *
55
+ * assert.deepStrictEqual(mut_history, [5, 6, 7]);
56
+ * ```
57
+ */
16
58
  export const skipWhile =
17
59
  <A,>(
18
60
  predicate: (value: A, index: SafeUint | -1) => boolean,
@@ -20,15 +62,6 @@ export const skipWhile =
20
62
  (parentObservable) =>
21
63
  new SkipWhileObservableClass(parentObservable, predicate);
22
64
 
23
- /* Specialized operators */
24
-
25
- export const skip = <A,>(
26
- n: PositiveSafeIntWithSmallInt,
27
- ): DropInitialValueOperator<A, A> =>
28
- !PositiveSafeInt.is(n) ? idFn : skipWhile((_, index) => index + 1 <= n);
29
-
30
- const idFn = <T,>(value: T): T => value;
31
-
32
65
  /* implementation */
33
66
 
34
67
  class SkipWhileObservableClass<A>
@@ -19,18 +19,52 @@ import {
19
19
  *
20
20
  * @example
21
21
  * ```ts
22
+ * // Timeline:
23
+ * //
24
+ * // searchQuery$ "a" "ab" "abc"
25
+ * // requests fetch1 fetch2 fetch3
26
+ * // results$ cancel cancel result3
27
+ * // fetch1 fetch2
28
+ * //
29
+ * // Explanation:
30
+ * // - switchMap cancels previous inner observables when a new value arrives
31
+ * // - Only the result from the latest search query is emitted
32
+ * // - Previous ongoing requests are cancelled
33
+ * // - Ideal for search-as-you-type scenarios
34
+ *
22
35
  * const searchQuery$ = source<string>();
23
36
  *
24
37
  * const results$ = searchQuery$.pipe(
25
- * switchMap((query) => fromPromise(fetchResults(query))),
38
+ * switchMap((query) => {
39
+ * const result$ = source<string[]>();
40
+ *
41
+ * setTimeout(() => {
42
+ * result$.next([query]);
43
+ *
44
+ * result$.complete();
45
+ * }, 10);
46
+ *
47
+ * return result$;
48
+ * }),
26
49
  * );
27
50
  *
28
- * results$.subscribe((results) => {
29
- * console.log(results);
51
+ * const mut_history: string[][] = [];
52
+ *
53
+ * results$.subscribe((value) => {
54
+ * mut_history.push(value);
55
+ * });
56
+ *
57
+ * searchQuery$.next('a');
58
+ *
59
+ * searchQuery$.next('ab');
60
+ *
61
+ * searchQuery$.next('abc');
62
+ *
63
+ * await new Promise((resolve) => {
64
+ * setTimeout(resolve, 200);
30
65
  * });
31
- * // Only the latest search results are emitted, previous searches are cancelled
32
66
  *
33
- * const fetchResults = async (_query: string): Promise<readonly unknown[]> => [];
67
+ * assert.deepStrictEqual(mut_history, [['abc']]);
34
68
  * ```
35
69
  *
36
70
  * @note To improve code readability, consider using `createState` instead of `switchMap`,