@typeroute/router 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -28
- package/dist/index.d.ts +8 -8
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,25 +9,31 @@
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/@typeroute/router">
|
|
11
11
|
<img
|
|
12
|
-
src="https://img.shields.io/npm/v/%40typeroute%2Frouter?
|
|
12
|
+
src="https://img.shields.io/npm/v/%40typeroute%2Frouter?color=0d1117&labelColor=0d1117"
|
|
13
13
|
alt="npm version"
|
|
14
14
|
/>
|
|
15
15
|
</a>
|
|
16
16
|
<a href="https://www.npmjs.com/package/@typeroute/router">
|
|
17
17
|
<img
|
|
18
|
-
src="https://img.
|
|
18
|
+
src="https://img.shields.io/npm/dw/%40typeroute%2Frouter?color=0d1117&labelColor=0d1117"
|
|
19
|
+
alt="weekly downloads"
|
|
20
|
+
/>
|
|
21
|
+
</a>
|
|
22
|
+
<a href="https://www.npmjs.com/package/@typeroute/router">
|
|
23
|
+
<img
|
|
24
|
+
src="https://img.badgesize.io/https://cdn.jsdelivr.net/npm/@typeroute/router/dist/index.js?compression=gzip&label=gzip&color=0d1117&labelColor=0d1117"
|
|
19
25
|
alt="gzip size"
|
|
20
26
|
/>
|
|
21
27
|
</a>
|
|
22
28
|
<a href="https://github.com/strblr/typeroute/blob/master/LICENSE">
|
|
23
29
|
<img
|
|
24
|
-
src="https://img.shields.io/npm/l/%40typeroute%2Frouter?
|
|
30
|
+
src="https://img.shields.io/npm/l/%40typeroute%2Frouter?color=0d1117&labelColor=0d1117"
|
|
25
31
|
alt="license"
|
|
26
32
|
/>
|
|
27
33
|
</a>
|
|
28
34
|
<a href="https://github.com/sponsors/strblr">
|
|
29
35
|
<img
|
|
30
|
-
src="https://img.shields.io/github/sponsors/strblr?
|
|
36
|
+
src="https://img.shields.io/github/sponsors/strblr?color=0d1117&labelColor=0d1117"
|
|
31
37
|
alt="sponsors"
|
|
32
38
|
/>
|
|
33
39
|
</a>
|
|
@@ -100,8 +106,8 @@ If you believe there's a mistake in the comparison table, please [open an issue]
|
|
|
100
106
|
# Table of contents
|
|
101
107
|
|
|
102
108
|
- [Comparison](#comparison)
|
|
103
|
-
- [Showcase](#showcase)
|
|
104
109
|
- [Installation](#installation)
|
|
110
|
+
- [Showcase](#showcase)
|
|
105
111
|
- [Defining routes](#defining-routes)
|
|
106
112
|
- [Nested routes and layouts](#nested-routes-and-layouts)
|
|
107
113
|
- [Setting up the router](#setting-up-the-router)
|
|
@@ -149,6 +155,16 @@ If you believe there's a mistake in the comparison table, please [open an issue]
|
|
|
149
155
|
|
|
150
156
|
---
|
|
151
157
|
|
|
158
|
+
# Installation
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm install @typeroute/router
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
TypeRoute requires React 18 or higher.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
152
168
|
# Showcase
|
|
153
169
|
|
|
154
170
|
Here's what routing looks like with TypeRoute:
|
|
@@ -156,12 +172,12 @@ Here's what routing looks like with TypeRoute:
|
|
|
156
172
|
```tsx
|
|
157
173
|
import { route, RouterRoot, Outlet, Link, useParams } from "@typeroute/router";
|
|
158
174
|
|
|
159
|
-
//
|
|
175
|
+
// Routes
|
|
160
176
|
const layout = route("/").component(() => (
|
|
161
177
|
<div>
|
|
162
178
|
<nav>
|
|
163
179
|
<Link to="/">Home</Link>
|
|
164
|
-
<Link to=
|
|
180
|
+
<Link to={user} params={{ id: "42" }}>
|
|
165
181
|
User
|
|
166
182
|
</Link>
|
|
167
183
|
</nav>
|
|
@@ -169,10 +185,9 @@ const layout = route("/").component(() => (
|
|
|
169
185
|
</div>
|
|
170
186
|
));
|
|
171
187
|
|
|
172
|
-
// Pages
|
|
173
188
|
const home = layout.route("/").component(() => <h1>Home</h1>);
|
|
174
189
|
|
|
175
|
-
const user = layout.route("/users/:id").component(
|
|
190
|
+
const user = layout.route("/users/:id").component(() => {
|
|
176
191
|
const { id } = useParams(user); // Fully typed
|
|
177
192
|
return <h1>User {id}</h1>;
|
|
178
193
|
});
|
|
@@ -197,16 +212,6 @@ Everything autocompletes and type-checks automatically. No heavy setup, no magic
|
|
|
197
212
|
|
|
198
213
|
---
|
|
199
214
|
|
|
200
|
-
# Installation
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
npm install @typeroute/router
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
TypeRoute requires React 18 or higher.
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
215
|
# Defining routes
|
|
211
216
|
|
|
212
217
|
Routes are created using the `route()` function, following the [builder pattern](https://dev.to/superviz/design-pattern-7-builder-pattern-10j4). You pass it a path and chain methods to configure the route.
|
|
@@ -298,7 +303,7 @@ Beyond paths and components, child routes also inherit search param validators,
|
|
|
298
303
|
|
|
299
304
|
# Setting up the router
|
|
300
305
|
|
|
301
|
-
Before setting up the router, you need to collect your navigable routes into
|
|
306
|
+
Before setting up the router, you need to collect your navigable routes into a collection (either array or record). When building nested route hierarchies, you'll often create intermediate parent routes solely for grouping and shared layouts. These intermediate routes shouldn't be included in your routes collection - only the final, navigable routes should be:
|
|
302
307
|
|
|
303
308
|
```tsx
|
|
304
309
|
// Intermediate route used for hierarchy
|
|
@@ -310,13 +315,16 @@ const about = layout.route("/about").component(About);
|
|
|
310
315
|
|
|
311
316
|
// Collect only the navigable routes
|
|
312
317
|
const routes = [home, about]; // ✅ Don't include `layout`
|
|
318
|
+
|
|
319
|
+
// Or equivalently:
|
|
320
|
+
const routes = { home, about };
|
|
313
321
|
```
|
|
314
322
|
|
|
315
|
-
This makes sure that only actual pages can be matched and appear in autocomplete. The intermediate routes still exist as part of the hierarchy, they just aren't directly navigable. Note that the order of routes in the
|
|
323
|
+
This makes sure that only actual pages can be matched and appear in autocomplete. The intermediate routes still exist as part of the hierarchy, they just aren't directly navigable. Note that the order of routes in the collection doesn't matter - TypeRoute uses a [ranking algorithm](#route-matching-and-ranking) to pick the most specific match.
|
|
316
324
|
|
|
317
325
|
The `RouterRoot` component is the entry point to TypeRoute. It listens to URL changes, matches the current path against your routes, and renders the matching route's component hierarchy.
|
|
318
326
|
|
|
319
|
-
There are two ways to set it up. The simplest is passing your routes
|
|
327
|
+
There are two ways to set it up. The simplest is passing your routes collection directly to `RouterRoot`. This creates a router instance internally (accessible via `useRouter`):
|
|
320
328
|
|
|
321
329
|
```tsx
|
|
322
330
|
import { RouterRoot } from "@typeroute/router";
|
|
@@ -1113,7 +1121,7 @@ For the path `/users/42`:
|
|
|
1113
1121
|
/users/* → [static, wildcard] → weights [2, 0]
|
|
1114
1122
|
```
|
|
1115
1123
|
|
|
1116
|
-
This ranking algorithm means you don't need to order your routes
|
|
1124
|
+
This ranking algorithm means you don't need to order your routes carefully. Define them in any order and TypeRoute figures out the right match regardless:
|
|
1117
1125
|
|
|
1118
1126
|
```tsx
|
|
1119
1127
|
const routes = [
|
|
@@ -1272,9 +1280,7 @@ function handleRequest(req: Request) {
|
|
|
1272
1280
|
if (ssrContext.redirect) {
|
|
1273
1281
|
return Response.redirect(ssrContext.redirect);
|
|
1274
1282
|
}
|
|
1275
|
-
|
|
1276
|
-
headers: { "Content-Type": "text/html" }
|
|
1277
|
-
});
|
|
1283
|
+
// ... Send pre-rendered HTML
|
|
1278
1284
|
}
|
|
1279
1285
|
```
|
|
1280
1286
|
|
|
@@ -1521,7 +1527,7 @@ The `Router` class is the core of TypeRoute. You can create an instance directly
|
|
|
1521
1527
|
**Properties:**
|
|
1522
1528
|
|
|
1523
1529
|
- `router.basePath` - The configured base path
|
|
1524
|
-
- `router.routes` - The array of routes
|
|
1530
|
+
- `router.routes` - The array of navigable routes
|
|
1525
1531
|
- `router.history` - The history instance
|
|
1526
1532
|
- `router.ssrContext` - The SSR context (if provided)
|
|
1527
1533
|
- `router.defaultLinkOptions` - Default link options
|
|
@@ -1917,7 +1923,7 @@ const unsubscribe = history.subscribe(() => {
|
|
|
1917
1923
|
|
|
1918
1924
|
```tsx
|
|
1919
1925
|
interface RouterOptions {
|
|
1920
|
-
routes: Route[]
|
|
1926
|
+
routes: Route[] | Record<string, Route>; // Collection of navigable routes
|
|
1921
1927
|
basePath?: string; // Base path prefix (default: "/")
|
|
1922
1928
|
history?: HistoryLike; // History implementation (default: BrowserHistory)
|
|
1923
1929
|
ssrContext?: SSRContext; // Context for server-side rendering
|
|
@@ -2014,6 +2020,8 @@ interface PreloadContext {
|
|
|
2014
2020
|
|
|
2015
2021
|
- Possibility to pass an arbitrary context to the Router instance for later use in preloads?
|
|
2016
2022
|
- 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.
|
|
2023
|
+
- Refactor: APIs like useParams, useSearch and useMatch should accept any route object and not just rely on the global routes array.
|
|
2024
|
+
- Refactor: allow `route()` and `.route()` to be called without passing an argument (defaulting to "/")?
|
|
2017
2025
|
- Document usage in test environments
|
|
2018
2026
|
- Navigation blockers (`useBlocker`, etc.)
|
|
2019
2027
|
- Open to suggestions, we can discuss them [here](https://github.com/strblr/typeroute/discussions).
|
package/dist/index.d.ts
CHANGED
|
@@ -14,9 +14,9 @@ type OptionalOnUndefined<T extends object> = Simplify<{ [K in keyof T as undefin
|
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/types.d.ts
|
|
16
16
|
interface Register {}
|
|
17
|
-
type
|
|
18
|
-
routes: infer
|
|
19
|
-
} ?
|
|
17
|
+
type NavigableRoute = Register extends {
|
|
18
|
+
routes: infer Routes;
|
|
19
|
+
} ? Routes extends ReadonlyArray<Route> ? Routes[number] : Routes extends Record<string, Route> ? Routes[keyof Routes] : Route : Route;
|
|
20
20
|
type Handle = Register extends {
|
|
21
21
|
handle: infer Handle;
|
|
22
22
|
} ? Handle : any;
|
|
@@ -38,14 +38,14 @@ interface PreloadContext<Ps extends {} = any, S extends {} = any> {
|
|
|
38
38
|
search: S;
|
|
39
39
|
}
|
|
40
40
|
interface RouterOptions {
|
|
41
|
-
routes:
|
|
41
|
+
routes: ReadonlyArray<NavigableRoute> | Record<string, NavigableRoute>;
|
|
42
42
|
basePath?: string;
|
|
43
43
|
history?: HistoryLike;
|
|
44
44
|
ssrContext?: SSRContext;
|
|
45
45
|
defaultLinkOptions?: LinkOptions;
|
|
46
46
|
}
|
|
47
|
-
type Pattern =
|
|
48
|
-
type GetRoute<P extends Pattern> = Extract<
|
|
47
|
+
type Pattern = NavigableRoute["_"]["pattern"];
|
|
48
|
+
type GetRoute<P extends Pattern> = Extract<NavigableRoute, {
|
|
49
49
|
_: {
|
|
50
50
|
pattern: P;
|
|
51
51
|
};
|
|
@@ -137,7 +137,7 @@ declare class Route<P extends string = string, Ps extends {} = any, S extends {}
|
|
|
137
137
|
//#endregion
|
|
138
138
|
//#region src/router/router.d.ts
|
|
139
139
|
declare class Router {
|
|
140
|
-
readonly routes:
|
|
140
|
+
readonly routes: ReadonlyArray<NavigableRoute>;
|
|
141
141
|
readonly basePath: string;
|
|
142
142
|
readonly history: HistoryLike;
|
|
143
143
|
readonly ssrContext?: SSRContext;
|
|
@@ -210,4 +210,4 @@ declare const LocationContext: react.Context<HistoryLocation | null>;
|
|
|
210
210
|
declare const MatchContext: react.Context<Match | null>;
|
|
211
211
|
declare const OutletContext: react.Context<ReactNode>;
|
|
212
212
|
//#endregion
|
|
213
|
-
export { BrowserHistory, ComponentLoader, GetRoute, Handle, HashHistory, HistoryLike, HistoryLocation, HistoryPushOptions, Link, LinkOptions, LinkProps, LocationContext, Match, MatchContext, MatchOptions, MemoryHistory, Middleware, Navigate, NavigateOptions, NavigateProps, Outlet, OutletContext, Params, Pattern, PreloadContext, Register, Route,
|
|
213
|
+
export { BrowserHistory, ComponentLoader, GetRoute, Handle, HashHistory, HistoryLike, HistoryLocation, HistoryPushOptions, Link, LinkOptions, LinkProps, LocationContext, Match, MatchContext, MatchOptions, MemoryHistory, Middleware, NavigableRoute, Navigate, NavigateOptions, NavigateProps, Outlet, OutletContext, Params, Pattern, PreloadContext, Register, Route, Router, RouterContext, RouterOptions, RouterRoot, RouterRootProps, SSRContext, Search, Updater, Validator, middleware, route, useHandles, useLocation, useMatch, useNavigate, useOutlet, useParams, useRouter, useSearch };
|
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,useCallback as s,useContext as c,useEffect as l,useInsertionEffect as u,useLayoutEffect as d,useMemo as f,useRef as p,useState as m,useSyncExternalStore as h}from"react";import{inject as g,parse as _}from"regexparam";import{jsx as v}from"react/jsx-runtime";function y(e){return`/${e}`.replaceAll(/\/+/g,`/`).replace(/(.+)\/$/,`$1`)}function b(e){let{keys:t,pattern:n}=_(e);return{pattern:e,keys:t,regex:n,loose:_(e,!0).pattern,weights:e.split(`/`).slice(1).map(e=>e.includes(`*`)?0:e.includes(`:`)?1:2)}}function x(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 S(e){return Object.entries(e).filter(([e,t])=>t!==void 0).map(([e,t])=>`${e}=${encodeURIComponent(w(t))}`).join(`&`)}function C(e){let t=new URLSearchParams(e);return Object.fromEntries([...t.entries()].map(([e,t])=>(t=decodeURIComponent(t),[e,T(t)?JSON.parse(t):t])))}function w(e){return typeof e==`string`&&!T(e)?e:JSON.stringify(e)}function T(e){try{return JSON.parse(e),!0}catch{return!1}}function E(e,t){return y(`${t}/${e}`)}function D(e,t){return(e===t||e.startsWith(`${t}/`))&&(e=e.slice(t.length)||`/`),e}function O(e,t){return[e,S(t)].filter(Boolean).join(`?`)}function k(e){let{pathname:t,search:n}=new URL(e,`http://w`);return{path:t,search:C(n)}}function A({keys:e,regex:t,loose:n},r,i,a){let o=(r?t:n).exec(D(i,a));if(!o)return null;let s={};return e.forEach((e,t)=>{let n=o[t+1];n&&(s[e]=n)}),s}function j(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 M=r(null),N=r(null),P=r(null),F=r(null);function I(){let e=c(M);if(e)return e;throw Error(`[TypeRoute] useRouter must be within a router context`)}function L(){let e=c(N);if(e)return e;throw Error(`[TypeRoute] useLocation must be within a router context`)}function R(e){let t=I(),{path:n}=L();return f(()=>t.match(n,e),[t,n,e.from,e.strict,e.params])}function z(){return c(F)}function B(){return I().navigate}function V(){let e=c(P);return f(()=>e?.route._.handles??[],[e])}function H(e){let t=R({from:e});if(t)return t.params;throw Error(`[TypeRoute] Can't read params for non-matching route ${e}`)}function U(e){let t=I(),{search:n,path:r}=L(),i=t.getRoute(e),a=f(()=>i._.validate(n),[i,n]);return[a,Z((e,n)=>{e=typeof e==`function`?e(a):e;let i=O(r,{...a,...e});t.navigate({url:i,replace:n})})]}var W=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:C(t),state:n}])[1]};constructor(){if(!window[G]){for(let e of[K,q]){let t=history[e];history[e]=function(...n){t.apply(this,n),dispatchEvent(new Event(e))}}window[G]=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?q:K](r,``,t)};subscribe=e=>(J.forEach(t=>window.addEventListener(t,e)),()=>{J.forEach(t=>window.removeEventListener(t,e))})};const G=Symbol.for(`wmp01`),K=`pushState`,q=`replaceState`,J=[`popstate`,K,q,`hashchange`];var Y=class{routes;basePath;history;ssrContext;defaultLinkOptions;_;constructor(e){let{routes:t,basePath:n=`/`,history:r,ssrContext:i,defaultLinkOptions:a}=e;this.routes=t,this.basePath=y(n),this.history=r??new W,this.ssrContext=i,this.defaultLinkOptions=a,this._={routeMap:new Map(
|
|
1
|
+
import{Component as e,Suspense as t,cloneElement as n,createContext as r,isValidElement as i,lazy as a,memo as o,useCallback as s,useContext as c,useEffect as l,useInsertionEffect as u,useLayoutEffect as d,useMemo as f,useRef as p,useState as m,useSyncExternalStore as h}from"react";import{inject as g,parse as _}from"regexparam";import{jsx as v}from"react/jsx-runtime";function y(e){return`/${e}`.replaceAll(/\/+/g,`/`).replace(/(.+)\/$/,`$1`)}function b(e){let{keys:t,pattern:n}=_(e);return{pattern:e,keys:t,regex:n,loose:_(e,!0).pattern,weights:e.split(`/`).slice(1).map(e=>e.includes(`*`)?0:e.includes(`:`)?1:2)}}function x(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 S(e){return Object.entries(e).filter(([e,t])=>t!==void 0).map(([e,t])=>`${e}=${encodeURIComponent(w(t))}`).join(`&`)}function C(e){let t=new URLSearchParams(e);return Object.fromEntries([...t.entries()].map(([e,t])=>(t=decodeURIComponent(t),[e,T(t)?JSON.parse(t):t])))}function w(e){return typeof e==`string`&&!T(e)?e:JSON.stringify(e)}function T(e){try{return JSON.parse(e),!0}catch{return!1}}function E(e,t){return y(`${t}/${e}`)}function D(e,t){return(e===t||e.startsWith(`${t}/`))&&(e=e.slice(t.length)||`/`),e}function O(e,t){return[e,S(t)].filter(Boolean).join(`?`)}function k(e){let{pathname:t,search:n}=new URL(e,`http://w`);return{path:t,search:C(n)}}function A({keys:e,regex:t,loose:n},r,i,a){let o=(r?t:n).exec(D(i,a));if(!o)return null;let s={};return e.forEach((e,t)=>{let n=o[t+1];n&&(s[e]=n)}),s}function j(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 M=r(null),N=r(null),P=r(null),F=r(null);function I(){let e=c(M);if(e)return e;throw Error(`[TypeRoute] useRouter must be within a router context`)}function L(){let e=c(N);if(e)return e;throw Error(`[TypeRoute] useLocation must be within a router context`)}function R(e){let t=I(),{path:n}=L();return f(()=>t.match(n,e),[t,n,e.from,e.strict,e.params])}function z(){return c(F)}function B(){return I().navigate}function V(){let e=c(P);return f(()=>e?.route._.handles??[],[e])}function H(e){let t=R({from:e});if(t)return t.params;throw Error(`[TypeRoute] Can't read params for non-matching route ${e}`)}function U(e){let t=I(),{search:n,path:r}=L(),i=t.getRoute(e),a=f(()=>i._.validate(n),[i,n]);return[a,Z((e,n)=>{e=typeof e==`function`?e(a):e;let i=O(r,{...a,...e});t.navigate({url:i,replace:n})})]}var W=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:C(t),state:n}])[1]};constructor(){if(!window[G]){for(let e of[K,q]){let t=history[e];history[e]=function(...n){t.apply(this,n),dispatchEvent(new Event(e))}}window[G]=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?q:K](r,``,t)};subscribe=e=>(J.forEach(t=>window.addEventListener(t,e)),()=>{J.forEach(t=>window.removeEventListener(t,e))})};const G=Symbol.for(`wmp01`),K=`pushState`,q=`replaceState`,J=[`popstate`,K,q,`hashchange`];var Y=class{routes;basePath;history;ssrContext;defaultLinkOptions;_;constructor(e){let{routes:t,basePath:n=`/`,history:r,ssrContext:i,defaultLinkOptions:a}=e;this.routes=Object.values(t),this.basePath=y(n),this.history=r??new W,this.ssrContext=i,this.defaultLinkOptions=a,this._={routeMap:new Map(this.routes.map(e=>[e._.pattern,e]))}}getRoute=e=>{if(typeof e!=`string`)return e;let t=this._.routeMap.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=A(a._,r,e,this.basePath);return o&&(!i||Object.keys(i).every(e=>i[e]===o[e]))?{route:a,params:o}:null};matchAll=e=>j(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 O(E(g(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})))};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})}}},X=class{stack=[];index=0;listeners=new Set;constructor(e=`/`){this.stack.push({...k(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={...k(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)})},ee=class extends W{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 te(e){let[t]=m(()=>`router`in e?e.router:new Y(e)),{subscribe:n,location:r}=t.history,i=h(n,r,r),a=f(()=>t.matchAll(i.path),[t,i.path]);return a||console.error(`[TypeRoute] No matching route for path`,i.path),f(()=>v(M.Provider,{value:t,children:v(N.Provider,{value:i,children:v(P.Provider,{value:a,children:a?.route._.components.reduceRight((e,t)=>v(F.Provider,{value:e,children:v(t,{})}),null)})})}),[t,i,a])}function ne(){return z()}function re(e){let t=I();return d(()=>t.navigate(e),[]),t.ssrContext&&(t.ssrContext.redirect=t.createUrl(e)),null}function ie(e){let t=I(),{to:r,replace:a,state:o,params:c,search:u,strict:d,preload:m,preloadDelay:h=50,style:g,className:_,activeStyle:y,activeClassName:b,asChild:x,children:S,...C}={...t.defaultLinkOptions,...e},w=p(null),T=p(null),E=t.createUrl(e),D=!!R({from:r,strict:d,params:c}),O=Z(()=>t.preload(e)),k=s(()=>{clearTimeout(T.current)},[]),A=s(()=>{k(),T.current=setTimeout(O,h)},[h,k]),j=f(()=>({"data-active":D,style:{...g,...D&&y},className:[_,D&&b].filter(Boolean).join(` `)||void 0}),[D,g,_,y,b]);l(()=>{if(m===`render`)A();else if(m===`viewport`&&w.current){let e=new IntersectionObserver(e=>e.forEach(e=>{e.isIntersecting?A():k()}));return e.observe(w.current),()=>{e.disconnect(),k()}}return k},[m,A,k]);let M=e=>{C.onClick?.(e),!(e.ctrlKey||e.metaKey||e.shiftKey||e.altKey||e.button!==0||e.defaultPrevented)&&(e.preventDefault(),t.navigate({url:E,replace:a,state:o}))},N=(e,t)=>n=>{t?.(n),m===`intent`&&!n.defaultPrevented&&e()},P={...C,...j,ref:ae(w,C.ref),href:E,onClick:M,onFocus:N(A,C.onFocus),onBlur:N(k,C.onBlur),onPointerEnter:N(A,C.onPointerEnter),onPointerLeave:N(k,C.onPointerLeave)};return x&&i(S)?n(S,P):v(`a`,{...P,children:S})}function ae(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=p(e);return u(()=>{t.current=e},[e]),p(((...e)=>t.current(...e))).current}function oe(e){return()=>v(t,{fallback:v(e,{}),children:z()})}function se(t){class n extends e{constructor(e){super(e),this.state={...e}}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?v(t,{error:this.state.error[0]}):this.props.children}}return()=>v(n,{children:z()})}function Q(e){return new $({...b(y(e)),validate:e=>e,handles:[],components:[],preloads:[]})}function ce(){return Q(``)}var $=class e{_;_types;constructor(e){this._=e}route=t=>new e({...this._,...b(y(`${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=x(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({params:e.params,search:this._.validate(e.search)})]});component=t=>new e({...this._,components:[...this._.components,o(t)]});lazy=e=>{let t=a(async()=>{let t=await e();return`default`in t?t:{default:t}});return this.preload(e).component(t)};suspense=e=>this.component(oe(e));error=e=>this.component(se(e));toString=()=>this._.pattern};export{W as BrowserHistory,ee as HashHistory,ie as Link,N as LocationContext,P as MatchContext,X as MemoryHistory,re as Navigate,ne as Outlet,F as OutletContext,$ as Route,Y as Router,M as RouterContext,te as RouterRoot,ce as middleware,Q as route,V as useHandles,L as useLocation,R as useMatch,B as useNavigate,z as useOutlet,H as useParams,I as useRouter,U as useSearch};
|