@bquery/bquery 1.2.0 → 1.4.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 +127 -27
- package/dist/batch-x7b2eZST.js +13 -0
- package/dist/batch-x7b2eZST.js.map +1 -0
- package/dist/component/component.d.ts +69 -0
- package/dist/component/component.d.ts.map +1 -0
- package/dist/component/html.d.ts +35 -0
- package/dist/component/html.d.ts.map +1 -0
- package/dist/component/index.d.ts +3 -126
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/props.d.ts +18 -0
- package/dist/component/props.d.ts.map +1 -0
- package/dist/component/types.d.ts +77 -0
- package/dist/component/types.d.ts.map +1 -0
- package/dist/component.es.mjs +90 -59
- package/dist/component.es.mjs.map +1 -1
- package/dist/core/collection.d.ts +55 -3
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/dom.d.ts +6 -0
- package/dist/core/dom.d.ts.map +1 -0
- package/dist/core/element.d.ts +31 -4
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/utils/array.d.ts +74 -0
- package/dist/core/utils/array.d.ts.map +1 -0
- package/dist/core/utils/function.d.ts +87 -0
- package/dist/core/utils/function.d.ts.map +1 -0
- package/dist/core/utils/index.d.ts +70 -0
- package/dist/core/utils/index.d.ts.map +1 -0
- package/dist/core/utils/misc.d.ts +63 -0
- package/dist/core/utils/misc.d.ts.map +1 -0
- package/dist/core/utils/number.d.ts +65 -0
- package/dist/core/utils/number.d.ts.map +1 -0
- package/dist/core/utils/object.d.ts +133 -0
- package/dist/core/utils/object.d.ts.map +1 -0
- package/dist/core/utils/string.d.ts +80 -0
- package/dist/core/utils/string.d.ts.map +1 -0
- package/dist/core/utils/type-guards.d.ts +79 -0
- package/dist/core/utils/type-guards.d.ts.map +1 -0
- package/dist/core-BhpuvPhy.js +170 -0
- package/dist/core-BhpuvPhy.js.map +1 -0
- package/dist/core.es.mjs +495 -489
- package/dist/core.es.mjs.map +1 -1
- package/dist/full.d.ts +2 -2
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +87 -64
- package/dist/full.es.mjs.map +1 -1
- package/dist/full.iife.js +2 -2
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +2 -2
- package/dist/full.umd.js.map +1 -1
- package/dist/index.es.mjs +138 -68
- package/dist/index.es.mjs.map +1 -1
- package/dist/motion/animate.d.ts +25 -0
- package/dist/motion/animate.d.ts.map +1 -0
- package/dist/motion/easing.d.ts +30 -0
- package/dist/motion/easing.d.ts.map +1 -0
- package/dist/motion/flip.d.ts +55 -0
- package/dist/motion/flip.d.ts.map +1 -0
- package/dist/motion/index.d.ts +11 -138
- package/dist/motion/index.d.ts.map +1 -1
- package/dist/motion/keyframes.d.ts +21 -0
- package/dist/motion/keyframes.d.ts.map +1 -0
- package/dist/motion/reduced-motion.d.ts +12 -0
- package/dist/motion/reduced-motion.d.ts.map +1 -0
- package/dist/motion/scroll.d.ts +15 -0
- package/dist/motion/scroll.d.ts.map +1 -0
- package/dist/motion/spring.d.ts +42 -0
- package/dist/motion/spring.d.ts.map +1 -0
- package/dist/motion/stagger.d.ts +22 -0
- package/dist/motion/stagger.d.ts.map +1 -0
- package/dist/motion/timeline.d.ts +21 -0
- package/dist/motion/timeline.d.ts.map +1 -0
- package/dist/motion/transition.d.ts +22 -0
- package/dist/motion/transition.d.ts.map +1 -0
- package/dist/motion/types.d.ts +182 -0
- package/dist/motion/types.d.ts.map +1 -0
- package/dist/motion.es.mjs +320 -61
- package/dist/motion.es.mjs.map +1 -1
- package/dist/persisted-DHoi3uEs.js +278 -0
- package/dist/persisted-DHoi3uEs.js.map +1 -0
- package/dist/platform/storage.d.ts.map +1 -1
- package/dist/platform.es.mjs +12 -7
- package/dist/platform.es.mjs.map +1 -1
- package/dist/reactive/batch.d.ts +13 -0
- package/dist/reactive/batch.d.ts.map +1 -0
- package/dist/reactive/computed.d.ts +50 -0
- package/dist/reactive/computed.d.ts.map +1 -0
- package/dist/reactive/core.d.ts +72 -0
- package/dist/reactive/core.d.ts.map +1 -0
- package/dist/reactive/effect.d.ts +15 -0
- package/dist/reactive/effect.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/internals.d.ts +42 -0
- package/dist/reactive/internals.d.ts.map +1 -0
- package/dist/reactive/linked.d.ts +36 -0
- package/dist/reactive/linked.d.ts.map +1 -0
- package/dist/reactive/persisted.d.ts +14 -0
- package/dist/reactive/persisted.d.ts.map +1 -0
- package/dist/reactive/readonly.d.ts +26 -0
- package/dist/reactive/readonly.d.ts.map +1 -0
- package/dist/reactive/signal.d.ts +13 -312
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/type-guards.d.ts +20 -0
- package/dist/reactive/type-guards.d.ts.map +1 -0
- package/dist/reactive/untrack.d.ts +29 -0
- package/dist/reactive/untrack.d.ts.map +1 -0
- package/dist/reactive/watch.d.ts +42 -0
- package/dist/reactive/watch.d.ts.map +1 -0
- package/dist/reactive.es.mjs +30 -163
- package/dist/reactive.es.mjs.map +1 -1
- package/dist/router/index.d.ts +6 -252
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/links.d.ts +44 -0
- package/dist/router/links.d.ts.map +1 -0
- package/dist/router/match.d.ts +20 -0
- package/dist/router/match.d.ts.map +1 -0
- package/dist/router/navigation.d.ts +45 -0
- package/dist/router/navigation.d.ts.map +1 -0
- package/dist/router/query.d.ts +16 -0
- package/dist/router/query.d.ts.map +1 -0
- package/dist/router/router.d.ts +34 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/state.d.ts +27 -0
- package/dist/router/state.d.ts.map +1 -0
- package/dist/router/types.d.ts +88 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/utils.d.ts +65 -0
- package/dist/router/utils.d.ts.map +1 -0
- package/dist/router.es.mjs +168 -132
- package/dist/router.es.mjs.map +1 -1
- package/dist/sanitize-Cxvxa-DX.js +283 -0
- package/dist/sanitize-Cxvxa-DX.js.map +1 -0
- package/dist/security/constants.d.ts +42 -0
- package/dist/security/constants.d.ts.map +1 -0
- package/dist/security/csp.d.ts +24 -0
- package/dist/security/csp.d.ts.map +1 -0
- package/dist/security/index.d.ts +4 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/sanitize-core.d.ts +13 -0
- package/dist/security/sanitize-core.d.ts.map +1 -0
- package/dist/security/sanitize.d.ts +5 -57
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/trusted-types.d.ts +25 -0
- package/dist/security/trusted-types.d.ts.map +1 -0
- package/dist/security/types.d.ts +36 -0
- package/dist/security/types.d.ts.map +1 -0
- package/dist/security.es.mjs +50 -277
- package/dist/security.es.mjs.map +1 -1
- package/dist/store/create-store.d.ts +15 -0
- package/dist/store/create-store.d.ts.map +1 -0
- package/dist/store/define-store.d.ts +28 -0
- package/dist/store/define-store.d.ts.map +1 -0
- package/dist/store/devtools.d.ts +22 -0
- package/dist/store/devtools.d.ts.map +1 -0
- package/dist/store/index.d.ts +10 -286
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/mapping.d.ts +28 -0
- package/dist/store/mapping.d.ts.map +1 -0
- package/dist/store/persisted.d.ts +13 -0
- package/dist/store/persisted.d.ts.map +1 -0
- package/dist/store/plugins.d.ts +13 -0
- package/dist/store/plugins.d.ts.map +1 -0
- package/dist/store/registry.d.ts +28 -0
- package/dist/store/registry.d.ts.map +1 -0
- package/dist/store/types.d.ts +71 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/utils.d.ts +28 -0
- package/dist/store/utils.d.ts.map +1 -0
- package/dist/store/watch.d.ts +23 -0
- package/dist/store/watch.d.ts.map +1 -0
- package/dist/store.es.mjs +22 -224
- package/dist/store.es.mjs.map +1 -1
- package/dist/type-guards-BdKlYYlS.js +32 -0
- package/dist/type-guards-BdKlYYlS.js.map +1 -0
- package/dist/untrack-DNnnqdlR.js +6 -0
- package/dist/untrack-DNnnqdlR.js.map +1 -0
- package/dist/view/directives/bind.d.ts +7 -0
- package/dist/view/directives/bind.d.ts.map +1 -0
- package/dist/view/directives/class.d.ts +8 -0
- package/dist/view/directives/class.d.ts.map +1 -0
- package/dist/view/directives/for.d.ts +23 -0
- package/dist/view/directives/for.d.ts.map +1 -0
- package/dist/view/directives/html.d.ts +7 -0
- package/dist/view/directives/html.d.ts.map +1 -0
- package/dist/view/directives/if.d.ts +7 -0
- package/dist/view/directives/if.d.ts.map +1 -0
- package/dist/view/directives/index.d.ts +12 -0
- package/dist/view/directives/index.d.ts.map +1 -0
- package/dist/view/directives/model.d.ts +7 -0
- package/dist/view/directives/model.d.ts.map +1 -0
- package/dist/view/directives/on.d.ts +7 -0
- package/dist/view/directives/on.d.ts.map +1 -0
- package/dist/view/directives/ref.d.ts +7 -0
- package/dist/view/directives/ref.d.ts.map +1 -0
- package/dist/view/directives/show.d.ts +7 -0
- package/dist/view/directives/show.d.ts.map +1 -0
- package/dist/view/directives/style.d.ts +7 -0
- package/dist/view/directives/style.d.ts.map +1 -0
- package/dist/view/directives/text.d.ts +7 -0
- package/dist/view/directives/text.d.ts.map +1 -0
- package/dist/view/evaluate.d.ts +43 -0
- package/dist/view/evaluate.d.ts.map +1 -0
- package/dist/view/index.d.ts +3 -93
- package/dist/view/index.d.ts.map +1 -1
- package/dist/view/mount.d.ts +69 -0
- package/dist/view/mount.d.ts.map +1 -0
- package/dist/view/process.d.ts +26 -0
- package/dist/view/process.d.ts.map +1 -0
- package/dist/view/types.d.ts +36 -0
- package/dist/view/types.d.ts.map +1 -0
- package/dist/view.es.mjs +358 -251
- package/dist/view.es.mjs.map +1 -1
- package/dist/watch-DXXv3iAI.js +58 -0
- package/dist/watch-DXXv3iAI.js.map +1 -0
- package/package.json +14 -14
- package/src/component/component.ts +289 -0
- package/src/component/html.ts +53 -0
- package/src/component/index.ts +40 -414
- package/src/component/props.ts +116 -0
- package/src/component/types.ts +85 -0
- package/src/core/collection.ts +181 -7
- package/src/core/dom.ts +38 -0
- package/src/core/element.ts +59 -25
- package/src/core/index.ts +48 -4
- package/src/core/utils/array.ts +102 -0
- package/src/core/utils/function.ts +151 -0
- package/src/core/utils/index.ts +83 -0
- package/src/core/utils/misc.ts +82 -0
- package/src/core/utils/number.ts +78 -0
- package/src/core/utils/object.ts +206 -0
- package/src/core/utils/string.ts +112 -0
- package/src/core/utils/type-guards.ts +112 -0
- package/src/full.ts +187 -150
- package/src/index.ts +36 -36
- package/src/motion/animate.ts +113 -0
- package/src/motion/easing.ts +40 -0
- package/src/motion/flip.ts +176 -0
- package/src/motion/index.ts +41 -358
- package/src/motion/keyframes.ts +46 -0
- package/src/motion/reduced-motion.ts +17 -0
- package/src/motion/scroll.ts +57 -0
- package/src/motion/spring.ts +150 -0
- package/src/motion/stagger.ts +43 -0
- package/src/motion/timeline.ts +246 -0
- package/src/motion/transition.ts +51 -0
- package/src/motion/types.ts +198 -0
- package/src/platform/storage.ts +215 -208
- package/src/reactive/batch.ts +22 -0
- package/src/reactive/computed.ts +92 -0
- package/src/reactive/core.ts +114 -0
- package/src/reactive/effect.ts +54 -0
- package/src/reactive/index.ts +23 -22
- package/src/reactive/internals.ts +122 -0
- package/src/reactive/linked.ts +56 -0
- package/src/reactive/persisted.ts +74 -0
- package/src/reactive/readonly.ts +35 -0
- package/src/reactive/signal.ts +20 -520
- package/src/reactive/type-guards.ts +22 -0
- package/src/reactive/untrack.ts +31 -0
- package/src/reactive/watch.ts +73 -0
- package/src/router/index.ts +41 -718
- package/src/router/links.ts +130 -0
- package/src/router/match.ts +106 -0
- package/src/router/navigation.ts +71 -0
- package/src/router/query.ts +35 -0
- package/src/router/router.ts +211 -0
- package/src/router/state.ts +46 -0
- package/src/router/types.ts +93 -0
- package/src/router/utils.ts +116 -0
- package/src/security/constants.ts +209 -0
- package/src/security/csp.ts +77 -0
- package/src/security/index.ts +4 -12
- package/src/security/sanitize-core.ts +364 -0
- package/src/security/sanitize.ts +66 -625
- package/src/security/trusted-types.ts +69 -0
- package/src/security/types.ts +40 -0
- package/src/store/create-store.ts +329 -0
- package/src/store/define-store.ts +48 -0
- package/src/store/devtools.ts +45 -0
- package/src/store/index.ts +22 -848
- package/src/store/mapping.ts +73 -0
- package/src/store/persisted.ts +61 -0
- package/src/store/plugins.ts +32 -0
- package/src/store/registry.ts +51 -0
- package/src/store/types.ts +94 -0
- package/src/store/utils.ts +141 -0
- package/src/store/watch.ts +52 -0
- package/src/view/directives/bind.ts +23 -0
- package/src/view/directives/class.ts +70 -0
- package/src/view/directives/for.ts +275 -0
- package/src/view/directives/html.ts +19 -0
- package/src/view/directives/if.ts +30 -0
- package/src/view/directives/index.ts +11 -0
- package/src/view/directives/model.ts +56 -0
- package/src/view/directives/on.ts +41 -0
- package/src/view/directives/ref.ts +41 -0
- package/src/view/directives/show.ts +26 -0
- package/src/view/directives/style.ts +47 -0
- package/src/view/directives/text.ts +15 -0
- package/src/view/evaluate.ts +290 -0
- package/src/view/index.ts +112 -1041
- package/src/view/mount.ts +200 -0
- package/src/view/process.ts +92 -0
- package/src/view/types.ts +44 -0
- package/dist/core/utils.d.ts +0 -313
- package/dist/core/utils.d.ts.map +0 -1
- package/src/core/utils.ts +0 -444
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router utilities.
|
|
3
|
+
* @module bquery/router
|
|
4
|
+
*/
|
|
5
|
+
import { type ReadonlySignal } from '../reactive/index';
|
|
6
|
+
import type { RouteDefinition } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* Flattens nested routes into a single array with full paths.
|
|
9
|
+
* Does NOT include the router base - base is only for browser history.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export declare const flattenRoutes: (routes: RouteDefinition[], parentPath?: string) => RouteDefinition[];
|
|
13
|
+
/**
|
|
14
|
+
* Resolves a route by name and params.
|
|
15
|
+
*
|
|
16
|
+
* @param name - The route name
|
|
17
|
+
* @param params - Route params to interpolate
|
|
18
|
+
* @returns The resolved path
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { resolve } from 'bquery/router';
|
|
23
|
+
*
|
|
24
|
+
* const path = resolve('user', { id: '42' });
|
|
25
|
+
* // Returns '/user/42' if route is defined as { name: 'user', path: '/user/:id' }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const resolve: (name: string, params?: Record<string, string>) => string;
|
|
29
|
+
/**
|
|
30
|
+
* Checks if a path matches the current route.
|
|
31
|
+
*
|
|
32
|
+
* @param path - Path to check
|
|
33
|
+
* @param exact - Whether to match exactly (default: false)
|
|
34
|
+
* @returns True if the path matches
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { isActive } from 'bquery/router';
|
|
39
|
+
*
|
|
40
|
+
* if (isActive('/dashboard')) {
|
|
41
|
+
* // Highlight nav item
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare const isActive: (path: string, exact?: boolean) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Creates a computed signal that checks if a path is active.
|
|
48
|
+
*
|
|
49
|
+
* @param path - Path to check
|
|
50
|
+
* @param exact - Whether to match exactly
|
|
51
|
+
* @returns A reactive signal
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { isActiveSignal } from 'bquery/router';
|
|
56
|
+
* import { effect } from 'bquery/reactive';
|
|
57
|
+
*
|
|
58
|
+
* const dashboardActive = isActiveSignal('/dashboard');
|
|
59
|
+
* effect(() => {
|
|
60
|
+
* navItem.classList.toggle('active', dashboardActive.value);
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare const isActiveSignal: (path: string, exact?: boolean) => ReadonlySignal<boolean>;
|
|
65
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/router/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAM/C;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,eAAe,EAAE,EAAE,mBAAe,KAAG,eAAe,EAiBzF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,SAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAAG,MAiB3E,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,EAAE,eAAa,KAAG,OAGtD,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,EAAE,eAAa,KAAG,cAAc,CAAC,OAAO,CAKlF,CAAC"}
|
package/dist/router.es.mjs
CHANGED
|
@@ -1,166 +1,202 @@
|
|
|
1
|
-
import {
|
|
2
|
-
let
|
|
3
|
-
const
|
|
1
|
+
import { c as N, s as D } from "./core-BhpuvPhy.js";
|
|
2
|
+
let M = null;
|
|
3
|
+
const w = D({
|
|
4
4
|
path: "",
|
|
5
5
|
params: {},
|
|
6
6
|
query: {},
|
|
7
7
|
matched: null,
|
|
8
8
|
hash: ""
|
|
9
|
-
}),
|
|
10
|
-
|
|
9
|
+
}), K = N(() => w.value), y = () => M, k = (e) => {
|
|
10
|
+
M = e;
|
|
11
|
+
}, L = async (e, o = {}) => {
|
|
12
|
+
const r = y();
|
|
13
|
+
if (!r)
|
|
14
|
+
throw new Error("bQuery router: No router initialized. Call createRouter() first.");
|
|
15
|
+
await r[o.replace ? "replace" : "push"](e);
|
|
16
|
+
}, U = () => {
|
|
17
|
+
const e = y();
|
|
18
|
+
e ? e.back() : history.back();
|
|
19
|
+
}, Z = () => {
|
|
20
|
+
const e = y();
|
|
21
|
+
e ? e.forward() : history.forward();
|
|
22
|
+
}, T = (e, o = {}) => (r) => {
|
|
23
|
+
r.preventDefault(), L(e, o).catch((t) => {
|
|
24
|
+
console.error("Navigation failed:", t);
|
|
25
|
+
});
|
|
26
|
+
}, j = (e) => {
|
|
27
|
+
const o = e ?? (typeof document < "u" ? document.body : null);
|
|
28
|
+
if (!o)
|
|
29
|
+
return () => {
|
|
30
|
+
};
|
|
31
|
+
const r = (t) => {
|
|
32
|
+
if (!(t instanceof MouseEvent) || t.defaultPrevented || t.button !== 0 || t.ctrlKey || t.metaKey || t.shiftKey || t.altKey || typeof Element > "u" || !(t.target instanceof Element)) return;
|
|
33
|
+
const n = t.target.closest("a");
|
|
34
|
+
if (!n) return;
|
|
35
|
+
const l = n.ownerDocument.defaultView?.HTMLAnchorElement ?? HTMLAnchorElement;
|
|
36
|
+
if (!(n instanceof l) || n.target || n.hasAttribute("download") || typeof window > "u" || n.origin !== window.location.origin) return;
|
|
37
|
+
const f = y();
|
|
38
|
+
if (!f) {
|
|
39
|
+
t.preventDefault(), L(n.pathname + n.search + n.hash).catch((u) => {
|
|
40
|
+
console.error("Navigation failed:", u);
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const d = f.base, $ = f.hash;
|
|
45
|
+
let g;
|
|
46
|
+
if ($ && n.hash && n.hash.startsWith("#/"))
|
|
47
|
+
g = n.hash.slice(1);
|
|
48
|
+
else {
|
|
49
|
+
let u = n.pathname;
|
|
50
|
+
d && d !== "/" && u.startsWith(d) && (u = u.slice(d.length) || "/"), g = u + n.search + n.hash;
|
|
51
|
+
}
|
|
52
|
+
t.preventDefault(), L(g).catch((u) => {
|
|
53
|
+
console.error("Navigation failed:", u);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
return o.addEventListener("click", r), () => o.removeEventListener("click", r);
|
|
57
|
+
}, _ = (e) => {
|
|
58
|
+
const o = {};
|
|
59
|
+
return new URLSearchParams(e).forEach((t, s) => {
|
|
60
|
+
const n = o[s];
|
|
61
|
+
n === void 0 ? o[s] = t : Array.isArray(n) ? n.push(t) : o[s] = [n, t];
|
|
62
|
+
}), o;
|
|
63
|
+
}, q = (e) => {
|
|
64
|
+
if (e === "*")
|
|
11
65
|
return /^.*$/;
|
|
12
|
-
const o = "\0P\0",
|
|
13
|
-
let t =
|
|
14
|
-
t = t.replace(/\*/g,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
u[p] = t[w + 1] || "";
|
|
27
|
-
}), { matched: e, params: u };
|
|
66
|
+
const o = "\0P\0", r = "\0W\0";
|
|
67
|
+
let t = e.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, () => o);
|
|
68
|
+
return t = t.replace(/\*/g, r), t = t.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&"), t = t.replace(/\u0000P\u0000/g, () => "([^/]+)"), t = t.replace(/\u0000W\u0000/g, ".*"), new RegExp(`^${t}$`);
|
|
69
|
+
}, H = (e) => {
|
|
70
|
+
const o = e.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
|
|
71
|
+
return o ? o.map((r) => r.slice(1)) : [];
|
|
72
|
+
}, Q = (e, o) => {
|
|
73
|
+
for (const r of o) {
|
|
74
|
+
const t = q(r.path), s = e.match(t);
|
|
75
|
+
if (s) {
|
|
76
|
+
const n = H(r.path), h = {};
|
|
77
|
+
return n.forEach((l, f) => {
|
|
78
|
+
h[l] = s[f + 1] || "";
|
|
79
|
+
}), { matched: r, params: h };
|
|
28
80
|
}
|
|
29
81
|
}
|
|
30
82
|
return null;
|
|
31
|
-
},
|
|
32
|
-
const
|
|
33
|
-
return new URLSearchParams(r).forEach((a, t) => {
|
|
34
|
-
const n = o[t];
|
|
35
|
-
n === void 0 ? o[t] = a : Array.isArray(n) ? n.push(a) : o[t] = [n, a];
|
|
36
|
-
}), o;
|
|
37
|
-
}, y = (r, o, e, a) => {
|
|
38
|
-
const t = W(r, a);
|
|
83
|
+
}, P = (e, o, r, t) => {
|
|
84
|
+
const s = Q(e, t);
|
|
39
85
|
return {
|
|
40
|
-
path:
|
|
41
|
-
params:
|
|
42
|
-
query:
|
|
43
|
-
matched:
|
|
44
|
-
hash:
|
|
86
|
+
path: e,
|
|
87
|
+
params: s?.params ?? {},
|
|
88
|
+
query: _(o),
|
|
89
|
+
matched: s?.matched ?? null,
|
|
90
|
+
hash: r.replace(/^#/, "")
|
|
45
91
|
};
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
92
|
+
}, z = (e, o = "") => {
|
|
93
|
+
const r = [];
|
|
94
|
+
for (const t of e) {
|
|
95
|
+
const s = t.path === "*" ? "*" : `${o}${t.path}`.replace(/\/+/g, "/");
|
|
96
|
+
r.push({
|
|
97
|
+
...t,
|
|
98
|
+
path: s
|
|
99
|
+
}), t.children && r.push(...z(t.children, s));
|
|
100
|
+
}
|
|
101
|
+
return r;
|
|
102
|
+
}, I = (e, o = {}) => {
|
|
103
|
+
const r = y();
|
|
104
|
+
if (!r)
|
|
105
|
+
throw new Error("bQuery router: No router initialized.");
|
|
106
|
+
const t = r.routes.find((n) => n.name === e);
|
|
107
|
+
if (!t)
|
|
108
|
+
throw new Error(`bQuery router: Route "${e}" not found.`);
|
|
109
|
+
let s = t.path;
|
|
110
|
+
for (const [n, h] of Object.entries(o))
|
|
111
|
+
s = s.replace(`:${n}`, encodeURIComponent(h));
|
|
112
|
+
return s;
|
|
113
|
+
}, G = (e, o = !1) => {
|
|
114
|
+
const r = w.value.path;
|
|
115
|
+
return o ? r === e : r.startsWith(e);
|
|
116
|
+
}, V = (e, o = !1) => N(() => {
|
|
117
|
+
const r = w.value.path;
|
|
118
|
+
return o ? r === e : r.startsWith(e);
|
|
119
|
+
}), B = (e) => {
|
|
120
|
+
const o = y();
|
|
121
|
+
o && o.destroy();
|
|
122
|
+
const { routes: r, base: t = "", hash: s = !1 } = e, n = [], h = [], l = z(r), f = () => {
|
|
123
|
+
if (s) {
|
|
124
|
+
const i = window.location.hash.slice(1) || "/", [m, c = ""] = i.split("#"), [R, p = ""] = m.split("?");
|
|
59
125
|
return {
|
|
60
|
-
pathname:
|
|
61
|
-
search:
|
|
62
|
-
hash:
|
|
126
|
+
pathname: R,
|
|
127
|
+
search: p ? `?${p}` : "",
|
|
128
|
+
hash: c ? `#${c}` : ""
|
|
63
129
|
};
|
|
64
130
|
}
|
|
65
|
-
let
|
|
66
|
-
return
|
|
67
|
-
pathname:
|
|
131
|
+
let a = window.location.pathname;
|
|
132
|
+
return t && (a === t || a.startsWith(t + "/")) && (a = a.slice(t.length) || "/"), {
|
|
133
|
+
pathname: a,
|
|
68
134
|
search: window.location.search,
|
|
69
135
|
hash: window.location.hash
|
|
70
136
|
};
|
|
71
|
-
},
|
|
72
|
-
const { pathname:
|
|
73
|
-
|
|
74
|
-
}, $ = async (
|
|
75
|
-
const { pathname:
|
|
76
|
-
for (const v of t)
|
|
77
|
-
if (await v(k, l) === !1)
|
|
78
|
-
return;
|
|
79
|
-
const L = a ? `#${s}` : `${e}${s}`;
|
|
80
|
-
history[c]({}, "", L), w();
|
|
137
|
+
}, d = () => {
|
|
138
|
+
const { pathname: a, search: i, hash: m } = f(), c = P(a, i, m, l);
|
|
139
|
+
w.value = c;
|
|
140
|
+
}, $ = async (a, i) => {
|
|
141
|
+
const { pathname: m, search: c, hash: R } = f(), p = P(m, c, R, l), A = new URL(a, window.location.origin), E = P(A.pathname, A.search, A.hash, l);
|
|
81
142
|
for (const v of n)
|
|
82
|
-
v(
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
143
|
+
if (await v(E, p) === !1)
|
|
144
|
+
return;
|
|
145
|
+
const b = s ? `#${a}` : `${t}${a}`;
|
|
146
|
+
history[i]({}, "", b), d();
|
|
147
|
+
for (const v of h)
|
|
148
|
+
v(w.value, p);
|
|
149
|
+
}, g = async () => {
|
|
150
|
+
const { pathname: a, search: i, hash: m } = f(), c = w.value, R = P(a, i, m, l);
|
|
151
|
+
for (const p of n)
|
|
152
|
+
if (await p(R, c) === !1) {
|
|
153
|
+
const E = new URLSearchParams(
|
|
154
|
+
Object.entries(c.query).flatMap(
|
|
155
|
+
([S, x]) => Array.isArray(x) ? x.map((C) => [S, C]) : [[S, x]]
|
|
156
|
+
)
|
|
157
|
+
).toString(), b = E ? `?${E}` : "", v = c.hash ? `#${c.hash}` : "", W = s ? `#${c.path}${b}${v}` : `${t}${c.path}${b}${v}`;
|
|
158
|
+
history.replaceState({}, "", W);
|
|
89
159
|
return;
|
|
90
160
|
}
|
|
91
|
-
|
|
92
|
-
for (const
|
|
93
|
-
|
|
161
|
+
d();
|
|
162
|
+
for (const p of h)
|
|
163
|
+
p(w.value, c);
|
|
94
164
|
};
|
|
95
|
-
window.addEventListener("popstate",
|
|
96
|
-
const
|
|
97
|
-
push: (
|
|
98
|
-
replace: (
|
|
165
|
+
window.addEventListener("popstate", g), d();
|
|
166
|
+
const u = {
|
|
167
|
+
push: (a) => $(a, "pushState"),
|
|
168
|
+
replace: (a) => $(a, "replaceState"),
|
|
99
169
|
back: () => history.back(),
|
|
100
170
|
forward: () => history.forward(),
|
|
101
|
-
go: (
|
|
102
|
-
beforeEach: (
|
|
103
|
-
const
|
|
104
|
-
|
|
171
|
+
go: (a) => history.go(a),
|
|
172
|
+
beforeEach: (a) => (n.push(a), () => {
|
|
173
|
+
const i = n.indexOf(a);
|
|
174
|
+
i > -1 && n.splice(i, 1);
|
|
105
175
|
}),
|
|
106
|
-
afterEach: (
|
|
107
|
-
const
|
|
108
|
-
|
|
176
|
+
afterEach: (a) => (h.push(a), () => {
|
|
177
|
+
const i = h.indexOf(a);
|
|
178
|
+
i > -1 && h.splice(i, 1);
|
|
109
179
|
}),
|
|
110
|
-
currentRoute:
|
|
111
|
-
routes:
|
|
180
|
+
currentRoute: K,
|
|
181
|
+
routes: l,
|
|
182
|
+
base: t,
|
|
183
|
+
hash: s,
|
|
112
184
|
destroy: () => {
|
|
113
|
-
window.removeEventListener("popstate",
|
|
185
|
+
window.removeEventListener("popstate", g), n.length = 0, h.length = 0, k(null);
|
|
114
186
|
}
|
|
115
187
|
};
|
|
116
|
-
return
|
|
117
|
-
}, x = (r, o = "") => {
|
|
118
|
-
const e = [];
|
|
119
|
-
for (const a of r) {
|
|
120
|
-
const t = a.path === "*" ? "*" : `${o}${a.path}`.replace(/\/+/g, "/");
|
|
121
|
-
e.push({
|
|
122
|
-
...a,
|
|
123
|
-
path: t
|
|
124
|
-
}), a.children && e.push(...x(a.children, t));
|
|
125
|
-
}
|
|
126
|
-
return e;
|
|
127
|
-
}, I = (r, o = {}) => {
|
|
128
|
-
if (!h)
|
|
129
|
-
throw new Error("bQuery router: No router initialized.");
|
|
130
|
-
const e = h.routes.find((t) => t.name === r);
|
|
131
|
-
if (!e)
|
|
132
|
-
throw new Error(`bQuery router: Route "${r}" not found.`);
|
|
133
|
-
let a = e.path;
|
|
134
|
-
for (const [t, n] of Object.entries(o))
|
|
135
|
-
a = a.replace(`:${t}`, encodeURIComponent(n));
|
|
136
|
-
return a;
|
|
137
|
-
}, M = (r, o = !1) => {
|
|
138
|
-
const e = d.value.path;
|
|
139
|
-
return o ? e === r : e.startsWith(r);
|
|
140
|
-
}, U = (r, o = !1) => E(() => {
|
|
141
|
-
const e = d.value.path;
|
|
142
|
-
return o ? e === r : e.startsWith(r);
|
|
143
|
-
}), j = (r, o = {}) => (e) => {
|
|
144
|
-
e.preventDefault(), P(r, o);
|
|
145
|
-
}, H = (r = document.body) => {
|
|
146
|
-
const o = (e) => {
|
|
147
|
-
const t = e.target.closest("a");
|
|
148
|
-
if (!t || t.target || t.hasAttribute("download") || t.origin !== window.location.origin) return;
|
|
149
|
-
const n = t.pathname + t.search + t.hash;
|
|
150
|
-
e.preventDefault(), P(n);
|
|
151
|
-
};
|
|
152
|
-
return r.addEventListener("click", o), () => r.removeEventListener("click", o);
|
|
188
|
+
return k(u), u;
|
|
153
189
|
};
|
|
154
190
|
export {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
191
|
+
U as back,
|
|
192
|
+
B as createRouter,
|
|
193
|
+
K as currentRoute,
|
|
158
194
|
Z as forward,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
195
|
+
j as interceptLinks,
|
|
196
|
+
G as isActive,
|
|
197
|
+
V as isActiveSignal,
|
|
198
|
+
T as link,
|
|
199
|
+
L as navigate,
|
|
164
200
|
I as resolve
|
|
165
201
|
};
|
|
166
202
|
//# sourceMappingURL=router.es.mjs.map
|
package/dist/router.es.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.es.mjs","sources":["../src/router/index.ts"],"sourcesContent":["/**\r\n * Minimal SPA router with History API integration.\r\n *\r\n * This module provides a lightweight, signal-based router for single-page\r\n * applications. Features include:\r\n * - History API navigation\r\n * - Route matching with params and wildcards\r\n * - Lazy route loading\r\n * - Navigation guards (beforeEach, afterEach)\r\n * - Reactive current route via signals\r\n * - Multi-value query params (e.g., `?tag=a&tag=b` → `{ tag: ['a', 'b'] }`)\r\n *\r\n * @module bquery/router\r\n *\r\n * @example\r\n * ```ts\r\n * import { createRouter, navigate, currentRoute } from 'bquery/router';\r\n * import { effect } from 'bquery/reactive';\r\n *\r\n * const router = createRouter({\r\n * routes: [\r\n * { path: '/', component: () => import('./Home') },\r\n * { path: '/user/:id', component: () => import('./User') },\r\n * { path: '*', component: () => import('./NotFound') },\r\n * ],\r\n * });\r\n *\r\n * effect(() => {\r\n * console.log('Route changed:', currentRoute.value);\r\n * });\r\n *\r\n * navigate('/user/42');\r\n * ```\r\n */\r\n\r\nimport { computed, signal, type ReadonlySignal, type Signal } from '../reactive/index';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Represents a parsed route with matched params.\r\n */\r\nexport type Route = {\r\n /** The current path (e.g., '/user/42') */\r\n path: string;\r\n /** Extracted route params (e.g., { id: '42' }) */\r\n params: Record<string, string>;\r\n /**\r\n * Query string params.\r\n * Each key maps to a single string value by default.\r\n * Only keys that appear multiple times in the query string become arrays.\r\n * @example\r\n * // ?foo=1 → { foo: '1' }\r\n * // ?tag=a&tag=b → { tag: ['a', 'b'] }\r\n * // ?x=1&y=2&x=3 → { x: ['1', '3'], y: '2' }\r\n */\r\n query: Record<string, string | string[]>;\r\n /** The matched route definition */\r\n matched: RouteDefinition | null;\r\n /** Hash fragment without # */\r\n hash: string;\r\n};\r\n\r\n/**\r\n * Route definition for configuration.\r\n */\r\nexport type RouteDefinition = {\r\n /** Path pattern (e.g., '/user/:id', '/posts/*') */\r\n path: string;\r\n /** Component loader (sync or async) */\r\n component: () => unknown | Promise<unknown>;\r\n /** Optional route name for programmatic navigation */\r\n name?: string;\r\n /** Optional metadata */\r\n meta?: Record<string, unknown>;\r\n /** Nested child routes */\r\n children?: RouteDefinition[];\r\n};\r\n\r\n/**\r\n * Router configuration options.\r\n */\r\nexport type RouterOptions = {\r\n /** Array of route definitions */\r\n routes: RouteDefinition[];\r\n /** Base path for all routes (default: '') */\r\n base?: string;\r\n /** Use hash-based routing instead of history (default: false) */\r\n hash?: boolean;\r\n};\r\n\r\n/**\r\n * Navigation guard function type.\r\n */\r\nexport type NavigationGuard = (to: Route, from: Route) => boolean | void | Promise<boolean | void>;\r\n\r\n/**\r\n * Router instance returned by createRouter.\r\n */\r\nexport type Router = {\r\n /** Navigate to a path */\r\n push: (path: string) => Promise<void>;\r\n /** Replace current history entry */\r\n replace: (path: string) => Promise<void>;\r\n /** Go back in history */\r\n back: () => void;\r\n /** Go forward in history */\r\n forward: () => void;\r\n /** Go to a specific history entry */\r\n go: (delta: number) => void;\r\n /** Add a beforeEach guard */\r\n beforeEach: (guard: NavigationGuard) => () => void;\r\n /** Add an afterEach hook */\r\n afterEach: (hook: (to: Route, from: Route) => void) => () => void;\r\n /** Current route (reactive) */\r\n currentRoute: ReadonlySignal<Route>;\r\n /** All route definitions */\r\n routes: RouteDefinition[];\r\n /** Destroy the router and cleanup listeners */\r\n destroy: () => void;\r\n};\r\n\r\n// ============================================================================\r\n// Internal State\r\n// ============================================================================\r\n\r\n/** @internal */\r\nlet activeRouter: Router | null = null;\r\n\r\n/** @internal */\r\nconst routeSignal: Signal<Route> = signal<Route>({\r\n path: '',\r\n params: {},\r\n query: {},\r\n matched: null,\r\n hash: '',\r\n});\r\n\r\n/**\r\n * Reactive signal containing the current route.\r\n *\r\n * @example\r\n * ```ts\r\n * import { currentRoute } from 'bquery/router';\r\n * import { effect } from 'bquery/reactive';\r\n *\r\n * effect(() => {\r\n * document.title = `Page: ${currentRoute.value.path}`;\r\n * });\r\n * ```\r\n */\r\nexport const currentRoute: ReadonlySignal<Route> = computed(() => routeSignal.value);\r\n\r\n// ============================================================================\r\n// Route Matching\r\n// ============================================================================\r\n\r\n/**\r\n * Converts a route path pattern to a RegExp for matching.\r\n * Uses placeholder approach to preserve :param and * patterns during escaping.\r\n * @internal\r\n */\r\nconst pathToRegex = (path: string): RegExp => {\r\n // Handle wildcard-only route\r\n if (path === '*') {\r\n return /^.*$/;\r\n }\r\n\r\n // Unique placeholders using null chars (won't appear in normal paths)\r\n const PARAM_MARKER = '\\u0000P\\u0000';\r\n const WILDCARD_MARKER = '\\u0000W\\u0000';\r\n\r\n // Store param names for restoration\r\n const paramNames: string[] = [];\r\n\r\n // Step 1: Extract :param patterns before escaping\r\n let pattern = path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, name) => {\r\n paramNames.push(name);\r\n return PARAM_MARKER;\r\n });\r\n\r\n // Step 2: Extract * wildcards before escaping\r\n pattern = pattern.replace(/\\*/g, WILDCARD_MARKER);\r\n\r\n // Step 3: Escape ALL regex metacharacters: \\ ^ $ . * + ? ( ) [ ] { } |\r\n pattern = pattern.replace(/[\\\\^$.*+?()[\\]{}|]/g, '\\\\$&');\r\n\r\n // Step 4: Restore param capture groups\r\n let paramIdx = 0;\r\n pattern = pattern.replace(/\\u0000P\\u0000/g, () => `(?<${paramNames[paramIdx++]}>[^/]+)`);\r\n\r\n // Step 5: Restore wildcards as .*\r\n pattern = pattern.replace(/\\u0000W\\u0000/g, '.*');\r\n\r\n return new RegExp(`^${pattern}$`);\r\n};\r\n\r\n/**\r\n * Extracts param names from a route path.\r\n * @internal\r\n */\r\nconst extractParamNames = (path: string): string[] => {\r\n const matches = path.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);\r\n return matches ? matches.map((m) => m.slice(1)) : [];\r\n};\r\n\r\n/**\r\n * Matches a path against route definitions and extracts params.\r\n * @internal\r\n */\r\nconst matchRoute = (\r\n path: string,\r\n routes: RouteDefinition[]\r\n): { matched: RouteDefinition; params: Record<string, string> } | null => {\r\n for (const route of routes) {\r\n const regex = pathToRegex(route.path);\r\n const match = path.match(regex);\r\n\r\n if (match) {\r\n const paramNames = extractParamNames(route.path);\r\n const params: Record<string, string> = {};\r\n\r\n // Extract named groups if available\r\n if (match.groups) {\r\n Object.assign(params, match.groups);\r\n } else {\r\n // Fallback for browsers without named groups\r\n paramNames.forEach((name, index) => {\r\n params[name] = match[index + 1] || '';\r\n });\r\n }\r\n\r\n return { matched: route, params };\r\n }\r\n }\r\n\r\n return null;\r\n};\r\n\r\n/**\r\n * Parses query string into an object.\r\n * Single values are stored as strings, duplicate keys become arrays.\r\n * @internal\r\n *\r\n * @example\r\n * parseQuery('?foo=1') // { foo: '1' }\r\n * parseQuery('?tag=a&tag=b') // { tag: ['a', 'b'] }\r\n * parseQuery('?x=1&y=2&x=3') // { x: ['1', '3'], y: '2' }\r\n */\r\nconst parseQuery = (search: string): Record<string, string | string[]> => {\r\n const query: Record<string, string | string[]> = {};\r\n const params = new URLSearchParams(search);\r\n\r\n params.forEach((value, key) => {\r\n const existing = query[key];\r\n if (existing === undefined) {\r\n // First occurrence: store as string\r\n query[key] = value;\r\n } else if (Array.isArray(existing)) {\r\n // Already an array: append\r\n existing.push(value);\r\n } else {\r\n // Second occurrence: convert to array\r\n query[key] = [existing, value];\r\n }\r\n });\r\n\r\n return query;\r\n};\r\n\r\n/**\r\n * Creates a Route object from the current URL.\r\n * @internal\r\n */\r\nconst createRoute = (\r\n pathname: string,\r\n search: string,\r\n hash: string,\r\n routes: RouteDefinition[]\r\n): Route => {\r\n const result = matchRoute(pathname, routes);\r\n\r\n return {\r\n path: pathname,\r\n params: result?.params ?? {},\r\n query: parseQuery(search),\r\n matched: result?.matched ?? null,\r\n hash: hash.replace(/^#/, ''),\r\n };\r\n};\r\n\r\n// ============================================================================\r\n// Navigation\r\n// ============================================================================\r\n\r\n/**\r\n * Navigates to a new path.\r\n *\r\n * @param path - The path to navigate to\r\n * @param options - Navigation options\r\n *\r\n * @example\r\n * ```ts\r\n * import { navigate } from 'bquery/router';\r\n *\r\n * // Push to history\r\n * await navigate('/dashboard');\r\n *\r\n * // Replace current entry\r\n * await navigate('/login', { replace: true });\r\n * ```\r\n */\r\nexport const navigate = async (\r\n path: string,\r\n options: { replace?: boolean } = {}\r\n): Promise<void> => {\r\n if (!activeRouter) {\r\n throw new Error('bQuery router: No router initialized. Call createRouter() first.');\r\n }\r\n\r\n await activeRouter[options.replace ? 'replace' : 'push'](path);\r\n};\r\n\r\n/**\r\n * Programmatically go back in history.\r\n *\r\n * @example\r\n * ```ts\r\n * import { back } from 'bquery/router';\r\n * back();\r\n * ```\r\n */\r\nexport const back = (): void => {\r\n if (activeRouter) {\r\n activeRouter.back();\r\n } else {\r\n history.back();\r\n }\r\n};\r\n\r\n/**\r\n * Programmatically go forward in history.\r\n *\r\n * @example\r\n * ```ts\r\n * import { forward } from 'bquery/router';\r\n * forward();\r\n * ```\r\n */\r\nexport const forward = (): void => {\r\n if (activeRouter) {\r\n activeRouter.forward();\r\n } else {\r\n history.forward();\r\n }\r\n};\r\n\r\n// ============================================================================\r\n// Router Creation\r\n// ============================================================================\r\n\r\n/**\r\n * Creates and initializes a router instance.\r\n *\r\n * @param options - Router configuration\r\n * @returns The router instance\r\n *\r\n * @example\r\n * ```ts\r\n * import { createRouter } from 'bquery/router';\r\n *\r\n * const router = createRouter({\r\n * routes: [\r\n * { path: '/', component: () => import('./pages/Home') },\r\n * { path: '/about', component: () => import('./pages/About') },\r\n * { path: '/user/:id', component: () => import('./pages/User') },\r\n * { path: '*', component: () => import('./pages/NotFound') },\r\n * ],\r\n * base: '/app',\r\n * });\r\n *\r\n * router.beforeEach((to, from) => {\r\n * if (to.path === '/admin' && !isAuthenticated()) {\r\n * return false; // Cancel navigation\r\n * }\r\n * });\r\n * ```\r\n */\r\nexport const createRouter = (options: RouterOptions): Router => {\r\n // Clean up any existing router to prevent guard leakage\r\n if (activeRouter) {\r\n activeRouter.destroy();\r\n }\r\n\r\n const { routes, base = '', hash: useHash = false } = options;\r\n\r\n // Instance-specific guards and hooks (not shared globally)\r\n const beforeGuards: NavigationGuard[] = [];\r\n const afterHooks: Array<(to: Route, from: Route) => void> = [];\r\n\r\n // Flatten nested routes\r\n const flatRoutes = flattenRoutes(routes, base);\r\n\r\n /**\r\n * Gets the current path from the URL.\r\n */\r\n const getCurrentPath = (): { pathname: string; search: string; hash: string } => {\r\n if (useHash) {\r\n const hashPath = window.location.hash.slice(1) || '/';\r\n const [pathname, rest = ''] = hashPath.split('?');\r\n const [search, hashPart = ''] = rest.split('#');\r\n return {\r\n pathname,\r\n search: search ? `?${search}` : '',\r\n hash: hashPart ? `#${hashPart}` : '',\r\n };\r\n }\r\n\r\n let pathname = window.location.pathname;\r\n if (base && pathname.startsWith(base)) {\r\n pathname = pathname.slice(base.length) || '/';\r\n }\r\n\r\n return {\r\n pathname,\r\n search: window.location.search,\r\n hash: window.location.hash,\r\n };\r\n };\r\n\r\n /**\r\n * Updates the route signal with current URL state.\r\n */\r\n const syncRoute = (): void => {\r\n const { pathname, search, hash } = getCurrentPath();\r\n const newRoute = createRoute(pathname, search, hash, flatRoutes);\r\n routeSignal.value = newRoute;\r\n };\r\n\r\n /**\r\n * Performs navigation with guards.\r\n */\r\n const performNavigation = async (\r\n path: string,\r\n method: 'pushState' | 'replaceState'\r\n ): Promise<void> => {\r\n const { pathname, search, hash } = getCurrentPath();\r\n const from = createRoute(pathname, search, hash, flatRoutes);\r\n\r\n // Parse the target path\r\n const url = new URL(path, window.location.origin);\r\n const toPath = useHash ? path : url.pathname;\r\n const to = createRoute(toPath, url.search, url.hash, flatRoutes);\r\n\r\n // Run beforeEach guards\r\n for (const guard of beforeGuards) {\r\n const result = await guard(to, from);\r\n if (result === false) {\r\n return; // Cancel navigation\r\n }\r\n }\r\n\r\n // Update browser history\r\n const fullPath = useHash ? `#${path}` : `${base}${path}`;\r\n history[method]({}, '', fullPath);\r\n\r\n // Update route signal\r\n syncRoute();\r\n\r\n // Run afterEach hooks\r\n for (const hook of afterHooks) {\r\n hook(routeSignal.value, from);\r\n }\r\n };\r\n\r\n /**\r\n * Handle popstate events (back/forward).\r\n */\r\n const handlePopState = async (): Promise<void> => {\r\n const { pathname, search, hash } = getCurrentPath();\r\n const from = routeSignal.value;\r\n const to = createRoute(pathname, search, hash, flatRoutes);\r\n\r\n // Run beforeEach guards (supports async guards)\r\n for (const guard of beforeGuards) {\r\n const result = await guard(to, from);\r\n if (result === false) {\r\n // Restore previous state\r\n const restorePath = useHash ? `#${from.path}` : `${base}${from.path}`;\r\n history.pushState({}, '', restorePath);\r\n return;\r\n }\r\n }\r\n\r\n syncRoute();\r\n\r\n for (const hook of afterHooks) {\r\n hook(routeSignal.value, from);\r\n }\r\n };\r\n\r\n // Attach popstate listener\r\n window.addEventListener('popstate', handlePopState);\r\n\r\n // Initialize route\r\n syncRoute();\r\n\r\n const router: Router = {\r\n push: (path: string) => performNavigation(path, 'pushState'),\r\n replace: (path: string) => performNavigation(path, 'replaceState'),\r\n back: () => history.back(),\r\n forward: () => history.forward(),\r\n go: (delta: number) => history.go(delta),\r\n\r\n beforeEach: (guard: NavigationGuard) => {\r\n beforeGuards.push(guard);\r\n return () => {\r\n const index = beforeGuards.indexOf(guard);\r\n if (index > -1) beforeGuards.splice(index, 1);\r\n };\r\n },\r\n\r\n afterEach: (hook: (to: Route, from: Route) => void) => {\r\n afterHooks.push(hook);\r\n return () => {\r\n const index = afterHooks.indexOf(hook);\r\n if (index > -1) afterHooks.splice(index, 1);\r\n };\r\n },\r\n\r\n currentRoute,\r\n routes: flatRoutes,\r\n\r\n destroy: () => {\r\n window.removeEventListener('popstate', handlePopState);\r\n beforeGuards.length = 0;\r\n afterHooks.length = 0;\r\n activeRouter = null;\r\n },\r\n };\r\n\r\n activeRouter = router;\r\n return router;\r\n};\r\n\r\n// ============================================================================\r\n// Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Flattens nested routes into a single array with full paths.\r\n * @internal\r\n */\r\nconst flattenRoutes = (routes: RouteDefinition[], base = ''): RouteDefinition[] => {\r\n const result: RouteDefinition[] = [];\r\n\r\n for (const route of routes) {\r\n const fullPath = route.path === '*' ? '*' : `${base}${route.path}`.replace(/\\/+/g, '/');\r\n\r\n result.push({\r\n ...route,\r\n path: fullPath,\r\n });\r\n\r\n if (route.children) {\r\n result.push(...flattenRoutes(route.children, fullPath));\r\n }\r\n }\r\n\r\n return result;\r\n};\r\n\r\n/**\r\n * Resolves a route by name and params.\r\n *\r\n * @param name - The route name\r\n * @param params - Route params to interpolate\r\n * @returns The resolved path\r\n *\r\n * @example\r\n * ```ts\r\n * import { resolve } from 'bquery/router';\r\n *\r\n * const path = resolve('user', { id: '42' });\r\n * // Returns '/user/42' if route is defined as { name: 'user', path: '/user/:id' }\r\n * ```\r\n */\r\nexport const resolve = (name: string, params: Record<string, string> = {}): string => {\r\n if (!activeRouter) {\r\n throw new Error('bQuery router: No router initialized.');\r\n }\r\n\r\n const route = activeRouter.routes.find((r) => r.name === name);\r\n if (!route) {\r\n throw new Error(`bQuery router: Route \"${name}\" not found.`);\r\n }\r\n\r\n let path = route.path;\r\n for (const [key, value] of Object.entries(params)) {\r\n path = path.replace(`:${key}`, encodeURIComponent(value));\r\n }\r\n\r\n return path;\r\n};\r\n\r\n/**\r\n * Checks if a path matches the current route.\r\n *\r\n * @param path - Path to check\r\n * @param exact - Whether to match exactly (default: false)\r\n * @returns True if the path matches\r\n *\r\n * @example\r\n * ```ts\r\n * import { isActive } from 'bquery/router';\r\n *\r\n * if (isActive('/dashboard')) {\r\n * // Highlight nav item\r\n * }\r\n * ```\r\n */\r\nexport const isActive = (path: string, exact = false): boolean => {\r\n const current = routeSignal.value.path;\r\n return exact ? current === path : current.startsWith(path);\r\n};\r\n\r\n/**\r\n * Creates a computed signal that checks if a path is active.\r\n *\r\n * @param path - Path to check\r\n * @param exact - Whether to match exactly\r\n * @returns A reactive signal\r\n *\r\n * @example\r\n * ```ts\r\n * import { isActiveSignal } from 'bquery/router';\r\n * import { effect } from 'bquery/reactive';\r\n *\r\n * const dashboardActive = isActiveSignal('/dashboard');\r\n * effect(() => {\r\n * navItem.classList.toggle('active', dashboardActive.value);\r\n * });\r\n * ```\r\n */\r\nexport const isActiveSignal = (path: string, exact = false): ReadonlySignal<boolean> => {\r\n return computed(() => {\r\n const current = routeSignal.value.path;\r\n return exact ? current === path : current.startsWith(path);\r\n });\r\n};\r\n\r\n// ============================================================================\r\n// Router Link Helper\r\n// ============================================================================\r\n\r\n/**\r\n * Creates click handler for router links.\r\n * Attach to anchor elements to enable client-side navigation.\r\n *\r\n * @param path - Target path\r\n * @param options - Navigation options\r\n * @returns Click event handler\r\n *\r\n * @example\r\n * ```ts\r\n * import { link } from 'bquery/router';\r\n * import { $ } from 'bquery/core';\r\n *\r\n * $('#nav-home').on('click', link('/'));\r\n * $('#nav-about').on('click', link('/about'));\r\n * ```\r\n */\r\nexport const link = (path: string, options: { replace?: boolean } = {}): ((e: Event) => void) => {\r\n return (e: Event) => {\r\n e.preventDefault();\r\n navigate(path, options);\r\n };\r\n};\r\n\r\n/**\r\n * Intercepts all link clicks within a container for client-side routing.\r\n * Only intercepts links with matching origins and no target attribute.\r\n *\r\n * @param container - The container element to intercept links in\r\n * @returns Cleanup function to remove the listener\r\n *\r\n * @example\r\n * ```ts\r\n * import { interceptLinks } from 'bquery/router';\r\n *\r\n * // Intercept all links in the app\r\n * const cleanup = interceptLinks(document.body);\r\n *\r\n * // Later, remove the interceptor\r\n * cleanup();\r\n * ```\r\n */\r\nexport const interceptLinks = (container: Element = document.body): (() => void) => {\r\n const handler = (e: Event) => {\r\n const target = e.target as HTMLElement;\r\n const anchor = target.closest('a');\r\n\r\n if (!anchor) return;\r\n if (anchor.target) return; // Has target attribute\r\n if (anchor.hasAttribute('download')) return;\r\n if (anchor.origin !== window.location.origin) return; // External link\r\n\r\n const path = anchor.pathname + anchor.search + anchor.hash;\r\n\r\n e.preventDefault();\r\n navigate(path);\r\n };\r\n\r\n container.addEventListener('click', handler);\r\n return () => container.removeEventListener('click', handler);\r\n};\r\n"],"names":["activeRouter","routeSignal","signal","currentRoute","computed","pathToRegex","path","PARAM_MARKER","WILDCARD_MARKER","paramNames","pattern","_","name","paramIdx","extractParamNames","matches","m","matchRoute","routes","route","regex","match","params","index","parseQuery","search","query","value","key","existing","createRoute","pathname","hash","result","navigate","options","back","forward","createRouter","base","useHash","beforeGuards","afterHooks","flatRoutes","flattenRoutes","getCurrentPath","hashPath","rest","hashPart","syncRoute","newRoute","performNavigation","method","from","url","toPath","to","guard","fullPath","hook","handlePopState","restorePath","router","delta","resolve","r","isActive","exact","current","isActiveSignal","link","interceptLinks","container","handler","anchor"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.es.mjs","sources":["../src/router/state.ts","../src/router/navigation.ts","../src/router/links.ts","../src/router/query.ts","../src/router/match.ts","../src/router/utils.ts","../src/router/router.ts"],"sourcesContent":["/**\n * Internal router state (active router and current route signal).\n * @module bquery/router\n */\n\nimport { computed, signal, type ReadonlySignal, type Signal } from '../reactive/index';\nimport type { Route, Router } from './types';\n\n// ============================================================================\n// Internal State\n// ============================================================================\n\n/** @internal */\nlet activeRouter: Router | null = null;\n\n/** @internal */\nexport const routeSignal: Signal<Route> = signal<Route>({\n path: '',\n params: {},\n query: {},\n matched: null,\n hash: '',\n});\n\n/**\n * Reactive signal containing the current route.\n *\n * @example\n * ```ts\n * import { currentRoute } from 'bquery/router';\n * import { effect } from 'bquery/reactive';\n *\n * effect(() => {\n * document.title = `Page: ${currentRoute.value.path}`;\n * });\n * ```\n */\nexport const currentRoute: ReadonlySignal<Route> = computed(() => routeSignal.value);\n\n/** @internal */\nexport const getActiveRouter = (): Router | null => activeRouter;\n\n/** @internal */\nexport const setActiveRouter = (router: Router | null): void => {\n activeRouter = router;\n};\n","/**\n * Navigation helpers and global router access.\n * @module bquery/router\n */\n\nimport { getActiveRouter } from './state';\n\n/**\n * Navigates to a new path.\n *\n * @param path - The path to navigate to\n * @param options - Navigation options\n *\n * @example\n * ```ts\n * import { navigate } from 'bquery/router';\n *\n * // Push to history\n * await navigate('/dashboard');\n *\n * // Replace current entry\n * await navigate('/login', { replace: true });\n * ```\n */\nexport const navigate = async (\n path: string,\n options: { replace?: boolean } = {}\n): Promise<void> => {\n const activeRouter = getActiveRouter();\n if (!activeRouter) {\n throw new Error('bQuery router: No router initialized. Call createRouter() first.');\n }\n\n await activeRouter[options.replace ? 'replace' : 'push'](path);\n};\n\n/**\n * Programmatically go back in history.\n *\n * @example\n * ```ts\n * import { back } from 'bquery/router';\n * back();\n * ```\n */\nexport const back = (): void => {\n const activeRouter = getActiveRouter();\n if (activeRouter) {\n activeRouter.back();\n } else {\n history.back();\n }\n};\n\n/**\n * Programmatically go forward in history.\n *\n * @example\n * ```ts\n * import { forward } from 'bquery/router';\n * forward();\n * ```\n */\nexport const forward = (): void => {\n const activeRouter = getActiveRouter();\n if (activeRouter) {\n activeRouter.forward();\n } else {\n history.forward();\n }\n};\n","/**\n * Link helpers for client-side navigation.\n * @module bquery/router\n */\n\nimport { getActiveRouter } from './state';\nimport { navigate } from './navigation';\n\n// ============================================================================\n// Router Link Helper\n// ============================================================================\n\n/**\n * Creates click handler for router links.\n * Attach to anchor elements to enable client-side navigation.\n *\n * @param path - Target path\n * @param options - Navigation options\n * @returns Click event handler\n *\n * @example\n * ```ts\n * import { link } from 'bquery/router';\n * import { $ } from 'bquery/core';\n *\n * $('#nav-home').on('click', link('/'));\n * $('#nav-about').on('click', link('/about'));\n * ```\n */\nexport const link = (path: string, options: { replace?: boolean } = {}): ((e: Event) => void) => {\n return (e: Event) => {\n e.preventDefault();\n void navigate(path, options).catch((err) => {\n console.error('Navigation failed:', err);\n });\n };\n};\n\n/**\n * Intercepts all link clicks within a container for client-side routing.\n * Only intercepts links with matching origins and no target attribute.\n *\n * @param container - The container element to intercept links in\n * @returns Cleanup function to remove the listener\n *\n * @example\n * ```ts\n * import { interceptLinks } from 'bquery/router';\n *\n * // Intercept all links in the app\n * const cleanup = interceptLinks(document.body);\n *\n * // Later, remove the interceptor\n * cleanup();\n * ```\n */\nexport const interceptLinks = (container?: Element): (() => void) => {\n // Provide safe default in DOM environments only\n const targetContainer = container ?? (typeof document !== 'undefined' ? document.body : null);\n if (!targetContainer) {\n // No container available (SSR or invalid input)\n return () => undefined;\n }\n\n const handler = (e: Event) => {\n // Only intercept standard left-clicks without modifier keys\n if (!(e instanceof MouseEvent)) return;\n if (e.defaultPrevented) return; // Already handled\n if (e.button !== 0) return; // Not left-click (middle-click opens new tab, right-click shows context menu)\n if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return; // Modifier keys (Ctrl/Cmd-click = new tab)\n\n // Guard against non-Element targets and non-DOM environments\n if (typeof Element === 'undefined' || !(e.target instanceof Element)) return;\n const target = e.target as HTMLElement;\n const anchor = target.closest('a');\n\n if (!anchor) return;\n\n // Cross-realm compatible anchor check: use owner document's constructor if available\n const anchorWindow = anchor.ownerDocument.defaultView;\n const AnchorConstructor = anchorWindow?.HTMLAnchorElement ?? HTMLAnchorElement;\n if (!(anchor instanceof AnchorConstructor)) return;\n\n if (anchor.target) return; // Has target attribute\n if (anchor.hasAttribute('download')) return;\n if (typeof window === 'undefined') return; // Non-window environment\n if (anchor.origin !== window.location.origin) return; // External link\n\n // Get active router config to handle base paths correctly.\n // If no router is active, proceed with no base/hash; navigate() will throw a\n // \"No router initialized\" error, which is caught and logged below.\n const router = getActiveRouter();\n if (!router) {\n // No active router - trigger navigate(), allowing its error to be logged here\n e.preventDefault();\n void navigate(anchor.pathname + anchor.search + anchor.hash).catch((err) => {\n console.error('Navigation failed:', err);\n });\n return;\n }\n\n const base = router.base;\n const useHash = router.hash;\n\n // Detect hash-routing mode: links written as href=\"#/page\"\n // In this case, anchor.hash contains the route path\n let path: string;\n if (useHash && anchor.hash && anchor.hash.startsWith('#/')) {\n // Hash-routing mode: extract path from the hash\n // e.g., href=\"#/page?foo=bar\" → path = \"/page?foo=bar\"\n path = anchor.hash.slice(1); // Remove leading #\n } else {\n // History mode: use pathname + search + hash\n // Strip base from pathname to avoid duplication (router.push() re-adds it)\n let pathname = anchor.pathname;\n if (base && base !== '/' && pathname.startsWith(base)) {\n pathname = pathname.slice(base.length) || '/';\n }\n path = pathname + anchor.search + anchor.hash;\n }\n\n e.preventDefault();\n void navigate(path).catch((err) => {\n console.error('Navigation failed:', err);\n });\n };\n\n targetContainer.addEventListener('click', handler);\n return () => targetContainer.removeEventListener('click', handler);\n};\n","/**\n * Query string helpers.\n * @module bquery/router\n */\n\n/**\n * Parses query string into an object.\n * Single values are stored as strings, duplicate keys become arrays.\n * @internal\n *\n * @example\n * parseQuery('?foo=1') // { foo: '1' }\n * parseQuery('?tag=a&tag=b') // { tag: ['a', 'b'] }\n * parseQuery('?x=1&y=2&x=3') // { x: ['1', '3'], y: '2' }\n */\nexport const parseQuery = (search: string): Record<string, string | string[]> => {\n const query: Record<string, string | string[]> = {};\n const params = new URLSearchParams(search);\n\n params.forEach((value, key) => {\n const existing = query[key];\n if (existing === undefined) {\n // First occurrence: store as string\n query[key] = value;\n } else if (Array.isArray(existing)) {\n // Already an array: append\n existing.push(value);\n } else {\n // Second occurrence: convert to array\n query[key] = [existing, value];\n }\n });\n\n return query;\n};\n","/**\n * Route matching helpers.\n * @module bquery/router\n */\n\nimport { parseQuery } from './query';\nimport type { Route, RouteDefinition } from './types';\n\n// ============================================================================\n// Route Matching\n// ============================================================================\n\n/**\n * Converts a route path pattern to a RegExp for matching.\n * Uses placeholder approach to preserve :param and * patterns during escaping.\n * Returns positional capture groups for maximum compatibility.\n * @internal\n */\nconst pathToRegex = (path: string): RegExp => {\n // Handle wildcard-only route\n if (path === '*') {\n return /^.*$/;\n }\n\n // Unique placeholders using null chars (won't appear in normal paths)\n const PARAM_MARKER = '\\u0000P\\u0000';\n const WILDCARD_MARKER = '\\u0000W\\u0000';\n\n // Step 1: Extract :param patterns before escaping\n let pattern = path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, () => {\n return PARAM_MARKER;\n });\n\n // Step 2: Extract * wildcards before escaping\n pattern = pattern.replace(/\\*/g, WILDCARD_MARKER);\n\n // Step 3: Escape ALL regex metacharacters: \\ ^ $ . * + ? ( ) [ ] { } |\n pattern = pattern.replace(/[\\\\^$.*+?()[\\]{}|]/g, '\\\\$&');\n\n // Step 4: Restore param capture groups (positional, not named)\n pattern = pattern.replace(/\\u0000P\\u0000/g, () => `([^/]+)`);\n\n // Step 5: Restore wildcards as .*\n pattern = pattern.replace(/\\u0000W\\u0000/g, '.*');\n\n return new RegExp(`^${pattern}$`);\n};\n\n/**\n * Extracts param names from a route path.\n * @internal\n */\nconst extractParamNames = (path: string): string[] => {\n const matches = path.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);\n return matches ? matches.map((m) => m.slice(1)) : [];\n};\n\n/**\n * Matches a path against route definitions and extracts params.\n * Uses positional captures for maximum compatibility.\n * @internal\n */\nexport const matchRoute = (\n path: string,\n routes: RouteDefinition[]\n): { matched: RouteDefinition; params: Record<string, string> } | null => {\n for (const route of routes) {\n const regex = pathToRegex(route.path);\n const match = path.match(regex);\n\n if (match) {\n const paramNames = extractParamNames(route.path);\n const params: Record<string, string> = {};\n\n // Map positional captures to param names\n paramNames.forEach((name, index) => {\n params[name] = match[index + 1] || '';\n });\n\n return { matched: route, params };\n }\n }\n\n return null;\n};\n\n/**\n * Creates a Route object from the current URL.\n * @internal\n */\nexport const createRoute = (\n pathname: string,\n search: string,\n hash: string,\n routes: RouteDefinition[]\n): Route => {\n const result = matchRoute(pathname, routes);\n\n return {\n path: pathname,\n params: result?.params ?? {},\n query: parseQuery(search),\n matched: result?.matched ?? null,\n hash: hash.replace(/^#/, ''),\n };\n};\n","/**\n * Router utilities.\n * @module bquery/router\n */\n\nimport { computed, type ReadonlySignal } from '../reactive/index';\nimport { getActiveRouter, routeSignal } from './state';\nimport type { RouteDefinition } from './types';\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Flattens nested routes into a single array with full paths.\n * Does NOT include the router base - base is only for browser history.\n * @internal\n */\nexport const flattenRoutes = (routes: RouteDefinition[], parentPath = ''): RouteDefinition[] => {\n const result: RouteDefinition[] = [];\n\n for (const route of routes) {\n const fullPath = route.path === '*' ? '*' : `${parentPath}${route.path}`.replace(/\\/+/g, '/');\n\n result.push({\n ...route,\n path: fullPath,\n });\n\n if (route.children) {\n result.push(...flattenRoutes(route.children, fullPath));\n }\n }\n\n return result;\n};\n\n/**\n * Resolves a route by name and params.\n *\n * @param name - The route name\n * @param params - Route params to interpolate\n * @returns The resolved path\n *\n * @example\n * ```ts\n * import { resolve } from 'bquery/router';\n *\n * const path = resolve('user', { id: '42' });\n * // Returns '/user/42' if route is defined as { name: 'user', path: '/user/:id' }\n * ```\n */\nexport const resolve = (name: string, params: Record<string, string> = {}): string => {\n const activeRouter = getActiveRouter();\n if (!activeRouter) {\n throw new Error('bQuery router: No router initialized.');\n }\n\n const route = activeRouter.routes.find((r) => r.name === name);\n if (!route) {\n throw new Error(`bQuery router: Route \"${name}\" not found.`);\n }\n\n let path = route.path;\n for (const [key, value] of Object.entries(params)) {\n path = path.replace(`:${key}`, encodeURIComponent(value));\n }\n\n return path;\n};\n\n/**\n * Checks if a path matches the current route.\n *\n * @param path - Path to check\n * @param exact - Whether to match exactly (default: false)\n * @returns True if the path matches\n *\n * @example\n * ```ts\n * import { isActive } from 'bquery/router';\n *\n * if (isActive('/dashboard')) {\n * // Highlight nav item\n * }\n * ```\n */\nexport const isActive = (path: string, exact = false): boolean => {\n const current = routeSignal.value.path;\n return exact ? current === path : current.startsWith(path);\n};\n\n/**\n * Creates a computed signal that checks if a path is active.\n *\n * @param path - Path to check\n * @param exact - Whether to match exactly\n * @returns A reactive signal\n *\n * @example\n * ```ts\n * import { isActiveSignal } from 'bquery/router';\n * import { effect } from 'bquery/reactive';\n *\n * const dashboardActive = isActiveSignal('/dashboard');\n * effect(() => {\n * navItem.classList.toggle('active', dashboardActive.value);\n * });\n * ```\n */\nexport const isActiveSignal = (path: string, exact = false): ReadonlySignal<boolean> => {\n return computed(() => {\n const current = routeSignal.value.path;\n return exact ? current === path : current.startsWith(path);\n });\n};\n","/**\n * Router creation and lifecycle management.\n * @module bquery/router\n */\n\nimport { createRoute } from './match';\nimport { currentRoute, getActiveRouter, routeSignal, setActiveRouter } from './state';\nimport type { NavigationGuard, Route, Router, RouterOptions } from './types';\nimport { flattenRoutes } from './utils';\n\n// ============================================================================\n// Router Creation\n// ============================================================================\n\n/**\n * Creates and initializes a router instance.\n *\n * @param options - Router configuration\n * @returns The router instance\n *\n * @example\n * ```ts\n * import { createRouter } from 'bquery/router';\n *\n * const router = createRouter({\n * routes: [\n * { path: '/', component: () => import('./pages/Home') },\n * { path: '/about', component: () => import('./pages/About') },\n * { path: '/user/:id', component: () => import('./pages/User') },\n * { path: '*', component: () => import('./pages/NotFound') },\n * ],\n * base: '/app',\n * });\n *\n * router.beforeEach((to, from) => {\n * if (to.path === '/admin' && !isAuthenticated()) {\n * return false; // Cancel navigation\n * }\n * });\n * ```\n */\nexport const createRouter = (options: RouterOptions): Router => {\n // Clean up any existing router to prevent guard leakage\n const existingRouter = getActiveRouter();\n if (existingRouter) {\n existingRouter.destroy();\n }\n\n const { routes, base = '', hash: useHash = false } = options;\n\n // Instance-specific guards and hooks (not shared globally)\n const beforeGuards: NavigationGuard[] = [];\n const afterHooks: Array<(to: Route, from: Route) => void> = [];\n\n // Flatten nested routes (base-relative, not including the base path)\n const flatRoutes = flattenRoutes(routes);\n\n /**\n * Gets the current path from the URL.\n */\n const getCurrentPath = (): { pathname: string; search: string; hash: string } => {\n if (useHash) {\n const hashPath = window.location.hash.slice(1) || '/';\n // In hash routing, URL structure is #/path?query#fragment\n // Extract hash fragment first (after the second #)\n const [pathWithQuery, hashPart = ''] = hashPath.split('#');\n // Then extract query from the path\n const [pathname, search = ''] = pathWithQuery.split('?');\n return {\n pathname,\n search: search ? `?${search}` : '',\n hash: hashPart ? `#${hashPart}` : '',\n };\n }\n\n let pathname = window.location.pathname;\n if (base && (pathname === base || pathname.startsWith(base + '/'))) {\n pathname = pathname.slice(base.length) || '/';\n }\n\n return {\n pathname,\n search: window.location.search,\n hash: window.location.hash,\n };\n };\n\n /**\n * Updates the route signal with current URL state.\n */\n const syncRoute = (): void => {\n const { pathname, search, hash } = getCurrentPath();\n const newRoute = createRoute(pathname, search, hash, flatRoutes);\n routeSignal.value = newRoute;\n };\n\n /**\n * Performs navigation with guards.\n */\n const performNavigation = async (\n path: string,\n method: 'pushState' | 'replaceState'\n ): Promise<void> => {\n const { pathname, search, hash } = getCurrentPath();\n const from = createRoute(pathname, search, hash, flatRoutes);\n\n // Parse the target path\n const url = new URL(path, window.location.origin);\n const to = createRoute(url.pathname, url.search, url.hash, flatRoutes);\n\n // Run beforeEach guards\n for (const guard of beforeGuards) {\n const result = await guard(to, from);\n if (result === false) {\n return; // Cancel navigation\n }\n }\n\n // Update browser history\n const fullPath = useHash ? `#${path}` : `${base}${path}`;\n history[method]({}, '', fullPath);\n\n // Update route signal\n syncRoute();\n\n // Run afterEach hooks\n for (const hook of afterHooks) {\n hook(routeSignal.value, from);\n }\n };\n\n /**\n * Handle popstate events (back/forward).\n */\n const handlePopState = async (): Promise<void> => {\n const { pathname, search, hash } = getCurrentPath();\n const from = routeSignal.value;\n const to = createRoute(pathname, search, hash, flatRoutes);\n\n // Run beforeEach guards (supports async guards)\n for (const guard of beforeGuards) {\n const result = await guard(to, from);\n if (result === false) {\n // Restore previous state with full URL (including query/hash)\n const queryString = new URLSearchParams(\n Object.entries(from.query).flatMap(([key, value]) =>\n Array.isArray(value) ? value.map((v) => [key, v]) : [[key, value]]\n )\n ).toString();\n const search = queryString ? `?${queryString}` : '';\n const hash = from.hash ? `#${from.hash}` : '';\n const restorePath = useHash\n ? `#${from.path}${search}${hash}`\n : `${base}${from.path}${search}${hash}`;\n history.replaceState({}, '', restorePath);\n return;\n }\n }\n\n syncRoute();\n\n for (const hook of afterHooks) {\n hook(routeSignal.value, from);\n }\n };\n\n // Attach popstate listener\n window.addEventListener('popstate', handlePopState);\n\n // Initialize route\n syncRoute();\n\n const router: Router = {\n push: (path: string) => performNavigation(path, 'pushState'),\n replace: (path: string) => performNavigation(path, 'replaceState'),\n back: () => history.back(),\n forward: () => history.forward(),\n go: (delta: number) => history.go(delta),\n\n beforeEach: (guard: NavigationGuard) => {\n beforeGuards.push(guard);\n return () => {\n const index = beforeGuards.indexOf(guard);\n if (index > -1) beforeGuards.splice(index, 1);\n };\n },\n\n afterEach: (hook: (to: Route, from: Route) => void) => {\n afterHooks.push(hook);\n return () => {\n const index = afterHooks.indexOf(hook);\n if (index > -1) afterHooks.splice(index, 1);\n };\n },\n\n currentRoute,\n routes: flatRoutes,\n base,\n hash: useHash,\n\n destroy: () => {\n window.removeEventListener('popstate', handlePopState);\n beforeGuards.length = 0;\n afterHooks.length = 0;\n setActiveRouter(null);\n },\n };\n\n setActiveRouter(router);\n return router;\n};\n"],"names":["activeRouter","routeSignal","signal","currentRoute","computed","getActiveRouter","setActiveRouter","router","navigate","path","options","back","forward","link","e","err","interceptLinks","container","targetContainer","handler","anchor","AnchorConstructor","base","useHash","pathname","parseQuery","search","query","value","key","existing","pathToRegex","PARAM_MARKER","WILDCARD_MARKER","pattern","extractParamNames","matches","m","matchRoute","routes","route","regex","match","paramNames","params","name","index","createRoute","hash","result","flattenRoutes","parentPath","fullPath","resolve","r","isActive","exact","current","isActiveSignal","createRouter","existingRouter","beforeGuards","afterHooks","flatRoutes","getCurrentPath","hashPath","pathWithQuery","hashPart","syncRoute","newRoute","performNavigation","method","from","url","to","guard","hook","handlePopState","queryString","v","restorePath","delta"],"mappings":"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"}
|