synstate 3.3.0 → 4.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 +14 -9
  2. package/dist/core/authoring.d.mts +7 -0
  3. package/dist/core/authoring.d.mts.map +1 -0
  4. package/dist/core/authoring.mjs +7 -0
  5. package/dist/core/authoring.mjs.map +1 -0
  6. package/dist/core/class/child-observable-class.d.mts +93 -0
  7. package/dist/core/class/child-observable-class.d.mts.map +1 -0
  8. package/dist/core/class/child-observable-class.mjs +248 -0
  9. package/dist/core/class/child-observable-class.mjs.map +1 -0
  10. package/dist/core/class/create-child-observable.d.mts +159 -0
  11. package/dist/core/class/create-child-observable.d.mts.map +1 -0
  12. package/dist/core/class/create-child-observable.mjs +183 -0
  13. package/dist/core/class/create-child-observable.mjs.map +1 -0
  14. package/dist/core/class/create-root-observable.d.mts +37 -0
  15. package/dist/core/class/create-root-observable.d.mts.map +1 -0
  16. package/dist/core/class/create-root-observable.mjs +52 -0
  17. package/dist/core/class/create-root-observable.mjs.map +1 -0
  18. package/dist/core/class/index.d.mts +9 -0
  19. package/dist/core/class/index.d.mts.map +1 -0
  20. package/dist/core/class/index.mjs +9 -0
  21. package/dist/core/class/index.mjs.map +1 -0
  22. package/dist/core/class/manager-observable-class.d.mts +26 -0
  23. package/dist/core/class/manager-observable-class.d.mts.map +1 -0
  24. package/dist/core/class/manager-observable-class.mjs +116 -0
  25. package/dist/core/class/manager-observable-class.mjs.map +1 -0
  26. package/dist/core/class/observable-base-class.d.mts +97 -0
  27. package/dist/core/class/observable-base-class.d.mts.map +1 -0
  28. package/dist/core/class/observable-base-class.mjs +219 -0
  29. package/dist/core/class/observable-base-class.mjs.map +1 -0
  30. package/dist/core/class/observable-node.d.mts +40 -0
  31. package/dist/core/class/observable-node.d.mts.map +1 -0
  32. package/dist/core/class/observable-node.mjs +10 -0
  33. package/dist/core/class/observable-node.mjs.map +1 -0
  34. package/dist/core/class/propagation-state.d.mts +24 -0
  35. package/dist/core/class/propagation-state.d.mts.map +1 -0
  36. package/dist/core/class/propagation-state.mjs +66 -0
  37. package/dist/core/class/propagation-state.mjs.map +1 -0
  38. package/dist/core/class/root-observable-class.d.mts +23 -0
  39. package/dist/core/class/root-observable-class.d.mts.map +1 -0
  40. package/dist/core/class/root-observable-class.mjs +58 -0
  41. package/dist/core/class/root-observable-class.mjs.map +1 -0
  42. package/dist/core/combine/combine.d.mts.map +1 -1
  43. package/dist/core/combine/combine.mjs +25 -14
  44. package/dist/core/combine/combine.mjs.map +1 -1
  45. package/dist/core/combine/merge.d.mts +0 -3
  46. package/dist/core/combine/merge.d.mts.map +1 -1
  47. package/dist/core/combine/merge.mjs +24 -15
  48. package/dist/core/combine/merge.mjs.map +1 -1
  49. package/dist/core/combine/zip.d.mts +4 -2
  50. package/dist/core/combine/zip.d.mts.map +1 -1
  51. package/dist/core/combine/zip.mjs +54 -18
  52. package/dist/core/combine/zip.mjs.map +1 -1
  53. package/dist/core/create/counter.d.mts +9 -3
  54. package/dist/core/create/counter.d.mts.map +1 -1
  55. package/dist/core/create/counter.mjs +69 -35
  56. package/dist/core/create/counter.mjs.map +1 -1
  57. package/dist/core/create/from-abortable-promise.d.mts.map +1 -1
  58. package/dist/core/create/from-abortable-promise.mjs +26 -18
  59. package/dist/core/create/from-abortable-promise.mjs.map +1 -1
  60. package/dist/core/create/from-promise.d.mts.map +1 -1
  61. package/dist/core/create/from-promise.mjs +30 -21
  62. package/dist/core/create/from-promise.mjs.map +1 -1
  63. package/dist/core/create/from-subscribable.d.mts.map +1 -1
  64. package/dist/core/create/from-subscribable.mjs +48 -12
  65. package/dist/core/create/from-subscribable.mjs.map +1 -1
  66. package/dist/core/create/just.d.mts.map +1 -1
  67. package/dist/core/create/just.mjs +4 -2
  68. package/dist/core/create/just.mjs.map +1 -1
  69. package/dist/core/create/source.d.mts +7 -0
  70. package/dist/core/create/source.d.mts.map +1 -1
  71. package/dist/core/create/source.mjs +27 -13
  72. package/dist/core/create/source.mjs.map +1 -1
  73. package/dist/core/create/timer.d.mts.map +1 -1
  74. package/dist/core/create/timer.mjs +40 -25
  75. package/dist/core/create/timer.mjs.map +1 -1
  76. package/dist/core/index.d.mts +2 -0
  77. package/dist/core/index.d.mts.map +1 -1
  78. package/dist/core/index.mjs +2 -0
  79. package/dist/core/index.mjs.map +1 -1
  80. package/dist/core/operators/audit.d.mts +3 -0
  81. package/dist/core/operators/audit.d.mts.map +1 -1
  82. package/dist/core/operators/audit.mjs +56 -28
  83. package/dist/core/operators/audit.mjs.map +1 -1
  84. package/dist/core/operators/debounce.d.mts +3 -0
  85. package/dist/core/operators/debounce.d.mts.map +1 -1
  86. package/dist/core/operators/debounce.mjs +57 -26
  87. package/dist/core/operators/debounce.mjs.map +1 -1
  88. package/dist/core/operators/filter.d.mts.map +1 -1
  89. package/dist/core/operators/filter.mjs +35 -21
  90. package/dist/core/operators/filter.mjs.map +1 -1
  91. package/dist/core/operators/index.d.mts +1 -0
  92. package/dist/core/operators/index.d.mts.map +1 -1
  93. package/dist/core/operators/index.mjs +1 -0
  94. package/dist/core/operators/index.mjs.map +1 -1
  95. package/dist/core/operators/map.d.mts.map +1 -1
  96. package/dist/core/operators/map.mjs +31 -17
  97. package/dist/core/operators/map.mjs.map +1 -1
  98. package/dist/core/operators/merge-map.d.mts +5 -3
  99. package/dist/core/operators/merge-map.d.mts.map +1 -1
  100. package/dist/core/operators/merge-map.mjs +49 -26
  101. package/dist/core/operators/merge-map.mjs.map +1 -1
  102. package/dist/core/operators/pairwise.d.mts.map +1 -1
  103. package/dist/core/operators/pairwise.mjs +31 -20
  104. package/dist/core/operators/pairwise.mjs.map +1 -1
  105. package/dist/core/operators/scan.d.mts.map +1 -1
  106. package/dist/core/operators/scan.mjs +27 -14
  107. package/dist/core/operators/scan.mjs.map +1 -1
  108. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  109. package/dist/core/operators/skip-if-no-change.mjs +33 -19
  110. package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
  111. package/dist/core/operators/skip-until.d.mts.map +1 -1
  112. package/dist/core/operators/skip-until.mjs +43 -20
  113. package/dist/core/operators/skip-until.mjs.map +1 -1
  114. package/dist/core/operators/skip-while.d.mts.map +1 -1
  115. package/dist/core/operators/skip-while.mjs +35 -20
  116. package/dist/core/operators/skip-while.mjs.map +1 -1
  117. package/dist/core/operators/switch-map.d.mts +5 -3
  118. package/dist/core/operators/switch-map.d.mts.map +1 -1
  119. package/dist/core/operators/switch-map.mjs +53 -25
  120. package/dist/core/operators/switch-map.mjs.map +1 -1
  121. package/dist/core/operators/take-until.d.mts.map +1 -1
  122. package/dist/core/operators/take-until.mjs +38 -19
  123. package/dist/core/operators/take-until.mjs.map +1 -1
  124. package/dist/core/operators/take-while.d.mts.map +1 -1
  125. package/dist/core/operators/take-while.mjs +36 -22
  126. package/dist/core/operators/take-while.mjs.map +1 -1
  127. package/dist/core/{predefined/operators → operators}/take.d.mts +7 -3
  128. package/dist/core/operators/take.d.mts.map +1 -0
  129. package/dist/core/operators/take.mjs +102 -0
  130. package/dist/core/operators/take.mjs.map +1 -0
  131. package/dist/core/operators/throttle.d.mts.map +1 -1
  132. package/dist/core/operators/throttle.mjs +41 -26
  133. package/dist/core/operators/throttle.mjs.map +1 -1
  134. package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
  135. package/dist/core/operators/with-buffered-from.mjs +44 -31
  136. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  137. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  138. package/dist/core/operators/with-current-value-from.mjs +36 -21
  139. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  140. package/dist/core/operators/with-initial-value.d.mts.map +1 -1
  141. package/dist/core/operators/with-initial-value.mjs +24 -12
  142. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  143. package/dist/core/predefined/operators/index.d.mts +0 -1
  144. package/dist/core/predefined/operators/index.d.mts.map +1 -1
  145. package/dist/core/predefined/operators/index.mjs +0 -1
  146. package/dist/core/predefined/operators/index.mjs.map +1 -1
  147. package/dist/core/scope/index.d.mts +2 -0
  148. package/dist/core/scope/index.d.mts.map +1 -0
  149. package/dist/core/scope/index.mjs +2 -0
  150. package/dist/core/scope/index.mjs.map +1 -0
  151. package/dist/core/scope/scope.d.mts +51 -0
  152. package/dist/core/scope/scope.d.mts.map +1 -0
  153. package/dist/core/scope/scope.mjs +194 -0
  154. package/dist/core/scope/scope.mjs.map +1 -0
  155. package/dist/core/types/id.d.mts +0 -2
  156. package/dist/core/types/id.d.mts.map +1 -1
  157. package/dist/core/types/index.d.mts +1 -0
  158. package/dist/core/types/index.d.mts.map +1 -1
  159. package/dist/core/types/index.mjs +1 -0
  160. package/dist/core/types/index.mjs.map +1 -1
  161. package/dist/core/types/observable-family.d.mts +6 -1
  162. package/dist/core/types/observable-family.d.mts.map +1 -1
  163. package/dist/core/types/observable-family.mjs.map +1 -1
  164. package/dist/core/types/observable.d.mts +40 -28
  165. package/dist/core/types/observable.d.mts.map +1 -1
  166. package/dist/core/types/observable.mjs +24 -0
  167. package/dist/core/types/observable.mjs.map +1 -1
  168. package/dist/core/types/scope.d.mts +27 -0
  169. package/dist/core/types/scope.d.mts.map +1 -0
  170. package/dist/core/types/scope.mjs +2 -0
  171. package/dist/core/types/scope.mjs.map +1 -0
  172. package/dist/core/utils/current-scope.d.mts +30 -0
  173. package/dist/core/utils/current-scope.d.mts.map +1 -0
  174. package/dist/core/utils/current-scope.mjs +26 -0
  175. package/dist/core/utils/current-scope.mjs.map +1 -0
  176. package/dist/core/utils/id-maker.d.mts +14 -3
  177. package/dist/core/utils/id-maker.d.mts.map +1 -1
  178. package/dist/core/utils/id-maker.mjs +18 -14
  179. package/dist/core/utils/id-maker.mjs.map +1 -1
  180. package/dist/core/utils/index.d.mts +2 -1
  181. package/dist/core/utils/index.d.mts.map +1 -1
  182. package/dist/core/utils/index.mjs +2 -1
  183. package/dist/core/utils/index.mjs.map +1 -1
  184. package/dist/core/utils/max-depth.d.mts +1 -0
  185. package/dist/core/utils/max-depth.d.mts.map +1 -1
  186. package/dist/core/utils/max-depth.mjs +9 -7
  187. package/dist/core/utils/max-depth.mjs.map +1 -1
  188. package/dist/core/utils/observable-brand.d.mts +13 -0
  189. package/dist/core/utils/observable-brand.d.mts.map +1 -0
  190. package/dist/core/utils/observable-brand.mjs +13 -0
  191. package/dist/core/utils/observable-brand.mjs.map +1 -0
  192. package/dist/utils/create-boolean-state.d.mts +2 -2
  193. package/dist/utils/create-event-emitter.d.mts +2 -2
  194. package/dist/utils/create-reducer.d.mts +17 -8
  195. package/dist/utils/create-reducer.d.mts.map +1 -1
  196. package/dist/utils/create-reducer.mjs +14 -6
  197. package/dist/utils/create-reducer.mjs.map +1 -1
  198. package/dist/utils/create-state.d.mts +9 -2
  199. package/dist/utils/create-state.d.mts.map +1 -1
  200. package/dist/utils/create-state.mjs +7 -0
  201. package/dist/utils/create-state.mjs.map +1 -1
  202. package/package.json +16 -16
  203. package/src/core/authoring.mts +19 -0
  204. package/src/core/class/child-observable-class.mts +374 -0
  205. package/src/core/{base → class}/circular-dependency-comparison.test.mts +46 -38
  206. package/src/core/{base → class}/circular-dependency.test.mts +38 -2
  207. package/src/core/class/complete-propagation.test.mts +264 -0
  208. package/src/core/class/create-child-observable.mts +370 -0
  209. package/src/core/class/create-root-observable.mts +107 -0
  210. package/src/core/class/index.mts +8 -0
  211. package/src/core/class/manager-observable-class.mts +166 -0
  212. package/src/core/class/observable-base-class.mts +315 -0
  213. package/src/core/{base → class}/observable-base.test.mts +1 -1
  214. package/src/core/class/observable-node.mts +54 -0
  215. package/src/core/class/propagation-order.test.mts +92 -0
  216. package/src/core/class/propagation-state.mts +80 -0
  217. package/src/core/class/remove-completed.test.mts +369 -0
  218. package/src/core/class/root-observable-class.mts +85 -0
  219. package/src/core/class/write-during-propagation.test.mts +234 -0
  220. package/src/core/combine/combine.mts +67 -55
  221. package/src/core/combine/merge.mts +33 -26
  222. package/src/core/combine/zip.mts +103 -58
  223. package/src/core/create/counter.mts +105 -54
  224. package/src/core/create/counter.test.mts +82 -0
  225. package/src/core/create/from-abortable-promise.mts +54 -42
  226. package/src/core/create/from-promise.mts +38 -29
  227. package/src/core/create/from-subscribable.mts +68 -23
  228. package/src/core/create/from-subscribable.test.mts +134 -0
  229. package/src/core/create/just.mts +4 -5
  230. package/src/core/create/source.mts +37 -13
  231. package/src/core/create/source.test.mts +41 -0
  232. package/src/core/create/timer.mts +51 -32
  233. package/src/core/index.mts +2 -0
  234. package/src/core/operators/audit.mts +79 -45
  235. package/src/core/operators/debounce-audit-complete.test.mts +121 -0
  236. package/src/core/operators/debounce.mts +81 -40
  237. package/src/core/operators/filter.mts +48 -31
  238. package/src/core/operators/index.mts +1 -0
  239. package/src/core/operators/inner-ownership.test.mts +280 -0
  240. package/src/core/operators/map.mts +49 -32
  241. package/src/core/operators/merge-map.mts +81 -46
  242. package/src/core/operators/pairwise.mts +40 -30
  243. package/src/core/operators/scan.mts +41 -24
  244. package/src/core/operators/skip-if-no-change.mts +45 -31
  245. package/src/core/operators/skip-until.mts +59 -33
  246. package/src/core/operators/skip-while.mts +49 -31
  247. package/src/core/operators/switch-map.mts +74 -38
  248. package/src/core/operators/take-until-skip-until-teardown.test.mts +133 -0
  249. package/src/core/operators/take-until.mts +49 -31
  250. package/src/core/operators/take-while.mts +50 -33
  251. package/src/core/operators/take.mts +138 -0
  252. package/src/core/operators/throttle.mts +55 -38
  253. package/src/core/operators/with-buffered-from.mts +45 -35
  254. package/src/core/operators/with-buffered-from.test.mts +57 -0
  255. package/src/core/operators/with-current-value-from.mts +39 -25
  256. package/src/core/operators/with-initial-value.mts +30 -21
  257. package/src/core/predefined/operators/index.mts +0 -1
  258. package/src/core/scope/index.mts +1 -0
  259. package/src/core/scope/scope.mts +260 -0
  260. package/src/core/scope/scope.test.mts +462 -0
  261. package/src/core/types/id.mts +0 -4
  262. package/src/core/types/index.mts +1 -0
  263. package/src/core/types/observable-family.mts +7 -1
  264. package/src/core/types/observable.mts +77 -42
  265. package/src/core/types/scope.mts +27 -0
  266. package/src/core/utils/current-scope.mts +61 -0
  267. package/src/core/utils/id-maker.mts +20 -30
  268. package/src/core/utils/index.mts +2 -1
  269. package/src/core/utils/max-depth.mts +12 -9
  270. package/src/core/utils/observable-brand.mts +12 -0
  271. package/src/utils/create-boolean-state.mts +2 -2
  272. package/src/utils/create-boolean-state.test.mts +18 -4
  273. package/src/utils/create-event-emitter.mts +2 -2
  274. package/src/utils/create-reducer.mts +22 -16
  275. package/src/utils/create-reducer.test.mts +43 -14
  276. package/src/utils/create-state.mts +9 -2
  277. package/src/utils/create-state.test.mts +32 -14
  278. package/dist/core/base/create-child-observable.d.mts +0 -65
  279. package/dist/core/base/create-child-observable.d.mts.map +0 -1
  280. package/dist/core/base/create-child-observable.mjs +0 -267
  281. package/dist/core/base/create-child-observable.mjs.map +0 -1
  282. package/dist/core/base/create-observable-base.d.mts +0 -147
  283. package/dist/core/base/create-observable-base.d.mts.map +0 -1
  284. package/dist/core/base/create-observable-base.mjs +0 -255
  285. package/dist/core/base/create-observable-base.mjs.map +0 -1
  286. package/dist/core/base/create-root-observable.d.mts +0 -37
  287. package/dist/core/base/create-root-observable.d.mts.map +0 -1
  288. package/dist/core/base/create-root-observable.mjs +0 -57
  289. package/dist/core/base/create-root-observable.mjs.map +0 -1
  290. package/dist/core/base/index.d.mts +0 -3
  291. package/dist/core/base/index.d.mts.map +0 -1
  292. package/dist/core/base/index.mjs +0 -3
  293. package/dist/core/base/index.mjs.map +0 -1
  294. package/dist/core/predefined/operators/take.d.mts.map +0 -1
  295. package/dist/core/predefined/operators/take.mjs +0 -44
  296. package/dist/core/predefined/operators/take.mjs.map +0 -1
  297. package/dist/core/utils/utils.d.mts +0 -6
  298. package/dist/core/utils/utils.d.mts.map +0 -1
  299. package/dist/core/utils/utils.mjs +0 -37
  300. package/dist/core/utils/utils.mjs.map +0 -1
  301. package/src/core/base/create-child-observable.mts +0 -457
  302. package/src/core/base/create-observable-base.mts +0 -548
  303. package/src/core/base/create-root-observable.mts +0 -113
  304. package/src/core/base/index.mts +0 -2
  305. package/src/core/predefined/operators/take.mts +0 -48
  306. package/src/core/utils/utils.mts +0 -56
  307. package/src/core/utils/utils.test.mts +0 -88
@@ -1 +1 @@
1
- {"version":3,"file":"create-state.d.mts","sourceRoot":"","sources":["../../src/utils/create-state.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAuBxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,gBACb,CAAC,YACL,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KACjC,SAAS,CACV,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EACrB,KAAK,EAAE,QAAQ,CAAC;IACd,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,CA0BH,CAAC"}
1
+ {"version":3,"file":"create-state.d.mts","sourceRoot":"","sources":["../../src/utils/create-state.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAuBxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,gBACb,CAAC,YACL,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KACjC,SAAS,CACV,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EACrB,KAAK,EAAE,QAAQ,CAAC;IACd,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,CA0BH,CAAC"}
@@ -66,6 +66,13 @@ const reducer = (state, action) => {
66
66
 
67
67
 
68
68
 
69
+
70
+
71
+
72
+
73
+
74
+
75
+
69
76
 
70
77
 
71
78
 
@@ -1 +1 @@
1
- {"version":3,"file":"create-state.mjs","sourceRoot":"","sources":["../../src/utils/create-state.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAqB,MAAM,sBAAsB,CAAC;AAaxE,MAAM,OAAO,GAAG,CAAK,KAAQ,EAAE,MAAiB,EAAK,EAAE;IACrD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,KAAK;YACR,OAAO,MAAM,CAAC,SAAS,CAAC;QAE1B,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,YAAe,EACf,OAAkC,EAUlC,EAAE;IACF,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,GAAG,aAAa,CACtD,OAAO,EACP,YAAY,EACZ,OAAO,CACR,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,QAAwB,EAAK,EAAE,CAClD,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAEzC,MAAM,QAAQ,GAAG,CAAC,SAAY,EAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAE3E,MAAM,UAAU,GAAG,GAAM,EAAE,CACzB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC;IAErD,OAAO;QACL,KAAK;QACL,QAAQ;QACR;YACE,WAAW;YACX,UAAU;YACV,WAAW;YACX,YAAY;SACb;KACO,CAAC;AACb,CAAC,CAAC"}
1
+ {"version":3,"file":"create-state.mjs","sourceRoot":"","sources":["../../src/utils/create-state.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAqB,MAAM,sBAAsB,CAAC;AAaxE,MAAM,OAAO,GAAG,CAAK,KAAQ,EAAE,MAAiB,EAAK,EAAE;IACrD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,KAAK;YACR,OAAO,MAAM,CAAC,SAAS,CAAC;QAE1B,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,YAAe,EACf,OAAkC,EAUlC,EAAE;IACF,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,GAAG,aAAa,CACtD,OAAO,EACP,YAAY,EACZ,OAAO,CACR,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,QAAwB,EAAK,EAAE,CAClD,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAEzC,MAAM,QAAQ,GAAG,CAAC,SAAY,EAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAE3E,MAAM,UAAU,GAAG,GAAM,EAAE,CACzB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC;IAErD,OAAO;QACL,KAAK;QACL,QAAQ;QACR;YACE,WAAW;YACX,UAAU;YACV,WAAW;YACX,YAAY;SACb;KACO,CAAC;AACb,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synstate",
3
- "version": "3.3.0",
3
+ "version": "4.0.0",
4
4
  "description": "Type-safe State Management Library for TypeScript/JavaScript",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,20 +29,20 @@
29
29
  "LICENSE"
30
30
  ],
31
31
  "dependencies": {
32
- "ts-data-forge": "^14.7.1",
32
+ "ts-data-forge": "^14.8.0",
33
33
  "ts-std-forge": "^0.5.1",
34
34
  "ts-type-forge": "^9.2.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@reduxjs/toolkit": "2.13.0",
38
- "@sumi-lang/cli": "0.0.5",
38
+ "@sumi-lang/cli": "0.0.6",
39
39
  "@types/react": "19.3.0",
40
40
  "@vitest/browser-playwright": "5.0.3",
41
41
  "argparse": "3.0.2",
42
- "eslint-config-typed": "5.17.4",
43
- "eslint-plugin-ts-data-forge": "0.8.6",
44
- "eslint-plugin-ts-fortress": "1.3.3",
45
- "eslint-plugin-ts-type-forge": "0.8.8",
42
+ "eslint-config-typed": "5.19.0",
43
+ "eslint-plugin-ts-data-forge": "0.9.0",
44
+ "eslint-plugin-ts-fortress": "1.3.4",
45
+ "eslint-plugin-ts-type-forge": "0.8.9",
46
46
  "jotai": "3.0.1",
47
47
  "mobx": "7.0.6",
48
48
  "playwright": "1.64.0",
@@ -51,8 +51,8 @@
51
51
  "prettier-plugin-packagejson": "3.0.2",
52
52
  "react": "19.3.0",
53
53
  "rxjs": "7.8.2",
54
- "synstate-react-hooks": "3.3.0",
55
- "synstate-react-hooks-compat": "3.3.0",
54
+ "synstate-react-hooks": "4.0.0",
55
+ "synstate-react-hooks-compat": "4.0.0",
56
56
  "ts-repo-utils": "10.7.1",
57
57
  "valtio": "2.3.2",
58
58
  "zustand": "5.0.15"
@@ -68,22 +68,22 @@
68
68
  "scripts": {
69
69
  "build": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/build.mts",
70
70
  "check:ext": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-ext.mts",
71
- "check:lint": "oxlint . && eslint .",
71
+ "check:lint": "node ../../tools/scripts/cmd/assert-deps-built.mts && oxlint . && eslint .",
72
72
  "check:sumi": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../languages/sumi/cli/src/cmd/sumi.mts check ./tsconfig.sumi.json",
73
- "check:test": "pnpm run z:vitest:node run",
74
- "check:test:browser": "pnpm run z:vitest --project='Browser' run",
75
- "check:test:cov": "pnpm run z:vitest:node run --coverage",
76
- "check:types": "node ../../node_modules/typescript-native/bin/tsc --noEmit",
73
+ "check:test": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest:node run",
74
+ "check:test:browser": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest --project='Browser' run",
75
+ "check:test:cov": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest:node run --coverage",
76
+ "check:types": "node ../../tools/scripts/cmd/assert-deps-built.mts && node ../../node_modules/typescript-native/bin/tsc --noEmit",
77
77
  "check-all": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-all.mts",
78
78
  "doc": "run-s gen:readme gen:jsdoc gen:sample-diffs fix:fmt",
79
79
  "fix:fmt": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-uncommitted.mts --cwd .",
80
80
  "fix:fmt:diff": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-diff-from.mts origin/main --cwd .",
81
81
  "fix:fmt:full": "pnpm run z:prettier .",
82
- "fix:lint": "oxlint . --fix && eslint . --fix",
82
+ "fix:lint": "node ../../tools/scripts/cmd/assert-deps-built.mts && oxlint . --fix && eslint . --fix",
83
83
  "fmt": "pnpm run fix:fmt",
84
84
  "gen": "run-s gen:index gen:readme gen:jsdoc gen:sample-diffs",
85
85
  "gen:index": "run-s gen:index:src fix:fmt",
86
- "gen:index:src": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/gen-index-ts.mts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts --exclude \"core/base/**\" --exclude \"core/utils/**\"",
86
+ "gen:index:src": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/gen-index-ts.mts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts --exclude \"core/class/**\" --exclude \"core/utils/**\"",
87
87
  "gen:jsdoc": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/embed-examples-in-jsdoc.mts",
88
88
  "gen:readme": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/embed-examples.mts",
89
89
  "gen:sample-diffs": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/generate-sample-diffs.mts",
@@ -0,0 +1,19 @@
1
+ /**
2
+ * What custom creators and operators are written with: the factories every
3
+ * built-in observable is made by (#2117). The rest of `core/base` stays
4
+ * internal.
5
+ */
6
+ export {
7
+ createAsyncChildObservable,
8
+ createInitializedSyncChildObservable,
9
+ createRootObservable,
10
+ createSyncChildObservable,
11
+ type AsyncChildObservableHooks,
12
+ type AsyncChildObservableTools,
13
+ type ChildObservableConfig,
14
+ type InitializedChildObservableConfig,
15
+ type RootObservableConfig,
16
+ type RootObservableTools,
17
+ type SyncChildObservableHooks,
18
+ type SyncChildObservableTools,
19
+ } from './class/index.mjs';
@@ -0,0 +1,374 @@
1
+ // @sumi-expect-error-file classes/no-class
2
+ // @sumi-expect-error-file banned-syntax/no-this
3
+ // @sumi-expect-error-file functions/prefer-arrow-function
4
+ // @sumi-expect-error-file mutation/no-mutation-without-mut-prefix
5
+ import { Arr, hasKey, isRecord, type Optional, type Some } from 'ts-data-forge';
6
+ import { panic } from 'ts-std-forge';
7
+ import type { MutableSet } from 'ts-type-forge';
8
+ import type {
9
+ AsyncChildObservable,
10
+ DropInitialValueOperator,
11
+ InitializedObservable,
12
+ InitializedSyncChildObservable,
13
+ KeepInitialValueOperator,
14
+ NonEmptyUnknownList,
15
+ Observable,
16
+ Operator,
17
+ SyncChildObservable,
18
+ WithInitialValueOperator,
19
+ Wrap,
20
+ } from '../types/index.mjs';
21
+ import { maxDepth } from '../utils/index.mjs';
22
+ import { ManagerObservableClass } from './manager-observable-class.mjs';
23
+ import { ObservableBaseClass } from './observable-base-class.mjs';
24
+ import {
25
+ toNode,
26
+ type ChildNode,
27
+ type ManagerNode,
28
+ } from './observable-node.mjs';
29
+ import { runAfterPropagation } from './propagation-state.mjs';
30
+
31
+ type ChildObservableConfig<A, P extends NonEmptyUnknownList> = Readonly<{
32
+ parents: Wrap<P>;
33
+
34
+ /**
35
+ * Defaults to one more than the deepest parent. An update reaches
36
+ * observables in ascending depth, so an observable `tryUpdate` reads with
37
+ * `getSnapshot` without being its child (`withCurrentValueFrom`'s second
38
+ * source) has to be shallower than this one too, or this one reads its value
39
+ * from before the update: pass `1 + Math.max(...)` over both then.
40
+ */
41
+ depth?: number;
42
+
43
+ initialValue: Optional<A>;
44
+ }>;
45
+
46
+ /**
47
+ * An observable derived from `parents` that emits, if at all, in the same
48
+ * update as they do (`map`, `filter`, `combine`, …).
49
+ *
50
+ * A subclass overrides `tryUpdate`, and, when it holds something to
51
+ * release, `teardown`; its constructor ends with `this.attach()`, once
52
+ * everything `tryUpdate` and `teardown` read is in place.
53
+ */
54
+ export class SyncChildObservableClass<A, const P extends NonEmptyUnknownList>
55
+ extends ObservableBaseClass<A, 'sync child', number>
56
+ implements SyncChildObservable<A, P>
57
+ {
58
+ #mut_isAttached: boolean;
59
+ readonly parents: Wrap<P>;
60
+ readonly managers: readonly ManagerNode[];
61
+
62
+ constructor({
63
+ parents,
64
+ depth = 1 + maxDepth(parents),
65
+ initialValue,
66
+ }: ChildObservableConfig<A, P>) {
67
+ super({
68
+ kind: 'sync child',
69
+ depth,
70
+ initialValue,
71
+ });
72
+
73
+ this.parents = parents;
74
+
75
+ this.managers = managersOf(parents);
76
+
77
+ this.#mut_isAttached = false;
78
+ }
79
+
80
+ /**
81
+ * Joins the current scope and registers this observable to its parents
82
+ * and the managers it is reached from. Completed parents never update
83
+ * again, so a child created under them is complete from the start
84
+ * (#2248), as is one its constructor completed; registering it would only
85
+ * leave a reference in each parent.
86
+ */
87
+ protected attach(): void {
88
+ this.adoptIntoCurrentScope();
89
+
90
+ if (this.isCompleted || this.parents.every((p) => p.isCompleted)) {
91
+ this.complete();
92
+ } else {
93
+ registerChild(this, this.managers);
94
+
95
+ this.#mut_isAttached = true;
96
+ }
97
+ }
98
+
99
+ protected override detach(): void {
100
+ if (!this.#mut_isAttached) {
101
+ return;
102
+ }
103
+
104
+ this.#mut_isAttached = false;
105
+
106
+ unregisterChild(this, this.managers);
107
+ }
108
+
109
+ override tryComplete(): void {
110
+ if (shouldComplete(this)) {
111
+ this.complete();
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Whether this observable can emit nothing more although some parent is
117
+ * still live, asked whenever a parent completes. `true` completes it then,
118
+ * as all parents completing does (`zip` once a completed source's queue is
119
+ * empty).
120
+ */
121
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
122
+ canEmitNoMore(): boolean {
123
+ return false;
124
+ }
125
+
126
+ /**
127
+ * Completes this observable once the update now propagating has reached
128
+ * every descendant, so that they see the value just passed on first.
129
+ */
130
+ protected completeAfterPropagation(): void {
131
+ runAfterPropagation(() => {
132
+ this.complete();
133
+ });
134
+ }
135
+ }
136
+
137
+ /**
138
+ * An observable derived from `parents` that may emit later, in an update of
139
+ * its own, through `startUpdate` (`debounce`, `switchMap`). It may also emit
140
+ * synchronously with `setNext`, as a sync child does. A subclass may also
141
+ * override `flush`, to pass on at completion what it still holds.
142
+ */
143
+ export class AsyncChildObservableClass<A, const P extends NonEmptyUnknownList>
144
+ extends ManagerObservableClass<A, 'async child', number>
145
+ implements AsyncChildObservable<A, P>
146
+ {
147
+ #mut_isAttached: boolean;
148
+ readonly parents: Wrap<P>;
149
+
150
+ constructor({
151
+ parents,
152
+ depth = 1 + maxDepth(parents),
153
+ initialValue,
154
+ }: ChildObservableConfig<A, P>) {
155
+ super({
156
+ kind: 'async child',
157
+ depth,
158
+ initialValue,
159
+ });
160
+
161
+ this.parents = parents;
162
+
163
+ this.#mut_isAttached = false;
164
+ }
165
+
166
+ /** See `SyncChildObservableClass.attach`. */
167
+ protected attach(): void {
168
+ this.adoptIntoCurrentScope();
169
+
170
+ if (this.isCompleted || this.parents.every((p) => p.isCompleted)) {
171
+ this.complete();
172
+ } else {
173
+ registerChild(this, managersOf(this.parents));
174
+
175
+ this.#mut_isAttached = true;
176
+ }
177
+ }
178
+
179
+ protected override detach(): void {
180
+ if (!this.#mut_isAttached) {
181
+ return;
182
+ }
183
+
184
+ this.#mut_isAttached = false;
185
+
186
+ unregisterChild(this, managersOf(this.parents));
187
+ }
188
+
189
+ override tryComplete(): void {
190
+ if (shouldComplete(this)) {
191
+ this.complete();
192
+ }
193
+ }
194
+
195
+ /** See `SyncChildObservableClass.canEmitNoMore`. */
196
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
197
+ canEmitNoMore(): boolean {
198
+ return false;
199
+ }
200
+
201
+ /** See `SyncChildObservableClass.completeAfterPropagation`. */
202
+ protected completeAfterPropagation(): void {
203
+ runAfterPropagation(() => {
204
+ this.complete();
205
+ });
206
+ }
207
+ }
208
+
209
+ /**
210
+ * A sync child that always holds a value: a subclass passes a `Some` as its
211
+ * `initialValue`. It differs from its base only in the types of
212
+ * `getSnapshot` and `pipe`.
213
+ */
214
+ export class InitializedSyncChildObservableClass<
215
+ A,
216
+ const P extends NonEmptyUnknownList,
217
+ >
218
+ extends SyncChildObservableClass<A, P>
219
+ implements InitializedSyncChildObservable<A, P>
220
+ {
221
+ override getSnapshot(): Some<A> {
222
+ // `initialValue` is a `Some`, and `setNext` only ever stores a `Some`.
223
+ // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
224
+ return super.getSnapshot() as Some<A>;
225
+ }
226
+
227
+ override pipe<B>(
228
+ operator: KeepInitialValueOperator<A, B> | WithInitialValueOperator<A, B>,
229
+ ): InitializedObservable<B>;
230
+
231
+ override pipe<B>(operator: DropInitialValueOperator<A, B>): Observable<B>;
232
+
233
+ override pipe<B>(operator: Operator<A, B>): Observable<B> {
234
+ return operator(this);
235
+ }
236
+ }
237
+
238
+ /* helpers */
239
+
240
+ /**
241
+ * Called by a parent that has completed: whether no live parent is left, or
242
+ * no value can come from the live ones.
243
+ */
244
+ const shouldComplete = (
245
+ child: ChildNode & Readonly<{ canEmitNoMore: () => boolean }>,
246
+ ): boolean =>
247
+ !child.isCompleted &&
248
+ (child.parents.every((p) => p.isCompleted) || child.canEmitNoMore());
249
+
250
+ /** Registers a child to its parents and to `managers`. */
251
+ const registerChild = (
252
+ child: ChildNode,
253
+ managers: readonly ManagerNode[],
254
+ ): void => {
255
+ if (checksCircularDependency) {
256
+ detectCircularDependency(child);
257
+ }
258
+
259
+ for (const p of child.parents) {
260
+ toNode(p).addChild(child);
261
+ }
262
+
263
+ for (const m of managers) {
264
+ m.addDescendant(child);
265
+ }
266
+ };
267
+
268
+ /**
269
+ * Undoes {@link registerChild} for a completed child: its parents forget it,
270
+ * and every manager it registered to stops propagating updates to it.
271
+ * Without this a completed node stayed reachable for as long as its root
272
+ * lived, and ran its `tryUpdate` on every update (#2134).
273
+ */
274
+ const unregisterChild = (
275
+ child: ChildNode,
276
+ managers: readonly ManagerNode[],
277
+ ): void => {
278
+ for (const p of child.parents) {
279
+ toNode(p).deleteChild(child);
280
+ }
281
+
282
+ for (const m of managers) {
283
+ m.deleteDescendant(child);
284
+ }
285
+ };
286
+
287
+ /**
288
+ * The managers the descendants of `parents` register to, shared with the
289
+ * parent when there is one.
290
+ */
291
+ const managersOf = (
292
+ parents: readonly Observable<unknown>[],
293
+ ): readonly ManagerNode[] =>
294
+ Arr.isFixedLengthArray(1, parents)
295
+ ? toNode(parents[0]).managers
296
+ : Array.from(new Set(parents.flatMap((p) => toNode(p).managers)));
297
+
298
+ const isDevelopmentProcess = (): boolean => {
299
+ const proc: unknown = Reflect.get(globalThis, 'process');
300
+
301
+ return (
302
+ isRecord(proc) &&
303
+ hasKey(proc, 'env') &&
304
+ isRecord(proc.env) &&
305
+ (!hasKey(proc.env, 'NODE_ENV') || proc.env.NODE_ENV !== 'production')
306
+ );
307
+ };
308
+
309
+ /**
310
+ * Whether {@link registerChild} checks for a circular dependency: everywhere
311
+ * but in a process whose `NODE_ENV` is `production`. A child's parents exist
312
+ * before it and cannot be changed afterwards, so only code that rewrites an
313
+ * observable's readonly `parents` can make a cycle, while the walk over every
314
+ * ancestor is paid on every creation. Where there is no `process` (a browser
315
+ * bundle) the check is off.
316
+ */
317
+ export const checksCircularDependency: boolean = isDevelopmentProcess();
318
+
319
+ /**
320
+ * Detects circular dependencies by walking the full ancestor chain of the
321
+ * child's parents and checking whether the child already appears among them.
322
+ *
323
+ * @throws {Error} if a circular dependency is detected
324
+ */
325
+ const detectCircularDependency = (child: ChildNode): void => {
326
+ const mut_visited = new Set<Observable<unknown>>();
327
+
328
+ const mut_inPath = new Set<Observable<unknown>>([
329
+ // @sumi-expect-error banned-syntax/no-unsafe-type-assertion
330
+ child as Observable<unknown>,
331
+ ]);
332
+
333
+ for (const parent of child.parents) {
334
+ if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {
335
+ // A programming error (invariant violation), not a recoverable
336
+ // failure. `panic` marks the error it throws, so the `Result` /
337
+ // `AsyncResult` boundaries rethrow it rather than turning an invariant
338
+ // violation into an `Err` a caller might handle (Sumi D-48 / D-53).
339
+ panic(
340
+ 'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
341
+ );
342
+ }
343
+ }
344
+ };
345
+
346
+ const hasCircularDependencyFrom = (
347
+ node: Observable<unknown>,
348
+ mut_visited: MutableSet<Observable<unknown>>,
349
+ mut_inPath: MutableSet<Observable<unknown>>,
350
+ ): boolean => {
351
+ if (mut_inPath.has(node)) {
352
+ return true;
353
+ }
354
+
355
+ if (mut_visited.has(node)) {
356
+ return false;
357
+ }
358
+
359
+ mut_visited.add(node);
360
+
361
+ mut_inPath.add(node);
362
+
363
+ if (node.kind !== 'root') {
364
+ for (const parent of node.parents) {
365
+ if (hasCircularDependencyFrom(parent, mut_visited, mut_inPath)) {
366
+ return true;
367
+ }
368
+ }
369
+ }
370
+
371
+ mut_inPath.delete(node);
372
+
373
+ return false;
374
+ };
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Comparison tests: how SynState, RxJS, and Jotai handle circular dependencies.
3
3
  *
4
- * - SynState: detects cycles at construction time and throws a clear error.
4
+ * - SynState: detects cycles at construction time and throws a clear error,
5
+ * outside production (`checksCircularDependency`).
5
6
  * - RxJS: pipe() always returns a new Observable, so static cycles cannot be
6
7
  * expressed through the public API. No detection is needed or provided.
7
8
  * - Jotai: circular atom definitions are silently accepted. A cycle manifests
@@ -13,55 +14,62 @@ import { Optional } from 'ts-data-forge';
13
14
  import { combine } from '../combine/index.mjs';
14
15
  import { source } from '../create/index.mjs';
15
16
  import { map } from '../operators/index.mjs';
16
- import { createSyncChildObservable } from './create-child-observable.mjs';
17
- import { createTryUpdateNotImplemented } from './create-observable-base.mjs';
17
+ import { checksCircularDependency } from './child-observable-class.mjs';
18
+ import {
19
+ createSyncChildObservable,
20
+ createTryUpdateNotImplemented,
21
+ } from './create-child-observable.mjs';
18
22
  import { createRootObservable } from './create-root-observable.mjs';
19
23
 
20
24
  describe('circular dependency comparison', () => {
21
25
  describe('SynState', () => {
22
- test('detects cycle at construction time with a clear error message', () => {
23
- const root = createRootObservable(
24
- { initialValue: Optional.some(0) },
25
- () => ({}),
26
- );
27
-
28
- const mut_childA = createSyncChildObservable(
29
- {
30
- parents: [root],
31
- initialValue: Optional.some(0),
32
- },
33
- createTryUpdateNotImplemented,
34
- );
35
-
36
- const childB = createSyncChildObservable(
37
- {
38
- parents: [mut_childA],
39
- initialValue: Optional.some(0),
40
- },
41
- createTryUpdateNotImplemented,
42
- );
26
+ // Only where the check runs: see `checksCircularDependency`.
27
+ test.runIf(checksCircularDependency)(
28
+ 'detects cycle at construction time with a clear error message',
29
+ () => {
30
+ const root = createRootObservable(
31
+ { initialValue: Optional.some(0) },
32
+ () => ({}),
33
+ );
43
34
 
44
- // Simulate a cycle: mut_childA → childB → mut_childA
45
- // In normal usage this is impossible — the check runs in every
46
- // child constructor before the reference becomes available.
47
- Object.defineProperty(mut_childA, 'parents', {
48
- value: [childB],
49
- writable: false,
50
- configurable: true,
51
- });
35
+ const mut_childA = createSyncChildObservable(
36
+ {
37
+ parents: [root],
38
+ initialValue: Optional.some(0),
39
+ },
40
+ createTryUpdateNotImplemented,
41
+ );
52
42
 
53
- expect(() => {
54
- createSyncChildObservable(
43
+ const childB = createSyncChildObservable(
55
44
  {
56
45
  parents: [mut_childA],
57
46
  initialValue: Optional.some(0),
58
47
  },
59
48
  createTryUpdateNotImplemented,
60
49
  );
61
- }).toThrow(
62
- 'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
63
- );
64
- });
50
+
51
+ // Simulate a cycle: mut_childA → childB → mut_childA
52
+ // In normal usage this is impossible — the check runs in every
53
+ // child constructor before the reference becomes available.
54
+ Object.defineProperty(mut_childA, 'parents', {
55
+ value: [childB],
56
+ writable: false,
57
+ configurable: true,
58
+ });
59
+
60
+ expect(() => {
61
+ createSyncChildObservable(
62
+ {
63
+ parents: [mut_childA],
64
+ initialValue: Optional.some(0),
65
+ },
66
+ createTryUpdateNotImplemented,
67
+ );
68
+ }).toThrow(
69
+ 'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
70
+ );
71
+ },
72
+ );
65
73
 
66
74
  test('accepts valid DAG (diamond dependency)', () => {
67
75
  const a$ = source(0);
@@ -2,15 +2,18 @@ import { Optional } from 'ts-data-forge';
2
2
  import { combine, merge } from '../combine/index.mjs';
3
3
  import { source } from '../create/index.mjs';
4
4
  import { map } from '../operators/index.mjs';
5
+ import { checksCircularDependency } from './child-observable-class.mjs';
5
6
  import {
6
7
  createAsyncChildObservable,
7
8
  createSyncChildObservable,
9
+ createTryUpdateNotImplemented,
8
10
  } from './create-child-observable.mjs';
9
- import { createTryUpdateNotImplemented } from './create-observable-base.mjs';
10
11
  import { createRootObservable } from './create-root-observable.mjs';
11
12
 
12
13
  describe('circular dependency detection', () => {
13
- describe('cycle in ancestor graph', () => {
14
+ // The check runs only in a process whose `NODE_ENV` is not `production`;
15
+ // the browser run has no `process`, and there nothing is checked.
16
+ describe.runIf(checksCircularDependency)('cycle in ancestor graph', () => {
14
17
  test('should throw when parent chain contains a cycle (A -> B -> A)', () => {
15
18
  const root = createRootObservable(
16
19
  { initialValue: Optional.some(0) },
@@ -180,6 +183,39 @@ describe('circular dependency detection', () => {
180
183
  });
181
184
  });
182
185
 
186
+ describe.skipIf(checksCircularDependency)('with the check off', () => {
187
+ test('does not walk the ancestors of a new child', () => {
188
+ const root = createRootObservable(
189
+ { initialValue: Optional.some(0) },
190
+ () => ({}),
191
+ );
192
+
193
+ const mut_childA = createSyncChildObservable(
194
+ {
195
+ parents: [root],
196
+ initialValue: Optional.some(0),
197
+ },
198
+ createTryUpdateNotImplemented,
199
+ );
200
+
201
+ Object.defineProperty(mut_childA, 'parents', {
202
+ value: [mut_childA],
203
+ writable: false,
204
+ configurable: true,
205
+ });
206
+
207
+ expect(() => {
208
+ createSyncChildObservable(
209
+ {
210
+ parents: [mut_childA],
211
+ initialValue: Optional.some(0),
212
+ },
213
+ createTryUpdateNotImplemented,
214
+ );
215
+ }).not.toThrow();
216
+ });
217
+ });
218
+
183
219
  describe('valid DAG patterns should not throw', () => {
184
220
  test('diamond dependency is not a cycle', () => {
185
221
  const root = createRootObservable(