routexiz 0.2.0 → 0.2.1-z2

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 CHANGED
@@ -6,7 +6,8 @@
6
6
 
7
7
  A lightweight, powerful, and modern router for React.
8
8
 
9
- > Tree-based routing — modeled like a TreeView, where each route is a node and navigation resolves a single path from root to leaf.
9
+ > Tree-based routing — modeled like a TreeView, where each route is a node and navigation resolves a single path from root to leaf.
10
+ > Nested layouts via `children`.
10
11
 
11
12
  ---
12
13
 
@@ -45,7 +46,7 @@ npm install routexiz
45
46
 
46
47
  ---
47
48
 
48
- # Quick
49
+ # Basic
49
50
 
50
51
  ```tsx
51
52
  import { route, RouterProvider } from "routexiz"
@@ -61,36 +62,30 @@ export default function App() {
61
62
 
62
63
  # Routing API
63
64
 
64
- <b>route()</b>
65
+ <b>route</b>
65
66
 
66
67
  ```ts
67
68
  route(path, component, config?)
68
69
  ```
69
70
 
70
- <b>builder API</b>
71
+ <b>builder</b>
71
72
 
72
73
  ```ts
73
- // home
74
- // /dashboard
74
+ // /home
75
75
  route("/", Layout, root => {
76
+ // /dashboard
76
77
  root.route("/dashboard", Dashboard, dash => {
77
78
  dash.guard(fn)
78
79
  dash.middleware(fn)
79
80
  dash.meta({ title: "Dashboard" })
80
81
  })
81
-
82
82
  // more
83
83
  })
84
84
 
85
85
  // /admin
86
- // admin/dashboard
87
86
  route("/admin", Admin, root => {
88
- root.route("/dashboard", AdminDashboard, dash => {
89
- dash.guard(fn)
90
- dash.middleware(fn)
91
- dash.meta({ title: "Dashboard" })
92
- })
93
-
87
+ // /admin/dashboard
88
+ root.route("/dashboard", AdminDashboard)
94
89
  // more
95
90
  })
96
91
 
@@ -98,6 +93,13 @@ route("/admin", Admin, root => {
98
93
 
99
94
  ```
100
95
 
96
+ <b>flatten</b>
97
+
98
+ ```ts
99
+ route("/admin", Admin)
100
+ route("/admin/dashboard", AdminDashboard)
101
+ ```
102
+
101
103
  > Supports multiple route trees and deeply nested routing via a flexible builder API.
102
104
 
103
105
  ---
@@ -120,6 +122,17 @@ navigate("/users/:id", {
120
122
 
121
123
  ---
122
124
 
125
+ ## redirect
126
+
127
+ ```ts
128
+ redirect("/users/:id", {
129
+ params: { id: 1 },
130
+ query: { tab: "profile" }
131
+ })
132
+ ```
133
+
134
+ ---
135
+
123
136
  ## Link
124
137
 
125
138
  ```tsx
@@ -166,8 +179,8 @@ A guard determines whether navigation is allowed.
166
179
  - Runs before entering a route
167
180
  - Can block navigation
168
181
  - Returns:
169
- - true → allow
170
- - false → block
182
+ - `true` → allow
183
+ - `false` → block
171
184
 
172
185
  ```ts
173
186
  dash.guard(({ params }) => {
@@ -867,7 +880,7 @@ It’s lightweight and flexible, designed for client-side SPAs.
867
880
  # Architecture
868
881
 
869
882
  ```text
870
- Link / navigate
883
+ Link / navigate / redirect
871
884
 
872
885
  matchRouteChain
873
886
 
@@ -877,7 +890,7 @@ loader (cached)
877
890
 
878
891
  Suspense
879
892
 
880
- render tree
893
+ render
881
894
  ```
882
895
 
883
896
  ---
@@ -890,17 +903,11 @@ Each route must resolve to a unique full path in the route tree.
890
903
 
891
904
  ## Why?
892
905
 
893
- Because routing is tree-based:
894
-
895
906
  - Navigation resolves a single path from root to leaf
896
907
  - Duplicate paths create ambiguous matches
897
908
  - This leads to unpredictable behavior
898
-
899
- ## Important
900
-
901
- ⚠️ Duplicate routes are NOT validated automatically.
902
-
903
- The router will NOT throw an error or warning.
909
+ - Duplicate routes are NOT validated automatically.
910
+ - The router will NOT throw an error or warning.
904
911
 
905
912
  ## Requirement
906
913
 
@@ -1 +1 @@
1
- import n,{createContext as t,useContext as e,useState as r,useRef as o,useEffect as a,Suspense as i,forwardRef as s}from"react";import{jsx as c,Fragment as u,jsxs as l}from"react/jsx-runtime";import h from"joinclass";const d=new Map;function p(n=1/0){const t=Date.now();let e=0;for(const[r,o]of d.entries())if(o.expiry<=t&&(d.delete(r),e++,e>=n))break}function f(n,t,e,r){const o=Date.now(),a=d.get(n);if(a&&!r&&(!e||a.expiry>o))return a.resource;const i=m(t());return d.set(n,{resource:i,expiry:o+(e??0)}),i}function m(n,t){let e=t?"success":"pending",r=t??null;const o=n.then(n=>{e="success",r=n},n=>{e="error",r=n});return{read(){if("pending"===e)throw o;if("error"===e)throw r;return r},update(n){r=n,e="success"},get:()=>r}}function y(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function g(n){return y(n).split("/").filter(Boolean)}function w(n){const[t,e]=n.split("#"),[r,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,e]=n.split("=");if(!t)return;const r=decodeURIComponent(t),o=decodeURIComponent(e??"");void 0!==a[r]?Array.isArray(a[r])?a[r].push(o):a[r]=[a[r],o]:a[r]=o}),{path:y(r),query:a,hash:e??""}}function v(n,t){const e=n[t];return Array.isArray(e)?e[0]:e}function P(n){const t={};if(!n)return t;if("string"==typeof n){const e=new URLSearchParams(n);e.forEach((n,r)=>{const o=e.getAll(r);t[r]=o.length>1?o:o[0]})}else for(const e in n){const r=n[e];Array.isArray(r)?t[e]=r.map(n=>String(n)):null!=r&&(t[e]=String(r))}return t}function b(n){const t=g(n);let e=10*t.length;for(const n of t)"*"===n?e+=0:n.startsWith(":")?e+=1:e+=5;return e}function q(n){return[...n].sort((n,t)=>b(t.path)-b(n.path))}function E(n,t){const{path:e,query:r,hash:o}=w(t),a=g(e),i=q(n).map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;i.length;){const{node:n,segIndex:t,params:e,chain:s}=i.pop(),c=g(n.path),u={...e};let l=0;for(;l<c.length;l++){const n=c[l],e=a[t+l];if(!e)break;if("*"===n){u["*"]=a.slice(t+l).join("/"),l=c.length;break}if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(l!==c.length)continue;const h=t+l,d=[...s,{node:n,params:u,query:r,hash:o}];if(h===a.length)return d;const p=q(n.children||[]);for(let n=p.length-1;n>=0;n--){const t=g(p[n].path).some(n=>n.startsWith(":"));i.push({node:p[n],segIndex:t?0:h,params:u,chain:d})}}return null}function k(n,t,e,r,o){const{path:a,query:i,hash:s}=w(n);let c=y(a);if(t)for(const n in t)c=c.replace(`:${n}`,encodeURIComponent(t[n]));const u=o?.replaceQuery?{...e??{}}:{...i,...e??{}},l=new URLSearchParams;for(const n in u){const t=u[n];Array.isArray(t)?t.forEach(t=>l.append(n,String(t))):l.append(n,String(t))}const h=l.toString();return h&&(c+=`?${h}`),r?c+=`#${r}`:s&&(c+=`#${s}`),c}function x(n){return JSON.stringify(Object.keys(n||{}).sort().reduce((t,e)=>(t[e]=n[e],t),{}))}function N(n,t){return`${n.path}|${x(t.params)}|${x(t.query)}|${t.hash}`}let S=[];function A(){S=[]}function R(){return S}function K(n){const t={guard:e=>(n.guardFns||(n.guardFns=[]),n.guardFns.push(e),t),middleware:e=>(n.middlewares||(n.middlewares=[]),n.middlewares.push(e),t),meta:e=>(n.meta={...n.meta||{},...e},t),route(t,e,r){const o={path:y(t),component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};return"function"==typeof r?K(o)(r):r&&(o.options=r),n.children.push(o),K(o).api}};return Object.assign(n=>n(t),{api:t})}function C(n,t,e){const r={path:y(n),component:t,children:[],options:{},parent:void 0};return"function"==typeof e?K(r)(e):e&&(r.options=e),S.push(r),K(r).api}const $=t(null),I=t(null),M=t(null),j=t({loading:!1,path:"",pendingPath:""}),F=t({name:"none",stage:"idle"});function L(){const n=e($);try{return n?.read()}catch{return}}function O(){const n=e(I);if(!n)throw new Error("RouteContext not found");return n}function B(){return e(M)??{}}function D(){try{const n=O();if(n?.query)return n.query}catch{}const{query:n}=w(window.location.pathname+window.location.search+window.location.hash);return n}function Q(){return e(j)}function U(){return e(F)}function W(){try{return L()}catch{return}}function T(){const{meta:n,params:t,path:e,query:r}=O();return{data:W(),params:t??{},query:r??{},path:e,meta:n}}const G=200;function J({name:n="fade",stage:t,duration:e=200,children:i}){const[s,d]=r(i),[p,f]=r(null),m=o();return a(()=>{m.current&&clearTimeout(m.current),"exiting"===t&&f(s),"entering"===t&&(d(i),m.current=window.setTimeout(()=>{f(null)},e))},[i,t,e]),"idle"===t?c(u,{children:i}):l("div",{className:h("rtx",`rtx-${n}`),children:[p&&c("div",{className:"rtx-exit",children:p}),c("div",{className:h("rtx-enter",`rtx-${t}`),children:s})]})}let z=!1,H=!1,V=c("div",{children:"Loading..."});const X=({error:n})=>l("div",{children:["Global Error: ",String(n)]});let Y=null;function Z(n,t){z=n,t&&(V=t)}function _(n,t){H=n,Y=t??null}function nn(){return z}function tn(){return H}function en(){return V}function rn(){return Y?({error:n})=>Y(n):X}class on extends n.Component{constructor(n){super(n),this.reset=()=>{this.setState(n=>({hasError:!1,error:null,resetKey:n.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(n){return{hasError:!0,error:n}}render(){if(this.state.hasError){const n=this.props.errorBoundary??(tn()?rn():void 0);return n?c(n,{error:this.state.error,params:this.props.params}):this.props.fallback??l("div",{children:["Error: ",String(this.state.error)]})}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function an({resource:t,errorBoundary:e,children:r,resetKey:o}){const[a,i]=n.useState(null);n.useEffect(()=>{i(null)},[o]);try{return t?.read(),a&&i(null),c(u,{children:r})}catch(n){if(n instanceof Promise)throw n;const t=e??(tn()?rn():void 0);if(t)return c(t,{error:n});throw n}}function sn({chain:n,data:t}){return c(u,{children:function e(r=0){const o=n[r];if(!o)return null;const a=o.node.component;if(!a)return e(r+1);const s={params:o.params,path:o.node.path,query:o.query,hash:o.hash},u=function(n,t){let e=n;for(;e;){const n=e.options?.fallback;if(n)return"function"==typeof n?n(t):n;e=e.parent}return nn()?en():null}(o.node,s),l=Object.assign({},...n.slice(0,r+1).map(n=>n.node.meta)),h=t[N(o.node,s)];return c(on,{fallback:u,errorBoundary:o.node.options?.errorBoundary,params:o.params,children:c(i,{fallback:u,children:c(I.Provider,{value:{path:o.node.path,params:o.params,query:P(o.query),hash:o.hash??"",meta:l},children:c($.Provider,{value:h,children:c(M.Provider,{value:o.params,children:c(an,{resource:h,errorBoundary:o.node.options?.errorBoundary,resetKey:o.params.id||o.params,children:c(a,{children:e(r+1)})})})})})})})}()})}let cn=null;function un(){return cn}function ln(n,t){const e=R(),[r,o]=n.split("?"),a={...P(o),...t?.query??{}},i=E(e,k(r,t?.params,a,t?.hash));i&&hn(i,{forceReload:!1})}function hn(n,t){const e={};return n.forEach(n=>{const r=n.node.options?.loader;if(!r)return;const o={params:n.params,path:n.node.path,query:n.query,hash:n.hash},a=N(n.node,o),i=n.node.options?.ttl,s=t?.forceReload??n.node.options?.shouldReload?.({prev:void 0,next:o})??!0;e[a]=f(a,()=>Promise.resolve(r(o)),i,s)}),e}function dn({transition:n,duration:t,wrapper:e}){const[i,s]=r({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:t??G}),u=o(0);async function l(e,r){const o=++u.current,a=R(),i=E(a,e)??E(a,"*")??[];for(const n of i)for(const t of n.node.guardFns||[]){if(!await t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}))return console.warn("Navigation blocked",e),!1}for(const n of i)for(const t of n.node.middlewares||[])await t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}).catch(console.error);const c=function(n,t,e){if(t?.transition)return t.transition;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(e.options?.transition)return e.options.transition;if(e.meta?.transition)return e.meta.transition}return e??"none"}(i,r,n),l=function(n,t,e){if(null!=t?.duration)return t.duration;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(null!=e.options?.duration)return e.options.duration}return null!=e?e:G}(i,r,t);s(n=>({...n,loading:!0,pendingPath:e,transition:{name:c,stage:"exiting"},duration:l})),await new Promise(n=>setTimeout(n,l));const h=hn(i,r);return o===u.current&&(s(n=>({...n,chain:i,data:{...n.data,...h},loading:!1,path:e,pendingPath:"",transition:{name:c,stage:"entering"},duration:l})),window.scrollTo(0,0),!0)}a(()=>{cn=l;const n=()=>window.location.pathname+window.location.search+window.location.hash;l(n());const t=()=>l(n());window.addEventListener("popstate",t);const e=()=>{const t=E(R(),n());if(!t)return;const e={};t.forEach(n=>{n.node.options?.revalidateOnFocus&&Object.assign(e,hn([n],{forceReload:!0}))}),Object.keys(e).length&&s(n=>({...n,data:{...n.data,...e}}))};return window.addEventListener("focus",e),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",e)}},[]);const h=c(sn,{chain:i.chain,data:i.data},i.path);let d=h;if("none"!==i.transition.name&&(d=c(J,{name:i.transition.name,stage:i.transition.stage,duration:i.duration,children:h})),e){d=c(e,{children:d})}return c(j.Provider,{value:{loading:i.loading,path:i.path,pendingPath:i.pendingPath},children:c(F.Provider,{value:i.transition,children:d})})}const pn=[],fn=[];function mn(n){pn.push(n)}function yn(n){fn.push(n)}async function gn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},o=k(n,r.params,r.query,r.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(n){return(await Promise.all(pn.map(t=>t(n)))).every(n=>!1!==n)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=un();if(!r.shallow&&a){if(!await a(o,r))return console.warn("Navigation blocked by route guard",o),!1}return e?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(n){for(const t of fn)await t(n)}(o),!0}function wn(n,t){return gn(n,t,!1)}function vn(n,t){return wn(n,t)}function Pn(){const n=(n,t)=>gn(n,t);return n.replace=(n,t)=>gn(n,t,!0),n}function bn(n){return Q().path===n}function qn(n,t){return()=>ln(n,t)}function En({to:n,transition:t,replace:e,params:r,query:i,hash:s,partialMatch:c,disablePrefetch:u,replaceQuery:l}){const h=Pn(),d=Q(),p=o(null),f=k(n,r,i,s,{replaceQuery:l}),m=c?d.path?.startsWith(f):d.path===f;return a(()=>{if(u)return;const n=p.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(ln(f),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[f,u]),{ref:p,fullPath:f,isActive:m,go:async()=>e?h.replace(n,{transition:t,params:r,query:i,hash:s}):h(n,{transition:t,params:r,query:i,hash:s})}}wn.replace=(n,t)=>gn(n,t,!0);const kn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:o,onKeyDown:a,disablePrefetch:i,...s},u)=>c("a",{ref:u,href:n,...s,onClick:async e=>{if(r&&r(e),e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;e.preventDefault();await t()||console.warn("Navigation blocked:",n)},onMouseEnter:n=>{o&&o(n),i||e?.()},onKeyDown:async e=>{if("Enter"===e.key){e.preventDefault();await t()||console.warn("Navigation blocked:",n)}a&&a(e)}}));function xn(n){const{to:t,transition:e,replace:r,activeClassName:o="active",onNavigate:a,partialMatch:i,params:s,query:u,hash:l,className:d,disablePrefetch:p,replaceQuery:f,...m}=n,{ref:y,fullPath:g,isActive:w,go:v}=En({to:t,transition:e,replace:r,params:s,query:u,hash:l,partialMatch:i,disablePrefetch:p,replaceQuery:f});return c(kn,{ref:y,fullPath:g,onNavigate:async()=>{const n=await v();return a?.(g),n},onPrefetch:()=>ln(g),className:h(d,w&&o),disablePrefetch:p,...m})}function Nn(n){const{to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,className:u,style:l,disablePrefetch:d,replaceQuery:p,...f}=n,{ref:m,fullPath:y,isActive:g,go:w}=En({to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,disablePrefetch:d,replaceQuery:p}),v="function"==typeof u?u({isActive:g}):u,P="function"==typeof l?l({isActive:g}):l;return c(kn,{ref:m,fullPath:y,onNavigate:async()=>(await w(),!0),onPrefetch:()=>ln(y),className:h(v),style:P,disablePrefetch:d,...f})}kn.displayName="LinkCore";export{G as DEFAULT_DURATION,xn as Link,$ as LoaderDataContext,Nn as NavLink,M as ParamsContext,I as RouteContext,dn as RouterProvider,j as RouterStateContext,J as Transition,F as TransitionContext,yn as addAfterNavigationHook,mn as addBeforeNavigationHook,N as buildCacheKey,k as buildPath,p as cleanupCache,A as clearRoutes,m as createResource,rn as getGlobalError,en as getGlobalFallback,un as getGlobalResolve,v as getQueryFirst,f as getResource,R as getRoutes,tn as getUseGlobalError,nn as getUseGlobalFallback,E as matchRouteChain,wn as navigate,y as normalize,P as normalizeQuery,w as parseQueryHash,ln as prefetch,vn as redirect,C as route,_ as setGlobalError,Z as setGlobalFallback,g as split,En as useLinkCore,L as useLoaderData,Pn as useNavigate,Q as useNavigation,B as useParams,qn as usePrefetch,D as useQuery,O as useRouteContext,bn as useRouteMatch,T as useRouterData,W as useSafeLoaderData,U as useTransition};
1
+ import n,{createContext as t,useContext as e,useState as r,useRef as o,useEffect as a,Suspense as i,forwardRef as s}from"react";import{jsx as c,Fragment as u,jsxs as l}from"react/jsx-runtime";import h from"joinclass";const d=new Map;function f(n=1/0){const t=Date.now();let e=0;for(const[r,o]of d.entries())if(o.expiry<=t&&(d.delete(r),e++,e>=n))break}function p(n,t,e,r){const o=Date.now(),a=d.get(n);if(a&&!r&&(!e||a.expiry>o))return a.resource;const i=m(t());return d.set(n,{resource:i,expiry:o+(e??0)}),i}function m(n,t){let e=t?"success":"pending",r=t??null;const o=n.then(n=>{e="success",r=n},n=>{e="error",r=n});return{read(){if("pending"===e)throw o;if("error"===e)throw r;return r},update(n){r=n,e="success"},get:()=>r}}function y(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function g(n){return y(n).split("/").filter(Boolean)}function w(n){const[t,e]=n.split("#"),[r,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,e]=n.split("=");if(!t)return;const r=decodeURIComponent(t),o=decodeURIComponent(e??"");void 0!==a[r]?Array.isArray(a[r])?a[r].push(o):a[r]=[a[r],o]:a[r]=o}),{path:y(r),query:a,hash:e??""}}function v(n,t){const e=n[t];return Array.isArray(e)?e[0]:e}function P(n){const t={};if(!n)return t;if("string"==typeof n){const e=new URLSearchParams(n);e.forEach((n,r)=>{const o=e.getAll(r);t[r]=o.length>1?o:o[0]})}else for(const e in n){const r=n[e];Array.isArray(r)?t[e]=r.map(n=>String(n)):null!=r&&(t[e]=String(r))}return t}function b(n){const t=g(n);let e=10*t.length;for(const n of t)"*"===n?e+=0:n.startsWith(":")?e+=1:e+=5;return e}function q(n){return[...n].sort((n,t)=>b(t.path)-b(n.path))}function x(n,t){const{path:e,query:r,hash:o}=w(t),a=g(e),i=q(n).map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;i.length;){const{node:n,segIndex:t,params:e,chain:s}=i.pop(),c=g(n.path),u={...e};let l=0;for(;l<c.length;l++){const n=c[l],e=a[t+l];if(!e)break;if("*"===n){u["*"]=a.slice(t+l).join("/"),l=c.length;break}if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(l!==c.length)continue;const h=t+l,d=[...s,{node:n,params:u,query:r,hash:o}];if(h===a.length)return d;const f=q(n.children||[]);for(let n=f.length-1;n>=0;n--){const t=g(f[n].path).some(n=>n.startsWith(":"));i.push({node:f[n],segIndex:t?0:h,params:u,chain:d})}}return null}function k(n,t,e,r,o){const{path:a,query:i,hash:s}=w(n);let c=y(a);if(t)for(const n in t)c=c.replace(`:${n}`,encodeURIComponent(t[n]));const u=o?.replaceQuery?{...e??{}}:{...i,...e??{}},l=new URLSearchParams;for(const n in u){const t=u[n];Array.isArray(t)?t.forEach(t=>l.append(n,String(t))):l.append(n,String(t))}const h=l.toString();return h&&(c+=`?${h}`),r?c+=`#${r}`:s&&(c+=`#${s}`),c}function E(n){return JSON.stringify(Object.keys(n||{}).sort().reduce((t,e)=>(t[e]=n[e],t),{}))}function N(n,t){return`${n.path}|${E(t.params)}|${E(t.query)}|${t.hash}`}let S=[];function A(){S=[]}function R(){return S}function K(n){const t={guard:e=>(n.guardFns||(n.guardFns=[]),n.guardFns.push(e),t),middleware:e=>(n.middlewares||(n.middlewares=[]),n.middlewares.push(e),t),meta:e=>(n.meta={...n.meta||{},...e},t),route(t,e,r){const o={path:y(t),component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};return"function"==typeof r?K(o)(r):r&&(o.options=r),n.children.push(o),K(o).api}};return Object.assign(n=>n(t),{api:t})}function C(n,t,e){const r={path:y(n),component:t,children:[],options:{},parent:void 0};return"function"==typeof e?K(r)(e):e&&(r.options=e),S.push(r),K(r).api}const $=t(null),j=t(null),I=t(null),M=t({loading:!1,path:"",pendingPath:""}),F=t({name:"none",stage:"idle"});function L(){const n=e($);try{return n?.read()}catch{return}}function O(){const n=e(j);if(!n)throw new Error("RouteContext not found");return n}function B(){return e(I)??{}}function D(){try{const n=O();if(n?.query)return n.query}catch{}const{query:n}=w(window.location.pathname+window.location.search+window.location.hash);return n}function Q(){return e(M)}function U(){return e(F)}function W(){try{return L()}catch{return}}function T(){const{meta:n,params:t,path:e,query:r}=O();return{data:W(),params:t??{},query:r??{},path:e,meta:n}}const G=200;function J({name:n="fade",stage:t,duration:e=200,children:i}){const[s,d]=r(i),[f,p]=r(null),m=o();return a(()=>{m.current&&clearTimeout(m.current),"exiting"===t&&p(s),"entering"===t&&(d(i),m.current=window.setTimeout(()=>{p(null)},e))},[i,t,e]),"idle"===t?c(u,{children:i}):l("div",{className:h("rtx",`rtx-${n}`),children:[f&&c("div",{className:"rtx-exit",children:f}),c("div",{className:h("rtx-enter",`rtx-${t}`),children:s})]})}let z=!1,H=!1,V=c("div",{children:"Loading..."});const X=({error:n})=>l("div",{children:["Global Error: ",String(n)]});let Y=null;function Z(n,t){z=n,t&&(V=t)}function _(n,t){H=n,Y=t??null}function nn(){return z}function tn(){return H}function en(){return V}function rn(){return Y?({error:n})=>Y(n):X}class on extends n.Component{constructor(n){super(n),this.reset=()=>{this.setState(n=>({hasError:!1,error:null,resetKey:n.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(n){return{hasError:!0,error:n}}render(){if(this.state.hasError){const n=this.props.errorBoundary??(tn()?rn():void 0);return n?c(n,{error:this.state.error,params:this.props.params}):this.props.fallback??l("div",{children:["Error: ",String(this.state.error)]})}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function an({resource:t,errorBoundary:e,children:r,resetKey:o}){const[a,i]=n.useState(null);n.useEffect(()=>{i(null)},[o]);try{return t?.read(),a&&i(null),c(u,{children:r})}catch(n){if(n instanceof Promise)throw n;const t=e??(tn()?rn():void 0);if(t)return c(t,{error:n});throw n}}function sn({chain:n,data:t}){return c(u,{children:function e(r=0){const o=n[r];if(!o)return null;const a=o.node.component;if(!a)return e(r+1);const s={params:o.params,path:o.node.path,query:o.query,hash:o.hash},u=function(n,t){let e=n;for(;e;){const n=e.options?.fallback;if(n)return"function"==typeof n?n(t):n;e=e.parent}return nn()?en():null}(o.node,s),l=Object.assign({},...n.slice(0,r+1).map(n=>n.node.meta)),h=t[N(o.node,s)];return c(on,{fallback:u,errorBoundary:o.node.options?.errorBoundary,params:o.params,children:c(i,{fallback:u,children:c(j.Provider,{value:{path:o.node.path,params:o.params,query:P(o.query),hash:o.hash??"",meta:l},children:c($.Provider,{value:h,children:c(I.Provider,{value:o.params,children:c(an,{resource:h,errorBoundary:o.node.options?.errorBoundary,resetKey:o.params.id||o.params,children:c(a,{children:e(r+1)})})})})})})})}()})}function cn(n,t){const e={};for(const r of n){const n=r.node.options?.loader;if(!n)continue;const o={params:r.params,path:r.node.path,query:r.query,hash:r.hash},a=N(r.node,o),i=r.node.options?.ttl,s=t?.forceReload??r.node.options?.shouldReload?.({prev:void 0,next:o})??!0;e[a]=p(a,()=>Promise.resolve(n(o)),i,s)}return e}let un=null;function ln(){return un}function hn(n,t){const e=R(),[r,o]=n.split("?"),a={...P(o),...t?.query??{}},i=x(e,k(r,t?.params,a,t?.hash));i&&cn(i,{forceReload:!1})}function dn({transition:n,duration:t,wrapper:e}){const[i,s]=r({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:t??G}),u=o(0),l=async(e,r)=>{const o=++u.current,a=function(n){const t=R();return x(t,n)??x(t,"*")??[]}(e),i=await async function(n,t){for(const e of n)for(const n of e.node.guardFns||[]){const r=await n({params:e.params,path:e.node.path,query:e.query,hash:e.hash});if(!1===r)return!1;if("object"==typeof r&&"redirect"in r)return await t(r.redirect),!1}return!0}(a,l);if(!i)return!1;await async function(n){for(const t of n)for(const n of t.node.middlewares||[])await Promise.resolve(n({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(a);const c=function(n,t,e){if(t?.transition)return t.transition;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(e.options?.transition)return e.options.transition;if(e.meta?.transition)return e.meta.transition}return e??"none"}(a,r,n),h=function(n,t,e){if(null!=t?.duration)return t.duration;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(null!=e.options?.duration)return e.options.duration}return null!=e?e:G}(a,r,t);!function(n,t,e,r){n(n=>({...n,loading:!0,pendingPath:t,transition:{name:e,stage:"exiting"},duration:r}))}(s,e,c,h),await new Promise(n=>setTimeout(n,h));const d=r?.shallow?{}:cn(a,r);return o===u.current&&(function(n,t,e,r,o,a){n(n=>({...n,chain:t,data:{...n.data,...e},loading:!1,path:r,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,a,d??{},e,c,h),r?.shallow||window.scrollTo(0,0),!0)};a(()=>{un=l;const n=()=>window.location.pathname+window.location.search+window.location.hash;l(n());const t=()=>l(n());window.addEventListener("popstate",t);const e=()=>{const t=x(R(),n());if(!t)return;const e={};for(const n of t)n.node.options?.revalidateOnFocus&&Object.assign(e,cn([n],{forceReload:!0}));Object.keys(e).length&&s(n=>({...n,data:{...n.data,...e}}))};return window.addEventListener("focus",e),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",e),un=null}},[]);const h=c(sn,{chain:i.chain,data:i.data},i.path);let d=h;if("none"!==i.transition.name&&(d=c(J,{name:i.transition.name,stage:i.transition.stage,duration:i.duration,children:h})),e){d=c(e,{children:d})}return c(M.Provider,{value:{loading:i.loading,path:i.path,pendingPath:i.pendingPath},children:c(F.Provider,{value:i.transition,children:d})})}const fn=[],pn=[];function mn(n){fn.push(n)}function yn(n){pn.push(n)}async function gn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},o=k(n,r.params,r.query,r.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(n){return(await Promise.all(fn.map(t=>t(n)))).every(n=>!1!==n)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=ln();if(!r.shallow&&a){if(!await a(o,r))return console.warn("Navigation blocked by route guard",o),!1}return e?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(n){for(const t of pn)await t(n)}(o),!0}function wn(n,t){return gn(n,t,!1)}function vn(n,t){return wn(n,t)}function Pn(){const n=(n,t)=>gn(n,t);return n.replace=(n,t)=>gn(n,t,!0),n}function bn(n){return Q().path===n}function qn(n,t){return()=>hn(n,t)}function xn({to:n,transition:t,replace:e,params:r,query:i,hash:s,partialMatch:c,disablePrefetch:u,replaceQuery:l}){const h=Pn(),d=Q(),f=o(null),p=k(n,r,i,s,{replaceQuery:l}),m=c?d.path?.startsWith(p):d.path===p;return a(()=>{if(u)return;const n=f.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(hn(p),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[p,u]),{ref:f,fullPath:p,isActive:m,go:async()=>e?h.replace(n,{transition:t,params:r,query:i,hash:s}):h(n,{transition:t,params:r,query:i,hash:s})}}wn.replace=function(n,t){return gn(n,t,!0)};const kn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:o,onKeyDown:a,disablePrefetch:i,...s},u)=>c("a",{ref:u,href:n,...s,onClick:async e=>{if(r&&r(e),e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;e.preventDefault();await t()||console.warn("Navigation blocked:",n)},onMouseEnter:n=>{o&&o(n),i||e?.()},onKeyDown:async e=>{if("Enter"===e.key){e.preventDefault();await t()||console.warn("Navigation blocked:",n)}a&&a(e)}}));function En(n){const{to:t,transition:e,replace:r,activeClassName:o="active",onNavigate:a,partialMatch:i,params:s,query:u,hash:l,className:d,disablePrefetch:f,replaceQuery:p,...m}=n,{ref:y,fullPath:g,isActive:w,go:v}=xn({to:t,transition:e,replace:r,params:s,query:u,hash:l,partialMatch:i,disablePrefetch:f,replaceQuery:p});return c(kn,{ref:y,fullPath:g,onNavigate:async()=>{const n=await v();return a?.(g),n},onPrefetch:()=>hn(g),className:h(d,w&&o),disablePrefetch:f,...m})}function Nn(n){const{to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,className:u,style:l,disablePrefetch:d,replaceQuery:f,...p}=n,{ref:m,fullPath:y,isActive:g,go:w}=xn({to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,disablePrefetch:d,replaceQuery:f}),v="function"==typeof u?u({isActive:g}):u,P="function"==typeof l?l({isActive:g}):l;return c(kn,{ref:m,fullPath:y,onNavigate:async()=>(await w(),!0),onPrefetch:()=>hn(y),className:h(v),style:P,disablePrefetch:d,...p})}kn.displayName="LinkCore";export{G as DEFAULT_DURATION,En as Link,$ as LoaderDataContext,Nn as NavLink,I as ParamsContext,j as RouteContext,dn as RouterProvider,M as RouterStateContext,J as Transition,F as TransitionContext,yn as addAfterNavigationHook,mn as addBeforeNavigationHook,N as buildCacheKey,k as buildPath,f as cleanupCache,A as clearRoutes,m as createResource,rn as getGlobalError,en as getGlobalFallback,ln as getGlobalResolve,v as getQueryFirst,p as getResource,R as getRoutes,tn as getUseGlobalError,nn as getUseGlobalFallback,x as matchRouteChain,wn as navigate,y as normalize,P as normalizeQuery,w as parseQueryHash,hn as prefetch,vn as redirect,C as route,_ as setGlobalError,Z as setGlobalFallback,g as split,xn as useLinkCore,L as useLoaderData,Pn as useNavigate,Q as useNavigation,B as useParams,qn as usePrefetch,D as useQuery,O as useRouteContext,bn as useRouteMatch,T as useRouterData,W as useSafeLoaderData,U as useTransition};
package/build/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");const n=new Map;function o(e,t,r,o){const s=Date.now(),i=n.get(e);if(i&&!o&&(!r||i.expiry>s))return i.resource;const c=a(t());return n.set(e,{resource:c,expiry:s+(r??0)}),c}function a(e,t){let r=t?"success":"pending",n=t??null;const o=e.then(e=>{r="success",n=e},e=>{r="error",n=e});return{read(){if("pending"===r)throw o;if("error"===r)throw n;return n},update(e){n=e,r="success"},get:()=>n}}function s(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function i(e){return s(e).split("/").filter(Boolean)}function c(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");if(!t)return;const n=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[n]?Array.isArray(a[n])?a[n].push(o):a[n]=[a[n],o]:a[n]=o}),{path:s(n),query:a,hash:r??""}}function u(e){const t={};if(!e)return t;if("string"==typeof e){const r=new URLSearchParams(e);r.forEach((e,n)=>{const o=r.getAll(n);t[n]=o.length>1?o:o[0]})}else for(const r in e){const n=e[r];Array.isArray(n)?t[r]=n.map(e=>String(e)):null!=n&&(t[r]=String(n))}return t}function l(e){const t=i(e);let r=10*t.length;for(const e of t)"*"===e?r+=0:e.startsWith(":")?r+=1:r+=5;return r}function p(e){return[...e].sort((e,t)=>l(t.path)-l(e.path))}function h(e,t){const{path:r,query:n,hash:o}=c(t),a=i(r),s=p(e).map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:c}=s.pop(),u=i(e.path),l={...r};let h=0;for(;h<u.length;h++){const e=u[h],r=a[t+h];if(!r)break;if("*"===e){l["*"]=a.slice(t+h).join("/"),h=u.length;break}if(e.startsWith(":"))l[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(h!==u.length)continue;const d=t+h,f=[...c,{node:e,params:l,query:n,hash:o}];if(d===a.length)return f;const x=p(e.children||[]);for(let e=x.length-1;e>=0;e--){const t=i(x[e].path).some(e=>e.startsWith(":"));s.push({node:x[e],segIndex:t?0:d,params:l,chain:f})}}return null}function d(e,t,r,n,o){const{path:a,query:i,hash:u}=c(e);let l=s(a);if(t)for(const e in t)l=l.replace(`:${e}`,encodeURIComponent(t[e]));const p=o?.replaceQuery?{...r??{}}:{...i,...r??{}},h=new URLSearchParams;for(const e in p){const t=p[e];Array.isArray(t)?t.forEach(t=>h.append(e,String(t))):h.append(e,String(t))}const d=h.toString();return d&&(l+=`?${d}`),n?l+=`#${n}`:u&&(l+=`#${u}`),l}function f(e){return JSON.stringify(Object.keys(e||{}).sort().reduce((t,r)=>(t[r]=e[r],t),{}))}function x(e,t){return`${e.path}|${f(t.params)}|${f(t.query)}|${t.hash}`}let m=[];function y(){return m}function g(e){const t={guard:r=>(e.guardFns||(e.guardFns=[]),e.guardFns.push(r),t),middleware:r=>(e.middlewares||(e.middlewares=[]),e.middlewares.push(r),t),meta:r=>(e.meta={...e.meta||{},...r},t),route(t,r,n){const o={path:s(t),component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};return"function"==typeof n?g(o)(n):n&&(o.options=n),e.children.push(o),g(o).api}};return Object.assign(e=>e(t),{api:t})}const w=e.createContext(null),v=e.createContext(null),b=e.createContext(null),P=e.createContext({loading:!1,path:"",pendingPath:""}),j=e.createContext({name:"none",stage:"idle"});function q(){const t=e.useContext(w);try{return t?.read()}catch{return}}function C(){const t=e.useContext(v);if(!t)throw new Error("RouteContext not found");return t}function R(){return e.useContext(P)}function k(){try{return q()}catch{return}}const E=200;function N({name:n="fade",stage:o,duration:a=200,children:s}){const[i,c]=e.useState(s),[u,l]=e.useState(null),p=e.useRef();return e.useEffect(()=>{p.current&&clearTimeout(p.current),"exiting"===o&&l(i),"entering"===o&&(c(s),p.current=window.setTimeout(()=>{l(null)},a))},[s,o,a]),"idle"===o?t.jsx(t.Fragment,{children:s}):t.jsxs("div",{className:r("rtx",`rtx-${n}`),children:[u&&t.jsx("div",{className:"rtx-exit",children:u}),t.jsx("div",{className:r("rtx-enter",`rtx-${o}`),children:i})]})}let S=!1,A=!1,F=t.jsx("div",{children:"Loading..."});const L=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});let D=null;function K(){return S}function Q(){return A}function I(){return F}function M(){return D?({error:e})=>D(e):L}class U extends e.Component{constructor(e){super(e),this.reset=()=>{this.setState(e=>({hasError:!1,error:null,resetKey:e.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}render(){if(this.state.hasError){const e=this.props.errorBoundary??(Q()?M():void 0);return e?t.jsx(e,{error:this.state.error,params:this.props.params}):this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]})}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function $({resource:r,errorBoundary:n,children:o,resetKey:a}){const[s,i]=e.useState(null);e.useEffect(()=>{i(null)},[a]);try{return r?.read(),s&&i(null),t.jsx(t.Fragment,{children:o})}catch(e){if(e instanceof Promise)throw e;const r=n??(Q()?M():void 0);if(r)return t.jsx(r,{error:e});throw e}}function O({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const s=r[a];if(!s)return null;const i=s.node.component;if(!i)return o(a+1);const c={params:s.params,path:s.node.path,query:s.query,hash:s.hash},l=function(e,t){let r=e;for(;r;){const e=r.options?.fallback;if(e)return"function"==typeof e?e(t):e;r=r.parent}return K()?I():null}(s.node,c),p=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta)),h=n[x(s.node,c)];return t.jsx(U,{fallback:l,errorBoundary:s.node.options?.errorBoundary,params:s.params,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(v.Provider,{value:{path:s.node.path,params:s.params,query:u(s.query),hash:s.hash??"",meta:p},children:t.jsx(w.Provider,{value:h,children:t.jsx(b.Provider,{value:s.params,children:t.jsx($,{resource:h,errorBoundary:s.node.options?.errorBoundary,resetKey:s.params.id||s.params,children:t.jsx(i,{children:o(a+1)})})})})})})})}()})}let T=null;function B(){return T}function G(e,t){const r=y(),[n,o]=e.split("?"),a={...u(o),...t?.query??{}},s=h(r,d(n,t?.params,a,t?.hash));s&&W(s,{forceReload:!1})}function W(e,t){const r={};return e.forEach(e=>{const n=e.node.options?.loader;if(!n)return;const a={params:e.params,path:e.node.path,query:e.query,hash:e.hash},s=x(e.node,a),i=e.node.options?.ttl,c=t?.forceReload??e.node.options?.shouldReload?.({prev:void 0,next:a})??!0;r[s]=o(s,()=>Promise.resolve(n(a)),i,c)}),r}const H=[],z=[];async function J(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=d(e,n.params,n.query,n.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(e){return(await Promise.all(H.map(t=>t(e)))).every(e=>!1!==e)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=B();if(!n.shallow&&a){if(!await a(o,n))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(e){for(const t of z)await t(e)}(o),!0}function _(e,t){return J(e,t,!1)}function V(){const e=(e,t)=>J(e,t);return e.replace=(e,t)=>J(e,t,!0),e}function X({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c,replaceQuery:u}){const l=V(),p=R(),h=e.useRef(null),f=d(t,o,a,s,{replaceQuery:u}),x=i?p.path?.startsWith(f):p.path===f;return e.useEffect(()=>{if(c)return;const e=h.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(G(f),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[f,c]),{ref:h,fullPath:f,isActive:x,go:async()=>n?l.replace(t,{transition:r,params:o,query:a,hash:s}):l(t,{transition:r,params:o,query:a,hash:s})}}_.replace=(e,t)=>J(e,t,!0);const Y=e.forwardRef(({fullPath:e,onNavigate:r,onPrefetch:n,onClick:o,onMouseEnter:a,onKeyDown:s,disablePrefetch:i,...c},u)=>t.jsx("a",{ref:u,href:e,...c,onClick:async t=>{if(o&&o(t),t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;t.preventDefault();await r()||console.warn("Navigation blocked:",e)},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:async t=>{if("Enter"===t.key){t.preventDefault();await r()||console.warn("Navigation blocked:",e)}s&&s(t)}}));Y.displayName="LinkCore",exports.DEFAULT_DURATION=E,exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:l,hash:p,className:h,disablePrefetch:d,replaceQuery:f,...x}=e,{ref:m,fullPath:y,isActive:g,go:w}=X({to:n,transition:o,replace:a,params:u,query:l,hash:p,partialMatch:c,disablePrefetch:d,replaceQuery:f});return t.jsx(Y,{ref:m,fullPath:y,onNavigate:async()=>{const e=await w();return i?.(y),e},onPrefetch:()=>G(y),className:r(h,g&&s),disablePrefetch:d,...x})},exports.LoaderDataContext=w,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:l,style:p,disablePrefetch:h,replaceQuery:d,...f}=e,{ref:x,fullPath:m,isActive:y,go:g}=X({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:h,replaceQuery:d}),w="function"==typeof l?l({isActive:y}):l,v="function"==typeof p?p({isActive:y}):p;return t.jsx(Y,{ref:x,fullPath:m,onNavigate:async()=>(await g(),!0),onPrefetch:()=>G(m),className:r(w),style:v,disablePrefetch:h,...f})},exports.ParamsContext=b,exports.RouteContext=v,exports.RouterProvider=function({transition:r,duration:n,wrapper:o}){const[a,s]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:n??E}),i=e.useRef(0);async function c(e,t){const o=++i.current,a=y(),c=h(a,e)??h(a,"*")??[];for(const t of c)for(const r of t.node.guardFns||[]){if(!await r({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked",e),!1}for(const e of c)for(const t of e.node.middlewares||[])await t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}).catch(console.error);const u=function(e,t,r){if(t?.transition)return t.transition;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(c,t,r),l=function(e,t,r){if(null!=t?.duration)return t.duration;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:E}(c,t,n);s(t=>({...t,loading:!0,pendingPath:e,transition:{name:u,stage:"exiting"},duration:l})),await new Promise(e=>setTimeout(e,l));const p=W(c,t);return o===i.current&&(s(t=>({...t,chain:c,data:{...t.data,...p},loading:!1,path:e,pendingPath:"",transition:{name:u,stage:"entering"},duration:l})),window.scrollTo(0,0),!0)}e.useEffect(()=>{T=c;const e=()=>window.location.pathname+window.location.search+window.location.hash;c(e());const t=()=>c(e());window.addEventListener("popstate",t);const r=()=>{const t=h(y(),e());if(!t)return;const r={};t.forEach(e=>{e.node.options?.revalidateOnFocus&&Object.assign(r,W([e],{forceReload:!0}))}),Object.keys(r).length&&s(e=>({...e,data:{...e.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r)}},[]);const u=t.jsx(O,{chain:a.chain,data:a.data},a.path);let l=u;if("none"!==a.transition.name&&(l=t.jsx(N,{name:a.transition.name,stage:a.transition.stage,duration:a.duration,children:u})),o){const e=o;l=t.jsx(e,{children:l})}return t.jsx(P.Provider,{value:{loading:a.loading,path:a.path,pendingPath:a.pendingPath},children:t.jsx(j.Provider,{value:a.transition,children:l})})},exports.RouterStateContext=P,exports.Transition=N,exports.TransitionContext=j,exports.addAfterNavigationHook=function(e){z.push(e)},exports.addBeforeNavigationHook=function(e){H.push(e)},exports.buildCacheKey=x,exports.buildPath=d,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[o,a]of n.entries())if(a.expiry<=t&&(n.delete(o),r++,r>=e))break},exports.clearRoutes=function(){m=[]},exports.createResource=a,exports.getGlobalError=M,exports.getGlobalFallback=I,exports.getGlobalResolve=B,exports.getQueryFirst=function(e,t){const r=e[t];return Array.isArray(r)?r[0]:r},exports.getResource=o,exports.getRoutes=y,exports.getUseGlobalError=Q,exports.getUseGlobalFallback=K,exports.matchRouteChain=h,exports.navigate=_,exports.normalize=s,exports.normalizeQuery=u,exports.parseQueryHash=c,exports.prefetch=G,exports.redirect=function(e,t){return _(e,t)},exports.route=function(e,t,r){const n={path:s(e),component:t,children:[],options:{},parent:void 0};return"function"==typeof r?g(n)(r):r&&(n.options=r),m.push(n),g(n).api},exports.setGlobalError=function(e,t){A=e,D=t??null},exports.setGlobalFallback=function(e,t){S=e,t&&(F=t)},exports.split=i,exports.useLinkCore=X,exports.useLoaderData=q,exports.useNavigate=V,exports.useNavigation=R,exports.useParams=function(){return e.useContext(b)??{}},exports.usePrefetch=function(e,t){return()=>G(e,t)},exports.useQuery=function(){try{const e=C();if(e?.query)return e.query}catch{}const{query:e}=c(window.location.pathname+window.location.search+window.location.hash);return e},exports.useRouteContext=C,exports.useRouteMatch=function(e){return R().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=C();return{data:k(),params:t??{},query:n??{},path:r,meta:e}},exports.useSafeLoaderData=k,exports.useTransition=function(){return e.useContext(j)};
1
+ "use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");const n=new Map;function o(e,t,r,o){const s=Date.now(),i=n.get(e);if(i&&!o&&(!r||i.expiry>s))return i.resource;const c=a(t());return n.set(e,{resource:c,expiry:s+(r??0)}),c}function a(e,t){let r=t?"success":"pending",n=t??null;const o=e.then(e=>{r="success",n=e},e=>{r="error",n=e});return{read(){if("pending"===r)throw o;if("error"===r)throw n;return n},update(e){n=e,r="success"},get:()=>n}}function s(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function i(e){return s(e).split("/").filter(Boolean)}function c(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");if(!t)return;const n=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[n]?Array.isArray(a[n])?a[n].push(o):a[n]=[a[n],o]:a[n]=o}),{path:s(n),query:a,hash:r??""}}function u(e){const t={};if(!e)return t;if("string"==typeof e){const r=new URLSearchParams(e);r.forEach((e,n)=>{const o=r.getAll(n);t[n]=o.length>1?o:o[0]})}else for(const r in e){const n=e[r];Array.isArray(n)?t[r]=n.map(e=>String(e)):null!=n&&(t[r]=String(n))}return t}function l(e){const t=i(e);let r=10*t.length;for(const e of t)"*"===e?r+=0:e.startsWith(":")?r+=1:r+=5;return r}function p(e){return[...e].sort((e,t)=>l(t.path)-l(e.path))}function h(e,t){const{path:r,query:n,hash:o}=c(t),a=i(r),s=p(e).map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:c}=s.pop(),u=i(e.path),l={...r};let h=0;for(;h<u.length;h++){const e=u[h],r=a[t+h];if(!r)break;if("*"===e){l["*"]=a.slice(t+h).join("/"),h=u.length;break}if(e.startsWith(":"))l[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(h!==u.length)continue;const d=t+h,f=[...c,{node:e,params:l,query:n,hash:o}];if(d===a.length)return f;const x=p(e.children||[]);for(let e=x.length-1;e>=0;e--){const t=i(x[e].path).some(e=>e.startsWith(":"));s.push({node:x[e],segIndex:t?0:d,params:l,chain:f})}}return null}function d(e,t,r,n,o){const{path:a,query:i,hash:u}=c(e);let l=s(a);if(t)for(const e in t)l=l.replace(`:${e}`,encodeURIComponent(t[e]));const p=o?.replaceQuery?{...r??{}}:{...i,...r??{}},h=new URLSearchParams;for(const e in p){const t=p[e];Array.isArray(t)?t.forEach(t=>h.append(e,String(t))):h.append(e,String(t))}const d=h.toString();return d&&(l+=`?${d}`),n?l+=`#${n}`:u&&(l+=`#${u}`),l}function f(e){return JSON.stringify(Object.keys(e||{}).sort().reduce((t,r)=>(t[r]=e[r],t),{}))}function x(e,t){return`${e.path}|${f(t.params)}|${f(t.query)}|${t.hash}`}let m=[];function y(){return m}function g(e){const t={guard:r=>(e.guardFns||(e.guardFns=[]),e.guardFns.push(r),t),middleware:r=>(e.middlewares||(e.middlewares=[]),e.middlewares.push(r),t),meta:r=>(e.meta={...e.meta||{},...r},t),route(t,r,n){const o={path:s(t),component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};return"function"==typeof n?g(o)(n):n&&(o.options=n),e.children.push(o),g(o).api}};return Object.assign(e=>e(t),{api:t})}const w=e.createContext(null),v=e.createContext(null),b=e.createContext(null),P=e.createContext({loading:!1,path:"",pendingPath:""}),j=e.createContext({name:"none",stage:"idle"});function q(){const t=e.useContext(w);try{return t?.read()}catch{return}}function C(){const t=e.useContext(v);if(!t)throw new Error("RouteContext not found");return t}function R(){return e.useContext(P)}function k(){try{return q()}catch{return}}const E=200;function N({name:n="fade",stage:o,duration:a=200,children:s}){const[i,c]=e.useState(s),[u,l]=e.useState(null),p=e.useRef();return e.useEffect(()=>{p.current&&clearTimeout(p.current),"exiting"===o&&l(i),"entering"===o&&(c(s),p.current=window.setTimeout(()=>{l(null)},a))},[s,o,a]),"idle"===o?t.jsx(t.Fragment,{children:s}):t.jsxs("div",{className:r("rtx",`rtx-${n}`),children:[u&&t.jsx("div",{className:"rtx-exit",children:u}),t.jsx("div",{className:r("rtx-enter",`rtx-${o}`),children:i})]})}let S=!1,A=!1,F=t.jsx("div",{children:"Loading..."});const L=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});let D=null;function K(){return S}function Q(){return A}function I(){return F}function M(){return D?({error:e})=>D(e):L}class U extends e.Component{constructor(e){super(e),this.reset=()=>{this.setState(e=>({hasError:!1,error:null,resetKey:e.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}render(){if(this.state.hasError){const e=this.props.errorBoundary??(Q()?M():void 0);return e?t.jsx(e,{error:this.state.error,params:this.props.params}):this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]})}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function $({resource:r,errorBoundary:n,children:o,resetKey:a}){const[s,i]=e.useState(null);e.useEffect(()=>{i(null)},[a]);try{return r?.read(),s&&i(null),t.jsx(t.Fragment,{children:o})}catch(e){if(e instanceof Promise)throw e;const r=n??(Q()?M():void 0);if(r)return t.jsx(r,{error:e});throw e}}function O({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const s=r[a];if(!s)return null;const i=s.node.component;if(!i)return o(a+1);const c={params:s.params,path:s.node.path,query:s.query,hash:s.hash},l=function(e,t){let r=e;for(;r;){const e=r.options?.fallback;if(e)return"function"==typeof e?e(t):e;r=r.parent}return K()?I():null}(s.node,c),p=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta)),h=n[x(s.node,c)];return t.jsx(U,{fallback:l,errorBoundary:s.node.options?.errorBoundary,params:s.params,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(v.Provider,{value:{path:s.node.path,params:s.params,query:u(s.query),hash:s.hash??"",meta:p},children:t.jsx(w.Provider,{value:h,children:t.jsx(b.Provider,{value:s.params,children:t.jsx($,{resource:h,errorBoundary:s.node.options?.errorBoundary,resetKey:s.params.id||s.params,children:t.jsx(i,{children:o(a+1)})})})})})})})}()})}function T(e,t){const r={};for(const n of e){const e=n.node.options?.loader;if(!e)continue;const a={params:n.params,path:n.node.path,query:n.query,hash:n.hash},s=x(n.node,a),i=n.node.options?.ttl,c=t?.forceReload??n.node.options?.shouldReload?.({prev:void 0,next:a})??!0;r[s]=o(s,()=>Promise.resolve(e(a)),i,c)}return r}let B=null;function G(){return B}function W(e,t){const r=y(),[n,o]=e.split("?"),a={...u(o),...t?.query??{}},s=h(r,d(n,t?.params,a,t?.hash));s&&T(s,{forceReload:!1})}const H=[],z=[];async function J(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=d(e,n.params,n.query,n.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(e){return(await Promise.all(H.map(t=>t(e)))).every(e=>!1!==e)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=G();if(!n.shallow&&a){if(!await a(o,n))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(e){for(const t of z)await t(e)}(o),!0}function _(e,t){return J(e,t,!1)}function V(){const e=(e,t)=>J(e,t);return e.replace=(e,t)=>J(e,t,!0),e}function X({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c,replaceQuery:u}){const l=V(),p=R(),h=e.useRef(null),f=d(t,o,a,s,{replaceQuery:u}),x=i?p.path?.startsWith(f):p.path===f;return e.useEffect(()=>{if(c)return;const e=h.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(W(f),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[f,c]),{ref:h,fullPath:f,isActive:x,go:async()=>n?l.replace(t,{transition:r,params:o,query:a,hash:s}):l(t,{transition:r,params:o,query:a,hash:s})}}_.replace=function(e,t){return J(e,t,!0)};const Y=e.forwardRef(({fullPath:e,onNavigate:r,onPrefetch:n,onClick:o,onMouseEnter:a,onKeyDown:s,disablePrefetch:i,...c},u)=>t.jsx("a",{ref:u,href:e,...c,onClick:async t=>{if(o&&o(t),t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;t.preventDefault();await r()||console.warn("Navigation blocked:",e)},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:async t=>{if("Enter"===t.key){t.preventDefault();await r()||console.warn("Navigation blocked:",e)}s&&s(t)}}));Y.displayName="LinkCore",exports.DEFAULT_DURATION=E,exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:l,hash:p,className:h,disablePrefetch:d,replaceQuery:f,...x}=e,{ref:m,fullPath:y,isActive:g,go:w}=X({to:n,transition:o,replace:a,params:u,query:l,hash:p,partialMatch:c,disablePrefetch:d,replaceQuery:f});return t.jsx(Y,{ref:m,fullPath:y,onNavigate:async()=>{const e=await w();return i?.(y),e},onPrefetch:()=>W(y),className:r(h,g&&s),disablePrefetch:d,...x})},exports.LoaderDataContext=w,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:l,style:p,disablePrefetch:h,replaceQuery:d,...f}=e,{ref:x,fullPath:m,isActive:y,go:g}=X({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:h,replaceQuery:d}),w="function"==typeof l?l({isActive:y}):l,v="function"==typeof p?p({isActive:y}):p;return t.jsx(Y,{ref:x,fullPath:m,onNavigate:async()=>(await g(),!0),onPrefetch:()=>W(m),className:r(w),style:v,disablePrefetch:h,...f})},exports.ParamsContext=b,exports.RouteContext=v,exports.RouterProvider=function({transition:r,duration:n,wrapper:o}){const[a,s]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:n??E}),i=e.useRef(0),c=async(e,t)=>{const o=++i.current,a=function(e){const t=y();return h(t,e)??h(t,"*")??[]}(e),u=await async function(e,t){for(const r of e)for(const e of r.node.guardFns||[]){const n=await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===n)return!1;if("object"==typeof n&&"redirect"in n)return await t(n.redirect),!1}return!0}(a,c);if(!u)return!1;await async function(e){for(const t of e)for(const e of t.node.middlewares||[])await Promise.resolve(e({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(a);const l=function(e,t,r){if(t?.transition)return t.transition;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(a,t,r),p=function(e,t,r){if(null!=t?.duration)return t.duration;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:E}(a,t,n);!function(e,t,r,n){e(e=>({...e,loading:!0,pendingPath:t,transition:{name:r,stage:"exiting"},duration:n}))}(s,e,l,p),await new Promise(e=>setTimeout(e,p));const d=t?.shallow?{}:T(a,t);return o===i.current&&(function(e,t,r,n,o,a){e(e=>({...e,chain:t,data:{...e.data,...r},loading:!1,path:n,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,a,d??{},e,l,p),t?.shallow||window.scrollTo(0,0),!0)};e.useEffect(()=>{B=c;const e=()=>window.location.pathname+window.location.search+window.location.hash;c(e());const t=()=>c(e());window.addEventListener("popstate",t);const r=()=>{const t=h(y(),e());if(!t)return;const r={};for(const e of t)e.node.options?.revalidateOnFocus&&Object.assign(r,T([e],{forceReload:!0}));Object.keys(r).length&&s(e=>({...e,data:{...e.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r),B=null}},[]);const u=t.jsx(O,{chain:a.chain,data:a.data},a.path);let l=u;if("none"!==a.transition.name&&(l=t.jsx(N,{name:a.transition.name,stage:a.transition.stage,duration:a.duration,children:u})),o){const e=o;l=t.jsx(e,{children:l})}return t.jsx(P.Provider,{value:{loading:a.loading,path:a.path,pendingPath:a.pendingPath},children:t.jsx(j.Provider,{value:a.transition,children:l})})},exports.RouterStateContext=P,exports.Transition=N,exports.TransitionContext=j,exports.addAfterNavigationHook=function(e){z.push(e)},exports.addBeforeNavigationHook=function(e){H.push(e)},exports.buildCacheKey=x,exports.buildPath=d,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[o,a]of n.entries())if(a.expiry<=t&&(n.delete(o),r++,r>=e))break},exports.clearRoutes=function(){m=[]},exports.createResource=a,exports.getGlobalError=M,exports.getGlobalFallback=I,exports.getGlobalResolve=G,exports.getQueryFirst=function(e,t){const r=e[t];return Array.isArray(r)?r[0]:r},exports.getResource=o,exports.getRoutes=y,exports.getUseGlobalError=Q,exports.getUseGlobalFallback=K,exports.matchRouteChain=h,exports.navigate=_,exports.normalize=s,exports.normalizeQuery=u,exports.parseQueryHash=c,exports.prefetch=W,exports.redirect=function(e,t){return _(e,t)},exports.route=function(e,t,r){const n={path:s(e),component:t,children:[],options:{},parent:void 0};return"function"==typeof r?g(n)(r):r&&(n.options=r),m.push(n),g(n).api},exports.setGlobalError=function(e,t){A=e,D=t??null},exports.setGlobalFallback=function(e,t){S=e,t&&(F=t)},exports.split=i,exports.useLinkCore=X,exports.useLoaderData=q,exports.useNavigate=V,exports.useNavigation=R,exports.useParams=function(){return e.useContext(b)??{}},exports.usePrefetch=function(e,t){return()=>W(e,t)},exports.useQuery=function(){try{const e=C();if(e?.query)return e.query}catch{}const{query:e}=c(window.location.pathname+window.location.search+window.location.hash);return e},exports.useRouteContext=C,exports.useRouteMatch=function(e){return R().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=C();return{data:k(),params:t??{},query:n??{},path:r,meta:e}},exports.useSafeLoaderData=k,exports.useTransition=function(){return e.useContext(j)};
@@ -8,7 +8,8 @@ type RouterProviderProps = {
8
8
  children: React.ReactNode;
9
9
  }>;
10
10
  };
11
- export declare function getGlobalResolve(): ((path: string, opts?: NavigateOpts) => Promise<boolean>) | null;
12
- export declare function prefetch(path: string, options?: NavigateOpts): void;
11
+ type ResolveFn = (path: string, opts?: NavigateOpts<any>) => Promise<boolean>;
12
+ export declare function getGlobalResolve(): ResolveFn | null;
13
+ export declare function prefetch<P extends string>(path: P, options?: NavigateOpts<P>): void;
13
14
  export declare function RouterProvider({ transition: defaultTransition, duration: providerDuration, wrapper, }: RouterProviderProps): JSX.Element;
14
15
  export {};
@@ -1,8 +1,8 @@
1
- import type { TransitionName } from './types';
2
- export type NavigateOpts = {
1
+ import type { ExtractParams, TransitionName } from './types';
2
+ export type NavigateOpts<P extends string> = {
3
3
  transition?: TransitionName;
4
4
  forceReload?: boolean;
5
- params?: Record<string, string | number>;
5
+ params?: ExtractParams<P>;
6
6
  query?: Record<string, string | number>;
7
7
  hash?: string;
8
8
  shallow?: boolean;
@@ -10,14 +10,14 @@ export type NavigateOpts = {
10
10
  };
11
11
  export declare function addBeforeNavigationHook(fn: (path: string) => boolean | Promise<boolean>): void;
12
12
  export declare function addAfterNavigationHook(fn: (path: string) => void | Promise<void>): void;
13
- export declare function navigate(path: string, opts?: NavigateOpts): Promise<boolean>;
13
+ export declare function navigate<P extends string>(path: P, opts?: NavigateOpts<P>): Promise<boolean>;
14
14
  export declare namespace navigate {
15
- var replace: (path: string, opts?: NavigateOpts) => Promise<boolean>;
15
+ var replace: <P extends string>(path: P, opts?: NavigateOpts<P>) => Promise<boolean>;
16
16
  }
17
- export declare function redirect(path: string, opts?: NavigateOpts): Promise<boolean>;
17
+ export declare function redirect<P extends string>(path: P, opts?: NavigateOpts<P>): Promise<boolean>;
18
18
  export declare function useNavigate(): {
19
- (path: string, opts?: NavigateOpts): Promise<boolean>;
20
- replace(path: string, opts?: NavigateOpts): Promise<boolean>;
19
+ <P extends string>(path: P, opts?: NavigateOpts<P>): Promise<boolean>;
20
+ replace<P extends string>(path: P, opts?: NavigateOpts<P>): Promise<boolean>;
21
21
  };
22
22
  export declare function useRouteMatch(pathPattern: string): boolean;
23
- export declare function usePrefetch(path: string, opts?: NavigateOpts): () => void;
23
+ export declare function usePrefetch<P extends string>(path: P, opts?: NavigateOpts<P>): () => void;
@@ -1,14 +1,17 @@
1
1
  import type { ReactNode } from 'react';
2
2
  export type TransitionName = 'fade' | 'slide' | 'scale' | 'none';
3
3
  export type TransitionStage = 'idle' | 'entering' | 'exiting';
4
- type LoaderContext<P = any> = {
4
+ export type QueryValue = string | number | string[] | number[];
5
+ export type Query = Record<string, QueryValue>;
6
+ export type LoaderContext<P = any> = {
5
7
  params: P;
6
8
  path: string;
7
- query?: Record<string, string>;
9
+ query?: Query;
8
10
  hash?: string;
9
11
  };
12
+ export type LoaderFn<P, R> = (context: LoaderContext<P>) => Promise<R> | R;
10
13
  export type RouteOptions<P = any, R = any> = {
11
- loader?: (context: LoaderContext<P>) => Promise<R> | R;
14
+ loader?: LoaderFn<P, R>;
12
15
  ttl?: number;
13
16
  shouldReload?: (context: {
14
17
  prev?: LoaderContext<P>;
@@ -27,10 +30,11 @@ export type RouteOptions<P = any, R = any> = {
27
30
  transition?: TransitionName;
28
31
  duration?: number;
29
32
  };
30
- export type QueryValue = string | string[];
31
- export type Query = Record<string, QueryValue>;
32
- export type GuardFn<P = any> = (context: LoaderContext<P>) => boolean | Promise<boolean>;
33
- export type MiddlewareFn<P = any> = (context: LoaderContext<P>) => any | Promise<any>;
33
+ type GuardResult = boolean | {
34
+ redirect: string;
35
+ };
36
+ export type GuardFn<P = any> = (ctx: LoaderContext<P>) => GuardResult | Promise<GuardResult>;
37
+ export type MiddlewareFn<P = any> = (context: LoaderContext<P>) => void | Promise<void>;
34
38
  export type RouteComponent = React.ComponentType<{
35
39
  children?: ReactNode;
36
40
  }> | (() => Promise<{
@@ -48,6 +52,12 @@ export type RouteNode<P = any, R = any> = {
48
52
  meta?: Record<string, any>;
49
53
  parent?: RouteNode;
50
54
  };
55
+ export type InternalRouteMatch<P = any> = {
56
+ node: RouteNode<P>;
57
+ params: P;
58
+ query?: Query;
59
+ hash?: string;
60
+ };
51
61
  export type ExtractParams<Path extends string> = Path extends `${string}:${infer Param}/${infer Rest}` ? {
52
62
  [K in Param | keyof ExtractParams<`/${Rest}`>]: string;
53
63
  } : Path extends `${string}:${infer Param}` ? {
@@ -59,4 +69,6 @@ export type BuilderRouteAPI<Params = any> = {
59
69
  meta: (obj: Record<string, any>) => BuilderRouteAPI<Params>;
60
70
  route: <Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<Params & ExtractParams<Path>>) => void) | RouteOptions<Params & ExtractParams<Path>, R>) => BuilderRouteAPI<Params & ExtractParams<Path>>;
61
71
  };
72
+ export type InferParams<T> = T extends InternalRouteMatch<infer P> ? P : never;
73
+ export type InferLoaderData<T> = T extends RouteNode<any, infer R> ? R : never;
62
74
  export {};
@@ -6,7 +6,7 @@ export declare function parseQueryHash(pathname: string): {
6
6
  query: Query;
7
7
  hash: string;
8
8
  };
9
- export declare function getQueryFirst(q: Query, key: string): string;
9
+ export declare function getQueryFirst(q: Query, key: string): string | number;
10
10
  export declare function normalizeQuery(input?: string | Record<string, any>): Record<string, string | string[]>;
11
11
  export declare function matchRouteChain(routes: RouteNode[], pathname: string): any[] | null;
12
12
  export declare function buildPath(path: string, params?: Record<string, any>, query?: Record<string, any>, hash?: string, options?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "routexiz",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-z2",
4
4
  "description": "A modern tree-based router for React with intent-driven navigation, Suspense-first data loading, and built-in guards, middleware, and caching.",
5
5
  "author": "Delpi.Kye",
6
6
  "license": "MIT",