what-router 0.12.2 → 0.12.3

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/dist/index.min.js CHANGED
@@ -1,4 +1,4 @@
1
- import{signal as x,effect as I,computed as b,batch as M,h as c,ErrorBoundary as X}from"what-core";function O(e){let t=e.replace(/\([\w-]+\)\//g,"").replace(/\[\.\.\.(\w+)\]/g,(i,u)=>`*:${u}`).replace(/\[(\w+)\]/g,":$1"),n=[],r=null,o=t.split("/").map(i=>i.startsWith("*:")?(r=i.slice(2),n.push(r),"(.+)"):i==="*"?(r="rest",n.push("rest"),"(.+)"):i.startsWith(":")?(n.push(i.slice(1)),"([^/]+)"):i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("/");return{regex:new RegExp(`^${o}$`),paramNames:n,catchAll:r}}var _=new Map,F=1e3;function H(e){let t=_.get(e);return t||(t=O(e),_.size>=F&&_.clear(),_.set(e,t)),t}function K(e,t){let n;try{n=decodeURIComponent(e)}catch{return null}let r=n.split(/[/\\]/);return!t&&r.length>1||r.includes("..")?null:n}function A(e,t){let n=t.filter(r=>r.path);n.sort((r,o)=>{let a=(r.path.match(/:/g)||[]).length+(r.path.includes("*")?100:0),i=(o.path.match(/:/g)||[]).length+(o.path.includes("*")?100:0);return a-i});for(let r of n){let{regex:o,paramNames:a,catchAll:i}=H(r.path),u=e.match(o);if(u){let p={},l=!1;if(a.forEach((s,d)=>{let g=K(u[d+1],s===i);if(g===null){l=!0;return}p[s]=g}),l)continue;return{route:r,params:p}}}return null}function C(e){let t=Object.create(null);if(!e)return t;let n=e.startsWith("?")?e.slice(1):e;for(let r of n.split("&")){let[o,a]=r.split("=");if(!o)continue;let i=decodeURIComponent(o),u=a?decodeURIComponent(a):"";Object.prototype.hasOwnProperty.call(t,i)?Array.isArray(t[i])?t[i].push(u):t[i]=[t[i],u]:t[i]=u}return t}var Q=new Set(["http:","https:","mailto:","tel:"]);function k(e){if(typeof e!="string")return!1;let n=e.trim().replace(/[\s\x00-\x1f]/g,"").toLowerCase();if(n.startsWith("javascript:")||n.startsWith("data:")||n.startsWith("vbscript:")||/^[/\\]{2}/.test(n))return!1;let r=n.match(/^([a-z][a-z0-9+.-]*:)/);return r?Q.has(r[1]):!n.includes("\\")}var h=x(typeof location<"u"?location.pathname+location.search+location.hash:"/"),G=x({}),D=x({}),y=x(!1),R=x(null),V=x(null),m={get url(){return h()},get path(){return h().split("?")[0].split("#")[0]},get params(){return G()},get query(){return D()},get hash(){let e=h().split("#")[1];return e?"#"+e:""},get isNavigating(){return y()},get error(){return V()}},T=[],q=[];function z(e,t){return e.push(t),()=>{let n=e.indexOf(t);n!==-1&&e.splice(n,1)}}function ue(e){return z(T,e)}function le(e){return z(q,e)}async function v(e,t={}){let{replace:n=!1,state:r=null,transition:o=!0,_fromPopstate:a=!1,_redirectChain:i=!1}=t;if(i||(w.length=0),!k(e)){typeof console<"u"&&console.warn("[what-router] Blocked navigation to unsafe URL:",e);return}if(typeof window<"u"&&e.startsWith("#")){let d=h().split("#")[0]+e;history.replaceState(r,"",d),h.set(d);let g=document.querySelector(e);g&&g.scrollIntoView({behavior:"smooth"});return}if(e===h()||y.peek())return;y.set(!0),R.set(e);let u=h();if(T.length){let l=!1;try{for(let s of T.slice())if(await s(e,u)===!1){l=!0;break}}catch(s){throw y.set(!1),R.set(null),s}if(l){y.set(!1),R.set(null),a&&typeof history<"u"&&history.pushState(null,"",u);return}}V.set(null);let p=()=>{a||(typeof window<"u"&&E.set(h(),{x:window.scrollX,y:window.scrollY}),n?history.replaceState(r,"",e):history.pushState(r,"",e)),h.set(e),y.set(!1),R.set(null)};if(o&&typeof document<"u"&&document.startViewTransition)try{await document.startViewTransition(p).finished}catch{}else p();if(q.length)for(let l of q.slice())l(e,u)}var B=Symbol.for("what.router.redirect"),Y=Symbol.for("what.navigation.signal");function fe(e,t={}){if(!k(e)){let r=typeof e=="string"?e:Object.prototype.toString.call(e),o=new Error(`[what-router] redirect() refused an unsafe target: ${r}`);throw o.code="ERR_UNSAFE_REDIRECT",o.suggestion='redirect() accepts same-origin paths and http:, https:, mailto: or tel: URLs only. Protocol-relative ("//host"), backslash-smuggled and javascript:/data: targets are open-redirect vectors. Check a user-supplied target against an allowlist first.',o.codeExample=`// Bad - a user-controlled target can leave your origin:
1
+ import{signal as v,effect as I,computed as b,batch as M,h as c,ErrorBoundary as X}from"what-core";function O(e){let t=e.replace(/\([\w-]+\)\//g,"").replace(/\[\.\.\.(\w+)\]/g,(i,l)=>`*:${l}`).replace(/\[(\w+)\]/g,":$1"),n=[],r=null,o=t.split("/").map(i=>i.startsWith("*:")?(r=i.slice(2),n.push(r),"(.+)"):i==="*"?(r="rest",n.push("rest"),"(.+)"):i.startsWith(":")?(n.push(i.slice(1)),"([^/]+)"):i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("/");return{regex:new RegExp(`^${o}$`),paramNames:n,catchAll:r}}var _=new Map,F=1e3;function H(e){let t=_.get(e);return t||(t=O(e),_.size>=F&&_.clear(),_.set(e,t)),t}function K(e,t){let n;try{n=decodeURIComponent(e)}catch{return null}let r=n.split(/[/\\]/);return!t&&r.length>1||r.includes("..")?null:n}function A(e,t){let n=t.filter(r=>r.path);n.sort((r,o)=>{let a=(r.path.match(/:/g)||[]).length+(r.path.includes("*")?100:0),i=(o.path.match(/:/g)||[]).length+(o.path.includes("*")?100:0);return a-i});for(let r of n){let{regex:o,paramNames:a,catchAll:i}=H(r.path),l=e.match(o);if(l){let p={},u=!1;if(a.forEach((s,d)=>{let g=K(l[d+1],s===i);if(g===null){u=!0;return}p[s]=g}),u)continue;return{route:r,params:p}}}return null}function C(e){let t=Object.create(null);if(!e)return t;let n=e.startsWith("?")?e.slice(1):e;for(let r of n.split("&")){let[o,a]=r.split("=");if(!o)continue;let i=decodeURIComponent(o),l=a?decodeURIComponent(a):"";Object.prototype.hasOwnProperty.call(t,i)?Array.isArray(t[i])?t[i].push(l):t[i]=[t[i],l]:t[i]=l}return t}var Q=new Set(["http:","https:","mailto:","tel:"]);function k(e){if(typeof e!="string")return!1;let n=e.trim().replace(/[\s\x00-\x1f]/g,"").toLowerCase();if(n.startsWith("javascript:")||n.startsWith("data:")||n.startsWith("vbscript:")||/^[/\\]{2}/.test(n))return!1;let r=n.match(/^([a-z][a-z0-9+.-]*:)/);return r?Q.has(r[1]):!n.includes("\\")}var h=v(typeof location<"u"?location.pathname+location.search+location.hash:"/"),G=v({}),D=v({}),y=v(!1),x=v(null),V=v(null),m={get url(){return h()},get path(){return h().split("?")[0].split("#")[0]},get params(){return G()},get query(){return D()},get hash(){let e=h().split("#")[1];return e?"#"+e:""},get isNavigating(){return y()},get error(){return V()}},T=[],q=[];function z(e,t){return e.push(t),()=>{let n=e.indexOf(t);n!==-1&&e.splice(n,1)}}function ue(e){return z(T,e)}function le(e){return z(q,e)}async function R(e,t={}){let{replace:n=!1,state:r=null,transition:o=!0,_fromPopstate:a=!1,_redirectChain:i=!1}=t;if(i||(w.length=0),!k(e)){typeof console<"u"&&console.warn("[what-router] Blocked navigation to unsafe URL:",e);return}if(typeof window<"u"&&e.startsWith("#")){let d=h().split("#")[0]+e;history.replaceState(r,"",d),h.set(d);let g=document.querySelector(e);g&&g.scrollIntoView({behavior:"smooth"});return}if(e===h()||y.peek())return;y.set(!0),x.set(e);let l=h();if(T.length){let u=!1;try{for(let s of T.slice())if(await s(e,l)===!1){u=!0;break}}catch(s){throw y.set(!1),x.set(null),s}if(u){y.set(!1),x.set(null),a&&typeof history<"u"&&history.pushState(null,"",l);return}}V.set(null);let p=()=>{a||(typeof window<"u"&&E.set(h(),{x:window.scrollX,y:window.scrollY}),n?history.replaceState(r,"",e):history.pushState(r,"",e)),h.set(e),y.set(!1),x.set(null)};if(o&&typeof document<"u"&&document.startViewTransition)try{let u=document.startViewTransition(p);u.ready&&typeof u.ready.catch=="function"&&u.ready.catch(()=>{}),await u.finished}catch{}else p();if(q.length)for(let u of q.slice())u(e,l)}var B=Symbol.for("what.router.redirect"),Y=Symbol.for("what.navigation.signal");function fe(e,t={}){if(!k(e)){let r=typeof e=="string"?e:Object.prototype.toString.call(e),o=new Error(`[what-router] redirect() refused an unsafe target: ${r}`);throw o.code="ERR_UNSAFE_REDIRECT",o.suggestion='redirect() accepts same-origin paths and http:, https:, mailto: or tel: URLs only. Protocol-relative ("//host"), backslash-smuggled and javascript:/data: targets are open-redirect vectors. Check a user-supplied target against an allowlist first.',o.codeExample=`// Bad - a user-controlled target can leave your origin:
2
2
  redirect(query.next);
3
3
 
4
4
  // Good - allowlist the target first:
@@ -12,4 +12,4 @@ redirect(ALLOWED.has(query.next) ? query.next : '/');`,o}let n=new Error(`[what-
12
12
  function Private() {
13
13
  if (!user()) redirect('/login');
14
14
  return <Secret />;
15
- }`,n[B]=!0,n[Y]=()=>{L(e,t)},n.to=e,n.options=t,n}typeof window<"u"&&window.addEventListener("popstate",()=>{E.set(h(),{x:window.scrollX,y:window.scrollY});let e=location.pathname+location.search+location.hash;v(e,{replace:!0,_fromPopstate:!0,transition:!1}).then(()=>{let t=E.get(e);t&&requestAnimationFrame(()=>window.scrollTo(t.x,t.y))})});function J(e,t){let n=[];if(!e.path)return n;let r=e.path.split("/").filter(Boolean),o="";for(let a of r){o+="/"+a;let i=t.find(u=>u.layout&&u.path===o+"/_layout");i&&n.push(i.layout)}return e.layout&&n.push(e.layout),n}var w=[],Z=10;function W(e){return c("div",{class:"what-redirect-loop"},c("h1",null,"Redirect Loop"),c("p",null,e))}function L(e,t){if(w.push(e),w.length>Z){let o=w.slice(-5).join(" \u2192 ");return w.length=0,console.error(`[what-router] Redirect loop detected: ${o}`),y.set(!1),R.set(null),W("Too many redirects. Check your middleware configuration.")}let n=new Set,r=!1;for(let o of w){if(n.has(o)){r=!0;break}n.add(o)}if(r){let o=w.join(" \u2192 ");return w.length=0,console.error(`[what-router] Redirect cycle detected: ${o}`),y.set(!1),R.set(null),W("Circular redirect detected. Check your middleware configuration.")}return v(e,{replace:!0,...t,_redirectChain:!0}),null}function ee(e,t,n){if(!e)return null;let r=e.split("?")[0].split("#")[0],o=A(r,t);return!o||o.route===n?null:o.route.loading||null}function te({routes:e,fallback:t,globalLayout:n}){let r=()=>{let a=h(),i=a.split("?")[0].split("#")[0],u=a.split("?")[1]?.split("#")[0]||"",p=y(),l=A(i,e);if(l){M(()=>{G.set(l.params),D.set(C(u))});let{route:s,params:d}=l,g=C(u);if(s.middleware&&s.middleware.length>0)for(let P of s.middleware){let N=P({path:i,params:d,query:g,route:s});if(N===!1)return t?c(t,{}):c("div",{class:"what-403"},c("h1",null,"403"),c("p",null,"Access denied"));if(typeof N=="string")return L(N)}let f,S=p?ee(R(),e,s):null;S?f=c(S,{}):s.loading&&p?f=c(s.loading,{}):f=c(s.component,{params:d,query:g,route:s}),s.error&&(f=c(X,{fallback:s.error},f));let j=J(s,e);for(let P of j.reverse())f=c(P,{params:d,query:g},f);return f}return t?c(t,{}):c("div",{class:"what-404"},c("h1",null,"404"),c("p",null,"Page not found"))},o=()=>{let a=null;try{return r()}catch(i){if(!i||!i[B])throw i;a=i}return L(a.to,a.options)};return n?c(n,{},o):o}function ne({href:e,class:t,className:n,children:r,replace:o,prefetch:a=!0,activeClass:i="active",exactActiveClass:u="exact-active",transition:p=!0,...l}){let s=k(e)?e:"about:blank";!k(e)&&typeof console<"u"&&console.warn("[what-router] Link blocked unsafe href:",e);let d=s.split("?")[0].split("#")[0];return c("a",{href:s,class:()=>{let f=m.path,S=d==="/"?f==="/":f===d||f.startsWith(d+"/");return[t||n,S&&i,f===d&&u].filter(Boolean).join(" ")||void 0},onclick:f=>{f.ctrlKey||f.metaKey||f.shiftKey||f.altKey||f.button!==0||(f.preventDefault(),v(s,{replace:o,transition:p}))},onmouseenter:a?()=>U(s):void 0,...l},...Array.isArray(r)?r:[r])}function de(e){return ne(e)}function pe(e){return Object.entries(e).map(([t,n])=>typeof n=="function"?{path:t,component:n}:{path:t,...n})}function re(e,t){let n=e.endsWith("/")?e.slice(0,-1):e,r=!t||t==="/"?"/":t.startsWith("/")?t:`/${t}`;return r==="/"?n||"/":n+r}function he(e,t,n={}){let{layout:r,loading:o,error:a}=n;return t.map(i=>({...i,path:re(e,i.path),layout:i.layout||r,loading:i.loading||o,error:i.error||a}))}function me(e,t,n={}){let{layout:r,middleware:o}=n;return t.map(a=>({...a,_group:e,layout:a.layout||r,middleware:[...a.middleware||[],...o||[]]}))}function ge({to:e}){return v(e,{replace:!0}),null}function ye(e,t){return n=>function(o){let a=e(o);return a instanceof Promise?c("div",{class:"what-guard-loading"},"Loading..."):a?c(n,o):typeof t=="string"?(v(t,{replace:!0}),null):c(t,o)}}function we(e,t={}){let{fallback:n="/login",loading:r=null}=t;return o=>function(i){let u=x("pending"),p=x(null),l=!1;return I(()=>(l=!1,Promise.resolve(e(i)).then(s=>{l||(p.set(s),u.set(s?"allowed":"denied"))}).catch(()=>{l||u.set("denied")}),()=>{l=!0})),()=>{let s=u();return s==="pending"?r?c(r,{}):null:s==="allowed"?c(o,i):typeof n=="string"?(v(n,{replace:!0}),null):c(n,i)}}}var $=new Set;function U(e){if(typeof document>"u"||$.has(e))return;$.add(e);let t=document.createElement("link");t.rel="prefetch",t.href=e,document.head.appendChild(t)}var E=new Map;function xe(){typeof window>"u"||(window.addEventListener("beforeunload",()=>{E.set(location.pathname,window.scrollY)}),I(()=>{let e=m.path,t=E.get(e);requestAnimationFrame(()=>{t!==void 0?window.scrollTo(0,t):m.hash?document.querySelector(m.hash)?.scrollIntoView():window.scrollTo(0,0)})}))}function ve(e){return{style:{viewTransitionName:e}}}function Re(e){typeof document>"u"||(document.documentElement.dataset.transition=e)}function be(){return{path:b(()=>m.path),params:b(()=>m.params),query:b(()=>m.query),hash:b(()=>m.hash),isNavigating:b(()=>m.isNavigating),navigate:v,prefetch:U}}function Ee(){return m.params}function Se(){return m.query}function _e(){return v}function Ae(e){U(e)}function Ce({children:e}){return e||null}function ke({routes:e,layout:t,fallback:n,error:r}){let o=e.map(a=>({path:a.path,component:a.component,layout:a.layout||void 0,loading:a.loading||void 0,error:a.error||r||void 0,_mode:a.mode||"client"}));return te({routes:o,globalLayout:t,fallback:n||oe})}function oe(){return c("div",{style:"text-align:center;padding:60px 20px"},c("h1",{style:"font-size:48px;margin-bottom:8px"},"404"),c("p",{style:"color:#64748b"},"Page not found"))}export{ke as FileRouter,ne as Link,de as NavLink,Ce as Outlet,ge as Redirect,te as Router,le as afterNavigate,we as asyncGuard,ue as beforeNavigate,O as compilePath,pe as defineRoutes,xe as enableScrollRestoration,ye as guard,k as isSafeUrl,A as matchRoute,v as navigate,he as nestedRoutes,C as parseQuery,U as prefetch,Ae as prefetchRoute,fe as redirect,m as route,me as routeGroup,Re as setViewTransition,_e as useNavigate,Ee as useParams,be as useRoute,Se as useSearch,ve as viewTransitionName};
15
+ }`,n[B]=!0,n[Y]=()=>{L(e,t)},n.to=e,n.options=t,n}typeof window<"u"&&window.addEventListener("popstate",()=>{E.set(h(),{x:window.scrollX,y:window.scrollY});let e=location.pathname+location.search+location.hash;R(e,{replace:!0,_fromPopstate:!0,transition:!1}).then(()=>{let t=E.get(e);t&&requestAnimationFrame(()=>window.scrollTo(t.x,t.y))})});function J(e,t){let n=[];if(!e.path)return n;let r=e.path.split("/").filter(Boolean),o="";for(let a of r){o+="/"+a;let i=t.find(l=>l.layout&&l.path===o+"/_layout");i&&n.push(i.layout)}return e.layout&&n.push(e.layout),n}var w=[],Z=10;function W(e){return c("div",{class:"what-redirect-loop"},c("h1",null,"Redirect Loop"),c("p",null,e))}function L(e,t){if(x.peek()===e)return null;if(w.push(e),w.length>Z){let o=w.slice(-5).join(" \u2192 ");return w.length=0,console.error(`[what-router] Redirect loop detected: ${o}`),y.set(!1),x.set(null),W("Too many redirects. Check your middleware configuration.")}let n=new Set,r=!1;for(let o of w){if(n.has(o)){r=!0;break}n.add(o)}if(r){let o=w.join(" \u2192 ");return w.length=0,console.error(`[what-router] Redirect cycle detected: ${o}`),y.set(!1),x.set(null),W("Circular redirect detected. Check your middleware configuration.")}return R(e,{replace:!0,...t,_redirectChain:!0}),null}function ee(e,t,n){if(!e)return null;let r=e.split("?")[0].split("#")[0],o=A(r,t);return!o||o.route===n?null:o.route.loading||null}function te({routes:e,fallback:t,globalLayout:n}){let r=()=>{let a=h(),i=a.split("?")[0].split("#")[0],l=a.split("?")[1]?.split("#")[0]||"",p=y(),u=A(i,e);if(u){M(()=>{G.set(u.params),D.set(C(l))});let{route:s,params:d}=u,g=C(l);if(s.middleware&&s.middleware.length>0)for(let P of s.middleware){let N=P({path:i,params:d,query:g,route:s});if(N===!1)return t?c(t,{}):c("div",{class:"what-403"},c("h1",null,"403"),c("p",null,"Access denied"));if(typeof N=="string")return L(N)}let f,S=p?ee(x(),e,s):null;S?f=c(S,{}):s.loading&&p?f=c(s.loading,{}):f=c(s.component,{params:d,query:g,route:s}),s.error&&(f=c(X,{fallback:s.error},f));let j=J(s,e);for(let P of j.reverse())f=c(P,{params:d,query:g},f);return f}return t?c(t,{}):c("div",{class:"what-404"},c("h1",null,"404"),c("p",null,"Page not found"))},o=()=>{let a=null;try{return r()}catch(i){if(!i||!i[B])throw i;a=i}return L(a.to,a.options)};return n?c(n,{},o):o}function ne({href:e,class:t,className:n,children:r,replace:o,prefetch:a=!0,activeClass:i="active",exactActiveClass:l="exact-active",transition:p=!0,...u}){let s=k(e)?e:"about:blank";!k(e)&&typeof console<"u"&&console.warn("[what-router] Link blocked unsafe href:",e);let d=s.split("?")[0].split("#")[0];return c("a",{href:s,class:()=>{let f=m.path,S=d==="/"?f==="/":f===d||f.startsWith(d+"/");return[t||n,S&&i,f===d&&l].filter(Boolean).join(" ")||void 0},onclick:f=>{f.ctrlKey||f.metaKey||f.shiftKey||f.altKey||f.button!==0||(f.preventDefault(),R(s,{replace:o,transition:p}))},onmouseenter:a?()=>U(s):void 0,...u},...Array.isArray(r)?r:[r])}function de(e){return ne(e)}function pe(e){return Object.entries(e).map(([t,n])=>typeof n=="function"?{path:t,component:n}:{path:t,...n})}function re(e,t){let n=e.endsWith("/")?e.slice(0,-1):e,r=!t||t==="/"?"/":t.startsWith("/")?t:`/${t}`;return r==="/"?n||"/":n+r}function he(e,t,n={}){let{layout:r,loading:o,error:a}=n;return t.map(i=>({...i,path:re(e,i.path),layout:i.layout||r,loading:i.loading||o,error:i.error||a}))}function me(e,t,n={}){let{layout:r,middleware:o}=n;return t.map(a=>({...a,_group:e,layout:a.layout||r,middleware:[...a.middleware||[],...o||[]]}))}function ge({to:e}){return R(e,{replace:!0}),null}function ye(e,t){return n=>function(o){let a=e(o);return a instanceof Promise?c("div",{class:"what-guard-loading"},"Loading..."):a?c(n,o):typeof t=="string"?(R(t,{replace:!0}),null):c(t,o)}}function we(e,t={}){let{fallback:n="/login",loading:r=null}=t;return o=>function(i){let l=v("pending"),p=v(null),u=!1;return I(()=>(u=!1,Promise.resolve(e(i)).then(s=>{u||(p.set(s),l.set(s?"allowed":"denied"))}).catch(()=>{u||l.set("denied")}),()=>{u=!0})),()=>{let s=l();return s==="pending"?r?c(r,{}):null:s==="allowed"?c(o,i):typeof n=="string"?(R(n,{replace:!0}),null):c(n,i)}}}var $=new Set;function U(e){if(typeof document>"u"||$.has(e))return;$.add(e);let t=document.createElement("link");t.rel="prefetch",t.href=e,document.head.appendChild(t)}var E=new Map;function xe(){typeof window>"u"||(window.addEventListener("beforeunload",()=>{E.set(location.pathname,window.scrollY)}),I(()=>{let e=m.path,t=E.get(e);requestAnimationFrame(()=>{t!==void 0?window.scrollTo(0,t):m.hash?document.querySelector(m.hash)?.scrollIntoView():window.scrollTo(0,0)})}))}function ve(e){return{style:{viewTransitionName:e}}}function Re(e){typeof document>"u"||(document.documentElement.dataset.transition=e)}function be(){return{path:b(()=>m.path),params:b(()=>m.params),query:b(()=>m.query),hash:b(()=>m.hash),isNavigating:b(()=>m.isNavigating),navigate:R,prefetch:U}}function Ee(){return m.params}function Se(){return m.query}function _e(){return R}function Ae(e){U(e)}function Ce({children:e}){return e||null}function ke({routes:e,layout:t,fallback:n,error:r}){let o=e.map(a=>({path:a.path,component:a.component,layout:a.layout||void 0,loading:a.loading||void 0,error:a.error||r||void 0,_mode:a.mode||"client"}));return te({routes:o,globalLayout:t,fallback:n||oe})}function oe(){return c("div",{style:"text-align:center;padding:60px 20px"},c("h1",{style:"font-size:48px;margin-bottom:8px"},"404"),c("p",{style:"color:#64748b"},"Page not found"))}export{ke as FileRouter,ne as Link,de as NavLink,Ce as Outlet,ge as Redirect,te as Router,le as afterNavigate,we as asyncGuard,ue as beforeNavigate,O as compilePath,pe as defineRoutes,xe as enableScrollRestoration,ye as guard,k as isSafeUrl,A as matchRoute,R as navigate,he as nestedRoutes,C as parseQuery,U as prefetch,Ae as prefetchRoute,fe as redirect,m as route,me as routeGroup,Re as setViewTransition,_e as useNavigate,Ee as useParams,be as useRoute,Se as useSearch,ve as viewTransitionName};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "what-router",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "What Framework - File-based & programmatic router with View Transitions",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -35,7 +35,7 @@
35
35
  "license": "MIT",
36
36
  "dependencies": {},
37
37
  "peerDependencies": {
38
- "what-core": "^0.12.2"
38
+ "what-core": "^0.12.3"
39
39
  },
40
40
  "repository": {
41
41
  "type": "git",
package/src/index.js CHANGED
@@ -184,7 +184,16 @@ export async function navigate(to, opts = {}) {
184
184
  // Use View Transitions API if available and enabled
185
185
  if (transition && typeof document !== 'undefined' && document.startViewTransition) {
186
186
  try {
187
- await document.startViewTransition(doNavigation).finished;
187
+ const viewTransition = document.startViewTransition(doNavigation);
188
+ // `.ready` rejects when the transition is skipped, which two navigations
189
+ // in quick succession do routinely. Awaiting only `.finished` left that
190
+ // rejection unhandled, so it surfaced as an uncaught page error
191
+ // ("Transition was skipped") and landed in whatever error reporter the
192
+ // app had installed, for a navigation that actually succeeded.
193
+ if (viewTransition.ready && typeof viewTransition.ready.catch === 'function') {
194
+ viewTransition.ready.catch(() => {});
195
+ }
196
+ await viewTransition.finished;
188
197
  } catch (e) {
189
198
  // Transition failed, navigation still happened
190
199
  }
@@ -328,6 +337,23 @@ function loopScreen(message) {
328
337
  // instead would never catch a cycle between two route components, because each
329
338
  // hop matches successfully before its component throws the next redirect.
330
339
  function handleRedirect(target, options) {
340
+ // A navigation to this exact target is already in flight, so this is a
341
+ // re-match of the same hop, not a second one.
342
+ //
343
+ // It happens on every re-match that occurs before the URL commits. With the
344
+ // View Transitions API, navigate() defers the `_url` write into the
345
+ // transition callback while `_isNavigating` flips immediately, and
346
+ // renderMatch reads `_isNavigating`, so the match re-runs against the
347
+ // still-old URL, the same middleware returns the same target, and the hop was
348
+ // counted twice. One guarded deep link produced 25 false "Redirect cycle
349
+ // detected" errors and a flash of the redirect-loop screen, and the detector
350
+ // then cleared `_isNavigating` / `_pendingUrl` while the real navigation was
351
+ // still in flight, corrupting router state mid-navigation.
352
+ //
353
+ // Chromium-only, because without View Transitions doNavigation() runs
354
+ // synchronously and the URL and the flag change together.
355
+ if (_pendingUrl.peek() === target) return null;
356
+
331
357
  _redirectHistory.push(target);
332
358
 
333
359
  if (_redirectHistory.length > MAX_REDIRECTS) {