synstate 0.1.1 → 1.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 (309) hide show
  1. package/README.md +317 -298
  2. package/dist/core/class/child-observable-class.d.mts.map +1 -1
  3. package/dist/core/class/child-observable-class.mjs +43 -10
  4. package/dist/core/class/child-observable-class.mjs.map +1 -1
  5. package/dist/core/class/observable-base-class.d.mts +4 -4
  6. package/dist/core/class/observable-base-class.d.mts.map +1 -1
  7. package/dist/core/class/observable-base-class.mjs +8 -8
  8. package/dist/core/class/observable-base-class.mjs.map +1 -1
  9. package/dist/core/class/root-observable-class.d.mts +1 -1
  10. package/dist/core/class/root-observable-class.d.mts.map +1 -1
  11. package/dist/core/class/root-observable-class.mjs +9 -9
  12. package/dist/core/class/root-observable-class.mjs.map +1 -1
  13. package/dist/core/combine/combine.d.mts +7 -7
  14. package/dist/core/combine/combine.mjs +13 -14
  15. package/dist/core/combine/combine.mjs.map +1 -1
  16. package/dist/core/combine/merge.d.mts +6 -6
  17. package/dist/core/combine/merge.mjs +9 -9
  18. package/dist/core/combine/merge.mjs.map +1 -1
  19. package/dist/core/combine/zip.d.mts +20 -19
  20. package/dist/core/combine/zip.d.mts.map +1 -1
  21. package/dist/core/combine/zip.mjs +22 -21
  22. package/dist/core/combine/zip.mjs.map +1 -1
  23. package/dist/core/create/{interval.d.mts → counter.d.mts} +14 -12
  24. package/dist/core/create/counter.d.mts.map +1 -0
  25. package/dist/core/create/{interval.mjs → counter.mjs} +21 -23
  26. package/dist/core/create/counter.mjs.map +1 -0
  27. package/dist/core/create/from-abortable-promise.d.mts +29 -0
  28. package/dist/core/create/from-abortable-promise.d.mts.map +1 -0
  29. package/dist/core/create/from-abortable-promise.mjs +70 -0
  30. package/dist/core/create/from-abortable-promise.mjs.map +1 -0
  31. package/dist/core/create/from-promise.d.mts +9 -6
  32. package/dist/core/create/from-promise.d.mts.map +1 -1
  33. package/dist/core/create/from-promise.mjs +8 -5
  34. package/dist/core/create/from-promise.mjs.map +1 -1
  35. package/dist/core/create/from-subscribable.d.mts +4 -4
  36. package/dist/core/create/from-subscribable.mjs +4 -4
  37. package/dist/core/create/index.d.mts +3 -3
  38. package/dist/core/create/index.d.mts.map +1 -1
  39. package/dist/core/create/index.mjs +4 -4
  40. package/dist/core/create/just.d.mts +32 -0
  41. package/dist/core/create/just.d.mts.map +1 -0
  42. package/dist/core/create/just.mjs +44 -0
  43. package/dist/core/create/just.mjs.map +1 -0
  44. package/dist/core/create/source.d.mts +7 -12
  45. package/dist/core/create/source.d.mts.map +1 -1
  46. package/dist/core/create/source.mjs +1 -6
  47. package/dist/core/create/source.mjs.map +1 -1
  48. package/dist/core/create/timer.d.mts +6 -4
  49. package/dist/core/create/timer.d.mts.map +1 -1
  50. package/dist/core/create/timer.mjs +6 -7
  51. package/dist/core/create/timer.mjs.map +1 -1
  52. package/dist/core/index.d.mts +1 -1
  53. package/dist/core/index.d.mts.map +1 -1
  54. package/dist/core/index.mjs +21 -14
  55. package/dist/core/index.mjs.map +1 -1
  56. package/dist/core/operators/audit.d.mts +97 -0
  57. package/dist/core/operators/audit.d.mts.map +1 -0
  58. package/dist/core/operators/audit.mjs +144 -0
  59. package/dist/core/operators/audit.mjs.map +1 -0
  60. package/dist/core/operators/debounce.d.mts +88 -0
  61. package/dist/core/operators/debounce.d.mts.map +1 -0
  62. package/dist/core/operators/debounce.mjs +130 -0
  63. package/dist/core/operators/debounce.mjs.map +1 -0
  64. package/dist/core/operators/filter.d.mts +5 -5
  65. package/dist/core/operators/filter.mjs +3 -3
  66. package/dist/core/operators/filter.mjs.map +1 -1
  67. package/dist/core/operators/index.d.mts +4 -4
  68. package/dist/core/operators/index.d.mts.map +1 -1
  69. package/dist/core/operators/index.mjs +6 -6
  70. package/dist/core/operators/map.d.mts +41 -0
  71. package/dist/core/operators/map.d.mts.map +1 -0
  72. package/dist/core/operators/map.mjs +71 -0
  73. package/dist/core/operators/map.mjs.map +1 -0
  74. package/dist/core/operators/merge-map.d.mts +57 -30
  75. package/dist/core/operators/merge-map.d.mts.map +1 -1
  76. package/dist/core/operators/merge-map.mjs +59 -32
  77. package/dist/core/operators/merge-map.mjs.map +1 -1
  78. package/dist/core/operators/pairwise.d.mts +6 -6
  79. package/dist/core/operators/pairwise.mjs +9 -9
  80. package/dist/core/operators/pairwise.mjs.map +1 -1
  81. package/dist/core/operators/scan.d.mts +6 -6
  82. package/dist/core/operators/scan.mjs +9 -9
  83. package/dist/core/operators/scan.mjs.map +1 -1
  84. package/dist/core/operators/skip-if-no-change.d.mts +21 -9
  85. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  86. package/dist/core/operators/skip-if-no-change.mjs +25 -13
  87. package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
  88. package/dist/core/operators/skip-until.d.mts +5 -5
  89. package/dist/core/operators/skip-until.mjs +8 -8
  90. package/dist/core/operators/skip-until.mjs.map +1 -1
  91. package/dist/core/operators/skip-while.d.mts +18 -9
  92. package/dist/core/operators/skip-while.d.mts.map +1 -1
  93. package/dist/core/operators/skip-while.mjs +28 -16
  94. package/dist/core/operators/skip-while.mjs.map +1 -1
  95. package/dist/core/operators/switch-map.d.mts +57 -26
  96. package/dist/core/operators/switch-map.d.mts.map +1 -1
  97. package/dist/core/operators/switch-map.mjs +59 -28
  98. package/dist/core/operators/switch-map.mjs.map +1 -1
  99. package/dist/core/operators/take-until.d.mts +5 -5
  100. package/dist/core/operators/take-until.mjs +8 -8
  101. package/dist/core/operators/take-until.mjs.map +1 -1
  102. package/dist/core/operators/take-while.d.mts +15 -8
  103. package/dist/core/operators/take-while.d.mts.map +1 -1
  104. package/dist/core/operators/take-while.mjs +19 -13
  105. package/dist/core/operators/take-while.mjs.map +1 -1
  106. package/dist/core/operators/throttle.d.mts +81 -0
  107. package/dist/core/operators/throttle.d.mts.map +1 -0
  108. package/dist/core/operators/throttle.mjs +126 -0
  109. package/dist/core/operators/throttle.mjs.map +1 -0
  110. package/dist/core/operators/with-buffered-from.d.mts +13 -9
  111. package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
  112. package/dist/core/operators/with-buffered-from.mjs +17 -13
  113. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  114. package/dist/core/operators/with-current-value-from.d.mts +14 -9
  115. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  116. package/dist/core/operators/with-current-value-from.mjs +18 -13
  117. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  118. package/dist/core/operators/with-initial-value.d.mts +5 -5
  119. package/dist/core/operators/with-initial-value.mjs +8 -8
  120. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  121. package/dist/core/predefined/index.d.mts +2 -0
  122. package/dist/core/predefined/index.d.mts.map +1 -0
  123. package/dist/core/predefined/index.mjs +12 -0
  124. package/dist/core/predefined/index.mjs.map +1 -0
  125. package/dist/core/predefined/operators/attach-index.d.mts +57 -0
  126. package/dist/core/predefined/operators/attach-index.d.mts.map +1 -0
  127. package/dist/core/predefined/operators/attach-index.mjs +62 -0
  128. package/dist/core/predefined/operators/attach-index.mjs.map +1 -0
  129. package/dist/core/predefined/operators/index.d.mts +12 -0
  130. package/dist/core/predefined/operators/index.d.mts.map +1 -0
  131. package/dist/core/predefined/operators/index.mjs +12 -0
  132. package/dist/core/predefined/operators/index.mjs.map +1 -0
  133. package/dist/core/predefined/operators/map-optional.d.mts +51 -0
  134. package/dist/core/predefined/operators/map-optional.d.mts.map +1 -0
  135. package/dist/core/predefined/operators/map-optional.mjs +55 -0
  136. package/dist/core/predefined/operators/map-optional.mjs.map +1 -0
  137. package/dist/core/predefined/operators/map-result-err.d.mts +51 -0
  138. package/dist/core/predefined/operators/map-result-err.d.mts.map +1 -0
  139. package/dist/core/predefined/operators/map-result-err.mjs +55 -0
  140. package/dist/core/predefined/operators/map-result-err.mjs.map +1 -0
  141. package/dist/core/predefined/operators/map-result-ok.d.mts +51 -0
  142. package/dist/core/predefined/operators/map-result-ok.d.mts.map +1 -0
  143. package/dist/core/predefined/operators/map-result-ok.mjs +55 -0
  144. package/dist/core/predefined/operators/map-result-ok.mjs.map +1 -0
  145. package/dist/core/predefined/operators/map-to.d.mts +43 -0
  146. package/dist/core/predefined/operators/map-to.d.mts.map +1 -0
  147. package/dist/core/predefined/operators/map-to.mjs +48 -0
  148. package/dist/core/predefined/operators/map-to.mjs.map +1 -0
  149. package/dist/core/predefined/operators/pluck.d.mts +47 -0
  150. package/dist/core/predefined/operators/pluck.d.mts.map +1 -0
  151. package/dist/core/predefined/operators/pluck.mjs +52 -0
  152. package/dist/core/predefined/operators/pluck.mjs.map +1 -0
  153. package/dist/core/predefined/operators/skip.d.mts +50 -0
  154. package/dist/core/predefined/operators/skip.d.mts.map +1 -0
  155. package/dist/core/predefined/operators/skip.mjs +56 -0
  156. package/dist/core/predefined/operators/skip.mjs.map +1 -0
  157. package/dist/core/predefined/operators/take.d.mts +44 -0
  158. package/dist/core/predefined/operators/take.d.mts.map +1 -0
  159. package/dist/core/predefined/operators/take.mjs +49 -0
  160. package/dist/core/predefined/operators/take.mjs.map +1 -0
  161. package/dist/core/predefined/operators/unwrap-optional.d.mts +44 -0
  162. package/dist/core/predefined/operators/unwrap-optional.d.mts.map +1 -0
  163. package/dist/core/predefined/operators/unwrap-optional.mjs +50 -0
  164. package/dist/core/predefined/operators/unwrap-optional.mjs.map +1 -0
  165. package/dist/core/predefined/operators/unwrap-result-err.d.mts +44 -0
  166. package/dist/core/predefined/operators/unwrap-result-err.d.mts.map +1 -0
  167. package/dist/core/predefined/operators/unwrap-result-err.mjs +48 -0
  168. package/dist/core/predefined/operators/unwrap-result-err.mjs.map +1 -0
  169. package/dist/core/predefined/operators/unwrap-result-ok.d.mts +44 -0
  170. package/dist/core/predefined/operators/unwrap-result-ok.d.mts.map +1 -0
  171. package/dist/core/predefined/operators/unwrap-result-ok.mjs +50 -0
  172. package/dist/core/predefined/operators/unwrap-result-ok.mjs.map +1 -0
  173. package/dist/core/types/id.d.mts +1 -1
  174. package/dist/core/types/id.d.mts.map +1 -1
  175. package/dist/core/types/index.d.mts +1 -0
  176. package/dist/core/types/index.d.mts.map +1 -1
  177. package/dist/core/types/observable-family.d.mts +8 -14
  178. package/dist/core/types/observable-family.d.mts.map +1 -1
  179. package/dist/core/types/observable.d.mts +3 -3
  180. package/dist/core/types/observable.d.mts.map +1 -1
  181. package/dist/core/types/timer.d.mts +2 -0
  182. package/dist/core/types/timer.d.mts.map +1 -0
  183. package/dist/core/types/timer.mjs +2 -0
  184. package/dist/core/types/timer.mjs.map +1 -0
  185. package/dist/core/utils/id-maker.d.mts +2 -2
  186. package/dist/core/utils/id-maker.d.mts.map +1 -1
  187. package/dist/core/utils/id-maker.mjs +3 -3
  188. package/dist/core/utils/id-maker.mjs.map +1 -1
  189. package/dist/core/utils/index.mjs +1 -1
  190. package/dist/entry-point.mjs +24 -15
  191. package/dist/entry-point.mjs.map +1 -1
  192. package/dist/globals.d.mts +0 -3
  193. package/dist/index.mjs +24 -15
  194. package/dist/index.mjs.map +1 -1
  195. package/dist/utils/collect-to-array.d.mts +3 -0
  196. package/dist/utils/collect-to-array.d.mts.map +1 -0
  197. package/dist/utils/collect-to-array.mjs +11 -0
  198. package/dist/utils/collect-to-array.mjs.map +1 -0
  199. package/dist/utils/create-boolean-state.d.mts +40 -0
  200. package/dist/utils/create-boolean-state.d.mts.map +1 -0
  201. package/dist/utils/create-boolean-state.mjs +53 -0
  202. package/dist/utils/create-boolean-state.mjs.map +1 -0
  203. package/dist/utils/create-event-emitter.d.mts +4 -4
  204. package/dist/utils/create-event-emitter.mjs +4 -4
  205. package/dist/utils/create-reducer.d.mts +10 -7
  206. package/dist/utils/create-reducer.d.mts.map +1 -1
  207. package/dist/utils/create-reducer.mjs +7 -7
  208. package/dist/utils/create-reducer.mjs.map +1 -1
  209. package/dist/utils/create-state.d.mts +8 -48
  210. package/dist/utils/create-state.d.mts.map +1 -1
  211. package/dist/utils/create-state.mjs +10 -60
  212. package/dist/utils/create-state.mjs.map +1 -1
  213. package/dist/utils/index.d.mts +2 -0
  214. package/dist/utils/index.d.mts.map +1 -1
  215. package/dist/utils/index.mjs +3 -1
  216. package/dist/utils/index.mjs.map +1 -1
  217. package/package.json +17 -11
  218. package/src/core/class/child-observable-class.mts +65 -9
  219. package/src/core/class/circular-dependency-comparison.test.mts +142 -0
  220. package/src/core/class/circular-dependency.test.mts +251 -0
  221. package/src/core/class/observable-base-class.mts +9 -9
  222. package/src/core/class/root-observable-class.mts +14 -10
  223. package/src/core/combine/combine.mts +15 -15
  224. package/src/core/combine/merge.mts +13 -14
  225. package/src/core/combine/zip.mts +26 -25
  226. package/src/core/create/{interval.mts → counter.mts} +32 -30
  227. package/src/core/create/from-abortable-promise.mts +83 -0
  228. package/src/core/create/from-promise.mts +10 -7
  229. package/src/core/create/from-subscribable.mts +4 -4
  230. package/src/core/create/index.mts +3 -3
  231. package/src/core/create/just.mts +43 -0
  232. package/src/core/create/source.mts +10 -14
  233. package/src/core/create/timer.mts +12 -11
  234. package/src/core/index.mts +1 -1
  235. package/src/core/operators/audit.mts +172 -0
  236. package/src/core/operators/debounce.mts +154 -0
  237. package/src/core/operators/filter.mts +9 -9
  238. package/src/core/operators/index.mts +4 -4
  239. package/src/core/operators/map.mts +124 -0
  240. package/src/core/operators/merge-map.mts +60 -33
  241. package/src/core/operators/pairwise.mts +10 -10
  242. package/src/core/operators/scan.mts +10 -10
  243. package/src/core/operators/skip-if-no-change.mts +26 -14
  244. package/src/core/operators/skip-until.mts +9 -9
  245. package/src/core/operators/skip-while.mts +30 -28
  246. package/src/core/operators/switch-map.mts +60 -29
  247. package/src/core/operators/take-until.mts +9 -9
  248. package/src/core/operators/take-while.mts +21 -19
  249. package/src/core/operators/{throttle-time.mts → throttle.mts} +58 -38
  250. package/src/core/operators/with-buffered-from.mts +18 -14
  251. package/src/core/operators/with-current-value-from.mts +19 -14
  252. package/src/core/operators/with-initial-value.mts +9 -9
  253. package/src/core/predefined/index.mts +1 -0
  254. package/src/core/predefined/operators/attach-index.mts +62 -0
  255. package/src/core/predefined/operators/index.mts +11 -0
  256. package/src/core/predefined/operators/map-optional.mts +55 -0
  257. package/src/core/predefined/operators/map-result-err.mts +55 -0
  258. package/src/core/predefined/operators/map-result-ok.mts +55 -0
  259. package/src/core/predefined/operators/map-to.mts +45 -0
  260. package/src/core/predefined/operators/pluck.mts +51 -0
  261. package/src/core/predefined/operators/skip.mts +57 -0
  262. package/src/core/predefined/operators/take.mts +47 -0
  263. package/src/core/predefined/operators/unwrap-optional.mts +49 -0
  264. package/src/core/predefined/operators/unwrap-result-err.mts +48 -0
  265. package/src/core/predefined/operators/unwrap-result-ok.mts +49 -0
  266. package/src/core/types/id.mts +1 -1
  267. package/src/core/types/index.mts +1 -0
  268. package/src/core/types/observable-family.mts +8 -24
  269. package/src/core/types/observable.mts +3 -3
  270. package/src/core/types/timer.mts +2 -0
  271. package/src/core/utils/id-maker.mts +4 -4
  272. package/src/globals.d.mts +0 -3
  273. package/src/utils/collect-to-array.mts +17 -0
  274. package/src/utils/create-boolean-state.mts +68 -0
  275. package/src/utils/create-event-emitter.mts +4 -4
  276. package/src/utils/create-reducer.mts +11 -8
  277. package/src/utils/create-state.mts +10 -75
  278. package/src/utils/index.mts +2 -0
  279. package/dist/core/create/from-array.d.mts +0 -39
  280. package/dist/core/create/from-array.d.mts.map +0 -1
  281. package/dist/core/create/from-array.mjs +0 -65
  282. package/dist/core/create/from-array.mjs.map +0 -1
  283. package/dist/core/create/interval.d.mts.map +0 -1
  284. package/dist/core/create/interval.mjs.map +0 -1
  285. package/dist/core/create/of.d.mts +0 -39
  286. package/dist/core/create/of.d.mts.map +0 -1
  287. package/dist/core/create/of.mjs +0 -63
  288. package/dist/core/create/of.mjs.map +0 -1
  289. package/dist/core/operators/audit-time.d.mts +0 -62
  290. package/dist/core/operators/audit-time.d.mts.map +0 -1
  291. package/dist/core/operators/audit-time.mjs +0 -109
  292. package/dist/core/operators/audit-time.mjs.map +0 -1
  293. package/dist/core/operators/debounce-time.d.mts +0 -51
  294. package/dist/core/operators/debounce-time.d.mts.map +0 -1
  295. package/dist/core/operators/debounce-time.mjs +0 -93
  296. package/dist/core/operators/debounce-time.mjs.map +0 -1
  297. package/dist/core/operators/map-with-index.d.mts +0 -54
  298. package/dist/core/operators/map-with-index.d.mts.map +0 -1
  299. package/dist/core/operators/map-with-index.mjs +0 -88
  300. package/dist/core/operators/map-with-index.mjs.map +0 -1
  301. package/dist/core/operators/throttle-time.d.mts +0 -62
  302. package/dist/core/operators/throttle-time.d.mts.map +0 -1
  303. package/dist/core/operators/throttle-time.mjs +0 -107
  304. package/dist/core/operators/throttle-time.mjs.map +0 -1
  305. package/src/core/create/from-array.mts +0 -76
  306. package/src/core/create/of.mts +0 -73
  307. package/src/core/operators/audit-time.mts +0 -136
  308. package/src/core/operators/debounce-time.mts +0 -116
  309. package/src/core/operators/map-with-index.mts +0 -183
@@ -0,0 +1,154 @@
1
+ import { Optional } from 'ts-data-forge';
2
+ import { AsyncChildObservableClass } from '../class/index.mjs';
3
+ import {
4
+ type DebounceOperatorObservable,
5
+ type KeepInitialValueOperator,
6
+ type Observable,
7
+ type TimerId,
8
+ type UpdateToken,
9
+ } from '../types/index.mjs';
10
+
11
+ /**
12
+ * Delays emissions from the source observable until a specified time has passed without another emission.
13
+ * Useful for handling user input events like typing or scrolling.
14
+ *
15
+ * @template A - The type of values from the source
16
+ * @param milliSeconds - The debounce duration in milliseconds
17
+ * @returns An operator that debounces the observable
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * // Timeline (250ms debounce):
22
+ * //
23
+ * // Time(x50ms) 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
24
+ * //
25
+ * // input$ 0 2 3 9 10 11 12 13 14
26
+ * // |- 250ms -> |- 250ms ->
27
+ * // debounced$ 3 14 (emitted after 250ms silence)
28
+ * //
29
+ * // Explanation:
30
+ * // - debounce emits the latest value AFTER a quiet period with no new emissions
31
+ * // - Unlike audit (which uses a fixed window), debounce resets the timer on every emission
32
+ * // - Useful for search-as-you-type and form validation
33
+ *
34
+ * const input$ = source<number>();
35
+ *
36
+ * const debounced$ = input$.pipe(debounce(250));
37
+ *
38
+ * const valueHistory: number[] = [];
39
+ *
40
+ * debounced$.subscribe((value) => {
41
+ * valueHistory.push(value);
42
+ * });
43
+ *
44
+ * const sleep = (ms: number): Promise<void> =>
45
+ * new Promise((resolve) => {
46
+ * setTimeout(resolve, ms);
47
+ * });
48
+ *
49
+ * input$.next(0);
50
+ *
51
+ * await sleep(200);
52
+ *
53
+ * input$.next(2);
54
+ *
55
+ * await sleep(100);
56
+ *
57
+ * input$.next(3);
58
+ *
59
+ * assert.deepStrictEqual(valueHistory, []);
60
+ *
61
+ * await sleep(300);
62
+ *
63
+ * assert.deepStrictEqual(valueHistory, [3]);
64
+ *
65
+ * await sleep(300);
66
+ *
67
+ * input$.next(9);
68
+ *
69
+ * await sleep(100);
70
+ *
71
+ * input$.next(10);
72
+ *
73
+ * await sleep(100);
74
+ *
75
+ * input$.next(11);
76
+ *
77
+ * await sleep(100);
78
+ *
79
+ * input$.next(12);
80
+ *
81
+ * await sleep(100);
82
+ *
83
+ * input$.next(13);
84
+ *
85
+ * await sleep(100);
86
+ *
87
+ * input$.next(14);
88
+ *
89
+ * assert.deepStrictEqual(valueHistory, [3]);
90
+ *
91
+ * await sleep(300);
92
+ *
93
+ * assert.deepStrictEqual(valueHistory, [3, 14]);
94
+ * ```
95
+ */
96
+ export const debounce = <A,>(
97
+ milliSeconds: number,
98
+ ): KeepInitialValueOperator<A, A> =>
99
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
100
+ ((parentObservable) =>
101
+ new DebounceObservableClass(
102
+ parentObservable,
103
+ milliSeconds,
104
+ )) as KeepInitialValueOperator<A, A>;
105
+
106
+ class DebounceObservableClass<A>
107
+ extends AsyncChildObservableClass<A, readonly [A]>
108
+ implements DebounceOperatorObservable<A>
109
+ {
110
+ readonly #milliSeconds: number;
111
+ #mut_timerId: TimerId | undefined;
112
+
113
+ constructor(parentObservable: Observable<A>, milliSeconds: number) {
114
+ super({
115
+ parents: [parentObservable],
116
+ initialValue: parentObservable.getSnapshot(),
117
+ });
118
+
119
+ this.#mut_timerId = undefined;
120
+
121
+ this.#milliSeconds = milliSeconds;
122
+ }
123
+
124
+ override tryUpdate(updateToken: UpdateToken): void {
125
+ const par = this.parents[0];
126
+
127
+ const sn = par.getSnapshot();
128
+
129
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
130
+ return; // skip update
131
+ }
132
+
133
+ this.#resetTimer();
134
+
135
+ // set timer
136
+ this.#mut_timerId = setTimeout(() => {
137
+ if (Optional.isNone(sn)) return;
138
+
139
+ this.startUpdate(sn.value);
140
+ }, this.#milliSeconds);
141
+ }
142
+
143
+ #resetTimer(): void {
144
+ if (this.#mut_timerId !== undefined) {
145
+ clearTimeout(this.#mut_timerId);
146
+ }
147
+ }
148
+
149
+ override complete(): void {
150
+ this.#resetTimer();
151
+
152
+ super.complete();
153
+ }
154
+ }
@@ -12,7 +12,7 @@ import {
12
12
  type FilterOperatorObservable,
13
13
  type InitializedObservable,
14
14
  type Observable,
15
- type UpdaterSymbol,
15
+ type UpdateToken,
16
16
  } from '../types/index.mjs';
17
17
  import { withInitialValue } from './with-initial-value.mjs';
18
18
 
@@ -40,29 +40,29 @@ import { withInitialValue } from './with-initial-value.mjs';
40
40
  *
41
41
  * const even$ = num$.pipe(filter((x) => x % 2 === 0));
42
42
  *
43
- * const mut_history: number[] = [];
43
+ * const valueHistory: number[] = [];
44
44
  *
45
45
  * even$.subscribe((x) => {
46
- * mut_history.push(x);
46
+ * valueHistory.push(x);
47
47
  * });
48
48
  *
49
49
  * num$.next(1); // nothing logged
50
50
  *
51
51
  * num$.next(2); // logs: 2
52
52
  *
53
- * assert.deepStrictEqual(mut_history, [2]);
53
+ * assert.deepStrictEqual(valueHistory, [2]);
54
54
  *
55
55
  * num$.next(3); // nothing logged
56
56
  *
57
57
  * num$.next(4); // logs: 4
58
58
  *
59
- * assert.deepStrictEqual(mut_history, [2, 4]);
59
+ * assert.deepStrictEqual(valueHistory, [2, 4]);
60
60
  *
61
61
  * num$.next(5);
62
62
  *
63
63
  * num$.next(6);
64
64
  *
65
- * assert.deepStrictEqual(mut_history, [2, 4, 6]);
65
+ * assert.deepStrictEqual(valueHistory, [2, 4, 6]);
66
66
  * ```
67
67
  */
68
68
  export function filter<A, B extends A>(
@@ -107,12 +107,12 @@ class FilterObservableClass<A>
107
107
  this.#predicate = predicate;
108
108
  }
109
109
 
110
- override tryUpdate(updaterSymbol: UpdaterSymbol): void {
110
+ override tryUpdate(updateToken: UpdateToken): void {
111
111
  const par = this.parents[0];
112
112
 
113
113
  const sn = par.getSnapshot();
114
114
 
115
- if (par.updaterSymbol !== updaterSymbol || Optional.isNone(sn)) {
115
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
116
116
  return; // skip update
117
117
  }
118
118
 
@@ -120,7 +120,7 @@ class FilterObservableClass<A>
120
120
  this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
121
121
 
122
122
  if (this.#predicate(sn.value, this.#mut_index)) {
123
- this.setNext(sn.value, updaterSymbol);
123
+ this.setNext(sn.value, updateToken);
124
124
  }
125
125
  }
126
126
  }
@@ -1,7 +1,7 @@
1
- export * from './audit-time.mjs';
2
- export * from './debounce-time.mjs';
1
+ export * from './audit.mjs';
2
+ export * from './debounce.mjs';
3
3
  export * from './filter.mjs';
4
- export * from './map-with-index.mjs';
4
+ export * from './map.mjs';
5
5
  export * from './merge-map.mjs';
6
6
  export * from './pairwise.mjs';
7
7
  export * from './scan.mjs';
@@ -11,7 +11,7 @@ export * from './skip-while.mjs';
11
11
  export * from './switch-map.mjs';
12
12
  export * from './take-until.mjs';
13
13
  export * from './take-while.mjs';
14
- export * from './throttle-time.mjs';
14
+ export * from './throttle.mjs';
15
15
  export * from './with-buffered-from.mjs';
16
16
  export * from './with-current-value-from.mjs';
17
17
  export * from './with-initial-value.mjs';
@@ -0,0 +1,124 @@
1
+ import { Optional, SafeUint, asSafeUint, expectType } from 'ts-data-forge';
2
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
+ import { source } from '../create/index.mjs';
4
+ import {
5
+ type InitializedObservable,
6
+ type KeepInitialValueOperator,
7
+ type MapOperatorObservable,
8
+ type Observable,
9
+ type UpdateToken,
10
+ } from '../types/index.mjs';
11
+ import { withInitialValue } from './with-initial-value.mjs';
12
+
13
+ /**
14
+ * Transforms each value emitted by the source using a mapping function that also receives the emission index.
15
+ *
16
+ * @template A - The type of values from the source
17
+ * @template B - The type of mapped values
18
+ * @param mapFn - A function that maps each value (receives value and index)
19
+ * @returns An operator that maps values with index
20
+ *
21
+ * @example
22
+ * ```ts
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>();
33
+ *
34
+ * const indexed$ = num$.pipe(map((x, i) => `${i}: ${x}`));
35
+ *
36
+ * const valueHistory: string[] = [];
37
+ *
38
+ * indexed$.subscribe((s) => {
39
+ * valueHistory.push(s);
40
+ * });
41
+ *
42
+ * num$.next('A'); // 0: A
43
+ *
44
+ * num$.next('B'); // 1: B
45
+ *
46
+ * num$.next('C'); // 2: C
47
+ *
48
+ * assert.deepStrictEqual(valueHistory, ['0: A', '1: B', '2: C']);
49
+ * ```
50
+ */
51
+ export const map = <A, B>(
52
+ mapFn: (x: A, index: SafeUint | -1) => B,
53
+ ): KeepInitialValueOperator<A, B> =>
54
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
55
+ ((parentObservable) =>
56
+ new MapObservableClass(
57
+ parentObservable,
58
+ mapFn,
59
+ )) as KeepInitialValueOperator<A, B>;
60
+
61
+ /* implementation */
62
+
63
+ class MapObservableClass<A, B>
64
+ extends SyncChildObservableClass<B, readonly [A]>
65
+ implements MapOperatorObservable<A, B>
66
+ {
67
+ readonly #mapFn: (x: A, index: SafeUint | -1) => B;
68
+ #mut_index: SafeUint | -1;
69
+
70
+ constructor(
71
+ parentObservable: Observable<A>,
72
+ mapFn: (x: A, index: SafeUint | -1) => B,
73
+ ) {
74
+ super({
75
+ parents: [parentObservable],
76
+ initialValue: Optional.map(parentObservable.getSnapshot(), (x) =>
77
+ mapFn(x, -1),
78
+ ),
79
+ });
80
+
81
+ this.#mut_index = -1;
82
+
83
+ this.#mapFn = mapFn;
84
+ }
85
+
86
+ override tryUpdate(updateToken: UpdateToken): void {
87
+ const par = this.parents[0];
88
+
89
+ const sn = par.getSnapshot();
90
+
91
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
92
+ return; // skip update
93
+ }
94
+
95
+ this.#mut_index =
96
+ this.#mut_index === -1 ? asSafeUint(0) : SafeUint.add(1, this.#mut_index);
97
+
98
+ this.setNext(this.#mapFn(sn.value, this.#mut_index), updateToken);
99
+ }
100
+ }
101
+
102
+ if (import.meta.vitest !== undefined) {
103
+ test('type test', () => {
104
+ expect(1).toBe(1); // dummy
105
+ });
106
+
107
+ {
108
+ const s: Observable<number> = source<number>();
109
+
110
+ const _d1 = s.pipe(map((x, i) => x + i));
111
+
112
+ expectType<typeof _d1, Observable<number>>('=');
113
+ }
114
+
115
+ {
116
+ const s = source<number>();
117
+
118
+ const m: InitializedObservable<number> = s.pipe(withInitialValue(0));
119
+
120
+ const _d = m.pipe(map((x, i) => x + i));
121
+
122
+ expectType<typeof _d, InitializedObservable<number>>('=');
123
+ }
124
+ }
@@ -5,7 +5,7 @@ import {
5
5
  type MergeMapOperatorObservable,
6
6
  type Observable,
7
7
  type Subscription,
8
- type UpdaterSymbol,
8
+ type UpdateToken,
9
9
  } from '../types/index.mjs';
10
10
 
11
11
  /**
@@ -21,56 +21,83 @@ import {
21
21
  * ```ts
22
22
  * // Timeline:
23
23
  * //
24
- * // ids$ 1 2 3
25
- * // requests fetch(1) fetch(2) fetch(3)
26
- * // users$ result1 result2 result3
27
- * // (parallel) (parallel) (parallel)
24
+ * // input$ A B C
25
+ * // inner A A1 A2 A3
26
+ * // inner B B1 B2 B3
27
+ * // inner C C1 C2 C3
28
+ * // result$ A1 A2 A3 B1 B2 C1 B3 C2 C3
28
29
  * //
29
30
  * // 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
31
+ * // - mergeMap creates an inner observable for each source value
32
+ * // - Unlike switchMap, previous inner observables are NOT cancelled
33
+ * // - B's inner continues even after C arrives (B3 is still emitted)
34
+ * // - All inner observables run concurrently and their results are merged
34
35
  *
35
- * const ids$ = source<number>();
36
+ * const input$ = source<string>();
36
37
  *
37
- * const users$ = ids$.pipe(
38
- * mergeMap((id) => {
39
- * const result$ = source<{ id: number }>();
38
+ * const result$ = input$.pipe(
39
+ * mergeMap((letter) => {
40
+ * const inner$ = source<string>();
40
41
  *
41
42
  * setTimeout(() => {
42
- * result$.next({ id });
43
- *
44
- * result$.complete();
43
+ * inner$.next(`${letter}1`);
45
44
  * }, 10);
46
45
  *
47
- * return result$;
46
+ * setTimeout(() => {
47
+ * inner$.next(`${letter}2`);
48
+ * }, 110);
49
+ *
50
+ * setTimeout(() => {
51
+ * inner$.next(`${letter}3`);
52
+ * }, 210);
53
+ *
54
+ * return inner$;
48
55
  * }),
49
56
  * );
50
57
  *
51
- * const mut_history: { id: number }[] = [];
58
+ * const valueHistory: string[] = [];
52
59
  *
53
- * users$.subscribe((value) => {
54
- * mut_history.push(value);
60
+ * result$.subscribe((value) => {
61
+ * valueHistory.push(value);
55
62
  * });
56
63
  *
57
- * ids$.next(1);
64
+ * const sleep = (ms: number): Promise<void> =>
65
+ * new Promise((resolve) => {
66
+ * setTimeout(resolve, ms);
67
+ * });
58
68
  *
59
- * ids$.next(2);
69
+ * // Emit A - inner emits A1, A2, A3 at 10ms, 110ms, 210ms
70
+ * input$.next('A');
60
71
  *
61
- * ids$.next(3);
72
+ * await sleep(250);
62
73
  *
63
- * await new Promise((resolve) => {
64
- * setTimeout(resolve, 200);
65
- * });
74
+ * assert.deepStrictEqual(valueHistory, ['A1', 'A2', 'A3']);
75
+ *
76
+ * // Emit B - inner starts emitting B1, B2 at 10ms, 110ms
77
+ * input$.next('B');
78
+ *
79
+ * await sleep(150);
66
80
  *
67
- * assert.deepStrictEqual(mut_history.length, 3);
81
+ * assert.deepStrictEqual(valueHistory, ['A1', 'A2', 'A3', 'B1', 'B2']);
68
82
  *
69
- * assert.isTrue(mut_history.some((u) => u.id === 1));
83
+ * // Emit C while B's inner is still running (B3 at 210ms not yet fired)
84
+ * // Unlike switchMap, B's inner is NOT cancelled
85
+ * input$.next('C');
70
86
  *
71
- * assert.isTrue(mut_history.some((u) => u.id === 2));
87
+ * await sleep(250);
72
88
  *
73
- * assert.isTrue(mut_history.some((u) => u.id === 3));
89
+ * // B3 appears between C1 and C2, showing the merge behavior
90
+ * assert.deepStrictEqual(valueHistory, [
91
+ * 'A1',
92
+ * 'A2',
93
+ * 'A3',
94
+ * 'B1',
95
+ * 'B2',
96
+ * 'C1',
97
+ * 'B3',
98
+ * 'C2',
99
+ * 'C3',
100
+ * ]);
74
101
  * ```
75
102
  *
76
103
  * @note To improve code readability, consider using `createState` instead of `mergeMap`,
@@ -84,7 +111,7 @@ export const mergeMap =
84
111
  new MergeMapObservableClass(parentObservable, mapToObservable);
85
112
 
86
113
  /**
87
- * Alias for `mergeMap()`.
114
+ * Alias for `mergeMap`.
88
115
  * @see mergeMap
89
116
  */
90
117
  export const flatMap = mergeMap;
@@ -113,12 +140,12 @@ class MergeMapObservableClass<A, B>
113
140
  this.#mut_subscriptions = [];
114
141
  }
115
142
 
116
- override tryUpdate(updaterSymbol: UpdaterSymbol): void {
143
+ override tryUpdate(updateToken: UpdateToken): void {
117
144
  const par = this.parents[0];
118
145
 
119
146
  const sn = par.getSnapshot();
120
147
 
121
- if (par.updaterSymbol !== updaterSymbol || Optional.isNone(sn)) {
148
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
122
149
  return; // skip update
123
150
  }
124
151
 
@@ -4,7 +4,7 @@ import {
4
4
  type DropInitialValueOperator,
5
5
  type Observable,
6
6
  type PairwiseOperatorObservable,
7
- type UpdaterSymbol,
7
+ type UpdateToken,
8
8
  } from '../types/index.mjs';
9
9
 
10
10
  /**
@@ -30,30 +30,30 @@ import {
30
30
  *
31
31
  * const pairs$ = num$.pipe(pairwise());
32
32
  *
33
- * const mut_history: (readonly [number, number])[] = [];
33
+ * const valueHistory: (readonly [number, number])[] = [];
34
34
  *
35
35
  * pairs$.subscribe(([prev, curr]) => {
36
- * mut_history.push([prev, curr]);
36
+ * valueHistory.push([prev, curr]);
37
37
  * });
38
38
  *
39
39
  * num$.next(1); // nothing logged
40
40
  *
41
- * assert.deepStrictEqual(mut_history, []);
41
+ * assert.deepStrictEqual(valueHistory, []);
42
42
  *
43
43
  * num$.next(2); // logs: 1, 2
44
44
  *
45
- * assert.deepStrictEqual(mut_history, [[1, 2]]);
45
+ * assert.deepStrictEqual(valueHistory, [[1, 2]]);
46
46
  *
47
47
  * num$.next(3); // logs: 2, 3
48
48
  *
49
- * assert.deepStrictEqual(mut_history, [
49
+ * assert.deepStrictEqual(valueHistory, [
50
50
  * [1, 2],
51
51
  * [2, 3],
52
52
  * ]);
53
53
  *
54
54
  * num$.next(4); // logs: 3, 4
55
55
  *
56
- * assert.deepStrictEqual(mut_history, [
56
+ * assert.deepStrictEqual(valueHistory, [
57
57
  * [1, 2],
58
58
  * [2, 3],
59
59
  * [3, 4],
@@ -83,12 +83,12 @@ class PairwiseObservableClass<A>
83
83
  this.#mut_previousValue = parentObservable.getSnapshot();
84
84
  }
85
85
 
86
- override tryUpdate(updaterSymbol: UpdaterSymbol): void {
86
+ override tryUpdate(updateToken: UpdateToken): void {
87
87
  const par = this.parents[0];
88
88
 
89
89
  const sn = par.getSnapshot();
90
90
 
91
- if (par.updaterSymbol !== updaterSymbol || Optional.isNone(sn)) {
91
+ if (par.updateToken !== updateToken || Optional.isNone(sn)) {
92
92
  return; // skip update
93
93
  }
94
94
 
@@ -100,7 +100,7 @@ class PairwiseObservableClass<A>
100
100
  this.#mut_previousValue = par.getSnapshot();
101
101
 
102
102
  if (cond) {
103
- this.setNext([prev.value, sn.value], updaterSymbol);
103
+ this.setNext([prev.value, sn.value], updateToken);
104
104
  }
105
105
  }
106
106
  }
@@ -3,7 +3,7 @@ import { InitializedSyncChildObservableClass } from '../class/index.mjs';
3
3
  import {
4
4
  type Observable,
5
5
  type ScanOperatorObservable,
6
- type UpdaterSymbol,
6
+ type UpdateToken,
7
7
  type WithInitialValueOperator,
8
8
  } from '../types/index.mjs';
9
9
 
@@ -35,25 +35,25 @@ import {
35
35
  *
36
36
  * const sum$ = num$.pipe(scan((acc, curr) => acc + curr, 0));
37
37
  *
38
- * const mut_history: number[] = [];
38
+ * const valueHistory: number[] = [];
39
39
  *
40
40
  * sum$.subscribe((x) => {
41
- * mut_history.push(x);
41
+ * valueHistory.push(x);
42
42
  * });
43
43
  *
44
- * assert.deepStrictEqual(mut_history, [0]);
44
+ * assert.deepStrictEqual(valueHistory, [0]);
45
45
  *
46
46
  * num$.next(1); // logs: 1
47
47
  *
48
- * assert.deepStrictEqual(mut_history, [0, 1]);
48
+ * assert.deepStrictEqual(valueHistory, [0, 1]);
49
49
  *
50
50
  * num$.next(2); // logs: 3
51
51
  *
52
- * assert.deepStrictEqual(mut_history, [0, 1, 3]);
52
+ * assert.deepStrictEqual(valueHistory, [0, 1, 3]);
53
53
  *
54
54
  * num$.next(3); // logs: 6
55
55
  *
56
- * assert.deepStrictEqual(mut_history, [0, 1, 3, 6]);
56
+ * assert.deepStrictEqual(valueHistory, [0, 1, 3, 6]);
57
57
  * ```
58
58
  */
59
59
  export const scan =
@@ -83,7 +83,7 @@ class ScanObservableClass<A, B>
83
83
  this.#reducer = reducer;
84
84
  }
85
85
 
86
- override tryUpdate(updaterSymbol: UpdaterSymbol): void {
86
+ override tryUpdate(updateToken: UpdateToken): void {
87
87
  const par = this.parents[0];
88
88
 
89
89
  const psn = par.getSnapshot();
@@ -91,13 +91,13 @@ class ScanObservableClass<A, B>
91
91
  const sn = this.getSnapshot();
92
92
 
93
93
  if (
94
- par.updaterSymbol !== updaterSymbol ||
94
+ par.updateToken !== updateToken ||
95
95
  Optional.isNone(psn) ||
96
96
  Optional.isNone(sn)
97
97
  ) {
98
98
  return; // skip update
99
99
  }
100
100
 
101
- this.setNext(this.#reducer(sn.value, psn.value), updaterSymbol);
101
+ this.setNext(this.#reducer(sn.value, psn.value), updateToken);
102
102
  }
103
103
  }