routexiz 0.0.21 → 0.1.0-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,7 @@
6
6
 
7
7
  A lightweight, powerful, and modern router for React.
8
8
 
9
- > Tree-based routing — similar to a TreeView, resolving a single branch per navigation.
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
10
 
11
11
  ---
12
12
 
@@ -78,20 +78,28 @@ route("/", Layout, root => {
78
78
  dash.middleware(fn)
79
79
  dash.meta({ title: "Dashboard" })
80
80
  })
81
+
82
+ // more
81
83
  })
82
84
 
83
- // admin
85
+ // /admin
84
86
  // admin/dashboard
85
- route("/addmin", Admin, root => {
87
+ route("/admin", Admin, root => {
86
88
  root.route("/dashboard", AdminDashboard, dash => {
87
89
  dash.guard(fn)
88
90
  dash.middleware(fn)
89
91
  dash.meta({ title: "Dashboard" })
90
92
  })
93
+
94
+ // more
91
95
  })
92
96
 
97
+ // more
98
+
93
99
  ```
94
100
 
101
+ > Supports multiple route trees and deeply nested routing via a flexible builder API.
102
+
95
103
  ---
96
104
 
97
105
  # Navigation
@@ -150,6 +158,14 @@ const data = useLoaderData()
150
158
 
151
159
  # Guards
152
160
 
161
+ A guard determines whether navigation is allowed.
162
+
163
+ - Runs before entering a route
164
+ - Can block navigation
165
+ - Returns:
166
+ - true → allow
167
+ - false → block
168
+
153
169
  ```ts
154
170
  dash.guard(({ params }) => {
155
171
  if (!isLoggedIn()) return false
@@ -160,6 +176,12 @@ dash.guard(({ params }) => {
160
176
 
161
177
  # Middleware
162
178
 
179
+ A middleware performs side effects during navigation.
180
+
181
+ - Runs after guards
182
+ - Cannot block navigation
183
+ - Used for processing, not decision-making
184
+
163
185
  ```ts
164
186
  dash.middleware(async ({ params }) => {
165
187
  console.log("run side effects")
@@ -168,6 +190,24 @@ dash.middleware(async ({ params }) => {
168
190
 
169
191
  ---
170
192
 
193
+ # Key Differences Guards - Middleware
194
+
195
+ | Feature | Guard | Middleware |
196
+ | --------------- | ------------------------- | -------------------- |
197
+ | Purpose | Control access | Perform side effects |
198
+ | Can block route | ✅ Yes | ❌ No |
199
+ | Return value | `true / false / redirect` | Ignored |
200
+ | Execution order | First | After guards |
201
+ | Async support | ✅ Yes | ✅ Yes |
202
+
203
+ <br />
204
+
205
+ <b>Mental Model</b>
206
+ - Guard = "Can we enter?"
207
+ - Middleware = "Do something while entering"
208
+
209
+ ---
210
+
171
211
  # Prefetch
172
212
 
173
213
  - Hover
@@ -189,6 +229,11 @@ export default function UsersList() {
189
229
  <Link to="/users/2" params={{ id: 2 }} query={{ tab: "profile" }}>
190
230
  User 2 (prefetch on viewport)
191
231
  </Link>
232
+
233
+ {/* disablePrefetch */}
234
+ <Link to="/users/2" params={{ id: 2 }} query={{ tab: "profile" }} disablePrefetch>
235
+ User 2 (prefetch on viewport)
236
+ </Link>
192
237
  </div>
193
238
  )
194
239
  }
@@ -418,6 +463,20 @@ function PageTransition() {
418
463
  }
419
464
  ```
420
465
 
466
+ ```css
467
+ .fade-entering {
468
+ opacity: 0;
469
+ transition: opacity 0.3s ease;
470
+ }
471
+
472
+ .fade-exiting {
473
+ opacity: 1;
474
+ }
475
+ ```
476
+
477
+ > You are responsible for defining CSS transitions for each stage.
478
+
479
+
421
480
  ## 6️⃣ useRouterData()
422
481
 
423
482
  ```ts
@@ -425,7 +484,7 @@ import React from "react"
425
484
  import { useRouterData } from "./hooks"
426
485
 
427
486
  export function UserPage() {
428
- const { data, params, query, path } = useRouterData<{ id: string; name: string }>()
487
+ const { data, params, query, path, meta } = useRouterData<{ id: string; name: string }>()
429
488
 
430
489
  return (
431
490
  <div>
@@ -434,6 +493,7 @@ export function UserPage() {
434
493
  <div>Params: {JSON.stringify(params)}</div>
435
494
  <div>Query: {JSON.stringify(query)}</div>
436
495
  <div>Data: {JSON.stringify(data)}</div>
496
+ <div>Meta: {JSON.stringify(meta)}</div>
437
497
  </div>
438
498
  )
439
499
  }
@@ -501,9 +561,10 @@ route("/", Home, root => {
501
561
 
502
562
  root.route("/dashboard", Dashboard, dash => {
503
563
 
504
- dash.route("/dashboard/users", Users)
564
+ // child
565
+ dash.route("/users", Users)
505
566
 
506
- dash.route("/dashboard/users/:id", User, {
567
+ dash.route("/users/:id", User, {
507
568
 
508
569
  loader: async ({ params }) => {
509
570
  await new Promise(r => setTimeout(r, 400))
@@ -730,6 +791,47 @@ render tree
730
791
 
731
792
  ---
732
793
 
794
+ # Duplicate Routes
795
+
796
+ `routexiz` does not support duplicate route paths.
797
+
798
+ Each route must resolve to a unique full path in the route tree.
799
+
800
+ ## Why?
801
+
802
+ Because routing is tree-based:
803
+
804
+ - Navigation resolves a single path from root to leaf
805
+ - Duplicate paths create ambiguous matches
806
+ - This leads to unpredictable behavior
807
+
808
+ ## Important
809
+
810
+ ⚠️ Duplicate routes are NOT validated automatically.
811
+
812
+ The router will NOT throw an error or warning.
813
+
814
+ ## Requirement
815
+
816
+ Ensure all routes are unique:
817
+
818
+ ```ts
819
+ route("/", Layout, root => {
820
+ root.route("/dashboard", Dashboard)
821
+ root.route("/dashboard/users", Users)
822
+
823
+ // ❌ BAD: duplicate
824
+ root.route("/dashboard/users", AnotherUsers)
825
+ })
826
+ ```
827
+
828
+ ## Recommendation
829
+ - Keep route paths explicit and unique
830
+ - Avoid defining the same full path in multiple branches
831
+ - Use nesting correctly to prevent duplication
832
+
833
+ ---
834
+
733
835
  # License
734
836
 
735
837
  MIT
@@ -1 +1 @@
1
- import n,{createContext as t,useContext as e,Suspense as r,useState as o,useEffect as a,forwardRef as s,useRef as i}from"react";import{jsx as c,jsxs as h,Fragment as u}from"react/jsx-runtime";import l from"joinclass";let d=[];function p(){d=[]}function f(){return d}function m(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=e,t),route(t,e,r){const o={path:t,component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};"function"==typeof r?m(o)(r):r&&(o.options=r),n.children.push(o);const a={guard:n=>(o.guardFns?.push(n),a),middleware:n=>(o.middlewares?.push(n),a),meta:n=>(o.meta=n,a),route(n,t,e){const r={path:n,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:o};return"function"==typeof e?m(r)(e):e&&(r.options=e),o.children.push(r),a}};return a}};return n=>{n(t)}}function y(n,t,e){const r={path:n,component:t,children:[],options:{}};"function"==typeof e?m(r)(e):e&&(r.options=e),d.push(r)}const g=t(null),w=t(null),v=t(null),P=t({loading:!1,path:"",pendingPath:""}),q=t({name:"",stage:"idle"});function b(){const n=e(g);return n?.read()}function E(){const n=e(w);if(!n)throw new Error("RouteContext not found");return n}function k(){return e(v)??{}}function N(){return E().query??{}}function S(){return e(P)}function x(){return e(q)}function R(){const n=E(),t=k(),e=N();let r;try{r=b()}catch{}return{data:r,params:t,query:e,path:n.path}}let A=!1,C=!1,F=c("div",{children:"Loading..."}),I=({error:n})=>h("div",{children:["Global Error: ",String(n)]});function K(n,t){A=n,t&&(F=t)}function M(n,t){C=n,t&&(I=({error:n})=>t(n))}function B(){return A}function D(){return C}function U(){return F}function L(){return I}function O(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function W(n){return O(n).split("/").filter(Boolean)}function $(n){const[t,e]=n.split("#"),[r,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,e]=n.split("=");t&&(a[decodeURIComponent(t)]=decodeURIComponent(e??""))}),{path:O(r),query:a,hash:e??""}}function j(n,t){const{path:e,query:r,hash:o}=$(t),a=W(e),s=n.map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:n,segIndex:t,params:e,chain:i}=s.pop(),c=W(n.path);let h=0;const u={...e};for(;h<c.length;h++){const n=c[h],e=a[t+h];if(!e)break;if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(h!==c.length)continue;const l=t+h,d=[...i,{node:n,params:u,query:r,hash:o}];if(l===a.length)return d;for(const t of n.children)s.push({node:t,segIndex:l,params:u,chain:d})}return null}function J(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 G(n,t,e,r){const{path:o,query:a,hash:s}=$(n);let i=O(o);if(t)for(const n in t)i=i.replace(`:${n}`,encodeURIComponent(t[n]));const c={...a,...e??{}};if(Object.keys(c).length){const n=new URLSearchParams;for(const t in c){const e=c[t];Array.isArray(e)?e.forEach(e=>n.append(t,String(e))):n.append(t,String(e))}const t=n.toString();t&&(i+=`?${t}`)}return r?i+=`#${r}`:s&&(i+=`#${s}`),i}class T 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??(D()?L():void 0),t=this.props.fallback??h("div",{children:["Error: ",String(this.state.error)]});return n?c(n,{error:this.state.error}):t}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function z({resource:n,errorBoundary:t,children:e}){try{return n?.read(),c(u,{children:e})}catch(n){if(n instanceof Promise)throw n;const e=t??(D()?L():void 0);if(e)return c(e,{error:n});throw n}}function H({chain:n,data:t}){return c(u,{children:function e(o=0){const a=n[o];if(!a)return null;const s=a.node.component,i=t[a.node.path];if(!s)return e(o+1);let h=a.node.options?.fallback,u=a.node.parent;for(;!h&&u;)h=u.options?.fallback,u=u.parent;return!h&&B()&&(h=U()),c(T,{fallback:h,errorBoundary:a.node.options?.errorBoundary,children:c(r,{fallback:h,children:c(w.Provider,{value:{path:a.node.path,params:a.params,query:J(a.node.query??window.location.search),hash:a.node.hash??""},children:c(g.Provider,{value:i,children:c(v.Provider,{value:a.params,children:c(z,{resource:i,errorBoundary:a.node.options?.errorBoundary,children:c(s,{children:e(o+1)})})})})})})})}()})}async function Q(n){const t=j(f(),n);if(!t)return{};const e={};t.forEach(n=>{const t=n.node.options?.loader;t&&(e[n.node.path]=Y(JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),()=>t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),n.node.options?.ttl))});for(const n in e)e[n].read();return e}const V=new Map;function X(n=1/0){const t=Date.now();let e=0;for(const[r,o]of V.entries())if(o.expiry<=t&&(V.delete(r),e++,e>=n))break}function Y(n,t,e,r){const o=Date.now(),a=V.get(n);if(a&&!r&&(!e||a.expiry>o))return a.resource;const s=Z(t());return V.set(n,{resource:s,expiry:o+(e??0)}),s}function Z(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}}let _=null;function nn(){return _}function tn(n,t){const e=f(),[r,o]=n.split("?"),a={...J(o),...t?.query??{}},s=j(e,G(r,t?.params,a,t?.hash));s&&en(s,{forceReload:!1})}function en(n,t){const e={};return n.forEach(n=>{const r=n.node.options?.loader;if(!r)return;const o=JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),a=n.node.options?.ttl,s=Y(o,()=>r({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),a,t?.forceReload);e[n.node.path]=s}),e}function rn(){const[n,t]=o({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function e(n,e){const r=f();let o=j(r,n)??j(r,"*")??[];for(const t of o)for(const e of t.node.guardFns||[]){if(!await e({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked by guard",n)}for(const n of o)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);t(t=>({...t,loading:!0,pendingPath:n,transition:{name:e?.transition||"fade",stage:"exiting"}}));const a=en(o,e);t(t=>({...t,chain:o,data:{...t.data,...a},loading:!1,path:n,pendingPath:"",transition:{name:e?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return a(()=>{_=e,e(window.location.pathname),window.addEventListener("popstate",()=>e(window.location.pathname))},[]),c(P.Provider,{value:{loading:n.loading,path:n.path,pendingPath:n.pendingPath},children:c(q.Provider,{value:n.transition,children:c(H,{chain:n.chain,data:n.data})})})}let on=[],an=[];function sn(n){on.push(n)}function cn(n){an.push(n)}async function hn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},o=G(n,r.params,r.query,r.hash),a=await async function(n){return Promise.all(on.map(t=>t(n))).then(n=>n.every(n=>!1!==n))}(o);if(!a)return console.warn("Navigation blocked by beforeNavigation hook",o);e?window.history.replaceState({},"",o):window.history.pushState({},"",o),nn()?.(o,r),async function(n){for(const t of an)await t(n)}(o)}function un(n,t){return hn(n,t,!1)}function ln(n,t){return un(n,t)}function dn(n){return S().path===n}function pn(n,t){return()=>tn(n,t)}function fn(){const n=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t);return n.replace=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t,!0),n}function mn({to:n,transition:t,replace:e,params:r,query:o,hash:s,partialMatch:c,disablePrefetch:h}){const u=fn(),l=S(),d=i(null),p=G(n,r,o,s),f=c?l.path?.startsWith(p):l.path===p;return a(()=>{if(h)return;const n=d.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(tn(p),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[p,h]),{ref:d,fullPath:p,isActive:f,go:()=>{e?u.replace(n,{transition:t,params:r,query:o,hash:s}):u(n,{transition:t,params:r,query:o,hash:s})}}}un.replace=(n,t)=>hn(n,t,!0);const yn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:o,onKeyDown:a,disablePrefetch:s,...i},h)=>c("a",{ref:h,href:n,...i,onClick:n=>{r&&r(n),n.metaKey||n.ctrlKey||n.shiftKey||(n.preventDefault(),t())},onMouseEnter:n=>{o&&o(n),s||e?.()},onKeyDown:n=>{"Enter"===n.key&&(n.preventDefault(),t()),a&&a(n)}}));function gn(n){const{to:t,transition:e,replace:r,activeClassName:o="active",onNavigate:a,partialMatch:s,params:i,query:h,hash:u,className:d,disablePrefetch:p,...f}=n,{ref:m,fullPath:y,isActive:g,go:w}=mn({to:t,transition:e,replace:r,params:i,query:h,hash:u,partialMatch:s,disablePrefetch:p});return c(yn,{ref:m,fullPath:y,onNavigate:()=>{w(),a?.(y)},onPrefetch:()=>tn(y),className:l(d,g&&o),disablePrefetch:p,...f})}function wn(n){const{to:t,transition:e,replace:r,params:o,query:a,hash:s,partialMatch:i,className:h,style:u,disablePrefetch:d,...p}=n,{ref:f,fullPath:m,isActive:y,go:g}=mn({to:t,transition:e,replace:r,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:d}),w="function"==typeof h?h({isActive:y}):h,v="function"==typeof u?u({isActive:y}):u;return c(yn,{ref:f,fullPath:m,onNavigate:g,onPrefetch:()=>tn(m),className:l(w),style:v,disablePrefetch:d,...p})}yn.displayName="LinkCore";export{gn as Link,g as LoaderDataContext,wn as NavLink,v as ParamsContext,w as RouteContext,rn as RouterProvider,P as RouterStateContext,q as TransitionContext,cn as addAfterNavigationHook,sn as addBeforeNavigationHook,G as buildPath,X as cleanupCache,p as clearRoutes,Z as createResource,L as getGlobalError,U as getGlobalFallback,nn as getGlobalResolve,Y as getResource,f as getRoutes,D as getUseGlobalError,B as getUseGlobalFallback,Q as loadRouteData,j as matchRouteChain,un as navigate,J as normalizeQuery,$ as parseQueryHash,tn as prefetch,ln as redirect,y as route,M as setGlobalError,K as setGlobalFallback,mn as useLinkCore,b as useLoaderData,fn as useNavigate,S as useNavigation,k as useParams,pn as usePrefetch,N as useQuery,E as useRouteContext,dn as useRouteMatch,R as useRouterData,x as useTransition};
1
+ import n,{createContext as t,useContext as e,Suspense as r,useState as a,useEffect as o,forwardRef as s,useRef as i}from"react";import{jsx as c,jsxs as h,Fragment as u}from"react/jsx-runtime";import l from"joinclass";let p=[];function d(){p=[]}function f(){return p}function m(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=e,t),route(t,e,r){const a={path:t,component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};"function"==typeof r?m(a)(r):r&&(a.options=r),n.children.push(a);const o={guard:n=>(a.guardFns?.push(n),o),middleware:n=>(a.middlewares?.push(n),o),meta:n=>(a.meta=n,o),route(n,t,e){const r={path:n,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:a};return"function"==typeof e?m(r)(e):e&&(r.options=e),a.children.push(r),o}};return o}};return n=>{n(t)}}function y(n,t,e){const r={path:n,component:t,children:[],options:{}};"function"==typeof e?m(r)(e):e&&(r.options=e),p.push(r)}const g=t(null),w=t(null),v=t(null),q=t({loading:!1,path:"",pendingPath:""}),P=t({name:"",stage:"idle"});function b(){const n=e(g);try{return n?.read()}catch{return}}function E(){const n=e(w);if(!n)throw new Error("RouteContext not found");return n}function k(){return e(v)??{}}function N(){return E().query??{}}function S(){return e(q)}function x(){return e(P)}function R(){const{meta:n,params:t,path:e,query:r}=E();let a;try{a=b()}catch{}return{data:a,params:t??{},query:r??{},path:e,meta:n}}let A=!1,C=!1,F=c("div",{children:"Loading..."}),I=({error:n})=>h("div",{children:["Global Error: ",String(n)]});function K(n,t){A=n,t&&(F=t)}function M(n,t){C=n,t&&(I=({error:n})=>t(n))}function B(){return A}function D(){return C}function U(){return F}function L(){return I}function O(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function j(n){return O(n).split("/").filter(Boolean)}function W(n){const[t,e]=n.split("#"),[r,a]=t.split("?"),o={};return a&&a.split("&").forEach(n=>{const[t,e]=n.split("=");t&&(o[decodeURIComponent(t)]=decodeURIComponent(e??""))}),{path:O(r),query:o,hash:e??""}}function $(n,t){const{path:e,query:r,hash:a}=W(t),o=j(e),s=n.map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:n,segIndex:t,params:e,chain:i}=s.pop(),c=j(n.path);let h=0;const u={...e};for(;h<c.length;h++){const n=c[h],e=o[t+h];if(!e)break;if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(h!==c.length)continue;const l=t+h,p=[...i,{node:n,params:u,query:r,hash:a}];if(l===o.length)return p;for(const t of n.children)s.push({node:t,segIndex:l,params:u,chain:p})}return null}function J(n){const t={};if(!n)return t;if("string"==typeof n){const e=new URLSearchParams(n);e.forEach((n,r)=>{const a=e.getAll(r);t[r]=a.length>1?a:a[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 G(n,t,e,r){const{path:a,query:o,hash:s}=W(n);let i=O(a);if(t)for(const n in t)i=i.replace(`:${n}`,encodeURIComponent(t[n]));const c={...o,...e??{}};if(Object.keys(c).length){const n=new URLSearchParams;for(const t in c){const e=c[t];Array.isArray(e)?e.forEach(e=>n.append(t,String(e))):n.append(t,String(e))}const t=n.toString();t&&(i+=`?${t}`)}return r?i+=`#${r}`:s&&(i+=`#${s}`),i}class T 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??(D()?L():void 0),t=this.props.fallback??h("div",{children:["Error: ",String(this.state.error)]});return n?c(n,{error:this.state.error}):t}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function z({resource:n,errorBoundary:t,children:e}){try{return n?.read(),c(u,{children:e})}catch(n){if(n instanceof Promise)throw n;const e=t??(D()?L():void 0);if(e)return c(e,{error:n});throw n}}function H({chain:n,data:t}){return c(u,{children:function e(a=0){const o=n[a];if(!o)return null;const s=o.node.component,i=t[o.node.path];if(!s)return e(a+1);let h=o.node.options?.fallback,u=o.node.parent;for(;!h&&u;)h=u.options?.fallback,u=u.parent;!h&&B()&&(h=U());const l=Object.assign({},...n.slice(0,a+1).map(n=>n.node.meta));return c(T,{fallback:h,errorBoundary:o.node.options?.errorBoundary,children:c(r,{fallback:h,children:c(w.Provider,{value:{path:o.node.path,params:o.params,query:J(o.node.query??window.location.search),hash:o.node.hash??"",meta:l},children:c(g.Provider,{value:i,children:c(v.Provider,{value:o.params,children:c(z,{resource:i,errorBoundary:o.node.options?.errorBoundary,children:c(s,{children:e(a+1)})})})})})})})}()})}async function Q(n){const t=$(f(),n);if(!t)return{};const e={};t.forEach(n=>{const t=n.node.options?.loader;t&&(e[n.node.path]=Y(JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),()=>t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),n.node.options?.ttl))});for(const n in e)e[n].read();return e}const V=new Map;function X(n=1/0){const t=Date.now();let e=0;for(const[r,a]of V.entries())if(a.expiry<=t&&(V.delete(r),e++,e>=n))break}function Y(n,t,e,r){const a=Date.now(),o=V.get(n);if(o&&!r&&(!e||o.expiry>a))return o.resource;const s=Z(t());return V.set(n,{resource:s,expiry:a+(e??0)}),s}function Z(n,t){let e=t?"success":"pending",r=t??null;const a=n.then(n=>{e="success",r=n},n=>{e="error",r=n});return{read(){if("pending"===e)throw a;if("error"===e)throw r;return r},update(n){r=n,e="success"},get:()=>r}}let _=null;function nn(){return _}function tn(n,t){const e=f(),[r,a]=n.split("?"),o={...J(a),...t?.query??{}},s=$(e,G(r,t?.params,o,t?.hash));s&&en(s,{forceReload:!1})}function en(n,t){const e={};return n.forEach(n=>{const r=n.node.options?.loader;if(!r)return;const a=JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),o=n.node.options?.ttl,s=Y(a,()=>r({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),o,t?.forceReload);e[n.node.path]=s}),e}function rn(){const[n,t]=a({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function e(n,e){const r=f();let a=$(r,n)??$(r,"*")??[];for(const t of a)for(const e of t.node.guardFns||[]){if(!await e({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked by guard",n)}for(const n of a)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);t(t=>({...t,loading:!0,pendingPath:n,transition:{name:e?.transition||"fade",stage:"exiting"}}));const o=en(a,e);t(t=>({...t,chain:a,data:{...t.data,...o},loading:!1,path:n,pendingPath:"",transition:{name:e?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return o(()=>{_=e,e(window.location.pathname),window.addEventListener("popstate",()=>e(window.location.pathname))},[]),c(q.Provider,{value:{loading:n.loading,path:n.path,pendingPath:n.pendingPath},children:c(P.Provider,{value:n.transition,children:c(H,{chain:n.chain,data:n.data})})})}let an=[],on=[];function sn(n){an.push(n)}function cn(n){on.push(n)}async function hn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},a=G(n,r.params,r.query,r.hash),o=await async function(n){return Promise.all(an.map(t=>t(n))).then(n=>n.every(n=>!1!==n))}(a);if(!o)return console.warn("Navigation blocked by beforeNavigation hook",a);e?window.history.replaceState({},"",a):window.history.pushState({},"",a),nn()?.(a,r),async function(n){for(const t of on)await t(n)}(a)}function un(n,t){return hn(n,t,!1)}function ln(n,t){return un(n,t)}function pn(n){return S().path===n}function dn(n,t){return()=>tn(n,t)}function fn(){const n=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t);return n.replace=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t,!0),n}function mn({to:n,transition:t,replace:e,params:r,query:a,hash:s,partialMatch:c,disablePrefetch:h}){const u=fn(),l=S(),p=i(null),d=G(n,r,a,s),f=c?l.path?.startsWith(d):l.path===d;return o(()=>{if(h)return;const n=p.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(tn(d),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[d,h]),{ref:p,fullPath:d,isActive:f,go:()=>{e?u.replace(n,{transition:t,params:r,query:a,hash:s}):u(n,{transition:t,params:r,query:a,hash:s})}}}un.replace=(n,t)=>hn(n,t,!0);const yn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:a,onKeyDown:o,disablePrefetch:s,...i},h)=>c("a",{ref:h,href:n,...i,onClick:n=>{r&&r(n),n.metaKey||n.ctrlKey||n.shiftKey||(n.preventDefault(),t())},onMouseEnter:n=>{a&&a(n),s||e?.()},onKeyDown:n=>{"Enter"===n.key&&(n.preventDefault(),t()),o&&o(n)}}));function gn(n){const{to:t,transition:e,replace:r,activeClassName:a="active",onNavigate:o,partialMatch:s,params:i,query:h,hash:u,className:p,disablePrefetch:d,...f}=n,{ref:m,fullPath:y,isActive:g,go:w}=mn({to:t,transition:e,replace:r,params:i,query:h,hash:u,partialMatch:s,disablePrefetch:d});return c(yn,{ref:m,fullPath:y,onNavigate:()=>{w(),o?.(y)},onPrefetch:()=>tn(y),className:l(p,g&&a),disablePrefetch:d,...f})}function wn(n){const{to:t,transition:e,replace:r,params:a,query:o,hash:s,partialMatch:i,className:h,style:u,disablePrefetch:p,...d}=n,{ref:f,fullPath:m,isActive:y,go:g}=mn({to:t,transition:e,replace:r,params:a,query:o,hash:s,partialMatch:i,disablePrefetch:p}),w="function"==typeof h?h({isActive:y}):h,v="function"==typeof u?u({isActive:y}):u;return c(yn,{ref:f,fullPath:m,onNavigate:g,onPrefetch:()=>tn(m),className:l(w),style:v,disablePrefetch:p,...d})}yn.displayName="LinkCore";export{gn as Link,g as LoaderDataContext,wn as NavLink,v as ParamsContext,w as RouteContext,rn as RouterProvider,q as RouterStateContext,P as TransitionContext,cn as addAfterNavigationHook,sn as addBeforeNavigationHook,G as buildPath,X as cleanupCache,d as clearRoutes,Z as createResource,L as getGlobalError,U as getGlobalFallback,nn as getGlobalResolve,Y as getResource,f as getRoutes,D as getUseGlobalError,B as getUseGlobalFallback,Q as loadRouteData,$ as matchRouteChain,un as navigate,J as normalizeQuery,W as parseQueryHash,tn as prefetch,ln as redirect,y as route,M as setGlobalError,K as setGlobalFallback,mn as useLinkCore,b as useLoaderData,fn as useNavigate,S as useNavigation,k as useParams,dn as usePrefetch,N as useQuery,E as useRouteContext,pn as useRouteMatch,R as useRouterData,x as useTransition};
package/build/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");let n=[];function o(){return n}function a(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=r,t),route(t,r,n){const o={path:t,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};"function"==typeof n?a(o)(n):n&&(o.options=n),e.children.push(o);const s={guard:e=>(o.guardFns?.push(e),s),middleware:e=>(o.middlewares?.push(e),s),meta:e=>(o.meta=e,s),route(e,t,r){const n={path:e,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:o};return"function"==typeof r?a(n)(r):r&&(n.options=r),o.children.push(n),s}};return s}};return e=>{e(t)}}const s=e.createContext(null),i=e.createContext(null),c=e.createContext(null),u=e.createContext({loading:!1,path:"",pendingPath:""}),h=e.createContext({name:"",stage:"idle"});function p(){const t=e.useContext(s);return t?.read()}function l(){const t=e.useContext(i);if(!t)throw new Error("RouteContext not found");return t}function d(){return e.useContext(c)??{}}function f(){return l().query??{}}function x(){return e.useContext(u)}let m=!1,g=!1,y=t.jsx("div",{children:"Loading..."}),v=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});function w(){return m}function P(){return g}function b(){return y}function q(){return v}function C(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function R(e){return C(e).split("/").filter(Boolean)}function j(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");t&&(a[decodeURIComponent(t)]=decodeURIComponent(r??""))}),{path:C(n),query:a,hash:r??""}}function k(e,t){const{path:r,query:n,hash:o}=j(t),a=R(r),s=e.map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:i}=s.pop(),c=R(e.path);let u=0;const h={...r};for(;u<c.length;u++){const e=c[u],r=a[t+u];if(!r)break;if(e.startsWith(":"))h[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(u!==c.length)continue;const p=t+u,l=[...i,{node:e,params:h,query:n,hash:o}];if(p===a.length)return l;for(const t of e.children)s.push({node:t,segIndex:p,params:h,chain:l})}return null}function E(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 N(e,t,r,n){const{path:o,query:a,hash:s}=j(e);let i=C(o);if(t)for(const e in t)i=i.replace(`:${e}`,encodeURIComponent(t[e]));const c={...a,...r??{}};if(Object.keys(c).length){const e=new URLSearchParams;for(const t in c){const r=c[t];Array.isArray(r)?r.forEach(r=>e.append(t,String(r))):e.append(t,String(r))}const t=e.toString();t&&(i+=`?${t}`)}return n?i+=`#${n}`:s&&(i+=`#${s}`),i}class S 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??(P()?q():void 0),r=this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]});return e?t.jsx(e,{error:this.state.error}):r}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function F({resource:e,errorBoundary:r,children:n}){try{return e?.read(),t.jsx(t.Fragment,{children:n})}catch(e){if(e instanceof Promise)throw e;const n=r??(P()?q():void 0);if(n)return t.jsx(n,{error:e});throw e}}function A({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const u=r[a];if(!u)return null;const h=u.node.component,p=n[u.node.path];if(!h)return o(a+1);let l=u.node.options?.fallback,d=u.node.parent;for(;!l&&d;)l=d.options?.fallback,d=d.parent;return!l&&w()&&(l=b()),t.jsx(S,{fallback:l,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(i.Provider,{value:{path:u.node.path,params:u.params,query:E(u.node.query??window.location.search),hash:u.node.hash??""},children:t.jsx(s.Provider,{value:p,children:t.jsx(c.Provider,{value:u.params,children:t.jsx(F,{resource:p,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(h,{children:o(a+1)})})})})})})})}()})}const D=new Map;function L(e,t,r,n){const o=Date.now(),a=D.get(e);if(a&&!n&&(!r||a.expiry>o))return a.resource;const s=M(t());return D.set(e,{resource:s,expiry:o+(r??0)}),s}function M(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}}let I=null;function K(){return I}function B(e,t){const r=o(),[n,a]=e.split("?"),s={...E(a),...t?.query??{}},i=k(r,N(n,t?.params,s,t?.hash));i&&G(i,{forceReload:!1})}function G(e,t){const r={};return e.forEach(e=>{const n=e.node.options?.loader;if(!n)return;const o=JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),a=e.node.options?.ttl,s=L(o,()=>n({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),a,t?.forceReload);r[e.node.path]=s}),r}let U=[],O=[];async function W(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=N(e,n.params,n.query,n.hash),a=await async function(e){return Promise.all(U.map(t=>t(e))).then(e=>e.every(e=>!1!==e))}(o);if(!a)return console.warn("Navigation blocked by beforeNavigation hook",o);r?window.history.replaceState({},"",o):window.history.pushState({},"",o),K()?.(o,n),async function(e){for(const t of O)await t(e)}(o)}function $(e,t){return W(e,t,!1)}function H(){const e=(e,t)=>W(N(e,t?.params,t?.query,t?.hash),t);return e.replace=(e,t)=>W(N(e,t?.params,t?.query,t?.hash),t,!0),e}function Q({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c}){const u=H(),h=x(),p=e.useRef(null),l=N(t,o,a,s),d=i?h.path?.startsWith(l):h.path===l;return e.useEffect(()=>{if(c)return;const e=p.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(B(l),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[l,c]),{ref:p,fullPath:l,isActive:d,go:()=>{n?u.replace(t,{transition:r,params:o,query:a,hash:s}):u(t,{transition:r,params:o,query:a,hash:s})}}}$.replace=(e,t)=>W(e,t,!0);const T=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:e=>{o&&o(e),e.metaKey||e.ctrlKey||e.shiftKey||(e.preventDefault(),r())},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),r()),s&&s(e)}}));T.displayName="LinkCore",exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:h,hash:p,className:l,disablePrefetch:d,...f}=e,{ref:x,fullPath:m,isActive:g,go:y}=Q({to:n,transition:o,replace:a,params:u,query:h,hash:p,partialMatch:c,disablePrefetch:d});return t.jsx(T,{ref:x,fullPath:m,onNavigate:()=>{y(),i?.(m)},onPrefetch:()=>B(m),className:r(l,g&&s),disablePrefetch:d,...f})},exports.LoaderDataContext=s,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:h,style:p,disablePrefetch:l,...d}=e,{ref:f,fullPath:x,isActive:m,go:g}=Q({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:l}),y="function"==typeof h?h({isActive:m}):h,v="function"==typeof p?p({isActive:m}):p;return t.jsx(T,{ref:f,fullPath:x,onNavigate:g,onPrefetch:()=>B(x),className:r(y),style:v,disablePrefetch:l,...d})},exports.ParamsContext=c,exports.RouteContext=i,exports.RouterProvider=function(){const[r,n]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function a(e,t){const r=o();let a=k(r,e)??k(r,"*")??[];for(const t of a)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 by guard",e)}for(const e of a)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);n(r=>({...r,loading:!0,pendingPath:e,transition:{name:t?.transition||"fade",stage:"exiting"}}));const s=G(a,t);n(r=>({...r,chain:a,data:{...r.data,...s},loading:!1,path:e,pendingPath:"",transition:{name:t?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return e.useEffect(()=>{I=a,a(window.location.pathname),window.addEventListener("popstate",()=>a(window.location.pathname))},[]),t.jsx(u.Provider,{value:{loading:r.loading,path:r.path,pendingPath:r.pendingPath},children:t.jsx(h.Provider,{value:r.transition,children:t.jsx(A,{chain:r.chain,data:r.data})})})},exports.RouterStateContext=u,exports.TransitionContext=h,exports.addAfterNavigationHook=function(e){O.push(e)},exports.addBeforeNavigationHook=function(e){U.push(e)},exports.buildPath=N,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[n,o]of D.entries())if(o.expiry<=t&&(D.delete(n),r++,r>=e))break},exports.clearRoutes=function(){n=[]},exports.createResource=M,exports.getGlobalError=q,exports.getGlobalFallback=b,exports.getGlobalResolve=K,exports.getResource=L,exports.getRoutes=o,exports.getUseGlobalError=P,exports.getUseGlobalFallback=w,exports.loadRouteData=async function(e){const t=k(o(),e);if(!t)return{};const r={};t.forEach(e=>{const t=e.node.options?.loader;t&&(r[e.node.path]=L(JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),()=>t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),e.node.options?.ttl))});for(const e in r)r[e].read();return r},exports.matchRouteChain=k,exports.navigate=$,exports.normalizeQuery=E,exports.parseQueryHash=j,exports.prefetch=B,exports.redirect=function(e,t){return $(e,t)},exports.route=function(e,t,r){const o={path:e,component:t,children:[],options:{}};"function"==typeof r?a(o)(r):r&&(o.options=r),n.push(o)},exports.setGlobalError=function(e,t){g=e,t&&(v=({error:e})=>t(e))},exports.setGlobalFallback=function(e,t){m=e,t&&(y=t)},exports.useLinkCore=Q,exports.useLoaderData=p,exports.useNavigate=H,exports.useNavigation=x,exports.useParams=d,exports.usePrefetch=function(e,t){return()=>B(e,t)},exports.useQuery=f,exports.useRouteContext=l,exports.useRouteMatch=function(e){return x().path===e},exports.useRouterData=function(){const e=l(),t=d(),r=f();let n;try{n=p()}catch{}return{data:n,params:t,query:r,path:e.path}},exports.useTransition=function(){return e.useContext(h)};
1
+ "use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");let n=[];function o(){return n}function a(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=r,t),route(t,r,n){const o={path:t,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};"function"==typeof n?a(o)(n):n&&(o.options=n),e.children.push(o);const s={guard:e=>(o.guardFns?.push(e),s),middleware:e=>(o.middlewares?.push(e),s),meta:e=>(o.meta=e,s),route(e,t,r){const n={path:e,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:o};return"function"==typeof r?a(n)(r):r&&(n.options=r),o.children.push(n),s}};return s}};return e=>{e(t)}}const s=e.createContext(null),i=e.createContext(null),c=e.createContext(null),u=e.createContext({loading:!1,path:"",pendingPath:""}),h=e.createContext({name:"",stage:"idle"});function p(){const t=e.useContext(s);try{return t?.read()}catch{return}}function l(){const t=e.useContext(i);if(!t)throw new Error("RouteContext not found");return t}function d(){return e.useContext(u)}let f=!1,x=!1,m=t.jsx("div",{children:"Loading..."}),y=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});function g(){return f}function v(){return x}function w(){return m}function b(){return y}function P(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function q(e){return P(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("=");t&&(a[decodeURIComponent(t)]=decodeURIComponent(r??""))}),{path:P(n),query:a,hash:r??""}}function R(e,t){const{path:r,query:n,hash:o}=C(t),a=q(r),s=e.map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:i}=s.pop(),c=q(e.path);let u=0;const h={...r};for(;u<c.length;u++){const e=c[u],r=a[t+u];if(!r)break;if(e.startsWith(":"))h[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(u!==c.length)continue;const p=t+u,l=[...i,{node:e,params:h,query:n,hash:o}];if(p===a.length)return l;for(const t of e.children)s.push({node:t,segIndex:p,params:h,chain:l})}return null}function j(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 k(e,t,r,n){const{path:o,query:a,hash:s}=C(e);let i=P(o);if(t)for(const e in t)i=i.replace(`:${e}`,encodeURIComponent(t[e]));const c={...a,...r??{}};if(Object.keys(c).length){const e=new URLSearchParams;for(const t in c){const r=c[t];Array.isArray(r)?r.forEach(r=>e.append(t,String(r))):e.append(t,String(r))}const t=e.toString();t&&(i+=`?${t}`)}return n?i+=`#${n}`:s&&(i+=`#${s}`),i}class E 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??(v()?b():void 0),r=this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]});return e?t.jsx(e,{error:this.state.error}):r}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function N({resource:e,errorBoundary:r,children:n}){try{return e?.read(),t.jsx(t.Fragment,{children:n})}catch(e){if(e instanceof Promise)throw e;const n=r??(v()?b():void 0);if(n)return t.jsx(n,{error:e});throw e}}function S({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const u=r[a];if(!u)return null;const h=u.node.component,p=n[u.node.path];if(!h)return o(a+1);let l=u.node.options?.fallback,d=u.node.parent;for(;!l&&d;)l=d.options?.fallback,d=d.parent;!l&&g()&&(l=w());const f=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta));return t.jsx(E,{fallback:l,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(i.Provider,{value:{path:u.node.path,params:u.params,query:j(u.node.query??window.location.search),hash:u.node.hash??"",meta:f},children:t.jsx(s.Provider,{value:p,children:t.jsx(c.Provider,{value:u.params,children:t.jsx(N,{resource:p,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(h,{children:o(a+1)})})})})})})})}()})}const F=new Map;function A(e,t,r,n){const o=Date.now(),a=F.get(e);if(a&&!n&&(!r||a.expiry>o))return a.resource;const s=D(t());return F.set(e,{resource:s,expiry:o+(r??0)}),s}function D(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}}let L=null;function M(){return L}function I(e,t){const r=o(),[n,a]=e.split("?"),s={...j(a),...t?.query??{}},i=R(r,k(n,t?.params,s,t?.hash));i&&K(i,{forceReload:!1})}function K(e,t){const r={};return e.forEach(e=>{const n=e.node.options?.loader;if(!n)return;const o=JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),a=e.node.options?.ttl,s=A(o,()=>n({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),a,t?.forceReload);r[e.node.path]=s}),r}let B=[],G=[];async function U(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=k(e,n.params,n.query,n.hash),a=await async function(e){return Promise.all(B.map(t=>t(e))).then(e=>e.every(e=>!1!==e))}(o);if(!a)return console.warn("Navigation blocked by beforeNavigation hook",o);r?window.history.replaceState({},"",o):window.history.pushState({},"",o),M()?.(o,n),async function(e){for(const t of G)await t(e)}(o)}function O(e,t){return U(e,t,!1)}function W(){const e=(e,t)=>U(k(e,t?.params,t?.query,t?.hash),t);return e.replace=(e,t)=>U(k(e,t?.params,t?.query,t?.hash),t,!0),e}function $({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c}){const u=W(),h=d(),p=e.useRef(null),l=k(t,o,a,s),f=i?h.path?.startsWith(l):h.path===l;return e.useEffect(()=>{if(c)return;const e=p.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(I(l),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[l,c]),{ref:p,fullPath:l,isActive:f,go:()=>{n?u.replace(t,{transition:r,params:o,query:a,hash:s}):u(t,{transition:r,params:o,query:a,hash:s})}}}O.replace=(e,t)=>U(e,t,!0);const H=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:e=>{o&&o(e),e.metaKey||e.ctrlKey||e.shiftKey||(e.preventDefault(),r())},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),r()),s&&s(e)}}));H.displayName="LinkCore",exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:h,hash:p,className:l,disablePrefetch:d,...f}=e,{ref:x,fullPath:m,isActive:y,go:g}=$({to:n,transition:o,replace:a,params:u,query:h,hash:p,partialMatch:c,disablePrefetch:d});return t.jsx(H,{ref:x,fullPath:m,onNavigate:()=>{g(),i?.(m)},onPrefetch:()=>I(m),className:r(l,y&&s),disablePrefetch:d,...f})},exports.LoaderDataContext=s,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:h,style:p,disablePrefetch:l,...d}=e,{ref:f,fullPath:x,isActive:m,go:y}=$({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:l}),g="function"==typeof h?h({isActive:m}):h,v="function"==typeof p?p({isActive:m}):p;return t.jsx(H,{ref:f,fullPath:x,onNavigate:y,onPrefetch:()=>I(x),className:r(g),style:v,disablePrefetch:l,...d})},exports.ParamsContext=c,exports.RouteContext=i,exports.RouterProvider=function(){const[r,n]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function a(e,t){const r=o();let a=R(r,e)??R(r,"*")??[];for(const t of a)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 by guard",e)}for(const e of a)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);n(r=>({...r,loading:!0,pendingPath:e,transition:{name:t?.transition||"fade",stage:"exiting"}}));const s=K(a,t);n(r=>({...r,chain:a,data:{...r.data,...s},loading:!1,path:e,pendingPath:"",transition:{name:t?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return e.useEffect(()=>{L=a,a(window.location.pathname),window.addEventListener("popstate",()=>a(window.location.pathname))},[]),t.jsx(u.Provider,{value:{loading:r.loading,path:r.path,pendingPath:r.pendingPath},children:t.jsx(h.Provider,{value:r.transition,children:t.jsx(S,{chain:r.chain,data:r.data})})})},exports.RouterStateContext=u,exports.TransitionContext=h,exports.addAfterNavigationHook=function(e){G.push(e)},exports.addBeforeNavigationHook=function(e){B.push(e)},exports.buildPath=k,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[n,o]of F.entries())if(o.expiry<=t&&(F.delete(n),r++,r>=e))break},exports.clearRoutes=function(){n=[]},exports.createResource=D,exports.getGlobalError=b,exports.getGlobalFallback=w,exports.getGlobalResolve=M,exports.getResource=A,exports.getRoutes=o,exports.getUseGlobalError=v,exports.getUseGlobalFallback=g,exports.loadRouteData=async function(e){const t=R(o(),e);if(!t)return{};const r={};t.forEach(e=>{const t=e.node.options?.loader;t&&(r[e.node.path]=A(JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),()=>t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),e.node.options?.ttl))});for(const e in r)r[e].read();return r},exports.matchRouteChain=R,exports.navigate=O,exports.normalizeQuery=j,exports.parseQueryHash=C,exports.prefetch=I,exports.redirect=function(e,t){return O(e,t)},exports.route=function(e,t,r){const o={path:e,component:t,children:[],options:{}};"function"==typeof r?a(o)(r):r&&(o.options=r),n.push(o)},exports.setGlobalError=function(e,t){x=e,t&&(y=({error:e})=>t(e))},exports.setGlobalFallback=function(e,t){f=e,t&&(m=t)},exports.useLinkCore=$,exports.useLoaderData=p,exports.useNavigate=W,exports.useNavigation=d,exports.useParams=function(){return e.useContext(c)??{}},exports.usePrefetch=function(e,t){return()=>I(e,t)},exports.useQuery=function(){return l().query??{}},exports.useRouteContext=l,exports.useRouteMatch=function(e){return d().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=l();let o;try{o=p()}catch{}return{data:o,params:t??{},query:n??{},path:r,meta:e}},exports.useTransition=function(){return e.useContext(h)};
@@ -1,10 +1,11 @@
1
1
  import React from "react";
2
2
  export type TransitionStage = "idle" | "entering" | "exiting";
3
- export type RouteContextType = {
3
+ export type RouteContextType<M = any> = {
4
4
  path: string;
5
5
  params: Record<string, any>;
6
6
  query: Record<string, string | string[]>;
7
7
  hash?: string;
8
+ meta?: M;
8
9
  };
9
10
  export type RouterState = {
10
11
  loading: boolean;
@@ -16,12 +17,12 @@ export type TransitionState = {
16
17
  stage: TransitionStage;
17
18
  };
18
19
  export declare const LoaderDataContext: React.Context<any>;
19
- export declare const RouteContext: React.Context<RouteContextType | null>;
20
+ export declare const RouteContext: React.Context<RouteContextType<any> | null>;
20
21
  export declare const ParamsContext: React.Context<any>;
21
22
  export declare const RouterStateContext: React.Context<RouterState>;
22
23
  export declare const TransitionContext: React.Context<TransitionState>;
23
- export declare function useLoaderData<T = unknown>(): T;
24
- export declare function useRouteContext(): RouteContextType;
24
+ export declare function useLoaderData<T = unknown>(): T | undefined;
25
+ export declare function useRouteContext<M = any>(): RouteContextType<M>;
25
26
  export declare function useParams<P extends Record<string, any> = Record<string, any>>(): P;
26
27
  export declare function useQuery<Q extends Record<string, any> = Record<string, any>>(): Q;
27
28
  export declare function useNavigation(): RouterState;
@@ -31,5 +32,6 @@ export type RouterData<T = unknown, P extends Record<string, any> = Record<strin
31
32
  params: P;
32
33
  query: Q;
33
34
  path: string;
35
+ meta?: any;
34
36
  };
35
37
  export declare function useRouterData<T = unknown, P extends Record<string, any> = Record<string, any>, Q extends Record<string, any> = Record<string, string>>(): RouterData<T, P, Q>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "routexiz",
3
- "version": "0.0.21",
4
- "description": "Intent-based routing for React. Navigate by intent instead of URLs.",
3
+ "version": "0.1.0-z2",
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",
7
7
  "sideEffects": false,