synstate 0.1.2 → 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 (307) hide show
  1. package/README.md +199 -146
  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 +6 -6
  14. package/dist/core/combine/combine.mjs +11 -12
  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 +0 -1
  53. package/dist/core/index.d.mts.map +1 -1
  54. package/dist/core/index.mjs +8 -13
  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 +4 -4
  70. package/dist/core/operators/{map-with-index.d.mts → map.d.mts} +11 -11
  71. package/dist/core/operators/map.d.mts.map +1 -0
  72. package/dist/core/operators/{map-with-index.mjs → map.mjs} +17 -17
  73. package/dist/core/operators/map.mjs.map +1 -0
  74. package/dist/core/operators/merge-map.d.mts +56 -29
  75. package/dist/core/operators/merge-map.d.mts.map +1 -1
  76. package/dist/core/operators/merge-map.mjs +58 -31
  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 +20 -8
  85. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  86. package/dist/core/operators/skip-if-no-change.mjs +23 -11
  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 -8
  92. package/dist/core/operators/skip-while.d.mts.map +1 -1
  93. package/dist/core/operators/skip-while.mjs +26 -11
  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 -7
  103. package/dist/core/operators/take-while.d.mts.map +1 -1
  104. package/dist/core/operators/take-while.mjs +18 -10
  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 +9 -9
  111. package/dist/core/operators/with-buffered-from.mjs +12 -12
  112. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  113. package/dist/core/operators/with-current-value-from.d.mts +10 -9
  114. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  115. package/dist/core/operators/with-current-value-from.mjs +13 -12
  116. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  117. package/dist/core/operators/with-initial-value.d.mts +5 -5
  118. package/dist/core/operators/with-initial-value.mjs +8 -8
  119. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  120. package/dist/core/predefined/index.mjs +0 -1
  121. package/dist/core/predefined/index.mjs.map +1 -1
  122. package/dist/core/predefined/operators/attach-index.d.mts +49 -0
  123. package/dist/core/predefined/operators/attach-index.d.mts.map +1 -1
  124. package/dist/core/predefined/operators/attach-index.mjs +51 -2
  125. package/dist/core/predefined/operators/attach-index.mjs.map +1 -1
  126. package/dist/core/predefined/operators/index.d.mts +0 -1
  127. package/dist/core/predefined/operators/index.d.mts.map +1 -1
  128. package/dist/core/predefined/operators/index.mjs +0 -1
  129. package/dist/core/predefined/operators/index.mjs.map +1 -1
  130. package/dist/core/predefined/operators/map-optional.d.mts +47 -0
  131. package/dist/core/predefined/operators/map-optional.d.mts.map +1 -1
  132. package/dist/core/predefined/operators/map-optional.mjs +49 -1
  133. package/dist/core/predefined/operators/map-optional.mjs.map +1 -1
  134. package/dist/core/predefined/operators/map-result-err.d.mts +47 -0
  135. package/dist/core/predefined/operators/map-result-err.d.mts.map +1 -1
  136. package/dist/core/predefined/operators/map-result-err.mjs +49 -1
  137. package/dist/core/predefined/operators/map-result-err.mjs.map +1 -1
  138. package/dist/core/predefined/operators/map-result-ok.d.mts +47 -0
  139. package/dist/core/predefined/operators/map-result-ok.d.mts.map +1 -1
  140. package/dist/core/predefined/operators/map-result-ok.mjs +49 -1
  141. package/dist/core/predefined/operators/map-result-ok.mjs.map +1 -1
  142. package/dist/core/predefined/operators/map-to.d.mts +40 -0
  143. package/dist/core/predefined/operators/map-to.d.mts.map +1 -1
  144. package/dist/core/predefined/operators/map-to.mjs +43 -1
  145. package/dist/core/predefined/operators/map-to.mjs.map +1 -1
  146. package/dist/core/predefined/operators/pluck.d.mts +39 -0
  147. package/dist/core/predefined/operators/pluck.d.mts.map +1 -1
  148. package/dist/core/predefined/operators/pluck.mjs +42 -1
  149. package/dist/core/predefined/operators/pluck.mjs.map +1 -1
  150. package/dist/core/predefined/operators/skip.d.mts +47 -0
  151. package/dist/core/predefined/operators/skip.d.mts.map +1 -1
  152. package/dist/core/predefined/operators/skip.mjs +47 -0
  153. package/dist/core/predefined/operators/skip.mjs.map +1 -1
  154. package/dist/core/predefined/operators/take.d.mts +41 -0
  155. package/dist/core/predefined/operators/take.d.mts.map +1 -1
  156. package/dist/core/predefined/operators/take.mjs +41 -0
  157. package/dist/core/predefined/operators/take.mjs.map +1 -1
  158. package/dist/core/predefined/operators/unwrap-optional.d.mts +40 -0
  159. package/dist/core/predefined/operators/unwrap-optional.d.mts.map +1 -1
  160. package/dist/core/predefined/operators/unwrap-optional.mjs +42 -1
  161. package/dist/core/predefined/operators/unwrap-optional.mjs.map +1 -1
  162. package/dist/core/predefined/operators/unwrap-result-err.d.mts +40 -0
  163. package/dist/core/predefined/operators/unwrap-result-err.d.mts.map +1 -1
  164. package/dist/core/predefined/operators/unwrap-result-err.mjs +42 -1
  165. package/dist/core/predefined/operators/unwrap-result-err.mjs.map +1 -1
  166. package/dist/core/predefined/operators/unwrap-result-ok.d.mts +40 -0
  167. package/dist/core/predefined/operators/unwrap-result-ok.d.mts.map +1 -1
  168. package/dist/core/predefined/operators/unwrap-result-ok.mjs +42 -1
  169. package/dist/core/predefined/operators/unwrap-result-ok.mjs.map +1 -1
  170. package/dist/core/types/id.d.mts +1 -1
  171. package/dist/core/types/id.d.mts.map +1 -1
  172. package/dist/core/types/index.d.mts +1 -0
  173. package/dist/core/types/index.d.mts.map +1 -1
  174. package/dist/core/types/observable-family.d.mts +8 -14
  175. package/dist/core/types/observable-family.d.mts.map +1 -1
  176. package/dist/core/types/observable.d.mts +3 -3
  177. package/dist/core/types/observable.d.mts.map +1 -1
  178. package/dist/core/types/timer.d.mts +2 -0
  179. package/dist/core/types/timer.d.mts.map +1 -0
  180. package/dist/core/types/timer.mjs +2 -0
  181. package/dist/core/types/timer.mjs.map +1 -0
  182. package/dist/core/utils/id-maker.d.mts +2 -2
  183. package/dist/core/utils/id-maker.d.mts.map +1 -1
  184. package/dist/core/utils/id-maker.mjs +3 -3
  185. package/dist/core/utils/id-maker.mjs.map +1 -1
  186. package/dist/core/utils/index.mjs +1 -1
  187. package/dist/entry-point.mjs +11 -14
  188. package/dist/entry-point.mjs.map +1 -1
  189. package/dist/globals.d.mts +0 -3
  190. package/dist/index.mjs +11 -14
  191. package/dist/index.mjs.map +1 -1
  192. package/dist/utils/collect-to-array.d.mts +3 -0
  193. package/dist/utils/collect-to-array.d.mts.map +1 -0
  194. package/dist/utils/collect-to-array.mjs +11 -0
  195. package/dist/utils/collect-to-array.mjs.map +1 -0
  196. package/dist/utils/create-boolean-state.d.mts +40 -0
  197. package/dist/utils/create-boolean-state.d.mts.map +1 -0
  198. package/dist/utils/create-boolean-state.mjs +53 -0
  199. package/dist/utils/create-boolean-state.mjs.map +1 -0
  200. package/dist/utils/create-event-emitter.d.mts +4 -4
  201. package/dist/utils/create-event-emitter.mjs +4 -4
  202. package/dist/utils/create-reducer.d.mts +10 -7
  203. package/dist/utils/create-reducer.d.mts.map +1 -1
  204. package/dist/utils/create-reducer.mjs +7 -7
  205. package/dist/utils/create-reducer.mjs.map +1 -1
  206. package/dist/utils/create-state.d.mts +8 -48
  207. package/dist/utils/create-state.d.mts.map +1 -1
  208. package/dist/utils/create-state.mjs +10 -60
  209. package/dist/utils/create-state.mjs.map +1 -1
  210. package/dist/utils/index.d.mts +2 -0
  211. package/dist/utils/index.d.mts.map +1 -1
  212. package/dist/utils/index.mjs +3 -1
  213. package/dist/utils/index.mjs.map +1 -1
  214. package/package.json +17 -11
  215. package/src/core/class/child-observable-class.mts +65 -9
  216. package/src/core/class/circular-dependency-comparison.test.mts +142 -0
  217. package/src/core/class/circular-dependency.test.mts +251 -0
  218. package/src/core/class/observable-base-class.mts +9 -9
  219. package/src/core/class/root-observable-class.mts +14 -10
  220. package/src/core/combine/combine.mts +13 -13
  221. package/src/core/combine/merge.mts +13 -14
  222. package/src/core/combine/zip.mts +26 -25
  223. package/src/core/create/{interval.mts → counter.mts} +32 -30
  224. package/src/core/create/from-abortable-promise.mts +83 -0
  225. package/src/core/create/from-promise.mts +10 -7
  226. package/src/core/create/from-subscribable.mts +4 -4
  227. package/src/core/create/index.mts +3 -3
  228. package/src/core/create/just.mts +43 -0
  229. package/src/core/create/source.mts +10 -14
  230. package/src/core/create/timer.mts +12 -11
  231. package/src/core/index.mts +0 -1
  232. package/src/core/operators/audit.mts +172 -0
  233. package/src/core/operators/debounce.mts +154 -0
  234. package/src/core/operators/filter.mts +9 -9
  235. package/src/core/operators/index.mts +4 -4
  236. package/src/core/operators/{map-with-index.mts → map.mts} +20 -20
  237. package/src/core/operators/merge-map.mts +59 -32
  238. package/src/core/operators/pairwise.mts +10 -10
  239. package/src/core/operators/scan.mts +10 -10
  240. package/src/core/operators/skip-if-no-change.mts +24 -12
  241. package/src/core/operators/skip-until.mts +9 -9
  242. package/src/core/operators/skip-while.mts +29 -12
  243. package/src/core/operators/switch-map.mts +60 -29
  244. package/src/core/operators/take-until.mts +9 -9
  245. package/src/core/operators/take-while.mts +19 -11
  246. package/src/core/operators/{throttle-time.mts → throttle.mts} +58 -38
  247. package/src/core/operators/with-buffered-from.mts +13 -13
  248. package/src/core/operators/with-current-value-from.mts +14 -13
  249. package/src/core/operators/with-initial-value.mts +9 -9
  250. package/src/core/predefined/operators/attach-index.mts +51 -2
  251. package/src/core/predefined/operators/index.mts +0 -1
  252. package/src/core/predefined/operators/map-optional.mts +48 -1
  253. package/src/core/predefined/operators/map-result-err.mts +48 -1
  254. package/src/core/predefined/operators/map-result-ok.mts +48 -1
  255. package/src/core/predefined/operators/map-to.mts +41 -1
  256. package/src/core/predefined/operators/pluck.mts +40 -1
  257. package/src/core/predefined/operators/skip.mts +47 -0
  258. package/src/core/predefined/operators/take.mts +41 -0
  259. package/src/core/predefined/operators/unwrap-optional.mts +41 -1
  260. package/src/core/predefined/operators/unwrap-result-err.mts +41 -1
  261. package/src/core/predefined/operators/unwrap-result-ok.mts +41 -1
  262. package/src/core/types/id.mts +1 -1
  263. package/src/core/types/index.mts +1 -0
  264. package/src/core/types/observable-family.mts +8 -24
  265. package/src/core/types/observable.mts +3 -3
  266. package/src/core/types/timer.mts +2 -0
  267. package/src/core/utils/id-maker.mts +4 -4
  268. package/src/globals.d.mts +0 -3
  269. package/src/utils/collect-to-array.mts +17 -0
  270. package/src/utils/create-boolean-state.mts +68 -0
  271. package/src/utils/create-event-emitter.mts +4 -4
  272. package/src/utils/create-reducer.mts +11 -8
  273. package/src/utils/create-state.mts +10 -75
  274. package/src/utils/index.mts +2 -0
  275. package/dist/core/create/from-array.d.mts +0 -39
  276. package/dist/core/create/from-array.d.mts.map +0 -1
  277. package/dist/core/create/from-array.mjs +0 -65
  278. package/dist/core/create/from-array.mjs.map +0 -1
  279. package/dist/core/create/interval.d.mts.map +0 -1
  280. package/dist/core/create/interval.mjs.map +0 -1
  281. package/dist/core/create/of.d.mts +0 -39
  282. package/dist/core/create/of.d.mts.map +0 -1
  283. package/dist/core/create/of.mjs +0 -63
  284. package/dist/core/create/of.mjs.map +0 -1
  285. package/dist/core/operators/audit-time.d.mts +0 -62
  286. package/dist/core/operators/audit-time.d.mts.map +0 -1
  287. package/dist/core/operators/audit-time.mjs +0 -109
  288. package/dist/core/operators/audit-time.mjs.map +0 -1
  289. package/dist/core/operators/debounce-time.d.mts +0 -51
  290. package/dist/core/operators/debounce-time.d.mts.map +0 -1
  291. package/dist/core/operators/debounce-time.mjs +0 -93
  292. package/dist/core/operators/debounce-time.mjs.map +0 -1
  293. package/dist/core/operators/map-with-index.d.mts.map +0 -1
  294. package/dist/core/operators/map-with-index.mjs.map +0 -1
  295. package/dist/core/operators/throttle-time.d.mts +0 -62
  296. package/dist/core/operators/throttle-time.d.mts.map +0 -1
  297. package/dist/core/operators/throttle-time.mjs +0 -107
  298. package/dist/core/operators/throttle-time.mjs.map +0 -1
  299. package/dist/core/predefined/operators/map.d.mts +0 -3
  300. package/dist/core/predefined/operators/map.d.mts.map +0 -1
  301. package/dist/core/predefined/operators/map.mjs +0 -8
  302. package/dist/core/predefined/operators/map.mjs.map +0 -1
  303. package/src/core/create/from-array.mts +0 -76
  304. package/src/core/create/of.mts +0 -73
  305. package/src/core/operators/audit-time.mts +0 -136
  306. package/src/core/operators/debounce-time.mts +0 -116
  307. package/src/core/predefined/operators/map.mts +0 -5
@@ -7,7 +7,7 @@ import { type InitializedObservable } from '../core/index.mjs';
7
7
  * @template A - The type of actions
8
8
  * @param reducer - A pure function that takes current state and action, returns new state
9
9
  * @param initialState - The initial value of the state
10
- * @returns An object containing the state observable, dispatch function, and snapshot getter
10
+ * @returns A 3-element tuple: `[state, dispatch, { getSnapshot, initialState }]`
11
11
  *
12
12
  * @example
13
13
  * ```ts
@@ -23,24 +23,27 @@ import { type InitializedObservable } from '../core/index.mjs';
23
23
  * 0,
24
24
  * );
25
25
  *
26
- * const mut_history: number[] = [];
26
+ * const stateHistory: number[] = [];
27
27
  *
28
28
  * state.subscribe((value: number) => {
29
- * mut_history.push(value);
29
+ * stateHistory.push(value);
30
30
  * });
31
31
  *
32
- * assert.deepStrictEqual(mut_history, [0]);
32
+ * assert.deepStrictEqual(stateHistory, [0]);
33
33
  *
34
34
  * dispatch({ type: 'increment' }); // logs: 1
35
35
  *
36
- * assert.deepStrictEqual(mut_history, [0, 1]);
36
+ * assert.deepStrictEqual(stateHistory, [0, 1]);
37
37
  *
38
38
  * dispatch({ type: 'increment' });
39
39
  *
40
40
  * dispatch({ type: 'decrement' });
41
41
  *
42
- * assert.deepStrictEqual(mut_history, [0, 1, 2, 1]);
42
+ * assert.deepStrictEqual(stateHistory, [0, 1, 2, 1]);
43
43
  * ```
44
44
  */
45
- export declare const createReducer: <S, A>(reducer: Reducer<S, A>, initialState: S) => readonly [state: InitializedObservable<S>, dispatch: (action: A) => S, getSnapshot: () => S];
45
+ export declare const createReducer: <S, A>(reducer: Reducer<S, A>, initialState: S) => readonly [state: InitializedObservable<S>, dispatch: (action: A) => S, Readonly<{
46
+ getSnapshot: () => S;
47
+ initialState: S;
48
+ }>];
46
49
  //# sourceMappingURL=create-reducer.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-reducer.d.mts","sourceRoot":"","sources":["../../src/utils/create-reducer.mts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,EAChC,SAAS,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,EACtB,cAAc,CAAC,KACd,SAAS,CACV,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAC1B,WAAW,EAAE,MAAM,CAAC,CAerB,CAAC"}
1
+ {"version":3,"file":"create-reducer.d.mts","sourceRoot":"","sources":["../../src/utils/create-reducer.mts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,EAChC,SAAS,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,EACtB,cAAc,CAAC,KACd,SAAS,CACV,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAC1B,QAAQ,CAAC;IACP,WAAW,EAAE,MAAM,CAAC,CAAC;IACrB,YAAY,EAAE,CAAC,CAAC;CACjB,CAAC,CAeH,CAAC"}
@@ -11,7 +11,7 @@ import { source } from '../core/create/source.mjs';
11
11
  * @template A - The type of actions
12
12
  * @param reducer - A pure function that takes current state and action, returns new state
13
13
  * @param initialState - The initial value of the state
14
- * @returns An object containing the state observable, dispatch function, and snapshot getter
14
+ * @returns A 3-element tuple: `[state, dispatch, { getSnapshot, initialState }]`
15
15
  *
16
16
  * @example
17
17
  * ```ts
@@ -27,23 +27,23 @@ import { source } from '../core/create/source.mjs';
27
27
  * 0,
28
28
  * );
29
29
  *
30
- * const mut_history: number[] = [];
30
+ * const stateHistory: number[] = [];
31
31
  *
32
32
  * state.subscribe((value: number) => {
33
- * mut_history.push(value);
33
+ * stateHistory.push(value);
34
34
  * });
35
35
  *
36
- * assert.deepStrictEqual(mut_history, [0]);
36
+ * assert.deepStrictEqual(stateHistory, [0]);
37
37
  *
38
38
  * dispatch({ type: 'increment' }); // logs: 1
39
39
  *
40
- * assert.deepStrictEqual(mut_history, [0, 1]);
40
+ * assert.deepStrictEqual(stateHistory, [0, 1]);
41
41
  *
42
42
  * dispatch({ type: 'increment' });
43
43
  *
44
44
  * dispatch({ type: 'decrement' });
45
45
  *
46
- * assert.deepStrictEqual(mut_history, [0, 1, 2, 1]);
46
+ * assert.deepStrictEqual(stateHistory, [0, 1, 2, 1]);
47
47
  * ```
48
48
  */
49
49
  const createReducer = (reducer, initialState) => {
@@ -54,7 +54,7 @@ const createReducer = (reducer, initialState) => {
54
54
  return nextState;
55
55
  };
56
56
  const getSnapshot = () => state.getSnapshot().value;
57
- return [state, dispatch, getSnapshot];
57
+ return [state, dispatch, { getSnapshot, initialState }];
58
58
  };
59
59
 
60
60
  export { createReducer };
@@ -1 +1 @@
1
- {"version":3,"file":"create-reducer.mjs","sources":["../../src/utils/create-reducer.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CG;MACU,aAAa,GAAG,CAC3B,OAAsB,EACtB,YAAe,KAKb;AACF,IAAA,MAAM,KAAK,GAAG,MAAM,CAAI,YAAY,CAAC;AAErC,IAAA,MAAM,QAAQ,GAAG,CAAC,MAAS,KAAO;AAChC,QAAA,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;AAE5D,QAAA,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;AAErB,QAAA,OAAO,SAAS;AAClB,IAAA,CAAC;IAED,MAAM,WAAW,GAAG,MAAS,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK;AAEtD,IAAA,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAU;AAChD;;;;"}
1
+ {"version":3,"file":"create-reducer.mjs","sources":["../../src/utils/create-reducer.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CG;MACU,aAAa,GAAG,CAC3B,OAAsB,EACtB,YAAe,KAQb;AACF,IAAA,MAAM,KAAK,GAAG,MAAM,CAAI,YAAY,CAAC;AAErC,IAAA,MAAM,QAAQ,GAAG,CAAC,MAAS,KAAO;AAChC,QAAA,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;AAE5D,QAAA,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;AAErB,QAAA,OAAO,SAAS;AAClB,IAAA,CAAC;IAED,MAAM,WAAW,GAAG,MAAS,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK;IAEtD,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,YAAY,EAAE,CAAU;AAClE;;;;"}
@@ -5,77 +5,37 @@ import { type InitializedObservable } from '../core/index.mjs';
5
5
  *
6
6
  * @template S - The type of the state
7
7
  * @param initialState - The initial value of the state
8
- * @returns An object containing the state observable and methods to manipulate it
8
+ * @returns A 3-element tuple: `[state, setState, { updateState, resetState, getSnapshot, initialState }]`
9
9
  *
10
10
  * @example
11
11
  * ```ts
12
12
  * const [state, setState, { updateState, resetState }] = createState(0);
13
13
  *
14
- * const mut_history: number[] = [];
14
+ * const stateHistory: number[] = [];
15
15
  *
16
16
  * state.subscribe((value: number) => {
17
- * mut_history.push(value);
17
+ * stateHistory.push(value);
18
18
  * });
19
19
  *
20
- * assert.deepStrictEqual(mut_history, [0]);
20
+ * assert.deepStrictEqual(stateHistory, [0]);
21
21
  *
22
22
  * setState(10); // logs: 10
23
23
  *
24
- * assert.deepStrictEqual(mut_history, [0, 10]);
24
+ * assert.deepStrictEqual(stateHistory, [0, 10]);
25
25
  *
26
26
  * updateState((prev: number) => prev + 1); // logs: 11
27
27
  *
28
- * assert.deepStrictEqual(mut_history, [0, 10, 11]);
28
+ * assert.deepStrictEqual(stateHistory, [0, 10, 11]);
29
29
  *
30
30
  * resetState(); // logs: 0
31
31
  *
32
- * assert.deepStrictEqual(mut_history, [0, 10, 11, 0]);
32
+ * assert.deepStrictEqual(stateHistory, [0, 10, 11, 0]);
33
33
  * ```
34
34
  */
35
35
  export declare const createState: <S>(initialState: S) => readonly [state: InitializedObservable<S>, setState: (v: S) => S, Readonly<{
36
36
  updateState: (updateFn: (prev: S) => S) => S;
37
37
  resetState: () => S;
38
38
  getSnapshot: () => S;
39
- }>];
40
- /**
41
- * Creates a reactive boolean state with convenient methods for boolean operations.
42
- * Extends `createState` with boolean-specific helpers like `toggle`, `setTrue`, and `setFalse`.
43
- *
44
- * @param initialState - The initial boolean value
45
- * @returns An object with the state observable and boolean-specific methods
46
- *
47
- * @example
48
- * ```ts
49
- * const [state, { setTrue, toggle }] = createBooleanState(false);
50
- *
51
- * const mut_history: boolean[] = [];
52
- *
53
- * state.subscribe((value: boolean) => {
54
- * mut_history.push(value);
55
- * });
56
- *
57
- * assert.deepStrictEqual(mut_history, [false]);
58
- *
59
- * setTrue(); // logs: true
60
- *
61
- * assert.deepStrictEqual(mut_history, [false, true]);
62
- *
63
- * toggle(); // logs: false
64
- *
65
- * assert.deepStrictEqual(mut_history, [false, true, false]);
66
- *
67
- * toggle(); // logs: true
68
- *
69
- * assert.deepStrictEqual(mut_history, [false, true, false, true]);
70
- * ```
71
- */
72
- export declare const createBooleanState: (initialState: boolean) => readonly [state: InitializedObservable<boolean>, Readonly<{
73
- setTrue: () => void;
74
- setFalse: () => void;
75
- setState: (next: boolean) => boolean;
76
- toggle: () => boolean;
77
- updateState: (updateFn: (prev: boolean) => boolean) => boolean;
78
- resetState: () => boolean;
79
- getSnapshot: () => boolean;
39
+ initialState: S;
80
40
  }>];
81
41
  //# sourceMappingURL=create-state.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-state.d.mts","sourceRoot":"","sources":["../../src/utils/create-state.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAwB/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAC3B,cAAc,CAAC,KACd,SAAS,CACV,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EACrB,QAAQ,CAAC;IACP,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,CAAC;CACtB,CAAC,CAwBH,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,kBAAkB,GAC7B,cAAc,OAAO,KACpB,SAAS,CACV,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,EACrC,QAAQ,CAAC;IACP,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;IACrC,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,KAAK,OAAO,CAAC;IAC/D,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,WAAW,EAAE,MAAM,OAAO,CAAC;CAC5B,CAAC,CAqBH,CAAC"}
1
+ {"version":3,"file":"create-state.d.mts","sourceRoot":"","sources":["../../src/utils/create-state.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAwB/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAC3B,cAAc,CAAC,KACd,SAAS,CACV,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EACrB,QAAQ,CAAC;IACP,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,CAAC;IACrB,YAAY,EAAE,CAAC,CAAC;CACjB,CAAC,CAyBH,CAAC"}
@@ -14,35 +14,35 @@ const reducer = (state, action) => {
14
14
  *
15
15
  * @template S - The type of the state
16
16
  * @param initialState - The initial value of the state
17
- * @returns An object containing the state observable and methods to manipulate it
17
+ * @returns A 3-element tuple: `[state, setState, { updateState, resetState, getSnapshot, initialState }]`
18
18
  *
19
19
  * @example
20
20
  * ```ts
21
21
  * const [state, setState, { updateState, resetState }] = createState(0);
22
22
  *
23
- * const mut_history: number[] = [];
23
+ * const stateHistory: number[] = [];
24
24
  *
25
25
  * state.subscribe((value: number) => {
26
- * mut_history.push(value);
26
+ * stateHistory.push(value);
27
27
  * });
28
28
  *
29
- * assert.deepStrictEqual(mut_history, [0]);
29
+ * assert.deepStrictEqual(stateHistory, [0]);
30
30
  *
31
31
  * setState(10); // logs: 10
32
32
  *
33
- * assert.deepStrictEqual(mut_history, [0, 10]);
33
+ * assert.deepStrictEqual(stateHistory, [0, 10]);
34
34
  *
35
35
  * updateState((prev: number) => prev + 1); // logs: 11
36
36
  *
37
- * assert.deepStrictEqual(mut_history, [0, 10, 11]);
37
+ * assert.deepStrictEqual(stateHistory, [0, 10, 11]);
38
38
  *
39
39
  * resetState(); // logs: 0
40
40
  *
41
- * assert.deepStrictEqual(mut_history, [0, 10, 11, 0]);
41
+ * assert.deepStrictEqual(stateHistory, [0, 10, 11, 0]);
42
42
  * ```
43
43
  */
44
44
  const createState = (initialState) => {
45
- const [state, dispatch, getSnapshot] = createReducer(reducer, initialState);
45
+ const [state, dispatch, { getSnapshot }] = createReducer(reducer, initialState);
46
46
  const updateState = (updateFn) => dispatch({ type: 'update', updateFn });
47
47
  const setState = (nextState) => dispatch({ type: 'set', nextState });
48
48
  const resetState = () => dispatch({ type: 'set', nextState: initialState });
@@ -53,60 +53,10 @@ const createState = (initialState) => {
53
53
  updateState,
54
54
  resetState,
55
55
  getSnapshot,
56
- },
57
- ];
58
- };
59
- /**
60
- * Creates a reactive boolean state with convenient methods for boolean operations.
61
- * Extends `createState` with boolean-specific helpers like `toggle`, `setTrue`, and `setFalse`.
62
- *
63
- * @param initialState - The initial boolean value
64
- * @returns An object with the state observable and boolean-specific methods
65
- *
66
- * @example
67
- * ```ts
68
- * const [state, { setTrue, toggle }] = createBooleanState(false);
69
- *
70
- * const mut_history: boolean[] = [];
71
- *
72
- * state.subscribe((value: boolean) => {
73
- * mut_history.push(value);
74
- * });
75
- *
76
- * assert.deepStrictEqual(mut_history, [false]);
77
- *
78
- * setTrue(); // logs: true
79
- *
80
- * assert.deepStrictEqual(mut_history, [false, true]);
81
- *
82
- * toggle(); // logs: false
83
- *
84
- * assert.deepStrictEqual(mut_history, [false, true, false]);
85
- *
86
- * toggle(); // logs: true
87
- *
88
- * assert.deepStrictEqual(mut_history, [false, true, false, true]);
89
- * ```
90
- */
91
- const createBooleanState = (initialState) => {
92
- const [state, setState, { updateState, resetState, getSnapshot }] = createState(initialState);
93
- return [
94
- state,
95
- {
96
- setTrue: () => {
97
- setState(true);
98
- },
99
- setFalse: () => {
100
- setState(false);
101
- },
102
- toggle: () => updateState((s) => !s),
103
- setState,
104
- updateState,
105
- resetState,
106
- getSnapshot,
56
+ initialState,
107
57
  },
108
58
  ];
109
59
  };
110
60
 
111
- export { createBooleanState, createState };
61
+ export { createState };
112
62
  //# sourceMappingURL=create-state.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-state.mjs","sources":["../../src/utils/create-state.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAcA,MAAM,OAAO,GAAG,CAAK,KAAQ,EAAE,MAAiB,KAAO;AACrD,IAAA,QAAQ,MAAM,CAAC,IAAI;AACjB,QAAA,KAAK,KAAK;YACR,OAAO,MAAM,CAAC,SAAS;AAEzB,QAAA,KAAK,QAAQ;AACX,YAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;;AAEnC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCG;AACI,MAAM,WAAW,GAAG,CACzB,YAAe,KASb;AACF,IAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,GAAG,aAAa,CAClD,OAAO,EACP,YAAY,CACb;AAED,IAAA,MAAM,WAAW,GAAG,CAAC,QAAwB,KAC3C,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAExC,IAAA,MAAM,QAAQ,GAAG,CAAC,SAAY,KAAQ,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAE1E,IAAA,MAAM,UAAU,GAAG,MACjB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAEpD,OAAO;QACL,KAAK;QACL,QAAQ;AACR,QAAA;YACE,WAAW;YACX,UAAU;YACV,WAAW;AACZ,SAAA;KACO;AACZ;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BG;AACI,MAAM,kBAAkB,GAAG,CAChC,YAAqB,KAYnB;AACF,IAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,GAC/D,WAAW,CAAC,YAAY,CAAC;IAE3B,OAAO;QACL,KAAK;AACL,QAAA;YACE,OAAO,EAAE,MAAK;gBACZ,QAAQ,CAAC,IAAI,CAAC;YAChB,CAAC;YACD,QAAQ,EAAE,MAAK;gBACb,QAAQ,CAAC,KAAK,CAAC;YACjB,CAAC;AACD,YAAA,MAAM,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACpC,QAAQ;YACR,WAAW;YACX,UAAU;YACV,WAAW;AACZ,SAAA;KACO;AACZ;;;;"}
1
+ {"version":3,"file":"create-state.mjs","sources":["../../src/utils/create-state.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAcA,MAAM,OAAO,GAAG,CAAK,KAAQ,EAAE,MAAiB,KAAO;AACrD,IAAA,QAAQ,MAAM,CAAC,IAAI;AACjB,QAAA,KAAK,KAAK;YACR,OAAO,MAAM,CAAC,SAAS;AAEzB,QAAA,KAAK,QAAQ;AACX,YAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;;AAEnC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCG;AACI,MAAM,WAAW,GAAG,CACzB,YAAe,KAUb;AACF,IAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,GAAG,aAAa,CACtD,OAAO,EACP,YAAY,CACb;AAED,IAAA,MAAM,WAAW,GAAG,CAAC,QAAwB,KAC3C,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAExC,IAAA,MAAM,QAAQ,GAAG,CAAC,SAAY,KAAQ,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAE1E,IAAA,MAAM,UAAU,GAAG,MACjB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAEpD,OAAO;QACL,KAAK;QACL,QAAQ;AACR,QAAA;YACE,WAAW;YACX,UAAU;YACV,WAAW;YACX,YAAY;AACb,SAAA;KACO;AACZ;;;;"}
@@ -1,3 +1,5 @@
1
+ export * from './collect-to-array.mjs';
2
+ export * from './create-boolean-state.mjs';
1
3
  export * from './create-event-emitter.mjs';
2
4
  export * from './create-reducer.mjs';
3
5
  export * from './create-state.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/utils/index.mts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/utils/index.mts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC"}
@@ -1,4 +1,6 @@
1
+ export { collectToArray } from './collect-to-array.mjs';
2
+ export { createBooleanState } from './create-boolean-state.mjs';
1
3
  export { createEventEmitter, createValueEmitter } from './create-event-emitter.mjs';
2
4
  export { createReducer } from './create-reducer.mjs';
3
- export { createBooleanState, createState } from './create-state.mjs';
5
+ export { createState } from './create-state.mjs';
4
6
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synstate",
3
- "version": "0.1.2",
3
+ "version": "1.0.0",
4
4
  "description": "Type-safe State Management Library for TypeScript/JavaScript",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,36 +28,42 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "dependencies": {
31
- "ts-data-forge": "^6.5.0"
31
+ "ts-data-forge": "^6.8.0"
32
32
  },
33
33
  "devDependencies": {
34
+ "@reduxjs/toolkit": "^2.11.2",
34
35
  "@types/argparse": "^2.0.17",
35
36
  "@types/react": "^19.2.14",
36
37
  "argparse": "^2.0.1",
37
- "react": "^19.2.4"
38
+ "jotai": "^2.18.1",
39
+ "mobx": "^6.15.0",
40
+ "react": "^19.2.4",
41
+ "rxjs": "^7.8.2",
42
+ "valtio": "^2.3.1",
43
+ "zustand": "^5.0.12"
38
44
  },
39
45
  "scripts": {
46
+ "benchmark": "tsx ./scripts/cmd/run-benchmark.mts",
47
+ "benchmark:cascaded-diamond": "tsx ./scripts/cmd/run-benchmark-cascaded-diamond.mts",
48
+ "benchmark:deep-chain": "tsx ./scripts/cmd/run-benchmark-deep-chain.mts",
40
49
  "build": "tsx ./scripts/cmd/build.mts",
41
50
  "build:min": "tsx ./scripts/cmd/build.mts --skip-check",
42
51
  "check:ext": "tsx ./scripts/cmd/check-ext.mts",
43
52
  "check-all": "tsx ./scripts/cmd/check-all.mts",
44
- "codemod": "convert-to-readonly '{src,samples,test}/**/*.{mts,tsx}'",
45
- "doc": "tsx ./scripts/cmd/gen-docs.mts",
53
+ "doc": "run-s doc:embed doc:embed:jsdoc doc:gen-sample-diffs fmt",
46
54
  "doc:embed": "tsx ./scripts/cmd/embed-examples.mts",
47
55
  "doc:embed:jsdoc": "tsx ./scripts/cmd/embed-examples-in-jsdoc.mts",
48
- "doc:preview": "vite preview --config ./configs/vite.doc.config.ts",
49
- "doc:watch": "typedoc --options ./configs/typedoc.config.mjs --watch",
50
- "fmt": "pnpm run z:prettier .",
56
+ "doc:gen-sample-diffs": "tsx ./scripts/cmd/generate-sample-diffs.mts",
57
+ "fmt": "format-uncommitted",
58
+ "fmt:full": "pnpm run z:prettier .",
51
59
  "gi": "run-s gi:src fmt",
52
- "gi:src": "gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts",
60
+ "gi:src": "gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts --exclude \"core/utils/**\"",
53
61
  "lint": "eslint .",
54
62
  "lint:fix": "eslint . --fix",
55
63
  "test": "pnpm run z:vitest:node run",
56
- "test:all": "run-p test test:stream",
57
64
  "test:browser": "pnpm run z:vitest --project='Browser' run",
58
65
  "test:cov": "pnpm run z:vitest:node run --coverage",
59
66
  "test:cov:ui": "vite preview --outDir ./coverage",
60
- "test:stream": "pnpm run z:vitest:stream run",
61
67
  "test:ui": "pnpm run z:vitest:node --ui",
62
68
  "testw": "pnpm run z:vitest:node watch",
63
69
  "tsc": "tsc --noEmit",
@@ -1,5 +1,6 @@
1
1
  import { Arr } from 'ts-data-forge';
2
2
  import {
3
+ isChildObservable,
3
4
  isManagerObservable,
4
5
  type AsyncChildObservable,
5
6
  type ChildObservable,
@@ -14,13 +15,64 @@ import {
14
15
  type WithInitialValueOperator,
15
16
  type Wrap,
16
17
  } from '../types/index.mjs';
17
- import { binarySearch, issueUpdaterSymbol, maxDepth } from '../utils/index.mjs';
18
+ import { binarySearch, issueUpdateToken, maxDepth } from '../utils/index.mjs';
18
19
  import { ObservableBaseClass } from './observable-base-class.mjs';
19
20
 
21
+ /**
22
+ * Detects circular dependencies by walking the full ancestor chain of the
23
+ * given parents and checking whether `child` already appears among them.
24
+ *
25
+ * @throws {Error} if a circular dependency is detected
26
+ */
27
+ const hasCircularDependencyFrom = (
28
+ node: Observable<unknown>,
29
+ mut_visited: MutableSet<ObservableId>,
30
+ mut_inPath: MutableSet<ObservableId>,
31
+ ): boolean => {
32
+ if (mut_inPath.has(node.id)) return true;
33
+
34
+ if (mut_visited.has(node.id)) return false;
35
+
36
+ mut_visited.add(node.id);
37
+
38
+ mut_inPath.add(node.id);
39
+
40
+ if (isChildObservable(node)) {
41
+ for (const parent of node.parents) {
42
+ if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {
43
+ return true;
44
+ }
45
+ }
46
+ }
47
+
48
+ mut_inPath.delete(node.id);
49
+
50
+ return false;
51
+ };
52
+
53
+ const detectCircularDependency = (
54
+ child: ChildObservable<unknown>,
55
+ parents: readonly Observable<unknown>[],
56
+ ): void => {
57
+ const mut_visited = new Set<ObservableId>();
58
+
59
+ const mut_inPath = new Set<ObservableId>([child.id]);
60
+
61
+ for (const parent of parents) {
62
+ if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {
63
+ throw new Error(
64
+ 'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
65
+ );
66
+ }
67
+ }
68
+ };
69
+
20
70
  const registerChild = <A,>(
21
71
  child: ChildObservable<A>,
22
72
  parents: ChildObservable<A>['parents'],
23
73
  ): void => {
74
+ detectCircularDependency(child, parents);
75
+
24
76
  for (const p of parents) {
25
77
  p.addChild(child);
26
78
  }
@@ -76,7 +128,7 @@ export class AsyncChildObservableClass<A, const P extends NonEmptyUnknownList>
76
128
  implements AsyncChildObservable<A, P>
77
129
  {
78
130
  readonly parents;
79
- #mut_procedure: readonly ChildObservable<unknown>[];
131
+ #mut_propagationOrder: readonly ChildObservable<unknown>[];
80
132
  protected readonly descendantsIdSet: MutableSet<ObservableId>;
81
133
 
82
134
  constructor({
@@ -96,7 +148,7 @@ export class AsyncChildObservableClass<A, const P extends NonEmptyUnknownList>
96
148
 
97
149
  this.parents = parents;
98
150
 
99
- this.#mut_procedure = [];
151
+ this.#mut_propagationOrder = [];
100
152
 
101
153
  this.descendantsIdSet = new Set<ObservableId>();
102
154
 
@@ -110,20 +162,24 @@ export class AsyncChildObservableClass<A, const P extends NonEmptyUnknownList>
110
162
  this.descendantsIdSet.add(child.id);
111
163
 
112
164
  const insertPos = binarySearch(
113
- this.#mut_procedure.map((a) => a.depth),
165
+ this.#mut_propagationOrder.map((a) => a.depth),
114
166
  child.depth,
115
167
  );
116
168
 
117
- this.#mut_procedure = Arr.toInserted(this.#mut_procedure, insertPos, child);
169
+ this.#mut_propagationOrder = Arr.toInserted(
170
+ this.#mut_propagationOrder,
171
+ insertPos,
172
+ child,
173
+ );
118
174
  }
119
175
 
120
176
  startUpdate(nextValue: A): void {
121
- const updaterSymbol = issueUpdaterSymbol();
177
+ const updateToken = issueUpdateToken();
122
178
 
123
- this.setNext(nextValue, updaterSymbol);
179
+ this.setNext(nextValue, updateToken);
124
180
 
125
- for (const p of this.#mut_procedure) {
126
- p.tryUpdate(updaterSymbol);
181
+ for (const p of this.#mut_propagationOrder) {
182
+ p.tryUpdate(updateToken);
127
183
  }
128
184
  }
129
185
 
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Comparison tests: how SynState, RxJS, and Jotai handle circular dependencies.
3
+ *
4
+ * - SynState: detects cycles at construction time and throws a clear error.
5
+ * - RxJS: pipe() always returns a new Observable, so static cycles cannot be
6
+ * expressed through the public API. No detection is needed or provided.
7
+ * - Jotai: circular atom definitions are silently accepted. A cycle manifests
8
+ * only at read time as a `Maximum call stack size exceeded` error.
9
+ */
10
+ /* eslint-disable functional/immutable-data */
11
+ /* eslint-disable no-new */
12
+ import { type Atom, atom, createStore } from 'jotai';
13
+ import { BehaviorSubject, combineLatest, map as rxMap } from 'rxjs';
14
+ import { Optional } from 'ts-data-forge';
15
+ import { combine } from '../combine/index.mjs';
16
+ import { source } from '../create/index.mjs';
17
+ import { map } from '../operators/index.mjs';
18
+ import { SyncChildObservableClass } from './child-observable-class.mjs';
19
+ import { RootObservableClass } from './root-observable-class.mjs';
20
+
21
+ describe('circular dependency comparison', () => {
22
+ describe('SynState', () => {
23
+ test('detects cycle at construction time with a clear error message', () => {
24
+ const root = new RootObservableClass({
25
+ initialValue: Optional.some(0),
26
+ });
27
+
28
+ const childA = new SyncChildObservableClass({
29
+ parents: [root],
30
+ initialValue: Optional.some(0),
31
+ });
32
+
33
+ const childB = new SyncChildObservableClass({
34
+ parents: [childA],
35
+ initialValue: Optional.some(0),
36
+ });
37
+
38
+ // Simulate a cycle: childA → childB → childA
39
+ // In normal usage this is impossible — the check runs in every
40
+ // child constructor before the reference becomes available.
41
+ Object.defineProperty(childA, 'parents', {
42
+ value: [childB],
43
+ writable: false,
44
+ configurable: true,
45
+ });
46
+
47
+ expect(() => {
48
+ new SyncChildObservableClass({
49
+ parents: [childA],
50
+ initialValue: Optional.some(0),
51
+ });
52
+ }).toThrow(
53
+ 'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
54
+ );
55
+ });
56
+
57
+ test('accepts valid DAG (diamond dependency)', () => {
58
+ const a$ = source(0);
59
+
60
+ const b$ = a$.pipe(map((x) => x * 10));
61
+
62
+ const c$ = a$.pipe(map((x) => x * 1000));
63
+
64
+ expect(() => {
65
+ combine([b$, c$]);
66
+ }).not.toThrow();
67
+ });
68
+ });
69
+
70
+ describe('RxJS', () => {
71
+ test('cannot express static graph cycles — pipe() always creates new instances', () => {
72
+ // RxJS pipe() returns a brand-new Observable each time, so there is no
73
+ // way to construct a cycle through the public API. This test documents
74
+ // that behavior rather than asserting error detection.
75
+ const a$ = new BehaviorSubject(0);
76
+
77
+ const b$ = a$.pipe(rxMap((x) => x + 1));
78
+
79
+ // b$ is a wholly separate object; there is no "parents" link back to a$.
80
+ expect(a$).not.toBe(b$);
81
+
82
+ // A diamond dependency works but produces glitches (intermediate states).
83
+ const left$ = a$.pipe(rxMap((x) => x * 10));
84
+
85
+ const right$ = a$.pipe(rxMap((x) => x * 1000));
86
+
87
+ const combined$ = combineLatest([left$, right$]).pipe(
88
+ rxMap(([l, r]) => l + r),
89
+ );
90
+
91
+ const mut_values: number[] = [];
92
+
93
+ combined$.subscribe((v) => {
94
+ mut_values.push(v);
95
+ });
96
+
97
+ a$.next(1);
98
+
99
+ a$.next(2);
100
+
101
+ // Glitch values (10, 1020) appear between the correct values.
102
+ assert.deepStrictEqual(mut_values, [0, 10, 1010, 1020, 2020]);
103
+ });
104
+ });
105
+
106
+ describe('Jotai', () => {
107
+ test('does not detect circular atom definitions — crashes on read', () => {
108
+ const store = createStore();
109
+
110
+ // Two atoms that depend on each other — an obvious cycle.
111
+ // Explicit type annotations are required because TypeScript cannot infer
112
+ // the type of mutually-recursive initializers.
113
+ const atomA: Atom<number> = atom((get) => get(atomB) + 1);
114
+
115
+ const atomB: Atom<number> = atom((get) => get(atomA) + 1);
116
+
117
+ // Definition succeeds without any error.
118
+ // Reading triggers infinite recursion.
119
+ expect(() => {
120
+ store.get(atomA);
121
+ }).toThrow('Maximum call stack size exceeded');
122
+ });
123
+
124
+ test('handles valid DAG (diamond dependency) correctly', () => {
125
+ const store = createStore();
126
+
127
+ const base = atom(1);
128
+
129
+ const left = atom((get) => get(base) * 10);
130
+
131
+ const right = atom((get) => get(base) * 1000);
132
+
133
+ const combined = atom((get) => get(left) + get(right));
134
+
135
+ expect(store.get(combined)).toBe(1010);
136
+
137
+ store.set(base, 2);
138
+
139
+ expect(store.get(combined)).toBe(2020);
140
+ });
141
+ });
142
+ });