@real-router/vue 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -4
- package/dist/cjs/index.d.ts +173 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.mts +173 -1
- package/dist/esm/index.d.mts.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/RouterProvider.ts +26 -1
- package/src/components/RouteView/RouteView.ts +12 -3
- package/src/components/RouteView/components.ts +19 -1
- package/src/components/RouteView/helpers.ts +93 -30
- package/src/components/RouteView/index.ts +1 -0
- package/src/components/RouteView/types.ts +5 -0
- package/src/composables/useRouteEnter.ts +124 -0
- package/src/composables/useRouteExit.ts +116 -0
- package/src/index.ts +16 -0
package/dist/esm/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Fragment as e,KeepAlive as t,Suspense as n,computed as r,defineComponent as i,h as a,inject as o,isVNode as s,markRaw as c,onScopeDispose as l,provide as u,shallowRef as d,watch as f}from"vue";import{UNKNOWN_ROUTE as p,getNavigator as m}from"@real-router/core";import{getRouteUtils as ee,startsWithSegment as h}from"@real-router/route-utils";import{createActiveRouteSource as te,createDismissableError as ne,createRouteNodeSource as re,createRouteSource as g,getTransitionSource as ie}from"@real-router/sources";import{getPluginApi as ae}from"@real-router/core/api";function _(){return null}const v=i({name:`RouteView.Match`,props:{segment:{type:String,required:!0},exact:{type:Boolean,default:!1},fallback:{type:[Object,Function],default:void 0},keepAlive:{type:Boolean,default:!1}},render:_}),y=i({name:`RouteView.NotFound`,render:_});function oe(e,t,n){return n?e===t:h(e,t)}function b(e){if(Array.isArray(e)){let t=[];for(let n of e)Array.isArray(n)?t.push(...b(n)):s(n)&&t.push(n);return t}return s(e)?[e]:[]}function x(t,n){let r=b(t);for(let t of r)t.type===v||t.type===y?n.push(t):t.type===e&&x(t.children,n)}function se(e,t,n){let r=null,i=!1,a,o=[];for(let s of e){if(s.type===y){r=s.children;continue}let e=s.props,c=e?.segment??``,l=e?.exact??!1,u=n?`${n}.${c}`:c;!i&&oe(t,u,l)&&(i=!0,a=e?.fallback,o.push(s))}if(!i&&t===p&&r!==null){let t=e.filter(e=>e.type===y).at(-1);t&&o.push(t)}return{rendered:o,activeMatchFound:i,fallback:a}}function S(e){let t=d(e.getSnapshot());return l(e.subscribe(()=>{t.value=e.getSnapshot()})),t}const C=Symbol(`RouterKey`),w=Symbol(`NavigatorKey`),T=Symbol(`RouteKey`),E=()=>{let e=o(C);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e};function D(e){let t=E(),n=S(re(t,e));return{navigator:m(t),route:r(()=>n.value.route),previousRoute:r(()=>n.value.previousRoute)}}function O(e){return e.children?.default?.()??null}function k(e,t){let n=e.get(t);if(n)return n;let r=c(i({name:`KeepAlive-${t}`,setup(e,t){return()=>t.slots.default?.()}}));return e.set(t,r),r}function A(e,t){if(t===void 0)return e;let r=typeof t==`function`?t():t;return a(n,{},{default:()=>e,fallback:()=>r})}const ce=c(i({name:`KeepAlive-placeholder`,render(){return null}}));function le(e,n,r){let i=e.props?.segment??`__not-found__`,o=k(n,i),s=O(e)??[];return A(a(t,null,{default:()=>a(o,{key:i},{default:()=>s})}),r)}function j(e){return e===!0||e===``||e===`keep-alive`}function ue(n,r,i){let o=n.props;if(j(o?.keepAlive)&&n.type===v){let i=o?.segment??`__not-found__`,s=k(r,i),c=O(n)??[];return a(e,[a(t,null,{default:()=>a(s,{key:i},{default:()=>c})})])}let s=O(n);return s?a(e,[a(t,null,{default:()=>a(ce)}),A(a(e,s),i)]):null}const de=i({name:`RouteView`,props:{nodeName:{type:String,required:!0},keepAlive:{type:Boolean,default:!1}},setup(t,{slots:n}){let r=D(t.nodeName),i=new Map,o=null,s=!1;function c(e,t){return t===o?s:(o=t,s=e.some(e=>e.type===v&&j(e.props?.keepAlive)),s)}return()=>{let o=r.route.value;if(!o)return null;let s=n.default?.(),l=[];x(s,l);let{rendered:u,fallback:d}=se(l,o.name,t.nodeName);if(u.length===0)return null;let f=u[0];if(t.keepAlive)return le(f,i,d);if(f.type!==v&&f.type!==y)return null;if(c(l,s))return ue(f,i,d);let p=O(f);return p?A(a(e,p),d):null}}}),fe=Object.assign(de,{Match:v,NotFound:y}),pe=Object.freeze({}),me=Object.freeze({}),M=`data-real-router-announcer`;function N(e,t){let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c=null,l,u=P(),d=(e,t)=>{s=e,clearTimeout(l),u.textContent=e,l=setTimeout(()=>{u.textContent=``,s=``},7e3),L(t)},f=setTimeout(()=>{if(a=!0,c!==null&&!o){let e=c;c=null,d(e,document.querySelector(`h1`))}},100),p=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=document.querySelector(`h1`),i=I(e,n,r,t);if(!(!i||i===s)){if(!a){c=i;return}d(i,t)}})})});return{destroy(){o=!0,p(),clearTimeout(l),clearTimeout(f),F()}}}function P(){let e=document.querySelector(`[${M}]`);if(e)return e;let t=document.createElement(`div`);return t.setAttribute(`style`,`position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0`),t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(M,``),document.body.prepend(t),t}function F(){document.querySelector(`[${M}]`)?.remove()}function I(e,t,n,r){if(n)return n(e);let i=r?.textContent.trim()??``,a=e.name.startsWith(`@@`)?``:e.name;return`${t}${i||document.title||a||globalThis.location.pathname}`}function L(e){e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}const R=`real-router:scroll`,z=Object.freeze({destroy:()=>{}});function he(e,t){if(globalThis.window===void 0)return z;let n=t?.mode??`restore`;if(n===`manual`)return z;let r=t?.anchorScrolling??!0,i=t?.scrollContainer,a=history.scrollRestoration;try{history.scrollRestoration=`manual`}catch{}let o=()=>{let e=i?.();return e?e.scrollTop:globalThis.scrollY},s=e=>{let t=i?.();t?t.scrollTop=e:globalThis.scrollTo(0,e)},c=()=>{let e=globalThis.location.hash;if(r&&e.length>1){let t;try{t=decodeURIComponent(e.slice(1))}catch{t=e.slice(1)}let n=document.getElementById(t);if(n){n.scrollIntoView();return}}s(0)},l=!1,u=e.subscribe(({route:e,previousRoute:t})=>{let r=e.context.navigation;t&&H(B(t),o()),requestAnimationFrame(()=>{if(!l){if(n===`top`||!r){c();return}if(r.navigationType!==`replace`){if(r.direction===`back`||r.navigationType===`traverse`||r.navigationType===`reload`){s(V()[B(e)]??0);return}c()}}})}),d=()=>{let t=e.getState();t&&H(B(t),o())};return globalThis.addEventListener(`pagehide`,d),{destroy:()=>{if(!l){l=!0,u(),globalThis.removeEventListener(`pagehide`,d);try{history.scrollRestoration=a}catch{}}}}}function B(e){return`${e.name}:${ge(e.params)}`}function V(){try{let e=sessionStorage.getItem(R);return e?JSON.parse(e):{}}catch{return{}}}function H(e,t){try{let n=V();n[e]=t,sessionStorage.setItem(R,JSON.stringify(n))}catch{}}function ge(e){return JSON.stringify(e,_e)}function _e(e,t){if(typeof t==`object`&&t&&!Array.isArray(t)){let e={},n=Object.keys(t).sort((e,t)=>e.localeCompare(t));for(let r of n)e[r]=t[r];return e}return t}function U(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function ve(e,t,n){try{let r=e.buildUrl;if(r){let e=r(t,n);if(e!==void 0)return e}return e.buildPath(t,n)}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function W(e){return e?e.match(/\S+/g)??[]:[]}function ye(e,t,n){if(e&&t){let e=W(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=W(n),i=new Set(r);for(let t of e)i.has(t)||(i.add(t),r.push(t));return r.join(` `)}return n??void 0}function be(e){e&&(e instanceof HTMLAnchorElement||e instanceof HTMLButtonElement||(e.hasAttribute(`role`)||e.setAttribute(`role`,`link`),e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`0`)))}function G(e,t){if(typeof e==`function`){e(t);return}if(Array.isArray(e)){let n=e;for(let e of n)if(typeof e==`function`&&(e(t),t.defaultPrevented))return}}const xe=i({name:`Link`,inheritAttrs:!1,props:{routeName:{type:String,required:!0},routeParams:{type:Object,default:()=>pe},routeOptions:{type:Object,default:()=>me},class:{type:String,default:void 0},activeClassName:{type:String,default:`active`},activeStrict:{type:Boolean,default:!1},ignoreQueryParams:{type:Boolean,default:!0},target:{type:String,default:void 0}},setup(e,{slots:t,attrs:n}){let i=E(),o=d(!1);f(()=>[e.routeName,e.routeParams,e.activeStrict,e.ignoreQueryParams],([e,t,n,r],a,s)=>{let c=te(i,e,t,{strict:n,ignoreQueryParams:r});o.value=c.getSnapshot(),s(c.subscribe(()=>{o.value=c.getSnapshot()}))},{immediate:!0,flush:`sync`});let s=r(()=>ve(i,e.routeName,e.routeParams)),c=r(()=>ye(o.value,e.activeClassName,e.class)),l=t=>{n.onClick!==void 0&&n.onClick!==null&&(G(n.onClick,t),t.defaultPrevented)||!U(t)||e.target===`_blank`||(t.preventDefault(),i.navigate(e.routeName,e.routeParams,e.routeOptions).catch(()=>{}))};return()=>{let r={};for(let e of Object.keys(n))e!==`onClick`&&(r[e]=n[e]);return a(`a`,{...r,href:s.value,class:c.value,target:e.target,onClick:l},t.default?.())}}}),Se=i({name:`RouterErrorBoundary`,props:{fallback:{type:Function,required:!0},onError:{type:Function,default:void 0}},setup(t,{slots:n}){let r=S(ne(E()));return f(()=>r.value.version,()=>{r.value.error&&t.onError?.(r.value.error,r.value.toRoute,r.value.fromRoute)},{immediate:!0}),()=>{let i=n.default?.()??[],o=r.value.error?t.fallback(r.value.error,r.value.resetError):null;return a(e,null,[...i,o])}}}),K=[];function q(e){return K.push(e),()=>{let t=K.lastIndexOf(e);t!==-1&&K.splice(t,1)}}function J(){let e=K.at(-1);if(!e)throw Error(`v-link directive requires a RouterProvider ancestor. Make sure RouterProvider is mounted.`);return e}const Y=new WeakMap;function X(e){return e==null?(console.error(`[real-router] v-link directive received null/undefined value. The element will not be wired for navigation.`),!1):typeof e.name==`string`?!0:(console.error("[real-router] v-link directive value is missing a string `name` field. The element will not be wired for navigation."),!1)}function Ce(e,t){return n=>{U(n)&&(n.preventDefault(),e.navigate(t.name,t.params??{},t.options??{}).catch(()=>{}))}}function we(e,t,n){return r=>{r.key===`Enter`&&!(n instanceof HTMLButtonElement)&&e.navigate(t.name,t.params??{},t.options??{}).catch(()=>{})}}function Z(e,t,n){let r=Ce(t,n),i=we(t,n,e);e.addEventListener(`click`,r),e.addEventListener(`keydown`,i),Y.set(e,{click:r,keydown:i})}function Q(e){let t=Y.get(e);t&&(e.removeEventListener(`click`,t.click),e.removeEventListener(`keydown`,t.keydown),Y.delete(e))}const Te={mounted(e,t){let n=J();be(e),e.style.cursor=`pointer`,X(t.value)&&Z(e,n,t.value)},updated(e,t){let n=J();Q(e),X(t.value)&&Z(e,n,t.value)},beforeUnmount(e){Q(e)}},Ee=()=>{let e=o(w);if(!e)throw Error(`useNavigator must be used within a RouterProvider`);return e},De=()=>ee(ae(E()).getTree()),Oe=()=>{let e=o(T);if(!e)throw Error(`useRoute must be used within a RouterProvider`);return e};function ke(){return S(ie(E()))}function $(e){let t=m(e),n=g(e),r=n.getSnapshot(),i=d(r.route),a=d(r.previousRoute);return{navigator:t,route:i,previousRoute:a,unsubscribe:n.subscribe(()=>{let e=n.getSnapshot();i.value=e.route,a.value=e.previousRoute})}}function Ae(e){return{install(t){let n=q(e),{navigator:r,route:i,previousRoute:a,unsubscribe:o}=$(e);`onUnmount`in t&&t.onUnmount(()=>{n(),o()}),t.provide(C,e),t.provide(w,r),t.provide(T,{navigator:r,route:i,previousRoute:a})}}}const je=i({name:`RouterProvider`,props:{router:{type:Object,required:!0},announceNavigation:{type:Boolean,default:!1},scrollRestoration:{type:Object}},setup(e,{slots:t}){f(()=>[e.router,e.announceNavigation],([e,t],n,r)=>{if(!t)return;let i=N(e);r(()=>{i.destroy()})},{immediate:!0}),f(()=>[e.router,e.scrollRestoration!==void 0,e.scrollRestoration?.mode,e.scrollRestoration?.anchorScrolling],([t,n,r,i],a,o)=>{if(!n)return;let s=he(t,{mode:r,anchorScrolling:i,scrollContainer:e.scrollRestoration?.scrollContainer});o(()=>{s.destroy()})},{immediate:!0});let n=q(e.router),{navigator:r,route:i,previousRoute:a,unsubscribe:o}=$(e.router);return l(()=>{n(),o()}),u(C,e.router),u(w,r),u(T,{navigator:r,route:i,previousRoute:a}),()=>t.default?.()}});export{xe as Link,w as NavigatorKey,T as RouteKey,fe as RouteView,Se as RouterErrorBoundary,C as RouterKey,je as RouterProvider,Ae as createRouterPlugin,Ee as useNavigator,Oe as useRoute,D as useRouteNode,De as useRouteUtils,E as useRouter,ke as useRouterTransition,Te as vLink};
|
|
1
|
+
import{Fragment as e,KeepAlive as t,Suspense as n,computed as r,defineComponent as i,h as a,inject as o,isVNode as s,markRaw as c,onScopeDispose as l,provide as u,shallowRef as d,watch as f}from"vue";import{UNKNOWN_ROUTE as p,getNavigator as m}from"@real-router/core";import{getRouteUtils as ee,startsWithSegment as te}from"@real-router/route-utils";import{createActiveRouteSource as ne,createDismissableError as re,createRouteNodeSource as ie,createRouteSource as ae,getTransitionSource as oe}from"@real-router/sources";import{getPluginApi as se}from"@real-router/core/api";function h(){return null}const g=i({name:`RouteView.Match`,props:{segment:{type:String,required:!0},exact:{type:Boolean,default:!1},fallback:{type:[Object,Function],default:void 0},keepAlive:{type:Boolean,default:!1}},render:h}),_=i({name:`RouteView.Self`,props:{fallback:{type:[Object,Function],default:void 0}},render:h}),v=i({name:`RouteView.NotFound`,render:h});function ce(e,t,n){return n?e===t:te(e,t)}function y(e){if(Array.isArray(e)){let t=[];for(let n of e)Array.isArray(n)?t.push(...y(n)):s(n)&&t.push(n);return t}return s(e)?[e]:[]}function b(t,n){let r=y(t);for(let t of r)t.type===g||t.type===_||t.type===v?n.push(t):t.type===e&&b(t.children,n)}function le(e,t){return e.type===v?(t.notFoundChildren=e.children,!0):e.type===_?(t.selfVNode===null&&(t.selfVNode=e,t.selfFallback=e.props?.fallback),!0):!1}function x(e,t,n){let r=e.props,i=r?.segment??``,a=r?.exact??!1;return{isActive:ce(t,n?`${n}.${i}`:i,a),fallback:r?.fallback}}function ue(e,t,n,r,i){if(r.selfVNode!==null&&t===n)return e.push(r.selfVNode),r.selfFallback;if(t===p&&r.notFoundChildren!==null){let t=i.filter(e=>e.type===v).at(-1);t&&e.push(t)}}function de(e,t,n){let r={selfVNode:null,selfFallback:void 0,notFoundChildren:null},i=!1,a,o=[];for(let s of e){if(le(s,r)||i)continue;let e=x(s,t,n);e.isActive&&(i=!0,a=e.fallback,o.push(s))}return i||(a=ue(o,t,n,r,e)),{rendered:o,activeMatchFound:i,fallback:a}}function S(e){let t=d(e.getSnapshot());return l(e.subscribe(()=>{t.value=e.getSnapshot()})),t}const C=Symbol(`RouterKey`),w=Symbol(`NavigatorKey`),T=Symbol(`RouteKey`),E=()=>{let e=o(C);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e};function D(e){let t=E(),n=S(ie(t,e));return{navigator:m(t),route:r(()=>n.value.route),previousRoute:r(()=>n.value.previousRoute)}}function O(e){return e.children?.default?.()??null}function k(e,t){let n=e.get(t);if(n)return n;let r=c(i({name:`KeepAlive-${t}`,setup(e,t){return()=>t.slots.default?.()}}));return e.set(t,r),r}function A(e,t){if(t===void 0)return e;let r=typeof t==`function`?t():t;return a(n,{},{default:()=>e,fallback:()=>r})}const fe=c(i({name:`KeepAlive-placeholder`,render(){return null}}));function pe(e,n,r){let i=e.props?.segment??`__not-found__`,o=k(n,i),s=O(e)??[];return A(a(t,null,{default:()=>a(o,{key:i},{default:()=>s})}),r)}function j(e){return e===!0||e===``||e===`keep-alive`}function me(n,r,i){let o=n.props;if(j(o?.keepAlive)&&n.type===g){let i=o?.segment??`__not-found__`,s=k(r,i),c=O(n)??[];return a(e,[a(t,null,{default:()=>a(s,{key:i},{default:()=>c})})])}let s=O(n);return s?a(e,[a(t,null,{default:()=>a(fe)}),A(a(e,s),i)]):null}const he=i({name:`RouteView`,props:{nodeName:{type:String,required:!0},keepAlive:{type:Boolean,default:!1}},setup(t,{slots:n}){let r=D(t.nodeName),i=new Map,o=null,s=!1;function c(e,t){return t===o?s:(o=t,s=e.some(e=>e.type===g&&j(e.props?.keepAlive)),s)}return()=>{let o=r.route.value;if(!o)return null;let s=n.default?.(),l=[];b(s,l);let{rendered:u,fallback:d}=de(l,o.name,t.nodeName);if(u.length===0)return null;let f=u[0];if(t.keepAlive)return pe(f,i,d);if(f.type!==g&&f.type!==_&&f.type!==v)return null;if(c(l,s))return me(f,i,d);let p=O(f);return p?A(a(e,p),d):null}}}),ge=Object.assign(he,{Match:g,Self:_,NotFound:v}),_e=Object.freeze({}),M=Object.freeze({}),N=`data-real-router-announcer`;function P(e,t){let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c=null,l,u=F(),d=(e,t)=>{s=e,clearTimeout(l),u.textContent=e,l=setTimeout(()=>{u.textContent=``,s=``},7e3),ye(t)},f=setTimeout(()=>{if(a=!0,c!==null&&!o){let e=c;c=null,d(e,document.querySelector(`h1`))}},100),p=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=document.querySelector(`h1`),i=ve(e,n,r,t);if(!(!i||i===s)){if(!a){c=i;return}d(i,t)}})})});return{destroy(){o=!0,p(),clearTimeout(l),clearTimeout(f),I()}}}function F(){let e=document.querySelector(`[${N}]`);if(e)return e;let t=document.createElement(`div`);return t.setAttribute(`style`,`position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0`),t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(N,``),document.body.prepend(t),t}function I(){document.querySelector(`[${N}]`)?.remove()}function ve(e,t,n,r){if(n)return n(e);let i=(r?.textContent??``).trim(),a=e.name.startsWith(`@@`)?``:e.name;return`${t}${i||document.title||a||globalThis.location.pathname}`}function ye(e){e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}const L=`real-router:scroll`,R=Object.freeze({destroy:()=>{}});function be(e,t){if(globalThis.window===void 0)return R;let n=t?.mode??`restore`;if(n===`manual`)return R;let r=t?.anchorScrolling??!0,i=t?.scrollContainer,a=history.scrollRestoration;try{history.scrollRestoration=`manual`}catch{}let o=()=>{let e=i?.();return e?e.scrollTop:globalThis.scrollY},s=e=>{let t=i?.();t?t.scrollTop=e:globalThis.scrollTo(0,e)},c=()=>{let e=globalThis.location.hash;if(r&&e.length>1){let t;try{t=decodeURIComponent(e.slice(1))}catch{t=e.slice(1)}let n=document.getElementById(t);if(n){n.scrollIntoView();return}}s(0)},l=!1,u=e.subscribe(({route:e,previousRoute:t})=>{let r=e.context.navigation;t&&V(z(t),o()),requestAnimationFrame(()=>{if(!l){if(n===`top`||!r){c();return}if(r.navigationType!==`replace`){if(r.direction===`back`||r.navigationType===`traverse`||r.navigationType===`reload`){s(B()[z(e)]??0);return}c()}}})}),d=()=>{let t=e.getState();t&&V(z(t),o())};return globalThis.addEventListener(`pagehide`,d),{destroy:()=>{if(!l){l=!0,u(),globalThis.removeEventListener(`pagehide`,d);try{history.scrollRestoration=a}catch{}}}}}function z(e){return`${e.name}:${xe(e.params)}`}function B(){try{let e=sessionStorage.getItem(L);return e?JSON.parse(e):{}}catch{return{}}}function V(e,t){try{let n=B();n[e]=t,sessionStorage.setItem(L,JSON.stringify(n))}catch{}}function xe(e){return JSON.stringify(e,Se)}function Se(e,t){if(typeof t==`object`&&t&&!Array.isArray(t)){let e={},n=Object.keys(t).sort((e,t)=>e.localeCompare(t));for(let r of n)e[r]=t[r];return e}return t}const Ce=Object.freeze({destroy:()=>{}});function we(e){if(typeof document>`u`||typeof document.startViewTransition!=`function`)return Ce;let t=null,n=null,r=!1,i=()=>{t?.(),t=null},a=e.subscribeLeave(({signal:e})=>{if(!e.aborted)return r=!1,i(),new Promise(a=>{let o=new Promise(e=>{t=e});e.addEventListener(`abort`,()=>{r||(i(),n?.skipTransition?.(),a())},{once:!0});try{n=document.startViewTransition(()=>(a(),o))}catch{i(),a()}})}),o=e.subscribe(()=>{let e=t;r=!0,t=null,e===null?n=null:setTimeout(()=>{e(),n=null},0)});return{destroy:()=>{a(),o(),n?.skipTransition?.(),n=null,i()}}}function H(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function Te(e,t,n){try{let r=e.buildUrl;if(r){let e=r(t,n);if(e!==void 0)return e}return e.buildPath(t,n)}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function U(e){return e?e.match(/\S+/g)??[]:[]}function Ee(e,t,n){if(e&&t){let e=U(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=U(n),i=new Set(r);for(let t of e)i.has(t)||(i.add(t),r.push(t));return r.join(` `)}return n??void 0}function De(e){e&&(e instanceof HTMLAnchorElement||e instanceof HTMLButtonElement||(e.hasAttribute(`role`)||e.setAttribute(`role`,`link`),e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`0`)))}function Oe(e,t){if(typeof e==`function`){e(t);return}if(Array.isArray(e)){let n=e;for(let e of n)if(typeof e==`function`&&(e(t),t.defaultPrevented))return}}const ke=i({name:`Link`,inheritAttrs:!1,props:{routeName:{type:String,required:!0},routeParams:{type:Object,default:()=>_e},routeOptions:{type:Object,default:()=>M},class:{type:String,default:void 0},activeClassName:{type:String,default:`active`},activeStrict:{type:Boolean,default:!1},ignoreQueryParams:{type:Boolean,default:!0},target:{type:String,default:void 0}},setup(e,{slots:t,attrs:n}){let i=E(),o=d(!1);f(()=>[e.routeName,e.routeParams,e.activeStrict,e.ignoreQueryParams],([e,t,n,r],a,s)=>{let c=ne(i,e,t,{strict:n,ignoreQueryParams:r});o.value=c.getSnapshot(),s(c.subscribe(()=>{o.value=c.getSnapshot()}))},{immediate:!0,flush:`sync`});let s=r(()=>Te(i,e.routeName,e.routeParams)),c=r(()=>Ee(o.value,e.activeClassName,e.class)),l=t=>{n.onClick!==void 0&&n.onClick!==null&&(Oe(n.onClick,t),t.defaultPrevented)||!H(t)||e.target===`_blank`||(t.preventDefault(),i.navigate(e.routeName,e.routeParams,e.routeOptions).catch(()=>{}))};return()=>{let r={};for(let e of Object.keys(n))e!==`onClick`&&(r[e]=n[e]);return a(`a`,{...r,href:s.value,class:c.value,target:e.target,onClick:l},t.default?.())}}}),Ae=i({name:`RouterErrorBoundary`,props:{fallback:{type:Function,required:!0},onError:{type:Function,default:void 0}},setup(t,{slots:n}){let r=S(re(E()));return f(()=>r.value.version,()=>{r.value.error&&t.onError?.(r.value.error,r.value.toRoute,r.value.fromRoute)},{immediate:!0}),()=>{let i=n.default?.()??[],o=r.value.error?t.fallback(r.value.error,r.value.resetError):null;return a(e,null,[...i,o])}}}),W=[];function G(e){return W.push(e),()=>{let t=W.lastIndexOf(e);t!==-1&&W.splice(t,1)}}function K(){let e=W.at(-1);if(!e)throw Error(`v-link directive requires a RouterProvider ancestor. Make sure RouterProvider is mounted.`);return e}const q=new WeakMap;function J(e){return e==null?(console.error(`[real-router] v-link directive received null/undefined value. The element will not be wired for navigation.`),!1):typeof e.name==`string`?!0:(console.error("[real-router] v-link directive value is missing a string `name` field. The element will not be wired for navigation."),!1)}function je(e,t){return n=>{H(n)&&(n.preventDefault(),e.navigate(t.name,t.params??{},t.options??{}).catch(()=>{}))}}function Me(e,t,n){return r=>{r.key===`Enter`&&!(n instanceof HTMLButtonElement)&&e.navigate(t.name,t.params??{},t.options??{}).catch(()=>{})}}function Y(e,t,n){let r=je(t,n),i=Me(t,n,e);e.addEventListener(`click`,r),e.addEventListener(`keydown`,i),q.set(e,{click:r,keydown:i})}function X(e){let t=q.get(e);t&&(e.removeEventListener(`click`,t.click),e.removeEventListener(`keydown`,t.keydown),q.delete(e))}const Z={mounted(e,t){let n=K();De(e),e.style.cursor=`pointer`,J(t.value)&&Y(e,n,t.value)},updated(e,t){let n=K();X(e),J(t.value)&&Y(e,n,t.value)},beforeUnmount(e){X(e)}},Ne=()=>{let e=o(w);if(!e)throw Error(`useNavigator must be used within a RouterProvider`);return e},Pe=()=>ee(se(E()).getTree()),Q=()=>{let e=o(T);if(!e)throw Error(`useRoute must be used within a RouterProvider`);return e};function Fe(){return S(oe(E()))}function Ie(e,t){let n=E(),r=t?.skipSameRoute??!0;l(n.subscribeLeave(({route:t,nextRoute:n,signal:i})=>{if(!(r&&t.name===n.name)&&!i.aborted)return e({route:t,nextRoute:n,signal:i})}))}function Le(e,t){let{route:n,previousRoute:r}=Q(),i=t?.skipSameRoute??!0,a=null;f(n,t=>{let n=r.value;t&&t.transition.from&&(i&&t.transition.from===t.name||a===t||!n||(a=t,e({route:t,previousRoute:n})))})}function $(e){let t=m(e),n=ae(e),r=n.getSnapshot(),i=d(r.route),a=d(r.previousRoute);return{navigator:t,route:i,previousRoute:a,unsubscribe:n.subscribe(()=>{let e=n.getSnapshot();i.value=e.route,a.value=e.previousRoute})}}function Re(e){return{install(t){let n=G(e),{navigator:r,route:i,previousRoute:a,unsubscribe:o}=$(e);`onUnmount`in t&&t.onUnmount(()=>{n(),o()}),t.provide(C,e),t.provide(w,r),t.provide(T,{navigator:r,route:i,previousRoute:a})}}}const ze=i({name:`RouterProvider`,props:{router:{type:Object,required:!0},announceNavigation:{type:Boolean,default:!1},scrollRestoration:{type:Object},viewTransitions:{type:Boolean,default:!1}},setup(e,{slots:t}){f(()=>[e.router,e.announceNavigation],([e,t],n,r)=>{if(!t)return;let i=P(e);r(()=>{i.destroy()})},{immediate:!0}),f(()=>[e.router,e.scrollRestoration!==void 0,e.scrollRestoration?.mode,e.scrollRestoration?.anchorScrolling],([t,n,r,i],a,o)=>{if(!n)return;let s=be(t,{mode:r,anchorScrolling:i,scrollContainer:e.scrollRestoration?.scrollContainer});o(()=>{s.destroy()})},{immediate:!0}),f(()=>[e.router,e.viewTransitions],([e,t],n,r)=>{if(!t)return;let i=we(e);r(()=>{i.destroy()})},{immediate:!0});let n=G(e.router),{navigator:r,route:i,previousRoute:a,unsubscribe:o}=$(e.router);return l(()=>{n(),o()}),u(C,e.router),u(w,r),u(T,{navigator:r,route:i,previousRoute:a}),()=>t.default?.()}});export{ke as Link,w as NavigatorKey,T as RouteKey,ge as RouteView,Ae as RouterErrorBoundary,C as RouterKey,ze as RouterProvider,Re as createRouterPlugin,Ne as useNavigator,Q as useRoute,Le as useRouteEnter,Ie as useRouteExit,D as useRouteNode,Pe as useRouteUtils,E as useRouter,Fe as useRouterTransition,Z as vLink};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/esm/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/components/RouteView/components.ts","../../src/components/RouteView/helpers.ts","../../src/useRefFromSource.ts","../../src/context.ts","../../src/composables/useRouter.ts","../../src/composables/useRouteNode.ts","../../src/components/RouteView/RouteView.ts","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/scroll-restore.ts","../../../../shared/dom-utils/link-utils.ts","../../src/components/Link.ts","../../src/components/RouterErrorBoundary.ts","../../src/directives/vLink.ts","../../src/composables/useNavigator.ts","../../src/composables/useRouteUtils.ts","../../src/composables/useRoute.ts","../../src/composables/useRouterTransition.ts","../../src/setupRouteProvision.ts","../../src/createRouterPlugin.ts","../../src/RouterProvider.ts"],"sourcesContent":["import { defineComponent } from \"vue\";\n\nimport type { PropType, VNode } from \"vue\";\n\nfunction renderNull() {\n return null;\n}\n\nexport const Match = defineComponent({\n name: \"RouteView.Match\",\n props: {\n segment: {\n type: String as PropType<string>,\n required: true,\n },\n exact: {\n type: Boolean,\n default: false,\n },\n fallback: {\n type: [Object, Function] as PropType<VNode | (() => VNode)>,\n default: undefined,\n },\n keepAlive: {\n type: Boolean,\n default: false,\n },\n },\n render: renderNull,\n});\n\nexport const NotFound = defineComponent({\n name: \"RouteView.NotFound\",\n render: renderNull,\n});\n","import { UNKNOWN_ROUTE } from \"@real-router/core\";\nimport { startsWithSegment } from \"@real-router/route-utils\";\nimport { Fragment, isVNode } from \"vue\";\n\nimport { Match, NotFound } from \"./components\";\n\nimport type { VNode } from \"vue\";\n\ntype FallbackType = VNode | (() => VNode) | undefined;\n\nfunction isSegmentMatch(\n routeName: string,\n fullSegmentName: string,\n exact: boolean,\n): boolean {\n if (exact) {\n return routeName === fullSegmentName;\n }\n\n return startsWithSegment(routeName, fullSegmentName);\n}\n\nfunction normalizeChildren(children: unknown): VNode[] {\n if (Array.isArray(children)) {\n const result: VNode[] = [];\n\n for (const child of children) {\n if (Array.isArray(child)) {\n result.push(...normalizeChildren(child));\n } else if (isVNode(child)) {\n result.push(child);\n }\n }\n\n return result;\n }\n\n if (isVNode(children)) {\n return [children];\n }\n\n return [];\n}\n\nexport function collectElements(children: unknown, result: VNode[]): void {\n const vnodes = normalizeChildren(children);\n\n for (const child of vnodes) {\n if (child.type === Match || child.type === NotFound) {\n result.push(child);\n } else if (child.type === Fragment) {\n collectElements(child.children, result);\n }\n }\n}\n\nexport function buildRenderList(\n elements: VNode[],\n routeName: string,\n nodeName: string,\n): {\n rendered: VNode[];\n activeMatchFound: boolean;\n fallback?: FallbackType;\n} {\n let notFoundChildren: unknown = null;\n let activeMatchFound = false;\n let fallback: FallbackType = undefined;\n const rendered: VNode[] = [];\n\n for (const child of elements) {\n if (child.type === NotFound) {\n notFoundChildren = child.children;\n continue;\n }\n\n const props = child.props as {\n segment: string;\n exact?: boolean;\n fallback?: FallbackType;\n } | null;\n const segment = props?.segment ?? \"\";\n const exact = props?.exact ?? false;\n const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;\n const isActive =\n !activeMatchFound && isSegmentMatch(routeName, fullSegmentName, exact);\n\n if (isActive) {\n activeMatchFound = true;\n fallback = props?.fallback;\n rendered.push(child);\n }\n }\n\n if (\n !activeMatchFound &&\n routeName === UNKNOWN_ROUTE &&\n notFoundChildren !== null\n ) {\n const nfElements = elements.filter((element) => element.type === NotFound);\n /* v8 ignore next 3 */\n const lastNf = nfElements.at(-1);\n\n if (lastNf) {\n rendered.push(lastNf);\n }\n }\n\n return { rendered, activeMatchFound, fallback };\n}\n","import { shallowRef, onScopeDispose } from \"vue\";\n\nimport type { RouterSource } from \"@real-router/sources\";\nimport type { ShallowRef } from \"vue\";\n\nexport function useRefFromSource<T>(source: RouterSource<T>): ShallowRef<T> {\n const ref = shallowRef(source.getSnapshot());\n\n const unsub = source.subscribe(() => {\n ref.value = source.getSnapshot();\n });\n\n onScopeDispose(unsub);\n\n return ref;\n}\n","import type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\nimport type { InjectionKey } from \"vue\";\n\nexport const RouterKey: InjectionKey<Router> = Symbol(\"RouterKey\");\n\nexport const NavigatorKey: InjectionKey<Navigator> = Symbol(\"NavigatorKey\");\n\nexport const RouteKey: InjectionKey<RouteContextType> = Symbol(\"RouteKey\");\n","import { inject } from \"vue\";\n\nimport { RouterKey } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = inject(RouterKey);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteNodeSource } from \"@real-router/sources\";\nimport { computed } from \"vue\";\n\nimport { useRefFromSource } from \"../useRefFromSource\";\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n const router = useRouter();\n\n const source = createRouteNodeSource(router, nodeName);\n const snapshot = useRefFromSource(source);\n\n // getNavigator is WeakMap-cached in core; no useMemo equivalent needed.\n const navigator = getNavigator(router);\n\n // Derive route/previousRoute via computed instead of mirroring with a sync\n // watch into two extra shallowRefs. computed shares snapshot's identity so\n // when the underlying source emits the same reference (idempotent or\n // out-of-node nav), consumers don't see a new ref.\n const route = computed(() => snapshot.value.route);\n const previousRoute = computed(() => snapshot.value.previousRoute);\n\n return {\n navigator,\n route,\n previousRoute,\n };\n}\n","import {\n Fragment,\n defineComponent,\n h,\n KeepAlive,\n markRaw,\n Suspense,\n} from \"vue\";\n\nimport { Match, NotFound } from \"./components\";\nimport { buildRenderList, collectElements } from \"./helpers\";\nimport { useRouteNode } from \"../../composables/useRouteNode\";\n\nimport type { Component, VNode } from \"vue\";\n\ntype SlotChildren = Record<string, (() => VNode[]) | undefined> | null;\n\nfunction getSlotContent(vnode: VNode): VNode[] | null {\n const slots = vnode.children as SlotChildren;\n\n return slots?.default?.() ?? null;\n}\n\nfunction getOrCreateWrapper(\n cache: Map<string, Component>,\n segment: string,\n): Component {\n const existing = cache.get(segment);\n\n if (existing) {\n return existing;\n }\n\n const wrapper = markRaw(\n defineComponent({\n name: `KeepAlive-${segment}`,\n setup(_wrapperProps, wrapperCtx) {\n return () => wrapperCtx.slots.default?.();\n },\n }),\n );\n\n cache.set(segment, wrapper);\n\n return wrapper;\n}\n\nfunction wrapWithSuspense(content: VNode, fallback: unknown): VNode {\n if (fallback === undefined) {\n return content;\n }\n\n const fallbackContent =\n typeof fallback === \"function\" ? (fallback as () => VNode)() : fallback;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment\n const suspenseComponent = Suspense as any;\n\n return h(\n suspenseComponent,\n {},\n {\n default: () => content,\n fallback: () => fallbackContent,\n },\n );\n}\n\nconst emptyKeepAlivePlaceholder = markRaw(\n defineComponent({\n name: \"KeepAlive-placeholder\",\n render() {\n return null;\n },\n }),\n);\n\nfunction renderWithRootKA(\n activeChild: VNode,\n wrapperCache: Map<string, Component>,\n fallback: unknown,\n): VNode {\n const activeProps = activeChild.props as { segment?: string } | null;\n const segment = activeProps?.segment ?? \"__not-found__\";\n const WrapperComponent = getOrCreateWrapper(wrapperCache, segment);\n const slotContent = getSlotContent(activeChild) ?? [];\n const keepAliveContent = h(KeepAlive, null, {\n default: () =>\n h(WrapperComponent, { key: segment }, { default: () => slotContent }),\n });\n\n return wrapWithSuspense(keepAliveContent, fallback);\n}\n\n// Vue compiles boolean-shorthand template attributes (`<Match keepAlive>`) to\n// an empty string instead of `true`, and converts them to `true` only when the\n// receiving component's prop is declared with `type: Boolean`. `Match` is a\n// marker component (`render: null`) — its props are inspected on the VNode\n// without ever going through Vue's prop-casting pipeline, so the raw `\"\"` (or\n// the hyphenated attribute name) reaches us here. Accept the same trio Vue's\n// runtime does.\nfunction isKeepAliveEnabled(value: unknown): boolean {\n return value === true || value === \"\" || value === \"keep-alive\";\n}\n\nfunction renderWithPerMatchKA(\n activeChild: VNode,\n wrapperCache: Map<string, Component>,\n fallback: unknown,\n): VNode | null {\n const matchProps = activeChild.props as {\n segment?: string;\n keepAlive?: unknown;\n } | null;\n\n if (isKeepAliveEnabled(matchProps?.keepAlive) && activeChild.type === Match) {\n /* v8 ignore start */\n const segment = matchProps?.segment ?? \"__not-found__\";\n /* v8 ignore stop */\n const WrapperComponent = getOrCreateWrapper(wrapperCache, segment);\n const slotContent = getSlotContent(activeChild) ?? [];\n\n return h(Fragment, [\n h(KeepAlive, null, {\n default: () =>\n h(WrapperComponent, { key: segment }, { default: () => slotContent }),\n }),\n ]);\n }\n\n const content = getSlotContent(activeChild);\n\n /* v8 ignore start */\n if (!content) {\n return null;\n }\n /* v8 ignore stop */\n\n return h(Fragment, [\n h(KeepAlive, null, { default: () => h(emptyKeepAlivePlaceholder) }),\n wrapWithSuspense(h(Fragment, content), fallback),\n ]);\n}\n\nconst RouteViewComponent = defineComponent({\n name: \"RouteView\",\n props: {\n nodeName: {\n type: String,\n required: true,\n },\n keepAlive: {\n type: Boolean,\n default: false,\n },\n },\n setup(props, { slots }) {\n const routeContext = useRouteNode(props.nodeName);\n const wrapperCache = new Map<string, Component>();\n\n // Cache per-Match `keepAlive` detection by slot output identity. Slot\n // contents change reference only when the parent re-renders with new\n // children, so steady-state navigations skip the O(n) `.some(...)` scan.\n let lastSlotOutput: unknown = null;\n let lastHasPerMatchKA = false;\n\n function detectPerMatchKA(elements: VNode[], slotOutput: unknown): boolean {\n /* v8 ignore next 3 -- @preserve: Vue's compiled slot wrapper allocates a\n new array per render call in JSDOM tests; identity-cache hits in\n production where parent compiled templates share slot output, but\n is unobservable through TestBed-style assertions. */\n if (slotOutput === lastSlotOutput) {\n return lastHasPerMatchKA;\n }\n\n lastSlotOutput = slotOutput;\n lastHasPerMatchKA = elements.some(\n (element) =>\n element.type === Match &&\n isKeepAliveEnabled(\n (element.props as { keepAlive?: unknown } | null)?.keepAlive,\n ),\n );\n\n return lastHasPerMatchKA;\n }\n\n return (): VNode | null => {\n const route = routeContext.route.value;\n\n if (!route) {\n return null;\n }\n\n const slotOutput = slots.default?.();\n const elements: VNode[] = [];\n\n collectElements(slotOutput, elements);\n\n const { rendered, fallback } = buildRenderList(\n elements,\n route.name,\n props.nodeName,\n );\n\n if (rendered.length === 0) {\n return null;\n }\n\n const activeChild = rendered[0];\n\n if (props.keepAlive) {\n return renderWithRootKA(activeChild, wrapperCache, fallback);\n }\n\n /* v8 ignore start */\n if (activeChild.type !== Match && activeChild.type !== NotFound) {\n return null;\n }\n /* v8 ignore stop */\n\n const hasPerMatchKA = detectPerMatchKA(elements, slotOutput);\n\n if (hasPerMatchKA) {\n return renderWithPerMatchKA(activeChild, wrapperCache, fallback);\n }\n\n const content = getSlotContent(activeChild);\n\n if (!content) {\n return null;\n }\n\n return wrapWithSuspense(h(Fragment, content), fallback);\n };\n },\n});\n\nexport const RouteView = Object.assign(RouteViewComponent, { Match, NotFound });\n\nexport type {\n RouteViewProps,\n MatchProps as RouteViewMatchProps,\n NotFoundProps as RouteViewNotFoundProps,\n} from \"./types\";\n","/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","import type { Router, State } from \"@real-router/core\";\n\nconst CLEAR_DELAY = 7000;\nconst SAFARI_READY_DELAY = 100;\nconst ANNOUNCER_ATTR = \"data-real-router-announcer\";\nconst INTERNAL_ROUTE_PREFIX = \"@@\";\nconst VISUALLY_HIDDEN =\n \"position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0\";\n\nexport interface RouteAnnouncerOptions {\n prefix?: string;\n getAnnouncementText?: (route: State) => string;\n}\n\nexport function createRouteAnnouncer(\n router: Router,\n options?: RouteAnnouncerOptions,\n): { destroy: () => void } {\n const prefix = options?.prefix ?? \"Navigated to \";\n const getCustomText = options?.getAnnouncementText;\n\n let isInitialNavigation = true;\n let isReady = false;\n let isDestroyed = false;\n let lastAnnouncedText = \"\";\n let pendingText: string | null = null;\n let clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const announcer = getOrCreateAnnouncer();\n\n const doAnnounce = (text: string, h1: HTMLElement | null): void => {\n lastAnnouncedText = text;\n clearTimeout(clearTimeoutId);\n announcer.textContent = text;\n clearTimeoutId = setTimeout(() => {\n announcer.textContent = \"\";\n lastAnnouncedText = \"\";\n }, CLEAR_DELAY);\n\n manageFocus(h1);\n };\n\n // Safari-ready delay: announcing before VoiceOver wires up the aria-live region\n // causes the first announcement to be silently dropped. Wait SAFARI_READY_DELAY ms\n // before marking the announcer \"ready\" — any navigation during that window is\n // buffered in pendingText and flushed once the delay expires.\n const safariTimeoutId = setTimeout(() => {\n isReady = true;\n\n if (pendingText !== null && !isDestroyed) {\n const text = pendingText;\n\n pendingText = null;\n doAnnounce(text, document.querySelector<HTMLElement>(\"h1\"));\n }\n }, SAFARI_READY_DELAY);\n\n const unsubscribe = router.subscribe(({ route }) => {\n if (isInitialNavigation) {\n isInitialNavigation = false;\n\n return;\n }\n\n // Double rAF: waits for two paint frames so the incoming route's DOM\n // (including the new <h1>) is fully rendered before resolveText reads it.\n // Single rAF fires before the new route's template has been attached,\n // which would cause resolveText to pick up the OLD h1 or fall back to\n // document.title / route.name prematurely.\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (isDestroyed) {\n return;\n }\n\n const h1 = document.querySelector<HTMLElement>(\"h1\");\n const text = resolveText(route, prefix, getCustomText, h1);\n\n if (!text || text === lastAnnouncedText) {\n return;\n }\n\n if (!isReady) {\n // Defer announcement until Safari-ready window elapses (see safariTimeoutId).\n pendingText = text;\n\n return;\n }\n\n doAnnounce(text, h1);\n });\n });\n });\n\n return {\n destroy() {\n isDestroyed = true;\n unsubscribe();\n clearTimeout(clearTimeoutId);\n clearTimeout(safariTimeoutId);\n removeAnnouncer();\n },\n };\n}\n\nfunction getOrCreateAnnouncer(): HTMLElement {\n const existing = document.querySelector<HTMLElement>(`[${ANNOUNCER_ATTR}]`);\n\n if (existing) {\n return existing;\n }\n\n const element = document.createElement(\"div\");\n\n element.setAttribute(\"style\", VISUALLY_HIDDEN);\n element.setAttribute(\"aria-live\", \"assertive\");\n element.setAttribute(\"aria-atomic\", \"true\");\n element.setAttribute(ANNOUNCER_ATTR, \"\");\n\n document.body.prepend(element);\n\n return element;\n}\n\nfunction removeAnnouncer(): void {\n document.querySelector(`[${ANNOUNCER_ATTR}]`)?.remove();\n}\n\nfunction resolveText(\n route: State,\n prefix: string,\n getCustomText: ((route: State) => string) | undefined,\n h1: HTMLElement | null,\n): string {\n if (getCustomText) {\n return getCustomText(route);\n }\n\n const h1Text = h1?.textContent.trim() ?? \"\";\n const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)\n ? \"\"\n : route.name;\n const rawText =\n h1Text || document.title || routeName || globalThis.location.pathname;\n\n return `${prefix}${rawText}`;\n}\n\nfunction manageFocus(h1: HTMLElement | null): void {\n if (!h1) {\n return;\n }\n\n if (!h1.hasAttribute(\"tabindex\")) {\n h1.setAttribute(\"tabindex\", \"-1\");\n }\n\n h1.focus({ preventScroll: true });\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst STORAGE_KEY = \"real-router:scroll\";\n\nconst NOOP_INSTANCE: { destroy: () => void } = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport type ScrollRestorationMode = \"restore\" | \"top\" | \"manual\";\n\nexport interface ScrollRestorationOptions {\n mode?: ScrollRestorationMode | undefined;\n anchorScrolling?: boolean | undefined;\n scrollContainer?: (() => HTMLElement | null) | undefined;\n}\n\ninterface NavigationContext {\n direction?: \"forward\" | \"back\" | \"unknown\";\n navigationType?: \"push\" | \"replace\" | \"traverse\" | \"reload\";\n}\n\nexport function createScrollRestoration(\n router: Router,\n options?: ScrollRestorationOptions,\n): { destroy: () => void } {\n if (typeof globalThis.window === \"undefined\") {\n return NOOP_INSTANCE;\n }\n\n const mode = options?.mode ?? \"restore\";\n\n // mode \"manual\" = utility does nothing. Don't flip history.scrollRestoration,\n // don't subscribe, don't register pagehide — leave the browser's native\n // auto-restore intact for the app to override if it wants to.\n if (mode === \"manual\") {\n return NOOP_INSTANCE;\n }\n\n const anchorEnabled = options?.anchorScrolling ?? true;\n const getContainer = options?.scrollContainer;\n\n const prevScrollRestoration = history.scrollRestoration;\n\n try {\n history.scrollRestoration = \"manual\";\n } catch {\n // Ignore — some embedded contexts may reject the assignment.\n }\n\n // Resolve the container lazily on every event so containers mounted AFTER\n // the provider still get correct scroll handling. Falls back to window when\n // the getter is absent or returns null (pre-mount).\n const readPos = (): number => {\n const element = getContainer?.();\n\n return element ? element.scrollTop : globalThis.scrollY;\n };\n\n const writePos = (top: number): void => {\n const element = getContainer?.();\n\n if (element) {\n element.scrollTop = top;\n } else {\n globalThis.scrollTo(0, top);\n }\n };\n\n const scrollToHashOrTop = (): void => {\n const hash = globalThis.location.hash;\n\n if (anchorEnabled && hash.length > 1) {\n // location.hash is percent-encoded; ids in the DOM are the raw string.\n // Decode for the match. Fall back to the raw slice if the hash contains\n // a malformed escape sequence (decodeURIComponent throws on those).\n let id: string;\n\n try {\n id = decodeURIComponent(hash.slice(1));\n } catch {\n id = hash.slice(1);\n }\n\n // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n const element = document.getElementById(id);\n\n if (element) {\n element.scrollIntoView();\n\n return;\n }\n }\n\n writePos(0);\n };\n\n let destroyed = false;\n\n const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n const nav = (route.context as { navigation?: NavigationContext })\n .navigation;\n\n // Browsers dispatch reload as the initial navigation after refresh, so\n // previousRoute is undefined and capture is naturally skipped. The\n // pre-refresh position was already persisted via pagehide.\n if (previousRoute) {\n putPos(keyOf(previousRoute), readPos());\n }\n\n // Single rAF so DOM is committed before we read anchors / write scroll.\n // Guard against destroy() racing with the callback.\n requestAnimationFrame(() => {\n if (destroyed) {\n return;\n }\n\n if (mode === \"top\" || !nav) {\n scrollToHashOrTop();\n\n return;\n }\n\n if (nav.navigationType === \"replace\") {\n return;\n }\n\n if (\n nav.direction === \"back\" ||\n nav.navigationType === \"traverse\" ||\n nav.navigationType === \"reload\"\n ) {\n writePos(loadStore()[keyOf(route)] ?? 0);\n\n return;\n }\n\n scrollToHashOrTop();\n });\n });\n\n const onPageHide = (): void => {\n const current = router.getState();\n\n if (current) {\n putPos(keyOf(current), readPos());\n }\n };\n\n globalThis.addEventListener(\"pagehide\", onPageHide);\n\n return {\n destroy: () => {\n if (destroyed) {\n return;\n }\n\n destroyed = true;\n unsubscribe();\n globalThis.removeEventListener(\"pagehide\", onPageHide);\n\n try {\n history.scrollRestoration = prevScrollRestoration;\n } catch {\n // Ignore.\n }\n },\n };\n}\n\nfunction keyOf(state: State): string {\n return `${state.name}:${canonicalJson(state.params)}`;\n}\n\nfunction loadStore(): Record<string, number> {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n\n return raw ? (JSON.parse(raw) as Record<string, number>) : {};\n } catch {\n return {};\n }\n}\n\nfunction putPos(key: string, pos: number): void {\n try {\n const store = loadStore();\n\n store[key] = pos;\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // Ignore quota / security errors.\n }\n}\n\nfunction canonicalJson(value: unknown): string {\n return JSON.stringify(value, canonicalReplacer);\n}\n\nfunction canonicalReplacer(_key: string, val: unknown): unknown {\n if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n const sorted: Record<string, unknown> = {};\n // eslint-disable-next-line unicorn/no-array-sort -- ng-packagr uses pre-ES2023 lib; toSorted unavailable\n const keys = Object.keys(val as Record<string, unknown>).sort(\n (left: string, right: string) => left.localeCompare(right),\n );\n\n for (const key of keys) {\n sorted[key] = (val as Record<string, unknown>)[key];\n }\n\n return sorted;\n }\n\n return val;\n}\n","import type { Router, Params } from \"@real-router/core\";\n\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 &&\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n\ntype BuildUrlFn = (name: string, params: Params) => string | undefined;\n\nexport function buildHref(\n router: Router,\n routeName: string,\n routeParams: Params,\n): string | undefined {\n try {\n const buildUrl = router.buildUrl as BuildUrlFn | undefined;\n\n if (buildUrl) {\n const url = buildUrl(routeName, routeParams);\n\n if (url !== undefined) {\n return url;\n }\n }\n\n return router.buildPath(routeName, routeParams);\n } catch {\n console.error(\n `[real-router] Route \"${routeName}\" is not defined. The element will render without an href attribute.`,\n );\n\n return undefined;\n }\n}\n\nfunction parseTokens(value: string | undefined): string[] {\n return value ? (value.match(/\\S+/g) ?? []) : [];\n}\n\nexport function buildActiveClassName(\n isActive: boolean,\n activeClassName: string | undefined,\n baseClassName: string | undefined,\n): string | undefined {\n if (isActive && activeClassName) {\n const activeTokens = parseTokens(activeClassName);\n\n if (activeTokens.length === 0) {\n return baseClassName ?? undefined;\n }\n if (!baseClassName) {\n return activeTokens.join(\" \");\n }\n\n const baseTokens = parseTokens(baseClassName);\n const seen = new Set(baseTokens);\n\n for (const token of activeTokens) {\n if (!seen.has(token)) {\n seen.add(token);\n baseTokens.push(token);\n }\n }\n\n return baseTokens.join(\" \");\n }\n\n return baseClassName ?? undefined;\n}\n\nexport function shallowEqual(\n prev: object | undefined,\n next: object | undefined,\n): boolean {\n if (Object.is(prev, next)) {\n return true;\n }\n if (!prev || !next) {\n return false;\n }\n\n const prevKeys = Object.keys(prev);\n\n if (prevKeys.length !== Object.keys(next).length) {\n return false;\n }\n\n const prevRecord = prev as Record<string, unknown>;\n const nextRecord = next as Record<string, unknown>;\n\n for (const key of prevKeys) {\n if (!Object.is(prevRecord[key], nextRecord[key])) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function applyLinkA11y(element: HTMLElement | null | undefined): void {\n if (!element) {\n return;\n }\n if (\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLButtonElement\n ) {\n return;\n }\n if (!element.hasAttribute(\"role\")) {\n element.setAttribute(\"role\", \"link\");\n }\n if (!element.hasAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n }\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { defineComponent, h, computed, shallowRef, watch } from \"vue\";\n\nimport { useRouter } from \"../composables/useRouter\";\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport { shouldNavigate, buildHref, buildActiveClassName } from \"../dom-utils\";\n\nimport type { Params, NavigationOptions } from \"@real-router/core\";\nimport type { PropType } from \"vue\";\n\ntype OnClickHandler = (evt: MouseEvent) => void;\n\n/**\n * Vue's compiled template binds multiple `@click` handlers as an array.\n * Single render-function `onClick` is a function. Both must be invoked.\n */\nfunction invokeAttributesOnClick(value: unknown, evt: MouseEvent): void {\n if (typeof value === \"function\") {\n (value as OnClickHandler)(evt);\n\n return;\n }\n if (Array.isArray(value)) {\n const handlers = value as OnClickHandler[];\n\n for (const fn of handlers) {\n if (typeof fn === \"function\") {\n fn(evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n }\n }\n}\n\nexport const Link = defineComponent({\n name: \"Link\",\n // Disable Vue's automatic attribute fallthrough. Without this, attrs.onClick\n // (function OR array) is auto-attached as a native click listener AND our\n // explicit onClick fires too — user handlers are double-invoked. We invoke\n // attrs.onClick manually inside handleClick to preserve preventDefault.\n inheritAttrs: false,\n props: {\n routeName: {\n type: String,\n required: true,\n },\n routeParams: {\n type: Object as PropType<Params>,\n default: () => EMPTY_PARAMS,\n },\n routeOptions: {\n type: Object as PropType<NavigationOptions>,\n default: () => EMPTY_OPTIONS,\n },\n class: {\n type: String,\n default: undefined,\n },\n activeClassName: {\n type: String,\n default: \"active\",\n },\n activeStrict: {\n type: Boolean,\n default: false,\n },\n ignoreQueryParams: {\n type: Boolean,\n default: true,\n },\n target: {\n type: String,\n default: undefined,\n },\n },\n setup(props, { slots, attrs }) {\n const router = useRouter();\n\n const isActive = shallowRef(false);\n\n // watch with an explicit dep getter recreates the source ONLY when\n // routeName/routeParams/strict/ignoreQueryParams change — not on every\n // reactive read inside the source factory (the watchEffect alternative\n // would also re-subscribe whenever isActive itself changed).\n watch(\n () =>\n [\n props.routeName,\n props.routeParams,\n props.activeStrict,\n props.ignoreQueryParams,\n ] as const,\n (\n [routeName, routeParams, activeStrict, ignoreQueryParams],\n _prev,\n onCleanup,\n ) => {\n const source = createActiveRouteSource(router, routeName, routeParams, {\n strict: activeStrict,\n ignoreQueryParams,\n });\n\n isActive.value = source.getSnapshot();\n\n const unsub = source.subscribe(() => {\n isActive.value = source.getSnapshot();\n });\n\n onCleanup(unsub);\n },\n { immediate: true, flush: \"sync\" },\n );\n\n const href = computed(() =>\n buildHref(router, props.routeName, props.routeParams),\n );\n\n const finalClassName = computed(() =>\n buildActiveClassName(isActive.value, props.activeClassName, props.class),\n );\n\n const handleClick = (evt: MouseEvent) => {\n // Vue allows attrs.onClick to be a function or an array of functions\n // (compiled templates with multiple @click bindings produce arrays).\n // Both must be invoked; treating arrays as \"no handler\" silently drops\n // user code.\n if (attrs.onClick !== undefined && attrs.onClick !== null) {\n invokeAttributesOnClick(attrs.onClick, evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n if (!shouldNavigate(evt) || props.target === \"_blank\") {\n return;\n }\n\n evt.preventDefault();\n router\n .navigate(props.routeName, props.routeParams, props.routeOptions)\n .catch(() => {});\n };\n\n return () => {\n // Build forwarded attrs without `onClick`. Vue's runtime auto-attaches\n // attrs.onClick (function OR array) as a native DOM listener, which would\n // double-invoke user handlers when combined with our explicit `onClick`.\n // We invoke the original attrs.onClick manually inside handleClick so the\n // preventDefault contract is preserved.\n const restAttributes: Record<string, unknown> = {};\n\n for (const key of Object.keys(attrs)) {\n if (key !== \"onClick\") {\n restAttributes[key] = (attrs as Record<string, unknown>)[key];\n }\n }\n\n return h(\n \"a\",\n {\n ...restAttributes,\n href: href.value,\n class: finalClassName.value,\n target: props.target,\n onClick: handleClick,\n },\n slots.default?.(),\n );\n };\n },\n});\n","import { createDismissableError } from \"@real-router/sources\";\nimport { defineComponent, h, watch, Fragment } from \"vue\";\n\nimport { useRouter } from \"../composables/useRouter\";\nimport { useRefFromSource } from \"../useRefFromSource\";\n\nimport type { RouterError, State } from \"@real-router/core\";\nimport type { VNode, PropType } from \"vue\";\n\nexport const RouterErrorBoundary = defineComponent({\n name: \"RouterErrorBoundary\",\n props: {\n fallback: {\n type: Function as PropType<\n (error: RouterError, resetError: () => void) => VNode\n >,\n required: true,\n },\n onError: {\n type: Function as PropType<\n (\n error: RouterError,\n toRoute: State | null,\n fromRoute: State | null,\n ) => void\n >,\n default: undefined,\n },\n },\n setup(props, { slots }) {\n const router = useRouter();\n const snapshot = useRefFromSource(createDismissableError(router));\n\n watch(\n () => snapshot.value.version,\n () => {\n if (snapshot.value.error) {\n props.onError?.(\n snapshot.value.error,\n snapshot.value.toRoute,\n snapshot.value.fromRoute,\n );\n }\n },\n { immediate: true },\n );\n\n return () => {\n const children = slots.default?.() ?? [];\n const errorVNode = snapshot.value.error\n ? props.fallback(snapshot.value.error, snapshot.value.resetError)\n : null;\n\n return h(Fragment, null, [...children, errorVNode]);\n };\n },\n});\n\nexport type RouterErrorBoundaryProps = InstanceType<\n typeof RouterErrorBoundary\n>[\"$props\"];\n","import { shouldNavigate, applyLinkA11y } from \"../dom-utils\";\n\nimport type { Router, NavigationOptions, Params } from \"@real-router/core\";\nimport type { Directive } from \"vue\";\n\nexport interface LinkDirectiveValue {\n name: string;\n params?: Params;\n options?: NavigationOptions;\n}\n\n/**\n * Router stack for nested RouterProviders. The active router is the top of\n * the stack. RouterProvider pushes its router on mount and pops it on unmount,\n * which preserves the parent context when an inner provider tears down.\n *\n * Without the stack, an unmounted child provider would leave the directive\n * pointing at a disposed router, and v-link in the still-mounted parent would\n * navigate via the wrong (or torn-down) instance.\n */\nconst routerStack: Router[] = [];\n\n/**\n * Pushes a router onto the active stack. Returns a release function that\n * removes that exact router from the stack regardless of position — safe\n * across out-of-order provider unmount sequences.\n *\n * @internal Used by RouterProvider during setup/teardown.\n */\nexport function pushDirectiveRouter(router: Router): () => void {\n routerStack.push(router);\n\n return () => {\n const idx = routerStack.lastIndexOf(router);\n\n if (idx !== -1) {\n routerStack.splice(idx, 1);\n }\n };\n}\n\n/**\n * Backwards-compatible alias. Replaces the active router unconditionally and\n * does NOT participate in the stack — use {@link pushDirectiveRouter} from\n * provider code instead. Kept for tests and direct callers.\n */\nexport function setDirectiveRouter(router: Router | null): void {\n if (router === null) {\n routerStack.length = 0;\n\n return;\n }\n if (routerStack.length === 0) {\n routerStack.push(router);\n\n return;\n }\n\n routerStack[routerStack.length - 1] = router;\n}\n\nexport function getDirectiveRouter(): Router {\n const top = routerStack.at(-1);\n\n if (!top) {\n throw new Error(\n \"v-link directive requires a RouterProvider ancestor. Make sure RouterProvider is mounted.\",\n );\n }\n\n return top;\n}\n\ninterface Handlers {\n click: (evt: MouseEvent) => void;\n keydown: (evt: KeyboardEvent) => void;\n}\n\n// Single WeakMap halves per-element bookkeeping vs two parallel maps.\nconst handlers = new WeakMap<HTMLElement, Handlers>();\n\n/**\n * Validates a directive binding value before attaching handlers.\n * Returns false (and warns once per call) when the value is missing or\n * has no `name` — silently doing nothing is preferable to a runtime crash\n * inside a click handler.\n */\nfunction isValidBinding(value: unknown): value is LinkDirectiveValue {\n if (value === null || value === undefined) {\n console.error(\n \"[real-router] v-link directive received null/undefined value. The element will not be wired for navigation.\",\n );\n\n return false;\n }\n if (typeof (value as { name?: unknown }).name !== \"string\") {\n console.error(\n \"[real-router] v-link directive value is missing a string `name` field. The element will not be wired for navigation.\",\n );\n\n return false;\n }\n\n return true;\n}\n\nfunction createClickHandler(\n router: Router,\n value: LinkDirectiveValue,\n): (evt: MouseEvent) => void {\n return (evt: MouseEvent) => {\n if (!shouldNavigate(evt)) {\n return;\n }\n\n evt.preventDefault();\n router\n .navigate(value.name, value.params ?? {}, value.options ?? {})\n .catch(() => {});\n };\n}\n\nfunction createKeydownHandler(\n router: Router,\n value: LinkDirectiveValue,\n element: HTMLElement,\n): (evt: KeyboardEvent) => void {\n return (evt: KeyboardEvent) => {\n if (evt.key === \"Enter\" && !(element instanceof HTMLButtonElement)) {\n router\n .navigate(value.name, value.params ?? {}, value.options ?? {})\n .catch(() => {});\n }\n };\n}\n\nfunction attachHandlers(\n element: HTMLElement,\n router: Router,\n value: LinkDirectiveValue,\n): void {\n const click = createClickHandler(router, value);\n const keydown = createKeydownHandler(router, value, element);\n\n element.addEventListener(\"click\", click);\n element.addEventListener(\"keydown\", keydown);\n\n handlers.set(element, { click, keydown });\n}\n\nfunction detachHandlers(element: HTMLElement): void {\n const entry = handlers.get(element);\n\n if (entry) {\n element.removeEventListener(\"click\", entry.click);\n element.removeEventListener(\"keydown\", entry.keydown);\n handlers.delete(element);\n }\n}\n\nexport const vLink: Directive<HTMLElement, LinkDirectiveValue> = {\n mounted(element, binding) {\n const router = getDirectiveRouter();\n\n applyLinkA11y(element);\n\n element.style.cursor = \"pointer\";\n\n if (!isValidBinding(binding.value)) {\n return;\n }\n\n attachHandlers(element, router, binding.value);\n },\n\n updated(element, binding) {\n const router = getDirectiveRouter();\n\n detachHandlers(element);\n\n if (!isValidBinding(binding.value)) {\n return;\n }\n\n attachHandlers(element, router, binding.value);\n },\n\n beforeUnmount(element) {\n detachHandlers(element);\n },\n};\n","import { inject } from \"vue\";\n\nimport { NavigatorKey } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = inject(NavigatorKey);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","import { getPluginApi } from \"@real-router/core/api\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n","import { inject } from \"vue\";\n\nimport { RouteKey } from \"../context\";\n\nimport type { RouteContext } from \"../types\";\nimport type { Params } from \"@real-router/core\";\n\nexport const useRoute = <P extends Params = Params>(): RouteContext<P> => {\n const routeContext = inject(RouteKey);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouterProvider\");\n }\n\n return routeContext as RouteContext<P>;\n};\n","import { getTransitionSource } from \"@real-router/sources\";\n\nimport { useRefFromSource } from \"../useRefFromSource\";\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouterTransitionSnapshot } from \"@real-router/sources\";\nimport type { ShallowRef } from \"vue\";\n\nexport function useRouterTransition(): ShallowRef<RouterTransitionSnapshot> {\n const router = useRouter();\n const source = getTransitionSource(router);\n\n return useRefFromSource(source);\n}\n","// packages/vue/src/setupRouteProvision.ts\n\nimport { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { shallowRef } from \"vue\";\n\nimport type { Router, Navigator, State } from \"@real-router/core\";\nimport type { ShallowRef } from \"vue\";\n\n/**\n * Shared setup for `RouterProvider` (component-scoped) and\n * `createRouterPlugin` (app-scoped). Builds the reactive route refs +\n * subscription bookkeeping in one place; callers wire the result into their\n * provide/inject mechanism and own teardown lifecycle.\n *\n * @internal\n */\nexport interface RouteProvision {\n navigator: Navigator;\n route: ShallowRef<State | undefined>;\n previousRoute: ShallowRef<State | undefined>;\n /** Call when the owning scope tears down to release the router subscription. */\n unsubscribe: () => void;\n}\n\nexport function setupRouteProvision(router: Router): RouteProvision {\n const navigator = getNavigator(router);\n const source = createRouteSource(router);\n const initial = source.getSnapshot();\n\n const route = shallowRef<State | undefined>(initial.route);\n const previousRoute = shallowRef<State | undefined>(initial.previousRoute);\n\n const unsubscribe = source.subscribe(() => {\n const snapshot = source.getSnapshot();\n\n route.value = snapshot.route;\n previousRoute.value = snapshot.previousRoute;\n });\n\n return { navigator, route, previousRoute, unsubscribe };\n}\n","import { NavigatorKey, RouteKey, RouterKey } from \"./context\";\nimport { pushDirectiveRouter } from \"./directives/vLink\";\nimport { setupRouteProvision } from \"./setupRouteProvision\";\n\nimport type { Router } from \"@real-router/core\";\nimport type { App, Plugin } from \"vue\";\n\nexport function createRouterPlugin(router: Router): Plugin<[]> {\n return {\n install(app): void {\n const releaseDirective = pushDirectiveRouter(router);\n\n const { navigator, route, previousRoute, unsubscribe } =\n setupRouteProvision(router);\n\n // Vue 3.5+ exposes app.onUnmount for plugin cleanup.\n // On older versions (3.3–3.4), the subscription is cleaned up\n // when the router is garbage-collected (same as vue-router).\n if (\"onUnmount\" in app) {\n (app as App & { onUnmount: (fn: () => void) => void }).onUnmount(() => {\n releaseDirective();\n unsubscribe();\n });\n }\n\n app.provide(RouterKey, router);\n app.provide(NavigatorKey, navigator);\n app.provide(RouteKey, { navigator, route, previousRoute });\n },\n };\n}\n","import { defineComponent, onScopeDispose, provide, watch } from \"vue\";\n\nimport { NavigatorKey, RouteKey, RouterKey } from \"./context\";\nimport { pushDirectiveRouter } from \"./directives/vLink\";\nimport { createRouteAnnouncer, createScrollRestoration } from \"./dom-utils\";\nimport { setupRouteProvision } from \"./setupRouteProvision\";\n\nimport type { ScrollRestorationOptions } from \"./dom-utils\";\nimport type { Router } from \"@real-router/core\";\nimport type { PropType } from \"vue\";\n\nexport const RouterProvider = defineComponent({\n name: \"RouterProvider\",\n props: {\n router: {\n type: Object as PropType<Router>,\n required: true,\n },\n announceNavigation: {\n type: Boolean,\n default: false,\n },\n scrollRestoration: {\n type: Object as PropType<ScrollRestorationOptions>,\n },\n },\n setup(props, { slots }) {\n // Reactive announceNavigation: setting prop true/false at runtime now\n // creates/destroys the announcer accordingly. Prior implementation read\n // the prop only inside onMounted, so toggling it post-mount silently no-op'd.\n watch(\n () => [props.router, props.announceNavigation] as const,\n ([router, enabled], _prev, onCleanup) => {\n if (!enabled) {\n return;\n }\n\n const announcer = createRouteAnnouncer(router);\n\n onCleanup(() => {\n announcer.destroy();\n });\n },\n { immediate: true },\n );\n\n // Watch by primitives so inline `{ mode: \"restore\" }` doesn't thrash.\n // scrollContainer is a getter invoked lazily on every event inside the\n // utility — swapping its reference doesn't change the resolved element,\n // so we intentionally omit it from watched sources.\n watch(\n () =>\n [\n props.router,\n props.scrollRestoration !== undefined,\n props.scrollRestoration?.mode,\n props.scrollRestoration?.anchorScrolling,\n ] as const,\n ([router, enabled, mode, anchorScrolling], _prev, onCleanup) => {\n if (!enabled) {\n return;\n }\n\n const sr = createScrollRestoration(router, {\n mode,\n anchorScrolling,\n scrollContainer: props.scrollRestoration?.scrollContainer,\n });\n\n onCleanup(() => {\n sr.destroy();\n });\n },\n { immediate: true },\n );\n\n // Push this provider's router on the v-link directive stack so nested\n // RouterProviders behave like nested DI scopes (LIFO). Release on unmount\n // restores the outer router for any v-link still mounted in the parent.\n const releaseDirective = pushDirectiveRouter(props.router);\n\n const { navigator, route, previousRoute, unsubscribe } =\n setupRouteProvision(props.router);\n\n onScopeDispose(() => {\n releaseDirective();\n unsubscribe();\n });\n\n provide(RouterKey, props.router);\n provide(NavigatorKey, navigator);\n provide(RouteKey, { navigator, route, previousRoute });\n\n return () => slots.default?.();\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["NOOP_INSTANCE"],"sources":["../../src/components/RouteView/components.ts","../../src/components/RouteView/helpers.ts","../../src/useRefFromSource.ts","../../src/context.ts","../../src/composables/useRouter.ts","../../src/composables/useRouteNode.ts","../../src/components/RouteView/RouteView.ts","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/scroll-restore.ts","../../../../shared/dom-utils/view-transitions.ts","../../../../shared/dom-utils/link-utils.ts","../../src/components/Link.ts","../../src/components/RouterErrorBoundary.ts","../../src/directives/vLink.ts","../../src/composables/useNavigator.ts","../../src/composables/useRouteUtils.ts","../../src/composables/useRoute.ts","../../src/composables/useRouterTransition.ts","../../src/composables/useRouteExit.ts","../../src/composables/useRouteEnter.ts","../../src/setupRouteProvision.ts","../../src/createRouterPlugin.ts","../../src/RouterProvider.ts"],"sourcesContent":["import { defineComponent } from \"vue\";\n\nimport type { SelfProps } from \"./types\";\nimport type { FunctionalComponent, PropType, VNode } from \"vue\";\n\nfunction renderNull() {\n return null;\n}\n\nexport const Match = defineComponent({\n name: \"RouteView.Match\",\n props: {\n segment: {\n type: String as PropType<string>,\n required: true,\n },\n exact: {\n type: Boolean,\n default: false,\n },\n fallback: {\n type: [Object, Function] as PropType<VNode | (() => VNode)>,\n default: undefined,\n },\n keepAlive: {\n type: Boolean,\n default: false,\n },\n },\n render: renderNull,\n});\n\n// Type Self via FunctionalComponent<SelfProps> so the SelfProps interface\n// is anchored to the component contract — knip otherwise flags SelfProps\n// as unused even though it's re-exported as RouteViewSelfProps for\n// consumers wrapping Self in custom HOCs.\nconst SelfImpl = defineComponent({\n name: \"RouteView.Self\",\n props: {\n fallback: {\n type: [Object, Function] as PropType<VNode | (() => VNode)>,\n default: undefined,\n },\n },\n render: renderNull,\n});\n\nexport const Self = SelfImpl as unknown as FunctionalComponent<SelfProps>;\n\nexport const NotFound = defineComponent({\n name: \"RouteView.NotFound\",\n render: renderNull,\n});\n","import { UNKNOWN_ROUTE } from \"@real-router/core\";\nimport { startsWithSegment } from \"@real-router/route-utils\";\nimport { Fragment, isVNode } from \"vue\";\n\nimport { Match, NotFound, Self } from \"./components\";\n\nimport type { VNode } from \"vue\";\n\ntype FallbackType = VNode | (() => VNode) | undefined;\n\ninterface FallbackSlots {\n selfVNode: VNode | null;\n selfFallback: FallbackType;\n notFoundChildren: unknown;\n}\n\nfunction isSegmentMatch(\n routeName: string,\n fullSegmentName: string,\n exact: boolean,\n): boolean {\n if (exact) {\n return routeName === fullSegmentName;\n }\n\n return startsWithSegment(routeName, fullSegmentName);\n}\n\nfunction normalizeChildren(children: unknown): VNode[] {\n if (Array.isArray(children)) {\n const result: VNode[] = [];\n\n for (const child of children) {\n if (Array.isArray(child)) {\n result.push(...normalizeChildren(child));\n } else if (isVNode(child)) {\n result.push(child);\n }\n }\n\n return result;\n }\n\n if (isVNode(children)) {\n return [children];\n }\n\n return [];\n}\n\nexport function collectElements(children: unknown, result: VNode[]): void {\n const vnodes = normalizeChildren(children);\n\n for (const child of vnodes) {\n if (\n child.type === Match ||\n child.type === Self ||\n child.type === NotFound\n ) {\n result.push(child);\n } else if (child.type === Fragment) {\n collectElements(child.children, result);\n }\n }\n}\n\nfunction recordFallback(child: VNode, slots: FallbackSlots): boolean {\n if (child.type === NotFound) {\n slots.notFoundChildren = child.children;\n\n return true;\n }\n\n if (child.type === Self) {\n if (slots.selfVNode === null) {\n slots.selfVNode = child;\n const props = child.props as { fallback?: FallbackType } | null;\n\n slots.selfFallback = props?.fallback;\n }\n\n return true;\n }\n\n return false;\n}\n\nfunction evaluateMatch(\n child: VNode,\n routeName: string,\n nodeName: string,\n): { isActive: boolean; fallback: FallbackType } {\n const props = child.props as {\n segment: string;\n exact?: boolean;\n fallback?: FallbackType;\n } | null;\n const segment = props?.segment ?? \"\";\n const exact = props?.exact ?? false;\n const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;\n const isActive = isSegmentMatch(routeName, fullSegmentName, exact);\n\n return { isActive, fallback: props?.fallback };\n}\n\nfunction appendFallback(\n rendered: VNode[],\n routeName: string,\n nodeName: string,\n slots: FallbackSlots,\n elements: VNode[],\n): FallbackType {\n if (slots.selfVNode !== null && routeName === nodeName) {\n rendered.push(slots.selfVNode);\n\n return slots.selfFallback;\n }\n\n if (routeName === UNKNOWN_ROUTE && slots.notFoundChildren !== null) {\n const nfElements = elements.filter((element) => element.type === NotFound);\n /* v8 ignore next 3 */\n const lastNf = nfElements.at(-1);\n\n if (lastNf) {\n rendered.push(lastNf);\n }\n }\n\n return undefined;\n}\n\nexport function buildRenderList(\n elements: VNode[],\n routeName: string,\n nodeName: string,\n): {\n rendered: VNode[];\n activeMatchFound: boolean;\n fallback?: FallbackType;\n} {\n const slots: FallbackSlots = {\n selfVNode: null,\n selfFallback: undefined,\n notFoundChildren: null,\n };\n let activeMatchFound = false;\n let fallback: FallbackType = undefined;\n const rendered: VNode[] = [];\n\n for (const child of elements) {\n if (recordFallback(child, slots)) {\n continue;\n }\n\n if (activeMatchFound) {\n continue;\n }\n\n const result = evaluateMatch(child, routeName, nodeName);\n\n if (result.isActive) {\n activeMatchFound = true;\n fallback = result.fallback;\n rendered.push(child);\n }\n }\n\n if (!activeMatchFound) {\n fallback = appendFallback(rendered, routeName, nodeName, slots, elements);\n }\n\n return { rendered, activeMatchFound, fallback };\n}\n","import { shallowRef, onScopeDispose } from \"vue\";\n\nimport type { RouterSource } from \"@real-router/sources\";\nimport type { ShallowRef } from \"vue\";\n\nexport function useRefFromSource<T>(source: RouterSource<T>): ShallowRef<T> {\n const ref = shallowRef(source.getSnapshot());\n\n const unsub = source.subscribe(() => {\n ref.value = source.getSnapshot();\n });\n\n onScopeDispose(unsub);\n\n return ref;\n}\n","import type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\nimport type { InjectionKey } from \"vue\";\n\nexport const RouterKey: InjectionKey<Router> = Symbol(\"RouterKey\");\n\nexport const NavigatorKey: InjectionKey<Navigator> = Symbol(\"NavigatorKey\");\n\nexport const RouteKey: InjectionKey<RouteContextType> = Symbol(\"RouteKey\");\n","import { inject } from \"vue\";\n\nimport { RouterKey } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = inject(RouterKey);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteNodeSource } from \"@real-router/sources\";\nimport { computed } from \"vue\";\n\nimport { useRefFromSource } from \"../useRefFromSource\";\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n const router = useRouter();\n\n const source = createRouteNodeSource(router, nodeName);\n const snapshot = useRefFromSource(source);\n\n // getNavigator is WeakMap-cached in core; no useMemo equivalent needed.\n const navigator = getNavigator(router);\n\n // Derive route/previousRoute via computed instead of mirroring with a sync\n // watch into two extra shallowRefs. computed shares snapshot's identity so\n // when the underlying source emits the same reference (idempotent or\n // out-of-node nav), consumers don't see a new ref.\n const route = computed(() => snapshot.value.route);\n const previousRoute = computed(() => snapshot.value.previousRoute);\n\n return {\n navigator,\n route,\n previousRoute,\n };\n}\n","import {\n Fragment,\n defineComponent,\n h,\n KeepAlive,\n markRaw,\n Suspense,\n} from \"vue\";\n\nimport { Match, NotFound, Self } from \"./components\";\nimport { buildRenderList, collectElements } from \"./helpers\";\nimport { useRouteNode } from \"../../composables/useRouteNode\";\n\nimport type { Component, VNode } from \"vue\";\n\ntype SlotChildren = Record<string, (() => VNode[]) | undefined> | null;\n\nfunction getSlotContent(vnode: VNode): VNode[] | null {\n const slots = vnode.children as SlotChildren;\n\n return slots?.default?.() ?? null;\n}\n\nfunction getOrCreateWrapper(\n cache: Map<string, Component>,\n segment: string,\n): Component {\n const existing = cache.get(segment);\n\n if (existing) {\n return existing;\n }\n\n const wrapper = markRaw(\n defineComponent({\n name: `KeepAlive-${segment}`,\n setup(_wrapperProps, wrapperCtx) {\n return () => wrapperCtx.slots.default?.();\n },\n }),\n );\n\n cache.set(segment, wrapper);\n\n return wrapper;\n}\n\nfunction wrapWithSuspense(content: VNode, fallback: unknown): VNode {\n if (fallback === undefined) {\n return content;\n }\n\n const fallbackContent =\n typeof fallback === \"function\" ? (fallback as () => VNode)() : fallback;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment\n const suspenseComponent = Suspense as any;\n\n return h(\n suspenseComponent,\n {},\n {\n default: () => content,\n fallback: () => fallbackContent,\n },\n );\n}\n\nconst emptyKeepAlivePlaceholder = markRaw(\n defineComponent({\n name: \"KeepAlive-placeholder\",\n render() {\n return null;\n },\n }),\n);\n\nfunction renderWithRootKA(\n activeChild: VNode,\n wrapperCache: Map<string, Component>,\n fallback: unknown,\n): VNode {\n const activeProps = activeChild.props as { segment?: string } | null;\n const segment = activeProps?.segment ?? \"__not-found__\";\n const WrapperComponent = getOrCreateWrapper(wrapperCache, segment);\n const slotContent = getSlotContent(activeChild) ?? [];\n const keepAliveContent = h(KeepAlive, null, {\n default: () =>\n h(WrapperComponent, { key: segment }, { default: () => slotContent }),\n });\n\n return wrapWithSuspense(keepAliveContent, fallback);\n}\n\n// Vue compiles boolean-shorthand template attributes (`<Match keepAlive>`) to\n// an empty string instead of `true`, and converts them to `true` only when the\n// receiving component's prop is declared with `type: Boolean`. `Match` is a\n// marker component (`render: null`) — its props are inspected on the VNode\n// without ever going through Vue's prop-casting pipeline, so the raw `\"\"` (or\n// the hyphenated attribute name) reaches us here. Accept the same trio Vue's\n// runtime does.\nfunction isKeepAliveEnabled(value: unknown): boolean {\n return value === true || value === \"\" || value === \"keep-alive\";\n}\n\nfunction renderWithPerMatchKA(\n activeChild: VNode,\n wrapperCache: Map<string, Component>,\n fallback: unknown,\n): VNode | null {\n const matchProps = activeChild.props as {\n segment?: string;\n keepAlive?: unknown;\n } | null;\n\n if (isKeepAliveEnabled(matchProps?.keepAlive) && activeChild.type === Match) {\n /* v8 ignore start */\n const segment = matchProps?.segment ?? \"__not-found__\";\n /* v8 ignore stop */\n const WrapperComponent = getOrCreateWrapper(wrapperCache, segment);\n const slotContent = getSlotContent(activeChild) ?? [];\n\n return h(Fragment, [\n h(KeepAlive, null, {\n default: () =>\n h(WrapperComponent, { key: segment }, { default: () => slotContent }),\n }),\n ]);\n }\n\n const content = getSlotContent(activeChild);\n\n /* v8 ignore start */\n if (!content) {\n return null;\n }\n /* v8 ignore stop */\n\n return h(Fragment, [\n h(KeepAlive, null, { default: () => h(emptyKeepAlivePlaceholder) }),\n wrapWithSuspense(h(Fragment, content), fallback),\n ]);\n}\n\nconst RouteViewComponent = defineComponent({\n name: \"RouteView\",\n props: {\n nodeName: {\n type: String,\n required: true,\n },\n keepAlive: {\n type: Boolean,\n default: false,\n },\n },\n setup(props, { slots }) {\n const routeContext = useRouteNode(props.nodeName);\n const wrapperCache = new Map<string, Component>();\n\n // Cache per-Match `keepAlive` detection by slot output identity. Slot\n // contents change reference only when the parent re-renders with new\n // children, so steady-state navigations skip the O(n) `.some(...)` scan.\n let lastSlotOutput: unknown = null;\n let lastHasPerMatchKA = false;\n\n function detectPerMatchKA(elements: VNode[], slotOutput: unknown): boolean {\n /* v8 ignore next 3 -- @preserve: Vue's compiled slot wrapper allocates a\n new array per render call in JSDOM tests; identity-cache hits in\n production where parent compiled templates share slot output, but\n is unobservable through TestBed-style assertions. */\n if (slotOutput === lastSlotOutput) {\n return lastHasPerMatchKA;\n }\n\n lastSlotOutput = slotOutput;\n lastHasPerMatchKA = elements.some(\n (element) =>\n element.type === Match &&\n isKeepAliveEnabled(\n (element.props as { keepAlive?: unknown } | null)?.keepAlive,\n ),\n );\n\n return lastHasPerMatchKA;\n }\n\n return (): VNode | null => {\n const route = routeContext.route.value;\n\n if (!route) {\n return null;\n }\n\n const slotOutput = slots.default?.();\n const elements: VNode[] = [];\n\n collectElements(slotOutput, elements);\n\n const { rendered, fallback } = buildRenderList(\n elements,\n route.name,\n props.nodeName,\n );\n\n if (rendered.length === 0) {\n return null;\n }\n\n const activeChild = rendered[0];\n\n if (props.keepAlive) {\n return renderWithRootKA(activeChild, wrapperCache, fallback);\n }\n\n /* v8 ignore start */\n if (\n activeChild.type !== Match &&\n activeChild.type !== Self &&\n activeChild.type !== NotFound\n ) {\n return null;\n }\n /* v8 ignore stop */\n\n const hasPerMatchKA = detectPerMatchKA(elements, slotOutput);\n\n if (hasPerMatchKA) {\n return renderWithPerMatchKA(activeChild, wrapperCache, fallback);\n }\n\n const content = getSlotContent(activeChild);\n\n if (!content) {\n return null;\n }\n\n return wrapWithSuspense(h(Fragment, content), fallback);\n };\n },\n});\n\nexport const RouteView = Object.assign(RouteViewComponent, {\n Match,\n Self,\n NotFound,\n});\n\nexport type {\n RouteViewProps,\n MatchProps as RouteViewMatchProps,\n SelfProps as RouteViewSelfProps,\n NotFoundProps as RouteViewNotFoundProps,\n} from \"./types\";\n","/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","import type { Router, State } from \"@real-router/core\";\n\nconst CLEAR_DELAY = 7000;\nconst SAFARI_READY_DELAY = 100;\nconst ANNOUNCER_ATTR = \"data-real-router-announcer\";\nconst INTERNAL_ROUTE_PREFIX = \"@@\";\nconst VISUALLY_HIDDEN =\n \"position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0\";\n\nexport interface RouteAnnouncerOptions {\n prefix?: string;\n getAnnouncementText?: (route: State) => string;\n}\n\nexport function createRouteAnnouncer(\n router: Router,\n options?: RouteAnnouncerOptions,\n): { destroy: () => void } {\n const prefix = options?.prefix ?? \"Navigated to \";\n const getCustomText = options?.getAnnouncementText;\n\n let isInitialNavigation = true;\n let isReady = false;\n let isDestroyed = false;\n let lastAnnouncedText = \"\";\n let pendingText: string | null = null;\n let clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const announcer = getOrCreateAnnouncer();\n\n const doAnnounce = (text: string, h1: HTMLElement | null): void => {\n lastAnnouncedText = text;\n clearTimeout(clearTimeoutId);\n announcer.textContent = text;\n clearTimeoutId = setTimeout(() => {\n announcer.textContent = \"\";\n lastAnnouncedText = \"\";\n }, CLEAR_DELAY);\n\n manageFocus(h1);\n };\n\n // Safari-ready delay: announcing before VoiceOver wires up the aria-live region\n // causes the first announcement to be silently dropped. Wait SAFARI_READY_DELAY ms\n // before marking the announcer \"ready\" — any navigation during that window is\n // buffered in pendingText and flushed once the delay expires.\n const safariTimeoutId = setTimeout(() => {\n isReady = true;\n\n if (pendingText !== null && !isDestroyed) {\n const text = pendingText;\n\n pendingText = null;\n doAnnounce(text, document.querySelector<HTMLElement>(\"h1\"));\n }\n }, SAFARI_READY_DELAY);\n\n const unsubscribe = router.subscribe(({ route }) => {\n if (isInitialNavigation) {\n isInitialNavigation = false;\n\n return;\n }\n\n // Double rAF: waits for two paint frames so the incoming route's DOM\n // (including the new <h1>) is fully rendered before resolveText reads it.\n // Single rAF fires before the new route's template has been attached,\n // which would cause resolveText to pick up the OLD h1 or fall back to\n // document.title / route.name prematurely.\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (isDestroyed) {\n return;\n }\n\n const h1 = document.querySelector<HTMLElement>(\"h1\");\n const text = resolveText(route, prefix, getCustomText, h1);\n\n if (!text || text === lastAnnouncedText) {\n return;\n }\n\n if (!isReady) {\n // Defer announcement until Safari-ready window elapses (see safariTimeoutId).\n pendingText = text;\n\n return;\n }\n\n doAnnounce(text, h1);\n });\n });\n });\n\n return {\n destroy() {\n isDestroyed = true;\n unsubscribe();\n clearTimeout(clearTimeoutId);\n clearTimeout(safariTimeoutId);\n removeAnnouncer();\n },\n };\n}\n\nfunction getOrCreateAnnouncer(): HTMLElement {\n const existing = document.querySelector<HTMLElement>(`[${ANNOUNCER_ATTR}]`);\n\n if (existing) {\n return existing;\n }\n\n const element = document.createElement(\"div\");\n\n element.setAttribute(\"style\", VISUALLY_HIDDEN);\n element.setAttribute(\"aria-live\", \"assertive\");\n element.setAttribute(\"aria-atomic\", \"true\");\n element.setAttribute(ANNOUNCER_ATTR, \"\");\n\n document.body.prepend(element);\n\n return element;\n}\n\nfunction removeAnnouncer(): void {\n document.querySelector(`[${ANNOUNCER_ATTR}]`)?.remove();\n}\n\nfunction resolveText(\n route: State,\n prefix: string,\n getCustomText: ((route: State) => string) | undefined,\n h1: HTMLElement | null,\n): string {\n if (getCustomText) {\n return getCustomText(route);\n }\n\n const h1Text = (h1?.textContent ?? \"\").trim();\n const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)\n ? \"\"\n : route.name;\n const rawText =\n h1Text || document.title || routeName || globalThis.location.pathname;\n\n return `${prefix}${rawText}`;\n}\n\nfunction manageFocus(h1: HTMLElement | null): void {\n if (!h1) {\n return;\n }\n\n if (!h1.hasAttribute(\"tabindex\")) {\n h1.setAttribute(\"tabindex\", \"-1\");\n }\n\n h1.focus({ preventScroll: true });\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst STORAGE_KEY = \"real-router:scroll\";\n\nconst NOOP_INSTANCE: { destroy: () => void } = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport type ScrollRestorationMode = \"restore\" | \"top\" | \"manual\";\n\nexport interface ScrollRestorationOptions {\n mode?: ScrollRestorationMode | undefined;\n anchorScrolling?: boolean | undefined;\n scrollContainer?: (() => HTMLElement | null) | undefined;\n}\n\ninterface NavigationContext {\n direction?: \"forward\" | \"back\" | \"unknown\";\n navigationType?: \"push\" | \"replace\" | \"traverse\" | \"reload\";\n}\n\nexport function createScrollRestoration(\n router: Router,\n options?: ScrollRestorationOptions,\n): { destroy: () => void } {\n if (typeof globalThis.window === \"undefined\") {\n return NOOP_INSTANCE;\n }\n\n const mode = options?.mode ?? \"restore\";\n\n // mode \"manual\" = utility does nothing. Don't flip history.scrollRestoration,\n // don't subscribe, don't register pagehide — leave the browser's native\n // auto-restore intact for the app to override if it wants to.\n if (mode === \"manual\") {\n return NOOP_INSTANCE;\n }\n\n const anchorEnabled = options?.anchorScrolling ?? true;\n const getContainer = options?.scrollContainer;\n\n const prevScrollRestoration = history.scrollRestoration;\n\n try {\n history.scrollRestoration = \"manual\";\n } catch {\n // Ignore — some embedded contexts may reject the assignment.\n }\n\n // Resolve the container lazily on every event so containers mounted AFTER\n // the provider still get correct scroll handling. Falls back to window when\n // the getter is absent or returns null (pre-mount).\n const readPos = (): number => {\n const element = getContainer?.();\n\n return element ? element.scrollTop : globalThis.scrollY;\n };\n\n const writePos = (top: number): void => {\n const element = getContainer?.();\n\n if (element) {\n element.scrollTop = top;\n } else {\n globalThis.scrollTo(0, top);\n }\n };\n\n const scrollToHashOrTop = (): void => {\n const hash = globalThis.location.hash;\n\n if (anchorEnabled && hash.length > 1) {\n // location.hash is percent-encoded; ids in the DOM are the raw string.\n // Decode for the match. Fall back to the raw slice if the hash contains\n // a malformed escape sequence (decodeURIComponent throws on those).\n let id: string;\n\n try {\n id = decodeURIComponent(hash.slice(1));\n } catch {\n id = hash.slice(1);\n }\n\n // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n const element = document.getElementById(id);\n\n if (element) {\n element.scrollIntoView();\n\n return;\n }\n }\n\n writePos(0);\n };\n\n let destroyed = false;\n\n const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n const nav = (route.context as { navigation?: NavigationContext })\n .navigation;\n\n // Browsers dispatch reload as the initial navigation after refresh, so\n // previousRoute is undefined and capture is naturally skipped. The\n // pre-refresh position was already persisted via pagehide.\n if (previousRoute) {\n putPos(keyOf(previousRoute), readPos());\n }\n\n // Single rAF so DOM is committed before we read anchors / write scroll.\n // Guard against destroy() racing with the callback.\n requestAnimationFrame(() => {\n if (destroyed) {\n return;\n }\n\n if (mode === \"top\" || !nav) {\n scrollToHashOrTop();\n\n return;\n }\n\n if (nav.navigationType === \"replace\") {\n return;\n }\n\n if (\n nav.direction === \"back\" ||\n nav.navigationType === \"traverse\" ||\n nav.navigationType === \"reload\"\n ) {\n writePos(loadStore()[keyOf(route)] ?? 0);\n\n return;\n }\n\n scrollToHashOrTop();\n });\n });\n\n const onPageHide = (): void => {\n const current = router.getState();\n\n if (current) {\n putPos(keyOf(current), readPos());\n }\n };\n\n globalThis.addEventListener(\"pagehide\", onPageHide);\n\n return {\n destroy: () => {\n if (destroyed) {\n return;\n }\n\n destroyed = true;\n unsubscribe();\n globalThis.removeEventListener(\"pagehide\", onPageHide);\n\n try {\n history.scrollRestoration = prevScrollRestoration;\n } catch {\n // Ignore.\n }\n },\n };\n}\n\nfunction keyOf(state: State): string {\n return `${state.name}:${canonicalJson(state.params)}`;\n}\n\nfunction loadStore(): Record<string, number> {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n\n return raw ? (JSON.parse(raw) as Record<string, number>) : {};\n } catch {\n return {};\n }\n}\n\nfunction putPos(key: string, pos: number): void {\n try {\n const store = loadStore();\n\n store[key] = pos;\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // Ignore quota / security errors.\n }\n}\n\nfunction canonicalJson(value: unknown): string {\n return JSON.stringify(value, canonicalReplacer);\n}\n\nfunction canonicalReplacer(_key: string, val: unknown): unknown {\n if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n const sorted: Record<string, unknown> = {};\n // eslint-disable-next-line unicorn/no-array-sort -- ng-packagr uses pre-ES2023 lib; toSorted unavailable\n const keys = Object.keys(val as Record<string, unknown>).sort(\n (left: string, right: string) => left.localeCompare(right),\n );\n\n for (const key of keys) {\n sorted[key] = (val as Record<string, unknown>)[key];\n }\n\n return sorted;\n }\n\n return val;\n}\n","import type { Router } from \"@real-router/core\";\n\nexport interface ViewTransitions {\n destroy: () => void;\n}\n\nconst NOOP_INSTANCE: ViewTransitions = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport function createViewTransitions(router: Router): ViewTransitions {\n if (\n typeof document === \"undefined\" ||\n typeof document.startViewTransition !== \"function\"\n ) {\n return NOOP_INSTANCE;\n }\n\n let closeVT: (() => void) | null = null;\n let currentVT: { skipTransition?: () => void } | null = null;\n // Tracks whether TRANSITION_SUCCESS fired for the current leave. Used to\n // distinguish \"benign cleanup abort\" (router's async path aborts its own\n // controller in a finally block after successful navigation) from \"real\n // cancellation\" (concurrent navigate, guard rejection, dispose).\n let successFired = false;\n\n const resolveAndClear = (): void => {\n closeVT?.();\n closeVT = null;\n };\n\n const offLeave = router.subscribeLeave(({ signal }) => {\n // Reentrant abort: signal already aborted when we're called. Open no VT\n // — router will fall through to TRANSITION_CANCELLED via isCurrentNav()\n // after leave resolves. addEventListener(\"abort\", ...) does not re-fire\n // for past events, so skipping startViewTransition is the safe path.\n if (signal.aborted) {\n return;\n }\n\n successFired = false;\n resolveAndClear();\n\n // Return a Promise so the router awaits until the browser invokes\n // updateCallback. This ensures old DOM snapshot is captured BEFORE the\n // router commits the new state — giving correct exit→state→entry\n // ordering (vs fire-and-forget, where URL changes before VT captures).\n return new Promise<void>((resolveLeave) => {\n // Capture the resolver synchronously BEFORE startViewTransition() is\n // called. The browser invokes updateCallback in a later task, but\n // router.subscribe (TRANSITION_SUCCESS) can fire before that. If we\n // captured `resolve` inside the callback, subscribe would see closeVT\n // still null and skip resolving — the deferred would hang for 4s\n // until the VT API aborts with TimeoutError.\n const deferred = new Promise<void>((resolve) => {\n closeVT = resolve;\n });\n\n signal.addEventListener(\n \"abort\",\n () => {\n if (successFired) {\n // Router's async path (#finishAsyncNavigation) aborts its own\n // controller in a finally block AFTER completeTransition (and\n // thus AFTER subscribe fired). This is cleanup, not\n // cancellation — VT is progressing normally, do nothing.\n return;\n }\n\n // Real cancellation (concurrent navigate, dispose). Resolve the\n // deferred so updateCallback can complete, skip the VT so no\n // stale animation leaks, and unblock the router if the abort\n // fires before updateCallback was invoked.\n resolveAndClear();\n currentVT?.skipTransition?.();\n resolveLeave();\n },\n { once: true },\n );\n\n try {\n currentVT = document.startViewTransition(() => {\n // Resolving here unblocks the router at the moment the browser\n // enters updateCallback — by spec, old DOM snapshot is captured\n // before this callback runs. Router now proceeds through\n // activation guards and setState; the VT animation waits on\n // `deferred`, which is resolved from router.subscribe after a\n // task-queue tick (see NOTE on setTimeout below).\n resolveLeave();\n\n return deferred;\n });\n } catch {\n // Defensive: spec says startViewTransition doesn't throw under\n // normal conditions, but Chromium has had edge cases (detached\n // document, extension interference). Clean up and unblock router.\n resolveAndClear();\n resolveLeave();\n }\n });\n });\n\n const offSuccess = router.subscribe(() => {\n const resolver = closeVT;\n\n successFired = true;\n closeVT = null;\n\n if (resolver === null) {\n currentVT = null;\n } else {\n // CRITICAL: CANNOT use requestAnimationFrame here. When the router\n // takes the async path (leave returned a Promise), subscribe fires\n // AFTER the browser has already transitioned VT into the\n // \"update-callback-called\" phase. In that phase Chromium sets\n // rendering suppression to true, which ALSO blocks rAF callbacks.\n // rAF would never fire → deferred never resolves → browser aborts\n // vt.ready with TimeoutError after 4s (observed in Chromium).\n //\n // setTimeout runs on the task queue independent of the rendering\n // pipeline, so it fires regardless of suppression. React's scheduler\n // uses MessageChannel tasks, which are queued before our setTimeout,\n // so the new DOM is committed by the time our callback runs.\n setTimeout(() => {\n resolver();\n currentVT = null;\n }, 0);\n }\n });\n\n return {\n destroy: () => {\n offLeave();\n offSuccess();\n currentVT?.skipTransition?.();\n currentVT = null;\n resolveAndClear();\n },\n };\n}\n","import type { Router, Params } from \"@real-router/core\";\n\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 &&\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n\ntype BuildUrlFn = (name: string, params: Params) => string | undefined;\n\nexport function buildHref(\n router: Router,\n routeName: string,\n routeParams: Params,\n): string | undefined {\n try {\n const buildUrl = router.buildUrl as BuildUrlFn | undefined;\n\n if (buildUrl) {\n const url = buildUrl(routeName, routeParams);\n\n if (url !== undefined) {\n return url;\n }\n }\n\n return router.buildPath(routeName, routeParams);\n } catch {\n console.error(\n `[real-router] Route \"${routeName}\" is not defined. The element will render without an href attribute.`,\n );\n\n return undefined;\n }\n}\n\nfunction parseTokens(value: string | undefined): string[] {\n return value ? (value.match(/\\S+/g) ?? []) : [];\n}\n\nexport function buildActiveClassName(\n isActive: boolean,\n activeClassName: string | undefined,\n baseClassName: string | undefined,\n): string | undefined {\n if (isActive && activeClassName) {\n const activeTokens = parseTokens(activeClassName);\n\n if (activeTokens.length === 0) {\n return baseClassName ?? undefined;\n }\n if (!baseClassName) {\n return activeTokens.join(\" \");\n }\n\n const baseTokens = parseTokens(baseClassName);\n const seen = new Set(baseTokens);\n\n for (const token of activeTokens) {\n if (!seen.has(token)) {\n seen.add(token);\n baseTokens.push(token);\n }\n }\n\n return baseTokens.join(\" \");\n }\n\n return baseClassName ?? undefined;\n}\n\nexport function shallowEqual(\n prev: object | undefined,\n next: object | undefined,\n): boolean {\n if (Object.is(prev, next)) {\n return true;\n }\n if (!prev || !next) {\n return false;\n }\n\n const prevKeys = Object.keys(prev);\n\n if (prevKeys.length !== Object.keys(next).length) {\n return false;\n }\n\n const prevRecord = prev as Record<string, unknown>;\n const nextRecord = next as Record<string, unknown>;\n\n for (const key of prevKeys) {\n if (!Object.is(prevRecord[key], nextRecord[key])) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function applyLinkA11y(element: HTMLElement | null | undefined): void {\n if (!element) {\n return;\n }\n if (\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLButtonElement\n ) {\n return;\n }\n if (!element.hasAttribute(\"role\")) {\n element.setAttribute(\"role\", \"link\");\n }\n if (!element.hasAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n }\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { defineComponent, h, computed, shallowRef, watch } from \"vue\";\n\nimport { useRouter } from \"../composables/useRouter\";\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport { shouldNavigate, buildHref, buildActiveClassName } from \"../dom-utils\";\n\nimport type { Params, NavigationOptions } from \"@real-router/core\";\nimport type { PropType } from \"vue\";\n\ntype OnClickHandler = (evt: MouseEvent) => void;\n\n/**\n * Vue's compiled template binds multiple `@click` handlers as an array.\n * Single render-function `onClick` is a function. Both must be invoked.\n */\nfunction invokeAttributesOnClick(value: unknown, evt: MouseEvent): void {\n if (typeof value === \"function\") {\n (value as OnClickHandler)(evt);\n\n return;\n }\n if (Array.isArray(value)) {\n const handlers = value as OnClickHandler[];\n\n for (const fn of handlers) {\n if (typeof fn === \"function\") {\n fn(evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n }\n }\n}\n\nexport const Link = defineComponent({\n name: \"Link\",\n // Disable Vue's automatic attribute fallthrough. Without this, attrs.onClick\n // (function OR array) is auto-attached as a native click listener AND our\n // explicit onClick fires too — user handlers are double-invoked. We invoke\n // attrs.onClick manually inside handleClick to preserve preventDefault.\n inheritAttrs: false,\n props: {\n routeName: {\n type: String,\n required: true,\n },\n routeParams: {\n type: Object as PropType<Params>,\n default: () => EMPTY_PARAMS,\n },\n routeOptions: {\n type: Object as PropType<NavigationOptions>,\n default: () => EMPTY_OPTIONS,\n },\n class: {\n type: String,\n default: undefined,\n },\n activeClassName: {\n type: String,\n default: \"active\",\n },\n activeStrict: {\n type: Boolean,\n default: false,\n },\n ignoreQueryParams: {\n type: Boolean,\n default: true,\n },\n target: {\n type: String,\n default: undefined,\n },\n },\n setup(props, { slots, attrs }) {\n const router = useRouter();\n\n const isActive = shallowRef(false);\n\n // watch with an explicit dep getter recreates the source ONLY when\n // routeName/routeParams/strict/ignoreQueryParams change — not on every\n // reactive read inside the source factory (the watchEffect alternative\n // would also re-subscribe whenever isActive itself changed).\n watch(\n () =>\n [\n props.routeName,\n props.routeParams,\n props.activeStrict,\n props.ignoreQueryParams,\n ] as const,\n (\n [routeName, routeParams, activeStrict, ignoreQueryParams],\n _prev,\n onCleanup,\n ) => {\n const source = createActiveRouteSource(router, routeName, routeParams, {\n strict: activeStrict,\n ignoreQueryParams,\n });\n\n isActive.value = source.getSnapshot();\n\n const unsub = source.subscribe(() => {\n isActive.value = source.getSnapshot();\n });\n\n onCleanup(unsub);\n },\n { immediate: true, flush: \"sync\" },\n );\n\n const href = computed(() =>\n buildHref(router, props.routeName, props.routeParams),\n );\n\n const finalClassName = computed(() =>\n buildActiveClassName(isActive.value, props.activeClassName, props.class),\n );\n\n const handleClick = (evt: MouseEvent) => {\n // Vue allows attrs.onClick to be a function or an array of functions\n // (compiled templates with multiple @click bindings produce arrays).\n // Both must be invoked; treating arrays as \"no handler\" silently drops\n // user code.\n if (attrs.onClick !== undefined && attrs.onClick !== null) {\n invokeAttributesOnClick(attrs.onClick, evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n if (!shouldNavigate(evt) || props.target === \"_blank\") {\n return;\n }\n\n evt.preventDefault();\n router\n .navigate(props.routeName, props.routeParams, props.routeOptions)\n .catch(() => {});\n };\n\n return () => {\n // Build forwarded attrs without `onClick`. Vue's runtime auto-attaches\n // attrs.onClick (function OR array) as a native DOM listener, which would\n // double-invoke user handlers when combined with our explicit `onClick`.\n // We invoke the original attrs.onClick manually inside handleClick so the\n // preventDefault contract is preserved.\n const restAttributes: Record<string, unknown> = {};\n\n for (const key of Object.keys(attrs)) {\n if (key !== \"onClick\") {\n restAttributes[key] = (attrs as Record<string, unknown>)[key];\n }\n }\n\n return h(\n \"a\",\n {\n ...restAttributes,\n href: href.value,\n class: finalClassName.value,\n target: props.target,\n onClick: handleClick,\n },\n slots.default?.(),\n );\n };\n },\n});\n","import { createDismissableError } from \"@real-router/sources\";\nimport { defineComponent, h, watch, Fragment } from \"vue\";\n\nimport { useRouter } from \"../composables/useRouter\";\nimport { useRefFromSource } from \"../useRefFromSource\";\n\nimport type { RouterError, State } from \"@real-router/core\";\nimport type { VNode, PropType } from \"vue\";\n\nexport const RouterErrorBoundary = defineComponent({\n name: \"RouterErrorBoundary\",\n props: {\n fallback: {\n type: Function as PropType<\n (error: RouterError, resetError: () => void) => VNode\n >,\n required: true,\n },\n onError: {\n type: Function as PropType<\n (\n error: RouterError,\n toRoute: State | null,\n fromRoute: State | null,\n ) => void\n >,\n default: undefined,\n },\n },\n setup(props, { slots }) {\n const router = useRouter();\n const snapshot = useRefFromSource(createDismissableError(router));\n\n watch(\n () => snapshot.value.version,\n () => {\n if (snapshot.value.error) {\n props.onError?.(\n snapshot.value.error,\n snapshot.value.toRoute,\n snapshot.value.fromRoute,\n );\n }\n },\n { immediate: true },\n );\n\n return () => {\n const children = slots.default?.() ?? [];\n const errorVNode = snapshot.value.error\n ? props.fallback(snapshot.value.error, snapshot.value.resetError)\n : null;\n\n return h(Fragment, null, [...children, errorVNode]);\n };\n },\n});\n\nexport type RouterErrorBoundaryProps = InstanceType<\n typeof RouterErrorBoundary\n>[\"$props\"];\n","import { shouldNavigate, applyLinkA11y } from \"../dom-utils\";\n\nimport type { Router, NavigationOptions, Params } from \"@real-router/core\";\nimport type { Directive } from \"vue\";\n\nexport interface LinkDirectiveValue {\n name: string;\n params?: Params;\n options?: NavigationOptions;\n}\n\n/**\n * Router stack for nested RouterProviders. The active router is the top of\n * the stack. RouterProvider pushes its router on mount and pops it on unmount,\n * which preserves the parent context when an inner provider tears down.\n *\n * Without the stack, an unmounted child provider would leave the directive\n * pointing at a disposed router, and v-link in the still-mounted parent would\n * navigate via the wrong (or torn-down) instance.\n */\nconst routerStack: Router[] = [];\n\n/**\n * Pushes a router onto the active stack. Returns a release function that\n * removes that exact router from the stack regardless of position — safe\n * across out-of-order provider unmount sequences.\n *\n * @internal Used by RouterProvider during setup/teardown.\n */\nexport function pushDirectiveRouter(router: Router): () => void {\n routerStack.push(router);\n\n return () => {\n const idx = routerStack.lastIndexOf(router);\n\n if (idx !== -1) {\n routerStack.splice(idx, 1);\n }\n };\n}\n\n/**\n * Backwards-compatible alias. Replaces the active router unconditionally and\n * does NOT participate in the stack — use {@link pushDirectiveRouter} from\n * provider code instead. Kept for tests and direct callers.\n */\nexport function setDirectiveRouter(router: Router | null): void {\n if (router === null) {\n routerStack.length = 0;\n\n return;\n }\n if (routerStack.length === 0) {\n routerStack.push(router);\n\n return;\n }\n\n routerStack[routerStack.length - 1] = router;\n}\n\nexport function getDirectiveRouter(): Router {\n const top = routerStack.at(-1);\n\n if (!top) {\n throw new Error(\n \"v-link directive requires a RouterProvider ancestor. Make sure RouterProvider is mounted.\",\n );\n }\n\n return top;\n}\n\ninterface Handlers {\n click: (evt: MouseEvent) => void;\n keydown: (evt: KeyboardEvent) => void;\n}\n\n// Single WeakMap halves per-element bookkeeping vs two parallel maps.\nconst handlers = new WeakMap<HTMLElement, Handlers>();\n\n/**\n * Validates a directive binding value before attaching handlers.\n * Returns false (and warns once per call) when the value is missing or\n * has no `name` — silently doing nothing is preferable to a runtime crash\n * inside a click handler.\n */\nfunction isValidBinding(value: unknown): value is LinkDirectiveValue {\n if (value === null || value === undefined) {\n console.error(\n \"[real-router] v-link directive received null/undefined value. The element will not be wired for navigation.\",\n );\n\n return false;\n }\n if (typeof (value as { name?: unknown }).name !== \"string\") {\n console.error(\n \"[real-router] v-link directive value is missing a string `name` field. The element will not be wired for navigation.\",\n );\n\n return false;\n }\n\n return true;\n}\n\nfunction createClickHandler(\n router: Router,\n value: LinkDirectiveValue,\n): (evt: MouseEvent) => void {\n return (evt: MouseEvent) => {\n if (!shouldNavigate(evt)) {\n return;\n }\n\n evt.preventDefault();\n router\n .navigate(value.name, value.params ?? {}, value.options ?? {})\n .catch(() => {});\n };\n}\n\nfunction createKeydownHandler(\n router: Router,\n value: LinkDirectiveValue,\n element: HTMLElement,\n): (evt: KeyboardEvent) => void {\n return (evt: KeyboardEvent) => {\n if (evt.key === \"Enter\" && !(element instanceof HTMLButtonElement)) {\n router\n .navigate(value.name, value.params ?? {}, value.options ?? {})\n .catch(() => {});\n }\n };\n}\n\nfunction attachHandlers(\n element: HTMLElement,\n router: Router,\n value: LinkDirectiveValue,\n): void {\n const click = createClickHandler(router, value);\n const keydown = createKeydownHandler(router, value, element);\n\n element.addEventListener(\"click\", click);\n element.addEventListener(\"keydown\", keydown);\n\n handlers.set(element, { click, keydown });\n}\n\nfunction detachHandlers(element: HTMLElement): void {\n const entry = handlers.get(element);\n\n if (entry) {\n element.removeEventListener(\"click\", entry.click);\n element.removeEventListener(\"keydown\", entry.keydown);\n handlers.delete(element);\n }\n}\n\nexport const vLink: Directive<HTMLElement, LinkDirectiveValue> = {\n mounted(element, binding) {\n const router = getDirectiveRouter();\n\n applyLinkA11y(element);\n\n element.style.cursor = \"pointer\";\n\n if (!isValidBinding(binding.value)) {\n return;\n }\n\n attachHandlers(element, router, binding.value);\n },\n\n updated(element, binding) {\n const router = getDirectiveRouter();\n\n detachHandlers(element);\n\n if (!isValidBinding(binding.value)) {\n return;\n }\n\n attachHandlers(element, router, binding.value);\n },\n\n beforeUnmount(element) {\n detachHandlers(element);\n },\n};\n","import { inject } from \"vue\";\n\nimport { NavigatorKey } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = inject(NavigatorKey);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","import { getPluginApi } from \"@real-router/core/api\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n","import { inject } from \"vue\";\n\nimport { RouteKey } from \"../context\";\n\nimport type { RouteContext } from \"../types\";\nimport type { Params } from \"@real-router/core\";\n\nexport const useRoute = <P extends Params = Params>(): RouteContext<P> => {\n const routeContext = inject(RouteKey);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouterProvider\");\n }\n\n return routeContext as RouteContext<P>;\n};\n","import { getTransitionSource } from \"@real-router/sources\";\n\nimport { useRefFromSource } from \"../useRefFromSource\";\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouterTransitionSnapshot } from \"@real-router/sources\";\nimport type { ShallowRef } from \"vue\";\n\nexport function useRouterTransition(): ShallowRef<RouterTransitionSnapshot> {\n const router = useRouter();\n const source = getTransitionSource(router);\n\n return useRefFromSource(source);\n}\n","import { onScopeDispose } from \"vue\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { State } from \"@real-router/core\";\n\nexport interface RouteExitContext {\n /** The route being left. */\n route: State;\n /** The route being navigated to. */\n nextRoute: State;\n /**\n * AbortSignal that fires when this navigation is superseded by a later\n * one (rapid clicks). Already filtered: when the handler runs,\n * `signal.aborted` is guaranteed to be `false`. Use\n * `signal.addEventListener(\"abort\", cleanup, { once: true })` for\n * cleanup that must run on cancellation.\n */\n signal: AbortSignal;\n}\n\nexport interface UseRouteExitOptions {\n /**\n * Skip the handler when `route.name === nextRoute.name`\n * (sort/filter/query-only navigations on the same route). Default:\n * `true`.\n */\n skipSameRoute?: boolean;\n}\n\nexport type RouteExitHandler = (\n context: RouteExitContext,\n) => void | Promise<void>;\n\n/**\n * Subscribe to the router's leave-window with the universal guards baked\n * in. Wraps `router.subscribeLeave` so consumers don't repeat the same\n * boilerplate every time:\n *\n * - **Reentrant abort pre-check**: if `signal.aborted` is already `true`\n * when the handler would run (rapid navigation superseded a slower\n * one), the handler is skipped entirely.\n * - **Same-route skip**: by default, `route.name === nextRoute.name`\n * short-circuits the handler — query-only navigations skip the work.\n * Opt out with `skipSameRoute: false`.\n *\n * Cleanup is bound to the component's effect scope via `onScopeDispose`.\n *\n * If the handler returns a Promise, the router blocks on it. If the\n * Promise resolves, navigation proceeds. If it rejects, the router emits\n * `TRANSITION_CANCELLED`.\n *\n * **Handler reactivity (Vue):** Vue composables run **once** during\n * `setup()`; `handler` is captured in closure at the call site. To vary\n * behavior over time, read refs/computeds inside the handler body — do\n * not rely on swapping the handler reference.\n *\n * @example Animation\n * ```ts\n * const ref = useTemplateRef<HTMLDivElement>(\"box\");\n *\n * useRouteExit(async ({ signal }) => {\n * const el = ref.value;\n * if (!el) return;\n * el.classList.add(\"fade-out\");\n * const cleanup = () => el.classList.remove(\"fade-out\");\n * signal.addEventListener(\"abort\", cleanup, { once: true });\n * try {\n * el.getBoundingClientRect();\n * await Promise.allSettled(el.getAnimations().map((a) => a.finished));\n * } finally {\n * cleanup();\n * }\n * });\n * ```\n *\n * @example Auto-save form draft\n * ```ts\n * useRouteExit(async ({ signal }) => {\n * if (formState.value.dirty) {\n * await api.saveDraft(formState.value, { signal });\n * }\n * });\n * ```\n *\n * @example Reading rich transition metadata via `nextRoute.transition`\n * ```ts\n * useRouteExit(({ route, nextRoute }) => {\n * if (nextRoute.transition.segments.deactivated.includes(\"products\")) {\n * productCache.clear();\n * }\n * if (nextRoute.transition.redirected) return;\n * });\n * ```\n */\nexport function useRouteExit(\n handler: RouteExitHandler,\n options?: UseRouteExitOptions,\n): void {\n const router = useRouter();\n const skipSameRoute = options?.skipSameRoute ?? true;\n\n const off = router.subscribeLeave(({ route, nextRoute, signal }) => {\n if (skipSameRoute && route.name === nextRoute.name) {\n return;\n }\n\n if (signal.aborted) {\n return;\n }\n\n return handler({ route, nextRoute, signal });\n });\n\n onScopeDispose(off);\n}\n","import { watch } from \"vue\";\n\nimport { useRoute } from \"./useRoute\";\n\nimport type { State } from \"@real-router/core\";\n\nexport interface RouteEnterContext {\n /** The route that was just activated. */\n route: State;\n /** The route that was active immediately before this navigation. */\n previousRoute: State;\n}\n\nexport type RouteEnterHandler = (context: RouteEnterContext) => void;\n\nexport interface UseRouteEnterOptions {\n /**\n * Skip the handler when `route.name === previousRoute.name`\n * (sort/filter/query-only navigations on the same route). Default:\n * `true`. Symmetric with `useRouteExit`'s same-name option.\n */\n skipSameRoute?: boolean;\n}\n\n/**\n * Fire `handler` once when the component mounts as a result of a\n * navigation. Mirror of `useRouteExit` for the entry side.\n *\n * What this composable covers that an ad-hoc `watch` + `useRoute()`\n * doesn't:\n *\n * - **Skip-initial**: `watch` (with default `immediate: false`) plus the\n * explicit `route.transition.from` guard ensures the handler doesn't\n * fire on the very first state committed by `router.start()`.\n * - **Same-route skip** (default): handler is skipped when\n * `route.transition.from === route.name`. Sort/filter/query-only\n * navigations re-trigger the watcher, but they are not \"entries\" in\n * the animation / analytics sense. Opt out with\n * `skipSameRoute: false`.\n * - **Mount-time `route` / `previousRoute` snapshot**: handler receives\n * the values that were live at the moment of the new state, not the\n * latest ones (which may have moved on if the user navigated again\n * before the watcher drained).\n *\n * **Handler reactivity (Vue):** Vue composables run **once** during\n * `setup()`; `handler` is captured in closure at the call site. To vary\n * behavior over time, read refs/computeds inside the handler body.\n *\n * @example Direction-aware entry animation\n * ```ts\n * useRouteEnter(({ route }) => {\n * const direction = route.context.browser?.direction;\n * ref.value?.classList.add(\n * direction === \"back\" ? \"slide-from-left\" : \"slide-from-right\",\n * );\n * });\n * ```\n *\n * @example Analytics page-enter event (skip-initial built-in)\n * ```ts\n * useRouteEnter(({ route, previousRoute }) => {\n * analytics.track(\"page_enter\", {\n * route: route.name,\n * from: previousRoute.name,\n * });\n * });\n * ```\n *\n * @example Reading rich transition metadata via `route.transition`\n * ```ts\n * useRouteEnter(({ route }) => {\n * if (route.transition.redirected) {\n * showToast(`Redirected from ${route.transition.from}`);\n * }\n * });\n * ```\n */\nexport function useRouteEnter(\n handler: RouteEnterHandler,\n options?: UseRouteEnterOptions,\n): void {\n const { route, previousRoute } = useRoute();\n const skipSameRoute = options?.skipSameRoute ?? true;\n let lastHandledRoute: State | null = null;\n\n watch(route, (newRoute) => {\n const prev = previousRoute.value;\n\n // Early-exit guards, top-down:\n //\n // - **Defensive + skip-initial**: `route` may be undefined during\n // SSR / pre-start hydration; `!transition.from` would catch the\n // first commit from `router.start()`. Vue's `watch` (default\n // `immediate: false`) does not fire on the initial state, so\n // both are unreachable in Vue (covered by React/Preact tests).\n // - **Skip-same-route**: query-only navigations have\n // `transition.from === route.name`. Opt-out via\n // `skipSameRoute: false`.\n // - **Defensive dedupe + missing `previousRoute`**: same `route`\n // ref between watcher activations is unexpected on Vue (driven\n // off ref identity); `!prev` is unreachable once\n // `transition.from` is set (core populates them together). Both\n // kept for parity with React; v8-ignored.\n /* v8 ignore start */\n if (!newRoute) {\n return;\n }\n if (!newRoute.transition.from) {\n return;\n }\n /* v8 ignore stop */\n if (skipSameRoute && newRoute.transition.from === newRoute.name) {\n return;\n }\n /* v8 ignore start */\n if (lastHandledRoute === newRoute || !prev) {\n return;\n }\n /* v8 ignore stop */\n\n lastHandledRoute = newRoute;\n handler({ route: newRoute, previousRoute: prev });\n });\n}\n","// packages/vue/src/setupRouteProvision.ts\n\nimport { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { shallowRef } from \"vue\";\n\nimport type { Router, Navigator, State } from \"@real-router/core\";\nimport type { ShallowRef } from \"vue\";\n\n/**\n * Shared setup for `RouterProvider` (component-scoped) and\n * `createRouterPlugin` (app-scoped). Builds the reactive route refs +\n * subscription bookkeeping in one place; callers wire the result into their\n * provide/inject mechanism and own teardown lifecycle.\n *\n * @internal\n */\nexport interface RouteProvision {\n navigator: Navigator;\n route: ShallowRef<State | undefined>;\n previousRoute: ShallowRef<State | undefined>;\n /** Call when the owning scope tears down to release the router subscription. */\n unsubscribe: () => void;\n}\n\nexport function setupRouteProvision(router: Router): RouteProvision {\n const navigator = getNavigator(router);\n const source = createRouteSource(router);\n const initial = source.getSnapshot();\n\n const route = shallowRef<State | undefined>(initial.route);\n const previousRoute = shallowRef<State | undefined>(initial.previousRoute);\n\n const unsubscribe = source.subscribe(() => {\n const snapshot = source.getSnapshot();\n\n route.value = snapshot.route;\n previousRoute.value = snapshot.previousRoute;\n });\n\n return { navigator, route, previousRoute, unsubscribe };\n}\n","import { NavigatorKey, RouteKey, RouterKey } from \"./context\";\nimport { pushDirectiveRouter } from \"./directives/vLink\";\nimport { setupRouteProvision } from \"./setupRouteProvision\";\n\nimport type { Router } from \"@real-router/core\";\nimport type { App, Plugin } from \"vue\";\n\nexport function createRouterPlugin(router: Router): Plugin<[]> {\n return {\n install(app): void {\n const releaseDirective = pushDirectiveRouter(router);\n\n const { navigator, route, previousRoute, unsubscribe } =\n setupRouteProvision(router);\n\n // Vue 3.5+ exposes app.onUnmount for plugin cleanup.\n // On older versions (3.3–3.4), the subscription is cleaned up\n // when the router is garbage-collected (same as vue-router).\n if (\"onUnmount\" in app) {\n (app as App & { onUnmount: (fn: () => void) => void }).onUnmount(() => {\n releaseDirective();\n unsubscribe();\n });\n }\n\n app.provide(RouterKey, router);\n app.provide(NavigatorKey, navigator);\n app.provide(RouteKey, { navigator, route, previousRoute });\n },\n };\n}\n","import { defineComponent, onScopeDispose, provide, watch } from \"vue\";\n\nimport { NavigatorKey, RouteKey, RouterKey } from \"./context\";\nimport { pushDirectiveRouter } from \"./directives/vLink\";\nimport {\n createRouteAnnouncer,\n createScrollRestoration,\n createViewTransitions,\n} from \"./dom-utils\";\nimport { setupRouteProvision } from \"./setupRouteProvision\";\n\nimport type { ScrollRestorationOptions } from \"./dom-utils\";\nimport type { Router } from \"@real-router/core\";\nimport type { PropType } from \"vue\";\n\nexport const RouterProvider = defineComponent({\n name: \"RouterProvider\",\n props: {\n router: {\n type: Object as PropType<Router>,\n required: true,\n },\n announceNavigation: {\n type: Boolean,\n default: false,\n },\n scrollRestoration: {\n type: Object as PropType<ScrollRestorationOptions>,\n },\n viewTransitions: {\n type: Boolean,\n default: false,\n },\n },\n setup(props, { slots }) {\n // Reactive announceNavigation: setting prop true/false at runtime now\n // creates/destroys the announcer accordingly. Prior implementation read\n // the prop only inside onMounted, so toggling it post-mount silently no-op'd.\n watch(\n () => [props.router, props.announceNavigation] as const,\n ([router, enabled], _prev, onCleanup) => {\n if (!enabled) {\n return;\n }\n\n const announcer = createRouteAnnouncer(router);\n\n onCleanup(() => {\n announcer.destroy();\n });\n },\n { immediate: true },\n );\n\n // Watch by primitives so inline `{ mode: \"restore\" }` doesn't thrash.\n // scrollContainer is a getter invoked lazily on every event inside the\n // utility — swapping its reference doesn't change the resolved element,\n // so we intentionally omit it from watched sources.\n watch(\n () =>\n [\n props.router,\n props.scrollRestoration !== undefined,\n props.scrollRestoration?.mode,\n props.scrollRestoration?.anchorScrolling,\n ] as const,\n ([router, enabled, mode, anchorScrolling], _prev, onCleanup) => {\n if (!enabled) {\n return;\n }\n\n const sr = createScrollRestoration(router, {\n mode,\n anchorScrolling,\n scrollContainer: props.scrollRestoration?.scrollContainer,\n });\n\n onCleanup(() => {\n sr.destroy();\n });\n },\n { immediate: true },\n );\n\n // Reactive viewTransitions: toggling prop creates/destroys the utility.\n watch(\n () => [props.router, props.viewTransitions] as const,\n ([router, enabled], _prev, onCleanup) => {\n if (!enabled) {\n return;\n }\n\n const vt = createViewTransitions(router);\n\n onCleanup(() => {\n vt.destroy();\n });\n },\n { immediate: true },\n );\n\n // Push this provider's router on the v-link directive stack so nested\n // RouterProviders behave like nested DI scopes (LIFO). Release on unmount\n // restores the outer router for any v-link still mounted in the parent.\n const releaseDirective = pushDirectiveRouter(props.router);\n\n const { navigator, route, previousRoute, unsubscribe } =\n setupRouteProvision(props.router);\n\n onScopeDispose(() => {\n releaseDirective();\n unsubscribe();\n });\n\n provide(RouterKey, props.router);\n provide(NavigatorKey, navigator);\n provide(RouteKey, { navigator, route, previousRoute });\n\n return () => slots.default?.();\n },\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "Vue 3 integration for Real-Router",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"license": "MIT",
|
|
51
51
|
"sideEffects": false,
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@real-router/core": "^0.50.
|
|
53
|
+
"@real-router/core": "^0.50.2",
|
|
54
54
|
"@real-router/route-utils": "^0.2.1",
|
|
55
55
|
"@real-router/sources": "^0.7.2"
|
|
56
56
|
},
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@testing-library/jest-dom": "6.9.1",
|
|
59
59
|
"@vue/test-utils": "2.4.6",
|
|
60
60
|
"vue": "3.5.13",
|
|
61
|
-
"@real-router/browser-plugin": "^0.
|
|
61
|
+
"@real-router/browser-plugin": "^0.16.0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"vue": ">=3.3.0"
|
package/src/RouterProvider.ts
CHANGED
|
@@ -2,7 +2,11 @@ import { defineComponent, onScopeDispose, provide, watch } from "vue";
|
|
|
2
2
|
|
|
3
3
|
import { NavigatorKey, RouteKey, RouterKey } from "./context";
|
|
4
4
|
import { pushDirectiveRouter } from "./directives/vLink";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createRouteAnnouncer,
|
|
7
|
+
createScrollRestoration,
|
|
8
|
+
createViewTransitions,
|
|
9
|
+
} from "./dom-utils";
|
|
6
10
|
import { setupRouteProvision } from "./setupRouteProvision";
|
|
7
11
|
|
|
8
12
|
import type { ScrollRestorationOptions } from "./dom-utils";
|
|
@@ -23,6 +27,10 @@ export const RouterProvider = defineComponent({
|
|
|
23
27
|
scrollRestoration: {
|
|
24
28
|
type: Object as PropType<ScrollRestorationOptions>,
|
|
25
29
|
},
|
|
30
|
+
viewTransitions: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: false,
|
|
33
|
+
},
|
|
26
34
|
},
|
|
27
35
|
setup(props, { slots }) {
|
|
28
36
|
// Reactive announceNavigation: setting prop true/false at runtime now
|
|
@@ -74,6 +82,23 @@ export const RouterProvider = defineComponent({
|
|
|
74
82
|
{ immediate: true },
|
|
75
83
|
);
|
|
76
84
|
|
|
85
|
+
// Reactive viewTransitions: toggling prop creates/destroys the utility.
|
|
86
|
+
watch(
|
|
87
|
+
() => [props.router, props.viewTransitions] as const,
|
|
88
|
+
([router, enabled], _prev, onCleanup) => {
|
|
89
|
+
if (!enabled) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const vt = createViewTransitions(router);
|
|
94
|
+
|
|
95
|
+
onCleanup(() => {
|
|
96
|
+
vt.destroy();
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
{ immediate: true },
|
|
100
|
+
);
|
|
101
|
+
|
|
77
102
|
// Push this provider's router on the v-link directive stack so nested
|
|
78
103
|
// RouterProviders behave like nested DI scopes (LIFO). Release on unmount
|
|
79
104
|
// restores the outer router for any v-link still mounted in the parent.
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
Suspense,
|
|
8
8
|
} from "vue";
|
|
9
9
|
|
|
10
|
-
import { Match, NotFound } from "./components";
|
|
10
|
+
import { Match, NotFound, Self } from "./components";
|
|
11
11
|
import { buildRenderList, collectElements } from "./helpers";
|
|
12
12
|
import { useRouteNode } from "../../composables/useRouteNode";
|
|
13
13
|
|
|
@@ -214,7 +214,11 @@ const RouteViewComponent = defineComponent({
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
/* v8 ignore start */
|
|
217
|
-
if (
|
|
217
|
+
if (
|
|
218
|
+
activeChild.type !== Match &&
|
|
219
|
+
activeChild.type !== Self &&
|
|
220
|
+
activeChild.type !== NotFound
|
|
221
|
+
) {
|
|
218
222
|
return null;
|
|
219
223
|
}
|
|
220
224
|
/* v8 ignore stop */
|
|
@@ -236,10 +240,15 @@ const RouteViewComponent = defineComponent({
|
|
|
236
240
|
},
|
|
237
241
|
});
|
|
238
242
|
|
|
239
|
-
export const RouteView = Object.assign(RouteViewComponent, {
|
|
243
|
+
export const RouteView = Object.assign(RouteViewComponent, {
|
|
244
|
+
Match,
|
|
245
|
+
Self,
|
|
246
|
+
NotFound,
|
|
247
|
+
});
|
|
240
248
|
|
|
241
249
|
export type {
|
|
242
250
|
RouteViewProps,
|
|
243
251
|
MatchProps as RouteViewMatchProps,
|
|
252
|
+
SelfProps as RouteViewSelfProps,
|
|
244
253
|
NotFoundProps as RouteViewNotFoundProps,
|
|
245
254
|
} from "./types";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineComponent } from "vue";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { SelfProps } from "./types";
|
|
4
|
+
import type { FunctionalComponent, PropType, VNode } from "vue";
|
|
4
5
|
|
|
5
6
|
function renderNull() {
|
|
6
7
|
return null;
|
|
@@ -29,6 +30,23 @@ export const Match = defineComponent({
|
|
|
29
30
|
render: renderNull,
|
|
30
31
|
});
|
|
31
32
|
|
|
33
|
+
// Type Self via FunctionalComponent<SelfProps> so the SelfProps interface
|
|
34
|
+
// is anchored to the component contract — knip otherwise flags SelfProps
|
|
35
|
+
// as unused even though it's re-exported as RouteViewSelfProps for
|
|
36
|
+
// consumers wrapping Self in custom HOCs.
|
|
37
|
+
const SelfImpl = defineComponent({
|
|
38
|
+
name: "RouteView.Self",
|
|
39
|
+
props: {
|
|
40
|
+
fallback: {
|
|
41
|
+
type: [Object, Function] as PropType<VNode | (() => VNode)>,
|
|
42
|
+
default: undefined,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
render: renderNull,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const Self = SelfImpl as unknown as FunctionalComponent<SelfProps>;
|
|
49
|
+
|
|
32
50
|
export const NotFound = defineComponent({
|
|
33
51
|
name: "RouteView.NotFound",
|
|
34
52
|
render: renderNull,
|