@elurjs/core 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +5 -5
  2. package/dist/lib/component.cjs +1 -1
  3. package/dist/lib/component.js +1 -1
  4. package/dist/lib/devtools.cjs +1 -1
  5. package/dist/lib/devtools.js +1 -1
  6. package/dist/lib/elur/component.cjs +1 -1
  7. package/dist/lib/elur/component.cjs.map +1 -1
  8. package/dist/lib/elur/component.d.cts +1 -1
  9. package/dist/lib/elur/component.d.ts +1 -1
  10. package/dist/lib/elur/component.js +1 -1
  11. package/dist/lib/elur/component.js.map +1 -1
  12. package/dist/lib/elur/devtools.cjs +1 -1
  13. package/dist/lib/elur/devtools.cjs.map +1 -1
  14. package/dist/lib/elur/devtools.js +1 -1
  15. package/dist/lib/elur/devtools.js.map +1 -1
  16. package/dist/lib/elur/lifecycle.cjs +1 -1
  17. package/dist/lib/elur/lifecycle.cjs.map +1 -1
  18. package/dist/lib/elur/lifecycle.d.cts +6 -0
  19. package/dist/lib/elur/lifecycle.d.ts +6 -0
  20. package/dist/lib/elur/lifecycle.js +40 -7
  21. package/dist/lib/elur/lifecycle.js.map +1 -1
  22. package/dist/lib/elur/reactivity.cjs +1 -1
  23. package/dist/lib/elur/reactivity.cjs.map +1 -1
  24. package/dist/lib/elur/reactivity.d.cts +7 -0
  25. package/dist/lib/elur/reactivity.d.ts +7 -0
  26. package/dist/lib/elur/reactivity.js +53 -23
  27. package/dist/lib/elur/reactivity.js.map +1 -1
  28. package/dist/lib/elur/router-registry.cjs +2 -0
  29. package/dist/lib/elur/router-registry.cjs.map +1 -0
  30. package/dist/lib/elur/router-registry.d.cts +14 -0
  31. package/dist/lib/elur/router-registry.d.ts +14 -0
  32. package/dist/lib/elur/router-registry.js +15 -0
  33. package/dist/lib/elur/router-registry.js.map +1 -0
  34. package/dist/lib/elur/router.cjs +3 -3
  35. package/dist/lib/elur/router.cjs.map +1 -1
  36. package/dist/lib/elur/router.d.cts +2 -5
  37. package/dist/lib/elur/router.d.ts +2 -5
  38. package/dist/lib/elur/router.js +159 -166
  39. package/dist/lib/elur/router.js.map +1 -1
  40. package/dist/lib/elur/template/html.cjs +1 -1
  41. package/dist/lib/elur/template/html.cjs.map +1 -1
  42. package/dist/lib/elur/template/html.js +1 -1
  43. package/dist/lib/elur/template/html.js.map +1 -1
  44. package/dist/lib/elur/template/types.cjs +1 -1
  45. package/dist/lib/elur/template/types.cjs.map +1 -1
  46. package/dist/lib/elur/template/types.js +1 -1
  47. package/dist/lib/elur/template/types.js.map +1 -1
  48. package/dist/lib/elur.cjs +1 -1
  49. package/dist/lib/elur.js +6 -5
  50. package/dist/lib/index.cjs +1 -1
  51. package/dist/lib/index.js +6 -5
  52. package/dist/lib/lifecycle.cjs +1 -1
  53. package/dist/lib/lifecycle.js +40 -7
  54. package/dist/lib/router.cjs +3 -3
  55. package/dist/lib/router.js +159 -166
  56. package/dist/lib/signals.cjs +1 -1
  57. package/dist/lib/signals.js +53 -23
  58. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ import { createInjectionKey as e } from "../context.js";
2
+ //#region src/elur/router-registry.ts
3
+ var t = e("elur:router"), n = [];
4
+ function r(e) {
5
+ let t = n.indexOf(e);
6
+ t >= 0 && n.splice(t, 1), n.push(e);
7
+ }
8
+ function i(e) {
9
+ let t = n.indexOf(e);
10
+ t >= 0 && n.splice(t, 1);
11
+ }
12
+ //#endregion
13
+ export { t as RouterKey, r as _debugRegisterRouter, i as _debugUnregisterRouter, n as _mountedRouters };
14
+
15
+ //# sourceMappingURL=router-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-registry.js","names":[],"sources":["../../../src/elur/router-registry.ts"],"sourcesContent":["// Router registry — tiny shared module holding the router injection key and\n// the debug registry of mounted routers.\n//\n// It exists so that `component.ts` (the `mount()` entry) does NOT import\n// `router.ts`: importing the full router module from `mount()` dragged the\n// entire router (~1k lines) into consumer bundles, defeating tree-shaking\n// for apps that only use signals + template + component.\n//\n// `router.ts` re-exports everything defined here, so the public API of the\n// \"./router\" subpath is unchanged.\n\nimport { createInjectionKey } from \"./context.js\";\nimport type { Router } from \"./router.js\";\n\n/** Injection key used to provide/resolve the active router. */\nexport const RouterKey = createInjectionKey<Router>(\"elur:router\");\n\n/**\n * Routers injected via mount({ router }) are not the global singleton. DevTools\n * tracks them separately so it can inspect the router actually active in the UI.\n *\n * @internal\n */\nexport const _mountedRouters: Router[] = [];\n\n/** @internal Register a router that was injected via mount({ router }). */\nexport function _debugRegisterRouter(router: Router): void {\n const idx = _mountedRouters.indexOf(router);\n if (idx >= 0) _mountedRouters.splice(idx, 1);\n _mountedRouters.push(router);\n}\n\n/** @internal Unregister a router when its mount point is unmounted. */\nexport function _debugUnregisterRouter(router: Router): void {\n const idx = _mountedRouters.indexOf(router);\n if (idx >= 0) _mountedRouters.splice(idx, 1);\n}\n"],"mappings":";;AAeA,IAAa,IAAY,EAA2B,cAAc,EAQrD,IAA4B,EAAE;AAG3C,SAAgB,EAAqB,GAAsB;CACvD,IAAM,IAAM,EAAgB,QAAQ,EAAO;AAE3C,CADI,KAAO,KAAG,EAAgB,OAAO,GAAK,EAAE,EAC5C,EAAgB,KAAK,EAAO;;AAIhC,SAAgB,EAAuB,GAAsB;CACzD,IAAM,IAAM,EAAgB,QAAQ,EAAO;AAC3C,CAAI,KAAO,KAAG,EAAgB,OAAO,GAAK,EAAE"}
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./signals.cjs`),t=require(`./lifecycle.cjs`),n=require(`./context.cjs`),r=require(`./elur/template/html.cjs`);var i=n.createInjectionKey(`elur:router`),a=null,o=null,s=[],c=`__elur_scroll`,l=`__elur_pos`;function u(){if(!a)throw Error(`[elur] No active router. Call createRouter() first, or instantiate an outlet that auto-bootstraps one (e.g. IonRouterOutlet).`);return a}function d(){return a!==null}function f(){return{left:window.scrollX??window.pageXOffset??0,top:window.scrollY??window.pageYOffset??0}}function ee(e){if(!e||typeof e!=`object`)return null;let t=e[c];if(!t||typeof t!=`object`)return null;let n=t.left,r=t.top;return typeof n!=`number`||typeof r!=`number`?null:{left:n,top:r}}function p(e){if(!e||typeof e!=`object`)return null;let t=e[l];return typeof t==`number`?t:null}function m(e,t,n){let r=e&&typeof e==`object`?{...e}:{};return r[c]={left:t.left,top:t.top},r[l]=n,r}function h(e){let t={};return new URLSearchParams(e).forEach((e,n)=>{t[n]=e}),t}function g(e){let t=new URLSearchParams;for(let[n,r]of Object.entries(e))r!=null&&r!==!1&&t.set(n,String(r));let n=t.toString();return n?`?`+n:``}function _(e){return e===`*`?[{kind:`wildcard`}]:e.split(`/`).filter(Boolean).map(e=>e===`*`?{kind:`wildcard`}:e.startsWith(`:`)?{kind:`param`,name:e.slice(1)}:{kind:`literal`,value:e})}function v(e,t){return t===`*`?e===``?`*`:e+`/*`:(e+(t.startsWith(`/`)?t:`/`+t)).replace(/\/+/g,`/`)||`/`}function y(e,t=``,n=[]){let r=[];for(let i of e){let e=v(t,i.path),a=[...n,i.component],o=_(e);r.push({fullPath:e,segments:o,chain:a,name:i.name,meta:i.meta,beforeEnter:i.beforeEnter,record:i}),i.children?.length&&r.push(...y(i.children,e,a))}return r}function b(e,t){let n=e.split(`/`).filter(Boolean),r=t.segments;if(r.length===1&&r[0].kind===`wildcard`)return{};let i=r.length>0&&r[r.length-1].kind===`wildcard`,a=i?r.slice(0,-1):r;if(i){if(n.length<a.length)return null}else if(n.length!==a.length)return null;let o={};for(let e=0;e<a.length;e++){let t=a[e];if(t.kind===`literal`){if(n[e]!==t.value)return null}else if(t.kind===`param`)try{o[t.name]=decodeURIComponent(n[e]??``)}catch{o[t.name]=n[e]??``}}return o}function x(e){return e.segments.reduce((e,t)=>t.kind===`literal`?e+2:t.kind===`param`?e+1:e,0)}function S(e,t){let n,r={},i=-1;for(let a of t){let t=b(e,a);if(t===null)continue;let o=x(a);o>i&&(n=a,r=t,i=o)}return n?{route:n,params:r}:void 0}function C(e){let t=e.trim();return!t||t===`/`?``:(t.startsWith(`/`)||(t=`/`+t),t.endsWith(`/`)&&(t=t.slice(0,-1)),t)}function w(){if(typeof document>`u`)return``;let e=document.querySelector(`base`);if(!e)return``;let t=e.getAttribute(`href`)||``;try{return C(new URL(t,window.location.origin).pathname)}catch{return C(t)}}function te(e){return e===!1?{allow:!1}:e===!0||e==null?{allow:!0}:typeof e==`string`?{allow:!1,redirect:e}:typeof e==`object`&&`redirect`in e&&typeof e.redirect==`string`?{allow:!1,redirect:e.redirect}:{allow:!0}}function T(t,n){let r=n?.base==null?w():C(n.base),i=n?.mode??`history`,s=i===`hash`,c=n?.scrollBehavior,l=new Map,u=!1;function d(e){return e?e.startsWith(`/`)?e:`/`+e:`/`}function _(e){let t=d(e||`/`);if(r&&t.startsWith(r)){let e=t.slice(r.length);return e===``?`/`:d(e)}return t}function v(e){let t=d(e);return r?(r+t).replace(/\/+/g,`/`)||`/`:t}function b(){let e=window.location.hash||``;if(e.startsWith(`#`)&&(e=e.slice(1)),!e)return{pathname:`/`,search:``};e.startsWith(`/`)||(e=`/`+e);let t=e.indexOf(`?`),n=t===-1?e:e.slice(0,t),r=t===-1?``:e.slice(t);return{pathname:_(n),search:r}}function x(){return s?b():{pathname:_(window.location.pathname||`/`),search:window.location.search||``}}function T(e,t){let n=v(e)+g(t);return s?`#`+n:n}function E(e,t){return d(e)+g(t)}let D=x(),O=D.pathname,k=h(D.search),A=y(t),j=new Map;for(let e of A)e.name&&(j.has(e.name)&&console.warn(`[Elur Router] Duplicate route name: "${e.name}"`),j.set(e.name,e));let M=S(O,A),N=e.signal(O),P=e.signal(M?.params??{}),F=e.signal(k),I=p(history.state)??0,L=e.signal({action:`initial`,direction:`none`}),R=e.signal(I>0);s?l.set(E(O,k),f()):history.replaceState(m(history.state,f(),I),``);function z(e){window.scrollTo(e.left,e.top)}function B(e,t,n){if(c){let r=c(e,t,n);if(!r)return;z(r);return}z(n??{left:0,top:0})}function V(e,t){let n=f();if(s){l.set(E(e,t),n);return}history.replaceState(m(history.state,n,I),``)}let H=[],U=[],W=0;function G(e,t,n,r,i){let a=[...H];n&&a.push(n);let o=++W;if(a.length===0){r();return}let s=0;function c(n){if(o!==W)return;let l=te(n);if(!l.allow){if(l.redirect&&l.redirect!==e){Q(l.redirect);return}if(l.redirect===e){r();return}i?.();return}if(s>=a.length){r();return}let u=a[s++](e,t);if(u instanceof Promise){u.then(c);return}c(u)}c(void 0)}let K=!1;function q(e,t){let n=e.indexOf(`?`),r=d((n===-1?e:e.slice(0,n))||`/`),i=n===-1?{}:h(e.slice(n)),a=t?{...i,...t}:i,o={};for(let[e,t]of Object.entries(a))t!=null&&t!==!1&&(o[e]=String(t));return{pathname:r,stringQuery:o}}function ne(e){let t=j.get(e.name);if(!t)throw Error(`[Elur Router] No route with name "${e.name}"`);return`/`+t.segments.map(t=>{if(t.kind===`literal`)return t.value;if(t.kind===`wildcard`)return``;let n=e.params?.[t.name];if(n==null)throw Error(`[Elur Router] Missing param "${t.name}" for route "${e.name}"`);return encodeURIComponent(String(n))}).filter(Boolean).join(`/`)}function J(e,t){return typeof e==`string`?q(e,t?.query):q(ne(e),{...e.query??{},...t?.query??{}})}o&&=(o(),null);let Y=(e,t,n,r,i)=>{let a=N.value,o={...F.value},s=S(e,A),c=`none`;r!=null&&(r<I?c=`back`:r>I&&(c=`forward`)),G(e,a,s?.route.beforeEnter,()=>{r!=null&&(I=r);let i=Z;Z=void 0,L.value={action:`pop`,direction:c,animation:i},P.value=s?.params??{},F.value=t,N.value=e,R.value=I>0,B(e,a,n);for(let t of U)try{t(e,a)}catch{}},()=>i(a,o))};if(s){let e=()=>{if(u){u=!1;return}let e=x(),t=h(e.search),n=l.get(E(e.pathname,t))??null;Y(e.pathname,t,n,null,(e,t)=>{u=!0,window.location.hash=T(e,t).slice(1),queueMicrotask(()=>{u=!1})})};window.addEventListener(`hashchange`,e),o=()=>window.removeEventListener(`hashchange`,e)}else{let e=e=>{let t=x(),n=h(t.search),r=ee(e.state??history.state),i=p(e.state??history.state);Y(t.pathname,n,r,i,(e,t)=>{history.pushState(m({},f(),I),``,T(e,t))})};window.addEventListener(`popstate`,e),o=()=>window.removeEventListener(`popstate`,e)}function X(e,t,n,r,i,a,o){o||(V(n,r),I+=1),L.value=a,P.value=i?.params??{},F.value=t,N.value=e,R.value=I>0;let c=T(e,t);if(s)l.set(E(e,t),{left:0,top:0}),o?history.replaceState(history.state,``,c):(u=!0,window.location.hash=c.slice(1),queueMicrotask(()=>{u=!1}));else{let e=m({},{left:0,top:0},I);o?history.replaceState(e,``,c):history.pushState(e,``,c)}B(e,n,null);for(let t of U)try{t(e,n)}catch{}}let Z;function Q(e,t){K=!0;let{pathname:n,stringQuery:r}=J(e,t),i=N.value,a={...F.value},o=S(n,A),s={action:`push`,direction:t?.direction??`forward`,animation:t?.animation};G(n,i,o?.route.beforeEnter,()=>X(n,r,i,a,o,s,!1))}function re(e,t){K=!0;let{pathname:n,stringQuery:r}=J(e,t),i=N.value,a={...F.value},o=S(n,A),s={action:`replace`,direction:t?.direction??`root`,animation:t?.animation};G(n,i,o?.route.beforeEnter,()=>X(n,r,i,a,o,s,!0))}function ie(e){e!==void 0&&(Z=e),history.back()}function ae(e){e!==void 0&&(Z=e),history.forward()}function oe(e){history.go(e)}function se(e,t=!0){let n=N.value;return t?n===e:n===e||n.startsWith(e.endsWith(`/`)?e:e+`/`)}function ce(e){let t=S(e,A);return t?{matched:!0,params:t.params,route:t.route.record}:{matched:!1,params:{},route:void 0}}function le(e){return H.push(e),()=>{let t=H.indexOf(e);t!==-1&&H.splice(t,1)}}function ue(e){return U.push(e),()=>{let t=U.indexOf(e);t!==-1&&U.splice(t,1)}}let $={current:N,params:P,query:F,intent:L,canGoBack:R,base:r||`/`,navigate:Q,replace:re,back:ie,forward:ae,go:oe,isActive:se,resolve:ce,beforeEach:le,afterEach:ue,routes:t,_flat:A,_guards:H,_base:r,_mode:i};return a&&console.warn(`[elur] A router already exists. The previous router is being replaced. Only one router instance should be active at a time.`),a=$,queueMicrotask(()=>{K||G(O,``,S(O,A)?.route.beforeEnter,()=>{},()=>{let e=T(`/`,{});s?(l.set(E(`/`,{}),{left:0,top:0}),history.replaceState(history.state,``,e)):history.replaceState(m({},{left:0,top:0},I),``,e);let t=S(`/`,A);L.value={action:`replace`,direction:`root`},N.value=`/`,P.value=t?.params??{},F.value={},R.value=I>0,B(`/`,O,null)})}),$}function E(){return n.inject(i)||u()}function D(){o&&=(o(),null),a=null,s.length=0}var O=class extends t.ElurComponent{_depth;_router;constructor(e=0,t){super(),this._depth=e,this._router=t}render(){let e=this._depth,t=this._router;return r.html`<div class="router-view">${()=>{let n=t??E(),i=S(n.current.value,n._flat);if(!i)return r.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./signals.cjs`),t=require(`./lifecycle.cjs`),n=require(`./context.cjs`),r=require(`./elur/template/html.cjs`),i=require(`./elur/router-registry.cjs`);var a=null,o=null,s=`__elur_scroll`,c=`__elur_pos`;function l(){if(!a)throw Error(`[elur] No active router. Call createRouter() first, or instantiate an outlet that auto-bootstraps one (e.g. IonRouterOutlet).`);return a}function u(){return a!==null}function d(){return{left:window.scrollX??window.pageXOffset??0,top:window.scrollY??window.pageYOffset??0}}function ee(e){if(!e||typeof e!=`object`)return null;let t=e[s];if(!t||typeof t!=`object`)return null;let n=t.left,r=t.top;return typeof n!=`number`||typeof r!=`number`?null:{left:n,top:r}}function f(e){if(!e||typeof e!=`object`)return null;let t=e[c];return typeof t==`number`?t:null}function p(e,t,n){let r=e&&typeof e==`object`?{...e}:{};return r[s]={left:t.left,top:t.top},r[c]=n,r}function m(e){let t={};return new URLSearchParams(e).forEach((e,n)=>{t[n]=e}),t}function h(e){let t=new URLSearchParams;for(let[n,r]of Object.entries(e))r!=null&&r!==!1&&t.set(n,String(r));let n=t.toString();return n?`?`+n:``}function g(e){return e===`*`?[{kind:`wildcard`}]:e.split(`/`).filter(Boolean).map(e=>e===`*`?{kind:`wildcard`}:e.startsWith(`:`)?{kind:`param`,name:e.slice(1)}:{kind:`literal`,value:e})}function _(e,t){return t===`*`?e===``?`*`:e+`/*`:(e+(t.startsWith(`/`)?t:`/`+t)).replace(/\/+/g,`/`)||`/`}function v(e,t=``,n=[]){let r=[];for(let i of e){let e=_(t,i.path),a=[...n,i.component],o=g(e);r.push({fullPath:e,segments:o,chain:a,name:i.name,meta:i.meta,beforeEnter:i.beforeEnter,record:i}),i.children?.length&&r.push(...v(i.children,e,a))}return r}function y(e,t){let n=e.split(`/`).filter(Boolean),r=t.segments;if(r.length===1&&r[0].kind===`wildcard`)return{};let i=r.length>0&&r[r.length-1].kind===`wildcard`,a=i?r.slice(0,-1):r;if(i){if(n.length<a.length)return null}else if(n.length!==a.length)return null;let o={};for(let e=0;e<a.length;e++){let t=a[e];if(t.kind===`literal`){if(n[e]!==t.value)return null}else if(t.kind===`param`)try{o[t.name]=decodeURIComponent(n[e]??``)}catch{o[t.name]=n[e]??``}}return o}function b(e){return e.segments.reduce((e,t)=>t.kind===`literal`?e+2:t.kind===`param`?e+1:e,0)}function x(e,t){let n,r={},i=-1;for(let a of t){let t=y(e,a);if(t===null)continue;let o=b(a);o>i&&(n=a,r=t,i=o)}return n?{route:n,params:r}:void 0}function S(e){let t=e.trim();return!t||t===`/`?``:(t.startsWith(`/`)||(t=`/`+t),t.endsWith(`/`)&&(t=t.slice(0,-1)),t)}function te(){if(typeof document>`u`)return``;let e=document.querySelector(`base`);if(!e)return``;let t=e.getAttribute(`href`)||``;try{return S(new URL(t,window.location.origin).pathname)}catch{return S(t)}}function C(e){return e===!1?{allow:!1}:e===!0||e==null?{allow:!0}:typeof e==`string`?{allow:!1,redirect:e}:typeof e==`object`&&`redirect`in e&&typeof e.redirect==`string`?{allow:!1,redirect:e.redirect}:{allow:!0}}function w(t,n){let r=n?.base==null?te():S(n.base),i=n?.mode??`history`,s=i===`hash`,c=n?.scrollBehavior,l=new Map,u=!1;function g(e){return e?e.startsWith(`/`)?e:`/`+e:`/`}function _(e){let t=g(e||`/`);if(r&&t.startsWith(r)){let e=t.slice(r.length);return e===``?`/`:g(e)}return t}function y(e){let t=g(e);return r?(r+t).replace(/\/+/g,`/`)||`/`:t}function b(){let e=window.location.hash||``;if(e.startsWith(`#`)&&(e=e.slice(1)),!e)return{pathname:`/`,search:``};e.startsWith(`/`)||(e=`/`+e);let t=e.indexOf(`?`),n=t===-1?e:e.slice(0,t),r=t===-1?``:e.slice(t);return{pathname:_(n),search:r}}function w(){return s?b():{pathname:_(window.location.pathname||`/`),search:window.location.search||``}}function T(e,t){let n=y(e)+h(t);return s?`#`+n:n}function E(e,t){return g(e)+h(t)}let D=w(),O=D.pathname,k=m(D.search),A=v(t),j=new Map;for(let e of A)e.name&&(j.has(e.name)&&console.warn(`[Elur Router] Duplicate route name: "${e.name}"`),j.set(e.name,e));let ne=x(O,A),M=e.signal(O),N=e.signal(ne?.params??{}),P=e.signal(k),F=f(history.state)??0,I=e.signal({action:`initial`,direction:`none`}),L=e.signal(F>0);s?l.set(E(O,k),d()):history.replaceState(p(history.state,d(),F),``);function R(e){window.scrollTo(e.left,e.top)}function z(e,t,n){if(c){let r=c(e,t,n);if(!r)return;R(r);return}R(n??{left:0,top:0})}function B(e,t){let n=d();if(s){l.set(E(e,t),n);return}history.replaceState(p(history.state,n,F),``)}let V=[],H=[],U=0;function W(e,t,n,r,i){let a=[...V];n&&a.push(n);let o=++U;if(a.length===0){r();return}let s=0;function c(n){if(o!==U)return;let l=C(n);if(!l.allow){if(l.redirect&&l.redirect!==e){Z(l.redirect);return}if(l.redirect===e){r();return}i?.();return}if(s>=a.length){r();return}let u=a[s++](e,t);if(u instanceof Promise){u.then(c);return}c(u)}c(void 0)}let G=!1;function K(e,t){let n=e.indexOf(`?`),r=g((n===-1?e:e.slice(0,n))||`/`),i=n===-1?{}:m(e.slice(n)),a=t?{...i,...t}:i,o={};for(let[e,t]of Object.entries(a))t!=null&&t!==!1&&(o[e]=String(t));return{pathname:r,stringQuery:o}}function re(e){let t=j.get(e.name);if(!t)throw Error(`[Elur Router] No route with name "${e.name}"`);return`/`+t.segments.map(t=>{if(t.kind===`literal`)return t.value;if(t.kind===`wildcard`)return``;let n=e.params?.[t.name];if(n==null)throw Error(`[Elur Router] Missing param "${t.name}" for route "${e.name}"`);return encodeURIComponent(String(n))}).filter(Boolean).join(`/`)}function q(e,t){return typeof e==`string`?K(e,t?.query):K(re(e),{...e.query??{},...t?.query??{}})}o&&=(o(),null);let J=(e,t,n,r,i)=>{let a=M.value,o={...P.value},s=x(e,A),c=`none`;r!=null&&(r<F?c=`back`:r>F&&(c=`forward`)),W(e,a,s?.route.beforeEnter,()=>{r!=null&&(F=r);let i=X;X=void 0,I.value={action:`pop`,direction:c,animation:i},N.value=s?.params??{},P.value=t,M.value=e,L.value=F>0,z(e,a,n);for(let t of H)try{t(e,a)}catch{}},()=>i(a,o))};if(s){let e=()=>{if(u){u=!1;return}let e=w(),t=m(e.search),n=l.get(E(e.pathname,t))??null;J(e.pathname,t,n,null,(e,t)=>{u=!0,window.location.hash=T(e,t).slice(1),queueMicrotask(()=>{u=!1})})};window.addEventListener(`hashchange`,e),o=()=>window.removeEventListener(`hashchange`,e)}else{let e=e=>{let t=w(),n=m(t.search),r=ee(e.state??history.state),i=f(e.state??history.state);J(t.pathname,n,r,i,(e,t)=>{history.pushState(p({},d(),F),``,T(e,t))})};window.addEventListener(`popstate`,e),o=()=>window.removeEventListener(`popstate`,e)}function Y(e,t,n,r,i,a,o){o||(B(n,r),F+=1),I.value=a,N.value=i?.params??{},P.value=t,M.value=e,L.value=F>0;let c=T(e,t);if(s)l.set(E(e,t),{left:0,top:0}),o?history.replaceState(history.state,``,c):(u=!0,window.location.hash=c.slice(1),queueMicrotask(()=>{u=!1}));else{let e=p({},{left:0,top:0},F);o?history.replaceState(e,``,c):history.pushState(e,``,c)}z(e,n,null);for(let t of H)try{t(e,n)}catch{}}let X;function Z(e,t){G=!0;let{pathname:n,stringQuery:r}=q(e,t),i=M.value,a={...P.value},o=x(n,A),s={action:`push`,direction:t?.direction??`forward`,animation:t?.animation};W(n,i,o?.route.beforeEnter,()=>Y(n,r,i,a,o,s,!1))}function ie(e,t){G=!0;let{pathname:n,stringQuery:r}=q(e,t),i=M.value,a={...P.value},o=x(n,A),s={action:`replace`,direction:t?.direction??`root`,animation:t?.animation};W(n,i,o?.route.beforeEnter,()=>Y(n,r,i,a,o,s,!0))}function ae(e){e!==void 0&&(X=e),history.back()}function oe(e){e!==void 0&&(X=e),history.forward()}function Q(e){history.go(e)}function se(e,t=!0){let n=M.value;return t?n===e:n===e||n.startsWith(e.endsWith(`/`)?e:e+`/`)}function ce(e){let t=x(e,A);return t?{matched:!0,params:t.params,route:t.route.record}:{matched:!1,params:{},route:void 0}}function le(e){return V.push(e),()=>{let t=V.indexOf(e);t!==-1&&V.splice(t,1)}}function ue(e){return H.push(e),()=>{let t=H.indexOf(e);t!==-1&&H.splice(t,1)}}let $={current:M,params:N,query:P,intent:I,canGoBack:L,base:r||`/`,navigate:Z,replace:ie,back:ae,forward:oe,go:Q,isActive:se,resolve:ce,beforeEach:le,afterEach:ue,routes:t,_flat:A,_guards:V,_base:r,_mode:i};return a&&console.warn(`[elur] A router already exists. The previous router is being replaced. Only one router instance should be active at a time.`),a=$,queueMicrotask(()=>{G||W(O,``,x(O,A)?.route.beforeEnter,()=>{},()=>{let e=T(`/`,{});s?(l.set(E(`/`,{}),{left:0,top:0}),history.replaceState(history.state,``,e)):history.replaceState(p({},{left:0,top:0},F),``,e);let t=x(`/`,A);I.value={action:`replace`,direction:`root`},M.value=`/`,N.value=t?.params??{},P.value={},L.value=F>0,z(`/`,O,null)})}),$}function T(){return n.inject(i.RouterKey)||l()}function E(){o&&=(o(),null),a=null,i._mountedRouters.length=0}var D=class extends t.ElurComponent{_depth;_router;constructor(e=0,t){super(),this._depth=e,this._router=t}render(){let e=this._depth,t=this._router;return r.html`<div class="router-view">${()=>{let n=t??T(),i=x(n.current.value,n._flat);if(!i)return r.html`
2
2
  <div style="color:#f87171;padding:16px 0">
3
3
  404 — Route not found: <strong>${n.current.value}</strong>
4
4
  </div>
@@ -6,7 +6,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
6
6
  <span></span>
7
7
  `;let a=i.route.chain[e];return a?a():r.html`
8
8
  <span></span>
9
- `}}</div>`}},k=class extends t.ElurComponent{_to;_label;_router;constructor(e,t,n){super(),this._to=e,this._label=t,this._router=n}render(){let e=this._to,t=this._label,n=this._router??E(),i=e.startsWith(`/`)?e:`/`+e,a=(n._base?n._base+i:i).replace(/\/+/g,`/`);return r.html`
9
+ `}}</div>`}},O=class extends t.ElurComponent{_to;_label;_router;constructor(e,t,n){super(),this._to=e,this._label=t,this._router=n}render(){let e=this._to,t=this._label,n=this._router??T(),i=e.startsWith(`/`)?e:`/`+e,a=(n._base?n._base+i:i).replace(/\/+/g,`/`);return r.html`
10
10
  <a href=${n._mode===`hash`?`#`+a:a} style=${()=>n.current.value===e?`color:#38bdf8;font-weight:700;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px;background:#0c2a3a`:`color:#a3a3a3;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px`} @click=${t=>{t.preventDefault(),n.navigate(e)}}>${t}</a>
11
- `}};function A(e){let t=s.indexOf(e);t>=0&&s.splice(t,1),s.push(e)}function j(e){let t=s.indexOf(e);t>=0&&s.splice(t,1)}function M(){let e=s.length?s[s.length-1]:a;if(!e)return null;let t=e,n=t.current.value,r=S(n,t._flat),i=r?.route.beforeEnter,o=t._guards.map((e,t)=>e.name||`beforeEach#${t+1}`);return i&&o.push(i.name||`beforeEnter`),{mode:t._mode,base:t._base||`/`,currentPath:n,params:{...t.params.value},query:{...t.query.value},matchedPath:r?.route.fullPath??null,activeGuards:{globalCount:t._guards.length,hasRouteGuard:!!i,names:o}}}exports.Link=k,exports.RouterKey=i,exports.RouterView=O,exports._debugGetRouterInternal=M,exports._debugRegisterRouter=A,exports._debugUnregisterRouter=j,exports._hasActiveRouter=d,exports._resetRouter=D,exports.createRouter=T,exports.elurRouter=E;
11
+ `}};function k(){let e=i._mountedRouters.length?i._mountedRouters[i._mountedRouters.length-1]:a;if(!e)return null;let t=e,n=t.current.value,r=x(n,t._flat),o=r?.route.beforeEnter,s=t._guards.map((e,t)=>e.name||`beforeEach#${t+1}`);return o&&s.push(o.name||`beforeEnter`),{mode:t._mode,base:t._base||`/`,currentPath:n,params:{...t.params.value},query:{...t.query.value},matchedPath:r?.route.fullPath??null,activeGuards:{globalCount:t._guards.length,hasRouteGuard:!!o,names:s}}}exports.Link=O,exports.RouterKey=i.RouterKey,exports.RouterView=D,exports._debugGetRouterInternal=k,exports._debugRegisterRouter=i._debugRegisterRouter,exports._debugUnregisterRouter=i._debugUnregisterRouter,exports._hasActiveRouter=u,exports._resetRouter=E,exports.createRouter=w,exports.elurRouter=T;
12
12
  //# sourceMappingURL=router.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.cjs","names":[],"sources":["../../src/elur/router.ts"],"sourcesContent":["import { signal } from \"./reactivity.js\";\nimport type { Signal } from \"./reactivity.js\";\nimport { ElurComponent } from \"./lifecycle.js\";\nimport type { ElurTemplate } from \"./template/index.js\";\nimport { html } from \"./template/index.js\";\nimport { createInjectionKey, inject } from \"./context.js\";\n\n// =============================================================================\n// Public types\n// =============================================================================\n\n/**\n * Value returned (or resolved) by a navigation guard.\n *\n * - `true` / `void` / `undefined` — allow.\n * - `false` — cancel (no redirect).\n * - `string` — redirect to that path.\n * - `{ redirect: string }` — redirect, object form.\n *\n * The object form exists so guards written for the outlet API can be reused\n * verbatim by the core. See `elur-ionic`'s `GuardResult`.\n */\nexport type NavigationGuardResult =\n | void\n | undefined\n | boolean\n | string\n | { redirect: string };\n\n/** Guard function invoked before navigation commits. */\nexport type NavigationGuard = (\n to: string,\n from: string,\n) => NavigationGuardResult | Promise<NavigationGuardResult>;\n\nexport interface RouteRecord {\n /** Optional unique name to enable named navigation. */\n name?: string;\n /** Route path segment. Supports literals, params (`:id`), and wildcards (`*`). */\n path: string;\n /**\n * Factory returning the view for this route level.\n *\n * OPTIONAL — when the core router is auto-bootstrapped by an outlet\n * (Ionic's IonRouterOutlet, future others), the outlet owns component\n * mounting and the core never invokes this. In that case, omit it.\n */\n component?: () => ElurTemplate | ElurComponent;\n /** Optional arbitrary metadata for guards, layouts, and auth checks. */\n meta?: Record<string, unknown>;\n /** Child routes. Paths are joined with the parent. */\n children?: RouteRecord[];\n /** Route-level guard. Runs only when entering this specific route. */\n beforeEnter?: NavigationGuard;\n}\n\n/** Callback for `afterEach` hooks — receives the committed `to` and `from` paths. */\nexport type AfterEachHook = (to: string, from: string) => void;\n\n/** Named route target for programmatic navigation. */\nexport interface NamedRouteLocation {\n name: string;\n params?: Record<string, string | number>;\n query?: Record<string, string | number | boolean | null | undefined>;\n}\n\n/** Navigation input accepted by `navigate` / `replace`. */\nexport type RouteLocation = string | NamedRouteLocation;\n\n/** Serializable scroll position used by the router for history restoration. */\nexport interface ScrollPosition {\n left: number;\n top: number;\n}\n\nexport type ScrollBehavior = (\n to: string,\n from: string,\n savedPosition: ScrollPosition | null,\n) => ScrollPosition | false | void;\n\nexport type RouterMode = \"history\" | \"hash\";\n\nexport interface ResolvedRoute {\n matched: boolean;\n params: Record<string, string>;\n route: RouteRecord | undefined;\n}\n\n// -----------------------------------------------------------------------------\n// Navigation intent\n// -----------------------------------------------------------------------------\n\nexport type NavigationAction = \"push\" | \"replace\" | \"pop\" | \"initial\";\nexport type NavigationDirection = \"forward\" | \"back\" | \"root\" | \"none\";\n\nexport interface NavigationIntent {\n action: NavigationAction;\n direction: NavigationDirection;\n animation?: unknown;\n}\n\nexport interface NavigateOptions {\n query?: Record<string, string | number | boolean | null | undefined>;\n direction?: NavigationDirection;\n animation?: unknown;\n}\n\nexport interface RouterOptions {\n base?: string;\n mode?: RouterMode;\n scrollBehavior?: ScrollBehavior;\n}\n\nexport interface Router {\n readonly current: Signal<string>;\n readonly params: Signal<Record<string, string>>;\n readonly query: Signal<Record<string, string>>;\n readonly base: string;\n readonly intent: Signal<NavigationIntent>;\n readonly canGoBack: Signal<boolean>;\n navigate(location: RouteLocation, options?: NavigateOptions): void;\n replace(location: RouteLocation, options?: NavigateOptions): void;\n back(animation?: unknown): void;\n forward(animation?: unknown): void;\n go(delta: number): void;\n isActive(path: string, exact?: boolean): boolean;\n resolve(path: string): ResolvedRoute;\n readonly routes: RouteRecord[];\n beforeEach(guard: NavigationGuard): () => void;\n afterEach(hook: AfterEachHook): () => void;\n}\n\nexport const RouterKey = createInjectionKey<Router>(\"elur:router\");\n\n// =============================================================================\n// Internals\n// =============================================================================\n\ntype Segment =\n | { kind: \"literal\"; value: string }\n | { kind: \"param\"; name: string }\n | { kind: \"wildcard\" };\n\ninterface FlatRoute {\n fullPath: string;\n segments: Segment[];\n chain: Array<(() => ElurTemplate | ElurComponent) | undefined>;\n name?: string;\n meta?: Record<string, unknown>;\n beforeEnter?: NavigationGuard;\n record: RouteRecord;\n}\n\ninterface RouterInternal extends Router {\n _flat: FlatRoute[];\n _guards: NavigationGuard[];\n _base: string;\n _mode: RouterMode;\n}\n\nlet _currentRouter: RouterInternal | null = null;\nlet _currentPopstateCleanup: (() => void) | null = null;\n\n// Routers injected via mount({ router }) are not the global singleton. DevTools\n// tracks them separately so it can inspect the router actually active in the UI.\nconst _mountedRouters: Router[] = [];\n\nconst SCROLL_STATE_KEY = \"__elur_scroll\";\nconst POSITION_STATE_KEY = \"__elur_pos\";\n\nfunction getRouter(): RouterInternal {\n if (!_currentRouter) {\n throw new Error(\n \"[elur] No active router. Call createRouter() first, \" +\n \"or instantiate an outlet that auto-bootstraps one (e.g. IonRouterOutlet).\"\n );\n }\n return _currentRouter;\n}\n\n/**\n * @internal Whether a router is currently active. Used by outlets that\n * want to auto-bootstrap if the user didn't call `createRouter()` themselves.\n */\nexport function _hasActiveRouter(): boolean {\n return _currentRouter !== null;\n}\n\n// =============================================================================\n// History state helpers\n// =============================================================================\n\nfunction getCurrentScrollPosition(): ScrollPosition {\n return {\n left: window.scrollX ?? window.pageXOffset ?? 0,\n top: window.scrollY ?? window.pageYOffset ?? 0,\n };\n}\n\nfunction readScrollPositionFromState(state: unknown): ScrollPosition | null {\n if (!state || typeof state !== \"object\") return null;\n const raw = (state as Record<string, unknown>)[SCROLL_STATE_KEY];\n if (!raw || typeof raw !== \"object\") return null;\n const left = (raw as Record<string, unknown>).left;\n const top = (raw as Record<string, unknown>).top;\n if (typeof left !== \"number\" || typeof top !== \"number\") return null;\n return { left, top };\n}\n\nfunction readPositionFromState(state: unknown): number | null {\n if (!state || typeof state !== \"object\") return null;\n const raw = (state as Record<string, unknown>)[POSITION_STATE_KEY];\n return typeof raw === \"number\" ? raw : null;\n}\n\nfunction buildHistoryState(\n prev: unknown,\n scroll: ScrollPosition,\n position: number,\n): Record<string, unknown> {\n const base = prev && typeof prev === \"object\"\n ? { ...(prev as Record<string, unknown>) }\n : {};\n base[SCROLL_STATE_KEY] = { left: scroll.left, top: scroll.top };\n base[POSITION_STATE_KEY] = position;\n return base;\n}\n\n// =============================================================================\n// Query / path helpers\n// =============================================================================\n\nfunction parseQuery(search: string): Record<string, string> {\n const result: Record<string, string> = {};\n new URLSearchParams(search).forEach((v, k) => { result[k] = v; });\n return result;\n}\n\nfunction buildQueryString(\n q: Record<string, string | number | boolean | null | undefined>,\n): string {\n const p = new URLSearchParams();\n for (const [k, v] of Object.entries(q)) {\n if (v != null && v !== false) p.set(k, String(v));\n }\n const s = p.toString();\n return s ? \"?\" + s : \"\";\n}\n\nfunction parseSegments(fullPath: string): Segment[] {\n if (fullPath === \"*\") return [{ kind: \"wildcard\" }];\n return fullPath\n .split(\"/\")\n .filter(Boolean)\n .map((part): Segment => {\n if (part === \"*\") return { kind: \"wildcard\" };\n if (part.startsWith(\":\")) return { kind: \"param\", name: part.slice(1) };\n return { kind: \"literal\", value: part };\n });\n}\n\nfunction joinPaths(parent: string, child: string): string {\n if (child === \"*\") return parent === \"\" ? \"*\" : parent + \"/*\";\n const segment = child.startsWith(\"/\") ? child : \"/\" + child;\n return (parent + segment).replace(/\\/+/g, \"/\") || \"/\";\n}\n\nfunction flattenRoutes(\n routes: RouteRecord[],\n parentPath = \"\",\n parentChain: Array<(() => ElurTemplate | ElurComponent) | undefined> = [],\n): FlatRoute[] {\n const result: FlatRoute[] = [];\n for (const route of routes) {\n const fullPath = joinPaths(parentPath, route.path);\n const chain = [...parentChain, route.component];\n const segments = parseSegments(fullPath);\n result.push({\n fullPath,\n segments,\n chain,\n name: route.name,\n meta: route.meta,\n beforeEnter: route.beforeEnter,\n record: route,\n });\n if (route.children?.length) {\n result.push(...flattenRoutes(route.children, fullPath, chain));\n }\n }\n return result;\n}\n\nfunction tryMatch(path: string, route: FlatRoute): Record<string, string> | null {\n const parts = path.split(\"/\").filter(Boolean);\n const segs = route.segments;\n if (segs.length === 1 && segs[0].kind === \"wildcard\") return {};\n const lastIsWild = segs.length > 0 && segs[segs.length - 1].kind === \"wildcard\";\n const fixedSegs = lastIsWild ? segs.slice(0, -1) : segs;\n if (lastIsWild) {\n if (parts.length < fixedSegs.length) return null;\n } else {\n if (parts.length !== fixedSegs.length) return null;\n }\n const params: Record<string, string> = {};\n for (let i = 0; i < fixedSegs.length; i++) {\n const seg = fixedSegs[i];\n if (seg.kind === \"literal\") {\n if (parts[i] !== seg.value) return null;\n } else if (seg.kind === \"param\") {\n try {\n params[seg.name] = decodeURIComponent(parts[i] ?? \"\");\n } catch {\n params[seg.name] = parts[i] ?? \"\";\n }\n }\n }\n return params;\n}\n\nfunction specificity(route: FlatRoute): number {\n return route.segments.reduce((acc, seg) => {\n if (seg.kind === \"literal\") return acc + 2;\n if (seg.kind === \"param\") return acc + 1;\n return acc;\n }, 0);\n}\n\nfunction matchFlat(\n path: string,\n flat: FlatRoute[],\n): { route: FlatRoute; params: Record<string, string> } | undefined {\n let best: FlatRoute | undefined;\n let bestParams: Record<string, string> = {};\n let bestScore = -1;\n for (const route of flat) {\n const params = tryMatch(path, route);\n if (params === null) continue;\n const score = specificity(route);\n if (score > bestScore) {\n best = route;\n bestParams = params;\n bestScore = score;\n }\n }\n return best ? { route: best, params: bestParams } : undefined;\n}\n\n// =============================================================================\n// Base path helpers\n// =============================================================================\n\nfunction normalizeBase(raw: string): string {\n let b = raw.trim();\n if (!b || b === \"/\") return \"\";\n if (!b.startsWith(\"/\")) b = \"/\" + b;\n if (b.endsWith(\"/\")) b = b.slice(0, -1);\n return b;\n}\n\nfunction detectBase(): string {\n if (typeof document === \"undefined\") return \"\";\n const baseEl = document.querySelector(\"base\");\n if (!baseEl) return \"\";\n const href = baseEl.getAttribute(\"href\") || \"\";\n try {\n const url = new URL(href, window.location.origin);\n return normalizeBase(url.pathname);\n } catch {\n return normalizeBase(href);\n }\n}\n\n// =============================================================================\n// Guard result normalization\n// =============================================================================\n\n/**\n * Normalize any guard result into the internal flow's {allow, redirect} shape.\n * Accepts: `true`, `false`, `void`/`undefined`, `string`, `{ redirect: string }`.\n */\nfunction normalizeGuardResult(\n r: NavigationGuardResult,\n): { allow: boolean; redirect?: string } {\n if (r === false) return { allow: false };\n if (r === true || r === undefined || r === null) return { allow: true };\n if (typeof r === \"string\") return { allow: false, redirect: r };\n if (typeof r === \"object\" && \"redirect\" in r && typeof r.redirect === \"string\") {\n return { allow: false, redirect: r.redirect };\n }\n // Unknown — be permissive rather than break navigation.\n return { allow: true };\n}\n\n// =============================================================================\n// createRouter\n// =============================================================================\n\nexport function createRouter(routes: RouteRecord[], options?: RouterOptions): Router {\n const _base = options?.base != null ? normalizeBase(options.base) : detectBase();\n const _mode: RouterMode = options?.mode ?? \"history\";\n const _isHashMode = _mode === \"hash\";\n const _scrollBehavior = options?.scrollBehavior;\n const _hashScrollPositions = new Map<string, ScrollPosition>();\n let _ignoreNextHashChange = false;\n\n function normalizeAppPath(raw: string): string {\n if (!raw) return \"/\";\n return raw.startsWith(\"/\") ? raw : \"/\" + raw;\n }\n\n function stripBase(rawPath: string): string {\n const path = normalizeAppPath(rawPath || \"/\");\n if (_base && path.startsWith(_base)) {\n const stripped = path.slice(_base.length);\n return stripped === \"\" ? \"/\" : normalizeAppPath(stripped);\n }\n return path;\n }\n\n function withBase(appPath: string): string {\n const p = normalizeAppPath(appPath);\n if (!_base) return p;\n return (_base + p).replace(/\\/+/g, \"/\") || \"/\";\n }\n\n function readHashLocation(): { pathname: string; search: string } {\n let raw = window.location.hash || \"\";\n if (raw.startsWith(\"#\")) raw = raw.slice(1);\n if (!raw) return { pathname: \"/\", search: \"\" };\n if (!raw.startsWith(\"/\")) raw = \"/\" + raw;\n const qIdx = raw.indexOf(\"?\");\n const pathname = qIdx === -1 ? raw : raw.slice(0, qIdx);\n const search = qIdx === -1 ? \"\" : raw.slice(qIdx);\n return { pathname: stripBase(pathname), search };\n }\n\n function readLocation(): { pathname: string; search: string } {\n if (_isHashMode) return readHashLocation();\n return {\n pathname: stripBase(window.location.pathname || \"/\"),\n search: window.location.search || \"\",\n };\n }\n\n function buildUrl(pathname: string, stringQuery: Record<string, string>): string {\n const fullPath = withBase(pathname) + buildQueryString(stringQuery);\n return _isHashMode ? \"#\" + fullPath : fullPath;\n }\n\n function routeKey(pathname: string, stringQuery: Record<string, string>): string {\n return normalizeAppPath(pathname) + buildQueryString(stringQuery);\n }\n\n // -------------------------------------------------------------------------\n // Initial state\n // -------------------------------------------------------------------------\n\n const initialLoc = readLocation();\n const initialPath = initialLoc.pathname;\n const initialQuery = parseQuery(initialLoc.search);\n const flat = flattenRoutes(routes);\n\n const _nameIndex = new Map<string, FlatRoute>();\n for (const route of flat) {\n if (!route.name) continue;\n if (_nameIndex.has(route.name)) {\n console.warn(`[Elur Router] Duplicate route name: \"${route.name}\"`);\n }\n _nameIndex.set(route.name, route);\n }\n const initialMatch = matchFlat(initialPath, flat);\n\n const current = signal(initialPath);\n const params = signal<Record<string, string>>(initialMatch?.params ?? {});\n const query = signal<Record<string, string>>(initialQuery);\n\n let _currentPosition = readPositionFromState(history.state) ?? 0;\n\n const intent = signal<NavigationIntent>({\n action: \"initial\",\n direction: \"none\",\n });\n\n const canGoBack = signal<boolean>(_currentPosition > 0);\n\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(initialPath, initialQuery), getCurrentScrollPosition());\n } else {\n history.replaceState(\n buildHistoryState(history.state, getCurrentScrollPosition(), _currentPosition),\n \"\",\n );\n }\n\n // -------------------------------------------------------------------------\n // Scroll\n // -------------------------------------------------------------------------\n\n function _scrollTo(pos: ScrollPosition): void {\n window.scrollTo(pos.left, pos.top);\n }\n\n function _applyScroll(to: string, from: string, savedPosition: ScrollPosition | null): void {\n if (_scrollBehavior) {\n const result = _scrollBehavior(to, from, savedPosition);\n if (!result) return;\n _scrollTo(result);\n return;\n }\n _scrollTo(savedPosition ?? { left: 0, top: 0 });\n }\n\n function _saveCurrentEntryScroll(pathname: string, stringQuery: Record<string, string>): void {\n const pos = getCurrentScrollPosition();\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(pathname, stringQuery), pos);\n return;\n }\n history.replaceState(\n buildHistoryState(history.state, pos, _currentPosition),\n \"\",\n );\n }\n\n // -------------------------------------------------------------------------\n // Guards\n // -------------------------------------------------------------------------\n\n const _guards: NavigationGuard[] = [];\n const _afterHooks: AfterEachHook[] = [];\n let _navGeneration = 0;\n\n function _runGuards(\n to: string,\n from: string,\n routeGuard: NavigationGuard | undefined,\n onCommit: () => void,\n onCancel?: () => void,\n ): void {\n const guards: NavigationGuard[] = [..._guards];\n if (routeGuard) guards.push(routeGuard);\n\n const gen = ++_navGeneration;\n\n if (guards.length === 0) { onCommit(); return; }\n\n let idx = 0;\n function runNext(prev: NavigationGuardResult): void {\n if (gen !== _navGeneration) return;\n\n const norm = normalizeGuardResult(prev);\n if (!norm.allow) {\n if (norm.redirect && norm.redirect !== to) {\n navigate(norm.redirect);\n return;\n }\n if (norm.redirect === to) {\n // Guarding TO the same path — treat as allow to avoid loops\n onCommit();\n return;\n }\n onCancel?.();\n return;\n }\n if (idx >= guards.length) { onCommit(); return; }\n const result = guards[idx++](to, from);\n if (result instanceof Promise) { result.then(runNext); return; }\n runNext(result);\n }\n runNext(undefined);\n }\n\n // -------------------------------------------------------------------------\n // Path / location resolution\n // -------------------------------------------------------------------------\n\n let _hasNavigated = false;\n\n function _parsePath(\n path: string,\n queryObj?: Record<string, string | number | boolean | null | undefined>,\n ): { pathname: string; stringQuery: Record<string, string> } {\n const qIdx = path.indexOf(\"?\");\n const rawPath = qIdx === -1 ? path : path.slice(0, qIdx);\n const pathname = normalizeAppPath(rawPath || \"/\");\n const inlineQ = qIdx === -1 ? {} : parseQuery(path.slice(qIdx));\n const finalQuery = queryObj ? { ...inlineQ, ...queryObj } : inlineQ;\n const stringQuery: Record<string, string> = {};\n for (const [k, v] of Object.entries(finalQuery)) {\n if (v != null && v !== false) stringQuery[k] = String(v);\n }\n return { pathname, stringQuery };\n }\n\n function _resolveNamedPath(location: NamedRouteLocation): string {\n const found = _nameIndex.get(location.name);\n if (!found) {\n throw new Error(`[Elur Router] No route with name \"${location.name}\"`);\n }\n const parts = found.segments.map((seg) => {\n if (seg.kind === \"literal\") return seg.value;\n if (seg.kind === \"wildcard\") return \"\";\n const value = location.params?.[seg.name];\n if (value == null) {\n throw new Error(\n `[Elur Router] Missing param \"${seg.name}\" for route \"${location.name}\"`,\n );\n }\n return encodeURIComponent(String(value));\n });\n return \"/\" + parts.filter(Boolean).join(\"/\");\n }\n\n function _resolveLocation(\n location: RouteLocation,\n options?: NavigateOptions,\n ): { pathname: string; stringQuery: Record<string, string> } {\n if (typeof location === \"string\") {\n return _parsePath(location, options?.query);\n }\n const pathname = _resolveNamedPath(location);\n const mergedQuery = { ...(location.query ?? {}), ...(options?.query ?? {}) };\n return _parsePath(pathname, mergedQuery);\n }\n\n // -------------------------------------------------------------------------\n // Popstate / hashchange listener\n // -------------------------------------------------------------------------\n\n if (_currentPopstateCleanup) {\n _currentPopstateCleanup();\n _currentPopstateCleanup = null;\n }\n\n const handleBrowserNav = (\n p: string,\n newQuery: Record<string, string>,\n savedPos: ScrollPosition | null,\n nextPosition: number | null,\n onCancelRestore: (from: string, fromQuery: Record<string, string>) => void,\n ) => {\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(p, flat);\n\n let direction: NavigationDirection = \"none\";\n if (nextPosition != null) {\n if (nextPosition < _currentPosition) direction = \"back\";\n else if (nextPosition > _currentPosition) direction = \"forward\";\n }\n\n _runGuards(\n p,\n from,\n m?.route.beforeEnter,\n () => {\n if (nextPosition != null) _currentPosition = nextPosition;\n const animation = _pendingPopAnimation;\n _pendingPopAnimation = undefined;\n intent.value = { action: \"pop\", direction, animation };\n params.value = m?.params ?? {};\n query.value = newQuery;\n current.value = p;\n canGoBack.value = _currentPosition > 0;\n _applyScroll(p, from, savedPos);\n for (const hook of _afterHooks) {\n try { hook(p, from); } catch { /* ignore */ }\n }\n },\n () => onCancelRestore(from, fromQuery),\n );\n };\n\n if (_isHashMode) {\n const onHashChange = () => {\n if (_ignoreNextHashChange) {\n _ignoreNextHashChange = false;\n return;\n }\n const loc = readLocation();\n const nextQuery = parseQuery(loc.search);\n const savedPos = _hashScrollPositions.get(routeKey(loc.pathname, nextQuery)) ?? null;\n handleBrowserNav(\n loc.pathname,\n nextQuery,\n savedPos,\n null,\n (from, fromQuery) => {\n _ignoreNextHashChange = true;\n window.location.hash = buildUrl(from, fromQuery).slice(1);\n queueMicrotask(() => { _ignoreNextHashChange = false; });\n },\n );\n };\n window.addEventListener(\"hashchange\", onHashChange);\n _currentPopstateCleanup = () => window.removeEventListener(\"hashchange\", onHashChange);\n } else {\n const onPopstate = (ev: PopStateEvent) => {\n const loc = readLocation();\n const nextQuery = parseQuery(loc.search);\n const savedPos = readScrollPositionFromState(ev.state ?? history.state);\n const nextPos = readPositionFromState(ev.state ?? history.state);\n handleBrowserNav(\n loc.pathname,\n nextQuery,\n savedPos,\n nextPos,\n (from, fromQuery) => {\n history.pushState(\n buildHistoryState({}, getCurrentScrollPosition(), _currentPosition),\n \"\",\n buildUrl(from, fromQuery),\n );\n },\n );\n };\n window.addEventListener(\"popstate\", onPopstate);\n _currentPopstateCleanup = () => window.removeEventListener(\"popstate\", onPopstate);\n }\n\n // -------------------------------------------------------------------------\n // Internal commit (programmatic navigation)\n // -------------------------------------------------------------------------\n\n function _commit(\n pathname: string,\n stringQuery: Record<string, string>,\n from: string,\n fromQuery: Record<string, string>,\n m: ReturnType<typeof matchFlat>,\n nextIntent: NavigationIntent,\n useReplace: boolean,\n ): void {\n if (!useReplace) {\n _saveCurrentEntryScroll(from, fromQuery);\n _currentPosition += 1;\n }\n\n intent.value = nextIntent;\n params.value = m?.params ?? {};\n query.value = stringQuery;\n current.value = pathname;\n canGoBack.value = _currentPosition > 0;\n\n const url = buildUrl(pathname, stringQuery);\n\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(pathname, stringQuery), { left: 0, top: 0 });\n if (useReplace) {\n history.replaceState(history.state, \"\", url);\n } else {\n _ignoreNextHashChange = true;\n window.location.hash = url.slice(1);\n queueMicrotask(() => { _ignoreNextHashChange = false; });\n }\n } else {\n const nextState = buildHistoryState({}, { left: 0, top: 0 }, _currentPosition);\n if (useReplace) {\n history.replaceState(nextState, \"\", url);\n } else {\n history.pushState(nextState, \"\", url);\n }\n }\n\n _applyScroll(pathname, from, null);\n for (const hook of _afterHooks) {\n try { hook(pathname, from); } catch { /* ignore */ }\n }\n }\n\n // -------------------------------------------------------------------------\n // Public navigation API\n // -------------------------------------------------------------------------\n\n let _pendingPopAnimation: unknown = undefined;\n\n function navigate(location: RouteLocation, options?: NavigateOptions): void {\n _hasNavigated = true;\n const { pathname, stringQuery } = _resolveLocation(location, options);\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(pathname, flat);\n\n const nextIntent: NavigationIntent = {\n action: \"push\",\n direction: options?.direction ?? \"forward\",\n animation: options?.animation,\n };\n\n _runGuards(\n pathname,\n from,\n m?.route.beforeEnter,\n () => _commit(pathname, stringQuery, from, fromQuery, m, nextIntent, false),\n );\n }\n\n function replace(location: RouteLocation, options?: NavigateOptions): void {\n _hasNavigated = true;\n const { pathname, stringQuery } = _resolveLocation(location, options);\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(pathname, flat);\n\n const nextIntent: NavigationIntent = {\n action: \"replace\",\n direction: options?.direction ?? \"root\",\n animation: options?.animation,\n };\n\n _runGuards(\n pathname,\n from,\n m?.route.beforeEnter,\n () => _commit(pathname, stringQuery, from, fromQuery, m, nextIntent, true),\n );\n }\n\n function back(animation?: unknown): void {\n if (animation !== undefined) _pendingPopAnimation = animation;\n history.back();\n }\n\n function forward(animation?: unknown): void {\n if (animation !== undefined) _pendingPopAnimation = animation;\n history.forward();\n }\n\n function go(delta: number): void { history.go(delta); }\n\n function isActive(path: string, exact = true): boolean {\n const cur = current.value;\n if (exact) return cur === path;\n return cur === path || cur.startsWith(path.endsWith(\"/\") ? path : path + \"/\");\n }\n\n function resolve(path: string): ResolvedRoute {\n const m = matchFlat(path, flat);\n if (!m) return { matched: false, params: {}, route: undefined };\n return { matched: true, params: m.params, route: m.route.record };\n }\n\n function beforeEach(guard: NavigationGuard): () => void {\n _guards.push(guard);\n return () => {\n const idx = _guards.indexOf(guard);\n if (idx !== -1) _guards.splice(idx, 1);\n };\n }\n\n function afterEach(hook: AfterEachHook): () => void {\n _afterHooks.push(hook);\n return () => {\n const idx = _afterHooks.indexOf(hook);\n if (idx !== -1) _afterHooks.splice(idx, 1);\n };\n }\n\n const router: RouterInternal = {\n current, params, query, intent, canGoBack,\n base: _base || \"/\",\n navigate, replace, back, forward, go,\n isActive, resolve,\n beforeEach, afterEach, routes,\n _flat: flat, _guards, _base, _mode,\n };\n\n if (_currentRouter) {\n console.warn(\n \"[elur] A router already exists. The previous router is being replaced. \" +\n \"Only one router instance should be active at a time.\",\n );\n }\n _currentRouter = router;\n\n queueMicrotask(() => {\n if (_hasNavigated) return;\n\n const m = matchFlat(initialPath, flat);\n _runGuards(\n initialPath,\n \"\",\n m?.route.beforeEnter,\n () => { /* allowed */ },\n () => {\n const fallback = \"/\";\n const url = buildUrl(fallback, {});\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(fallback, {}), { left: 0, top: 0 });\n history.replaceState(history.state, \"\", url);\n } else {\n history.replaceState(\n buildHistoryState({}, { left: 0, top: 0 }, _currentPosition),\n \"\",\n url,\n );\n }\n const fm = matchFlat(fallback, flat);\n intent.value = { action: \"replace\", direction: \"root\" };\n current.value = fallback;\n params.value = fm?.params ?? {};\n query.value = {};\n canGoBack.value = _currentPosition > 0;\n _applyScroll(fallback, initialPath, null);\n },\n );\n });\n\n return router;\n}\n\nexport function elurRouter(): Router {\n const injected = inject(RouterKey);\n if (injected) return injected;\n return getRouter();\n}\n\n/** @internal */\nexport function _resetRouter(): void {\n if (_currentPopstateCleanup) {\n _currentPopstateCleanup();\n _currentPopstateCleanup = null;\n }\n _currentRouter = null;\n _mountedRouters.length = 0;\n}\n\nexport class RouterView extends ElurComponent {\n private _depth: number;\n private _router?: RouterInternal;\n\n constructor(depth = 0, router?: Router) {\n super();\n this._depth = depth;\n this._router = router as RouterInternal | undefined;\n }\n\n render(): ElurTemplate {\n const depth = this._depth;\n const explicitRouter = this._router;\n return html`<div class=\"router-view\">${() => {\n const router = explicitRouter ?? elurRouter() as RouterInternal;\n const matched = matchFlat(router.current.value, router._flat);\n if (!matched) {\n return html`\n <div style=\"color:#f87171;padding:16px 0\">\n 404 — Route not found: <strong>${router.current.value}</strong>\n </div>\n `;\n }\n if (depth >= matched.route.chain.length) {\n return html`\n <span></span>\n `;\n }\n const factory = matched.route.chain[depth];\n // chain entries can be undefined when the route was registered without\n // a `component` (typical when an outlet auto-bootstraps the router).\n // In that case there's nothing to render at this depth.\n if (!factory) return html`\n <span></span>\n `;\n return factory();\n }}</div>`;\n }\n}\n\nexport class Link extends ElurComponent {\n private _to: string;\n private _label: string;\n private _router?: RouterInternal;\n\n constructor(to: string, label: string, router?: Router) {\n super();\n this._to = to;\n this._label = label;\n this._router = router as RouterInternal | undefined;\n }\n\n render(): ElurTemplate {\n const to = this._to;\n const label = this._label;\n const router = this._router ?? elurRouter() as RouterInternal;\n const appPath = to.startsWith(\"/\") ? to : \"/\" + to;\n const fullPath = (router._base ? (router._base + appPath) : appPath).replace(/\\/+/g, \"/\");\n const href = router._mode === \"hash\" ? \"#\" + fullPath : fullPath;\n return html`\n <a href=${href} style=${() => router.current.value === to\n ? \"color:#38bdf8;font-weight:700;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px;background:#0c2a3a\"\n : \"color:#a3a3a3;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px\"} @click=${(e: Event) => { e.preventDefault(); router.navigate(to); }}>${label}</a>\n `;\n }\n}\n\nexport interface _RouterDebugInternal {\n mode: RouterMode;\n base: string;\n currentPath: string;\n params: Record<string, string>;\n query: Record<string, string>;\n matchedPath: string | null;\n activeGuards: { globalCount: number; hasRouteGuard: boolean; names: string[] };\n}\n\n/** @internal Register a router that was injected via mount({ router }). */\nexport function _debugRegisterRouter(router: Router): void {\n const idx = _mountedRouters.indexOf(router);\n if (idx >= 0) _mountedRouters.splice(idx, 1);\n _mountedRouters.push(router);\n}\n\n/** @internal Unregister a router when its mount point is unmounted. */\nexport function _debugUnregisterRouter(router: Router): void {\n const idx = _mountedRouters.indexOf(router);\n if (idx >= 0) _mountedRouters.splice(idx, 1);\n}\n\nexport function _debugGetRouterInternal(): _RouterDebugInternal | null {\n // Prefer the most recently mounted injected router over the global singleton\n // so devtools inspect the router actually active in the current UI.\n const router = _mountedRouters.length\n ? _mountedRouters[_mountedRouters.length - 1]\n : _currentRouter;\n if (!router) return null;\n const internal = router as RouterInternal;\n const currentPath = internal.current.value;\n const matched = matchFlat(currentPath, internal._flat);\n const routeGuard = matched?.route.beforeEnter;\n const names = internal._guards.map((g, idx) => g.name || `beforeEach#${idx + 1}`);\n if (routeGuard) names.push(routeGuard.name || \"beforeEnter\");\n return {\n mode: internal._mode,\n base: internal._base || \"/\",\n currentPath,\n params: { ...internal.params.value },\n query: { ...internal.query.value },\n matchedPath: matched?.route.fullPath ?? null,\n activeGuards: {\n globalCount: internal._guards.length,\n hasRouteGuard: Boolean(routeGuard),\n names,\n },\n };\n}"],"mappings":"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"}
1
+ {"version":3,"file":"router.cjs","names":[],"sources":["../../src/elur/router.ts"],"sourcesContent":["import { signal } from \"./reactivity.js\";\nimport type { Signal } from \"./reactivity.js\";\nimport { ElurComponent } from \"./lifecycle.js\";\nimport type { ElurTemplate } from \"./template/index.js\";\nimport { html } from \"./template/index.js\";\nimport { inject } from \"./context.js\";\nimport { RouterKey, _mountedRouters, _debugRegisterRouter, _debugUnregisterRouter } from \"./router-registry.js\";\n\n// =============================================================================\n// Public types\n// =============================================================================\n\n/**\n * Value returned (or resolved) by a navigation guard.\n *\n * - `true` / `void` / `undefined` — allow.\n * - `false` — cancel (no redirect).\n * - `string` — redirect to that path.\n * - `{ redirect: string }` — redirect, object form.\n *\n * The object form exists so guards written for the outlet API can be reused\n * verbatim by the core. See `elur-ionic`'s `GuardResult`.\n */\nexport type NavigationGuardResult =\n | void\n | undefined\n | boolean\n | string\n | { redirect: string };\n\n/** Guard function invoked before navigation commits. */\nexport type NavigationGuard = (\n to: string,\n from: string,\n) => NavigationGuardResult | Promise<NavigationGuardResult>;\n\nexport interface RouteRecord {\n /** Optional unique name to enable named navigation. */\n name?: string;\n /** Route path segment. Supports literals, params (`:id`), and wildcards (`*`). */\n path: string;\n /**\n * Factory returning the view for this route level.\n *\n * OPTIONAL — when the core router is auto-bootstrapped by an outlet\n * (Ionic's IonRouterOutlet, future others), the outlet owns component\n * mounting and the core never invokes this. In that case, omit it.\n */\n component?: () => ElurTemplate | ElurComponent;\n /** Optional arbitrary metadata for guards, layouts, and auth checks. */\n meta?: Record<string, unknown>;\n /** Child routes. Paths are joined with the parent. */\n children?: RouteRecord[];\n /** Route-level guard. Runs only when entering this specific route. */\n beforeEnter?: NavigationGuard;\n}\n\n/** Callback for `afterEach` hooks — receives the committed `to` and `from` paths. */\nexport type AfterEachHook = (to: string, from: string) => void;\n\n/** Named route target for programmatic navigation. */\nexport interface NamedRouteLocation {\n name: string;\n params?: Record<string, string | number>;\n query?: Record<string, string | number | boolean | null | undefined>;\n}\n\n/** Navigation input accepted by `navigate` / `replace`. */\nexport type RouteLocation = string | NamedRouteLocation;\n\n/** Serializable scroll position used by the router for history restoration. */\nexport interface ScrollPosition {\n left: number;\n top: number;\n}\n\nexport type ScrollBehavior = (\n to: string,\n from: string,\n savedPosition: ScrollPosition | null,\n) => ScrollPosition | false | void;\n\nexport type RouterMode = \"history\" | \"hash\";\n\nexport interface ResolvedRoute {\n matched: boolean;\n params: Record<string, string>;\n route: RouteRecord | undefined;\n}\n\n// -----------------------------------------------------------------------------\n// Navigation intent\n// -----------------------------------------------------------------------------\n\nexport type NavigationAction = \"push\" | \"replace\" | \"pop\" | \"initial\";\nexport type NavigationDirection = \"forward\" | \"back\" | \"root\" | \"none\";\n\nexport interface NavigationIntent {\n action: NavigationAction;\n direction: NavigationDirection;\n animation?: unknown;\n}\n\nexport interface NavigateOptions {\n query?: Record<string, string | number | boolean | null | undefined>;\n direction?: NavigationDirection;\n animation?: unknown;\n}\n\nexport interface RouterOptions {\n base?: string;\n mode?: RouterMode;\n scrollBehavior?: ScrollBehavior;\n}\n\nexport interface Router {\n readonly current: Signal<string>;\n readonly params: Signal<Record<string, string>>;\n readonly query: Signal<Record<string, string>>;\n readonly base: string;\n readonly intent: Signal<NavigationIntent>;\n readonly canGoBack: Signal<boolean>;\n navigate(location: RouteLocation, options?: NavigateOptions): void;\n replace(location: RouteLocation, options?: NavigateOptions): void;\n back(animation?: unknown): void;\n forward(animation?: unknown): void;\n go(delta: number): void;\n isActive(path: string, exact?: boolean): boolean;\n resolve(path: string): ResolvedRoute;\n readonly routes: RouteRecord[];\n beforeEach(guard: NavigationGuard): () => void;\n afterEach(hook: AfterEachHook): () => void;\n}\n\n// RouterKey and the mounted-router debug registry live in router-registry.ts\n// so that component.ts (mount) can use them without importing this module —\n// keeping the router out of bundles that never call createRouter().\nexport { RouterKey, _debugRegisterRouter, _debugUnregisterRouter };\n\n// =============================================================================\n// Internals\n// =============================================================================\n\ntype Segment =\n | { kind: \"literal\"; value: string }\n | { kind: \"param\"; name: string }\n | { kind: \"wildcard\" };\n\ninterface FlatRoute {\n fullPath: string;\n segments: Segment[];\n chain: Array<(() => ElurTemplate | ElurComponent) | undefined>;\n name?: string;\n meta?: Record<string, unknown>;\n beforeEnter?: NavigationGuard;\n record: RouteRecord;\n}\n\ninterface RouterInternal extends Router {\n _flat: FlatRoute[];\n _guards: NavigationGuard[];\n _base: string;\n _mode: RouterMode;\n}\n\nlet _currentRouter: RouterInternal | null = null;\nlet _currentPopstateCleanup: (() => void) | null = null;\n\nconst SCROLL_STATE_KEY = \"__elur_scroll\";\nconst POSITION_STATE_KEY = \"__elur_pos\";\n\nfunction getRouter(): RouterInternal {\n if (!_currentRouter) {\n throw new Error(\n \"[elur] No active router. Call createRouter() first, \" +\n \"or instantiate an outlet that auto-bootstraps one (e.g. IonRouterOutlet).\"\n );\n }\n return _currentRouter;\n}\n\n/**\n * @internal Whether a router is currently active. Used by outlets that\n * want to auto-bootstrap if the user didn't call `createRouter()` themselves.\n */\nexport function _hasActiveRouter(): boolean {\n return _currentRouter !== null;\n}\n\n// =============================================================================\n// History state helpers\n// =============================================================================\n\nfunction getCurrentScrollPosition(): ScrollPosition {\n return {\n left: window.scrollX ?? window.pageXOffset ?? 0,\n top: window.scrollY ?? window.pageYOffset ?? 0,\n };\n}\n\nfunction readScrollPositionFromState(state: unknown): ScrollPosition | null {\n if (!state || typeof state !== \"object\") return null;\n const raw = (state as Record<string, unknown>)[SCROLL_STATE_KEY];\n if (!raw || typeof raw !== \"object\") return null;\n const left = (raw as Record<string, unknown>).left;\n const top = (raw as Record<string, unknown>).top;\n if (typeof left !== \"number\" || typeof top !== \"number\") return null;\n return { left, top };\n}\n\nfunction readPositionFromState(state: unknown): number | null {\n if (!state || typeof state !== \"object\") return null;\n const raw = (state as Record<string, unknown>)[POSITION_STATE_KEY];\n return typeof raw === \"number\" ? raw : null;\n}\n\nfunction buildHistoryState(\n prev: unknown,\n scroll: ScrollPosition,\n position: number,\n): Record<string, unknown> {\n const base = prev && typeof prev === \"object\"\n ? { ...(prev as Record<string, unknown>) }\n : {};\n base[SCROLL_STATE_KEY] = { left: scroll.left, top: scroll.top };\n base[POSITION_STATE_KEY] = position;\n return base;\n}\n\n// =============================================================================\n// Query / path helpers\n// =============================================================================\n\nfunction parseQuery(search: string): Record<string, string> {\n const result: Record<string, string> = {};\n new URLSearchParams(search).forEach((v, k) => { result[k] = v; });\n return result;\n}\n\nfunction buildQueryString(\n q: Record<string, string | number | boolean | null | undefined>,\n): string {\n const p = new URLSearchParams();\n for (const [k, v] of Object.entries(q)) {\n if (v != null && v !== false) p.set(k, String(v));\n }\n const s = p.toString();\n return s ? \"?\" + s : \"\";\n}\n\nfunction parseSegments(fullPath: string): Segment[] {\n if (fullPath === \"*\") return [{ kind: \"wildcard\" }];\n return fullPath\n .split(\"/\")\n .filter(Boolean)\n .map((part): Segment => {\n if (part === \"*\") return { kind: \"wildcard\" };\n if (part.startsWith(\":\")) return { kind: \"param\", name: part.slice(1) };\n return { kind: \"literal\", value: part };\n });\n}\n\nfunction joinPaths(parent: string, child: string): string {\n if (child === \"*\") return parent === \"\" ? \"*\" : parent + \"/*\";\n const segment = child.startsWith(\"/\") ? child : \"/\" + child;\n return (parent + segment).replace(/\\/+/g, \"/\") || \"/\";\n}\n\nfunction flattenRoutes(\n routes: RouteRecord[],\n parentPath = \"\",\n parentChain: Array<(() => ElurTemplate | ElurComponent) | undefined> = [],\n): FlatRoute[] {\n const result: FlatRoute[] = [];\n for (const route of routes) {\n const fullPath = joinPaths(parentPath, route.path);\n const chain = [...parentChain, route.component];\n const segments = parseSegments(fullPath);\n result.push({\n fullPath,\n segments,\n chain,\n name: route.name,\n meta: route.meta,\n beforeEnter: route.beforeEnter,\n record: route,\n });\n if (route.children?.length) {\n result.push(...flattenRoutes(route.children, fullPath, chain));\n }\n }\n return result;\n}\n\nfunction tryMatch(path: string, route: FlatRoute): Record<string, string> | null {\n const parts = path.split(\"/\").filter(Boolean);\n const segs = route.segments;\n if (segs.length === 1 && segs[0].kind === \"wildcard\") return {};\n const lastIsWild = segs.length > 0 && segs[segs.length - 1].kind === \"wildcard\";\n const fixedSegs = lastIsWild ? segs.slice(0, -1) : segs;\n if (lastIsWild) {\n if (parts.length < fixedSegs.length) return null;\n } else {\n if (parts.length !== fixedSegs.length) return null;\n }\n const params: Record<string, string> = {};\n for (let i = 0; i < fixedSegs.length; i++) {\n const seg = fixedSegs[i];\n if (seg.kind === \"literal\") {\n if (parts[i] !== seg.value) return null;\n } else if (seg.kind === \"param\") {\n try {\n params[seg.name] = decodeURIComponent(parts[i] ?? \"\");\n } catch {\n params[seg.name] = parts[i] ?? \"\";\n }\n }\n }\n return params;\n}\n\nfunction specificity(route: FlatRoute): number {\n return route.segments.reduce((acc, seg) => {\n if (seg.kind === \"literal\") return acc + 2;\n if (seg.kind === \"param\") return acc + 1;\n return acc;\n }, 0);\n}\n\nfunction matchFlat(\n path: string,\n flat: FlatRoute[],\n): { route: FlatRoute; params: Record<string, string> } | undefined {\n let best: FlatRoute | undefined;\n let bestParams: Record<string, string> = {};\n let bestScore = -1;\n for (const route of flat) {\n const params = tryMatch(path, route);\n if (params === null) continue;\n const score = specificity(route);\n if (score > bestScore) {\n best = route;\n bestParams = params;\n bestScore = score;\n }\n }\n return best ? { route: best, params: bestParams } : undefined;\n}\n\n// =============================================================================\n// Base path helpers\n// =============================================================================\n\nfunction normalizeBase(raw: string): string {\n let b = raw.trim();\n if (!b || b === \"/\") return \"\";\n if (!b.startsWith(\"/\")) b = \"/\" + b;\n if (b.endsWith(\"/\")) b = b.slice(0, -1);\n return b;\n}\n\nfunction detectBase(): string {\n if (typeof document === \"undefined\") return \"\";\n const baseEl = document.querySelector(\"base\");\n if (!baseEl) return \"\";\n const href = baseEl.getAttribute(\"href\") || \"\";\n try {\n const url = new URL(href, window.location.origin);\n return normalizeBase(url.pathname);\n } catch {\n return normalizeBase(href);\n }\n}\n\n// =============================================================================\n// Guard result normalization\n// =============================================================================\n\n/**\n * Normalize any guard result into the internal flow's {allow, redirect} shape.\n * Accepts: `true`, `false`, `void`/`undefined`, `string`, `{ redirect: string }`.\n */\nfunction normalizeGuardResult(\n r: NavigationGuardResult,\n): { allow: boolean; redirect?: string } {\n if (r === false) return { allow: false };\n if (r === true || r === undefined || r === null) return { allow: true };\n if (typeof r === \"string\") return { allow: false, redirect: r };\n if (typeof r === \"object\" && \"redirect\" in r && typeof r.redirect === \"string\") {\n return { allow: false, redirect: r.redirect };\n }\n // Unknown — be permissive rather than break navigation.\n return { allow: true };\n}\n\n// =============================================================================\n// createRouter\n// =============================================================================\n\nexport function createRouter(routes: RouteRecord[], options?: RouterOptions): Router {\n const _base = options?.base != null ? normalizeBase(options.base) : detectBase();\n const _mode: RouterMode = options?.mode ?? \"history\";\n const _isHashMode = _mode === \"hash\";\n const _scrollBehavior = options?.scrollBehavior;\n const _hashScrollPositions = new Map<string, ScrollPosition>();\n let _ignoreNextHashChange = false;\n\n function normalizeAppPath(raw: string): string {\n if (!raw) return \"/\";\n return raw.startsWith(\"/\") ? raw : \"/\" + raw;\n }\n\n function stripBase(rawPath: string): string {\n const path = normalizeAppPath(rawPath || \"/\");\n if (_base && path.startsWith(_base)) {\n const stripped = path.slice(_base.length);\n return stripped === \"\" ? \"/\" : normalizeAppPath(stripped);\n }\n return path;\n }\n\n function withBase(appPath: string): string {\n const p = normalizeAppPath(appPath);\n if (!_base) return p;\n return (_base + p).replace(/\\/+/g, \"/\") || \"/\";\n }\n\n function readHashLocation(): { pathname: string; search: string } {\n let raw = window.location.hash || \"\";\n if (raw.startsWith(\"#\")) raw = raw.slice(1);\n if (!raw) return { pathname: \"/\", search: \"\" };\n if (!raw.startsWith(\"/\")) raw = \"/\" + raw;\n const qIdx = raw.indexOf(\"?\");\n const pathname = qIdx === -1 ? raw : raw.slice(0, qIdx);\n const search = qIdx === -1 ? \"\" : raw.slice(qIdx);\n return { pathname: stripBase(pathname), search };\n }\n\n function readLocation(): { pathname: string; search: string } {\n if (_isHashMode) return readHashLocation();\n return {\n pathname: stripBase(window.location.pathname || \"/\"),\n search: window.location.search || \"\",\n };\n }\n\n function buildUrl(pathname: string, stringQuery: Record<string, string>): string {\n const fullPath = withBase(pathname) + buildQueryString(stringQuery);\n return _isHashMode ? \"#\" + fullPath : fullPath;\n }\n\n function routeKey(pathname: string, stringQuery: Record<string, string>): string {\n return normalizeAppPath(pathname) + buildQueryString(stringQuery);\n }\n\n // -------------------------------------------------------------------------\n // Initial state\n // -------------------------------------------------------------------------\n\n const initialLoc = readLocation();\n const initialPath = initialLoc.pathname;\n const initialQuery = parseQuery(initialLoc.search);\n const flat = flattenRoutes(routes);\n\n const _nameIndex = new Map<string, FlatRoute>();\n for (const route of flat) {\n if (!route.name) continue;\n if (_nameIndex.has(route.name)) {\n console.warn(`[Elur Router] Duplicate route name: \"${route.name}\"`);\n }\n _nameIndex.set(route.name, route);\n }\n const initialMatch = matchFlat(initialPath, flat);\n\n const current = signal(initialPath);\n const params = signal<Record<string, string>>(initialMatch?.params ?? {});\n const query = signal<Record<string, string>>(initialQuery);\n\n let _currentPosition = readPositionFromState(history.state) ?? 0;\n\n const intent = signal<NavigationIntent>({\n action: \"initial\",\n direction: \"none\",\n });\n\n const canGoBack = signal<boolean>(_currentPosition > 0);\n\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(initialPath, initialQuery), getCurrentScrollPosition());\n } else {\n history.replaceState(\n buildHistoryState(history.state, getCurrentScrollPosition(), _currentPosition),\n \"\",\n );\n }\n\n // -------------------------------------------------------------------------\n // Scroll\n // -------------------------------------------------------------------------\n\n function _scrollTo(pos: ScrollPosition): void {\n window.scrollTo(pos.left, pos.top);\n }\n\n function _applyScroll(to: string, from: string, savedPosition: ScrollPosition | null): void {\n if (_scrollBehavior) {\n const result = _scrollBehavior(to, from, savedPosition);\n if (!result) return;\n _scrollTo(result);\n return;\n }\n _scrollTo(savedPosition ?? { left: 0, top: 0 });\n }\n\n function _saveCurrentEntryScroll(pathname: string, stringQuery: Record<string, string>): void {\n const pos = getCurrentScrollPosition();\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(pathname, stringQuery), pos);\n return;\n }\n history.replaceState(\n buildHistoryState(history.state, pos, _currentPosition),\n \"\",\n );\n }\n\n // -------------------------------------------------------------------------\n // Guards\n // -------------------------------------------------------------------------\n\n const _guards: NavigationGuard[] = [];\n const _afterHooks: AfterEachHook[] = [];\n let _navGeneration = 0;\n\n function _runGuards(\n to: string,\n from: string,\n routeGuard: NavigationGuard | undefined,\n onCommit: () => void,\n onCancel?: () => void,\n ): void {\n const guards: NavigationGuard[] = [..._guards];\n if (routeGuard) guards.push(routeGuard);\n\n const gen = ++_navGeneration;\n\n if (guards.length === 0) { onCommit(); return; }\n\n let idx = 0;\n function runNext(prev: NavigationGuardResult): void {\n if (gen !== _navGeneration) return;\n\n const norm = normalizeGuardResult(prev);\n if (!norm.allow) {\n if (norm.redirect && norm.redirect !== to) {\n navigate(norm.redirect);\n return;\n }\n if (norm.redirect === to) {\n // Guarding TO the same path — treat as allow to avoid loops\n onCommit();\n return;\n }\n onCancel?.();\n return;\n }\n if (idx >= guards.length) { onCommit(); return; }\n const result = guards[idx++](to, from);\n if (result instanceof Promise) { result.then(runNext); return; }\n runNext(result);\n }\n runNext(undefined);\n }\n\n // -------------------------------------------------------------------------\n // Path / location resolution\n // -------------------------------------------------------------------------\n\n let _hasNavigated = false;\n\n function _parsePath(\n path: string,\n queryObj?: Record<string, string | number | boolean | null | undefined>,\n ): { pathname: string; stringQuery: Record<string, string> } {\n const qIdx = path.indexOf(\"?\");\n const rawPath = qIdx === -1 ? path : path.slice(0, qIdx);\n const pathname = normalizeAppPath(rawPath || \"/\");\n const inlineQ = qIdx === -1 ? {} : parseQuery(path.slice(qIdx));\n const finalQuery = queryObj ? { ...inlineQ, ...queryObj } : inlineQ;\n const stringQuery: Record<string, string> = {};\n for (const [k, v] of Object.entries(finalQuery)) {\n if (v != null && v !== false) stringQuery[k] = String(v);\n }\n return { pathname, stringQuery };\n }\n\n function _resolveNamedPath(location: NamedRouteLocation): string {\n const found = _nameIndex.get(location.name);\n if (!found) {\n throw new Error(`[Elur Router] No route with name \"${location.name}\"`);\n }\n const parts = found.segments.map((seg) => {\n if (seg.kind === \"literal\") return seg.value;\n if (seg.kind === \"wildcard\") return \"\";\n const value = location.params?.[seg.name];\n if (value == null) {\n throw new Error(\n `[Elur Router] Missing param \"${seg.name}\" for route \"${location.name}\"`,\n );\n }\n return encodeURIComponent(String(value));\n });\n return \"/\" + parts.filter(Boolean).join(\"/\");\n }\n\n function _resolveLocation(\n location: RouteLocation,\n options?: NavigateOptions,\n ): { pathname: string; stringQuery: Record<string, string> } {\n if (typeof location === \"string\") {\n return _parsePath(location, options?.query);\n }\n const pathname = _resolveNamedPath(location);\n const mergedQuery = { ...(location.query ?? {}), ...(options?.query ?? {}) };\n return _parsePath(pathname, mergedQuery);\n }\n\n // -------------------------------------------------------------------------\n // Popstate / hashchange listener\n // -------------------------------------------------------------------------\n\n if (_currentPopstateCleanup) {\n _currentPopstateCleanup();\n _currentPopstateCleanup = null;\n }\n\n const handleBrowserNav = (\n p: string,\n newQuery: Record<string, string>,\n savedPos: ScrollPosition | null,\n nextPosition: number | null,\n onCancelRestore: (from: string, fromQuery: Record<string, string>) => void,\n ) => {\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(p, flat);\n\n let direction: NavigationDirection = \"none\";\n if (nextPosition != null) {\n if (nextPosition < _currentPosition) direction = \"back\";\n else if (nextPosition > _currentPosition) direction = \"forward\";\n }\n\n _runGuards(\n p,\n from,\n m?.route.beforeEnter,\n () => {\n if (nextPosition != null) _currentPosition = nextPosition;\n const animation = _pendingPopAnimation;\n _pendingPopAnimation = undefined;\n intent.value = { action: \"pop\", direction, animation };\n params.value = m?.params ?? {};\n query.value = newQuery;\n current.value = p;\n canGoBack.value = _currentPosition > 0;\n _applyScroll(p, from, savedPos);\n for (const hook of _afterHooks) {\n try { hook(p, from); } catch { /* ignore */ }\n }\n },\n () => onCancelRestore(from, fromQuery),\n );\n };\n\n if (_isHashMode) {\n const onHashChange = () => {\n if (_ignoreNextHashChange) {\n _ignoreNextHashChange = false;\n return;\n }\n const loc = readLocation();\n const nextQuery = parseQuery(loc.search);\n const savedPos = _hashScrollPositions.get(routeKey(loc.pathname, nextQuery)) ?? null;\n handleBrowserNav(\n loc.pathname,\n nextQuery,\n savedPos,\n null,\n (from, fromQuery) => {\n _ignoreNextHashChange = true;\n window.location.hash = buildUrl(from, fromQuery).slice(1);\n queueMicrotask(() => { _ignoreNextHashChange = false; });\n },\n );\n };\n window.addEventListener(\"hashchange\", onHashChange);\n _currentPopstateCleanup = () => window.removeEventListener(\"hashchange\", onHashChange);\n } else {\n const onPopstate = (ev: PopStateEvent) => {\n const loc = readLocation();\n const nextQuery = parseQuery(loc.search);\n const savedPos = readScrollPositionFromState(ev.state ?? history.state);\n const nextPos = readPositionFromState(ev.state ?? history.state);\n handleBrowserNav(\n loc.pathname,\n nextQuery,\n savedPos,\n nextPos,\n (from, fromQuery) => {\n history.pushState(\n buildHistoryState({}, getCurrentScrollPosition(), _currentPosition),\n \"\",\n buildUrl(from, fromQuery),\n );\n },\n );\n };\n window.addEventListener(\"popstate\", onPopstate);\n _currentPopstateCleanup = () => window.removeEventListener(\"popstate\", onPopstate);\n }\n\n // -------------------------------------------------------------------------\n // Internal commit (programmatic navigation)\n // -------------------------------------------------------------------------\n\n function _commit(\n pathname: string,\n stringQuery: Record<string, string>,\n from: string,\n fromQuery: Record<string, string>,\n m: ReturnType<typeof matchFlat>,\n nextIntent: NavigationIntent,\n useReplace: boolean,\n ): void {\n if (!useReplace) {\n _saveCurrentEntryScroll(from, fromQuery);\n _currentPosition += 1;\n }\n\n intent.value = nextIntent;\n params.value = m?.params ?? {};\n query.value = stringQuery;\n current.value = pathname;\n canGoBack.value = _currentPosition > 0;\n\n const url = buildUrl(pathname, stringQuery);\n\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(pathname, stringQuery), { left: 0, top: 0 });\n if (useReplace) {\n history.replaceState(history.state, \"\", url);\n } else {\n _ignoreNextHashChange = true;\n window.location.hash = url.slice(1);\n queueMicrotask(() => { _ignoreNextHashChange = false; });\n }\n } else {\n const nextState = buildHistoryState({}, { left: 0, top: 0 }, _currentPosition);\n if (useReplace) {\n history.replaceState(nextState, \"\", url);\n } else {\n history.pushState(nextState, \"\", url);\n }\n }\n\n _applyScroll(pathname, from, null);\n for (const hook of _afterHooks) {\n try { hook(pathname, from); } catch { /* ignore */ }\n }\n }\n\n // -------------------------------------------------------------------------\n // Public navigation API\n // -------------------------------------------------------------------------\n\n let _pendingPopAnimation: unknown = undefined;\n\n function navigate(location: RouteLocation, options?: NavigateOptions): void {\n _hasNavigated = true;\n const { pathname, stringQuery } = _resolveLocation(location, options);\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(pathname, flat);\n\n const nextIntent: NavigationIntent = {\n action: \"push\",\n direction: options?.direction ?? \"forward\",\n animation: options?.animation,\n };\n\n _runGuards(\n pathname,\n from,\n m?.route.beforeEnter,\n () => _commit(pathname, stringQuery, from, fromQuery, m, nextIntent, false),\n );\n }\n\n function replace(location: RouteLocation, options?: NavigateOptions): void {\n _hasNavigated = true;\n const { pathname, stringQuery } = _resolveLocation(location, options);\n const from = current.value;\n const fromQuery = { ...query.value };\n const m = matchFlat(pathname, flat);\n\n const nextIntent: NavigationIntent = {\n action: \"replace\",\n direction: options?.direction ?? \"root\",\n animation: options?.animation,\n };\n\n _runGuards(\n pathname,\n from,\n m?.route.beforeEnter,\n () => _commit(pathname, stringQuery, from, fromQuery, m, nextIntent, true),\n );\n }\n\n function back(animation?: unknown): void {\n if (animation !== undefined) _pendingPopAnimation = animation;\n history.back();\n }\n\n function forward(animation?: unknown): void {\n if (animation !== undefined) _pendingPopAnimation = animation;\n history.forward();\n }\n\n function go(delta: number): void { history.go(delta); }\n\n function isActive(path: string, exact = true): boolean {\n const cur = current.value;\n if (exact) return cur === path;\n return cur === path || cur.startsWith(path.endsWith(\"/\") ? path : path + \"/\");\n }\n\n function resolve(path: string): ResolvedRoute {\n const m = matchFlat(path, flat);\n if (!m) return { matched: false, params: {}, route: undefined };\n return { matched: true, params: m.params, route: m.route.record };\n }\n\n function beforeEach(guard: NavigationGuard): () => void {\n _guards.push(guard);\n return () => {\n const idx = _guards.indexOf(guard);\n if (idx !== -1) _guards.splice(idx, 1);\n };\n }\n\n function afterEach(hook: AfterEachHook): () => void {\n _afterHooks.push(hook);\n return () => {\n const idx = _afterHooks.indexOf(hook);\n if (idx !== -1) _afterHooks.splice(idx, 1);\n };\n }\n\n const router: RouterInternal = {\n current, params, query, intent, canGoBack,\n base: _base || \"/\",\n navigate, replace, back, forward, go,\n isActive, resolve,\n beforeEach, afterEach, routes,\n _flat: flat, _guards, _base, _mode,\n };\n\n if (_currentRouter) {\n console.warn(\n \"[elur] A router already exists. The previous router is being replaced. \" +\n \"Only one router instance should be active at a time.\",\n );\n }\n _currentRouter = router;\n\n queueMicrotask(() => {\n if (_hasNavigated) return;\n\n const m = matchFlat(initialPath, flat);\n _runGuards(\n initialPath,\n \"\",\n m?.route.beforeEnter,\n () => { /* allowed */ },\n () => {\n const fallback = \"/\";\n const url = buildUrl(fallback, {});\n if (_isHashMode) {\n _hashScrollPositions.set(routeKey(fallback, {}), { left: 0, top: 0 });\n history.replaceState(history.state, \"\", url);\n } else {\n history.replaceState(\n buildHistoryState({}, { left: 0, top: 0 }, _currentPosition),\n \"\",\n url,\n );\n }\n const fm = matchFlat(fallback, flat);\n intent.value = { action: \"replace\", direction: \"root\" };\n current.value = fallback;\n params.value = fm?.params ?? {};\n query.value = {};\n canGoBack.value = _currentPosition > 0;\n _applyScroll(fallback, initialPath, null);\n },\n );\n });\n\n return router;\n}\n\nexport function elurRouter(): Router {\n const injected = inject(RouterKey);\n if (injected) return injected;\n return getRouter();\n}\n\n/** @internal */\nexport function _resetRouter(): void {\n if (_currentPopstateCleanup) {\n _currentPopstateCleanup();\n _currentPopstateCleanup = null;\n }\n _currentRouter = null;\n _mountedRouters.length = 0;\n}\n\nexport class RouterView extends ElurComponent {\n private _depth: number;\n private _router?: RouterInternal;\n\n constructor(depth = 0, router?: Router) {\n super();\n this._depth = depth;\n this._router = router as RouterInternal | undefined;\n }\n\n render(): ElurTemplate {\n const depth = this._depth;\n const explicitRouter = this._router;\n return html`<div class=\"router-view\">${() => {\n const router = explicitRouter ?? elurRouter() as RouterInternal;\n const matched = matchFlat(router.current.value, router._flat);\n if (!matched) {\n return html`\n <div style=\"color:#f87171;padding:16px 0\">\n 404 — Route not found: <strong>${router.current.value}</strong>\n </div>\n `;\n }\n if (depth >= matched.route.chain.length) {\n return html`\n <span></span>\n `;\n }\n const factory = matched.route.chain[depth];\n // chain entries can be undefined when the route was registered without\n // a `component` (typical when an outlet auto-bootstraps the router).\n // In that case there's nothing to render at this depth.\n if (!factory) return html`\n <span></span>\n `;\n return factory();\n }}</div>`;\n }\n}\n\nexport class Link extends ElurComponent {\n private _to: string;\n private _label: string;\n private _router?: RouterInternal;\n\n constructor(to: string, label: string, router?: Router) {\n super();\n this._to = to;\n this._label = label;\n this._router = router as RouterInternal | undefined;\n }\n\n render(): ElurTemplate {\n const to = this._to;\n const label = this._label;\n const router = this._router ?? elurRouter() as RouterInternal;\n const appPath = to.startsWith(\"/\") ? to : \"/\" + to;\n const fullPath = (router._base ? (router._base + appPath) : appPath).replace(/\\/+/g, \"/\");\n const href = router._mode === \"hash\" ? \"#\" + fullPath : fullPath;\n return html`\n <a href=${href} style=${() => router.current.value === to\n ? \"color:#38bdf8;font-weight:700;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px;background:#0c2a3a\"\n : \"color:#a3a3a3;text-decoration:none;cursor:pointer;padding:4px 10px;border-radius:4px\"} @click=${(e: Event) => { e.preventDefault(); router.navigate(to); }}>${label}</a>\n `;\n }\n}\n\nexport interface _RouterDebugInternal {\n mode: RouterMode;\n base: string;\n currentPath: string;\n params: Record<string, string>;\n query: Record<string, string>;\n matchedPath: string | null;\n activeGuards: { globalCount: number; hasRouteGuard: boolean; names: string[] };\n}\n\nexport function _debugGetRouterInternal(): _RouterDebugInternal | null {\n // Prefer the most recently mounted injected router over the global singleton\n // so devtools inspect the router actually active in the current UI.\n const router = _mountedRouters.length\n ? _mountedRouters[_mountedRouters.length - 1]\n : _currentRouter;\n if (!router) return null;\n const internal = router as RouterInternal;\n const currentPath = internal.current.value;\n const matched = matchFlat(currentPath, internal._flat);\n const routeGuard = matched?.route.beforeEnter;\n const names = internal._guards.map((g, idx) => g.name || `beforeEach#${idx + 1}`);\n if (routeGuard) names.push(routeGuard.name || \"beforeEnter\");\n return {\n mode: internal._mode,\n base: internal._base || \"/\",\n currentPath,\n params: { ...internal.params.value },\n query: { ...internal.query.value },\n matchedPath: matched?.route.fullPath ?? null,\n activeGuards: {\n globalCount: internal._guards.length,\n hasRouteGuard: Boolean(routeGuard),\n names,\n },\n };\n}"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { Signal } from "./reactivity.js";
2
2
  import { ElurComponent } from "./lifecycle.js";
3
3
  import type { ElurTemplate } from "./template/index.js";
4
+ import { RouterKey, _debugRegisterRouter, _debugUnregisterRouter } from "./router-registry.js";
4
5
  /**
5
6
  * Value returned (or resolved) by a navigation guard.
6
7
  *
@@ -94,7 +95,7 @@ export interface Router {
94
95
  beforeEach(guard: NavigationGuard): () => void;
95
96
  afterEach(hook: AfterEachHook): () => void;
96
97
  }
97
- export declare const RouterKey: import("./context.js").InjectionKey<Router>;
98
+ export { RouterKey, _debugRegisterRouter, _debugUnregisterRouter };
98
99
  /**
99
100
  * @internal Whether a router is currently active. Used by outlets that
100
101
  * want to auto-bootstrap if the user didn't call `createRouter()` themselves.
@@ -130,8 +131,4 @@ export interface _RouterDebugInternal {
130
131
  names: string[];
131
132
  };
132
133
  }
133
- /** @internal Register a router that was injected via mount({ router }). */
134
- export declare function _debugRegisterRouter(router: Router): void;
135
- /** @internal Unregister a router when its mount point is unmounted. */
136
- export declare function _debugUnregisterRouter(router: Router): void;
137
134
  export declare function _debugGetRouterInternal(): _RouterDebugInternal | null;
@@ -1,6 +1,7 @@
1
1
  import type { Signal } from "./reactivity.js";
2
2
  import { ElurComponent } from "./lifecycle.js";
3
3
  import type { ElurTemplate } from "./template/index.js";
4
+ import { RouterKey, _debugRegisterRouter, _debugUnregisterRouter } from "./router-registry.js";
4
5
  /**
5
6
  * Value returned (or resolved) by a navigation guard.
6
7
  *
@@ -94,7 +95,7 @@ export interface Router {
94
95
  beforeEach(guard: NavigationGuard): () => void;
95
96
  afterEach(hook: AfterEachHook): () => void;
96
97
  }
97
- export declare const RouterKey: import("./context.js").InjectionKey<Router>;
98
+ export { RouterKey, _debugRegisterRouter, _debugUnregisterRouter };
98
99
  /**
99
100
  * @internal Whether a router is currently active. Used by outlets that
100
101
  * want to auto-bootstrap if the user didn't call `createRouter()` themselves.
@@ -130,8 +131,4 @@ export interface _RouterDebugInternal {
130
131
  names: string[];
131
132
  };
132
133
  }
133
- /** @internal Register a router that was injected via mount({ router }). */
134
- export declare function _debugRegisterRouter(router: Router): void;
135
- /** @internal Unregister a router when its mount point is unmounted. */
136
- export declare function _debugUnregisterRouter(router: Router): void;
137
134
  export declare function _debugGetRouterInternal(): _RouterDebugInternal | null;