routexiz 0.2.2 → 0.2.3-z1

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
@@ -4,10 +4,10 @@
4
4
 
5
5
  <a href="https://codesandbox.io/p/sandbox/2lslks" target="_blank">LIVE EXAMPLE</a>
6
6
 
7
- A lightweight, powerful, and modern router for React.
7
+ A lightweight, modern, and flexible React router with tree-based routing, Suspense-first data loading, and nested route support.
8
8
 
9
- > Tree-based routing modeled like a TreeView, where each route is a node and navigation resolves a single path from root to leaf.
10
- > Nested layouts via `children`.
9
+ > Routes are modeled like a tree, where each route is a node and navigation resolves a path from root to leaf.
10
+ > Nested layouts, guards, middlewares, and per-route hooks are fully supported.
11
11
 
12
12
  ---
13
13
 
@@ -60,53 +60,33 @@ export default function App() {
60
60
 
61
61
  ---
62
62
 
63
- # Routing API
64
-
65
- <b>route</b>
66
-
67
- ```ts
68
- route(path, component, config?)
69
- ```
70
-
71
- <b>builder</b>
63
+ # Nested Routing & Builder API
72
64
 
73
65
  ```ts
74
- // /home
75
66
  route("/", Layout, root => {
76
- // /dashboard
77
67
  root.route("/dashboard", Dashboard, dash => {
78
- dash.guard(fn)
79
- dash.middleware(fn)
80
- dash.meta({ title: "Dashboard" })
81
- })
82
- // more
83
- })
84
-
85
- // /admin
86
- route("/admin", Admin, root => {
87
- // /admin/dashboard
88
- root.route("/dashboard", AdminDashboard)
89
- // more
90
- })
91
-
92
- // more
93
-
94
- ```
68
+ dash.route("/stats", () => <div>Dashboard Stats Page</div>);
69
+ dash.route("/settings", () => <div>Dashboard Settings Page</div>);
70
+ });
95
71
 
96
- <b>flatten</b>
72
+ root.route("/users", Users);
73
+ root.route("/users/:id", User, {
74
+ loader: async ({ params }) => ({ id: params.id, name: "User " + params.id }),
75
+ fallback: <div>Loading user...</div>,
76
+ errorBoundary: ({ error }) => <div>Error: {String(error)}</div>,
77
+ });
97
78
 
98
- ```ts
99
- route("/admin", Admin)
100
- route("/admin/dashboard", AdminDashboard)
79
+ root.route("*", NotFound); // wildcard fallback
80
+ });
101
81
  ```
102
82
 
103
- > Supports multiple route trees and deeply nested routing via a flexible builder API.
83
+ > Each node can have its own layout, children, loader, hooks, and options.
104
84
 
105
85
  ---
106
86
 
107
87
  # Navigation
108
88
 
109
- ## useNavigate
89
+ ## useNavigate / navigate
110
90
 
111
91
  ```ts
112
92
  const navigate = useNavigate()
@@ -125,10 +105,9 @@ navigate("/users/:id", {
125
105
  ## redirect
126
106
 
127
107
  ```ts
128
- redirect("/users/:id", {
129
- params: { id: 1 },
130
- query: { tab: "profile" }
131
- })
108
+ import { redirect } from "routexiz";
109
+
110
+ redirect("/users/:id", { params: { id: 1 } });
132
111
  ```
133
112
 
134
113
  ---
@@ -136,24 +115,16 @@ redirect("/users/:id", {
136
115
  ## Link
137
116
 
138
117
  ```tsx
139
- <Link
140
- to="/users/:id"
141
- params={{ id: 1 }}
142
- query={{ tab: "profile" }}
143
- >
144
- Open User
118
+ <Link to="/users/:id" params={{ id: 1 }} query={{ tab: "profile" }}>
119
+ User 1
145
120
  </Link>
146
- ```
147
-
148
- ## NavLink
149
121
 
150
- ```tsx
151
122
  <NavLink
152
123
  to="/users/:id"
153
124
  params={{ id: 1 }}
154
125
  className={({ isActive }) => isActive ? "active" : "link"}
155
126
  >
156
- Open User
127
+ User 1
157
128
  </NavLink>
158
129
  ```
159
130
 
@@ -161,54 +132,76 @@ redirect("/users/:id", {
161
132
 
162
133
  # Data Loading
163
134
 
135
+ Loader allows async data fetching per route, compatible with React Suspense:
136
+
164
137
  ```ts
165
- route("/users/:id", User, {
166
- loader: async ({ params }) => fetchUser(params.id)
167
- })
138
+ root.route("/users/:id", User, {
139
+ loader: async ({ params }) => fetchUser(params.id),
140
+ fallback: <div>Loading user...</div>,
141
+ errorBoundary: ({ error }) => <div>Error: {String(error)}</div>
142
+ });
143
+ ```
168
144
 
145
+ Use useLoaderData() to access loaded data inside components
146
+
147
+ ```ts
169
148
  // Access loader data:
170
149
  const data = useLoaderData()
171
150
  ```
172
151
 
173
152
  ---
174
153
 
175
- # Guards
154
+ # Route Options & Hooks
155
+
156
+
157
+ ## 1️⃣ Guard
176
158
 
177
159
  A guard determines whether navigation is allowed.
178
160
 
179
- - Runs before entering a route
161
+ - Runs before entering the route
180
162
  - Can block navigation
181
- - Returns:
182
- - `true` → allow
183
- - `false` → block
184
163
 
185
164
  ```ts
186
- dash.guard(({ params }) => {
187
- if (!isLoggedIn()) return false
188
- })
165
+ root.route("/dashboard", Dashboard, dash => {
166
+ dash.guard(({ params }) => {
167
+ if (!isLoggedIn()) return false; // block
168
+ return true; // allow
169
+ });
170
+ });
189
171
  ```
190
172
 
191
173
  ---
192
174
 
193
- # Middleware
175
+ ## 2️⃣ Middleware
194
176
 
195
- A middleware performs side effects during navigation.
177
+ Middleware runs after guards, used for side effects.
196
178
 
197
- - Runs after guards
198
179
  - Cannot block navigation
199
- - Used for processing, not decision-making
180
+ - Can be async
200
181
 
201
182
  ```ts
202
- dash.middleware(async ({ params }) => {
203
- console.log("run side effects")
204
- })
183
+ dash.middleware(async ({ params, query }) => {
184
+ console.log("Visited dashboard with params:", params);
185
+ });
205
186
  ```
206
187
 
207
188
  ---
208
189
 
209
- # Key Differences Guards - Middleware
190
+ ## 3️⃣ onEnter / onLeave
191
+
192
+ Hooks triggered when entering or leaving a route node:
193
+ ```ts
194
+ root.onEnter(({ path, params }) => console.log("Enter root:", path));
195
+ root.onLeave(({ path, params }) => console.log("Leave root:", path));
196
+ ```
210
197
 
211
- | Feature | Guard | Middleware |
198
+ - onEnter called when node becomes active
199
+ - onLeave → called when node is left
200
+
201
+
202
+ <b> Guards - Middleware</b>
203
+
204
+ | Creteria | Guard | Middleware |
212
205
  | --------------- | ------------------------- | -------------------- |
213
206
  | Purpose | Control access | Perform side effects |
214
207
  | Can block route | ✅ Yes | ❌ No |
@@ -224,57 +217,27 @@ dash.middleware(async ({ params }) => {
224
217
 
225
218
  ---
226
219
 
227
- # Prefetch
220
+ ## 4️⃣ prefetch
228
221
 
229
- - Hover
230
- - Viewport (IntersectionObserver)
222
+ Prefetch loader data in advance:
231
223
 
232
224
  ```tsx
233
- import { Link } from "routexiz"
234
-
235
- export default function UsersList() {
236
- return (
237
- <div>
238
- <h2>Users</h2>
239
- {/* Prefetch on hover */}
240
- <Link to="/users/1" params={{ id: 1 }} query={{ tab: "profile" }}>
241
- User 1 (hover to prefetch)
242
- </Link>
243
-
244
- {/* Prefetch when enters viewport */}
245
- <Link to="/users/2" params={{ id: 2 }} query={{ tab: "profile" }}>
246
- User 2 (prefetch on viewport)
247
- </Link>
248
-
249
- {/* disablePrefetch */}
250
- <Link to="/users/2" params={{ id: 3 }} query={{ tab: "profile" }} disablePrefetch>
251
- User 3
252
- </Link>
253
- </div>
254
- )
255
- }
225
+ <Link to="/users/:id" params={{ id: 1 }}>User 1</Link>
256
226
  ```
257
227
 
258
- > Prefetch is enabled by default on hover and when the link enters the viewport.
259
-
260
- ---
261
-
262
- # Error Handling
263
-
264
- ```ts
265
- route("/users/:id", User, {
266
- errorBoundary: ({ error }) => <div>Error: {String(error)}</div>
267
- })
268
- ```
228
+ - Hover prefetch by default
229
+ - Viewport → prefetch when enters viewport
230
+ - Disable: disablePrefetch
269
231
 
270
232
  ---
271
233
 
272
- # Fallback
234
+ ## 5️⃣ errorBoundary / fallback
273
235
 
274
236
  ```ts
275
- route("/users/:id", User, {
276
- fallback: <div>Loading...</div>
277
- })
237
+ root.route("/users/:id", User, {
238
+ fallback: <div>Loading user...</div>,
239
+ errorBoundary: ({ error }) => <div>Error: {String(error)}</div>,
240
+ });
278
241
  ```
279
242
 
280
243
  ---
@@ -556,6 +519,129 @@ export default function App() {
556
519
 
557
520
  ---
558
521
 
522
+ # Data Preloading (SSR / Advanced)
523
+
524
+ `routexiz` provides loadRouteData() to preload all route loaders before rendering.
525
+
526
+ This is useful for:
527
+ - SSR (Server-Side Rendering)
528
+ - Prefetching with full data
529
+ - Avoiding loading flicker
530
+
531
+ ## 1️⃣ Basic Usage
532
+ ```ts
533
+ import { loadRouteData } from "routexiz"
534
+
535
+ const data = await loadRouteData("/users/1")
536
+ ```
537
+
538
+ ## 2️⃣ Example Route
539
+
540
+ ```ts
541
+ route("/users/:id", User, {
542
+ loader: async ({ params }) => {
543
+ await new Promise(r => setTimeout(r, 300))
544
+ return { id: params.id, name: "User " + params.id }
545
+ }
546
+ })
547
+ ```
548
+
549
+ ## 3️⃣ Access Loaded Data
550
+
551
+ `loadRouteData` returns a map of resources (Suspense-based):
552
+
553
+ ```ts
554
+ const data = await loadRouteData("/users/1")
555
+
556
+ const user = data["/users/:id"].get()
557
+
558
+ console.log(user)
559
+ // { id: "1", name: "User 1" }
560
+ ```
561
+
562
+ ## 4️⃣ SSR Example
563
+ ```ts
564
+ import { loadRouteData } from "routexiz"
565
+
566
+ async function render(url: string) {
567
+ const data = await loadRouteData(url)
568
+
569
+ const user = data["/users/:id"]?.get()
570
+
571
+ return `
572
+ <html>
573
+ <body>
574
+ <h1>${user?.name}</h1>
575
+ </body>
576
+ </html>
577
+ `
578
+ }
579
+
580
+ render("/users/1")
581
+ ```
582
+
583
+ ## 5️⃣ Nested Routes Example
584
+ ```ts
585
+ route("/", Layout, root => {
586
+ root.route("/dashboard", Dashboard, dash => {
587
+ dash.route("/users/:id", User, {
588
+ loader: async ({ params }) => {
589
+ return { id: params.id }
590
+ }
591
+ })
592
+ })
593
+ })
594
+
595
+ ```
596
+
597
+ ```ts
598
+ const data = await loadRouteData("/dashboard/users/1")
599
+
600
+ const user = data["/dashboard/users/:id"].get()
601
+ ```
602
+
603
+ ## 6️⃣ Notes
604
+
605
+ - Each route loader returns a resource (Suspense-based)
606
+
607
+ - Use .get() to read data safely
608
+
609
+ - .read() is used internally to trigger loading
610
+
611
+ ## 7️⃣ Difference vs prefetch
612
+
613
+ | Function | Behavior |
614
+ | ----------------- | ------------------------------ |
615
+ | `prefetch()` | Trigger loading (non-blocking) |
616
+ | `loadRouteData()` | Await full data (blocking) |
617
+
618
+ ## When to use
619
+
620
+ - ✅ SSR (recommended)
621
+ - ✅ Full preloading before navigation
622
+ - ✅ Testing / debugging loaders
623
+
624
+ ---
625
+
626
+ # Comparison
627
+
628
+ `routexiz` focuses on **modern React routing** with Suspense-first `data loading`, `nested routes`, `guards`, `middleware`, and `prefetch/caching`.
629
+ It’s lightweight and flexible, designed for client-side SPAs.
630
+
631
+
632
+ | Criteria | routexiz | React Router | TanStack Router | Remix |
633
+ | ------------------------- | -------- | ------------ | --------------- | ----- |
634
+ | Nested routes builder API | ✅ | ✅ | ✅ | ✅ |
635
+ | Suspense-first loaders | ✅ | ⚠️ | ✅ | ✅ |
636
+ | Guards & middleware | ✅ | ⚠️ | ✅ | ⚠️ |
637
+ | Prefetch / caching | ✅ | ❌ | ✅ | ⚠️ |
638
+ | Error boundary per route | ✅ | ⚠️ | ✅ | ✅ |
639
+ | Transition support | ✅ | ❌ | ⚠️ | ⚠️ |
640
+ | Lightweight & minimal | ✅ | ⚠️ | ⚠️ | ⚠️ |
641
+
642
+ ---
643
+
644
+
559
645
  # Full Example
560
646
 
561
647
  ## Users.tsx
@@ -583,21 +669,17 @@ export function Users() {
583
669
  ## App.tsx
584
670
 
585
671
  ```ts
586
- import React, { ReactNode } from "react";
672
+ import React, { ReactNode, Suspense } from "react";
587
673
  import {
588
674
  Link,
589
675
  RouterProvider,
590
- setGlobalFallback,
591
- setGlobalError,
592
676
  route,
593
677
  useLoaderData,
594
678
  useParams,
595
679
  useQuery,
596
- useNavigate,
597
- navigate,
598
680
  } from "routexiz";
599
681
 
600
- import "routexiz/styles.css"
682
+ import "routexiz/styles.css";
601
683
 
602
684
  /* =========================
603
685
  DEMO PAGES
@@ -612,6 +694,7 @@ function Layout({ children }: { children?: ReactNode }) {
612
694
  <Link to="/dashboard/stats">Dashboard Stats</Link> |{" "}
613
695
  <Link to="/dashboard/settings">Dashboard Settings</Link> |{" "}
614
696
  <Link to="/users">Users</Link> |{" "}
697
+ <Link to="/users/1">User 1</Link> |{" "}
615
698
  <Link to="/about">About</Link> |{" "}
616
699
  <Link to="/about/team">Team</Link> |{" "}
617
700
  <Link to="/contact">Contact</Link> |{" "}
@@ -629,6 +712,7 @@ function Dashboard({ children }: { children?: ReactNode }) {
629
712
  return <div>Dashboard {children}</div>;
630
713
  }
631
714
 
715
+ // Lazy-loaded Users list
632
716
  const Users = React.lazy(() => import("./Users"));
633
717
 
634
718
  function User() {
@@ -662,28 +746,21 @@ function Contact() {
662
746
  return <div>Contact Page</div>;
663
747
  }
664
748
 
665
- function Blog({ children }: { children?: ReactNode }) {
749
+ function Blog() {
666
750
  const data = useLoaderData<any>();
667
- return (
668
- <div>
669
- <h3>Blog: {data.slug}</h3>
670
- {children}
671
- </div>
672
- );
751
+ return <div>Blog Post: {data.slug}</div>;
673
752
  }
674
753
 
675
754
  function Search() {
676
- const query = useQuery();
677
755
  const params = useParams();
678
-
756
+ const query = useQuery();
679
757
  const term = Array.isArray(query.term) ? query.term[0] : query.term;
680
758
 
681
759
  return (
682
760
  <div>
683
761
  <h3>Search Page</h3>
684
- <div>Term param: {params.term || "none"}</div>
685
- <div>Term query: {term || "none"}</div>
686
- <div>Full query: {JSON.stringify(query)}</div>
762
+ <div>Param: {params.term || "none"}</div>
763
+ <div>Query: {term || "none"}</div>
687
764
  </div>
688
765
  );
689
766
  }
@@ -695,18 +772,26 @@ function NotFound() {
695
772
  /* =========================
696
773
  ROUTES SETUP
697
774
  ========================= */
698
-
699
775
  function setupRoutes() {
700
776
  route("/", Layout, (root) => {
777
+ // Dashboard + nested
701
778
  root.route("/dashboard", Dashboard, (dash) => {
779
+ dash.guard(({ path }) => {
780
+ console.log("Guard dashboard:", path);
781
+ return true;
782
+ });
783
+ dash.middleware(({ path }) => console.log("Middleware dashboard:", path));
784
+ dash.onEnter(({ path }) => console.log("Enter dashboard:", path));
785
+ dash.onLeave(({ path }) => console.log("Leave dashboard:", path));
786
+
702
787
  dash.route("/stats", () => <div>Dashboard Stats Page</div>);
703
788
  dash.route("/settings", () => <div>Dashboard Settings Page</div>);
704
789
  });
705
790
 
706
- root.route("users", Users); // list page
707
- root.route("users/:id/profile", () => <div>User Profile Page</div>);
708
- root.route("users/:id/posts", () => <div>User Posts Page</div>);
791
+ // Users list
792
+ root.route("/users", Users); // lazy page
709
793
 
794
+ // Dynamic user pages
710
795
  root.route("/users/:id", User, {
711
796
  loader: async ({ params }) => {
712
797
  await new Promise((r) => setTimeout(r, 400));
@@ -717,15 +802,22 @@ function setupRoutes() {
717
802
  errorBoundary: ({ error }) => <div>Error: {String(error)}</div>,
718
803
  });
719
804
 
720
- root.route("about", About, about => {
805
+ // Profile & posts subroutes
806
+ root.route("/users/:id/profile", () => <div>User Profile Page</div>);
807
+ root.route("/users/:id/posts", () => <div>User Posts Page</div>);
808
+
809
+ // About nested
810
+ root.route("/about", About, (about) => {
721
811
  about.route("team", Team);
722
812
  });
723
813
 
724
- // root.route("about/team", Team);
725
- root.route("contact", Contact);
814
+ root.route("/contact", Contact);
815
+
816
+ // wildcard fallback
726
817
  root.route("*", NotFound);
727
818
  });
728
819
 
820
+ // Blog route with loader
729
821
  route("/blog/:slug", Blog, {
730
822
  loader: async ({ params }) => {
731
823
  await new Promise((r) => setTimeout(r, 200));
@@ -733,6 +825,7 @@ function setupRoutes() {
733
825
  },
734
826
  });
735
827
 
828
+ // Search route
736
829
  route("/search", Search);
737
830
  route("/search/:term", Search);
738
831
  }
@@ -745,136 +838,15 @@ setupRoutes();
745
838
  export default function App() {
746
839
  return (
747
840
  <div>
748
- <h1>Multiple Routes Demo</h1>
749
- <Navbar />
750
- <RouterProvider />
841
+ <h1>Full RouteXiz Demo</h1>
842
+ <Suspense fallback={<div>Loading...</div>}>
843
+ <RouterProvider />
844
+ </Suspense>
751
845
  </div>
752
- )
753
- }
754
- ```
755
-
756
- ---
757
-
758
- # Data Preloading (SSR / Advanced)
759
-
760
- `routexiz` provides loadRouteData() to preload all route loaders before rendering.
761
-
762
- This is useful for:
763
- - SSR (Server-Side Rendering)
764
- - Prefetching with full data
765
- - Avoiding loading flicker
766
-
767
- ## 1️⃣ Basic Usage
768
- ```ts
769
- import { loadRouteData } from "routexiz"
770
-
771
- const data = await loadRouteData("/users/1")
772
- ```
773
-
774
- ## 2️⃣ Example Route
775
-
776
- ```ts
777
- route("/users/:id", User, {
778
- loader: async ({ params }) => {
779
- await new Promise(r => setTimeout(r, 300))
780
- return { id: params.id, name: "User " + params.id }
781
- }
782
- })
783
- ```
784
-
785
- ## 3️⃣ Access Loaded Data
786
-
787
- `loadRouteData` returns a map of resources (Suspense-based):
788
-
789
- ```ts
790
- const data = await loadRouteData("/users/1")
791
-
792
- const user = data["/users/:id"].get()
793
-
794
- console.log(user)
795
- // { id: "1", name: "User 1" }
796
- ```
797
-
798
- ## 4️⃣ SSR Example
799
- ```ts
800
- import { loadRouteData } from "routexiz"
801
-
802
- async function render(url: string) {
803
- const data = await loadRouteData(url)
804
-
805
- const user = data["/users/:id"]?.get()
806
-
807
- return `
808
- <html>
809
- <body>
810
- <h1>${user?.name}</h1>
811
- </body>
812
- </html>
813
- `
846
+ );
814
847
  }
815
-
816
- render("/users/1")
817
- ```
818
-
819
- ## 5️⃣ Nested Routes Example
820
- ```ts
821
- route("/", Layout, root => {
822
- root.route("/dashboard", Dashboard, dash => {
823
- dash.route("/users/:id", User, {
824
- loader: async ({ params }) => {
825
- return { id: params.id }
826
- }
827
- })
828
- })
829
- })
830
-
831
- ```
832
-
833
- ```ts
834
- const data = await loadRouteData("/dashboard/users/1")
835
-
836
- const user = data["/dashboard/users/:id"].get()
837
848
  ```
838
849
 
839
- ## 6️⃣ Notes
840
-
841
- - Each route loader returns a resource (Suspense-based)
842
-
843
- - Use .get() to read data safely
844
-
845
- - .read() is used internally to trigger loading
846
-
847
- ## 7️⃣ Difference vs prefetch
848
-
849
- | Function | Behavior |
850
- | ----------------- | ------------------------------ |
851
- | `prefetch()` | Trigger loading (non-blocking) |
852
- | `loadRouteData()` | Await full data (blocking) |
853
-
854
- ## When to use
855
-
856
- - ✅ SSR (recommended)
857
- - ✅ Full preloading before navigation
858
- - ✅ Testing / debugging loaders
859
-
860
- ---
861
-
862
- # Comparison
863
-
864
- `routexiz` focuses on **modern React routing** with Suspense-first `data loading`, `nested routes`, `guards`, `middleware`, and `prefetch/caching`.
865
- It’s lightweight and flexible, designed for client-side SPAs.
866
-
867
-
868
- | Criteria | routexiz | React Router | TanStack Router | Remix |
869
- | ------------------------- | -------- | ------------ | --------------- | ----- |
870
- | Nested routes builder API | ✅ | ✅ | ✅ | ✅ |
871
- | Suspense-first loaders | ✅ | ⚠️ | ✅ | ✅ |
872
- | Guards & middleware | ✅ | ⚠️ | ✅ | ⚠️ |
873
- | Prefetch / caching | ✅ | ❌ | ✅ | ⚠️ |
874
- | Error boundary per route | ✅ | ⚠️ | ✅ | ✅ |
875
- | Transition support | ✅ | ❌ | ⚠️ | ⚠️ |
876
- | Lightweight & minimal | ✅ | ⚠️ | ⚠️ | ⚠️ |
877
-
878
850
  ---
879
851
 
880
852
  # Architecture
@@ -1 +1 @@
1
- import n,{createContext as t,useContext as e,useState as r,useRef as o,useEffect as a,Suspense as i,forwardRef as s}from"react";import{jsx as c,Fragment as u,jsxs as l}from"react/jsx-runtime";import h from"joinclass";const d=new Map;function f(n=1/0){const t=Date.now();let e=0;for(const[r,o]of d.entries())if(o.expiry<=t&&(d.delete(r),e++,e>=n))break}function p(n,t,e,r){const o=Date.now(),a=d.get(n);if(a&&!r&&(!e||a.expiry>o))return a.resource;const i=m(t());return d.set(n,{resource:i,expiry:o+(e??0)}),i}function m(n,t){let e=t?"success":"pending",r=t??null;const o=n.then(n=>{e="success",r=n},n=>{e="error",r=n});return{read(){if("pending"===e)throw o;if("error"===e)throw r;return r},update(n){r=n,e="success"},get:()=>r}}function y(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function g(n){return y(n).split("/").filter(Boolean)}function w(n){const[t,e]=n.split("#"),[r,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,e]=n.split("=");if(!t)return;const r=decodeURIComponent(t),o=decodeURIComponent(e??"");void 0!==a[r]?Array.isArray(a[r])?a[r].push(o):a[r]=[a[r],o]:a[r]=o}),{path:y(r),query:a,hash:e??""}}function v(n,t){const e=n[t];return Array.isArray(e)?e[0]:e}function P(n){const t={};if(!n)return t;if("string"==typeof n){const e=new URLSearchParams(n);e.forEach((n,r)=>{const o=e.getAll(r);t[r]=o.length>1?o:o[0]})}else for(const e in n){const r=n[e];Array.isArray(r)?t[e]=r.map(n=>String(n)):null!=r&&(t[e]=String(r))}return t}function b(n){const t=g(n);let e=10*t.length;for(const n of t)"*"===n?e+=0:n.startsWith(":")?e+=1:e+=5;return e}function q(n){return[...n].sort((n,t)=>b(t.path)-b(n.path))}function x(n,t){const{path:e,query:r,hash:o}=w(t),a=g(e),i=q(n).map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;i.length;){const{node:n,segIndex:t,params:e,chain:s}=i.pop(),c=g(n.path),u={...e};let l=0;for(;l<c.length;l++){const n=c[l],e=a[t+l];if(!e)break;if("*"===n){u["*"]=a.slice(t+l).join("/"),l=c.length;break}if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(l!==c.length)continue;const h=t+l,d=[...s,{node:n,params:u,query:r,hash:o}];if(h===a.length)return d;const f=q(n.children||[]);for(let n=f.length-1;n>=0;n--){const t=g(f[n].path).some(n=>n.startsWith(":"));i.push({node:f[n],segIndex:t?0:h,params:u,chain:d})}}return null}function k(n,t,e,r,o){const{path:a,query:i,hash:s}=w(n);let c=y(a);if(t)for(const n in t)c=c.replace(`:${n}`,encodeURIComponent(t[n]));const u=o?.replaceQuery?{...e??{}}:{...i,...e??{}},l=new URLSearchParams;for(const n in u){const t=u[n];Array.isArray(t)?t.forEach(t=>l.append(n,String(t))):l.append(n,String(t))}const h=l.toString();return h&&(c+=`?${h}`),r?c+=`#${r}`:s&&(c+=`#${s}`),c}function E(n){return JSON.stringify(Object.keys(n||{}).sort().reduce((t,e)=>(t[e]=n[e],t),{}))}function N(n,t){return`${n.path}|${E(t.params)}|${E(t.query)}|${t.hash}`}let S=[];function A(){S=[]}function R(){return S}function K(n){const t={guard:e=>(n.guardFns||(n.guardFns=[]),n.guardFns.push(e),t),middleware:e=>(n.middlewares||(n.middlewares=[]),n.middlewares.push(e),t),meta:e=>(n.meta={...n.meta||{},...e},t),route(t,e,r){const o={path:y(t),component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};return"function"==typeof r?K(o)(r):r&&(o.options=r),n.children.push(o),K(o).api}};return Object.assign(n=>n(t),{api:t})}function C(n,t,e){const r={path:y(n),component:t,children:[],options:{},parent:void 0};return"function"==typeof e?K(r)(e):e&&(r.options=e),S.push(r),K(r).api}const $=t(null),j=t(null),I=t(null),M=t({loading:!1,path:"",pendingPath:""}),F=t({name:"none",stage:"idle"});function L(){const n=e($);try{return n?.read()}catch{return}}function O(){const n=e(j);if(!n)throw new Error("RouteContext not found");return n}function B(){return e(I)??{}}function D(){try{const n=O();if(n?.query)return n.query}catch{}const{query:n}=w(window.location.pathname+window.location.search+window.location.hash);return n}function Q(){return e(M)}function U(){return e(F)}function W(){try{return L()}catch{return}}function T(){const{meta:n,params:t,path:e,query:r}=O();return{data:W(),params:t??{},query:r??{},path:e,meta:n}}const G=200;function J({name:n="fade",stage:t,duration:e=200,children:i}){const[s,d]=r(i),[f,p]=r(null),m=o();return a(()=>{m.current&&clearTimeout(m.current),"exiting"===t&&p(s),"entering"===t&&(d(i),m.current=window.setTimeout(()=>{p(null)},e))},[i,t,e]),"idle"===t?c(u,{children:i}):l("div",{className:h("rtx",`rtx-${n}`),children:[f&&c("div",{className:"rtx-exit",children:f}),c("div",{className:h("rtx-enter",`rtx-${t}`),children:s})]})}let z=!1,H=!1,V=c("div",{children:"Loading..."});const X=({error:n})=>l("div",{children:["Global Error: ",String(n)]});let Y=null;function Z(n,t){z=n,t&&(V=t)}function _(n,t){H=n,Y=t??null}function nn(){return z}function tn(){return H}function en(){return V}function rn(){return Y?({error:n})=>Y(n):X}class on extends n.Component{constructor(n){super(n),this.reset=()=>{this.setState(n=>({hasError:!1,error:null,resetKey:n.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(n){return{hasError:!0,error:n}}render(){if(this.state.hasError){const n=this.props.errorBoundary??(tn()?rn():void 0);return n?c(n,{error:this.state.error,params:this.props.params}):this.props.fallback??l("div",{children:["Error: ",String(this.state.error)]})}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function an({resource:t,errorBoundary:e,children:r,resetKey:o}){const[a,i]=n.useState(null);n.useEffect(()=>{i(null)},[o]);try{return t?.read(),a&&i(null),c(u,{children:r})}catch(n){if(n instanceof Promise)throw n;const t=e??(tn()?rn():void 0);if(t)return c(t,{error:n});throw n}}function sn({chain:n,data:t}){return c(u,{children:function e(r=0){const o=n[r];if(!o)return null;const a=o.node.component;if(!a)return e(r+1);const s={params:o.params,path:o.node.path,query:o.query,hash:o.hash},u=function(n,t){let e=n;for(;e;){const n=e.options?.fallback;if(n)return"function"==typeof n?n(t):n;e=e.parent}return nn()?en():null}(o.node,s),l=Object.assign({},...n.slice(0,r+1).map(n=>n.node.meta)),h=t[N(o.node,s)];return c(on,{fallback:u,errorBoundary:o.node.options?.errorBoundary,params:o.params,children:c(i,{fallback:u,children:c(j.Provider,{value:{path:o.node.path,params:o.params,query:P(o.query),hash:o.hash??"",meta:l},children:c($.Provider,{value:h,children:c(I.Provider,{value:o.params,children:c(an,{resource:h,errorBoundary:o.node.options?.errorBoundary,resetKey:o.params.id||o.params,children:c(a,{children:e(r+1)})})})})})})})}()})}function cn(n,t){const e={};for(const r of n){const n=r.node.options?.loader;if(!n)continue;const o={params:r.params,path:r.node.path,query:r.query,hash:r.hash},a=N(r.node,o),i=r.node.options?.ttl,s=t?.forceReload??r.node.options?.shouldReload?.({prev:void 0,next:o})??!0;e[a]=p(a,()=>Promise.resolve(n(o)),i,s)}return e}let un=null;function ln(){return un}function hn(n,t){const e=R(),[r,o]=n.split("?"),a={...P(o),...t?.query??{}},i=x(e,k(r,t?.params,a,t?.hash));i&&cn(i,{forceReload:!1})}function dn({transition:n,duration:t,wrapper:e}){const[i,s]=r({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:t??G}),u=o(0),l=async(e,r)=>{const o=++u.current,a=function(n){const t=R();return x(t,n)??x(t,"*")??[]}(e),i=await async function(n,t){for(const e of n)for(const n of e.node.guardFns||[]){const r=await n({params:e.params,path:e.node.path,query:e.query,hash:e.hash});if(!1===r)return!1;if("object"==typeof r&&"redirect"in r)return await t(r.redirect),!1}return!0}(a,l);if(!i)return!1;await async function(n){for(const t of n)for(const n of t.node.middlewares||[])await Promise.resolve(n({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(a);const c=function(n,t,e){if(t?.transition)return t.transition;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(e.options?.transition)return e.options.transition;if(e.meta?.transition)return e.meta.transition}return e??"none"}(a,r,n),h=function(n,t,e){if(null!=t?.duration)return t.duration;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(null!=e.options?.duration)return e.options.duration}return null!=e?e:G}(a,r,t);!function(n,t,e,r){n(n=>({...n,loading:!0,pendingPath:t,transition:{name:e,stage:"exiting"},duration:r}))}(s,e,c,h),await new Promise(n=>setTimeout(n,h));const d=r?.shallow?{}:cn(a,r);return o===u.current&&(function(n,t,e,r,o,a){n(n=>({...n,chain:t,data:{...n.data,...e},loading:!1,path:r,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,a,d??{},e,c,h),r?.shallow||window.scrollTo(0,0),!0)};a(()=>{un=l;const n=()=>window.location.pathname+window.location.search+window.location.hash;l(n());const t=()=>l(n());window.addEventListener("popstate",t);const e=()=>{const t=x(R(),n());if(!t)return;const e={};for(const n of t)n.node.options?.revalidateOnFocus&&Object.assign(e,cn([n],{forceReload:!0}));Object.keys(e).length&&s(n=>({...n,data:{...n.data,...e}}))};return window.addEventListener("focus",e),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",e),un=null}},[]);const h=c(sn,{chain:i.chain,data:i.data},i.path);let d=h;if("none"!==i.transition.name&&(d=c(J,{name:i.transition.name,stage:i.transition.stage,duration:i.duration,children:h})),e){d=c(e,{children:d})}return c(M.Provider,{value:{loading:i.loading,path:i.path,pendingPath:i.pendingPath},children:c(F.Provider,{value:i.transition,children:d})})}const fn=[],pn=[];function mn(n){fn.push(n)}function yn(n){pn.push(n)}async function gn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},o=k(n,r.params,r.query,r.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(n){return(await Promise.all(fn.map(t=>t(n)))).every(n=>!1!==n)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=ln();if(!r.shallow&&a){if(!await a(o,r))return console.warn("Navigation blocked by route guard",o),!1}return e?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(n){for(const t of pn)await t(n)}(o),!0}function wn(n,t){return gn(n,t,!1)}function vn(n,t){return wn(n,t)}function Pn(){const n=(n,t)=>gn(n,t);return n.replace=(n,t)=>gn(n,t,!0),n}function bn(n){return Q().path===n}function qn(n,t){return()=>hn(n,t)}function xn({to:n,transition:t,replace:e,params:r,query:i,hash:s,partialMatch:c,disablePrefetch:u,replaceQuery:l}){const h=Pn(),d=Q(),f=o(null),p=k(n,r,i,s,{replaceQuery:l}),m=c?d.path?.startsWith(p):d.path===p;return a(()=>{if(u)return;const n=f.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(hn(p),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[p,u]),{ref:f,fullPath:p,isActive:m,go:async()=>e?h.replace(n,{transition:t,params:r,query:i,hash:s}):h(n,{transition:t,params:r,query:i,hash:s})}}wn.replace=function(n,t){return gn(n,t,!0)};const kn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:o,onKeyDown:a,disablePrefetch:i,...s},u)=>c("a",{ref:u,href:n,...s,onClick:async e=>{if(r&&r(e),e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;e.preventDefault();await t()||console.warn("Navigation blocked:",n)},onMouseEnter:n=>{o&&o(n),i||e?.()},onKeyDown:async e=>{if("Enter"===e.key){e.preventDefault();await t()||console.warn("Navigation blocked:",n)}a&&a(e)}}));function En(n){const{to:t,transition:e,replace:r,activeClassName:o="active",onNavigate:a,partialMatch:i,params:s,query:u,hash:l,className:d,disablePrefetch:f,replaceQuery:p,...m}=n,{ref:y,fullPath:g,isActive:w,go:v}=xn({to:t,transition:e,replace:r,params:s,query:u,hash:l,partialMatch:i,disablePrefetch:f,replaceQuery:p});return c(kn,{ref:y,fullPath:g,onNavigate:async()=>{const n=await v();return a?.(g),n},onPrefetch:()=>hn(g),className:h(d,w&&o),disablePrefetch:f,...m})}function Nn(n){const{to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,className:u,style:l,disablePrefetch:d,replaceQuery:f,...p}=n,{ref:m,fullPath:y,isActive:g,go:w}=xn({to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,disablePrefetch:d,replaceQuery:f}),v="function"==typeof u?u({isActive:g}):u,P="function"==typeof l?l({isActive:g}):l;return c(kn,{ref:m,fullPath:y,onNavigate:async()=>(await w(),!0),onPrefetch:()=>hn(y),className:h(v),style:P,disablePrefetch:d,...p})}kn.displayName="LinkCore";export{G as DEFAULT_DURATION,En as Link,$ as LoaderDataContext,Nn as NavLink,I as ParamsContext,j as RouteContext,dn as RouterProvider,M as RouterStateContext,J as Transition,F as TransitionContext,yn as addAfterNavigationHook,mn as addBeforeNavigationHook,N as buildCacheKey,k as buildPath,f as cleanupCache,A as clearRoutes,m as createResource,rn as getGlobalError,en as getGlobalFallback,ln as getGlobalResolve,v as getQueryFirst,p as getResource,R as getRoutes,tn as getUseGlobalError,nn as getUseGlobalFallback,x as matchRouteChain,wn as navigate,y as normalize,P as normalizeQuery,w as parseQueryHash,hn as prefetch,vn as redirect,C as route,_ as setGlobalError,Z as setGlobalFallback,g as split,xn as useLinkCore,L as useLoaderData,Pn as useNavigate,Q as useNavigation,B as useParams,qn as usePrefetch,D as useQuery,O as useRouteContext,bn as useRouteMatch,T as useRouterData,W as useSafeLoaderData,U as useTransition};
1
+ import n,{createContext as t,useContext as r,useState as e,useRef as o,useEffect as a,Suspense as i,forwardRef as s}from"react";import{jsx as c,Fragment as u,jsxs as h}from"react/jsx-runtime";import l from"joinclass";const f=new Map;function p(n=1/0){const t=Date.now();let r=0;for(const[e,o]of f.entries())if(o.expiry<=t&&(f.delete(e),r++,r>=n))break}function d(n,t,r,e){const o=Date.now(),a=f.get(n);if(a&&!e&&(!r||a.expiry>o))return a.resource;const i=y(t());return f.set(n,{resource:i,expiry:o+(r??0)}),i}function y(n,t){let r=t?"success":"pending",e=t??null;const o=n.then(n=>{r="success",e=n},n=>{r="error",e=n});return{read(){if("pending"===r)throw o;if("error"===r)throw e;return e},update(n){e=n,r="success"},get:()=>e}}function m(n){return(n=n.trim()).startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function g(n){return m(n).split("/").filter(Boolean)}function w(n){const[t,r]=n.split("#"),[e,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,r]=n.split("=");if(!t)return;const e=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[e]?Array.isArray(a[e])?a[e].push(o):a[e]=[a[e],o]:a[e]=o}),{path:m(e),query:a,hash:r??""}}function v(n,t){const r=n[t];return Array.isArray(r)?r[0]:r}function q(n){const t={};if(!n)return t;if("string"==typeof n){const r=new URLSearchParams(n);r.forEach((n,e)=>{const o=r.getAll(e);t[e]=o.length>1?o:o[0]})}else for(const r in n){const e=n[r];Array.isArray(e)?t[r]=e.map(String):null!=e&&(t[r]=String(e))}return t}function P(n){const t=g(n);let r=10*t.length;for(const n of t)"*"===n?r-=100:n.startsWith(":")?r+=1:r+=5;return r}function b(n){return"*"===n}function E(n){const t=[],r=[];for(const e of n)b(e.path)?t.push(e):r.push(e);return r.sort((n,t)=>P(t.path)-P(n.path)),[...r,...t]}function k(n,t){const{path:r,query:e,hash:o}=w(t),a=g(r),i=E(n).reverse().map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;i.length;){const{node:n,segIndex:t,params:r,chain:s}=i.pop(),c=g(n.path),u={...r};let h=0;for(;h<c.length;h++){const n=c[h],r=a[t+h];if(!r)break;if("*"===n){if(h!==c.length-1)break;const n=a.slice(t+h);u["*"]=n.join("/"),u._subPath="/"+n.join("/"),h=c.length;break}if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(r);else if(n!==r)break}if(h!==c.length)continue;const l=t+h,f=[...s,{node:n,params:u,query:e,hash:o}];if(l===a.length&&!b(n.path))return f;const p=E(n.children||[]);if(0===p.length){if(l===a.length)return f;continue}const d=[],y=[];for(const n of p)"*"===n.path?y.push(n):d.push(n);for(let n=y.length-1;n>=0;n--)i.push({node:y[n],segIndex:l,params:u,chain:f});for(let n=d.length-1;n>=0;n--)i.push({node:d[n],segIndex:l,params:u,chain:f})}return null}function x(n,t,r,e,o){const{path:a,query:i,hash:s}=w(n);let c=m(a);if(t)for(const n in t)c=c.replace(`:${n}`,encodeURIComponent(t[n]));const u=o?.replaceQuery?{...r??{}}:{...i,...r??{}},h=new URLSearchParams;for(const n in u){const t=u[n];Array.isArray(t)?t.forEach(t=>h.append(n,String(t))):h.append(n,String(t))}const l=h.toString();return l&&(c+=`?${l}`),e?c+=`#${e}`:s&&(c+=`#${s}`),c}function N(n){return JSON.stringify(Object.keys(n||{}).sort().reduce((t,r)=>(t[r]=n[r],t),{}))}function S(n,t){return`${n.path}|${N(t.params)}|${N(t.query)}|${t.hash}`}function A(n){return(...t)=>Promise.resolve(n(...t))}let L=[];function R(){L=[]}function j(){return L}function K(n){const t={guard:r=>(n.guardFns||(n.guardFns=[]),n.guardFns.push(r),t),middleware:r=>(n.middlewares||(n.middlewares=[]),n.middlewares.push(r),t),meta:r=>(n.meta={...n.meta||{},...r},t),onEnter:r=>(n.onEnter||(n.onEnter=[]),n.onEnter.push(r),t),onLeave:r=>(n.onLeave||(n.onLeave=[]),n.onLeave.push(r),t),onError:r=>(n.onError||(n.onError=[]),n.onError.push(r),t),policy:r=>(n.policies||(n.policies=[]),n.policies.push(r),t),errorBoundary:r=>(n.errorBoundary=r,t),prefetch:r=>(n.prefetch=r,t),route(t,r,e){const o=m(t),a={path:o,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:n};"function"==typeof e?K(a)(e):e&&(a.options=e);const i=b(o)&&function(n){return!n||""===n||"/"===n}(n?.path);if(i){n.children&&(n.children=n.children.filter(n=>!b(n.path)));const t=L.find(n=>b(n.path));return t?(Object.assign(t,{...a,parent:void 0}),K(t).api):(a.parent=void 0,L.push(a),K(a).api)}return n.children.push(a),K(a).api}};return Object.assign(n=>n(t),{api:t})}function C(n,t,r){const e={path:m(n),component:t,children:[],options:{},parent:void 0};if("function"==typeof r?K(e)(r):r&&(e.options=r),b(e.path)){const n=L.find(n=>b(n.path));if(n)return Object.assign(n,e),K(n).api}return L.push(e),K(e).api}const I=t(null),O=t(null),$=t(null),B=t({loading:!1,path:"",pendingPath:""}),M=t({name:"none",stage:"idle"});function F(){const n=r(I);try{return n?.read()}catch{return}}function D(){const n=r(O);if(!n)throw new Error("RouteContext not found");return n}function Q(){return r($)??{}}function U(){try{const n=D();if(n?.query)return n.query}catch{}const{query:n}=w(window.location.pathname+window.location.search+window.location.hash);return n}function W(){return r(B)}function T(){return r(M)}function G(){try{return F()}catch{return}}function J(){const{meta:n,params:t,path:r,query:e}=D();return{data:G(),params:t??{},query:e??{},path:r,meta:n}}const _=200;function z({name:n="fade",stage:t,duration:r=200,children:i}){const[s,f]=e(i),[p,d]=e(null),y=o();return a(()=>{y.current&&clearTimeout(y.current),"exiting"===t&&d(s),"entering"===t&&(f(i),y.current=window.setTimeout(()=>{d(null)},r))},[i,t,r]),"idle"===t?c(u,{children:i}):h("div",{className:l("rtx",`rtx-${n}`),children:[p&&c("div",{className:"rtx-exit",children:p}),c("div",{className:l("rtx-enter",`rtx-${t}`),children:s})]})}let H=!1,V=!1,X=c("div",{children:"Loading..."});const Y=({error:n})=>h("div",{children:["Global Error: ",String(n)]});let Z=null;function nn(n,t){H=n,t&&(X=t)}function tn(n,t){V=n,Z=t??null}function rn(){return H}function en(){return V}function on(){return X}function an(){return Z?({error:n})=>Z(n):Y}class sn 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??(en()?an():void 0);return n?c(n,{error:this.state.error,params:this.props.params}):this.props.fallback??h("div",{children:["Error: ",String(this.state.error)]})}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function cn({resource:t,errorBoundary:r,children:e,resetKey:o}){const[a,i]=n.useState(null);n.useEffect(()=>{i(null)},[o]);try{return t?.read(),a&&i(null),c(u,{children:e})}catch(n){if(n instanceof Promise)throw n;const t=r??(en()?an():void 0);if(t)return c(t,{error:n});throw n}}function un({chain:n,data:t}){return c(u,{children:function r(e=0){const o=n[e];if(!o)return null;const a=o.node.component;if(!a)return r(e+1);const s={params:o.params,path:o.node.path,query:o.query,hash:o.hash},u=function(n,t){let r=n;for(;r;){const n=r.options?.fallback;if(n)return"function"==typeof n?n(t):n;r=r.parent}return rn()?on():null}(o.node,s),h=Object.assign({},...n.slice(0,e+1).map(n=>n.node.meta)),l=t[S(o.node,s)];return c(sn,{fallback:u,errorBoundary:o.node.options?.errorBoundary,params:o.params,children:c(i,{fallback:u,children:c(O.Provider,{value:{path:o.node.path,params:o.params,query:q(o.query),hash:o.hash??"",meta:h},children:c(I.Provider,{value:l,children:c($.Provider,{value:o.params,children:c(cn,{resource:l,errorBoundary:o.node.options?.errorBoundary,resetKey:o.params.id||o.params,children:c(a,{children:r(e+1)})})})})})})})}()})}function hn(n,t){const r={};for(const e of n){const n=e.node.options?.loader;if(!n)continue;const o={params:e.params,path:e.node.path,query:e.query,hash:e.hash},a=S(e.node,o),i=e.node.options?.ttl,s=t?.forceReload??e.node.options?.shouldReload?.({prev:void 0,next:o})??!0;r[a]=d(a,()=>Promise.resolve(n(o)),i,s)}return r}let ln=null;function fn(){return ln}function pn(n,t){const r=j(),[e,o]=n.split("?"),a={...q(o),...t?.query??{}},i=k(r,x(e,t?.params,a,t?.hash));if(i){for(const n of i)n.node.prefetch&&n.node.prefetch({params:n.params,path:n.node.path,query:n.query,hash:n.hash});hn(i,{forceReload:!1})}}function dn({transition:n,duration:t,wrapper:r}){const[i,s]=e({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:t??_}),u=o(0),h=async(r,e)=>{const o=++u.current,a=i.chain,c=function(n){const t=j();return k(t,n)??k(t,"*")??[]}(r);try{await async function(n){for(let t=n.length-1;t>=0;t--){const r=n[t];for(const n of r.node.onLeave||[])await n({params:r.params,path:r.node.path,query:r.query,hash:r.hash})}}(a);const i=await async function(n,t){for(const r of n)for(const n of r.node.policies||[]){const e=await n({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===e)return!1;if("string"==typeof e)return t(e),!1}return!0}(c,h);if(!i)return!1;const l=await async function(n,t){for(const r of n)for(const n of r.node.guardFns||[]){const e=await n({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===e)return!1;if("object"==typeof e&&"redirect"in e)return await t(e.redirect),!1}return!0}(c,h);if(!l)return!1;await async function(n){for(const t of n)for(const n of t.node.middlewares||[])await Promise.resolve(n({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(c);const f=function(n,t,r){if(t?.transition)return t.transition;for(let t=n.length-1;t>=0;t--){const r=n[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(c,e,n),p=function(n,t,r){if(null!=t?.duration)return t.duration;for(let t=n.length-1;t>=0;t--){const r=n[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:_}(c,e,t);!function(n,t,r,e){n(n=>({...n,loading:!0,pendingPath:t,transition:{name:r,stage:"exiting"},duration:e}))}(s,r,f,p),await new Promise(n=>setTimeout(n,p)),await async function(n){for(const t of n)for(const n of t.node.onEnter||[])await n({params:t.params,path:t.node.path,query:t.query,hash:t.hash})}(c);const d=e?.shallow?{}:hn(c,e);return o===u.current&&(function(n,t,r,e,o,a){n(n=>({...n,chain:t,data:{...n.data,...r},loading:!1,path:e,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,c,d??{},r,f,p),e?.shallow||window.scrollTo(0,0),!0)}catch(n){return await async function(n,t){for(let r=t.length-1;r>=0;r--){const e=t[r];for(const t of e.node.onError||[])try{await t(n,{params:e.params,path:e.node.path,query:e.query,hash:e.hash})}catch(n){console.error("Error in onError hook",n)}}}(n,c),!1}};a(()=>{ln=h;const n=()=>window.location.pathname+window.location.search+window.location.hash;h(n());const t=()=>h(n());window.addEventListener("popstate",t);const r=()=>{const t=k(j(),n());if(!t)return;const r={};for(const n of t)n.node.options?.revalidateOnFocus&&Object.assign(r,hn([n],{forceReload:!0}));Object.keys(r).length&&s(n=>({...n,data:{...n.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r),ln=null}},[]);const l=c(un,{chain:i.chain,data:i.data},i.path);let f=l;if("none"!==i.transition.name&&(f=c(z,{name:i.transition.name,stage:i.transition.stage,duration:i.duration,children:l})),r){f=c(r,{children:f})}return c(B.Provider,{value:{loading:i.loading,path:i.path,pendingPath:i.pendingPath},children:c(M.Provider,{value:i.transition,children:f})})}const yn=[],mn=[];function gn(n){yn.push(n)}function wn(n){mn.push(n)}async function vn(n,t,r=!1){const e={...t,forceReload:t?.forceReload??!0},o=x(n,e.params,e.query,e.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(n){return(await Promise.all(yn.map(t=>t(n)))).every(n=>!1!==n)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=fn();if(!e.shallow&&a){if(!await a(o,e))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(n){for(const t of mn)await t(n)}(o),!0}function qn(n,t){return vn(n,t,!1)}function Pn(n,t){return qn(n,t)}function bn(){const n=(n,t)=>vn(n,t);return n.replace=(n,t)=>vn(n,t,!0),n}function En(n){return W().path===n}function kn(n,t){return()=>pn(n,t)}function xn({to:n,transition:t,replace:r,params:e,query:i,hash:s,partialMatch:c,disablePrefetch:u,replaceQuery:h}){const l=bn(),f=W(),p=o(null),d=x(n,e,i,s,{replaceQuery:h}),y=c?f.path?.startsWith(d):f.path===d;return a(()=>{if(u)return;const n=p.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(pn(d),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[d,u]),{ref:p,fullPath:d,isActive:y,go:async()=>r?l.replace(n,{transition:t,params:e,query:i,hash:s}):l(n,{transition:t,params:e,query:i,hash:s})}}qn.replace=function(n,t){return vn(n,t,!0)};const Nn=s(({fullPath:n,onNavigate:t,onPrefetch:r,onClick:e,onMouseEnter:o,onKeyDown:a,disablePrefetch:i,...s},u)=>c("a",{ref:u,href:n,...s,onClick:async r=>{if(e&&e(r),r.metaKey||r.ctrlKey||r.shiftKey||r.altKey)return;r.preventDefault();await t()||console.warn("Navigation blocked:",n)},onMouseEnter:n=>{o&&o(n),i||r?.()},onKeyDown:async r=>{if("Enter"===r.key){r.preventDefault();await t()||console.warn("Navigation blocked:",n)}a&&a(r)}}));function Sn(n){const{to:t,transition:r,replace:e,activeClassName:o="active",onNavigate:a,partialMatch:i,params:s,query:u,hash:h,className:f,disablePrefetch:p,replaceQuery:d,...y}=n,{ref:m,fullPath:g,isActive:w,go:v}=xn({to:t,transition:r,replace:e,params:s,query:u,hash:h,partialMatch:i,disablePrefetch:p,replaceQuery:d});return c(Nn,{ref:m,fullPath:g,onNavigate:async()=>{const n=await v();return a?.(g),n},onPrefetch:()=>pn(g),className:l(f,w&&o),disablePrefetch:p,...y})}function An(n){const{to:t,transition:r,replace:e,params:o,query:a,hash:i,partialMatch:s,className:u,style:h,disablePrefetch:f,replaceQuery:p,...d}=n,{ref:y,fullPath:m,isActive:g,go:w}=xn({to:t,transition:r,replace:e,params:o,query:a,hash:i,partialMatch:s,disablePrefetch:f,replaceQuery:p}),v="function"==typeof u?u({isActive:g}):u,q="function"==typeof h?h({isActive:g}):h;return c(Nn,{ref:y,fullPath:m,onNavigate:async()=>(await w(),!0),onPrefetch:()=>pn(m),className:l(v),style:q,disablePrefetch:f,...d})}Nn.displayName="LinkCore";export{_ as DEFAULT_DURATION,Sn as Link,I as LoaderDataContext,An as NavLink,$ as ParamsContext,O as RouteContext,dn as RouterProvider,B as RouterStateContext,z as Transition,M as TransitionContext,wn as addAfterNavigationHook,gn as addBeforeNavigationHook,S as buildCacheKey,x as buildPath,p as cleanupCache,R as clearRoutes,y as createResource,an as getGlobalError,on as getGlobalFallback,fn as getGlobalResolve,v as getQueryFirst,d as getResource,j as getRoutes,en as getUseGlobalError,rn as getUseGlobalFallback,b as isWildcard,k as matchRouteChain,qn as navigate,m as normalize,q as normalizeQuery,w as parseQueryHash,pn as prefetch,Pn as redirect,C as route,tn as setGlobalError,nn as setGlobalFallback,g as split,xn as useLinkCore,F as useLoaderData,bn as useNavigate,W as useNavigation,Q as useParams,kn as usePrefetch,U as useQuery,D as useRouteContext,En as useRouteMatch,J as useRouterData,G as useSafeLoaderData,T as useTransition,A as wrapAsync};
package/build/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");const n=new Map;function o(e,t,r,o){const s=Date.now(),i=n.get(e);if(i&&!o&&(!r||i.expiry>s))return i.resource;const c=a(t());return n.set(e,{resource:c,expiry:s+(r??0)}),c}function a(e,t){let r=t?"success":"pending",n=t??null;const o=e.then(e=>{r="success",n=e},e=>{r="error",n=e});return{read(){if("pending"===r)throw o;if("error"===r)throw n;return n},update(e){n=e,r="success"},get:()=>n}}function s(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function i(e){return s(e).split("/").filter(Boolean)}function c(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");if(!t)return;const n=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[n]?Array.isArray(a[n])?a[n].push(o):a[n]=[a[n],o]:a[n]=o}),{path:s(n),query:a,hash:r??""}}function u(e){const t={};if(!e)return t;if("string"==typeof e){const r=new URLSearchParams(e);r.forEach((e,n)=>{const o=r.getAll(n);t[n]=o.length>1?o:o[0]})}else for(const r in e){const n=e[r];Array.isArray(n)?t[r]=n.map(e=>String(e)):null!=n&&(t[r]=String(n))}return t}function l(e){const t=i(e);let r=10*t.length;for(const e of t)"*"===e?r+=0:e.startsWith(":")?r+=1:r+=5;return r}function p(e){return[...e].sort((e,t)=>l(t.path)-l(e.path))}function h(e,t){const{path:r,query:n,hash:o}=c(t),a=i(r),s=p(e).map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:c}=s.pop(),u=i(e.path),l={...r};let h=0;for(;h<u.length;h++){const e=u[h],r=a[t+h];if(!r)break;if("*"===e){l["*"]=a.slice(t+h).join("/"),h=u.length;break}if(e.startsWith(":"))l[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(h!==u.length)continue;const d=t+h,f=[...c,{node:e,params:l,query:n,hash:o}];if(d===a.length)return f;const x=p(e.children||[]);for(let e=x.length-1;e>=0;e--){const t=i(x[e].path).some(e=>e.startsWith(":"));s.push({node:x[e],segIndex:t?0:d,params:l,chain:f})}}return null}function d(e,t,r,n,o){const{path:a,query:i,hash:u}=c(e);let l=s(a);if(t)for(const e in t)l=l.replace(`:${e}`,encodeURIComponent(t[e]));const p=o?.replaceQuery?{...r??{}}:{...i,...r??{}},h=new URLSearchParams;for(const e in p){const t=p[e];Array.isArray(t)?t.forEach(t=>h.append(e,String(t))):h.append(e,String(t))}const d=h.toString();return d&&(l+=`?${d}`),n?l+=`#${n}`:u&&(l+=`#${u}`),l}function f(e){return JSON.stringify(Object.keys(e||{}).sort().reduce((t,r)=>(t[r]=e[r],t),{}))}function x(e,t){return`${e.path}|${f(t.params)}|${f(t.query)}|${t.hash}`}let m=[];function y(){return m}function g(e){const t={guard:r=>(e.guardFns||(e.guardFns=[]),e.guardFns.push(r),t),middleware:r=>(e.middlewares||(e.middlewares=[]),e.middlewares.push(r),t),meta:r=>(e.meta={...e.meta||{},...r},t),route(t,r,n){const o={path:s(t),component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};return"function"==typeof n?g(o)(n):n&&(o.options=n),e.children.push(o),g(o).api}};return Object.assign(e=>e(t),{api:t})}const w=e.createContext(null),v=e.createContext(null),b=e.createContext(null),P=e.createContext({loading:!1,path:"",pendingPath:""}),j=e.createContext({name:"none",stage:"idle"});function q(){const t=e.useContext(w);try{return t?.read()}catch{return}}function C(){const t=e.useContext(v);if(!t)throw new Error("RouteContext not found");return t}function R(){return e.useContext(P)}function k(){try{return q()}catch{return}}const E=200;function N({name:n="fade",stage:o,duration:a=200,children:s}){const[i,c]=e.useState(s),[u,l]=e.useState(null),p=e.useRef();return e.useEffect(()=>{p.current&&clearTimeout(p.current),"exiting"===o&&l(i),"entering"===o&&(c(s),p.current=window.setTimeout(()=>{l(null)},a))},[s,o,a]),"idle"===o?t.jsx(t.Fragment,{children:s}):t.jsxs("div",{className:r("rtx",`rtx-${n}`),children:[u&&t.jsx("div",{className:"rtx-exit",children:u}),t.jsx("div",{className:r("rtx-enter",`rtx-${o}`),children:i})]})}let S=!1,A=!1,F=t.jsx("div",{children:"Loading..."});const L=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});let D=null;function K(){return S}function Q(){return A}function I(){return F}function M(){return D?({error:e})=>D(e):L}class U extends e.Component{constructor(e){super(e),this.reset=()=>{this.setState(e=>({hasError:!1,error:null,resetKey:e.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}render(){if(this.state.hasError){const e=this.props.errorBoundary??(Q()?M():void 0);return e?t.jsx(e,{error:this.state.error,params:this.props.params}):this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]})}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function $({resource:r,errorBoundary:n,children:o,resetKey:a}){const[s,i]=e.useState(null);e.useEffect(()=>{i(null)},[a]);try{return r?.read(),s&&i(null),t.jsx(t.Fragment,{children:o})}catch(e){if(e instanceof Promise)throw e;const r=n??(Q()?M():void 0);if(r)return t.jsx(r,{error:e});throw e}}function O({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const s=r[a];if(!s)return null;const i=s.node.component;if(!i)return o(a+1);const c={params:s.params,path:s.node.path,query:s.query,hash:s.hash},l=function(e,t){let r=e;for(;r;){const e=r.options?.fallback;if(e)return"function"==typeof e?e(t):e;r=r.parent}return K()?I():null}(s.node,c),p=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta)),h=n[x(s.node,c)];return t.jsx(U,{fallback:l,errorBoundary:s.node.options?.errorBoundary,params:s.params,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(v.Provider,{value:{path:s.node.path,params:s.params,query:u(s.query),hash:s.hash??"",meta:p},children:t.jsx(w.Provider,{value:h,children:t.jsx(b.Provider,{value:s.params,children:t.jsx($,{resource:h,errorBoundary:s.node.options?.errorBoundary,resetKey:s.params.id||s.params,children:t.jsx(i,{children:o(a+1)})})})})})})})}()})}function T(e,t){const r={};for(const n of e){const e=n.node.options?.loader;if(!e)continue;const a={params:n.params,path:n.node.path,query:n.query,hash:n.hash},s=x(n.node,a),i=n.node.options?.ttl,c=t?.forceReload??n.node.options?.shouldReload?.({prev:void 0,next:a})??!0;r[s]=o(s,()=>Promise.resolve(e(a)),i,c)}return r}let B=null;function G(){return B}function W(e,t){const r=y(),[n,o]=e.split("?"),a={...u(o),...t?.query??{}},s=h(r,d(n,t?.params,a,t?.hash));s&&T(s,{forceReload:!1})}const H=[],z=[];async function J(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=d(e,n.params,n.query,n.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(e){return(await Promise.all(H.map(t=>t(e)))).every(e=>!1!==e)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=G();if(!n.shallow&&a){if(!await a(o,n))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(e){for(const t of z)await t(e)}(o),!0}function _(e,t){return J(e,t,!1)}function V(){const e=(e,t)=>J(e,t);return e.replace=(e,t)=>J(e,t,!0),e}function X({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c,replaceQuery:u}){const l=V(),p=R(),h=e.useRef(null),f=d(t,o,a,s,{replaceQuery:u}),x=i?p.path?.startsWith(f):p.path===f;return e.useEffect(()=>{if(c)return;const e=h.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(W(f),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[f,c]),{ref:h,fullPath:f,isActive:x,go:async()=>n?l.replace(t,{transition:r,params:o,query:a,hash:s}):l(t,{transition:r,params:o,query:a,hash:s})}}_.replace=function(e,t){return J(e,t,!0)};const Y=e.forwardRef(({fullPath:e,onNavigate:r,onPrefetch:n,onClick:o,onMouseEnter:a,onKeyDown:s,disablePrefetch:i,...c},u)=>t.jsx("a",{ref:u,href:e,...c,onClick:async t=>{if(o&&o(t),t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;t.preventDefault();await r()||console.warn("Navigation blocked:",e)},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:async t=>{if("Enter"===t.key){t.preventDefault();await r()||console.warn("Navigation blocked:",e)}s&&s(t)}}));Y.displayName="LinkCore",exports.DEFAULT_DURATION=E,exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:l,hash:p,className:h,disablePrefetch:d,replaceQuery:f,...x}=e,{ref:m,fullPath:y,isActive:g,go:w}=X({to:n,transition:o,replace:a,params:u,query:l,hash:p,partialMatch:c,disablePrefetch:d,replaceQuery:f});return t.jsx(Y,{ref:m,fullPath:y,onNavigate:async()=>{const e=await w();return i?.(y),e},onPrefetch:()=>W(y),className:r(h,g&&s),disablePrefetch:d,...x})},exports.LoaderDataContext=w,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:l,style:p,disablePrefetch:h,replaceQuery:d,...f}=e,{ref:x,fullPath:m,isActive:y,go:g}=X({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:h,replaceQuery:d}),w="function"==typeof l?l({isActive:y}):l,v="function"==typeof p?p({isActive:y}):p;return t.jsx(Y,{ref:x,fullPath:m,onNavigate:async()=>(await g(),!0),onPrefetch:()=>W(m),className:r(w),style:v,disablePrefetch:h,...f})},exports.ParamsContext=b,exports.RouteContext=v,exports.RouterProvider=function({transition:r,duration:n,wrapper:o}){const[a,s]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:n??E}),i=e.useRef(0),c=async(e,t)=>{const o=++i.current,a=function(e){const t=y();return h(t,e)??h(t,"*")??[]}(e),u=await async function(e,t){for(const r of e)for(const e of r.node.guardFns||[]){const n=await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===n)return!1;if("object"==typeof n&&"redirect"in n)return await t(n.redirect),!1}return!0}(a,c);if(!u)return!1;await async function(e){for(const t of e)for(const e of t.node.middlewares||[])await Promise.resolve(e({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(a);const l=function(e,t,r){if(t?.transition)return t.transition;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(a,t,r),p=function(e,t,r){if(null!=t?.duration)return t.duration;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:E}(a,t,n);!function(e,t,r,n){e(e=>({...e,loading:!0,pendingPath:t,transition:{name:r,stage:"exiting"},duration:n}))}(s,e,l,p),await new Promise(e=>setTimeout(e,p));const d=t?.shallow?{}:T(a,t);return o===i.current&&(function(e,t,r,n,o,a){e(e=>({...e,chain:t,data:{...e.data,...r},loading:!1,path:n,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,a,d??{},e,l,p),t?.shallow||window.scrollTo(0,0),!0)};e.useEffect(()=>{B=c;const e=()=>window.location.pathname+window.location.search+window.location.hash;c(e());const t=()=>c(e());window.addEventListener("popstate",t);const r=()=>{const t=h(y(),e());if(!t)return;const r={};for(const e of t)e.node.options?.revalidateOnFocus&&Object.assign(r,T([e],{forceReload:!0}));Object.keys(r).length&&s(e=>({...e,data:{...e.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r),B=null}},[]);const u=t.jsx(O,{chain:a.chain,data:a.data},a.path);let l=u;if("none"!==a.transition.name&&(l=t.jsx(N,{name:a.transition.name,stage:a.transition.stage,duration:a.duration,children:u})),o){const e=o;l=t.jsx(e,{children:l})}return t.jsx(P.Provider,{value:{loading:a.loading,path:a.path,pendingPath:a.pendingPath},children:t.jsx(j.Provider,{value:a.transition,children:l})})},exports.RouterStateContext=P,exports.Transition=N,exports.TransitionContext=j,exports.addAfterNavigationHook=function(e){z.push(e)},exports.addBeforeNavigationHook=function(e){H.push(e)},exports.buildCacheKey=x,exports.buildPath=d,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[o,a]of n.entries())if(a.expiry<=t&&(n.delete(o),r++,r>=e))break},exports.clearRoutes=function(){m=[]},exports.createResource=a,exports.getGlobalError=M,exports.getGlobalFallback=I,exports.getGlobalResolve=G,exports.getQueryFirst=function(e,t){const r=e[t];return Array.isArray(r)?r[0]:r},exports.getResource=o,exports.getRoutes=y,exports.getUseGlobalError=Q,exports.getUseGlobalFallback=K,exports.matchRouteChain=h,exports.navigate=_,exports.normalize=s,exports.normalizeQuery=u,exports.parseQueryHash=c,exports.prefetch=W,exports.redirect=function(e,t){return _(e,t)},exports.route=function(e,t,r){const n={path:s(e),component:t,children:[],options:{},parent:void 0};return"function"==typeof r?g(n)(r):r&&(n.options=r),m.push(n),g(n).api},exports.setGlobalError=function(e,t){A=e,D=t??null},exports.setGlobalFallback=function(e,t){S=e,t&&(F=t)},exports.split=i,exports.useLinkCore=X,exports.useLoaderData=q,exports.useNavigate=V,exports.useNavigation=R,exports.useParams=function(){return e.useContext(b)??{}},exports.usePrefetch=function(e,t){return()=>W(e,t)},exports.useQuery=function(){try{const e=C();if(e?.query)return e.query}catch{}const{query:e}=c(window.location.pathname+window.location.search+window.location.hash);return e},exports.useRouteContext=C,exports.useRouteMatch=function(e){return R().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=C();return{data:k(),params:t??{},query:n??{},path:r,meta:e}},exports.useSafeLoaderData=k,exports.useTransition=function(){return e.useContext(j)};
1
+ "use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");const n=new Map;function o(e,t,r,o){const s=Date.now(),i=n.get(e);if(i&&!o&&(!r||i.expiry>s))return i.resource;const c=a(t());return n.set(e,{resource:c,expiry:s+(r??0)}),c}function a(e,t){let r=t?"success":"pending",n=t??null;const o=e.then(e=>{r="success",n=e},e=>{r="error",n=e});return{read(){if("pending"===r)throw o;if("error"===r)throw n;return n},update(e){n=e,r="success"},get:()=>n}}function s(e){return(e=e.trim()).startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function i(e){return s(e).split("/").filter(Boolean)}function c(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");if(!t)return;const n=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[n]?Array.isArray(a[n])?a[n].push(o):a[n]=[a[n],o]:a[n]=o}),{path:s(n),query:a,hash:r??""}}function u(e){const t={};if(!e)return t;if("string"==typeof e){const r=new URLSearchParams(e);r.forEach((e,n)=>{const o=r.getAll(n);t[n]=o.length>1?o:o[0]})}else for(const r in e){const n=e[r];Array.isArray(n)?t[r]=n.map(String):null!=n&&(t[r]=String(n))}return t}function l(e){const t=i(e);let r=10*t.length;for(const e of t)"*"===e?r-=100:e.startsWith(":")?r+=1:r+=5;return r}function h(e){return"*"===e}function p(e){const t=[],r=[];for(const n of e)h(n.path)?t.push(n):r.push(n);return r.sort((e,t)=>l(t.path)-l(e.path)),[...r,...t]}function f(e,t){const{path:r,query:n,hash:o}=c(t),a=i(r),s=p(e).reverse().map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:c}=s.pop(),u=i(e.path),l={...r};let f=0;for(;f<u.length;f++){const e=u[f],r=a[t+f];if(!r)break;if("*"===e){if(f!==u.length-1)break;const e=a.slice(t+f);l["*"]=e.join("/"),l._subPath="/"+e.join("/"),f=u.length;break}if(e.startsWith(":"))l[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(f!==u.length)continue;const d=t+f,y=[...c,{node:e,params:l,query:n,hash:o}];if(d===a.length&&!h(e.path))return y;const m=p(e.children||[]);if(0===m.length){if(d===a.length)return y;continue}const x=[],g=[];for(const e of m)"*"===e.path?g.push(e):x.push(e);for(let e=g.length-1;e>=0;e--)s.push({node:g[e],segIndex:d,params:l,chain:y});for(let e=x.length-1;e>=0;e--)s.push({node:x[e],segIndex:d,params:l,chain:y})}return null}function d(e,t,r,n,o){const{path:a,query:i,hash:u}=c(e);let l=s(a);if(t)for(const e in t)l=l.replace(`:${e}`,encodeURIComponent(t[e]));const h=o?.replaceQuery?{...r??{}}:{...i,...r??{}},p=new URLSearchParams;for(const e in h){const t=h[e];Array.isArray(t)?t.forEach(t=>p.append(e,String(t))):p.append(e,String(t))}const f=p.toString();return f&&(l+=`?${f}`),n?l+=`#${n}`:u&&(l+=`#${u}`),l}function y(e){return JSON.stringify(Object.keys(e||{}).sort().reduce((t,r)=>(t[r]=e[r],t),{}))}function m(e,t){return`${e.path}|${y(t.params)}|${y(t.query)}|${t.hash}`}let x=[];function g(){return x}function w(e){const t={guard:r=>(e.guardFns||(e.guardFns=[]),e.guardFns.push(r),t),middleware:r=>(e.middlewares||(e.middlewares=[]),e.middlewares.push(r),t),meta:r=>(e.meta={...e.meta||{},...r},t),onEnter:r=>(e.onEnter||(e.onEnter=[]),e.onEnter.push(r),t),onLeave:r=>(e.onLeave||(e.onLeave=[]),e.onLeave.push(r),t),onError:r=>(e.onError||(e.onError=[]),e.onError.push(r),t),policy:r=>(e.policies||(e.policies=[]),e.policies.push(r),t),errorBoundary:r=>(e.errorBoundary=r,t),prefetch:r=>(e.prefetch=r,t),route(t,r,n){const o=s(t),a={path:o,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};"function"==typeof n?w(a)(n):n&&(a.options=n);const i=h(o)&&function(e){return!e||""===e||"/"===e}(e?.path);if(i){e.children&&(e.children=e.children.filter(e=>!h(e.path)));const t=x.find(e=>h(e.path));return t?(Object.assign(t,{...a,parent:void 0}),w(t).api):(a.parent=void 0,x.push(a),w(a).api)}return e.children.push(a),w(a).api}};return Object.assign(e=>e(t),{api:t})}const v=e.createContext(null),b=e.createContext(null),q=e.createContext(null),P=e.createContext({loading:!1,path:"",pendingPath:""}),j=e.createContext({name:"none",stage:"idle"});function E(){const t=e.useContext(v);try{return t?.read()}catch{return}}function C(){const t=e.useContext(b);if(!t)throw new Error("RouteContext not found");return t}function R(){return e.useContext(P)}function k(){try{return E()}catch{return}}const N=200;function S({name:n="fade",stage:o,duration:a=200,children:s}){const[i,c]=e.useState(s),[u,l]=e.useState(null),h=e.useRef();return e.useEffect(()=>{h.current&&clearTimeout(h.current),"exiting"===o&&l(i),"entering"===o&&(c(s),h.current=window.setTimeout(()=>{l(null)},a))},[s,o,a]),"idle"===o?t.jsx(t.Fragment,{children:s}):t.jsxs("div",{className:r("rtx",`rtx-${n}`),children:[u&&t.jsx("div",{className:"rtx-exit",children:u}),t.jsx("div",{className:r("rtx-enter",`rtx-${o}`),children:i})]})}let L=!1,A=!1,F=t.jsx("div",{children:"Loading..."});const D=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});let K=null;function I(){return L}function O(){return A}function Q(){return F}function B(){return K?({error:e})=>K(e):D}class M 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??(O()?B():void 0);return e?t.jsx(e,{error:this.state.error,params:this.props.params}):this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]})}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function U({resource:r,errorBoundary:n,children:o,resetKey:a}){const[s,i]=e.useState(null);e.useEffect(()=>{i(null)},[a]);try{return r?.read(),s&&i(null),t.jsx(t.Fragment,{children:o})}catch(e){if(e instanceof Promise)throw e;const r=n??(O()?B():void 0);if(r)return t.jsx(r,{error:e});throw e}}function $({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const s=r[a];if(!s)return null;const i=s.node.component;if(!i)return o(a+1);const c={params:s.params,path:s.node.path,query:s.query,hash:s.hash},l=function(e,t){let r=e;for(;r;){const e=r.options?.fallback;if(e)return"function"==typeof e?e(t):e;r=r.parent}return I()?Q():null}(s.node,c),h=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta)),p=n[m(s.node,c)];return t.jsx(M,{fallback:l,errorBoundary:s.node.options?.errorBoundary,params:s.params,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(b.Provider,{value:{path:s.node.path,params:s.params,query:u(s.query),hash:s.hash??"",meta:h},children:t.jsx(v.Provider,{value:p,children:t.jsx(q.Provider,{value:s.params,children:t.jsx(U,{resource:p,errorBoundary:s.node.options?.errorBoundary,resetKey:s.params.id||s.params,children:t.jsx(i,{children:o(a+1)})})})})})})})}()})}function T(e,t){const r={};for(const n of e){const e=n.node.options?.loader;if(!e)continue;const a={params:n.params,path:n.node.path,query:n.query,hash:n.hash},s=m(n.node,a),i=n.node.options?.ttl,c=t?.forceReload??n.node.options?.shouldReload?.({prev:void 0,next:a})??!0;r[s]=o(s,()=>Promise.resolve(e(a)),i,c)}return r}let G=null;function W(){return G}function H(e,t){const r=g(),[n,o]=e.split("?"),a={...u(o),...t?.query??{}},s=f(r,d(n,t?.params,a,t?.hash));if(s){for(const e of s)e.node.prefetch&&e.node.prefetch({params:e.params,path:e.node.path,query:e.query,hash:e.hash});T(s,{forceReload:!1})}}const z=[],_=[];async function J(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=d(e,n.params,n.query,n.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(e){return(await Promise.all(z.map(t=>t(e)))).every(e=>!1!==e)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=W();if(!n.shallow&&a){if(!await a(o,n))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(e){for(const t of _)await t(e)}(o),!0}function V(e,t){return J(e,t,!1)}function X(){const e=(e,t)=>J(e,t);return e.replace=(e,t)=>J(e,t,!0),e}function Y({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c,replaceQuery:u}){const l=X(),h=R(),p=e.useRef(null),f=d(t,o,a,s,{replaceQuery:u}),y=i?h.path?.startsWith(f):h.path===f;return e.useEffect(()=>{if(c)return;const e=p.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(H(f),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[f,c]),{ref:p,fullPath:f,isActive:y,go:async()=>n?l.replace(t,{transition:r,params:o,query:a,hash:s}):l(t,{transition:r,params:o,query:a,hash:s})}}V.replace=function(e,t){return J(e,t,!0)};const Z=e.forwardRef(({fullPath:e,onNavigate:r,onPrefetch:n,onClick:o,onMouseEnter:a,onKeyDown:s,disablePrefetch:i,...c},u)=>t.jsx("a",{ref:u,href:e,...c,onClick:async t=>{if(o&&o(t),t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;t.preventDefault();await r()||console.warn("Navigation blocked:",e)},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:async t=>{if("Enter"===t.key){t.preventDefault();await r()||console.warn("Navigation blocked:",e)}s&&s(t)}}));Z.displayName="LinkCore",exports.DEFAULT_DURATION=N,exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:l,hash:h,className:p,disablePrefetch:f,replaceQuery:d,...y}=e,{ref:m,fullPath:x,isActive:g,go:w}=Y({to:n,transition:o,replace:a,params:u,query:l,hash:h,partialMatch:c,disablePrefetch:f,replaceQuery:d});return t.jsx(Z,{ref:m,fullPath:x,onNavigate:async()=>{const e=await w();return i?.(x),e},onPrefetch:()=>H(x),className:r(p,g&&s),disablePrefetch:f,...y})},exports.LoaderDataContext=v,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:l,style:h,disablePrefetch:p,replaceQuery:f,...d}=e,{ref:y,fullPath:m,isActive:x,go:g}=Y({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:p,replaceQuery:f}),w="function"==typeof l?l({isActive:x}):l,v="function"==typeof h?h({isActive:x}):h;return t.jsx(Z,{ref:y,fullPath:m,onNavigate:async()=>(await g(),!0),onPrefetch:()=>H(m),className:r(w),style:v,disablePrefetch:p,...d})},exports.ParamsContext=q,exports.RouteContext=b,exports.RouterProvider=function({transition:r,duration:n,wrapper:o}){const[a,s]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:n??N}),i=e.useRef(0),c=async(e,t)=>{const o=++i.current,u=a.chain,l=function(e){const t=g();return f(t,e)??f(t,"*")??[]}(e);try{await async function(e){for(let t=e.length-1;t>=0;t--){const r=e[t];for(const e of r.node.onLeave||[])await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash})}}(u);const a=await async function(e,t){for(const r of e)for(const e of r.node.policies||[]){const n=await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===n)return!1;if("string"==typeof n)return t(n),!1}return!0}(l,c);if(!a)return!1;const h=await async function(e,t){for(const r of e)for(const e of r.node.guardFns||[]){const n=await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===n)return!1;if("object"==typeof n&&"redirect"in n)return await t(n.redirect),!1}return!0}(l,c);if(!h)return!1;await async function(e){for(const t of e)for(const e of t.node.middlewares||[])await Promise.resolve(e({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(l);const p=function(e,t,r){if(t?.transition)return t.transition;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(l,t,r),f=function(e,t,r){if(null!=t?.duration)return t.duration;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:N}(l,t,n);!function(e,t,r,n){e(e=>({...e,loading:!0,pendingPath:t,transition:{name:r,stage:"exiting"},duration:n}))}(s,e,p,f),await new Promise(e=>setTimeout(e,f)),await async function(e){for(const t of e)for(const e of t.node.onEnter||[])await e({params:t.params,path:t.node.path,query:t.query,hash:t.hash})}(l);const d=t?.shallow?{}:T(l,t);return o===i.current&&(function(e,t,r,n,o,a){e(e=>({...e,chain:t,data:{...e.data,...r},loading:!1,path:n,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,l,d??{},e,p,f),t?.shallow||window.scrollTo(0,0),!0)}catch(e){return await async function(e,t){for(let r=t.length-1;r>=0;r--){const n=t[r];for(const t of n.node.onError||[])try{await t(e,{params:n.params,path:n.node.path,query:n.query,hash:n.hash})}catch(e){console.error("Error in onError hook",e)}}}(e,l),!1}};e.useEffect(()=>{G=c;const e=()=>window.location.pathname+window.location.search+window.location.hash;c(e());const t=()=>c(e());window.addEventListener("popstate",t);const r=()=>{const t=f(g(),e());if(!t)return;const r={};for(const e of t)e.node.options?.revalidateOnFocus&&Object.assign(r,T([e],{forceReload:!0}));Object.keys(r).length&&s(e=>({...e,data:{...e.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r),G=null}},[]);const u=t.jsx($,{chain:a.chain,data:a.data},a.path);let l=u;if("none"!==a.transition.name&&(l=t.jsx(S,{name:a.transition.name,stage:a.transition.stage,duration:a.duration,children:u})),o){const e=o;l=t.jsx(e,{children:l})}return t.jsx(P.Provider,{value:{loading:a.loading,path:a.path,pendingPath:a.pendingPath},children:t.jsx(j.Provider,{value:a.transition,children:l})})},exports.RouterStateContext=P,exports.Transition=S,exports.TransitionContext=j,exports.addAfterNavigationHook=function(e){_.push(e)},exports.addBeforeNavigationHook=function(e){z.push(e)},exports.buildCacheKey=m,exports.buildPath=d,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[o,a]of n.entries())if(a.expiry<=t&&(n.delete(o),r++,r>=e))break},exports.clearRoutes=function(){x=[]},exports.createResource=a,exports.getGlobalError=B,exports.getGlobalFallback=Q,exports.getGlobalResolve=W,exports.getQueryFirst=function(e,t){const r=e[t];return Array.isArray(r)?r[0]:r},exports.getResource=o,exports.getRoutes=g,exports.getUseGlobalError=O,exports.getUseGlobalFallback=I,exports.isWildcard=h,exports.matchRouteChain=f,exports.navigate=V,exports.normalize=s,exports.normalizeQuery=u,exports.parseQueryHash=c,exports.prefetch=H,exports.redirect=function(e,t){return V(e,t)},exports.route=function(e,t,r){const n={path:s(e),component:t,children:[],options:{},parent:void 0};if("function"==typeof r?w(n)(r):r&&(n.options=r),h(n.path)){const e=x.find(e=>h(e.path));if(e)return Object.assign(e,n),w(e).api}return x.push(n),w(n).api},exports.setGlobalError=function(e,t){A=e,K=t??null},exports.setGlobalFallback=function(e,t){L=e,t&&(F=t)},exports.split=i,exports.useLinkCore=Y,exports.useLoaderData=E,exports.useNavigate=X,exports.useNavigation=R,exports.useParams=function(){return e.useContext(q)??{}},exports.usePrefetch=function(e,t){return()=>H(e,t)},exports.useQuery=function(){try{const e=C();if(e?.query)return e.query}catch{}const{query:e}=c(window.location.pathname+window.location.search+window.location.hash);return e},exports.useRouteContext=C,exports.useRouteMatch=function(e){return R().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=C();return{data:k(),params:t??{},query:n??{},path:r,meta:e}},exports.useSafeLoaderData=k,exports.useTransition=function(){return e.useContext(j)},exports.wrapAsync=function(e){return(...t)=>Promise.resolve(e(...t))};
@@ -1,5 +1,5 @@
1
1
  import type { BuilderRouteAPI, ExtractParams, RouteComponent, RouteNode, RouteOptions } from './types';
2
2
  export declare function clearRoutes(): void;
3
3
  export declare function getRoutes(): RouteNode[];
4
- export declare function route<Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<ExtractParams<Path>>) => void) | RouteOptions<ExtractParams<Path>, R>): BuilderRouteAPI<ExtractParams<Path>>;
4
+ export declare function route<Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<ExtractParams<Path>>) => void) | RouteOptions<ExtractParams<Path>, R>): BuilderRouteAPI<any> | BuilderRouteAPI<ExtractParams<Path>>;
5
5
  export declare function loadRouteData(path: string): Promise<Record<string, any>>;
@@ -10,6 +10,11 @@ export type LoaderContext<P = any> = {
10
10
  hash?: string;
11
11
  };
12
12
  export type LoaderFn<P, R> = (context: LoaderContext<P>) => Promise<R> | R;
13
+ export type OnEnterFn<P = any> = (context: LoaderContext<P>) => void | Promise<void>;
14
+ export type OnLeaveFn<P = any> = (context: LoaderContext<P>) => void | Promise<void>;
15
+ export type OnErrorFn<P = any> = (error: unknown, context: LoaderContext<P>) => void | boolean | Promise<void | boolean>;
16
+ export type PolicyFn<P = any> = (context: LoaderContext<P>) => boolean | Promise<boolean>;
17
+ export type PrefetchFn<P = any> = (context: LoaderContext<P>) => Promise<any>;
13
18
  export type RouteOptions<P = any, R = any> = {
14
19
  loader?: LoaderFn<P, R>;
15
20
  ttl?: number;
@@ -33,12 +38,12 @@ export type RouteOptions<P = any, R = any> = {
33
38
  type GuardResult = boolean | {
34
39
  redirect: string;
35
40
  };
36
- export type GuardFn<P = any> = (ctx: LoaderContext<P>) => GuardResult | Promise<GuardResult>;
41
+ export type GuardFn<P = any> = (context: LoaderContext<P>) => GuardResult | Promise<GuardResult>;
37
42
  export type MiddlewareFn<P = any> = (context: LoaderContext<P>) => void | Promise<void>;
38
- export type RouteComponent = React.ComponentType<{
43
+ export type RouteComponent<P = any> = React.ComponentType<P & {
39
44
  children?: ReactNode;
40
45
  }> | (() => Promise<{
41
- default: React.ComponentType<{
46
+ default: React.ComponentType<P & {
42
47
  children?: ReactNode;
43
48
  }>;
44
49
  }>);
@@ -46,11 +51,17 @@ export type RouteNode<P = any, R = any> = {
46
51
  path: string;
47
52
  component?: RouteComponent;
48
53
  children: RouteNode[];
54
+ parent?: RouteNode;
49
55
  options: RouteOptions<P, R>;
50
56
  guardFns?: GuardFn<P>[];
51
57
  middlewares?: MiddlewareFn<P>[];
52
58
  meta?: Record<string, any>;
53
- parent?: RouteNode;
59
+ onEnter?: OnEnterFn<P>[];
60
+ onLeave?: OnLeaveFn<P>[];
61
+ onError?: OnErrorFn<P>[];
62
+ policies?: PolicyFn<P>[];
63
+ prefetch?: PrefetchFn<P>;
64
+ errorBoundary?: RouteComponent;
54
65
  };
55
66
  export type InternalRouteMatch<P = any> = {
56
67
  node: RouteNode<P>;
@@ -67,6 +78,15 @@ export type BuilderRouteAPI<Params = any> = {
67
78
  guard: (fn: GuardFn<Params>) => BuilderRouteAPI<Params>;
68
79
  middleware: (fn: MiddlewareFn<Params>) => BuilderRouteAPI<Params>;
69
80
  meta: (obj: Record<string, any>) => BuilderRouteAPI<Params>;
81
+ onEnter: (fn: OnEnterFn<Params>) => BuilderRouteAPI<Params>;
82
+ onLeave: (fn: OnLeaveFn<Params>) => BuilderRouteAPI<Params>;
83
+ onError: (fn: OnErrorFn<Params>) => BuilderRouteAPI<Params>;
84
+ policy: (fn: PolicyFn<Params>) => BuilderRouteAPI<Params>;
85
+ prefetch: (fn: PrefetchFn<Params>) => BuilderRouteAPI<Params>;
86
+ errorBoundary: (component: RouteComponent<{
87
+ error: any;
88
+ params?: Params;
89
+ }>) => BuilderRouteAPI<Params>;
70
90
  route: <Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<Params & ExtractParams<Path>>) => void) | RouteOptions<Params & ExtractParams<Path>, R>) => BuilderRouteAPI<Params & ExtractParams<Path>>;
71
91
  };
72
92
  export type InferParams<T> = T extends InternalRouteMatch<infer P> ? P : never;
@@ -1,4 +1,4 @@
1
- import type { Query, RouteNode } from './types';
1
+ import type { InternalRouteMatch, Query, RouteNode } from './types';
2
2
  export declare function normalize(path: string): string;
3
3
  export declare function split(path: string): string[];
4
4
  export declare function parseQueryHash(pathname: string): {
@@ -8,8 +8,10 @@ export declare function parseQueryHash(pathname: string): {
8
8
  };
9
9
  export declare function getQueryFirst(q: Query, key: string): string | number;
10
10
  export declare function normalizeQuery(input?: string | Record<string, any>): Record<string, string | string[]>;
11
- export declare function matchRouteChain(routes: RouteNode[], pathname: string): any[] | null;
11
+ export declare function isWildcard(path: string): path is "*";
12
+ export declare function matchRouteChain(routes: RouteNode[], pathname: string): InternalRouteMatch[] | null;
12
13
  export declare function buildPath(path: string, params?: Record<string, any>, query?: Record<string, any>, hash?: string, options?: {
13
14
  replaceQuery?: boolean;
14
15
  }): string;
15
16
  export declare function buildCacheKey(node: RouteNode, context: any): string;
17
+ export declare function wrapAsync<T extends (...args: any[]) => any>(fn: T): (...args: Parameters<T>) => Promise<any>;
package/build/styles.css CHANGED
@@ -17,11 +17,11 @@
17
17
  FADE
18
18
  ========================= */
19
19
  .rtx-fade .rtx-entering {
20
- animation: rtxFadeIn 0.25s ease forwards;
20
+ animation: rtxFadeIn 0.20s ease forwards;
21
21
  }
22
22
 
23
23
  .rtx-fade .rtx-exit {
24
- animation: rtxFadeOut 0.25s ease forwards;
24
+ animation: rtxFadeOut 0.20s ease forwards;
25
25
  }
26
26
 
27
27
  @keyframes rtxFadeIn {
@@ -38,11 +38,11 @@
38
38
  SLIDE (from right)
39
39
  ========================= */
40
40
  .rtx-slide .rtx-entering {
41
- animation: rtxSlideIn 0.25s ease forwards;
41
+ animation: rtxSlideIn 0.20s ease forwards;
42
42
  }
43
43
 
44
44
  .rtx-slide .rtx-exit {
45
- animation: rtxSlideOut 0.25s ease forwards;
45
+ animation: rtxSlideOut 0.20s ease forwards;
46
46
  }
47
47
 
48
48
  @keyframes rtxSlideIn {
@@ -59,11 +59,11 @@
59
59
  SCALE (zoom)
60
60
  ========================= */
61
61
  .rtx-scale .rtx-entering {
62
- animation: rtxScaleIn 0.25s ease forwards;
62
+ animation: rtxScaleIn 0.20s ease forwards;
63
63
  }
64
64
 
65
65
  .rtx-scale .rtx-exit {
66
- animation: rtxScaleOut 0.25s ease forwards;
66
+ animation: rtxScaleOut 0.20s ease forwards;
67
67
  }
68
68
 
69
69
  @keyframes rtxScaleIn {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "routexiz",
3
- "version": "0.2.2",
3
+ "version": "0.2.3-z1",
4
4
  "description": "A modern tree-based router for React with intent-driven navigation, Suspense-first data loading, and built-in guards, middleware, and caching.",
5
5
  "author": "Delpi.Kye",
6
6
  "license": "MIT",