@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.
- package/README.md +716 -586
- 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-C5QOVvRn.js +421 -0
- package/dist/a11y-C5QOVvRn.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-CuuTijA6.js +684 -0
- package/dist/component-CuuTijA6.js.map +1 -0
- package/dist/component.es.mjs +9 -6
- package/dist/{config-DRmZZno3.js → config-BW35FKuA.js} +4 -4
- package/dist/{config-DRmZZno3.js.map → config-BW35FKuA.js.map} +1 -1
- package/dist/constraints-3lV9yyBw.js +100 -0
- package/dist/constraints-3lV9yyBw.js.map +1 -0
- package/dist/core/collection.d.ts +48 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/element.d.ts +92 -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 +8 -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-Cjl7GUu8.js +717 -0
- package/dist/core-Cjl7GUu8.js.map +1 -0
- package/dist/core-DnlyjbF2.js +112 -0
- package/dist/core-DnlyjbF2.js.map +1 -0
- package/dist/core.es.mjs +45 -44
- package/dist/custom-directives-7wAShnnd.js +9 -0
- package/dist/custom-directives-7wAShnnd.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-D2fQLhDN.js +122 -0
- package/dist/devtools-D2fQLhDN.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-B8EgyzaI.js +244 -0
- package/dist/dnd-B8EgyzaI.js.map +1 -0
- package/dist/dnd.es.mjs +6 -0
- package/dist/env-NeVmr4Gf.js +19 -0
- package/dist/env-NeVmr4Gf.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 +39 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/types.d.ts +139 -0
- package/dist/forms/types.d.ts.map +1 -0
- package/dist/forms/validators.d.ts +179 -0
- package/dist/forms/validators.d.ts.map +1 -0
- package/dist/forms-C3yovgH9.js +141 -0
- package/dist/forms-C3yovgH9.js.map +1 -0
- package/dist/forms.es.mjs +14 -0
- package/dist/full.d.ts +35 -7
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +182 -91
- 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/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-BnnhTFOS.js +89 -0
- package/dist/i18n-BnnhTFOS.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 +227 -136
- 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-Di2Ta22s.js +340 -0
- package/dist/media-Di2Ta22s.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-qPj_TYGv.js +530 -0
- package/dist/motion-qPj_TYGv.js.map +1 -0
- package/dist/motion.es.mjs +27 -23
- package/dist/{view-C70lA3vf.js → mount-SM07RUa6.js} +166 -160
- package/dist/mount-SM07RUa6.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-CPbCprb6.js} +21 -22
- package/dist/{platform-Dr9b6fsq.js.map → platform-CPbCprb6.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-cPoOHFLY.js +64 -0
- package/dist/plugin-cPoOHFLY.js.map +1 -0
- package/dist/plugin.es.mjs +9 -0
- package/dist/reactive/computed.d.ts +7 -0
- package/dist/reactive/computed.d.ts.map +1 -1
- package/dist/reactive-Cfv0RK6x.js +233 -0
- package/dist/reactive-Cfv0RK6x.js.map +1 -0
- package/dist/reactive.es.mjs +19 -20
- package/dist/registry-CWf368tT.js +26 -0
- package/dist/registry-CWf368tT.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 +14 -6
- 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/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-BrthaP_z.js +473 -0
- package/dist/router-BrthaP_z.js.map +1 -0
- package/dist/router.es.mjs +13 -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-B2qd_WBB.js +248 -0
- package/dist/ssr-B2qd_WBB.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-DWpyH6p5.js +338 -0
- package/dist/store-DWpyH6p5.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-CsqjNUyy.js +224 -0
- package/dist/testing-CsqjNUyy.js.map +1 -0
- package/dist/testing.es.mjs +9 -0
- package/dist/type-guards-Do9DWgNp.js +44 -0
- package/dist/type-guards-Do9DWgNp.js.map +1 -0
- package/dist/untrack-DJVQQ2WM.js +33 -0
- package/dist/untrack-DJVQQ2WM.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 +177 -141
- 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 +104 -29
- package/src/component/html.ts +5 -5
- package/src/component/index.ts +2 -0
- package/src/component/library.ts +26 -2
- package/src/component/scope.ts +212 -0
- package/src/component/types.ts +94 -40
- package/src/core/collection.ts +707 -628
- package/src/core/element.ts +981 -774
- package/src/core/env.ts +60 -0
- package/src/core/index.ts +49 -48
- package/src/core/shared.ts +62 -13
- package/src/core/utils/index.ts +148 -83
- 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 +278 -0
- package/src/forms/index.ts +65 -0
- package/src/forms/types.ts +154 -0
- package/src/forms/validators.ts +265 -0
- package/src/full.ts +253 -2
- 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 +108 -36
- package/src/media/battery.ts +116 -0
- package/src/media/breakpoints.ts +131 -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 +57 -48
- package/src/motion/morph.ts +151 -0
- package/src/motion/parallax.ts +120 -0
- package/src/motion/reduced-motion.ts +66 -17
- package/src/motion/types.ts +271 -208
- package/src/motion/typewriter.ts +164 -0
- package/src/plugin/index.ts +37 -0
- package/src/plugin/registry.ts +269 -0
- package/src/plugin/types.ts +137 -0
- package/src/reactive/computed.ts +130 -92
- package/src/router/bq-link.ts +279 -0
- package/src/router/constraints.ts +201 -0
- package/src/router/index.ts +49 -41
- package/src/router/match.ts +312 -106
- package/src/router/path-pattern.ts +52 -0
- package/src/router/query.ts +38 -35
- package/src/router/router.ts +402 -211
- package/src/router/types.ts +139 -93
- package/src/router/use-route.ts +68 -0
- package/src/router/utils.ts +157 -116
- package/src/security/index.ts +2 -7
- package/src/security/sanitize.ts +70 -70
- package/src/security/trusted-html.ts +71 -71
- package/src/ssr/hydrate.ts +82 -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 +467 -329
- package/src/store/define-store.ts +49 -49
- package/src/store/index.ts +27 -22
- package/src/store/mapping.ts +74 -74
- package/src/store/persisted.ts +206 -19
- package/src/store/types.ts +157 -2
- package/src/store/utils.ts +135 -141
- package/src/store/watch.ts +53 -53
- package/src/storybook/index.ts +2 -1
- 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 +30 -0
- package/src/view/evaluate.ts +292 -290
- package/src/view/process.ts +108 -92
- 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 +0 -648
- 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
package/src/router/match.ts
CHANGED
|
@@ -1,106 +1,312 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route matching helpers.
|
|
3
|
-
* @module bquery/router
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { parseQuery } from './query';
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Route matching helpers.
|
|
3
|
+
* @module bquery/router
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { parseQuery } from './query';
|
|
7
|
+
import { getNormalizedRouteConstraint, getRouteConstraintRegex } from './constraints';
|
|
8
|
+
import { isParamChar, isParamStart, readConstraint } from './path-pattern';
|
|
9
|
+
import type { Route, RouteDefinition } from './types';
|
|
10
|
+
|
|
11
|
+
const readConstraintOrThrow = (
|
|
12
|
+
path: string,
|
|
13
|
+
startIndex: number
|
|
14
|
+
): { constraint: string; endIndex: number } => {
|
|
15
|
+
const parsedConstraint = readConstraint(path, startIndex);
|
|
16
|
+
if (!parsedConstraint) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`bQuery router: Invalid route param constraint syntax in path "${path}" at index ${startIndex}.`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return parsedConstraint;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type RouteParamDescriptor = {
|
|
25
|
+
name: string;
|
|
26
|
+
constraint?: string;
|
|
27
|
+
nextIndex: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const validatedRoutePathCache = new Set<string>();
|
|
31
|
+
|
|
32
|
+
const readParamDescriptor = (path: string, index: number): RouteParamDescriptor | null => {
|
|
33
|
+
if (path[index] !== ':' || !isParamStart(path[index + 1])) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let nameEnd = index + 2;
|
|
38
|
+
while (nameEnd < path.length && isParamChar(path[nameEnd])) {
|
|
39
|
+
nameEnd++;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let nextIndex = nameEnd;
|
|
43
|
+
let constraint: string | undefined;
|
|
44
|
+
|
|
45
|
+
if (path[nameEnd] === '(') {
|
|
46
|
+
const parsedConstraint = readConstraintOrThrow(path, nameEnd);
|
|
47
|
+
constraint = parsedConstraint.constraint;
|
|
48
|
+
nextIndex = parsedConstraint.endIndex;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
name: path.slice(index + 1, nameEnd),
|
|
53
|
+
constraint,
|
|
54
|
+
nextIndex,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const validateRoutePathPattern = (path: string): void => {
|
|
59
|
+
if (validatedRoutePathCache.has(path)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < path.length; ) {
|
|
64
|
+
const char = path[i];
|
|
65
|
+
|
|
66
|
+
if (char === ':' && isParamStart(path[i + 1])) {
|
|
67
|
+
const param = readParamDescriptor(path, i);
|
|
68
|
+
if (param?.constraint) {
|
|
69
|
+
getNormalizedRouteConstraint(param.constraint);
|
|
70
|
+
}
|
|
71
|
+
i = param?.nextIndex ?? i + 1;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
i++;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
validatedRoutePathCache.add(path);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const findSegmentBoundary = (value: string, startIndex: number): number => {
|
|
82
|
+
const slashIndex = value.indexOf('/', startIndex);
|
|
83
|
+
return slashIndex === -1 ? value.length : slashIndex;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const readNextStaticChunk = (path: string, startIndex: number): string => {
|
|
87
|
+
let chunkEnd = startIndex;
|
|
88
|
+
|
|
89
|
+
while (chunkEnd < path.length) {
|
|
90
|
+
if (path[chunkEnd] === '*') {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (path[chunkEnd] === ':' && isParamStart(path[chunkEnd + 1])) {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
chunkEnd++;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return path.slice(startIndex, chunkEnd);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const findAnchoredCandidateEnds = (
|
|
105
|
+
actualPath: string,
|
|
106
|
+
startIndex: number,
|
|
107
|
+
limit: number,
|
|
108
|
+
nextStaticChunk: string
|
|
109
|
+
): number[] => {
|
|
110
|
+
const candidates: number[] = [];
|
|
111
|
+
let searchIndex = startIndex;
|
|
112
|
+
|
|
113
|
+
while (searchIndex <= limit) {
|
|
114
|
+
const candidateEnd = actualPath.indexOf(nextStaticChunk, searchIndex);
|
|
115
|
+
if (candidateEnd === -1 || candidateEnd > limit) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
candidates.push(candidateEnd);
|
|
120
|
+
searchIndex = candidateEnd + 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return candidates.reverse();
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const matchPathPattern = (routePath: string, actualPath: string): Record<string, string> | null => {
|
|
127
|
+
// Memoization keeps wildcard/param backtracking linear for repeated subproblems
|
|
128
|
+
// within a single route/path match attempt.
|
|
129
|
+
const memo = new Map<string, Record<string, string> | null>();
|
|
130
|
+
|
|
131
|
+
const matchFrom = (routeIndex: number, pathIndex: number): Record<string, string> | null => {
|
|
132
|
+
const memoKey = `${routeIndex}:${pathIndex}`;
|
|
133
|
+
if (memo.has(memoKey)) {
|
|
134
|
+
return memo.get(memoKey) ?? null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (routeIndex === routePath.length) {
|
|
138
|
+
const result = pathIndex === actualPath.length ? {} : null;
|
|
139
|
+
memo.set(memoKey, result);
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const routeChar = routePath[routeIndex];
|
|
144
|
+
|
|
145
|
+
if (routeChar === '*') {
|
|
146
|
+
if (routeIndex === routePath.length - 1) {
|
|
147
|
+
const result = {};
|
|
148
|
+
memo.set(memoKey, result);
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const nextStaticChunk = readNextStaticChunk(routePath, routeIndex + 1);
|
|
153
|
+
const anchoredCandidateEnds =
|
|
154
|
+
nextStaticChunk.length > 0
|
|
155
|
+
? findAnchoredCandidateEnds(actualPath, pathIndex, actualPath.length, nextStaticChunk)
|
|
156
|
+
: null;
|
|
157
|
+
|
|
158
|
+
const iterateCandidateEnds = anchoredCandidateEnds
|
|
159
|
+
? (callback: (candidateEnd: number) => Record<string, string> | null) => {
|
|
160
|
+
for (const candidateEnd of anchoredCandidateEnds) {
|
|
161
|
+
const result = callback(candidateEnd);
|
|
162
|
+
if (result) {
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
: (callback: (candidateEnd: number) => Record<string, string> | null) => {
|
|
169
|
+
for (let candidateEnd = actualPath.length; candidateEnd >= pathIndex; candidateEnd--) {
|
|
170
|
+
const result = callback(candidateEnd);
|
|
171
|
+
if (result) {
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const wildcardMatch = iterateCandidateEnds((candidateEnd) => {
|
|
179
|
+
const suffixMatch = matchFrom(routeIndex + 1, candidateEnd);
|
|
180
|
+
if (suffixMatch) {
|
|
181
|
+
memo.set(memoKey, suffixMatch);
|
|
182
|
+
return suffixMatch;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (wildcardMatch) {
|
|
188
|
+
return wildcardMatch;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
memo.set(memoKey, null);
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const param = readParamDescriptor(routePath, routeIndex);
|
|
196
|
+
if (param) {
|
|
197
|
+
const constraintRegex = param.constraint
|
|
198
|
+
? getRouteConstraintRegex(param.constraint)
|
|
199
|
+
: undefined;
|
|
200
|
+
const candidateLimit = param.constraint
|
|
201
|
+
? actualPath.length
|
|
202
|
+
: findSegmentBoundary(actualPath, pathIndex);
|
|
203
|
+
const nextStaticChunk = readNextStaticChunk(routePath, param.nextIndex);
|
|
204
|
+
const anchoredCandidateEnds =
|
|
205
|
+
nextStaticChunk.length > 0
|
|
206
|
+
? findAnchoredCandidateEnds(actualPath, pathIndex, candidateLimit, nextStaticChunk)
|
|
207
|
+
: null;
|
|
208
|
+
|
|
209
|
+
const iterateCandidateEnds = anchoredCandidateEnds
|
|
210
|
+
? (callback: (candidateEnd: number) => Record<string, string> | null) => {
|
|
211
|
+
for (const candidateEnd of anchoredCandidateEnds) {
|
|
212
|
+
if (candidateEnd <= pathIndex) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const result = callback(candidateEnd);
|
|
216
|
+
if (result) {
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
: (callback: (candidateEnd: number) => Record<string, string> | null) => {
|
|
223
|
+
for (let candidateEnd = candidateLimit; candidateEnd > pathIndex; candidateEnd--) {
|
|
224
|
+
const result = callback(candidateEnd);
|
|
225
|
+
if (result) {
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const paramMatch = iterateCandidateEnds((candidateEnd) => {
|
|
233
|
+
const candidateValue = actualPath.slice(pathIndex, candidateEnd);
|
|
234
|
+
|
|
235
|
+
if (constraintRegex) {
|
|
236
|
+
if (!constraintRegex.test(candidateValue)) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const suffixMatch = matchFrom(param.nextIndex, candidateEnd);
|
|
242
|
+
if (suffixMatch) {
|
|
243
|
+
const result = {
|
|
244
|
+
[param.name]: candidateValue,
|
|
245
|
+
...suffixMatch,
|
|
246
|
+
};
|
|
247
|
+
memo.set(memoKey, result);
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
if (paramMatch) {
|
|
254
|
+
return paramMatch;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
memo.set(memoKey, null);
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (pathIndex >= actualPath.length || routeChar !== actualPath[pathIndex]) {
|
|
262
|
+
memo.set(memoKey, null);
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const result = matchFrom(routeIndex + 1, pathIndex + 1);
|
|
267
|
+
memo.set(memoKey, result);
|
|
268
|
+
return result;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
return matchFrom(0, 0);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Matches a path against route definitions and extracts params.
|
|
276
|
+
* @internal
|
|
277
|
+
*/
|
|
278
|
+
export const matchRoute = (
|
|
279
|
+
path: string,
|
|
280
|
+
routes: RouteDefinition[]
|
|
281
|
+
): { matched: RouteDefinition; params: Record<string, string> } | null => {
|
|
282
|
+
for (const route of routes) {
|
|
283
|
+
validateRoutePathPattern(route.path);
|
|
284
|
+
const params = matchPathPattern(route.path, path);
|
|
285
|
+
if (params) {
|
|
286
|
+
return { matched: route, params };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return null;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Creates a Route object from the current URL.
|
|
295
|
+
* @internal
|
|
296
|
+
*/
|
|
297
|
+
export const createRoute = (
|
|
298
|
+
pathname: string,
|
|
299
|
+
search: string,
|
|
300
|
+
hash: string,
|
|
301
|
+
routes: RouteDefinition[]
|
|
302
|
+
): Route => {
|
|
303
|
+
const result = matchRoute(pathname, routes);
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
path: pathname,
|
|
307
|
+
params: result?.params ?? {},
|
|
308
|
+
query: parseQuery(search),
|
|
309
|
+
matched: result?.matched ?? null,
|
|
310
|
+
hash: hash.replace(/^#/, ''),
|
|
311
|
+
};
|
|
312
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for parsing route path params and constraints.
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Validates whether a character can start a route param name. @internal */
|
|
7
|
+
export const isParamStart = (char: string | undefined): boolean =>
|
|
8
|
+
char !== undefined &&
|
|
9
|
+
((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || char === '_');
|
|
10
|
+
|
|
11
|
+
/** Validates whether a character can appear after the start of a route param name. @internal */
|
|
12
|
+
export const isParamChar = (char: string | undefined): boolean =>
|
|
13
|
+
isParamStart(char) || (char !== undefined && char >= '0' && char <= '9');
|
|
14
|
+
|
|
15
|
+
/** Reads a route param constraint while preserving escaped chars and nested groups. @internal */
|
|
16
|
+
export const readConstraint = (
|
|
17
|
+
path: string,
|
|
18
|
+
startIndex: number
|
|
19
|
+
): { constraint: string; endIndex: number } | null => {
|
|
20
|
+
let depth = 1;
|
|
21
|
+
let constraint = '';
|
|
22
|
+
let i = startIndex + 1;
|
|
23
|
+
let inCharacterClass = false;
|
|
24
|
+
|
|
25
|
+
while (i < path.length) {
|
|
26
|
+
const char = path[i];
|
|
27
|
+
|
|
28
|
+
if (char === '\\' && i + 1 < path.length) {
|
|
29
|
+
constraint += char + path[i + 1];
|
|
30
|
+
i += 2;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (char === '[' && !inCharacterClass) {
|
|
35
|
+
inCharacterClass = true;
|
|
36
|
+
} else if (char === ']' && inCharacterClass) {
|
|
37
|
+
inCharacterClass = false;
|
|
38
|
+
} else if (!inCharacterClass && char === '(') {
|
|
39
|
+
depth++;
|
|
40
|
+
} else if (!inCharacterClass && char === ')') {
|
|
41
|
+
depth--;
|
|
42
|
+
if (depth === 0) {
|
|
43
|
+
return { constraint, endIndex: i + 1 };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
constraint += char;
|
|
48
|
+
i++;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null;
|
|
52
|
+
};
|
package/src/router/query.ts
CHANGED
|
@@ -1,35 +1,38 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Query string helpers.
|
|
3
|
-
* @module bquery/router
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* parseQuery('?
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
params
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
} else {
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Query string helpers.
|
|
3
|
+
* @module bquery/router
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isPrototypePollutionKey } from '../core/utils/object';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parses query string into an object.
|
|
10
|
+
* Single values are stored as strings, duplicate keys become arrays.
|
|
11
|
+
* @internal
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* parseQuery('?foo=1') // { foo: '1' }
|
|
15
|
+
* parseQuery('?tag=a&tag=b') // { tag: ['a', 'b'] }
|
|
16
|
+
* parseQuery('?x=1&y=2&x=3') // { x: ['1', '3'], y: '2' }
|
|
17
|
+
*/
|
|
18
|
+
export const parseQuery = (search: string): Record<string, string | string[]> => {
|
|
19
|
+
const query: Record<string, string | string[]> = {};
|
|
20
|
+
const params = new URLSearchParams(search);
|
|
21
|
+
|
|
22
|
+
params.forEach((value, key) => {
|
|
23
|
+
if (isPrototypePollutionKey(key)) return;
|
|
24
|
+
const existing = query[key];
|
|
25
|
+
if (existing === undefined) {
|
|
26
|
+
// First occurrence: store as string
|
|
27
|
+
query[key] = value;
|
|
28
|
+
} else if (Array.isArray(existing)) {
|
|
29
|
+
// Already an array: append
|
|
30
|
+
existing.push(value);
|
|
31
|
+
} else {
|
|
32
|
+
// Second occurrence: convert to array
|
|
33
|
+
query[key] = [existing, value];
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return query;
|
|
38
|
+
};
|