@typeroute/router 0.10.0 → 0.11.1
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 +103 -42
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ If you believe there's a mistake in the comparison table, please [open an issue]
|
|
|
107
107
|
|
|
108
108
|
- [Comparison](#comparison)
|
|
109
109
|
- [Installation](#installation)
|
|
110
|
-
- [
|
|
110
|
+
- [Motivation](#motivation)
|
|
111
111
|
- [Defining routes](#defining-routes)
|
|
112
112
|
- [Nested routes and layouts](#nested-routes-and-layouts)
|
|
113
113
|
- [Setting up the router](#setting-up-the-router)
|
|
@@ -124,6 +124,7 @@ If you believe there's a mistake in the comparison table, please [open an issue]
|
|
|
124
124
|
- [Route preloading](#route-preloading)
|
|
125
125
|
- [Programmatic navigation](#programmatic-navigation)
|
|
126
126
|
- [Declarative navigation](#declarative-navigation)
|
|
127
|
+
- [Index routes](#index-routes)
|
|
127
128
|
- [Lazy loading](#lazy-loading)
|
|
128
129
|
- [Data preloading](#data-preloading)
|
|
129
130
|
- [Error boundaries](#error-boundaries)
|
|
@@ -136,6 +137,7 @@ If you believe there's a mistake in the comparison table, please [open an issue]
|
|
|
136
137
|
- [Cookbook](#cookbook)
|
|
137
138
|
- [Quick start example](#quick-start-example)
|
|
138
139
|
- [Server-side rendering (SSR)](#server-side-rendering-ssr)
|
|
140
|
+
- [Not-found pages](#not-found-pages)
|
|
139
141
|
- [Scroll to top on navigation](#scroll-to-top-on-navigation)
|
|
140
142
|
- [Matching a route anywhere](#matching-a-route-anywhere)
|
|
141
143
|
- [Global link configuration](#global-link-configuration)
|
|
@@ -165,48 +167,13 @@ TypeRoute requires React 18 or higher.
|
|
|
165
167
|
|
|
166
168
|
---
|
|
167
169
|
|
|
168
|
-
#
|
|
170
|
+
# Motivation
|
|
169
171
|
|
|
170
|
-
|
|
172
|
+
Most React routers today either lack type safety entirely, or achieve it through build plugins and code generation. TypeRoute takes a different path: it uses TypeScript's own inference to give you full autocompletion and type checking - for routes, params, search params, navigation - without any tooling beyond the TypeScript compiler you're already running.
|
|
171
173
|
|
|
172
|
-
|
|
173
|
-
import { route, RouterRoot, Outlet, Link, useParams } from "@typeroute/router";
|
|
174
|
-
|
|
175
|
-
// Routes
|
|
176
|
-
const layout = route("/").component(() => (
|
|
177
|
-
<div>
|
|
178
|
-
<nav>
|
|
179
|
-
<Link to="/">Home</Link>
|
|
180
|
-
<Link to={user} params={{ id: "42" }}>
|
|
181
|
-
User
|
|
182
|
-
</Link>
|
|
183
|
-
</nav>
|
|
184
|
-
<Outlet />
|
|
185
|
-
</div>
|
|
186
|
-
));
|
|
187
|
-
|
|
188
|
-
const home = layout.route("/").component(() => <h1>Home</h1>);
|
|
189
|
-
|
|
190
|
-
const user = layout.route("/users/:id").component(() => {
|
|
191
|
-
const { id } = useParams(user); // Fully typed
|
|
192
|
-
return <h1>User {id}</h1>;
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// Setup
|
|
196
|
-
const routes = [home, user];
|
|
197
|
-
|
|
198
|
-
function App() {
|
|
199
|
-
return <RouterRoot routes={routes} />;
|
|
200
|
-
}
|
|
174
|
+
The API is deliberately small. You define routes with a builder, register them once through module augmentation, and that's it. Routes nest, middlewares compose, and types inherit down the tree. There's no config file, no CLI, no codegen step. The whole thing ships at ~4kB gzipped before tree-shaking.
|
|
201
175
|
|
|
202
|
-
|
|
203
|
-
interface Register {
|
|
204
|
-
routes: typeof routes;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Everything autocompletes and type-checks automatically. No heavy setup, no magic, just a simple API that gets out of your way.
|
|
176
|
+
TypeRoute doesn't try to be a framework. It doesn't own your data fetching, your file structure, or force you into SSR. It handles routing - matching URLs to components and managing navigation - and stays out of the way for everything else.
|
|
210
177
|
|
|
211
178
|
👉 [Try it live in the StackBlitz playground](https://stackblitz.com/edit/typeroute-demo?file=src%2Fapp.tsx)
|
|
212
179
|
|
|
@@ -823,6 +790,42 @@ Note that `Navigate` uses `useLayoutEffect` internally to ensure the navigation
|
|
|
823
790
|
|
|
824
791
|
---
|
|
825
792
|
|
|
793
|
+
# Index routes
|
|
794
|
+
|
|
795
|
+
Layout routes often need a child route at `"/"` just to show default content at the parent's path:
|
|
796
|
+
|
|
797
|
+
```tsx
|
|
798
|
+
const dashboard = route("/dashboard").component(DashboardLayout);
|
|
799
|
+
|
|
800
|
+
const overview = dashboard.route("/").component(Overview);
|
|
801
|
+
const settings = dashboard.route("/settings").component(Settings);
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
This is perfectly fine, but `.index()` offers a shorthand. It defines what renders when no child route matches, directly on the parent:
|
|
805
|
+
|
|
806
|
+
```tsx
|
|
807
|
+
const dashboard = route("/dashboard")
|
|
808
|
+
.component(DashboardLayout)
|
|
809
|
+
.index(Overview);
|
|
810
|
+
|
|
811
|
+
const settings = dashboard.route("/settings").component(Settings);
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
Here's what renders at each path:
|
|
815
|
+
|
|
816
|
+
```
|
|
817
|
+
/dashboard → DashboardLayout > Overview
|
|
818
|
+
/dashboard/settings → DashboardLayout > Settings
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
Under the hood, `.index(Comp)` is equivalent to `.component(() => useOutlet() ?? <Comp />)`. Note that when using `.index()`, the layout route itself becomes navigable. Include it in your routes collection instead of the former child route:
|
|
822
|
+
|
|
823
|
+
```tsx
|
|
824
|
+
const routes = [dashboard, settings];
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
---
|
|
828
|
+
|
|
826
829
|
# Lazy loading
|
|
827
830
|
|
|
828
831
|
Load route components on demand with `.lazy()`. The function you pass should return a dynamic import:
|
|
@@ -1317,7 +1320,55 @@ import { routes } from "./routes";
|
|
|
1317
1320
|
hydrateRoot(rootElement, <RouterRoot routes={routes} />);
|
|
1318
1321
|
```
|
|
1319
1322
|
|
|
1320
|
-
You can also manually set `ssrContext.statusCode` in your components during SSR to control the response status (like 404 for not
|
|
1323
|
+
You can also manually set `ssrContext.statusCode` in your components during SSR to control the response status (like 404 for not-found pages).
|
|
1324
|
+
|
|
1325
|
+
## Not-found pages
|
|
1326
|
+
|
|
1327
|
+
Since TypeRoute uses a [ranking algorithm](#route-matching-and-ranking) where wildcards have the lowest weight, a catch-all `/*` route naturally acts as a fallback. It only matches when no other route does, regardless of definition order:
|
|
1328
|
+
|
|
1329
|
+
```tsx
|
|
1330
|
+
const home = route("/").component(HomePage);
|
|
1331
|
+
const notFound = route("/*").component(NotFoundPage);
|
|
1332
|
+
const about = route("/about").component(AboutPage);
|
|
1333
|
+
|
|
1334
|
+
const routes = [home, notFound, about];
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1337
|
+
```tsx
|
|
1338
|
+
function NotFoundPage() {
|
|
1339
|
+
return (
|
|
1340
|
+
<div>
|
|
1341
|
+
<h1>404</h1>
|
|
1342
|
+
<p>This page doesn't exist.</p>
|
|
1343
|
+
<Link to="/">Go home</Link>
|
|
1344
|
+
</div>
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
This also works for scoped not-found pages. If you want a fallback specific to a section of your app, attach the catch-all to that section's parent route:
|
|
1350
|
+
|
|
1351
|
+
```tsx
|
|
1352
|
+
const dashboard = route("/dashboard").component(DashboardLayout);
|
|
1353
|
+
|
|
1354
|
+
const overview = dashboard.route("/").component(Overview);
|
|
1355
|
+
const settings = dashboard.route("/settings").component(Settings);
|
|
1356
|
+
const dashboardNotFound = dashboard.route("/*").component(DashboardNotFound);
|
|
1357
|
+
```
|
|
1358
|
+
|
|
1359
|
+
Here, `/dashboard/anything-else` renders `DashboardNotFound` inside the dashboard layout.
|
|
1360
|
+
|
|
1361
|
+
If you're doing SSR, you can set a 404 status code from the not-found component using `ssrContext`:
|
|
1362
|
+
|
|
1363
|
+
```tsx
|
|
1364
|
+
function NotFoundPage() {
|
|
1365
|
+
const router = useRouter();
|
|
1366
|
+
if (router.ssrContext) {
|
|
1367
|
+
router.ssrContext.statusCode = 404;
|
|
1368
|
+
}
|
|
1369
|
+
// ...
|
|
1370
|
+
}
|
|
1371
|
+
```
|
|
1321
1372
|
|
|
1322
1373
|
## Scroll to top on navigation
|
|
1323
1374
|
|
|
@@ -1676,6 +1727,17 @@ const dashboard = route("/dashboard").use(auth).component(Dashboard);
|
|
|
1676
1727
|
const users = route("/users").component(UsersPage);
|
|
1677
1728
|
```
|
|
1678
1729
|
|
|
1730
|
+
**`.index(component)`** renders a component when no child route matches. See [Index routes](#index-routes).
|
|
1731
|
+
|
|
1732
|
+
- `component` - `ComponentType` - A React component
|
|
1733
|
+
- Returns: `Route` - A new route object
|
|
1734
|
+
|
|
1735
|
+
```tsx
|
|
1736
|
+
const dashboard = route("/dashboard")
|
|
1737
|
+
.component(DashboardLayout)
|
|
1738
|
+
.index(Overview);
|
|
1739
|
+
```
|
|
1740
|
+
|
|
1679
1741
|
**`.lazy(loader)`** adds a lazy-loaded component to render when this route matches.
|
|
1680
1742
|
|
|
1681
1743
|
- `loader` - `ComponentLoader` - A function returning a dynamic import promise
|
|
@@ -2045,7 +2107,6 @@ interface PreloadOptions {
|
|
|
2045
2107
|
- Relative path navigation? Not sure it's worth the extra bundle size given that users can export/import route objects and pass them as navigation option.
|
|
2046
2108
|
- Refactor: APIs like useParams, useSearch and useMatch should accept any route object and not just rely on the global routes collection.
|
|
2047
2109
|
- Refactor: allow `route()` and `.route()` to be called without passing an argument (defaulting to "/")?
|
|
2048
|
-
- A builder method `.index(component)` to simplify patterns like `useOutlet() ?? <div>Index page</div>`, rendering a component only when no child route matched. In practice, this can spare the definition of a child route for `"/"`.
|
|
2049
2110
|
- Document usage in test environments
|
|
2050
2111
|
- Navigation blockers (`useBlocker`, etc.)
|
|
2051
2112
|
- Open to suggestions, we can discuss them [here](https://github.com/strblr/typeroute/discussions).
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ interface Middleware<S extends {} = any> {
|
|
|
30
30
|
handle: (handle: Handle) => Middleware<S>;
|
|
31
31
|
preload: (preload: (options: PreloadOptions<{}, S>) => Promise<any>) => Middleware<S>;
|
|
32
32
|
component: (component: ComponentType) => Middleware<S>;
|
|
33
|
+
index: (component: ComponentType) => Middleware<S>;
|
|
33
34
|
lazy: (loader: ComponentLoader) => Middleware<S>;
|
|
34
35
|
suspense: (fallback: ComponentType) => Middleware<S>;
|
|
35
36
|
error: (fallback: ComponentType<{
|
|
@@ -132,6 +133,7 @@ declare class Route<P extends string = string, Ps extends {} = any, S extends {}
|
|
|
132
133
|
handle: (handle: Handle) => Route<P, Ps, S>;
|
|
133
134
|
preload: (preload: (options: PreloadOptions<Ps, S>) => Promise<any>) => Route<P, Ps, S>;
|
|
134
135
|
component: (component: ComponentType) => Route<P, Ps, S>;
|
|
136
|
+
index: (component: ComponentType) => Route<P, Ps, S>;
|
|
135
137
|
lazy: (loader: ComponentLoader) => Route<P, Ps, S>;
|
|
136
138
|
suspense: (fallback: ComponentType) => Route<P, Ps, S>;
|
|
137
139
|
error: (fallback: ComponentType<{
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Component as e,Suspense as t,cloneElement as n,createContext as r,isValidElement as i,lazy as a,memo as o,
|
|
1
|
+
import{Component as e,Suspense as t,cloneElement as n,createContext as r,isValidElement as i,lazy as a,memo as o,useContext as s,useEffect as c,useInsertionEffect as ee,useLayoutEffect as l,useMemo as u,useRef as d,useState as f,useSyncExternalStore as p}from"react";import{inject as m,parse as h}from"regexparam";import{jsx as g}from"react/jsx-runtime";function _(e){return`/${e}`.replaceAll(/\/+/g,`/`).replace(/(.+)\/$/,`$1`)}function v(e){let{keys:t,pattern:n}=h(e);return{pattern:e,keys:t,regex:n,loose:h(e,!0).pattern,weights:e.split(`/`).slice(1).map(e=>e.includes(`*`)?0:e.includes(`:`)?1:2)}}function y(e){return typeof e==`function`?e:t=>{let n=e[`~standard`].validate(t);if(n instanceof Promise)throw Error(`[TypeRoute] Validation can't be async`);if(n.issues)throw Error(`[TypeRoute] Validation failed`,{cause:n.issues});return n.value}}function b(e){return Object.entries(e).filter(([e,t])=>t!==void 0).map(([e,t])=>`${e}=${encodeURIComponent(S(t))}`).join(`&`)}function x(e){let t={};return new URLSearchParams(e).forEach((e,n)=>{t[n]=C(e)?JSON.parse(e):e}),t}function S(e){return typeof e==`string`&&!C(e)?e:JSON.stringify(e)}function C(e){try{return JSON.parse(e),!0}catch{return!1}}function w(e,t){return _(`${t}/${e}`)}function T(e,t){return(e===t||e.startsWith(`${t}/`))&&(e=e.slice(t.length)||`/`),e}function E(e,t){return[e,b(t)].filter(Boolean).join(`?`)}function D(e){let{pathname:t,search:n}=new URL(e,`http://w`);return{path:t,search:x(n)}}function O({keys:e,regex:t,loose:n},r,i,a){let o=(r?t:n).exec(T(i,a));if(!o)return null;let s={};return e.forEach((e,t)=>{let n=o[t+1];n&&(s[e]=n)}),s}function k(e){return[...e].sort((e,t)=>{let n=e.route._.weights,r=t.route._.weights,i=Math.max(n.length,r.length);for(let e=0;e<i;e++){let t=n[e]??-1,i=r[e]??-1;if(t!==i)return i-t}return 0})}const A=r(null),j=r(null),M=r(null),N=r(null);function P(){let e=s(A);if(e)return e;throw Error(`[TypeRoute] useRouter must be within a router context`)}function F(){let e=s(j);if(e)return e;throw Error(`[TypeRoute] useLocation must be within a router context`)}function I(e){let t=P(),{path:n}=F();return u(()=>t.match(n,e),[t,n,e.from,e.strict,e.params])}function L(){return s(N)}function te(){return P().navigate}function ne(){let e=s(M);return u(()=>e?.route._.handles??[],[e])}function R(e){let t=I({from:e});if(t)return t.params;throw Error(`[TypeRoute] Can't read params for non-matching route ${e}`)}function z(e){let t=P(),{search:n,path:r}=F(),i=t.getRoute(e),a=u(()=>i._.validate(n),[i,n]);return[a,Z((e,n)=>{e=typeof e==`function`?e(a):e;let i=E(r,{...a,...e});t.navigate({url:i,replace:n})})]}var B=class{_loc=(e,t)=>{let{state:n}=history,[r,i]=this._??[];return i?.path===e&&r===t&&i.state===n?i:(this._=[t,{path:e,search:x(t),state:n}])[1]};constructor(){if(!window[V]){for(let e of[H,U]){let t=history[e];history[e]=function(...n){t.apply(this,n),dispatchEvent(new Event(e))}}window[V]=1}}location=()=>this._loc(location.pathname,location.search);go=e=>history.go(e);push=e=>{let{url:t,replace:n,state:r}=e;history[n?U:H](r,``,t)};subscribe=e=>(W.forEach(t=>addEventListener(t,e)),()=>{W.forEach(t=>removeEventListener(t,e))})};const V=Symbol.for(`wmp01`),H=`pushState`,U=`replaceState`,W=[`popstate`,H,U,`hashchange`];var G=class{constructor(e){let{routes:t,basePath:n=`/`,history:r,context:i,ssrContext:a,defaultLinkOptions:o}=e;this.routes=Object.values(t),this.basePath=_(n),this.history=r??new B,this.context=i,this.ssrContext=a,this.defaultLinkOptions=o,this._=new Map(this.routes.map(e=>[e._.pattern,e]))}getRoute=e=>{if(typeof e!=`string`)return e;let t=this._.get(e);if(!t)throw Error(`[TypeRoute] Route not found for ${e}`);return t};match=(e,t)=>{let{from:n,strict:r,params:i}=t,a=this.getRoute(n),o=O(a._,r,e,this.basePath);return o&&(!i||Object.keys(i).every(e=>i[e]===o[e]))?{route:a,params:o}:null};matchAll=e=>k(this.routes.map(t=>this.match(e,{from:t,strict:!0})).filter(e=>!!e))[0]??null;createUrl=e=>{let{to:t,params:n={},search:r={}}=e,{pattern:i}=this.getRoute(t)._;return E(w(m(i,n),this.basePath),r)};preload=async e=>{let{to:t,params:n={},search:r={}}=e,{preloads:i}=this.getRoute(t)._;await Promise.all(i.map(e=>e({params:n,search:r,context:this.context})))};navigate=e=>{if(typeof e==`number`)this.history.go(e);else if(`url`in e)this.history.push(e);else{let{replace:t,state:n}=e;this.history.push({url:this.createUrl(e),replace:t,state:n})}}},K=class{stack=[];index=0;listeners=new Set;constructor(e=`/`){this.stack.push({...D(e),state:void 0})}location=()=>this.stack[this.index];go=e=>{let t=this.index+e;this.stack[t]&&(this.index=t,this.listeners.forEach(e=>e()))};push=e=>{let{url:t,replace:n,state:r}=e,i={...D(t),state:r};this.stack=this.stack.slice(0,this.index+1),n?this.stack[this.index]=i:this.index=this.stack.push(i)-1,this.listeners.forEach(e=>e())};subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)})},q=class extends B{location=()=>{let{pathname:e,search:t}=new URL(location.hash.slice(1),`http://w`);return this._loc(e,t)};push=e=>{let{url:t,replace:n,state:r}=e;history[n?`replaceState`:`pushState`](r,``,`#${t}`)}};function J(e){let[t]=f(()=>`router`in e?e.router:new G(e)),{subscribe:n,location:r}=t.history,i=p(n,r,r),a=u(()=>t.matchAll(i.path),[t,i.path]);return a||console.error(`[TypeRoute] No matching route for path`,i.path),u(()=>g(A.Provider,{value:t,children:g(j.Provider,{value:i,children:g(M.Provider,{value:a,children:a?.route._.components.reduceRight((e,t)=>g(N.Provider,{value:e,children:g(t,{})}),null)})})}),[t,i,a])}function Y(){return L()}function X(e){let t=P();return l(()=>t.navigate(e),[]),t.ssrContext&&(t.ssrContext.redirect=t.createUrl(e)),null}function re(e){let t=P(),{to:r,replace:a,state:o,params:s,search:ee,strict:l,preload:u,preloadDelay:f=50,style:p,className:m,activeStyle:h,activeClassName:_,asChild:v,children:y,...b}={...t.defaultLinkOptions,...e},x=d(null),S=d(null),C=t.createUrl(e),w=!!I({from:r,strict:l,params:s}),T=Z(()=>{clearTimeout(S.current)}),E=Z(()=>{T(),S.current=setTimeout(()=>t.preload(e),f)}),D={"data-active":w,style:{...p,...w&&h},className:[m,w&&_].filter(Boolean).join(` `)||void 0};c(()=>{if(u===`render`)E();else if(u===`viewport`&&x.current){let e=new IntersectionObserver(e=>e.forEach(e=>{e.isIntersecting?E():T()}));return e.observe(x.current),()=>{e.disconnect(),T()}}return T},[u]);let O=e=>{b.onClick?.(e),!(e.ctrlKey||e.metaKey||e.shiftKey||e.altKey||e.button||e.defaultPrevented)&&(e.preventDefault(),t.navigate({url:C,replace:a,state:o}))},k=(e,t)=>n=>{t?.(n),u===`intent`&&!n.defaultPrevented&&e()},A={...b,...D,ref:ie(x,b.ref),href:C,onClick:O,onFocus:k(E,b.onFocus),onBlur:k(T,b.onBlur),onPointerEnter:k(E,b.onPointerEnter),onPointerLeave:k(T,b.onPointerLeave)};return v&&i(y)?n(y,A):g(`a`,{...A,children:y})}function ie(e,t){return t?n=>{e.current=n;let r=typeof t==`function`?t(n):void(t.current=n);return r&&(()=>{e.current=null,r()})}:e}function Z(e){let t=d(e);return ee(()=>{t.current=e},[e]),d(((...e)=>t.current(...e))).current}function ae(e){return()=>L()??g(e,{})}function oe(e){return()=>g(t,{fallback:g(e,{}),children:L()})}function se(t){class n extends e{state={...this.props};static getDerivedStateFromError(e){return{error:[e]}}static getDerivedStateFromProps(e,t){return e.children===t.children?t:{...e,error:void 0}}render(){return this.state.error?g(t,{error:this.state.error[0]}):this.props.children}}return()=>g(n,{children:L()})}function Q(e){return new $({...v(_(e)),validate:e=>e,handles:[],components:[],preloads:[]})}function ce(){return Q(``)}var $=class e{constructor(e){this._=e}route=t=>new e({...this._,...v(_(`${this._.pattern}/${t}`)),p:this});use=t=>{let{_:n}=t;return new e({...this._,handles:[...this._.handles,...n.handles],components:[...this._.components,...n.components],preloads:[...this._.preloads,...n.preloads]}).search(n.validate)};search=t=>(t=y(t),new e({...this._,validate:e=>{let n=this._.validate(e);return{...n,...t({...e,...n})}}}));handle=t=>new e({...this._,handles:[...this._.handles,t]});preload=t=>new e({...this._,preloads:[...this._.preloads,e=>t({...e,search:this._.validate(e.search)})]});component=t=>new e({...this._,components:[...this._.components,o(t)]});index=e=>this.component(ae(e));lazy=e=>this.preload(e).component(a(()=>e().then(e=>`default`in e?e:{default:e})));suspense=e=>this.component(oe(e));error=e=>this.component(se(e));toString=()=>this._.pattern};export{B as BrowserHistory,q as HashHistory,re as Link,j as LocationContext,M as MatchContext,K as MemoryHistory,X as Navigate,Y as Outlet,N as OutletContext,$ as Route,G as Router,A as RouterContext,J as RouterRoot,ce as middleware,Q as route,ne as useHandles,F as useLocation,I as useMatch,te as useNavigate,L as useOutlet,R as useParams,P as useRouter,z as useSearch};
|