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,89 @@
1
+ import { expectType, Optional } from 'ts-data-forge';
2
+ import {
3
+ type AsyncChildObservable,
4
+ type ChildObservable,
5
+ type Observable,
6
+ type ObservableBase,
7
+ type RootObservable,
8
+ type SyncChildObservable,
9
+ } from '../types/index.mjs';
10
+ import {
11
+ AsyncChildObservableClass,
12
+ SyncChildObservableClass,
13
+ } from './child-observable-class.mjs';
14
+ import { RootObservableClass } from './root-observable-class.mjs';
15
+
16
+ /**
17
+ * Inheritance
18
+ *
19
+ * ```txt
20
+ * ObservableBase
21
+ * |
22
+ * +- SyncChildObservable \ \
23
+ * | +-- ChildObservable \
24
+ * +- AsyncChildObservable X +-- Observable
25
+ * | +-- ManagerObservable /
26
+ * +- RootObservableType / /
27
+ * ```
28
+ */
29
+
30
+ // type tests
31
+
32
+ expectType<Observable<number>, Observable<number>>('=');
33
+
34
+ expectType<Observable<string>, Observable<number>>('!<=');
35
+
36
+ // inheritance
37
+ expectType<ChildObservable<number>, ObservableBase<number>>('<=');
38
+
39
+ expectType<SyncChildObservable<number>, ChildObservable<number>>('<=');
40
+
41
+ expectType<AsyncChildObservable<number>, ChildObservable<number>>('<=');
42
+
43
+ expectType<RootObservable<number>, ObservableBase<number>>('<=');
44
+
45
+ expectType<SyncChildObservable<number>, ChildObservable<number>>('<=');
46
+
47
+ expectType<Observable<number>, ObservableBase<number>>('<=');
48
+
49
+ expectType<ObservableBase<number>, Observable<number>>('!<=');
50
+
51
+ expectType<ChildObservable<number>, Observable<number>>('<=');
52
+
53
+ // ObservableBase is covariant
54
+ expectType<ObservableBase<1>, ObservableBase<number>>('<=');
55
+
56
+ expectType<ObservableBase<number>, ObservableBase<1>>('!<=');
57
+
58
+ // Observable is covariant
59
+ expectType<Observable<1>, Observable<number>>('<=');
60
+
61
+ expectType<Observable<number>, Observable<1>>('!<=');
62
+
63
+ const root = new RootObservableClass({
64
+ initialValue: Optional.some(0),
65
+ });
66
+
67
+ expectType<typeof root, RootObservable<number>>('<=');
68
+
69
+ const syncChild = new SyncChildObservableClass({
70
+ parents: [root],
71
+ initialValue: Optional.some(0),
72
+ });
73
+
74
+ expectType<typeof syncChild, SyncChildObservable<number>>('<=');
75
+
76
+ const asyncChild = new AsyncChildObservableClass({
77
+ parents: [root],
78
+ initialValue: Optional.some(0),
79
+ });
80
+
81
+ expectType<typeof asyncChild, AsyncChildObservable<number>>('<=');
82
+
83
+ test('SyncChildObservableClass', () => {
84
+ expect(syncChild.depth).toBe(1);
85
+ });
86
+
87
+ test('AsyncChildObservableClass', () => {
88
+ expect(asyncChild.depth).toBe(1);
89
+ });
@@ -0,0 +1,68 @@
1
+ import { Arr, Optional } from 'ts-data-forge';
2
+ import {
3
+ isRootObservable,
4
+ type ChildObservable,
5
+ type ObservableId,
6
+ type RootObservable,
7
+ } from '../types/index.mjs';
8
+ import { binarySearch, issueUpdaterSymbol } from '../utils/index.mjs';
9
+ import { ObservableBaseClass } from './observable-base-class.mjs';
10
+
11
+ export class RootObservableClass<A>
12
+ extends ObservableBaseClass<A, 'root', 0>
13
+ implements RootObservable<A>
14
+ {
15
+ #mut_procedure: readonly ChildObservable<unknown>[];
16
+ protected readonly _descendantsIdSet: MutableSet<ObservableId>;
17
+
18
+ constructor({
19
+ initialValue,
20
+ }: Readonly<{
21
+ initialValue: ReturnType<RootObservable<A>['getSnapshot']>;
22
+ }>) {
23
+ super({
24
+ kind: 'root',
25
+ depth: 0,
26
+ initialValue,
27
+ });
28
+
29
+ this.#mut_procedure = [];
30
+
31
+ this._descendantsIdSet = new Set<ObservableId>();
32
+ }
33
+
34
+ addDescendant<B>(child: ChildObservable<B>): void {
35
+ if (this._descendantsIdSet.has(child.id)) return;
36
+
37
+ this._descendantsIdSet.add(child.id);
38
+
39
+ const insertPos = binarySearch(
40
+ this.#mut_procedure.map((a) => a.depth),
41
+ child.depth,
42
+ );
43
+
44
+ this.#mut_procedure = Arr.toInserted(this.#mut_procedure, insertPos, child);
45
+ }
46
+
47
+ startUpdate(nextValue: A): void {
48
+ const updaterSymbol = issueUpdaterSymbol();
49
+
50
+ this.setNext(nextValue, updaterSymbol);
51
+
52
+ for (const p of this.#mut_procedure) {
53
+ p.tryUpdate(updaterSymbol);
54
+ }
55
+ }
56
+ }
57
+
58
+ if (import.meta.vitest !== undefined) {
59
+ test('isRootObservable', () => {
60
+ assert.isTrue(
61
+ isRootObservable(
62
+ new RootObservableClass({
63
+ initialValue: Optional.some(0),
64
+ }),
65
+ ),
66
+ );
67
+ });
68
+ }
@@ -0,0 +1,144 @@
1
+ import { Arr, Optional, expectType } from 'ts-data-forge';
2
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
+ import { fromArray, source } from '../create/index.mjs';
4
+ import { withInitialValue } from '../operators/index.mjs';
5
+ import {
6
+ type CombineObservable,
7
+ type CombineObservableRefined,
8
+ type InitializedObservable,
9
+ type InitializedSyncChildObservable,
10
+ type NonEmptyUnknownList,
11
+ type Observable,
12
+ type SyncChildObservable,
13
+ type UpdaterSymbol,
14
+ type Wrap,
15
+ } from '../types/index.mjs';
16
+
17
+ /**
18
+ * Combines multiple observables into a single observable that emits an array of their latest values.
19
+ * Emits whenever any of the source observables emit, but only after all sources have emitted at least once.
20
+ *
21
+ * @template OS - Tuple type of source observables
22
+ * @param parents - Array of observables to combine
23
+ * @returns A combined observable emitting tuples of values
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const name$ = source<string>();
28
+ *
29
+ * const age$ = source<number>();
30
+ *
31
+ * const user$ = combine([name$, age$]);
32
+ *
33
+ * user$.subscribe(([name_, age]) => {
34
+ * console.log({ name: name_, age });
35
+ * });
36
+ *
37
+ * name$.next('Alice');
38
+ *
39
+ * age$.next(25); // logs: { name: 'Alice', age: 25 }
40
+ *
41
+ * name$.next('Bob'); // logs: { name: 'Bob', age: 25 }
42
+ * ```
43
+ */
44
+ export const combine = <const OS extends NonEmptyArray<Observable<unknown>>>(
45
+ parents: OS,
46
+ ): CombineObservableRefined<OS> =>
47
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
48
+ new CombineObservableClass(
49
+ parents,
50
+ ) as unknown as CombineObservableRefined<OS>;
51
+
52
+ /**
53
+ * Alias for `combine()`.
54
+ * @see combine
55
+ */
56
+ export const combineLatest = combine; // alias
57
+
58
+ class CombineObservableClass<const A extends NonEmptyUnknownList>
59
+ extends SyncChildObservableClass<A, A>
60
+ implements CombineObservable<A>
61
+ {
62
+ constructor(parents: Wrap<A>) {
63
+ const parentsValues = parents.map((p) => p.getSnapshot());
64
+
65
+ super({
66
+ parents,
67
+ initialValue: parentsValues.every(Optional.isSome)
68
+ ? Optional.some(
69
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
70
+ Arr.map(parentsValues, (c) => c.value) as A,
71
+ )
72
+ : Optional.none,
73
+ });
74
+ }
75
+
76
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
77
+ if (this.parents.every((o) => o.updaterSymbol !== updaterSymbol)) return; // all parents are skipped
78
+
79
+ const parentValues = this.parents.map((a) => a.getSnapshot());
80
+
81
+ if (parentValues.every(Optional.isSome)) {
82
+ const nextValue =
83
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
84
+ Arr.map(parentValues, (a) => a.value) as A;
85
+
86
+ this.setNext(nextValue, updaterSymbol);
87
+ }
88
+ }
89
+ }
90
+
91
+ {
92
+ {
93
+ const s1: Observable<1> = source<1>();
94
+
95
+ const s2: Observable<2> = source<2>();
96
+
97
+ const _d = combine([s1, s2]);
98
+
99
+ expectType<typeof _d, CombineObservable<readonly [1, 2]>>('=');
100
+
101
+ expectType<typeof _d, Observable<readonly [1, 2]>>('<=');
102
+ }
103
+
104
+ {
105
+ const s1: InitializedObservable<1> = source<1>().pipe(withInitialValue(1));
106
+
107
+ const s2: Observable<2> = source<2>();
108
+
109
+ const _d = combine([s1, s2]);
110
+
111
+ expectType<typeof _d, CombineObservable<readonly [1, 2]>>('=');
112
+
113
+ expectType<typeof _d, Observable<readonly [1, 2]>>('<=');
114
+ }
115
+
116
+ {
117
+ const s1: InitializedObservable<1> = source<1>().pipe(withInitialValue(1));
118
+
119
+ const s2: InitializedObservable<2> = source<2>().pipe(withInitialValue(2));
120
+
121
+ const _d = combine([s1, s2]);
122
+
123
+ // Returns InitializedObservable if all OS are InitializedObservable
124
+ expectType<typeof _d, InitializedObservable<readonly [1, 2]>>('<=');
125
+ }
126
+
127
+ const r1 = fromArray([1, 2, 3]);
128
+
129
+ const r2 = fromArray(['a', 'b', 'c']);
130
+
131
+ const _c = combine([r1, r2]);
132
+
133
+ const _ci = combine([
134
+ r1.pipe(withInitialValue(0)),
135
+ r2.pipe(withInitialValue(0)),
136
+ ]);
137
+
138
+ expectType<typeof _c, SyncChildObservable<readonly [number, string]>>('<=');
139
+
140
+ expectType<
141
+ typeof _ci,
142
+ InitializedSyncChildObservable<readonly [number, number | string]>
143
+ >('<=');
144
+ }
@@ -0,0 +1,3 @@
1
+ export * from './combine.mjs';
2
+ export * from './merge.mjs';
3
+ export * from './zip.mjs';
@@ -0,0 +1,84 @@
1
+ import { Optional, expectType } from 'ts-data-forge';
2
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
+ import { fromArray } from '../create/index.mjs';
4
+ import {
5
+ type MergeObservable,
6
+ type MergeObservableRefined,
7
+ type NonEmptyUnknownList,
8
+ type Observable,
9
+ type SyncChildObservable,
10
+ type UpdaterSymbol,
11
+ type Wrap,
12
+ } from '../types/index.mjs';
13
+
14
+ /**
15
+ * Merges multiple observables into a single observable that emits all values from all sources.
16
+ * Emits whenever any source observable emits a value.
17
+ *
18
+ * @template OS - Tuple type of source observables
19
+ * @param parents - Array of observables to merge
20
+ * @returns A merged observable emitting values from any source
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const clicks$ = source<MouseEvent>();
25
+ *
26
+ * const keys$ = source<KeyboardEvent>();
27
+ *
28
+ * const events$ = merge([clicks$, keys$]);
29
+ *
30
+ * events$.subscribe((event_) => {
31
+ * console.log(event_);
32
+ * });
33
+ * // Logs any mouse click or keyboard event
34
+ * ```
35
+ *
36
+ * @note To improve code readability, consider using `createState` instead of `merge`,
37
+ * subscribing to `parents` and calling `setState` within it.
38
+ */
39
+ export const merge = <const OS extends NonEmptyArray<Observable<unknown>>>(
40
+ parents: OS,
41
+ ): MergeObservableRefined<OS> =>
42
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
43
+ new MergeObservableClass(parents) as MergeObservableRefined<OS>;
44
+
45
+ class MergeObservableClass<const P extends NonEmptyUnknownList>
46
+ extends SyncChildObservableClass<ArrayElement<P>, P>
47
+ implements MergeObservable<P>
48
+ {
49
+ constructor(parents: Wrap<P>) {
50
+ super({
51
+ parents,
52
+ initialValue: Optional.none,
53
+ });
54
+ }
55
+
56
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
57
+ const parentToUse = this.parents.find(
58
+ (o) =>
59
+ o.updaterSymbol === updaterSymbol && Optional.isSome(o.getSnapshot()),
60
+ );
61
+
62
+ if (parentToUse === undefined) return;
63
+
64
+ const nextValue =
65
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
66
+ Optional.unwrap(parentToUse.getSnapshot()) as ArrayElement<P>;
67
+
68
+ this.setNext(nextValue, updaterSymbol);
69
+ }
70
+ }
71
+
72
+ if (import.meta.vitest !== undefined) {
73
+ test('type test', () => {
74
+ expect(1).toBe(1); // dummy
75
+ });
76
+
77
+ const r1 = fromArray([1, 2, 3]);
78
+
79
+ const r2 = fromArray(['a', 'b', 'c']);
80
+
81
+ const _m = merge([r1, r2] as const);
82
+
83
+ expectType<typeof _m, SyncChildObservable<number | string>>('<=');
84
+ }
@@ -0,0 +1,149 @@
1
+ import { Arr, Optional, createQueue, expectType } from 'ts-data-forge';
2
+ import { SyncChildObservableClass } from '../class/index.mjs';
3
+ import { fromArray, source } from '../create/index.mjs';
4
+ import { withInitialValue } from '../operators/index.mjs';
5
+ import {
6
+ type InitializedObservable,
7
+ type InitializedSyncChildObservable,
8
+ type NonEmptyUnknownList,
9
+ type Observable,
10
+ type SyncChildObservable,
11
+ type TupleToQueueTuple,
12
+ type UpdaterSymbol,
13
+ type Wrap,
14
+ type ZipObservable,
15
+ type ZipObservableRefined,
16
+ } from '../types/index.mjs';
17
+
18
+ /**
19
+ * Combines multiple observables by pairing their emissions by index.
20
+ * Waits for all sources to emit their nth value before emitting the nth tuple.
21
+ * Completes when any source completes.
22
+ *
23
+ * @template OS - Tuple type of source observables
24
+ * @param parents - Array of observables to zip
25
+ * @returns A zipped observable emitting tuples of values
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const letters$ = fromArray(['a', 'b', 'c']);
30
+ *
31
+ * const numbers$ = fromArray([1, 2, 3]);
32
+ *
33
+ * const zipped$ = zip([letters$, numbers$]);
34
+ *
35
+ * zipped$.subscribe(([letter, num]) => {
36
+ * console.log(letter, num);
37
+ * });
38
+ * // logs: a 1, b 2, c 3
39
+ * ```
40
+ */
41
+ export const zip = <const OS extends NonEmptyArray<Observable<unknown>>>(
42
+ parents: OS,
43
+ ): ZipObservableRefined<OS> =>
44
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
45
+ new ZipObservableClass(parents) as unknown as ZipObservableRefined<OS>;
46
+
47
+ class ZipObservableClass<const A extends NonEmptyUnknownList>
48
+ extends SyncChildObservableClass<A, A>
49
+ implements ZipObservable<A>
50
+ {
51
+ readonly #queues: TupleToQueueTuple<A>;
52
+
53
+ constructor(parents: Wrap<A>) {
54
+ const parentsValues = parents.map((p) => p.getSnapshot());
55
+
56
+ super({
57
+ parents,
58
+ initialValue: parentsValues.every(Optional.isSome)
59
+ ? Optional.some(
60
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
61
+ Arr.map(parentsValues, (c) => c.value) as A,
62
+ )
63
+ : Optional.none,
64
+ });
65
+
66
+ this.#queues = Arr.map(parents, () =>
67
+ createQueue(),
68
+ ) satisfies TupleToQueueTuple<A>;
69
+ }
70
+
71
+ override tryUpdate(updaterSymbol: UpdaterSymbol): void {
72
+ const queues = this.#queues;
73
+
74
+ for (const [index, par] of this.parents.entries()) {
75
+ const sn = par.getSnapshot();
76
+
77
+ if (par.updaterSymbol === updaterSymbol && Optional.isSome(sn)) {
78
+ queues[index]?.enqueue(sn.value);
79
+ }
80
+ }
81
+
82
+ if (queues.every((list) => !list.isEmpty)) {
83
+ const nextValue =
84
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
85
+ Arr.map(queues, (q) => Optional.unwrap(q.dequeue())) as A;
86
+
87
+ this.setNext(nextValue, updaterSymbol);
88
+ }
89
+ }
90
+ }
91
+
92
+ if (import.meta.vitest !== undefined) {
93
+ test('type test', () => {
94
+ expect(1).toBe(1); // dummy
95
+ });
96
+
97
+ {
98
+ const s1: Observable<1> = source<1>();
99
+
100
+ const s2: Observable<2> = source<2>();
101
+
102
+ const _d = zip([s1, s2]);
103
+
104
+ expectType<typeof _d, ZipObservable<readonly [1, 2]>>('=');
105
+
106
+ expectType<typeof _d, Observable<readonly [1, 2]>>('<=');
107
+ }
108
+
109
+ {
110
+ const s1: InitializedObservable<1> = source<1>().pipe(withInitialValue(1));
111
+
112
+ const s2: Observable<2> = source<2>();
113
+
114
+ const _d = zip([s1, s2]);
115
+
116
+ expectType<typeof _d, ZipObservable<readonly [1, 2]>>('=');
117
+
118
+ expectType<typeof _d, Observable<readonly [1, 2]>>('<=');
119
+ }
120
+
121
+ {
122
+ const s1: InitializedObservable<1> = source<1>().pipe(withInitialValue(1));
123
+
124
+ const s2: InitializedObservable<2> = source<2>().pipe(withInitialValue(2));
125
+
126
+ const _d = zip([s1, s2]);
127
+
128
+ // Returns InitializedObservable if all OS are InitializedObservable
129
+ expectType<typeof _d, InitializedObservable<readonly [1, 2]>>('<=');
130
+ }
131
+
132
+ const r1 = fromArray([1, 2, 3]);
133
+
134
+ const r2 = fromArray(['a', 'b', 'c']);
135
+
136
+ const _z = zip([r1, r2] as const);
137
+
138
+ const _zi = zip([
139
+ r1.pipe(withInitialValue(0)),
140
+ r2.pipe(withInitialValue('0')),
141
+ ] as const);
142
+
143
+ expectType<typeof _z, SyncChildObservable<readonly [number, string]>>('<=');
144
+
145
+ expectType<
146
+ typeof _zi,
147
+ InitializedSyncChildObservable<readonly [number, string]>
148
+ >('<=');
149
+ }
@@ -0,0 +1,58 @@
1
+ import { Optional } from 'ts-data-forge';
2
+ import { RootObservableClass } from '../class/index.mjs';
3
+ import { type FromArrayObservable } from '../types/index.mjs';
4
+
5
+ /**
6
+ * Creates an observable that emits all values from an array sequentially, then completes.
7
+ *
8
+ * @template A - The type of array elements
9
+ * @param values - The array of values to emit
10
+ * @param startManually - If true, waits for manual start (default: false)
11
+ * @returns An observable that emits array values
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const nums$ = fromArray([1, 2, 3]);
16
+ *
17
+ * nums$.subscribe((x) => {
18
+ * console.log(x);
19
+ * });
20
+ * // logs: 1, 2, 3
21
+ * ```
22
+ */
23
+ export const fromArray = <A,>(
24
+ values: readonly A[],
25
+ startManually: boolean = false,
26
+ ): FromArrayObservable<A> =>
27
+ new FromArrayObservableClass<A>(values, startManually);
28
+
29
+ class FromArrayObservableClass<A>
30
+ extends RootObservableClass<A>
31
+ implements FromArrayObservable<A>
32
+ {
33
+ readonly #values: readonly A[];
34
+
35
+ constructor(values: readonly A[], startManually: boolean = false) {
36
+ super({ initialValue: Optional.none });
37
+
38
+ this.#values = values;
39
+
40
+ if (!startManually) {
41
+ setTimeout(() => {
42
+ this.emit();
43
+ }, 0);
44
+ }
45
+ }
46
+
47
+ emit(): this {
48
+ if (this.isCompleted) return this;
49
+
50
+ for (const v of this.#values) {
51
+ this.startUpdate(v);
52
+ }
53
+
54
+ this.complete();
55
+
56
+ return this;
57
+ }
58
+ }
@@ -0,0 +1,58 @@
1
+ import { Optional, Result } from 'ts-data-forge';
2
+ import { RootObservableClass } from '../class/index.mjs';
3
+ import { type FromPromiseObservable } from '../types/index.mjs';
4
+
5
+ /**
6
+ * Creates an observable from a Promise.
7
+ * Emits Result.ok when the promise resolves, or Result.err when it rejects.
8
+ *
9
+ * @template A - The type of the resolved value
10
+ * @template E - The type of the error
11
+ * @param promise - The promise to convert to observable
12
+ * @returns An observable that emits the promise result
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const data$ = fromPromise(fetch('/api/data').then((r) => r.json()));
17
+ *
18
+ * data$.subscribe((result) => {
19
+ * if (Result.isOk(result)) {
20
+ * console.log('Data:', result.value);
21
+ * } else {
22
+ * console.error('Error:', result.value);
23
+ * }
24
+ * });
25
+ * ```
26
+ */
27
+ export const fromPromise = <A, E = unknown>(
28
+ promise: Readonly<Promise<A>>,
29
+ ): FromPromiseObservable<A, E> => new FromPromiseObservableClass(promise);
30
+
31
+ class FromPromiseObservableClass<A, E = unknown>
32
+ extends RootObservableClass<Result<A, E>>
33
+ implements FromPromiseObservable<A, E>
34
+ {
35
+ constructor(promise: Readonly<Promise<A>>) {
36
+ super({ initialValue: Optional.none });
37
+
38
+ promise
39
+ .then((value) => {
40
+ if (this.isCompleted) return;
41
+
42
+ this.startUpdate(Result.ok(value));
43
+ })
44
+ .catch((error: unknown) => {
45
+ if (this.isCompleted) return;
46
+
47
+ this.startUpdate(
48
+ Result.err(
49
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
50
+ error as E,
51
+ ),
52
+ );
53
+ })
54
+ .finally(() => {
55
+ this.complete();
56
+ });
57
+ }
58
+ }
@@ -0,0 +1,37 @@
1
+ import { Optional, Result } from 'ts-data-forge';
2
+ import { RootObservableClass } from '../class/index.mjs';
3
+ import {
4
+ type FromSubscribableObservable,
5
+ type Subscribable,
6
+ } from '../types/index.mjs';
7
+
8
+ export const fromSubscribable = <A, E = unknown>(
9
+ subscribable: Subscribable<A>,
10
+ ): FromSubscribableObservable<A, E> =>
11
+ new FromSubscribableObservableClass(subscribable);
12
+
13
+ class FromSubscribableObservableClass<A, E = unknown>
14
+ extends RootObservableClass<Result<A, E>>
15
+ implements FromSubscribableObservable<A, E>
16
+ {
17
+ constructor(subscribable: Subscribable<A>) {
18
+ super({ initialValue: Optional.none });
19
+
20
+ subscribable.subscribe(
21
+ (nextValue) => {
22
+ this.startUpdate(Result.ok(nextValue));
23
+ },
24
+ (error?: unknown) => {
25
+ this.startUpdate(
26
+ Result.err(
27
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
28
+ error as E,
29
+ ),
30
+ );
31
+ },
32
+ () => {
33
+ this.complete();
34
+ },
35
+ );
36
+ }
37
+ }
@@ -0,0 +1,7 @@
1
+ export * from './from-array.mjs';
2
+ export * from './from-promise.mjs';
3
+ export * from './from-subscribable.mjs';
4
+ export * from './interval.mjs';
5
+ export * from './of.mjs';
6
+ export * from './source.mjs';
7
+ export * from './timer.mjs';