@bquery/bquery 1.6.0 → 1.8.1

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 (402) hide show
  1. package/README.md +192 -18
  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-DVBCy09c.js +421 -0
  19. package/dist/a11y-DVBCy09c.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-L3-JfOFz.js +684 -0
  31. package/dist/component-L3-JfOFz.js.map +1 -0
  32. package/dist/component.es.mjs +9 -6
  33. package/dist/{config-DRmZZno3.js → config-DhT9auRm.js} +4 -4
  34. package/dist/{config-DRmZZno3.js.map → config-DhT9auRm.js.map} +1 -1
  35. package/dist/constraints-D5RHQLmP.js +100 -0
  36. package/dist/constraints-D5RHQLmP.js.map +1 -0
  37. package/dist/core/collection.d.ts +134 -0
  38. package/dist/core/collection.d.ts.map +1 -1
  39. package/dist/core/element.d.ts +120 -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 +14 -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-DdtZHzsS.js +168 -0
  50. package/dist/core-DdtZHzsS.js.map +1 -0
  51. package/dist/{core-CCEabVHl.js → core-EMYSLzaT.js} +293 -194
  52. package/dist/core-EMYSLzaT.js.map +1 -0
  53. package/dist/core.es.mjs +48 -46
  54. package/dist/custom-directives-Dr4C5lVV.js +9 -0
  55. package/dist/custom-directives-Dr4C5lVV.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-BhB2iDPT.js +122 -0
  63. package/dist/devtools-BhB2iDPT.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-NwZBYh4l.js +244 -0
  76. package/dist/dnd-NwZBYh4l.js.map +1 -0
  77. package/dist/dnd.es.mjs +6 -0
  78. package/dist/env-CTdvLaH2.js +19 -0
  79. package/dist/env-CTdvLaH2.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 +40 -0
  83. package/dist/forms/index.d.ts.map +1 -0
  84. package/dist/forms/types.d.ts +185 -0
  85. package/dist/forms/types.d.ts.map +1 -0
  86. package/dist/forms/use-field.d.ts +34 -0
  87. package/dist/forms/use-field.d.ts.map +1 -0
  88. package/dist/forms/validators.d.ts +204 -0
  89. package/dist/forms/validators.d.ts.map +1 -0
  90. package/dist/forms-UcRHsYxC.js +227 -0
  91. package/dist/forms-UcRHsYxC.js.map +1 -0
  92. package/dist/forms.es.mjs +16 -0
  93. package/dist/full.d.ts +30 -11
  94. package/dist/full.d.ts.map +1 -1
  95. package/dist/full.es.mjs +209 -93
  96. package/dist/full.iife.js +47 -31
  97. package/dist/full.iife.js.map +1 -1
  98. package/dist/full.umd.js +47 -31
  99. package/dist/full.umd.js.map +1 -1
  100. package/dist/function-Cybd57JV.js +33 -0
  101. package/dist/function-Cybd57JV.js.map +1 -0
  102. package/dist/i18n/formatting.d.ts +40 -0
  103. package/dist/i18n/formatting.d.ts.map +1 -0
  104. package/dist/i18n/i18n.d.ts +48 -0
  105. package/dist/i18n/i18n.d.ts.map +1 -0
  106. package/dist/i18n/index.d.ts +57 -0
  107. package/dist/i18n/index.d.ts.map +1 -0
  108. package/dist/i18n/translate.d.ts +83 -0
  109. package/dist/i18n/translate.d.ts.map +1 -0
  110. package/dist/i18n/types.d.ts +156 -0
  111. package/dist/i18n/types.d.ts.map +1 -0
  112. package/dist/i18n-kuF6Ekj6.js +89 -0
  113. package/dist/i18n-kuF6Ekj6.js.map +1 -0
  114. package/dist/i18n.es.mjs +6 -0
  115. package/dist/index.d.ts +11 -0
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.es.mjs +257 -143
  118. package/dist/media/battery.d.ts +35 -0
  119. package/dist/media/battery.d.ts.map +1 -0
  120. package/dist/media/breakpoints.d.ts +51 -0
  121. package/dist/media/breakpoints.d.ts.map +1 -0
  122. package/dist/media/clipboard.d.ts +30 -0
  123. package/dist/media/clipboard.d.ts.map +1 -0
  124. package/dist/media/device-sensors.d.ts +54 -0
  125. package/dist/media/device-sensors.d.ts.map +1 -0
  126. package/dist/media/geolocation.d.ts +38 -0
  127. package/dist/media/geolocation.d.ts.map +1 -0
  128. package/dist/media/index.d.ts +42 -0
  129. package/dist/media/index.d.ts.map +1 -0
  130. package/dist/media/media-query.d.ts +36 -0
  131. package/dist/media/media-query.d.ts.map +1 -0
  132. package/dist/media/network.d.ts +35 -0
  133. package/dist/media/network.d.ts.map +1 -0
  134. package/dist/media/types.d.ts +173 -0
  135. package/dist/media/types.d.ts.map +1 -0
  136. package/dist/media/viewport.d.ts +32 -0
  137. package/dist/media/viewport.d.ts.map +1 -0
  138. package/dist/media-i-fB5WxI.js +340 -0
  139. package/dist/media-i-fB5WxI.js.map +1 -0
  140. package/dist/media.es.mjs +12 -0
  141. package/dist/motion/index.d.ts +7 -3
  142. package/dist/motion/index.d.ts.map +1 -1
  143. package/dist/motion/morph.d.ts +27 -0
  144. package/dist/motion/morph.d.ts.map +1 -0
  145. package/dist/motion/parallax.d.ts +30 -0
  146. package/dist/motion/parallax.d.ts.map +1 -0
  147. package/dist/motion/reduced-motion.d.ts +36 -3
  148. package/dist/motion/reduced-motion.d.ts.map +1 -1
  149. package/dist/motion/types.d.ts +58 -0
  150. package/dist/motion/types.d.ts.map +1 -1
  151. package/dist/motion/typewriter.d.ts +31 -0
  152. package/dist/motion/typewriter.d.ts.map +1 -0
  153. package/dist/motion-BJsAuULb.js +530 -0
  154. package/dist/motion-BJsAuULb.js.map +1 -0
  155. package/dist/motion.es.mjs +27 -23
  156. package/dist/{view-C70lA3vf.js → mount-B4Y8bk8Z.js} +166 -160
  157. package/dist/mount-B4Y8bk8Z.js.map +1 -0
  158. package/dist/{object-qGpWr6-J.js → object-BCk-1c8T.js} +5 -4
  159. package/dist/{object-qGpWr6-J.js.map → object-BCk-1c8T.js.map} +1 -1
  160. package/dist/{platform-Dr9b6fsq.js → platform-Dw2gE3zI.js} +21 -22
  161. package/dist/{platform-Dr9b6fsq.js.map → platform-Dw2gE3zI.js.map} +1 -1
  162. package/dist/platform.es.mjs +2 -2
  163. package/dist/plugin/index.d.ts +22 -0
  164. package/dist/plugin/index.d.ts.map +1 -0
  165. package/dist/plugin/registry.d.ts +108 -0
  166. package/dist/plugin/registry.d.ts.map +1 -0
  167. package/dist/plugin/types.d.ts +110 -0
  168. package/dist/plugin/types.d.ts.map +1 -0
  169. package/dist/plugin-C2WuC8SF.js +66 -0
  170. package/dist/plugin-C2WuC8SF.js.map +1 -0
  171. package/dist/plugin.es.mjs +9 -0
  172. package/dist/reactive/async-data.d.ts +28 -3
  173. package/dist/reactive/async-data.d.ts.map +1 -1
  174. package/dist/reactive/computed.d.ts +10 -0
  175. package/dist/reactive/computed.d.ts.map +1 -1
  176. package/dist/reactive/effect.d.ts +3 -0
  177. package/dist/reactive/effect.d.ts.map +1 -1
  178. package/dist/reactive/http.d.ts +194 -0
  179. package/dist/reactive/http.d.ts.map +1 -0
  180. package/dist/reactive/index.d.ts +2 -2
  181. package/dist/reactive/index.d.ts.map +1 -1
  182. package/dist/reactive/pagination.d.ts +126 -0
  183. package/dist/reactive/pagination.d.ts.map +1 -0
  184. package/dist/reactive/polling.d.ts +55 -0
  185. package/dist/reactive/polling.d.ts.map +1 -0
  186. package/dist/reactive/readonly.d.ts +20 -1
  187. package/dist/reactive/readonly.d.ts.map +1 -1
  188. package/dist/reactive/rest.d.ts +293 -0
  189. package/dist/reactive/rest.d.ts.map +1 -0
  190. package/dist/reactive/scope.d.ts +140 -0
  191. package/dist/reactive/scope.d.ts.map +1 -0
  192. package/dist/reactive/signal.d.ts +16 -2
  193. package/dist/reactive/signal.d.ts.map +1 -1
  194. package/dist/reactive/to-value.d.ts +57 -0
  195. package/dist/reactive/to-value.d.ts.map +1 -0
  196. package/dist/reactive/websocket.d.ts +285 -0
  197. package/dist/reactive/websocket.d.ts.map +1 -0
  198. package/dist/reactive-DwkhUJfP.js +1148 -0
  199. package/dist/reactive-DwkhUJfP.js.map +1 -0
  200. package/dist/reactive.es.mjs +38 -20
  201. package/dist/registry-B08iilIh.js +26 -0
  202. package/dist/registry-B08iilIh.js.map +1 -0
  203. package/dist/router/bq-link.d.ts +112 -0
  204. package/dist/router/bq-link.d.ts.map +1 -0
  205. package/dist/router/constraints.d.ts +9 -0
  206. package/dist/router/constraints.d.ts.map +1 -0
  207. package/dist/router/index.d.ts +15 -7
  208. package/dist/router/index.d.ts.map +1 -1
  209. package/dist/router/match.d.ts +0 -1
  210. package/dist/router/match.d.ts.map +1 -1
  211. package/dist/router/path-pattern.d.ts +14 -0
  212. package/dist/router/path-pattern.d.ts.map +1 -0
  213. package/dist/router/query.d.ts.map +1 -1
  214. package/dist/router/router.d.ts +3 -1
  215. package/dist/router/router.d.ts.map +1 -1
  216. package/dist/router/state.d.ts +25 -2
  217. package/dist/router/state.d.ts.map +1 -1
  218. package/dist/router/types.d.ts +48 -4
  219. package/dist/router/types.d.ts.map +1 -1
  220. package/dist/router/use-route.d.ts +50 -0
  221. package/dist/router/use-route.d.ts.map +1 -0
  222. package/dist/router/utils.d.ts +3 -0
  223. package/dist/router/utils.d.ts.map +1 -1
  224. package/dist/router-CQikC9Ed.js +492 -0
  225. package/dist/router-CQikC9Ed.js.map +1 -0
  226. package/dist/router.es.mjs +14 -10
  227. package/dist/{sanitize-Bs2dkMby.js → sanitize-B1V4JswB.js} +2 -1
  228. package/dist/{sanitize-Bs2dkMby.js.map → sanitize-B1V4JswB.js.map} +1 -1
  229. package/dist/security/index.d.ts +2 -2
  230. package/dist/security/index.d.ts.map +1 -1
  231. package/dist/security.es.mjs +1 -1
  232. package/dist/ssr/hydrate.d.ts +65 -0
  233. package/dist/ssr/hydrate.d.ts.map +1 -0
  234. package/dist/ssr/index.d.ts +59 -0
  235. package/dist/ssr/index.d.ts.map +1 -0
  236. package/dist/ssr/render.d.ts +62 -0
  237. package/dist/ssr/render.d.ts.map +1 -0
  238. package/dist/ssr/serialize.d.ts +118 -0
  239. package/dist/ssr/serialize.d.ts.map +1 -0
  240. package/dist/ssr/types.d.ts +70 -0
  241. package/dist/ssr/types.d.ts.map +1 -0
  242. package/dist/ssr-_dAcGdzu.js +248 -0
  243. package/dist/ssr-_dAcGdzu.js.map +1 -0
  244. package/dist/ssr.es.mjs +9 -0
  245. package/dist/store/create-store.d.ts.map +1 -1
  246. package/dist/store/index.d.ts +1 -1
  247. package/dist/store/index.d.ts.map +1 -1
  248. package/dist/store/persisted.d.ts +38 -4
  249. package/dist/store/persisted.d.ts.map +1 -1
  250. package/dist/store/types.d.ts +138 -1
  251. package/dist/store/types.d.ts.map +1 -1
  252. package/dist/store/utils.d.ts +2 -2
  253. package/dist/store/utils.d.ts.map +1 -1
  254. package/dist/store-Cb3gPRve.js +338 -0
  255. package/dist/store-Cb3gPRve.js.map +1 -0
  256. package/dist/store.es.mjs +11 -10
  257. package/dist/storybook/index.d.ts.map +1 -1
  258. package/dist/storybook.es.mjs +1 -1
  259. package/dist/storybook.es.mjs.map +1 -1
  260. package/dist/testing/index.d.ts +23 -0
  261. package/dist/testing/index.d.ts.map +1 -0
  262. package/dist/testing/testing.d.ts +156 -0
  263. package/dist/testing/testing.d.ts.map +1 -0
  264. package/dist/testing/types.d.ts +134 -0
  265. package/dist/testing/types.d.ts.map +1 -0
  266. package/dist/testing-C5Sjfsna.js +224 -0
  267. package/dist/testing-C5Sjfsna.js.map +1 -0
  268. package/dist/testing.es.mjs +9 -0
  269. package/dist/type-guards-BMX2c0LP.js +44 -0
  270. package/dist/type-guards-BMX2c0LP.js.map +1 -0
  271. package/dist/untrack-D0fnO5k2.js +36 -0
  272. package/dist/untrack-D0fnO5k2.js.map +1 -0
  273. package/dist/view/custom-directives.d.ts +20 -0
  274. package/dist/view/custom-directives.d.ts.map +1 -0
  275. package/dist/view/evaluate.d.ts.map +1 -1
  276. package/dist/view/process.d.ts.map +1 -1
  277. package/dist/view.es.mjs +9 -9
  278. package/package.json +47 -11
  279. package/src/a11y/announce.ts +131 -0
  280. package/src/a11y/audit.ts +314 -0
  281. package/src/a11y/index.ts +68 -0
  282. package/src/a11y/media-preferences.ts +255 -0
  283. package/src/a11y/roving-tab-index.ts +164 -0
  284. package/src/a11y/skip-link.ts +255 -0
  285. package/src/a11y/trap-focus.ts +184 -0
  286. package/src/a11y/types.ts +183 -0
  287. package/src/component/component.ts +599 -524
  288. package/src/component/html.ts +153 -153
  289. package/src/component/index.ts +52 -50
  290. package/src/component/library.ts +540 -518
  291. package/src/component/scope.ts +212 -0
  292. package/src/component/types.ts +310 -256
  293. package/src/core/collection.ts +249 -1
  294. package/src/core/element.ts +252 -11
  295. package/src/core/env.ts +60 -0
  296. package/src/core/index.ts +1 -0
  297. package/src/core/shared.ts +64 -0
  298. package/src/core/utils/index.ts +66 -1
  299. package/src/devtools/devtools.ts +410 -0
  300. package/src/devtools/index.ts +48 -0
  301. package/src/devtools/types.ts +104 -0
  302. package/src/dnd/draggable.ts +296 -0
  303. package/src/dnd/droppable.ts +228 -0
  304. package/src/dnd/index.ts +62 -0
  305. package/src/dnd/sortable.ts +307 -0
  306. package/src/dnd/types.ts +293 -0
  307. package/src/forms/create-form.ts +320 -0
  308. package/src/forms/index.ts +70 -0
  309. package/src/forms/types.ts +203 -0
  310. package/src/forms/use-field.ts +231 -0
  311. package/src/forms/validators.ts +294 -0
  312. package/src/full.ts +554 -229
  313. package/src/i18n/formatting.ts +67 -0
  314. package/src/i18n/i18n.ts +200 -0
  315. package/src/i18n/index.ts +67 -0
  316. package/src/i18n/translate.ts +182 -0
  317. package/src/i18n/types.ts +171 -0
  318. package/src/index.ts +72 -0
  319. package/src/media/battery.ts +116 -0
  320. package/src/media/breakpoints.ts +129 -0
  321. package/src/media/clipboard.ts +80 -0
  322. package/src/media/device-sensors.ts +158 -0
  323. package/src/media/geolocation.ts +119 -0
  324. package/src/media/index.ts +76 -0
  325. package/src/media/media-query.ts +92 -0
  326. package/src/media/network.ts +115 -0
  327. package/src/media/types.ts +177 -0
  328. package/src/media/viewport.ts +84 -0
  329. package/src/motion/index.ts +11 -2
  330. package/src/motion/morph.ts +151 -0
  331. package/src/motion/parallax.ts +120 -0
  332. package/src/motion/reduced-motion.ts +52 -3
  333. package/src/motion/types.ts +63 -0
  334. package/src/motion/typewriter.ts +164 -0
  335. package/src/plugin/index.ts +37 -0
  336. package/src/plugin/registry.ts +284 -0
  337. package/src/plugin/types.ts +137 -0
  338. package/src/reactive/async-data.ts +250 -29
  339. package/src/reactive/computed.ts +53 -1
  340. package/src/reactive/effect.ts +29 -6
  341. package/src/reactive/http.ts +790 -0
  342. package/src/reactive/index.ts +60 -0
  343. package/src/reactive/pagination.ts +317 -0
  344. package/src/reactive/polling.ts +179 -0
  345. package/src/reactive/readonly.ts +52 -8
  346. package/src/reactive/rest.ts +859 -0
  347. package/src/reactive/scope.ts +276 -0
  348. package/src/reactive/signal.ts +61 -1
  349. package/src/reactive/to-value.ts +71 -0
  350. package/src/reactive/websocket.ts +849 -0
  351. package/src/router/bq-link.ts +279 -0
  352. package/src/router/constraints.ts +204 -0
  353. package/src/router/index.ts +15 -7
  354. package/src/router/match.ts +255 -49
  355. package/src/router/path-pattern.ts +52 -0
  356. package/src/router/query.ts +3 -0
  357. package/src/router/router.ts +258 -48
  358. package/src/router/state.ts +51 -3
  359. package/src/router/types.ts +50 -4
  360. package/src/router/use-route.ts +68 -0
  361. package/src/router/utils.ts +44 -3
  362. package/src/security/index.ts +12 -17
  363. package/src/security/sanitize.ts +70 -70
  364. package/src/security/trusted-html.ts +71 -71
  365. package/src/ssr/hydrate.ts +84 -0
  366. package/src/ssr/index.ts +70 -0
  367. package/src/ssr/render.ts +508 -0
  368. package/src/ssr/serialize.ts +296 -0
  369. package/src/ssr/types.ts +81 -0
  370. package/src/store/create-store.ts +146 -8
  371. package/src/store/define-store.ts +49 -49
  372. package/src/store/index.ts +5 -0
  373. package/src/store/mapping.ts +74 -74
  374. package/src/store/persisted.ts +245 -62
  375. package/src/store/types.ts +247 -92
  376. package/src/store/utils.ts +4 -10
  377. package/src/store/watch.ts +53 -53
  378. package/src/storybook/index.ts +480 -479
  379. package/src/testing/index.ts +42 -0
  380. package/src/testing/testing.ts +593 -0
  381. package/src/testing/types.ts +170 -0
  382. package/src/view/custom-directives.ts +28 -0
  383. package/src/view/evaluate.ts +2 -0
  384. package/src/view/process.ts +19 -3
  385. package/dist/component-BEQgt5hl.js +0 -600
  386. package/dist/component-BEQgt5hl.js.map +0 -1
  387. package/dist/core-BGQJVw0-.js +0 -35
  388. package/dist/core-BGQJVw0-.js.map +0 -1
  389. package/dist/core-CCEabVHl.js.map +0 -1
  390. package/dist/effect-AFRW_Plg.js +0 -84
  391. package/dist/effect-AFRW_Plg.js.map +0 -1
  392. package/dist/motion-D9TcHxOF.js +0 -415
  393. package/dist/motion-D9TcHxOF.js.map +0 -1
  394. package/dist/reactive-DSkct0dO.js +0 -254
  395. package/dist/reactive-DSkct0dO.js.map +0 -1
  396. package/dist/router-CbDhl8rS.js +0 -188
  397. package/dist/router-CbDhl8rS.js.map +0 -1
  398. package/dist/store-BwDvI45q.js +0 -263
  399. package/dist/store-BwDvI45q.js.map +0 -1
  400. package/dist/untrack-B0rVscTc.js +0 -7
  401. package/dist/untrack-B0rVscTc.js.map +0 -1
  402. package/dist/view-C70lA3vf.js.map +0 -1
@@ -47,15 +47,27 @@ export interface AsyncDataState<TData> {
47
47
  execute: () => Promise<TData | undefined>;
48
48
  /** Alias for execute(). */
49
49
  refresh: () => Promise<TData | undefined>;
50
+ /** Abort the current in-flight request (useFetch only; no-op for useAsyncData). */
51
+ abort: () => void;
50
52
  /** Clear data, error, and status back to the initial state. */
51
53
  clear: () => void;
52
54
  /** Dispose reactive watchers and prevent future executions. */
53
55
  dispose: () => void;
54
56
  }
55
57
 
58
+ /** Configuration for automatic request retries in useFetch(). */
59
+ export interface UseFetchRetryConfig {
60
+ /** Maximum number of retry attempts (default: 3). */
61
+ count: number;
62
+ /** Delay in ms between retries, or a function receiving the attempt index. */
63
+ delay?: number | ((attempt: number) => number);
64
+ /** Predicate deciding whether to retry. Defaults to network / 5xx errors. */
65
+ retryOn?: (error: Error, attempt: number) => boolean;
66
+ }
67
+
56
68
  /** Options for useFetch(). */
57
69
  export interface UseFetchOptions<TResponse = unknown, TData = TResponse>
58
- extends UseAsyncDataOptions<TResponse, TData>, Omit<RequestInit, 'body' | 'headers'> {
70
+ extends UseAsyncDataOptions<TResponse, TData>, Omit<RequestInit, 'body' | 'headers' | 'signal'> {
59
71
  /** Base URL prepended to relative URLs. */
60
72
  baseUrl?: string;
61
73
  /** Query parameters appended to the request URL. */
@@ -68,6 +80,14 @@ export interface UseFetchOptions<TResponse = unknown, TData = TResponse>
68
80
  parseAs?: BqueryFetchParseAs;
69
81
  /** Custom fetch implementation for testing or adapters. */
70
82
  fetcher?: typeof fetch;
83
+ /** Request timeout in milliseconds. 0 means no timeout. */
84
+ timeout?: number;
85
+ /** External AbortSignal for request cancellation. */
86
+ signal?: AbortSignal;
87
+ /** Retry configuration. Pass a number for simple retry count, or a config object. */
88
+ retry?: number | UseFetchRetryConfig;
89
+ /** Custom status validation. Returns `true` for acceptable statuses. */
90
+ validateStatus?: (status: number) => boolean;
71
91
  }
72
92
 
73
93
  /** Input accepted by useFetch(). */
@@ -340,23 +360,101 @@ export const useAsyncData = <TResult, TData = TResult>(
340
360
  pending,
341
361
  execute,
342
362
  refresh: execute,
363
+ abort: () => {},
343
364
  clear,
344
365
  dispose,
345
366
  };
346
367
  };
347
368
 
369
+ /** @internal */
370
+ const DEFAULT_VALIDATE_STATUS = (status: number): boolean => status >= 200 && status < 300;
371
+
372
+ /** @internal */
373
+ const isDomExceptionNamed = (error: unknown, name: string): error is DOMException =>
374
+ error instanceof DOMException && error.name === name;
375
+
376
+ /** @internal */
377
+ const isTimeoutDomException = (error: unknown): error is DOMException =>
378
+ isDomExceptionNamed(error, 'TimeoutError');
379
+
380
+ /** @internal */
381
+ const isAbortDomException = (error: unknown): error is DOMException =>
382
+ isDomExceptionNamed(error, 'AbortError');
383
+
384
+ /** @internal */
385
+ const DEFAULT_RETRY_ON = (error: Error): boolean => {
386
+ if (
387
+ isAbortDomException(error) ||
388
+ isTimeoutDomException(error) ||
389
+ (error as Error & { code?: string }).code === 'ABORT' ||
390
+ (error as Error & { code?: string }).code === 'TIMEOUT'
391
+ ) {
392
+ return false;
393
+ }
394
+ const status = (error as Error & { status?: number }).status;
395
+ return status === undefined || status >= 500;
396
+ };
397
+
398
+ /** @internal */
399
+ const normalizeRetryConfig = (retry: UseFetchOptions['retry']): UseFetchRetryConfig | undefined => {
400
+ if (retry == null) return undefined;
401
+ if (typeof retry === 'number') return { count: retry };
402
+ return retry;
403
+ };
404
+
405
+ /** @internal */
406
+ const resolveRetryDelay = (delay: UseFetchRetryConfig['delay'], attempt: number): number => {
407
+ if (delay == null) return Math.min(1000 * 2 ** attempt, 30_000);
408
+ if (typeof delay === 'number') return delay;
409
+ return delay(attempt);
410
+ };
411
+
412
+ /** @internal */
413
+ const sleepWithSignal = (ms: number, abortSignal?: AbortSignal): Promise<void> =>
414
+ new Promise<void>((resolve, reject) => {
415
+ if (abortSignal?.aborted) {
416
+ reject(abortSignal.reason ?? new DOMException('The operation was aborted.', 'AbortError'));
417
+ return;
418
+ }
419
+ let cleanedUp = false;
420
+ let timer: ReturnType<typeof setTimeout>;
421
+
422
+ const onAbort = (): void => {
423
+ if (cleanedUp) return;
424
+ cleanedUp = true;
425
+ clearTimeout(timer);
426
+ abortSignal?.removeEventListener('abort', onAbort);
427
+ reject(abortSignal?.reason ?? new DOMException('The operation was aborted.', 'AbortError'));
428
+ };
429
+
430
+ timer = setTimeout(() => {
431
+ if (cleanedUp) return;
432
+ cleanedUp = true;
433
+ abortSignal?.removeEventListener('abort', onAbort);
434
+ resolve();
435
+ }, ms);
436
+
437
+ abortSignal?.addEventListener('abort', onAbort, { once: true });
438
+ });
439
+
348
440
  /**
349
441
  * Reactive fetch composable using the browser Fetch API.
350
442
  *
443
+ * Supports timeout, abort, retry, and custom status validation in addition
444
+ * to the core useFetch features (query params, JSON body, baseUrl, watch).
445
+ *
351
446
  * @template TResponse - Raw parsed response type
352
447
  * @template TData - Stored response type after optional transformation
353
448
  * @param input - Request URL, Request object, or lazy input factory
354
449
  * @param options - Request and reactive state options
355
- * @returns Reactive fetch state with execute(), refresh(), and clear()
450
+ * @returns Reactive fetch state with execute(), refresh(), abort(), clear(), and dispose()
356
451
  *
357
452
  * @example
358
453
  * ```ts
359
- * const users = useFetch<{ id: number; name: string }[]>('/api/users');
454
+ * const users = useFetch<{ id: number; name: string }[]>('/api/users', {
455
+ * timeout: 5000,
456
+ * retry: 3,
457
+ * });
360
458
  * ```
361
459
  */
362
460
  export const useFetch = <TResponse = unknown, TData = TResponse>(
@@ -366,8 +464,22 @@ export const useFetch = <TResponse = unknown, TData = TResponse>(
366
464
  const fetchConfig = getBqueryConfig().fetch;
367
465
  const parseAs = options.parseAs ?? fetchConfig?.parseAs ?? 'json';
368
466
  const fetcher = options.fetcher ?? fetch;
467
+ const validateStatus = options.validateStatus ?? DEFAULT_VALIDATE_STATUS;
468
+
469
+ let currentAbortController: AbortController | null = null;
470
+ const normalizeAbortLikeError = (reason: unknown, didTimeout: boolean): Error => {
471
+ const isTimeout =
472
+ didTimeout ||
473
+ isTimeoutDomException(reason) ||
474
+ isTimeoutDomException(currentAbortController?.signal.reason);
475
+
476
+ return Object.assign(
477
+ new Error(isTimeout ? `Request timeout of ${options.timeout}ms exceeded` : 'Request aborted'),
478
+ { code: isTimeout ? 'TIMEOUT' : 'ABORT' }
479
+ );
480
+ };
369
481
 
370
- return useAsyncData<TResponse, TData>(async () => {
482
+ const state = useAsyncData<TResponse, TData>(async () => {
371
483
  const requestInput = resolveInput(input);
372
484
  const requestUrl =
373
485
  typeof requestInput === 'string' || requestInput instanceof URL
@@ -380,7 +492,7 @@ export const useFetch = <TResponse = unknown, TData = TResponse>(
380
492
  appendQuery(requestUrl, options.query);
381
493
  }
382
494
 
383
- const headers = toHeaders(
495
+ const baseHeaders = toHeaders(
384
496
  fetchConfig?.headers,
385
497
  requestInput instanceof Request ? requestInput.headers : undefined,
386
498
  options.headers
@@ -393,23 +505,51 @@ export const useFetch = <TResponse = unknown, TData = TResponse>(
393
505
  throw new Error(`Cannot send a request body with ${bodylessMethod} requests`);
394
506
  }
395
507
  const requestInitMethod = resolveRequestInitMethod(explicitMethod, requestInput, method);
396
- const requestInit: RequestInit = {
508
+ const retryConfig = normalizeRetryConfig(options.retry);
509
+ const maxAttempts = (retryConfig?.count ?? 0) + 1;
510
+
511
+ // Abort controller: compose timeout + external signal + manual abort
512
+ const abortController = new AbortController();
513
+ currentAbortController = abortController;
514
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
515
+ let didTimeout = false;
516
+ let externalAbortHandler: (() => void) | undefined;
517
+
518
+ if (options.signal) {
519
+ if (options.signal.aborted) {
520
+ abortController.abort(options.signal.reason);
521
+ } else {
522
+ externalAbortHandler = () => abortController.abort(options.signal?.reason);
523
+ options.signal.addEventListener('abort', externalAbortHandler, { once: true });
524
+ }
525
+ }
526
+
527
+ if (options.timeout && options.timeout > 0) {
528
+ timeoutId = setTimeout(() => {
529
+ didTimeout = true;
530
+ abortController.abort(new DOMException('Request timeout', 'TimeoutError'));
531
+ }, options.timeout);
532
+ }
533
+
534
+ const baseRequestInit: Omit<RequestInit, 'body' | 'signal'> = {
397
535
  ...options,
398
536
  method: requestInitMethod,
399
- headers,
400
- body: serializeBody(options.body, headers),
537
+ headers: baseHeaders,
401
538
  };
402
539
 
403
- delete (requestInit as Partial<UseFetchOptions>).baseUrl;
404
- delete (requestInit as Partial<UseFetchOptions>).query;
405
- delete (requestInit as Partial<UseFetchOptions>).parseAs;
406
- delete (requestInit as Partial<UseFetchOptions>).fetcher;
407
- delete (requestInit as Partial<UseFetchOptions>).defaultValue;
408
- delete (requestInit as Partial<UseFetchOptions>).immediate;
409
- delete (requestInit as Partial<UseFetchOptions>).watch;
410
- delete (requestInit as Partial<UseFetchOptions>).transform;
411
- delete (requestInit as Partial<UseFetchOptions>).onSuccess;
412
- delete (requestInit as Partial<UseFetchOptions>).onError;
540
+ delete (baseRequestInit as Partial<UseFetchOptions>).baseUrl;
541
+ delete (baseRequestInit as Partial<UseFetchOptions>).query;
542
+ delete (baseRequestInit as Partial<UseFetchOptions>).parseAs;
543
+ delete (baseRequestInit as Partial<UseFetchOptions>).fetcher;
544
+ delete (baseRequestInit as Partial<UseFetchOptions>).defaultValue;
545
+ delete (baseRequestInit as Partial<UseFetchOptions>).immediate;
546
+ delete (baseRequestInit as Partial<UseFetchOptions>).watch;
547
+ delete (baseRequestInit as Partial<UseFetchOptions>).transform;
548
+ delete (baseRequestInit as Partial<UseFetchOptions>).onSuccess;
549
+ delete (baseRequestInit as Partial<UseFetchOptions>).onError;
550
+ delete (baseRequestInit as Partial<UseFetchOptions>).timeout;
551
+ delete (baseRequestInit as Partial<UseFetchOptions>).retry;
552
+ delete (baseRequestInit as Partial<UseFetchOptions>).validateStatus;
413
553
 
414
554
  let requestTarget: Request | string | URL = requestUrl ?? requestInput;
415
555
  if (
@@ -417,22 +557,103 @@ export const useFetch = <TResponse = unknown, TData = TResponse>(
417
557
  requestUrl &&
418
558
  requestUrl.toString() !== requestInput.url
419
559
  ) {
420
- // Rebuild Request inputs when query params changed so the updated URL is preserved.
421
- // String/URL inputs already use `requestUrl` directly, so only Request objects need rebuilding.
422
560
  requestTarget = new Request(requestUrl.toString(), toRequestInit(requestInput));
423
561
  }
424
- const response = await fetcher(requestTarget, requestInit);
425
-
426
- if (!response.ok) {
427
- throw Object.assign(new Error(`Request failed with status ${response.status}`), {
428
- response,
429
- status: response.status,
430
- statusText: response.statusText,
431
- });
562
+
563
+ const createAttemptRequestInit = (): RequestInit => {
564
+ const headers = new Headers(baseHeaders);
565
+ return {
566
+ ...baseRequestInit,
567
+ headers,
568
+ body: serializeBody(options.body, headers),
569
+ signal: abortController.signal,
570
+ };
571
+ };
572
+
573
+ if (
574
+ maxAttempts > 1 &&
575
+ typeof ReadableStream !== 'undefined' &&
576
+ options.body instanceof ReadableStream
577
+ ) {
578
+ throw new Error('Cannot retry requests with ReadableStream bodies');
432
579
  }
433
580
 
434
- return parseResponse<TResponse>(response, parseAs);
581
+ if (
582
+ maxAttempts > 1 &&
583
+ typeof Request !== 'undefined' &&
584
+ requestTarget instanceof Request &&
585
+ requestTarget.body !== null
586
+ ) {
587
+ throw new Error('Cannot retry requests with non-replayable Request bodies');
588
+ }
589
+ let lastError: Error | undefined;
590
+
591
+ try {
592
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
593
+ try {
594
+ const response = await fetcher(requestTarget, createAttemptRequestInit());
595
+
596
+ if (!validateStatus(response.status)) {
597
+ throw Object.assign(new Error(`Request failed with status ${response.status}`), {
598
+ response,
599
+ status: response.status,
600
+ statusText: response.statusText,
601
+ });
602
+ }
603
+
604
+ return await parseResponse<TResponse>(response, parseAs);
605
+ } catch (error) {
606
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
607
+
608
+ // Abort errors should not be retried
609
+ if (
610
+ abortController.signal.aborted ||
611
+ isAbortDomException(normalizedError) ||
612
+ isTimeoutDomException(normalizedError)
613
+ ) {
614
+ throw normalizeAbortLikeError(
615
+ abortController.signal.aborted ? abortController.signal.reason : normalizedError,
616
+ didTimeout
617
+ );
618
+ }
619
+
620
+ lastError = normalizedError;
621
+
622
+ const shouldRetry = retryConfig
623
+ ? (retryConfig.retryOn ?? DEFAULT_RETRY_ON)(normalizedError, attempt)
624
+ : false;
625
+
626
+ if (!shouldRetry || attempt >= maxAttempts - 1) {
627
+ throw normalizedError;
628
+ }
629
+
630
+ await sleepWithSignal(
631
+ resolveRetryDelay(retryConfig!.delay, attempt),
632
+ abortController.signal
633
+ );
634
+ }
635
+ }
636
+
637
+ throw lastError!;
638
+ } finally {
639
+ if (timeoutId !== undefined) clearTimeout(timeoutId);
640
+ if (options.signal && externalAbortHandler) {
641
+ options.signal.removeEventListener('abort', externalAbortHandler);
642
+ }
643
+ if (currentAbortController === abortController) {
644
+ currentAbortController = null;
645
+ }
646
+ }
435
647
  }, options);
648
+
649
+ // Override abort with real abort logic
650
+ state.abort = (): void => {
651
+ if (currentAbortController) {
652
+ currentAbortController.abort(new DOMException('Request aborted', 'AbortError'));
653
+ }
654
+ };
655
+
656
+ return state;
436
657
  };
437
658
 
438
659
  /**
@@ -8,8 +8,10 @@ import {
8
8
  registerDependency,
9
9
  scheduleObserver,
10
10
  track,
11
+ withoutCurrentObserver,
11
12
  type ReactiveSource,
12
13
  } from './internals';
14
+ import { getActiveScope, hasScopeDisposer } from './scope';
13
15
 
14
16
  /**
15
17
  * A computed value that derives from other reactive sources.
@@ -21,9 +23,14 @@ import {
21
23
  */
22
24
  export class Computed<T> implements ReactiveSource {
23
25
  private cachedValue!: T;
26
+ private hasCachedValue = false;
24
27
  private dirty = true;
28
+ private disposed = false;
25
29
  private subscribers = new Set<() => void>();
26
30
  private readonly markDirty = () => {
31
+ if (this.disposed) {
32
+ return;
33
+ }
27
34
  this.dirty = true;
28
35
  // Create snapshot to avoid issues with subscribers modifying the set during iteration
29
36
  const subscribersSnapshot = Array.from(this.subscribers);
@@ -43,6 +50,14 @@ export class Computed<T> implements ReactiveSource {
43
50
  * During untrack calls, getCurrentObserver returns undefined, preventing dependency tracking.
44
51
  */
45
52
  get value(): T {
53
+ if (this.disposed) {
54
+ if (!this.hasCachedValue) {
55
+ this.cachedValue = withoutCurrentObserver(() => this.compute());
56
+ this.hasCachedValue = true;
57
+ }
58
+ return this.cachedValue;
59
+ }
60
+
46
61
  const current = getCurrentObserver();
47
62
  if (current) {
48
63
  this.subscribers.add(current);
@@ -53,6 +68,7 @@ export class Computed<T> implements ReactiveSource {
53
68
  // Clear old dependencies before recomputing
54
69
  clearDependencies(this.markDirty);
55
70
  this.cachedValue = track(this.markDirty, this.compute);
71
+ this.hasCachedValue = true;
56
72
  }
57
73
  return this.cachedValue;
58
74
  }
@@ -64,11 +80,20 @@ export class Computed<T> implements ReactiveSource {
64
80
  * @returns The current cached value (recomputes if dirty)
65
81
  */
66
82
  peek(): T {
83
+ if (this.disposed) {
84
+ if (!this.hasCachedValue) {
85
+ this.cachedValue = withoutCurrentObserver(() => this.compute());
86
+ this.hasCachedValue = true;
87
+ }
88
+ return this.cachedValue;
89
+ }
90
+
67
91
  if (this.dirty) {
68
92
  this.dirty = false;
69
93
  // Clear old dependencies before recomputing
70
94
  clearDependencies(this.markDirty);
71
95
  this.cachedValue = track(this.markDirty, this.compute);
96
+ this.hasCachedValue = true;
72
97
  }
73
98
  return this.cachedValue;
74
99
  }
@@ -80,13 +105,40 @@ export class Computed<T> implements ReactiveSource {
80
105
  unsubscribe(observer: () => void): void {
81
106
  this.subscribers.delete(observer);
82
107
  }
108
+
109
+ /**
110
+ * Disposes the computed value by unsubscribing its internal observer
111
+ * from all upstream dependencies and clearing subscribers.
112
+ */
113
+ dispose(): void {
114
+ this.disposed = true;
115
+ if (this.dirty) {
116
+ this.hasCachedValue = false;
117
+ }
118
+ this.dirty = false;
119
+ clearDependencies(this.markDirty);
120
+ this.subscribers.clear();
121
+ }
83
122
  }
84
123
 
85
124
  /**
86
125
  * Creates a new computed value.
87
126
  *
127
+ * If created inside an {@link effectScope}, the computed value is automatically
128
+ * collected and will be disposed when the scope stops.
129
+ *
88
130
  * @template T - The type of the computed value
89
131
  * @param fn - Function that computes the value from reactive sources
90
132
  * @returns A new Computed instance
91
133
  */
92
- export const computed = <T>(fn: () => T): Computed<T> => new Computed(fn);
134
+ export const computed = <T>(fn: () => T): Computed<T> => {
135
+ const c = new Computed(fn);
136
+
137
+ // Auto-register with the current scope so scope.stop() disposes this computed
138
+ const scope = getActiveScope();
139
+ if (hasScopeDisposer(scope)) {
140
+ scope._addDisposer(() => c.dispose());
141
+ }
142
+
143
+ return c;
144
+ };
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { CleanupFn, Observer, track, clearDependencies } from './internals';
6
+ import { getActiveScope, hasScopeDisposer } from './scope';
6
7
 
7
8
  /**
8
9
  * Creates a side effect that automatically re-runs when dependencies change.
@@ -10,12 +11,16 @@ import { CleanupFn, Observer, track, clearDependencies } from './internals';
10
11
  * The effect runs immediately upon creation and then re-runs whenever
11
12
  * any signal or computed value read inside it changes.
12
13
  *
14
+ * If created inside an {@link effectScope}, the effect is automatically
15
+ * collected and will be disposed when the scope stops.
16
+ *
13
17
  * @param fn - The effect function to run
14
18
  * @returns A cleanup function to stop the effect
15
19
  */
16
20
  export const effect = (fn: () => void | CleanupFn): CleanupFn => {
17
21
  let cleanupFn: CleanupFn | void;
18
22
  let isDisposed = false;
23
+ const scope = getActiveScope();
19
24
 
20
25
  const runCleanup = (): void => {
21
26
  if (cleanupFn) {
@@ -28,6 +33,25 @@ export const effect = (fn: () => void | CleanupFn): CleanupFn => {
28
33
  }
29
34
  };
30
35
 
36
+ const clearEffectState = (): void => {
37
+ runCleanup();
38
+ // Clean up all dependencies when effect is disposed
39
+ clearDependencies(observer);
40
+ };
41
+
42
+ const dispose: CleanupFn = () => {
43
+ if (isDisposed) {
44
+ return;
45
+ }
46
+
47
+ isDisposed = true;
48
+ clearEffectState();
49
+ };
50
+
51
+ if (hasScopeDisposer(scope)) {
52
+ scope._addDisposer(dispose);
53
+ }
54
+
31
55
  const observer: Observer = () => {
32
56
  if (isDisposed) return;
33
57
 
@@ -41,14 +65,13 @@ export const effect = (fn: () => void | CleanupFn): CleanupFn => {
41
65
  } catch (error) {
42
66
  console.error('bQuery reactive: Error in effect', error);
43
67
  }
68
+
69
+ if (isDisposed) {
70
+ clearEffectState();
71
+ }
44
72
  };
45
73
 
46
74
  observer();
47
75
 
48
- return () => {
49
- isDisposed = true;
50
- runCleanup();
51
- // Clean up all dependencies when effect is disposed
52
- clearDependencies(observer);
53
- };
76
+ return dispose;
54
77
  };