@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.
- package/README.md +192 -18
- package/dist/a11y/announce.d.ts +43 -0
- package/dist/a11y/announce.d.ts.map +1 -0
- package/dist/a11y/audit.d.ts +42 -0
- package/dist/a11y/audit.d.ts.map +1 -0
- package/dist/a11y/index.d.ts +53 -0
- package/dist/a11y/index.d.ts.map +1 -0
- package/dist/a11y/media-preferences.d.ts +77 -0
- package/dist/a11y/media-preferences.d.ts.map +1 -0
- package/dist/a11y/roving-tab-index.d.ts +38 -0
- package/dist/a11y/roving-tab-index.d.ts.map +1 -0
- package/dist/a11y/skip-link.d.ts +37 -0
- package/dist/a11y/skip-link.d.ts.map +1 -0
- package/dist/a11y/trap-focus.d.ts +49 -0
- package/dist/a11y/trap-focus.d.ts.map +1 -0
- package/dist/a11y/types.d.ts +152 -0
- package/dist/a11y/types.d.ts.map +1 -0
- package/dist/a11y-DVBCy09c.js +421 -0
- package/dist/a11y-DVBCy09c.js.map +1 -0
- package/dist/a11y.es.mjs +14 -0
- package/dist/component/component.d.ts.map +1 -1
- package/dist/component/html.d.ts.map +1 -1
- package/dist/component/index.d.ts +2 -1
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/library.d.ts.map +1 -1
- package/dist/component/scope.d.ts +138 -0
- package/dist/component/scope.d.ts.map +1 -0
- package/dist/component/types.d.ts +53 -1
- package/dist/component/types.d.ts.map +1 -1
- package/dist/component-L3-JfOFz.js +684 -0
- package/dist/component-L3-JfOFz.js.map +1 -0
- package/dist/component.es.mjs +9 -6
- package/dist/{config-DRmZZno3.js → config-DhT9auRm.js} +4 -4
- package/dist/{config-DRmZZno3.js.map → config-DhT9auRm.js.map} +1 -1
- package/dist/constraints-D5RHQLmP.js +100 -0
- package/dist/constraints-D5RHQLmP.js.map +1 -0
- package/dist/core/collection.d.ts +134 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/element.d.ts +120 -0
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/env.d.ts +18 -0
- package/dist/core/env.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/shared.d.ts +14 -0
- package/dist/core/shared.d.ts.map +1 -1
- package/dist/core/utils/index.d.ts +52 -41
- package/dist/core/utils/index.d.ts.map +1 -1
- package/dist/core-DdtZHzsS.js +168 -0
- package/dist/core-DdtZHzsS.js.map +1 -0
- package/dist/{core-CCEabVHl.js → core-EMYSLzaT.js} +293 -194
- package/dist/core-EMYSLzaT.js.map +1 -0
- package/dist/core.es.mjs +48 -46
- package/dist/custom-directives-Dr4C5lVV.js +9 -0
- package/dist/custom-directives-Dr4C5lVV.js.map +1 -0
- package/dist/devtools/devtools.d.ts +212 -0
- package/dist/devtools/devtools.d.ts.map +1 -0
- package/dist/devtools/index.d.ts +20 -0
- package/dist/devtools/index.d.ts.map +1 -0
- package/dist/devtools/types.d.ts +69 -0
- package/dist/devtools/types.d.ts.map +1 -0
- package/dist/devtools-BhB2iDPT.js +122 -0
- package/dist/devtools-BhB2iDPT.js.map +1 -0
- package/dist/devtools.es.mjs +19 -0
- package/dist/dnd/draggable.d.ts +51 -0
- package/dist/dnd/draggable.d.ts.map +1 -0
- package/dist/dnd/droppable.d.ts +38 -0
- package/dist/dnd/droppable.d.ts.map +1 -0
- package/dist/dnd/index.d.ts +47 -0
- package/dist/dnd/index.d.ts.map +1 -0
- package/dist/dnd/sortable.d.ts +43 -0
- package/dist/dnd/sortable.d.ts.map +1 -0
- package/dist/dnd/types.d.ts +250 -0
- package/dist/dnd/types.d.ts.map +1 -0
- package/dist/dnd-NwZBYh4l.js +244 -0
- package/dist/dnd-NwZBYh4l.js.map +1 -0
- package/dist/dnd.es.mjs +6 -0
- package/dist/env-CTdvLaH2.js +19 -0
- package/dist/env-CTdvLaH2.js.map +1 -0
- package/dist/forms/create-form.d.ts +49 -0
- package/dist/forms/create-form.d.ts.map +1 -0
- package/dist/forms/index.d.ts +40 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/types.d.ts +185 -0
- package/dist/forms/types.d.ts.map +1 -0
- package/dist/forms/use-field.d.ts +34 -0
- package/dist/forms/use-field.d.ts.map +1 -0
- package/dist/forms/validators.d.ts +204 -0
- package/dist/forms/validators.d.ts.map +1 -0
- package/dist/forms-UcRHsYxC.js +227 -0
- package/dist/forms-UcRHsYxC.js.map +1 -0
- package/dist/forms.es.mjs +16 -0
- package/dist/full.d.ts +30 -11
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +209 -93
- package/dist/full.iife.js +47 -31
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +47 -31
- package/dist/full.umd.js.map +1 -1
- package/dist/function-Cybd57JV.js +33 -0
- package/dist/function-Cybd57JV.js.map +1 -0
- package/dist/i18n/formatting.d.ts +40 -0
- package/dist/i18n/formatting.d.ts.map +1 -0
- package/dist/i18n/i18n.d.ts +48 -0
- package/dist/i18n/i18n.d.ts.map +1 -0
- package/dist/i18n/index.d.ts +57 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/translate.d.ts +83 -0
- package/dist/i18n/translate.d.ts.map +1 -0
- package/dist/i18n/types.d.ts +156 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n-kuF6Ekj6.js +89 -0
- package/dist/i18n-kuF6Ekj6.js.map +1 -0
- package/dist/i18n.es.mjs +6 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.mjs +257 -143
- package/dist/media/battery.d.ts +35 -0
- package/dist/media/battery.d.ts.map +1 -0
- package/dist/media/breakpoints.d.ts +51 -0
- package/dist/media/breakpoints.d.ts.map +1 -0
- package/dist/media/clipboard.d.ts +30 -0
- package/dist/media/clipboard.d.ts.map +1 -0
- package/dist/media/device-sensors.d.ts +54 -0
- package/dist/media/device-sensors.d.ts.map +1 -0
- package/dist/media/geolocation.d.ts +38 -0
- package/dist/media/geolocation.d.ts.map +1 -0
- package/dist/media/index.d.ts +42 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/media-query.d.ts +36 -0
- package/dist/media/media-query.d.ts.map +1 -0
- package/dist/media/network.d.ts +35 -0
- package/dist/media/network.d.ts.map +1 -0
- package/dist/media/types.d.ts +173 -0
- package/dist/media/types.d.ts.map +1 -0
- package/dist/media/viewport.d.ts +32 -0
- package/dist/media/viewport.d.ts.map +1 -0
- package/dist/media-i-fB5WxI.js +340 -0
- package/dist/media-i-fB5WxI.js.map +1 -0
- package/dist/media.es.mjs +12 -0
- package/dist/motion/index.d.ts +7 -3
- package/dist/motion/index.d.ts.map +1 -1
- package/dist/motion/morph.d.ts +27 -0
- package/dist/motion/morph.d.ts.map +1 -0
- package/dist/motion/parallax.d.ts +30 -0
- package/dist/motion/parallax.d.ts.map +1 -0
- package/dist/motion/reduced-motion.d.ts +36 -3
- package/dist/motion/reduced-motion.d.ts.map +1 -1
- package/dist/motion/types.d.ts +58 -0
- package/dist/motion/types.d.ts.map +1 -1
- package/dist/motion/typewriter.d.ts +31 -0
- package/dist/motion/typewriter.d.ts.map +1 -0
- package/dist/motion-BJsAuULb.js +530 -0
- package/dist/motion-BJsAuULb.js.map +1 -0
- package/dist/motion.es.mjs +27 -23
- package/dist/{view-C70lA3vf.js → mount-B4Y8bk8Z.js} +166 -160
- package/dist/mount-B4Y8bk8Z.js.map +1 -0
- package/dist/{object-qGpWr6-J.js → object-BCk-1c8T.js} +5 -4
- package/dist/{object-qGpWr6-J.js.map → object-BCk-1c8T.js.map} +1 -1
- package/dist/{platform-Dr9b6fsq.js → platform-Dw2gE3zI.js} +21 -22
- package/dist/{platform-Dr9b6fsq.js.map → platform-Dw2gE3zI.js.map} +1 -1
- package/dist/platform.es.mjs +2 -2
- package/dist/plugin/index.d.ts +22 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/registry.d.ts +108 -0
- package/dist/plugin/registry.d.ts.map +1 -0
- package/dist/plugin/types.d.ts +110 -0
- package/dist/plugin/types.d.ts.map +1 -0
- package/dist/plugin-C2WuC8SF.js +66 -0
- package/dist/plugin-C2WuC8SF.js.map +1 -0
- package/dist/plugin.es.mjs +9 -0
- package/dist/reactive/async-data.d.ts +28 -3
- package/dist/reactive/async-data.d.ts.map +1 -1
- package/dist/reactive/computed.d.ts +10 -0
- package/dist/reactive/computed.d.ts.map +1 -1
- package/dist/reactive/effect.d.ts +3 -0
- package/dist/reactive/effect.d.ts.map +1 -1
- package/dist/reactive/http.d.ts +194 -0
- package/dist/reactive/http.d.ts.map +1 -0
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/pagination.d.ts +126 -0
- package/dist/reactive/pagination.d.ts.map +1 -0
- package/dist/reactive/polling.d.ts +55 -0
- package/dist/reactive/polling.d.ts.map +1 -0
- package/dist/reactive/readonly.d.ts +20 -1
- package/dist/reactive/readonly.d.ts.map +1 -1
- package/dist/reactive/rest.d.ts +293 -0
- package/dist/reactive/rest.d.ts.map +1 -0
- package/dist/reactive/scope.d.ts +140 -0
- package/dist/reactive/scope.d.ts.map +1 -0
- package/dist/reactive/signal.d.ts +16 -2
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/to-value.d.ts +57 -0
- package/dist/reactive/to-value.d.ts.map +1 -0
- package/dist/reactive/websocket.d.ts +285 -0
- package/dist/reactive/websocket.d.ts.map +1 -0
- package/dist/reactive-DwkhUJfP.js +1148 -0
- package/dist/reactive-DwkhUJfP.js.map +1 -0
- package/dist/reactive.es.mjs +38 -20
- package/dist/registry-B08iilIh.js +26 -0
- package/dist/registry-B08iilIh.js.map +1 -0
- package/dist/router/bq-link.d.ts +112 -0
- package/dist/router/bq-link.d.ts.map +1 -0
- package/dist/router/constraints.d.ts +9 -0
- package/dist/router/constraints.d.ts.map +1 -0
- package/dist/router/index.d.ts +15 -7
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/match.d.ts +0 -1
- package/dist/router/match.d.ts.map +1 -1
- package/dist/router/path-pattern.d.ts +14 -0
- package/dist/router/path-pattern.d.ts.map +1 -0
- package/dist/router/query.d.ts.map +1 -1
- package/dist/router/router.d.ts +3 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/state.d.ts +25 -2
- package/dist/router/state.d.ts.map +1 -1
- package/dist/router/types.d.ts +48 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/use-route.d.ts +50 -0
- package/dist/router/use-route.d.ts.map +1 -0
- package/dist/router/utils.d.ts +3 -0
- package/dist/router/utils.d.ts.map +1 -1
- package/dist/router-CQikC9Ed.js +492 -0
- package/dist/router-CQikC9Ed.js.map +1 -0
- package/dist/router.es.mjs +14 -10
- package/dist/{sanitize-Bs2dkMby.js → sanitize-B1V4JswB.js} +2 -1
- package/dist/{sanitize-Bs2dkMby.js.map → sanitize-B1V4JswB.js.map} +1 -1
- package/dist/security/index.d.ts +2 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security.es.mjs +1 -1
- package/dist/ssr/hydrate.d.ts +65 -0
- package/dist/ssr/hydrate.d.ts.map +1 -0
- package/dist/ssr/index.d.ts +59 -0
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/ssr/render.d.ts +62 -0
- package/dist/ssr/render.d.ts.map +1 -0
- package/dist/ssr/serialize.d.ts +118 -0
- package/dist/ssr/serialize.d.ts.map +1 -0
- package/dist/ssr/types.d.ts +70 -0
- package/dist/ssr/types.d.ts.map +1 -0
- package/dist/ssr-_dAcGdzu.js +248 -0
- package/dist/ssr-_dAcGdzu.js.map +1 -0
- package/dist/ssr.es.mjs +9 -0
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/persisted.d.ts +38 -4
- package/dist/store/persisted.d.ts.map +1 -1
- package/dist/store/types.d.ts +138 -1
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/utils.d.ts +2 -2
- package/dist/store/utils.d.ts.map +1 -1
- package/dist/store-Cb3gPRve.js +338 -0
- package/dist/store-Cb3gPRve.js.map +1 -0
- package/dist/store.es.mjs +11 -10
- package/dist/storybook/index.d.ts.map +1 -1
- package/dist/storybook.es.mjs +1 -1
- package/dist/storybook.es.mjs.map +1 -1
- package/dist/testing/index.d.ts +23 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/testing.d.ts +156 -0
- package/dist/testing/testing.d.ts.map +1 -0
- package/dist/testing/types.d.ts +134 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing-C5Sjfsna.js +224 -0
- package/dist/testing-C5Sjfsna.js.map +1 -0
- package/dist/testing.es.mjs +9 -0
- package/dist/type-guards-BMX2c0LP.js +44 -0
- package/dist/type-guards-BMX2c0LP.js.map +1 -0
- package/dist/untrack-D0fnO5k2.js +36 -0
- package/dist/untrack-D0fnO5k2.js.map +1 -0
- package/dist/view/custom-directives.d.ts +20 -0
- package/dist/view/custom-directives.d.ts.map +1 -0
- package/dist/view/evaluate.d.ts.map +1 -1
- package/dist/view/process.d.ts.map +1 -1
- package/dist/view.es.mjs +9 -9
- package/package.json +47 -11
- package/src/a11y/announce.ts +131 -0
- package/src/a11y/audit.ts +314 -0
- package/src/a11y/index.ts +68 -0
- package/src/a11y/media-preferences.ts +255 -0
- package/src/a11y/roving-tab-index.ts +164 -0
- package/src/a11y/skip-link.ts +255 -0
- package/src/a11y/trap-focus.ts +184 -0
- package/src/a11y/types.ts +183 -0
- package/src/component/component.ts +599 -524
- package/src/component/html.ts +153 -153
- package/src/component/index.ts +52 -50
- package/src/component/library.ts +540 -518
- package/src/component/scope.ts +212 -0
- package/src/component/types.ts +310 -256
- package/src/core/collection.ts +249 -1
- package/src/core/element.ts +252 -11
- package/src/core/env.ts +60 -0
- package/src/core/index.ts +1 -0
- package/src/core/shared.ts +64 -0
- package/src/core/utils/index.ts +66 -1
- package/src/devtools/devtools.ts +410 -0
- package/src/devtools/index.ts +48 -0
- package/src/devtools/types.ts +104 -0
- package/src/dnd/draggable.ts +296 -0
- package/src/dnd/droppable.ts +228 -0
- package/src/dnd/index.ts +62 -0
- package/src/dnd/sortable.ts +307 -0
- package/src/dnd/types.ts +293 -0
- package/src/forms/create-form.ts +320 -0
- package/src/forms/index.ts +70 -0
- package/src/forms/types.ts +203 -0
- package/src/forms/use-field.ts +231 -0
- package/src/forms/validators.ts +294 -0
- package/src/full.ts +554 -229
- package/src/i18n/formatting.ts +67 -0
- package/src/i18n/i18n.ts +200 -0
- package/src/i18n/index.ts +67 -0
- package/src/i18n/translate.ts +182 -0
- package/src/i18n/types.ts +171 -0
- package/src/index.ts +72 -0
- package/src/media/battery.ts +116 -0
- package/src/media/breakpoints.ts +129 -0
- package/src/media/clipboard.ts +80 -0
- package/src/media/device-sensors.ts +158 -0
- package/src/media/geolocation.ts +119 -0
- package/src/media/index.ts +76 -0
- package/src/media/media-query.ts +92 -0
- package/src/media/network.ts +115 -0
- package/src/media/types.ts +177 -0
- package/src/media/viewport.ts +84 -0
- package/src/motion/index.ts +11 -2
- package/src/motion/morph.ts +151 -0
- package/src/motion/parallax.ts +120 -0
- package/src/motion/reduced-motion.ts +52 -3
- package/src/motion/types.ts +63 -0
- package/src/motion/typewriter.ts +164 -0
- package/src/plugin/index.ts +37 -0
- package/src/plugin/registry.ts +284 -0
- package/src/plugin/types.ts +137 -0
- package/src/reactive/async-data.ts +250 -29
- package/src/reactive/computed.ts +53 -1
- package/src/reactive/effect.ts +29 -6
- package/src/reactive/http.ts +790 -0
- package/src/reactive/index.ts +60 -0
- package/src/reactive/pagination.ts +317 -0
- package/src/reactive/polling.ts +179 -0
- package/src/reactive/readonly.ts +52 -8
- package/src/reactive/rest.ts +859 -0
- package/src/reactive/scope.ts +276 -0
- package/src/reactive/signal.ts +61 -1
- package/src/reactive/to-value.ts +71 -0
- package/src/reactive/websocket.ts +849 -0
- package/src/router/bq-link.ts +279 -0
- package/src/router/constraints.ts +204 -0
- package/src/router/index.ts +15 -7
- package/src/router/match.ts +255 -49
- package/src/router/path-pattern.ts +52 -0
- package/src/router/query.ts +3 -0
- package/src/router/router.ts +258 -48
- package/src/router/state.ts +51 -3
- package/src/router/types.ts +50 -4
- package/src/router/use-route.ts +68 -0
- package/src/router/utils.ts +44 -3
- package/src/security/index.ts +12 -17
- package/src/security/sanitize.ts +70 -70
- package/src/security/trusted-html.ts +71 -71
- package/src/ssr/hydrate.ts +84 -0
- package/src/ssr/index.ts +70 -0
- package/src/ssr/render.ts +508 -0
- package/src/ssr/serialize.ts +296 -0
- package/src/ssr/types.ts +81 -0
- package/src/store/create-store.ts +146 -8
- package/src/store/define-store.ts +49 -49
- package/src/store/index.ts +5 -0
- package/src/store/mapping.ts +74 -74
- package/src/store/persisted.ts +245 -62
- package/src/store/types.ts +247 -92
- package/src/store/utils.ts +4 -10
- package/src/store/watch.ts +53 -53
- package/src/storybook/index.ts +480 -479
- package/src/testing/index.ts +42 -0
- package/src/testing/testing.ts +593 -0
- package/src/testing/types.ts +170 -0
- package/src/view/custom-directives.ts +28 -0
- package/src/view/evaluate.ts +2 -0
- package/src/view/process.ts +19 -3
- package/dist/component-BEQgt5hl.js +0 -600
- package/dist/component-BEQgt5hl.js.map +0 -1
- package/dist/core-BGQJVw0-.js +0 -35
- package/dist/core-BGQJVw0-.js.map +0 -1
- package/dist/core-CCEabVHl.js.map +0 -1
- package/dist/effect-AFRW_Plg.js +0 -84
- package/dist/effect-AFRW_Plg.js.map +0 -1
- package/dist/motion-D9TcHxOF.js +0 -415
- package/dist/motion-D9TcHxOF.js.map +0 -1
- package/dist/reactive-DSkct0dO.js +0 -254
- package/dist/reactive-DSkct0dO.js.map +0 -1
- package/dist/router-CbDhl8rS.js +0 -188
- package/dist/router-CbDhl8rS.js.map +0 -1
- package/dist/store-BwDvI45q.js +0 -263
- package/dist/store-BwDvI45q.js.map +0 -1
- package/dist/untrack-B0rVscTc.js +0 -7
- package/dist/untrack-B0rVscTc.js.map +0 -1
- 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(),
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
404
|
-
delete (
|
|
405
|
-
delete (
|
|
406
|
-
delete (
|
|
407
|
-
delete (
|
|
408
|
-
delete (
|
|
409
|
-
delete (
|
|
410
|
-
delete (
|
|
411
|
-
delete (
|
|
412
|
-
delete (
|
|
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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
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
|
/**
|
package/src/reactive/computed.ts
CHANGED
|
@@ -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> =>
|
|
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
|
+
};
|
package/src/reactive/effect.ts
CHANGED
|
@@ -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
|
};
|