@bquery/bquery 1.6.0 → 1.7.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 (359) hide show
  1. package/README.md +716 -586
  2. package/dist/a11y/announce.d.ts +43 -0
  3. package/dist/a11y/announce.d.ts.map +1 -0
  4. package/dist/a11y/audit.d.ts +42 -0
  5. package/dist/a11y/audit.d.ts.map +1 -0
  6. package/dist/a11y/index.d.ts +53 -0
  7. package/dist/a11y/index.d.ts.map +1 -0
  8. package/dist/a11y/media-preferences.d.ts +77 -0
  9. package/dist/a11y/media-preferences.d.ts.map +1 -0
  10. package/dist/a11y/roving-tab-index.d.ts +38 -0
  11. package/dist/a11y/roving-tab-index.d.ts.map +1 -0
  12. package/dist/a11y/skip-link.d.ts +37 -0
  13. package/dist/a11y/skip-link.d.ts.map +1 -0
  14. package/dist/a11y/trap-focus.d.ts +49 -0
  15. package/dist/a11y/trap-focus.d.ts.map +1 -0
  16. package/dist/a11y/types.d.ts +152 -0
  17. package/dist/a11y/types.d.ts.map +1 -0
  18. package/dist/a11y-C5QOVvRn.js +421 -0
  19. package/dist/a11y-C5QOVvRn.js.map +1 -0
  20. package/dist/a11y.es.mjs +14 -0
  21. package/dist/component/component.d.ts.map +1 -1
  22. package/dist/component/html.d.ts.map +1 -1
  23. package/dist/component/index.d.ts +2 -1
  24. package/dist/component/index.d.ts.map +1 -1
  25. package/dist/component/library.d.ts.map +1 -1
  26. package/dist/component/scope.d.ts +138 -0
  27. package/dist/component/scope.d.ts.map +1 -0
  28. package/dist/component/types.d.ts +53 -1
  29. package/dist/component/types.d.ts.map +1 -1
  30. package/dist/component-CuuTijA6.js +684 -0
  31. package/dist/component-CuuTijA6.js.map +1 -0
  32. package/dist/component.es.mjs +9 -6
  33. package/dist/{config-DRmZZno3.js → config-BW35FKuA.js} +4 -4
  34. package/dist/{config-DRmZZno3.js.map → config-BW35FKuA.js.map} +1 -1
  35. package/dist/constraints-3lV9yyBw.js +100 -0
  36. package/dist/constraints-3lV9yyBw.js.map +1 -0
  37. package/dist/core/collection.d.ts +48 -0
  38. package/dist/core/collection.d.ts.map +1 -1
  39. package/dist/core/element.d.ts +92 -0
  40. package/dist/core/element.d.ts.map +1 -1
  41. package/dist/core/env.d.ts +18 -0
  42. package/dist/core/env.d.ts.map +1 -0
  43. package/dist/core/index.d.ts +1 -0
  44. package/dist/core/index.d.ts.map +1 -1
  45. package/dist/core/shared.d.ts +8 -0
  46. package/dist/core/shared.d.ts.map +1 -1
  47. package/dist/core/utils/index.d.ts +52 -41
  48. package/dist/core/utils/index.d.ts.map +1 -1
  49. package/dist/core-Cjl7GUu8.js +717 -0
  50. package/dist/core-Cjl7GUu8.js.map +1 -0
  51. package/dist/core-DnlyjbF2.js +112 -0
  52. package/dist/core-DnlyjbF2.js.map +1 -0
  53. package/dist/core.es.mjs +45 -44
  54. package/dist/custom-directives-7wAShnnd.js +9 -0
  55. package/dist/custom-directives-7wAShnnd.js.map +1 -0
  56. package/dist/devtools/devtools.d.ts +212 -0
  57. package/dist/devtools/devtools.d.ts.map +1 -0
  58. package/dist/devtools/index.d.ts +20 -0
  59. package/dist/devtools/index.d.ts.map +1 -0
  60. package/dist/devtools/types.d.ts +69 -0
  61. package/dist/devtools/types.d.ts.map +1 -0
  62. package/dist/devtools-D2fQLhDN.js +122 -0
  63. package/dist/devtools-D2fQLhDN.js.map +1 -0
  64. package/dist/devtools.es.mjs +19 -0
  65. package/dist/dnd/draggable.d.ts +51 -0
  66. package/dist/dnd/draggable.d.ts.map +1 -0
  67. package/dist/dnd/droppable.d.ts +38 -0
  68. package/dist/dnd/droppable.d.ts.map +1 -0
  69. package/dist/dnd/index.d.ts +47 -0
  70. package/dist/dnd/index.d.ts.map +1 -0
  71. package/dist/dnd/sortable.d.ts +43 -0
  72. package/dist/dnd/sortable.d.ts.map +1 -0
  73. package/dist/dnd/types.d.ts +250 -0
  74. package/dist/dnd/types.d.ts.map +1 -0
  75. package/dist/dnd-B8EgyzaI.js +244 -0
  76. package/dist/dnd-B8EgyzaI.js.map +1 -0
  77. package/dist/dnd.es.mjs +6 -0
  78. package/dist/env-NeVmr4Gf.js +19 -0
  79. package/dist/env-NeVmr4Gf.js.map +1 -0
  80. package/dist/forms/create-form.d.ts +49 -0
  81. package/dist/forms/create-form.d.ts.map +1 -0
  82. package/dist/forms/index.d.ts +39 -0
  83. package/dist/forms/index.d.ts.map +1 -0
  84. package/dist/forms/types.d.ts +139 -0
  85. package/dist/forms/types.d.ts.map +1 -0
  86. package/dist/forms/validators.d.ts +179 -0
  87. package/dist/forms/validators.d.ts.map +1 -0
  88. package/dist/forms-C3yovgH9.js +141 -0
  89. package/dist/forms-C3yovgH9.js.map +1 -0
  90. package/dist/forms.es.mjs +14 -0
  91. package/dist/full.d.ts +35 -7
  92. package/dist/full.d.ts.map +1 -1
  93. package/dist/full.es.mjs +182 -91
  94. package/dist/full.iife.js +47 -31
  95. package/dist/full.iife.js.map +1 -1
  96. package/dist/full.umd.js +47 -31
  97. package/dist/full.umd.js.map +1 -1
  98. package/dist/i18n/formatting.d.ts +40 -0
  99. package/dist/i18n/formatting.d.ts.map +1 -0
  100. package/dist/i18n/i18n.d.ts +48 -0
  101. package/dist/i18n/i18n.d.ts.map +1 -0
  102. package/dist/i18n/index.d.ts +57 -0
  103. package/dist/i18n/index.d.ts.map +1 -0
  104. package/dist/i18n/translate.d.ts +83 -0
  105. package/dist/i18n/translate.d.ts.map +1 -0
  106. package/dist/i18n/types.d.ts +156 -0
  107. package/dist/i18n/types.d.ts.map +1 -0
  108. package/dist/i18n-BnnhTFOS.js +89 -0
  109. package/dist/i18n-BnnhTFOS.js.map +1 -0
  110. package/dist/i18n.es.mjs +6 -0
  111. package/dist/index.d.ts +11 -0
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.es.mjs +227 -136
  114. package/dist/media/battery.d.ts +35 -0
  115. package/dist/media/battery.d.ts.map +1 -0
  116. package/dist/media/breakpoints.d.ts +51 -0
  117. package/dist/media/breakpoints.d.ts.map +1 -0
  118. package/dist/media/clipboard.d.ts +30 -0
  119. package/dist/media/clipboard.d.ts.map +1 -0
  120. package/dist/media/device-sensors.d.ts +54 -0
  121. package/dist/media/device-sensors.d.ts.map +1 -0
  122. package/dist/media/geolocation.d.ts +38 -0
  123. package/dist/media/geolocation.d.ts.map +1 -0
  124. package/dist/media/index.d.ts +42 -0
  125. package/dist/media/index.d.ts.map +1 -0
  126. package/dist/media/media-query.d.ts +36 -0
  127. package/dist/media/media-query.d.ts.map +1 -0
  128. package/dist/media/network.d.ts +35 -0
  129. package/dist/media/network.d.ts.map +1 -0
  130. package/dist/media/types.d.ts +173 -0
  131. package/dist/media/types.d.ts.map +1 -0
  132. package/dist/media/viewport.d.ts +32 -0
  133. package/dist/media/viewport.d.ts.map +1 -0
  134. package/dist/media-Di2Ta22s.js +340 -0
  135. package/dist/media-Di2Ta22s.js.map +1 -0
  136. package/dist/media.es.mjs +12 -0
  137. package/dist/motion/index.d.ts +7 -3
  138. package/dist/motion/index.d.ts.map +1 -1
  139. package/dist/motion/morph.d.ts +27 -0
  140. package/dist/motion/morph.d.ts.map +1 -0
  141. package/dist/motion/parallax.d.ts +30 -0
  142. package/dist/motion/parallax.d.ts.map +1 -0
  143. package/dist/motion/reduced-motion.d.ts +36 -3
  144. package/dist/motion/reduced-motion.d.ts.map +1 -1
  145. package/dist/motion/types.d.ts +58 -0
  146. package/dist/motion/types.d.ts.map +1 -1
  147. package/dist/motion/typewriter.d.ts +31 -0
  148. package/dist/motion/typewriter.d.ts.map +1 -0
  149. package/dist/motion-qPj_TYGv.js +530 -0
  150. package/dist/motion-qPj_TYGv.js.map +1 -0
  151. package/dist/motion.es.mjs +27 -23
  152. package/dist/{view-C70lA3vf.js → mount-SM07RUa6.js} +166 -160
  153. package/dist/mount-SM07RUa6.js.map +1 -0
  154. package/dist/{object-qGpWr6-J.js → object-BCk-1c8T.js} +5 -4
  155. package/dist/{object-qGpWr6-J.js.map → object-BCk-1c8T.js.map} +1 -1
  156. package/dist/{platform-Dr9b6fsq.js → platform-CPbCprb6.js} +21 -22
  157. package/dist/{platform-Dr9b6fsq.js.map → platform-CPbCprb6.js.map} +1 -1
  158. package/dist/platform.es.mjs +2 -2
  159. package/dist/plugin/index.d.ts +22 -0
  160. package/dist/plugin/index.d.ts.map +1 -0
  161. package/dist/plugin/registry.d.ts +108 -0
  162. package/dist/plugin/registry.d.ts.map +1 -0
  163. package/dist/plugin/types.d.ts +110 -0
  164. package/dist/plugin/types.d.ts.map +1 -0
  165. package/dist/plugin-cPoOHFLY.js +64 -0
  166. package/dist/plugin-cPoOHFLY.js.map +1 -0
  167. package/dist/plugin.es.mjs +9 -0
  168. package/dist/reactive/computed.d.ts +7 -0
  169. package/dist/reactive/computed.d.ts.map +1 -1
  170. package/dist/reactive-Cfv0RK6x.js +233 -0
  171. package/dist/reactive-Cfv0RK6x.js.map +1 -0
  172. package/dist/reactive.es.mjs +19 -20
  173. package/dist/registry-CWf368tT.js +26 -0
  174. package/dist/registry-CWf368tT.js.map +1 -0
  175. package/dist/router/bq-link.d.ts +112 -0
  176. package/dist/router/bq-link.d.ts.map +1 -0
  177. package/dist/router/constraints.d.ts +9 -0
  178. package/dist/router/constraints.d.ts.map +1 -0
  179. package/dist/router/index.d.ts +14 -6
  180. package/dist/router/index.d.ts.map +1 -1
  181. package/dist/router/match.d.ts +0 -1
  182. package/dist/router/match.d.ts.map +1 -1
  183. package/dist/router/path-pattern.d.ts +14 -0
  184. package/dist/router/path-pattern.d.ts.map +1 -0
  185. package/dist/router/query.d.ts.map +1 -1
  186. package/dist/router/router.d.ts +3 -1
  187. package/dist/router/router.d.ts.map +1 -1
  188. package/dist/router/types.d.ts +48 -4
  189. package/dist/router/types.d.ts.map +1 -1
  190. package/dist/router/use-route.d.ts +50 -0
  191. package/dist/router/use-route.d.ts.map +1 -0
  192. package/dist/router/utils.d.ts +3 -0
  193. package/dist/router/utils.d.ts.map +1 -1
  194. package/dist/router-BrthaP_z.js +473 -0
  195. package/dist/router-BrthaP_z.js.map +1 -0
  196. package/dist/router.es.mjs +13 -10
  197. package/dist/{sanitize-Bs2dkMby.js → sanitize-B1V4JswB.js} +2 -1
  198. package/dist/{sanitize-Bs2dkMby.js.map → sanitize-B1V4JswB.js.map} +1 -1
  199. package/dist/security/index.d.ts +2 -2
  200. package/dist/security/index.d.ts.map +1 -1
  201. package/dist/security.es.mjs +1 -1
  202. package/dist/ssr/hydrate.d.ts +65 -0
  203. package/dist/ssr/hydrate.d.ts.map +1 -0
  204. package/dist/ssr/index.d.ts +59 -0
  205. package/dist/ssr/index.d.ts.map +1 -0
  206. package/dist/ssr/render.d.ts +62 -0
  207. package/dist/ssr/render.d.ts.map +1 -0
  208. package/dist/ssr/serialize.d.ts +118 -0
  209. package/dist/ssr/serialize.d.ts.map +1 -0
  210. package/dist/ssr/types.d.ts +70 -0
  211. package/dist/ssr/types.d.ts.map +1 -0
  212. package/dist/ssr-B2qd_WBB.js +248 -0
  213. package/dist/ssr-B2qd_WBB.js.map +1 -0
  214. package/dist/ssr.es.mjs +9 -0
  215. package/dist/store/create-store.d.ts.map +1 -1
  216. package/dist/store/index.d.ts +1 -1
  217. package/dist/store/index.d.ts.map +1 -1
  218. package/dist/store/persisted.d.ts +38 -4
  219. package/dist/store/persisted.d.ts.map +1 -1
  220. package/dist/store/types.d.ts +138 -1
  221. package/dist/store/types.d.ts.map +1 -1
  222. package/dist/store/utils.d.ts +2 -2
  223. package/dist/store/utils.d.ts.map +1 -1
  224. package/dist/store-DWpyH6p5.js +338 -0
  225. package/dist/store-DWpyH6p5.js.map +1 -0
  226. package/dist/store.es.mjs +11 -10
  227. package/dist/storybook/index.d.ts.map +1 -1
  228. package/dist/storybook.es.mjs +1 -1
  229. package/dist/storybook.es.mjs.map +1 -1
  230. package/dist/testing/index.d.ts +23 -0
  231. package/dist/testing/index.d.ts.map +1 -0
  232. package/dist/testing/testing.d.ts +156 -0
  233. package/dist/testing/testing.d.ts.map +1 -0
  234. package/dist/testing/types.d.ts +134 -0
  235. package/dist/testing/types.d.ts.map +1 -0
  236. package/dist/testing-CsqjNUyy.js +224 -0
  237. package/dist/testing-CsqjNUyy.js.map +1 -0
  238. package/dist/testing.es.mjs +9 -0
  239. package/dist/type-guards-Do9DWgNp.js +44 -0
  240. package/dist/type-guards-Do9DWgNp.js.map +1 -0
  241. package/dist/untrack-DJVQQ2WM.js +33 -0
  242. package/dist/untrack-DJVQQ2WM.js.map +1 -0
  243. package/dist/view/custom-directives.d.ts +20 -0
  244. package/dist/view/custom-directives.d.ts.map +1 -0
  245. package/dist/view/evaluate.d.ts.map +1 -1
  246. package/dist/view/process.d.ts.map +1 -1
  247. package/dist/view.es.mjs +9 -9
  248. package/package.json +177 -141
  249. package/src/a11y/announce.ts +131 -0
  250. package/src/a11y/audit.ts +314 -0
  251. package/src/a11y/index.ts +68 -0
  252. package/src/a11y/media-preferences.ts +255 -0
  253. package/src/a11y/roving-tab-index.ts +164 -0
  254. package/src/a11y/skip-link.ts +255 -0
  255. package/src/a11y/trap-focus.ts +184 -0
  256. package/src/a11y/types.ts +183 -0
  257. package/src/component/component.ts +104 -29
  258. package/src/component/html.ts +5 -5
  259. package/src/component/index.ts +2 -0
  260. package/src/component/library.ts +26 -2
  261. package/src/component/scope.ts +212 -0
  262. package/src/component/types.ts +94 -40
  263. package/src/core/collection.ts +707 -628
  264. package/src/core/element.ts +981 -774
  265. package/src/core/env.ts +60 -0
  266. package/src/core/index.ts +49 -48
  267. package/src/core/shared.ts +62 -13
  268. package/src/core/utils/index.ts +148 -83
  269. package/src/devtools/devtools.ts +410 -0
  270. package/src/devtools/index.ts +48 -0
  271. package/src/devtools/types.ts +104 -0
  272. package/src/dnd/draggable.ts +296 -0
  273. package/src/dnd/droppable.ts +228 -0
  274. package/src/dnd/index.ts +62 -0
  275. package/src/dnd/sortable.ts +307 -0
  276. package/src/dnd/types.ts +293 -0
  277. package/src/forms/create-form.ts +278 -0
  278. package/src/forms/index.ts +65 -0
  279. package/src/forms/types.ts +154 -0
  280. package/src/forms/validators.ts +265 -0
  281. package/src/full.ts +253 -2
  282. package/src/i18n/formatting.ts +67 -0
  283. package/src/i18n/i18n.ts +200 -0
  284. package/src/i18n/index.ts +67 -0
  285. package/src/i18n/translate.ts +182 -0
  286. package/src/i18n/types.ts +171 -0
  287. package/src/index.ts +108 -36
  288. package/src/media/battery.ts +116 -0
  289. package/src/media/breakpoints.ts +131 -0
  290. package/src/media/clipboard.ts +80 -0
  291. package/src/media/device-sensors.ts +158 -0
  292. package/src/media/geolocation.ts +119 -0
  293. package/src/media/index.ts +76 -0
  294. package/src/media/media-query.ts +92 -0
  295. package/src/media/network.ts +115 -0
  296. package/src/media/types.ts +177 -0
  297. package/src/media/viewport.ts +84 -0
  298. package/src/motion/index.ts +57 -48
  299. package/src/motion/morph.ts +151 -0
  300. package/src/motion/parallax.ts +120 -0
  301. package/src/motion/reduced-motion.ts +66 -17
  302. package/src/motion/types.ts +271 -208
  303. package/src/motion/typewriter.ts +164 -0
  304. package/src/plugin/index.ts +37 -0
  305. package/src/plugin/registry.ts +269 -0
  306. package/src/plugin/types.ts +137 -0
  307. package/src/reactive/computed.ts +130 -92
  308. package/src/router/bq-link.ts +279 -0
  309. package/src/router/constraints.ts +201 -0
  310. package/src/router/index.ts +49 -41
  311. package/src/router/match.ts +312 -106
  312. package/src/router/path-pattern.ts +52 -0
  313. package/src/router/query.ts +38 -35
  314. package/src/router/router.ts +402 -211
  315. package/src/router/types.ts +139 -93
  316. package/src/router/use-route.ts +68 -0
  317. package/src/router/utils.ts +157 -116
  318. package/src/security/index.ts +2 -7
  319. package/src/security/sanitize.ts +70 -70
  320. package/src/security/trusted-html.ts +71 -71
  321. package/src/ssr/hydrate.ts +82 -0
  322. package/src/ssr/index.ts +70 -0
  323. package/src/ssr/render.ts +508 -0
  324. package/src/ssr/serialize.ts +296 -0
  325. package/src/ssr/types.ts +81 -0
  326. package/src/store/create-store.ts +467 -329
  327. package/src/store/define-store.ts +49 -49
  328. package/src/store/index.ts +27 -22
  329. package/src/store/mapping.ts +74 -74
  330. package/src/store/persisted.ts +206 -19
  331. package/src/store/types.ts +157 -2
  332. package/src/store/utils.ts +135 -141
  333. package/src/store/watch.ts +53 -53
  334. package/src/storybook/index.ts +2 -1
  335. package/src/testing/index.ts +42 -0
  336. package/src/testing/testing.ts +593 -0
  337. package/src/testing/types.ts +170 -0
  338. package/src/view/custom-directives.ts +30 -0
  339. package/src/view/evaluate.ts +292 -290
  340. package/src/view/process.ts +108 -92
  341. package/dist/component-BEQgt5hl.js +0 -600
  342. package/dist/component-BEQgt5hl.js.map +0 -1
  343. package/dist/core-BGQJVw0-.js +0 -35
  344. package/dist/core-BGQJVw0-.js.map +0 -1
  345. package/dist/core-CCEabVHl.js +0 -648
  346. package/dist/core-CCEabVHl.js.map +0 -1
  347. package/dist/effect-AFRW_Plg.js +0 -84
  348. package/dist/effect-AFRW_Plg.js.map +0 -1
  349. package/dist/motion-D9TcHxOF.js +0 -415
  350. package/dist/motion-D9TcHxOF.js.map +0 -1
  351. package/dist/reactive-DSkct0dO.js +0 -254
  352. package/dist/reactive-DSkct0dO.js.map +0 -1
  353. package/dist/router-CbDhl8rS.js +0 -188
  354. package/dist/router-CbDhl8rS.js.map +0 -1
  355. package/dist/store-BwDvI45q.js +0 -263
  356. package/dist/store-BwDvI45q.js.map +0 -1
  357. package/dist/untrack-B0rVscTc.js +0 -7
  358. package/dist/untrack-B0rVscTc.js.map +0 -1
  359. package/dist/view-C70lA3vf.js.map +0 -1
@@ -1,329 +1,467 @@
1
- /**
2
- * Store creation logic.
3
- */
4
-
5
- import {
6
- batch,
7
- computed,
8
- signal,
9
- untrack,
10
- type ReadonlySignal,
11
- type Signal,
12
- } from '../reactive/index';
13
- import { notifyDevtoolsStateChange, registerDevtoolsStore } from './devtools';
14
- import { applyPlugins } from './plugins';
15
- import { getStore, hasStore, registerStore } from './registry';
16
- import type { Getters, Store, StoreDefinition, StoreSubscriber } from './types';
17
- import { deepClone, detectNestedMutations, isDev } from './utils';
18
-
19
- /**
20
- * Creates a reactive store with state, getters, and actions.
21
- *
22
- * @template S - State type
23
- * @template G - Getters type
24
- * @template A - Actions type
25
- * @param definition - Store definition
26
- * @returns The reactive store instance
27
- */
28
- export const createStore = <
29
- S extends Record<string, unknown>,
30
- G extends Record<string, unknown> = Record<string, never>,
31
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
- A extends Record<string, (...args: any[]) => any> = Record<string, never>,
33
- >(
34
- definition: StoreDefinition<S, G, A>
35
- ): Store<S, G, A> => {
36
- const { id, state: stateFactory, getters = {} as Getters<S, G>, actions = {} as A } = definition;
37
-
38
- // Check for duplicate store IDs
39
- if (hasStore(id)) {
40
- console.warn(`bQuery store: Store "${id}" already exists. Returning existing instance.`);
41
- return getStore(id) as Store<S, G, A>;
42
- }
43
-
44
- // Create initial state
45
- const initialState = stateFactory();
46
-
47
- // Create signals for each state property
48
- const stateSignals = new Map<keyof S, Signal<unknown>>();
49
- for (const key of Object.keys(initialState) as Array<keyof S>) {
50
- stateSignals.set(key, signal(initialState[key]));
51
- }
52
-
53
- // Subscribers for $subscribe
54
- const subscribers: Array<StoreSubscriber<S>> = [];
55
-
56
- /**
57
- * Gets the current state.
58
- *
59
- * For subscriber notifications (where a plain object snapshot is needed),
60
- * this creates a shallow copy. For internal reads, use stateProxy directly.
61
- *
62
- * **Note:** Returns a shallow snapshot. Nested object mutations will NOT
63
- * trigger reactive updates. This differs from frameworks like Pinia that
64
- * use deep reactivity. To update nested state, replace the entire object.
65
- *
66
- * Uses `untrack()` to prevent accidental dependency tracking when called
67
- * from within reactive contexts (e.g., `effect()` or `computed()`).
68
- *
69
- * @internal
70
- */
71
- const getCurrentState = (): S =>
72
- untrack(() => {
73
- return { ...stateProxy };
74
- });
75
-
76
- /**
77
- * Notifies subscribers of state changes.
78
- * Short-circuits if there are no subscribers and devtools aren't active
79
- * to avoid unnecessary snapshot overhead.
80
- * @internal
81
- */
82
- const notifySubscribers = (): void => {
83
- // Early return if no subscribers and no devtools hook
84
- const hasDevtools =
85
- typeof window !== 'undefined' &&
86
- typeof window.__BQUERY_DEVTOOLS__?.onStateChange === 'function';
87
- if (subscribers.length === 0 && !hasDevtools) {
88
- return;
89
- }
90
-
91
- const currentState = getCurrentState();
92
- for (const callback of subscribers) {
93
- callback(currentState);
94
- }
95
-
96
- notifyDevtoolsStateChange(id, currentState);
97
- };
98
-
99
- /**
100
- * Cached state proxy that lazily reads signal values.
101
- * Uses a Proxy to avoid creating new objects on each access.
102
- *
103
- * **Note:** This returns a shallow snapshot of the state. Nested object
104
- * mutations will NOT trigger reactive updates. For nested reactivity,
105
- * replace the entire object or use signals for nested properties.
106
- *
107
- * @internal
108
- */
109
- const stateProxy = new Proxy({} as S, {
110
- get: (_, prop: string | symbol) => {
111
- const key = prop as keyof S;
112
- if (stateSignals.has(key)) {
113
- return stateSignals.get(key)!.value;
114
- }
115
- return undefined;
116
- },
117
- ownKeys: () => Array.from(stateSignals.keys()) as string[],
118
- getOwnPropertyDescriptor: (_, prop) => {
119
- if (stateSignals.has(prop as keyof S)) {
120
- return { enumerable: true, configurable: true };
121
- }
122
- return undefined;
123
- },
124
- has: (_, prop) => stateSignals.has(prop as keyof S),
125
- });
126
-
127
- // Create computed getters
128
- const getterComputed = new Map<keyof G, ReadonlySignal<unknown>>();
129
-
130
- // Build the store proxy
131
- const store = {} as Store<S, G, A>;
132
-
133
- // Define state properties with getters/setters
134
- for (const key of Object.keys(initialState) as Array<keyof S>) {
135
- Object.defineProperty(store, key, {
136
- get: () => stateSignals.get(key)!.value,
137
- set: (value: unknown) => {
138
- stateSignals.get(key)!.value = value;
139
- notifySubscribers();
140
- },
141
- enumerable: true,
142
- configurable: false,
143
- });
144
- }
145
-
146
- // Define getters as computed properties
147
- for (const key of Object.keys(getters) as Array<keyof G>) {
148
- const getterFn = getters[key];
149
-
150
- // Create computed that reads from state signals via proxy (more efficient)
151
- const computedGetter = computed(() => {
152
- const state = stateProxy;
153
- // For getter dependencies, pass a proxy that reads from computed getters
154
- const getterProxy = new Proxy({} as G, {
155
- get: (_, prop: string | symbol) => {
156
- const propKey = prop as keyof G;
157
- if (getterComputed.has(propKey)) {
158
- return getterComputed.get(propKey)!.value;
159
- }
160
- return undefined;
161
- },
162
- });
163
- return getterFn(state, getterProxy);
164
- });
165
-
166
- getterComputed.set(key, computedGetter as unknown as ReadonlySignal<unknown>);
167
-
168
- Object.defineProperty(store, key, {
169
- get: () => computedGetter.value,
170
- enumerable: true,
171
- configurable: false,
172
- });
173
- }
174
-
175
- // Bind actions to the store context
176
- for (const key of Object.keys(actions) as Array<keyof A>) {
177
- const actionFn = actions[key];
178
-
179
- // Wrap action to enable 'this' binding
180
- (store as Record<string, unknown>)[key as string] = function (...args: unknown[]) {
181
- // Create a context that allows 'this.property' access
182
- const context = new Proxy(store, {
183
- get: (target, prop) => {
184
- if (typeof prop === 'string' && stateSignals.has(prop as keyof S)) {
185
- return stateSignals.get(prop as keyof S)!.value;
186
- }
187
- return (target as Record<string, unknown>)[prop as string];
188
- },
189
- set: (target, prop, value) => {
190
- if (typeof prop === 'string' && stateSignals.has(prop as keyof S)) {
191
- stateSignals.get(prop as keyof S)!.value = value;
192
- notifySubscribers();
193
- return true;
194
- }
195
- // Allow non-state property assignments (e.g., temporary variables in actions)
196
- // by delegating to the target object rather than returning false
197
- return Reflect.set(target, prop, value);
198
- },
199
- });
200
-
201
- return actionFn.apply(context, args);
202
- };
203
- }
204
-
205
- // Add store utility methods
206
- Object.defineProperties(store, {
207
- $id: {
208
- value: id,
209
- writable: false,
210
- enumerable: false,
211
- },
212
- $reset: {
213
- value: () => {
214
- const fresh = stateFactory();
215
- batch(() => {
216
- for (const [key, sig] of stateSignals) {
217
- sig.value = fresh[key];
218
- }
219
- });
220
- notifySubscribers();
221
- },
222
- writable: false,
223
- enumerable: false,
224
- },
225
- $subscribe: {
226
- value: (callback: StoreSubscriber<S>) => {
227
- subscribers.push(callback);
228
- return () => {
229
- const index = subscribers.indexOf(callback);
230
- if (index > -1) subscribers.splice(index, 1);
231
- };
232
- },
233
- writable: false,
234
- enumerable: false,
235
- },
236
- $patch: {
237
- value: (partial: Partial<S> | ((state: S) => void)) => {
238
- batch(() => {
239
- if (typeof partial === 'function') {
240
- // Capture state before mutation for nested mutation detection
241
- const stateBefore = isDev ? deepClone(getCurrentState()) : null;
242
- const signalValuesBefore = isDev
243
- ? new Map(Array.from(stateSignals.entries()).map(([k, s]) => [k, s.value]))
244
- : null;
245
-
246
- // Mutation function
247
- const state = getCurrentState();
248
- partial(state);
249
-
250
- // Detect nested mutations in development mode
251
- if (isDev && stateBefore && signalValuesBefore) {
252
- const mutatedKeys = detectNestedMutations(stateBefore, state, signalValuesBefore);
253
- if (mutatedKeys.length > 0) {
254
- console.warn(
255
- `[bQuery store "${id}"] Nested mutation detected in $patch() for keys: ${mutatedKeys
256
- .map(String)
257
- .join(', ')}.\n` +
258
- 'Nested object mutations do not trigger reactive updates because the store uses shallow reactivity.\n' +
259
- 'To fix this, either:\n' +
260
- ' 1. Replace the entire object: state.user = { ...state.user, name: "New" }\n' +
261
- ' 2. Use $patchDeep() for automatic deep cloning\n' +
262
- 'See: https://bquery.dev/guide/store#deep-reactivity'
263
- );
264
- }
265
- }
266
-
267
- for (const [key, value] of Object.entries(state) as Array<[keyof S, unknown]>) {
268
- if (stateSignals.has(key)) {
269
- stateSignals.get(key)!.value = value;
270
- }
271
- }
272
- } else {
273
- // Partial object
274
- for (const [key, value] of Object.entries(partial) as Array<[keyof S, unknown]>) {
275
- if (stateSignals.has(key)) {
276
- stateSignals.get(key)!.value = value;
277
- }
278
- }
279
- }
280
- });
281
- notifySubscribers();
282
- },
283
- writable: false,
284
- enumerable: false,
285
- },
286
- $patchDeep: {
287
- value: (partial: Partial<S> | ((state: S) => void)) => {
288
- batch(() => {
289
- if (typeof partial === 'function') {
290
- // Deep clone state before mutation to ensure new references
291
- const state = deepClone(getCurrentState());
292
- partial(state);
293
-
294
- for (const [key, value] of Object.entries(state) as Array<[keyof S, unknown]>) {
295
- if (stateSignals.has(key)) {
296
- stateSignals.get(key)!.value = value;
297
- }
298
- }
299
- } else {
300
- // Deep clone each value in partial to ensure new references
301
- for (const [key, value] of Object.entries(partial) as Array<[keyof S, unknown]>) {
302
- if (stateSignals.has(key)) {
303
- stateSignals.get(key)!.value = deepClone(value);
304
- }
305
- }
306
- }
307
- });
308
- notifySubscribers();
309
- },
310
- writable: false,
311
- enumerable: false,
312
- },
313
- $state: {
314
- get: () => getCurrentState(),
315
- enumerable: false,
316
- },
317
- });
318
-
319
- // Register store
320
- registerStore(id, store);
321
-
322
- // Apply plugins
323
- applyPlugins(store, definition);
324
-
325
- // Notify devtools
326
- registerDevtoolsStore(id, store);
327
-
328
- return store;
329
- };
1
+ /**
2
+ * Store creation logic.
3
+ */
4
+
5
+ import { isPromise } from '../core/utils/type-guards';
6
+ import {
7
+ batch,
8
+ computed,
9
+ signal,
10
+ untrack,
11
+ type ReadonlySignal,
12
+ type Signal,
13
+ } from '../reactive/index';
14
+ import { notifyDevtoolsStateChange, registerDevtoolsStore } from './devtools';
15
+ import { applyPlugins } from './plugins';
16
+ import { getStore, hasStore, registerStore } from './registry';
17
+ import type {
18
+ ActionContext,
19
+ Getters,
20
+ OnActionCallback,
21
+ Store,
22
+ StoreDefinition,
23
+ StoreSubscriber,
24
+ } from './types';
25
+ import { deepClone, detectNestedMutations, isDev } from './utils';
26
+
27
+ /**
28
+ * Creates a reactive store with state, getters, and actions.
29
+ *
30
+ * @template S - State type
31
+ * @template G - Getters type
32
+ * @template A - Actions type
33
+ * @param definition - Store definition
34
+ * @returns The reactive store instance
35
+ */
36
+ export const createStore = <
37
+ S extends Record<string, unknown>,
38
+ G extends Record<string, unknown> = Record<string, never>,
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ A extends Record<string, (...args: any[]) => any> = Record<string, never>,
41
+ >(
42
+ definition: StoreDefinition<S, G, A>
43
+ ): Store<S, G, A> => {
44
+ const { id, state: stateFactory, getters = {} as Getters<S, G>, actions = {} as A } = definition;
45
+
46
+ // Check for duplicate store IDs
47
+ if (hasStore(id)) {
48
+ console.warn(`bQuery store: Store "${id}" already exists. Returning existing instance.`);
49
+ return getStore(id) as Store<S, G, A>;
50
+ }
51
+
52
+ // Create initial state
53
+ const initialState = stateFactory();
54
+
55
+ // Create signals for each state property
56
+ const stateSignals = new Map<keyof S, Signal<unknown>>();
57
+ for (const key of Object.keys(initialState) as Array<keyof S>) {
58
+ stateSignals.set(key, signal(initialState[key]));
59
+ }
60
+
61
+ // Subscribers for $subscribe
62
+ const subscribers: Array<StoreSubscriber<S>> = [];
63
+
64
+ // Action lifecycle hooks for $onAction
65
+ const actionListeners: Array<OnActionCallback<S, G, A>> = [];
66
+
67
+ const reportOnActionError = (
68
+ phase: 'listener' | 'after' | 'onError',
69
+ actionName: string,
70
+ error: unknown
71
+ ): void => {
72
+ if (!isDev() || typeof console === 'undefined' || typeof console.error !== 'function') return;
73
+ console.error(
74
+ `[bQuery store "${id}"] Error in $onAction ${phase} for action "${actionName}"`,
75
+ error
76
+ );
77
+ };
78
+
79
+ const warnedAsyncOnActionListeners = new WeakSet<OnActionCallback<S, G, A>>();
80
+
81
+ const warnAsyncOnActionListener = (
82
+ listener: OnActionCallback<S, G, A>,
83
+ actionName: string
84
+ ): void => {
85
+ if (!isDev() || typeof console === 'undefined' || typeof console.warn !== 'function') return;
86
+ if (warnedAsyncOnActionListeners.has(listener)) return;
87
+ warnedAsyncOnActionListeners.add(listener);
88
+ console.warn(
89
+ `[bQuery store "${id}"] Async $onAction listener detected for action "${actionName}". If it awaits, register after()/onError() before the first await; late registrations will not affect the current action.`
90
+ );
91
+ };
92
+
93
+ /**
94
+ * Executes an action observer callback without allowing observer failures to
95
+ * affect the action result. Handles both synchronous exceptions and async
96
+ * rejections, routing all failures through the standard $onAction logger.
97
+ *
98
+ * @internal
99
+ */
100
+ const runOnActionCallback = (
101
+ phase: 'listener' | 'after' | 'onError',
102
+ actionName: string,
103
+ callback: () => unknown,
104
+ listener?: OnActionCallback<S, G, A>
105
+ ): void => {
106
+ try {
107
+ const result = callback();
108
+ if (isPromise(result)) {
109
+ if (phase === 'listener' && listener) {
110
+ warnAsyncOnActionListener(listener, actionName);
111
+ }
112
+ void result.catch((error) => {
113
+ reportOnActionError(phase, actionName, error);
114
+ });
115
+ }
116
+ } catch (error) {
117
+ reportOnActionError(phase, actionName, error);
118
+ }
119
+ };
120
+
121
+ /**
122
+ * Gets the current state.
123
+ *
124
+ * For subscriber notifications (where a plain object snapshot is needed),
125
+ * this creates a shallow copy. For internal reads, use stateProxy directly.
126
+ *
127
+ * **Note:** Returns a shallow snapshot. Nested object mutations will NOT
128
+ * trigger reactive updates. This differs from frameworks like Pinia that
129
+ * use deep reactivity. To update nested state, replace the entire object.
130
+ *
131
+ * Uses `untrack()` to prevent accidental dependency tracking when called
132
+ * from within reactive contexts (e.g., `effect()` or `computed()`).
133
+ *
134
+ * @internal
135
+ */
136
+ const getCurrentState = (): S =>
137
+ untrack(() => {
138
+ return { ...stateProxy };
139
+ });
140
+
141
+ /**
142
+ * Notifies subscribers of state changes.
143
+ * Short-circuits if there are no subscribers and devtools aren't active
144
+ * to avoid unnecessary snapshot overhead.
145
+ * @internal
146
+ */
147
+ const notifySubscribers = (): void => {
148
+ // Early return if no subscribers and no devtools hook
149
+ const hasDevtools =
150
+ typeof window !== 'undefined' &&
151
+ typeof window.__BQUERY_DEVTOOLS__?.onStateChange === 'function';
152
+ if (subscribers.length === 0 && !hasDevtools) {
153
+ return;
154
+ }
155
+
156
+ const currentState = getCurrentState();
157
+ for (const callback of subscribers) {
158
+ callback(currentState);
159
+ }
160
+
161
+ notifyDevtoolsStateChange(id, currentState);
162
+ };
163
+
164
+ /**
165
+ * Cached state proxy that lazily reads signal values.
166
+ * Uses a Proxy to avoid creating new objects on each access.
167
+ *
168
+ * **Note:** This returns a shallow snapshot of the state. Nested object
169
+ * mutations will NOT trigger reactive updates. For nested reactivity,
170
+ * replace the entire object or use signals for nested properties.
171
+ *
172
+ * @internal
173
+ */
174
+ const stateProxy = new Proxy({} as S, {
175
+ get: (_, prop: string | symbol) => {
176
+ const key = prop as keyof S;
177
+ if (stateSignals.has(key)) {
178
+ return stateSignals.get(key)!.value;
179
+ }
180
+ return undefined;
181
+ },
182
+ ownKeys: () => Array.from(stateSignals.keys()) as string[],
183
+ getOwnPropertyDescriptor: (_, prop) => {
184
+ if (stateSignals.has(prop as keyof S)) {
185
+ return { enumerable: true, configurable: true };
186
+ }
187
+ return undefined;
188
+ },
189
+ has: (_, prop) => stateSignals.has(prop as keyof S),
190
+ });
191
+
192
+ // Create computed getters
193
+ const getterComputed = new Map<keyof G, ReadonlySignal<unknown>>();
194
+
195
+ // Build the store proxy
196
+ const store = {} as Store<S, G, A>;
197
+
198
+ // Define state properties with getters/setters
199
+ for (const key of Object.keys(initialState) as Array<keyof S>) {
200
+ Object.defineProperty(store, key, {
201
+ get: () => stateSignals.get(key)!.value,
202
+ set: (value: unknown) => {
203
+ stateSignals.get(key)!.value = value;
204
+ notifySubscribers();
205
+ },
206
+ enumerable: true,
207
+ configurable: false,
208
+ });
209
+ }
210
+
211
+ // Define getters as computed properties
212
+ for (const key of Object.keys(getters) as Array<keyof G>) {
213
+ const getterFn = getters[key];
214
+
215
+ // Create computed that reads from state signals via proxy (more efficient)
216
+ const computedGetter = computed(() => {
217
+ const state = stateProxy;
218
+ // For getter dependencies, pass a proxy that reads from computed getters
219
+ const getterProxy = new Proxy({} as G, {
220
+ get: (_, prop: string | symbol) => {
221
+ const propKey = prop as keyof G;
222
+ if (getterComputed.has(propKey)) {
223
+ return getterComputed.get(propKey)!.value;
224
+ }
225
+ return undefined;
226
+ },
227
+ });
228
+ return getterFn(state, getterProxy);
229
+ });
230
+
231
+ getterComputed.set(key, computedGetter as unknown as ReadonlySignal<unknown>);
232
+
233
+ Object.defineProperty(store, key, {
234
+ get: () => computedGetter.value,
235
+ enumerable: true,
236
+ configurable: false,
237
+ });
238
+ }
239
+
240
+ // Bind actions to the store context, with $onAction lifecycle support
241
+ for (const key of Object.keys(actions) as Array<keyof A>) {
242
+ const actionFn = actions[key];
243
+ const actionName = key as keyof A & string;
244
+
245
+ // Wrap action to enable 'this' binding and $onAction hooks
246
+ (store as Record<string, unknown>)[actionName] = function (...args: unknown[]) {
247
+ // Create a context that allows 'this.property' access
248
+ const context = new Proxy(store, {
249
+ get: (target, prop) => {
250
+ if (typeof prop === 'string' && stateSignals.has(prop as keyof S)) {
251
+ return stateSignals.get(prop as keyof S)!.value;
252
+ }
253
+ return (target as Record<string, unknown>)[prop as string];
254
+ },
255
+ set: (target, prop, value) => {
256
+ if (typeof prop === 'string' && stateSignals.has(prop as keyof S)) {
257
+ stateSignals.get(prop as keyof S)!.value = value;
258
+ notifySubscribers();
259
+ return true;
260
+ }
261
+ // Allow non-state property assignments (e.g., temporary variables in actions)
262
+ // by delegating to the target object rather than returning false
263
+ return Reflect.set(target, prop, value);
264
+ },
265
+ });
266
+
267
+ // Run $onAction hooks if any listeners are registered
268
+ if (actionListeners.length === 0) {
269
+ return actionFn.apply(context, args);
270
+ }
271
+
272
+ const afterHooks: Array<(result: unknown) => void> = [];
273
+ const errorHooks: Array<(error: unknown) => void> = [];
274
+ const listenerSnapshot = [...actionListeners];
275
+
276
+ const listenerContext = {
277
+ name: actionName,
278
+ store,
279
+ args: args as Parameters<A[typeof actionName]>,
280
+ after: (callback: (result: Awaited<ReturnType<A[typeof actionName]>>) => void) => {
281
+ afterHooks.push((result) =>
282
+ callback(result as Awaited<ReturnType<A[typeof actionName]>>)
283
+ );
284
+ },
285
+ onError: (callback: (error: unknown) => void) => {
286
+ errorHooks.push(callback);
287
+ },
288
+ } satisfies ActionContext<S, G, A, typeof actionName>;
289
+
290
+ // Notify all action listeners (before phase)
291
+ for (const listener of listenerSnapshot) {
292
+ runOnActionCallback('listener', actionName, () => listener(listenerContext), listener);
293
+ }
294
+
295
+ let result: unknown;
296
+ try {
297
+ result = actionFn.apply(context, args);
298
+ } catch (error) {
299
+ for (const hook of errorHooks) {
300
+ runOnActionCallback('onError', actionName, () => hook(error));
301
+ }
302
+ throw error;
303
+ }
304
+
305
+ // Handle async actions (promises)
306
+ if (isPromise(result)) {
307
+ return result.then(
308
+ (resolved) => {
309
+ for (const hook of afterHooks) {
310
+ runOnActionCallback('after', actionName, () => hook(resolved));
311
+ }
312
+ return resolved;
313
+ },
314
+ (error) => {
315
+ for (const hook of errorHooks) {
316
+ runOnActionCallback('onError', actionName, () => hook(error));
317
+ }
318
+ throw error;
319
+ }
320
+ );
321
+ }
322
+
323
+ // Sync action — run after hooks immediately
324
+ for (const hook of afterHooks) {
325
+ runOnActionCallback('after', actionName, () => hook(result));
326
+ }
327
+ return result;
328
+ };
329
+ }
330
+
331
+ // Add store utility methods
332
+ Object.defineProperties(store, {
333
+ $id: {
334
+ value: id,
335
+ writable: false,
336
+ enumerable: false,
337
+ },
338
+ $reset: {
339
+ value: () => {
340
+ const fresh = stateFactory();
341
+ batch(() => {
342
+ for (const [key, sig] of stateSignals) {
343
+ sig.value = fresh[key];
344
+ }
345
+ });
346
+ notifySubscribers();
347
+ },
348
+ writable: false,
349
+ enumerable: false,
350
+ },
351
+ $subscribe: {
352
+ value: (callback: StoreSubscriber<S>) => {
353
+ subscribers.push(callback);
354
+ return () => {
355
+ const index = subscribers.indexOf(callback);
356
+ if (index > -1) subscribers.splice(index, 1);
357
+ };
358
+ },
359
+ writable: false,
360
+ enumerable: false,
361
+ },
362
+ $onAction: {
363
+ value: (callback: OnActionCallback<S, G, A>) => {
364
+ actionListeners.push(callback);
365
+ return () => {
366
+ const index = actionListeners.indexOf(callback);
367
+ if (index > -1) actionListeners.splice(index, 1);
368
+ };
369
+ },
370
+ writable: false,
371
+ enumerable: false,
372
+ },
373
+ $patch: {
374
+ value: (partial: Partial<S> | ((state: S) => void)) => {
375
+ batch(() => {
376
+ if (typeof partial === 'function') {
377
+ // Capture state before mutation for nested mutation detection
378
+ const devMode = isDev();
379
+ const stateBefore = devMode ? deepClone(getCurrentState()) : null;
380
+ const signalValuesBefore = devMode
381
+ ? new Map(Array.from(stateSignals.entries()).map(([k, s]) => [k, s.value]))
382
+ : null;
383
+
384
+ // Mutation function
385
+ const state = getCurrentState();
386
+ partial(state);
387
+
388
+ // Detect nested mutations in development mode
389
+ if (devMode && stateBefore && signalValuesBefore) {
390
+ const mutatedKeys = detectNestedMutations(stateBefore, state, signalValuesBefore);
391
+ if (mutatedKeys.length > 0) {
392
+ console.warn(
393
+ `[bQuery store "${id}"] Nested mutation detected in $patch() for keys: ${mutatedKeys
394
+ .map(String)
395
+ .join(', ')}.\n` +
396
+ 'Nested object mutations do not trigger reactive updates because the store uses shallow reactivity.\n' +
397
+ 'To fix this, either:\n' +
398
+ ' 1. Replace the entire object: state.user = { ...state.user, name: "New" }\n' +
399
+ ' 2. Use $patchDeep() for automatic deep cloning\n' +
400
+ 'See: https://bquery.dev/guide/store#deep-reactivity'
401
+ );
402
+ }
403
+ }
404
+
405
+ for (const [key, value] of Object.entries(state) as Array<[keyof S, unknown]>) {
406
+ if (stateSignals.has(key)) {
407
+ stateSignals.get(key)!.value = value;
408
+ }
409
+ }
410
+ } else {
411
+ // Partial object
412
+ for (const [key, value] of Object.entries(partial) as Array<[keyof S, unknown]>) {
413
+ if (stateSignals.has(key)) {
414
+ stateSignals.get(key)!.value = value;
415
+ }
416
+ }
417
+ }
418
+ });
419
+ notifySubscribers();
420
+ },
421
+ writable: false,
422
+ enumerable: false,
423
+ },
424
+ $patchDeep: {
425
+ value: (partial: Partial<S> | ((state: S) => void)) => {
426
+ batch(() => {
427
+ if (typeof partial === 'function') {
428
+ // Deep clone state before mutation to ensure new references
429
+ const state = deepClone(getCurrentState());
430
+ partial(state);
431
+
432
+ for (const [key, value] of Object.entries(state) as Array<[keyof S, unknown]>) {
433
+ if (stateSignals.has(key)) {
434
+ stateSignals.get(key)!.value = value;
435
+ }
436
+ }
437
+ } else {
438
+ // Deep clone each value in partial to ensure new references
439
+ for (const [key, value] of Object.entries(partial) as Array<[keyof S, unknown]>) {
440
+ if (stateSignals.has(key)) {
441
+ stateSignals.get(key)!.value = deepClone(value);
442
+ }
443
+ }
444
+ }
445
+ });
446
+ notifySubscribers();
447
+ },
448
+ writable: false,
449
+ enumerable: false,
450
+ },
451
+ $state: {
452
+ get: () => getCurrentState(),
453
+ enumerable: false,
454
+ },
455
+ });
456
+
457
+ // Register store
458
+ registerStore(id, store);
459
+
460
+ // Apply plugins
461
+ applyPlugins(store, definition);
462
+
463
+ // Notify devtools
464
+ registerDevtoolsStore(id, store);
465
+
466
+ return store;
467
+ };