@richie-router/react 0.0.1 → 0.1.2

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.
@@ -0,0 +1,887 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __toCommonJS = (from) => {
33
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
34
+ if (entry)
35
+ return entry;
36
+ entry = __defProp({}, "__esModule", { value: true });
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (var key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(entry, key))
40
+ __defProp(entry, key, {
41
+ get: __accessProp.bind(from, key),
42
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
43
+ });
44
+ }
45
+ __moduleCache.set(from, entry);
46
+ return entry;
47
+ };
48
+ var __moduleCache;
49
+ var __returnValue = (v) => v;
50
+ function __exportSetter(name, newValue) {
51
+ this[name] = __returnValue.bind(null, newValue);
52
+ }
53
+ var __export = (target, all) => {
54
+ for (var name in all)
55
+ __defProp(target, name, {
56
+ get: all[name],
57
+ enumerable: true,
58
+ configurable: true,
59
+ set: __exportSetter.bind(all, name)
60
+ });
61
+ };
62
+
63
+ // packages/react/src/router.tsx
64
+ var exports_router = {};
65
+ __export(exports_router, {
66
+ useSearch: () => useSearch,
67
+ useRouterState: () => useRouterState,
68
+ useRouter: () => useRouter,
69
+ useParams: () => useParams,
70
+ useNavigate: () => useNavigate,
71
+ useMatches: () => useMatches,
72
+ useMatch: () => useMatch,
73
+ useLocation: () => useLocation,
74
+ useElementScrollRestoration: () => useElementScrollRestoration,
75
+ useBlocker: () => useBlocker,
76
+ redirect: () => import_core.redirect,
77
+ notFound: () => import_core.notFound,
78
+ linkOptions: () => linkOptions,
79
+ isRedirect: () => import_core.isRedirect,
80
+ isNotFound: () => import_core.isNotFound,
81
+ getRouteApi: () => getRouteApi,
82
+ createRouter: () => createRouter,
83
+ createRouteMask: () => createRouteMask,
84
+ createRootRoute: () => createRootRoute,
85
+ createMemoryHistory: () => import_history.createMemoryHistory,
86
+ createLink: () => createLink,
87
+ createHashHistory: () => import_history.createHashHistory,
88
+ createFileRoute: () => createFileRoute,
89
+ createBrowserHistory: () => import_history.createBrowserHistory,
90
+ RouterProvider: () => RouterProvider,
91
+ Router: () => Router,
92
+ Outlet: () => Outlet,
93
+ Link: () => Link,
94
+ Block: () => Block
95
+ });
96
+ module.exports = __toCommonJS(exports_router);
97
+ var import_react = __toESM(require("react"));
98
+ var import_core = require("@richie-router/core");
99
+ var import_history = require("./history.cjs");
100
+ var jsx_dev_runtime = require("react/jsx-dev-runtime");
101
+ var RouterContext = import_react.default.createContext(null);
102
+ var RouterStateContext = import_react.default.createContext(null);
103
+ var OutletContext = import_react.default.createContext(null);
104
+ var MatchContext = import_react.default.createContext(null);
105
+ var MANAGED_HEAD_ATTRIBUTE = "data-richie-router-head";
106
+ var EMPTY_HEAD = { meta: [], links: [], scripts: [], styles: [] };
107
+ function routeHasRecord(value) {
108
+ return typeof value === "object" && value !== null;
109
+ }
110
+ function resolveParamsInput(input, previous) {
111
+ if (input === undefined) {
112
+ return previous;
113
+ }
114
+ return typeof input === "function" ? input(previous) : input;
115
+ }
116
+ function resolveSearchInput(input, previous) {
117
+ if (input === true) {
118
+ return previous;
119
+ }
120
+ if (input === undefined) {
121
+ return {};
122
+ }
123
+ return typeof input === "function" ? input(previous) : input;
124
+ }
125
+ function attachRouteApi(route) {
126
+ const api = route;
127
+ api.useParams = () => useRouteMatchByFullPath(route.fullPath).params;
128
+ api.useSearch = () => useRouteMatchByFullPath(route.fullPath).search;
129
+ api.useNavigate = () => useNavigate();
130
+ api.useMatch = () => useRouteMatchByFullPath(route.fullPath);
131
+ return api;
132
+ }
133
+ function createFileRoute(path) {
134
+ return function(options) {
135
+ const route = import_core.createRouteNode(path, options);
136
+ return attachRouteApi(route);
137
+ };
138
+ }
139
+ function createRootRoute(options) {
140
+ const route = import_core.createRouteNode("__root__", options, { isRoot: true });
141
+ return attachRouteApi(route);
142
+ }
143
+
144
+ class Router {
145
+ routeTree;
146
+ history;
147
+ options;
148
+ state;
149
+ routesByFullPath = new Map;
150
+ routesByTo = new Map;
151
+ listeners = new Set;
152
+ headCache = new Map;
153
+ parseSearch;
154
+ stringifySearch;
155
+ started = false;
156
+ unsubscribeHistory;
157
+ constructor(options) {
158
+ this.routeTree = options.routeTree;
159
+ this.options = options;
160
+ this.history = options.history ?? (typeof window === "undefined" ? import_history.createMemoryHistory() : import_history.createBrowserHistory());
161
+ this.parseSearch = options.parseSearch ?? import_core.defaultParseSearch;
162
+ this.stringifySearch = options.stringifySearch ?? import_core.defaultStringifySearch;
163
+ for (const route of import_core.collectRoutes(this.routeTree)) {
164
+ this.routesByFullPath.set(route.fullPath, route);
165
+ }
166
+ for (const branch of import_core.collectBranches(this.routeTree)) {
167
+ this.routesByTo.set(branch.leaf.to, branch.leaf);
168
+ }
169
+ const location = this.readLocation();
170
+ const initialHeadSnapshot = typeof window !== "undefined" ? window.__RICHIE_ROUTER_HEAD__ : undefined;
171
+ const initialHead = initialHeadSnapshot && initialHeadSnapshot.href === location.href ? initialHeadSnapshot.head : EMPTY_HEAD;
172
+ if (typeof window !== "undefined" && initialHeadSnapshot !== undefined) {
173
+ delete window.__RICHIE_ROUTER_HEAD__;
174
+ }
175
+ this.state = {
176
+ status: "loading",
177
+ location,
178
+ matches: this.buildMatches(location),
179
+ head: initialHead,
180
+ error: null
181
+ };
182
+ }
183
+ getSnapshot = () => this.state;
184
+ subscribe = (listener) => {
185
+ this.listeners.add(listener);
186
+ return () => {
187
+ this.listeners.delete(listener);
188
+ };
189
+ };
190
+ start() {
191
+ if (this.started) {
192
+ return;
193
+ }
194
+ this.started = true;
195
+ this.unsubscribeHistory = this.history.listen(() => {
196
+ this.handleHistoryChange();
197
+ });
198
+ if (this.state.status === "loading") {
199
+ this.load();
200
+ }
201
+ }
202
+ dispose() {
203
+ this.unsubscribeHistory?.();
204
+ this.unsubscribeHistory = undefined;
205
+ this.started = false;
206
+ }
207
+ async load(options) {
208
+ const nextLocation = this.readLocation();
209
+ await this.commitLocation(nextLocation, {
210
+ request: options?.request,
211
+ replace: true,
212
+ writeHistory: false
213
+ });
214
+ }
215
+ async navigate(options) {
216
+ const href = this.buildHref(options);
217
+ const location = import_core.createParsedLocation(href, options.state ?? null, this.parseSearch);
218
+ await this.commitLocation(location, {
219
+ replace: options.replace ?? false,
220
+ writeHistory: true,
221
+ resetScroll: options.resetScroll
222
+ });
223
+ }
224
+ async preloadRoute(options) {
225
+ const href = this.buildHref(options);
226
+ const location = import_core.createParsedLocation(href, options.state ?? null, this.parseSearch);
227
+ try {
228
+ await this.resolveLocation(location);
229
+ } catch {}
230
+ }
231
+ async invalidate() {
232
+ this.headCache.clear();
233
+ await this.load();
234
+ }
235
+ buildHref(options) {
236
+ const targetRoute = this.routesByTo.get(options.to) ?? null;
237
+ const fromMatch = options.from ? this.findMatchByTo(options.from) : null;
238
+ const previousParams = fromMatch?.params ?? {};
239
+ const previousSearch = fromMatch?.search ?? this.state.location.search;
240
+ const params = resolveParamsInput(options.params, previousParams);
241
+ const path = import_core.buildPath(options.to, params);
242
+ const searchValue = resolveSearchInput(options.search, previousSearch);
243
+ const search = this.stringifySearch(routeHasRecord(searchValue) ? searchValue : {});
244
+ const hash = options.hash ? `#${options.hash.replace(/^#/, "")}` : "";
245
+ const normalizedPath = this.applyTrailingSlash(path, targetRoute);
246
+ return `${normalizedPath}${search}${hash}`;
247
+ }
248
+ readLocation() {
249
+ const location = this.history.location;
250
+ return import_core.createParsedLocation(location.href, location.state, this.parseSearch);
251
+ }
252
+ applyTrailingSlash(pathname, route) {
253
+ const trailingSlash = this.options.trailingSlash ?? "preserve";
254
+ if (trailingSlash === "preserve") {
255
+ return pathname;
256
+ }
257
+ if (pathname === "/") {
258
+ return "/";
259
+ }
260
+ if (trailingSlash === "always") {
261
+ return pathname.endsWith("/") ? pathname : `${pathname}/`;
262
+ }
263
+ if (route && route.fullPath.endsWith("/") && route.to === pathname) {
264
+ return pathname;
265
+ }
266
+ return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
267
+ }
268
+ notify() {
269
+ for (const listener of this.listeners) {
270
+ listener();
271
+ }
272
+ }
273
+ findMatchByTo(to) {
274
+ const route = this.routesByTo.get(to);
275
+ if (!route) {
276
+ return null;
277
+ }
278
+ return this.state.matches.find((match) => match.route.fullPath === route.fullPath) ?? null;
279
+ }
280
+ buildMatches(location) {
281
+ const matched = import_core.matchRouteTree(this.routeTree, location.pathname) ?? [];
282
+ const rawSearch = location.search;
283
+ let accumulatedSearch = { ...rawSearch };
284
+ return matched.map(({ route, params }) => {
285
+ const nextSearch = this.resolveSearch(route, rawSearch);
286
+ if (routeHasRecord(nextSearch)) {
287
+ accumulatedSearch = {
288
+ ...accumulatedSearch,
289
+ ...nextSearch
290
+ };
291
+ }
292
+ return {
293
+ id: route.fullPath,
294
+ pathname: location.pathname,
295
+ params,
296
+ route,
297
+ search: accumulatedSearch,
298
+ to: route.to
299
+ };
300
+ });
301
+ }
302
+ resolveSearch(route, rawSearch) {
303
+ const fromSchema = route.searchSchema ? route.searchSchema.parse(rawSearch) : {};
304
+ if (routeHasRecord(fromSchema)) {
305
+ return fromSchema;
306
+ }
307
+ return rawSearch;
308
+ }
309
+ async resolveLocation(location, options) {
310
+ const matched = import_core.matchRouteTree(this.routeTree, location.pathname);
311
+ if (!matched) {
312
+ throw import_core.notFound();
313
+ }
314
+ const rawSearch = location.search;
315
+ let accumulatedSearch = { ...rawSearch };
316
+ const matches = [];
317
+ for (const { route, params } of matched) {
318
+ const nextSearch = this.resolveSearch(route, rawSearch);
319
+ if (routeHasRecord(nextSearch)) {
320
+ accumulatedSearch = {
321
+ ...accumulatedSearch,
322
+ ...nextSearch
323
+ };
324
+ }
325
+ if (route.options.beforeLoad) {
326
+ await route.options.beforeLoad({
327
+ location,
328
+ params,
329
+ search: accumulatedSearch,
330
+ navigate: async (navigateOptions) => {
331
+ await this.navigate(navigateOptions);
332
+ },
333
+ cause: this.state.location.pathname === location.pathname ? "stay" : "enter"
334
+ });
335
+ }
336
+ matches.push({
337
+ id: route.fullPath,
338
+ pathname: location.pathname,
339
+ params,
340
+ route,
341
+ search: accumulatedSearch,
342
+ to: route.to
343
+ });
344
+ }
345
+ const head = await this.resolveLocationHead(matches, location, options?.request);
346
+ return { matches, head, error: null };
347
+ }
348
+ async resolveLocationHead(matches, location, request) {
349
+ const resolvedHeadByRoute = new Map;
350
+ for (const match of matches) {
351
+ if (!match.route.serverHead) {
352
+ continue;
353
+ }
354
+ resolvedHeadByRoute.set(match.route.fullPath, await this.loadRouteHead(match.route, match.params, match.search, location, request));
355
+ }
356
+ return import_core.resolveHeadConfig(matches, resolvedHeadByRoute);
357
+ }
358
+ async loadRouteHead(route, params, search, location, request) {
359
+ const cacheKey = JSON.stringify({
360
+ routeId: route.fullPath,
361
+ params,
362
+ search
363
+ });
364
+ const cached = this.headCache.get(cacheKey);
365
+ if (cached && cached.expiresAt > Date.now()) {
366
+ return cached.head;
367
+ }
368
+ const response = this.options.loadRouteHead !== undefined ? await this.options.loadRouteHead({
369
+ route,
370
+ routeId: route.fullPath,
371
+ params,
372
+ search,
373
+ location,
374
+ request
375
+ }) : await this.fetchRouteHead(route, params, search);
376
+ this.headCache.set(cacheKey, {
377
+ head: response.head,
378
+ expiresAt: Date.now() + (response.staleTime ?? 0)
379
+ });
380
+ return response.head;
381
+ }
382
+ async fetchRouteHead(route, params, search) {
383
+ const basePath = this.options.headBasePath ?? "/head-api";
384
+ const searchParams = new URLSearchParams({
385
+ routeId: route.fullPath,
386
+ params: JSON.stringify(params),
387
+ search: JSON.stringify(search)
388
+ });
389
+ const response = await fetch(`${basePath}?${searchParams.toString()}`);
390
+ if (!response.ok) {
391
+ if (response.status === 404) {
392
+ throw import_core.notFound();
393
+ }
394
+ throw new Error(`Failed to resolve server head for route "${route.fullPath}"`);
395
+ }
396
+ return await response.json();
397
+ }
398
+ async commitLocation(location, options) {
399
+ this.state = {
400
+ ...this.state,
401
+ status: "loading",
402
+ location
403
+ };
404
+ this.notify();
405
+ try {
406
+ const resolved = await this.resolveLocation(location, {
407
+ request: options.request
408
+ });
409
+ if (options.writeHistory) {
410
+ if (options.replace) {
411
+ this.history.replace(location.href, location.state);
412
+ } else {
413
+ this.history.push(location.href, location.state);
414
+ }
415
+ }
416
+ this.state = {
417
+ status: "idle",
418
+ location,
419
+ matches: resolved.matches,
420
+ head: resolved.head,
421
+ error: resolved.error
422
+ };
423
+ this.notify();
424
+ this.restoreScroll(options.resetScroll);
425
+ } catch (error) {
426
+ if (import_core.isRedirect(error)) {
427
+ await this.navigate({
428
+ ...error.options,
429
+ replace: error.options.replace ?? true
430
+ });
431
+ return;
432
+ }
433
+ const errorMatches = this.buildMatches(location);
434
+ if (options.writeHistory) {
435
+ if (options.replace) {
436
+ this.history.replace(location.href, location.state);
437
+ } else {
438
+ this.history.push(location.href, location.state);
439
+ }
440
+ }
441
+ this.state = {
442
+ status: "idle",
443
+ location,
444
+ matches: errorMatches,
445
+ head: import_core.resolveHeadConfig(errorMatches),
446
+ error
447
+ };
448
+ this.notify();
449
+ }
450
+ }
451
+ restoreScroll(resetScroll) {
452
+ if (typeof window === "undefined") {
453
+ return;
454
+ }
455
+ if (!this.options.scrollRestoration || resetScroll === false) {
456
+ return;
457
+ }
458
+ const selectors = this.options.scrollToTopSelectors ?? [];
459
+ if (selectors.length === 0) {
460
+ window.scrollTo({ top: 0, left: 0, behavior: "instant" });
461
+ return;
462
+ }
463
+ for (const selector of selectors) {
464
+ const element = document.querySelector(selector);
465
+ if (element instanceof HTMLElement) {
466
+ element.scrollTo({ top: 0, left: 0, behavior: "instant" });
467
+ }
468
+ }
469
+ }
470
+ async handleHistoryChange() {
471
+ const nextLocation = this.readLocation();
472
+ await this.commitLocation(nextLocation, {
473
+ replace: true,
474
+ writeHistory: false
475
+ });
476
+ }
477
+ }
478
+ function createRouter(options) {
479
+ return new Router(options);
480
+ }
481
+ function useRouterContext() {
482
+ const router = import_react.default.useContext(RouterContext);
483
+ if (!router) {
484
+ throw new Error("Richie Router hooks must be used inside <RouterProvider>.");
485
+ }
486
+ return router;
487
+ }
488
+ function useRouterStateContext() {
489
+ const state = import_react.default.useContext(RouterStateContext);
490
+ if (!state) {
491
+ throw new Error("Richie Router hooks must be used inside <RouterProvider>.");
492
+ }
493
+ return state;
494
+ }
495
+ function useRouteMatchByFullPath(fullPath) {
496
+ const state = useRouterStateContext();
497
+ const currentRenderedMatch = import_react.default.useContext(MatchContext);
498
+ return state.matches.find((match) => match.route.fullPath === fullPath) ?? (currentRenderedMatch?.route.fullPath === fullPath ? currentRenderedMatch : null) ?? (() => {
499
+ throw new Error(`No active match found for "${fullPath}".`);
500
+ })();
501
+ }
502
+ function resolveHookMatch(from) {
503
+ const router = useRouterContext();
504
+ const state = useRouterStateContext();
505
+ const currentRenderedMatch = import_react.default.useContext(MatchContext);
506
+ if (!from) {
507
+ return currentRenderedMatch ?? state.matches.at(-1) ?? (() => {
508
+ throw new Error("No active route match is available.");
509
+ })();
510
+ }
511
+ const targetRoute = router.routesByTo.get(from);
512
+ if (!targetRoute) {
513
+ throw new Error(`Unknown route path "${from}".`);
514
+ }
515
+ return state.matches.find((match) => match.route.fullPath === targetRoute.fullPath) ?? (() => {
516
+ throw new Error(`The route "${from}" is not part of the current match set.`);
517
+ })();
518
+ }
519
+ function createManagedHeadElements(head) {
520
+ if (typeof document === "undefined") {
521
+ return [];
522
+ }
523
+ const elements = [];
524
+ const managed = (element) => {
525
+ element.setAttribute(MANAGED_HEAD_ATTRIBUTE, "true");
526
+ return element;
527
+ };
528
+ for (const meta of head.meta ?? []) {
529
+ if ("title" in meta) {
530
+ const title = managed(document.createElement("title"));
531
+ title.textContent = meta.title;
532
+ elements.push(title);
533
+ continue;
534
+ }
535
+ const tag = managed(document.createElement("meta"));
536
+ if ("charset" in meta) {
537
+ tag.setAttribute("charset", meta.charset);
538
+ } else if ("name" in meta) {
539
+ tag.setAttribute("name", meta.name);
540
+ tag.setAttribute("content", meta.content);
541
+ } else if ("property" in meta) {
542
+ tag.setAttribute("property", meta.property);
543
+ tag.setAttribute("content", meta.content);
544
+ } else {
545
+ tag.setAttribute("http-equiv", meta.httpEquiv);
546
+ tag.setAttribute("content", meta.content);
547
+ }
548
+ elements.push(tag);
549
+ }
550
+ for (const link of head.links ?? []) {
551
+ const tag = managed(document.createElement("link"));
552
+ tag.setAttribute("rel", link.rel);
553
+ tag.setAttribute("href", link.href);
554
+ if (link.type)
555
+ tag.setAttribute("type", link.type);
556
+ if (link.media)
557
+ tag.setAttribute("media", link.media);
558
+ if (link.sizes)
559
+ tag.setAttribute("sizes", link.sizes);
560
+ if (link.crossorigin)
561
+ tag.setAttribute("crossorigin", link.crossorigin);
562
+ elements.push(tag);
563
+ }
564
+ for (const style of head.styles ?? []) {
565
+ const tag = managed(document.createElement("style"));
566
+ if (style.media)
567
+ tag.setAttribute("media", style.media);
568
+ tag.textContent = style.children;
569
+ elements.push(tag);
570
+ }
571
+ for (const script of head.scripts ?? []) {
572
+ const tag = managed(document.createElement("script"));
573
+ if (script.src)
574
+ tag.setAttribute("src", script.src);
575
+ if (script.type)
576
+ tag.setAttribute("type", script.type);
577
+ if (script.async)
578
+ tag.async = true;
579
+ if (script.defer)
580
+ tag.defer = true;
581
+ if (script.children)
582
+ tag.textContent = script.children;
583
+ elements.push(tag);
584
+ }
585
+ return elements;
586
+ }
587
+ function reconcileDocumentHead(head) {
588
+ if (typeof document === "undefined") {
589
+ return;
590
+ }
591
+ for (const element of Array.from(document.head.querySelectorAll(`[${MANAGED_HEAD_ATTRIBUTE}]`))) {
592
+ element.remove();
593
+ }
594
+ const elements = createManagedHeadElements(head);
595
+ for (const element of elements) {
596
+ document.head.appendChild(element);
597
+ }
598
+ }
599
+ function RenderMatches({ matches, index }) {
600
+ const match = matches[index];
601
+ if (!match) {
602
+ return null;
603
+ }
604
+ const Component = match.route.options.component;
605
+ const outlet = /* @__PURE__ */ jsx_dev_runtime.jsxDEV(RenderMatches, {
606
+ matches,
607
+ index: index + 1
608
+ }, undefined, false, undefined, this);
609
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(MatchContext.Provider, {
610
+ value: match,
611
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(OutletContext.Provider, {
612
+ value: outlet,
613
+ children: Component ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Component, {}, undefined, false, undefined, this) : outlet
614
+ }, undefined, false, undefined, this)
615
+ }, undefined, false, undefined, this);
616
+ }
617
+ function renderError(error, matches, router) {
618
+ const reversed = [...matches].reverse();
619
+ if (import_core.isNotFound(error)) {
620
+ const NotFoundComponent = reversed.find((match) => match.route.options.notFoundComponent)?.route.options.notFoundComponent ?? router.options.defaultNotFoundComponent;
621
+ if (NotFoundComponent) {
622
+ return import_react.default.createElement(NotFoundComponent);
623
+ }
624
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
625
+ children: "Not Found"
626
+ }, undefined, false, undefined, this);
627
+ }
628
+ const ErrorComponent = reversed.find((match) => match.route.options.errorComponent)?.route.options.errorComponent ?? router.options.defaultErrorComponent;
629
+ if (ErrorComponent) {
630
+ return import_react.default.createElement(ErrorComponent, {
631
+ error,
632
+ reset: () => {
633
+ router.invalidate();
634
+ }
635
+ });
636
+ }
637
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("pre", {
638
+ children: error instanceof Error ? error.message : "Unknown routing error"
639
+ }, undefined, false, undefined, this);
640
+ }
641
+ function RouterProvider({ router }) {
642
+ const snapshot = import_react.default.useSyncExternalStore(router.subscribe, router.getSnapshot, router.getSnapshot);
643
+ import_react.default.useEffect(() => {
644
+ router.start();
645
+ return () => {
646
+ router.dispose();
647
+ };
648
+ }, [router]);
649
+ import_react.default.useEffect(() => {
650
+ reconcileDocumentHead(snapshot.head);
651
+ }, [snapshot.head]);
652
+ const content = snapshot.error ? renderError(snapshot.error, snapshot.matches, router) : /* @__PURE__ */ jsx_dev_runtime.jsxDEV(RenderMatches, {
653
+ matches: snapshot.matches,
654
+ index: 0
655
+ }, undefined, false, undefined, this);
656
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(RouterContext.Provider, {
657
+ value: router,
658
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(RouterStateContext.Provider, {
659
+ value: snapshot,
660
+ children: content
661
+ }, undefined, false, undefined, this)
662
+ }, undefined, false, undefined, this);
663
+ }
664
+ function Outlet() {
665
+ return import_react.default.useContext(OutletContext);
666
+ }
667
+ function useRouter() {
668
+ return useRouterContext();
669
+ }
670
+ function useMatches() {
671
+ return useRouterStateContext().matches;
672
+ }
673
+ function useMatch(options) {
674
+ return resolveHookMatch(options?.from);
675
+ }
676
+ function useParams(options) {
677
+ return resolveHookMatch(options?.from).params;
678
+ }
679
+ function useSearch(options) {
680
+ return resolveHookMatch(options?.from).search;
681
+ }
682
+ function useNavigate() {
683
+ const router = useRouterContext();
684
+ return import_react.default.useCallback(async (options) => {
685
+ await router.navigate(options);
686
+ }, [router]);
687
+ }
688
+ function useLocation() {
689
+ return useRouterStateContext().location;
690
+ }
691
+ function useRouterState(options) {
692
+ const router = useRouterContext();
693
+ return import_react.default.useSyncExternalStore(router.subscribe, () => options.select(router.getSnapshot()), () => options.select(router.getSnapshot()));
694
+ }
695
+ function useBlocker(options) {
696
+ const current = useLocation();
697
+ const [next, setNext] = import_react.default.useState(null);
698
+ import_react.default.useEffect(() => {
699
+ if (!options.enableBeforeUnload || typeof window === "undefined") {
700
+ return;
701
+ }
702
+ const listener = (event) => {
703
+ if (!options.shouldBlockFn?.({ current, next })) {
704
+ return;
705
+ }
706
+ event.preventDefault();
707
+ event.returnValue = "";
708
+ };
709
+ window.addEventListener("beforeunload", listener);
710
+ return () => window.removeEventListener("beforeunload", listener);
711
+ }, [current, next, options]);
712
+ return {
713
+ status: next ? "blocked" : "idle",
714
+ next,
715
+ proceed() {
716
+ setNext(null);
717
+ },
718
+ reset() {
719
+ setNext(null);
720
+ }
721
+ };
722
+ }
723
+ function Block() {
724
+ return null;
725
+ }
726
+ function useElementScrollRestoration() {
727
+ return {
728
+ ref: () => {}
729
+ };
730
+ }
731
+ function useResolvedLink(props) {
732
+ const router = useRouterContext();
733
+ const href = router.buildHref(props);
734
+ const location = useLocation();
735
+ const pathOnly = href.split(/[?#]/u)[0] ?? href;
736
+ const isActive = pathOnly === location.pathname;
737
+ return { href, isActive, router };
738
+ }
739
+ var LinkComponent = import_react.default.forwardRef(function LinkInner(props, ref) {
740
+ const allProps = props;
741
+ const {
742
+ to,
743
+ from,
744
+ params,
745
+ search,
746
+ hash,
747
+ replace,
748
+ resetScroll,
749
+ state,
750
+ mask,
751
+ ignoreBlocker,
752
+ activeProps,
753
+ children,
754
+ onClick,
755
+ onMouseEnter,
756
+ onFocus,
757
+ preload,
758
+ ...anchorProps
759
+ } = allProps;
760
+ const navigation = {
761
+ to,
762
+ from,
763
+ params,
764
+ search,
765
+ hash,
766
+ replace,
767
+ resetScroll,
768
+ state,
769
+ mask,
770
+ ignoreBlocker
771
+ };
772
+ const { href, isActive, router } = useResolvedLink(navigation);
773
+ const preloadMode = preload ?? router.options.defaultPreload;
774
+ const preloadDelay = router.options.defaultPreloadDelay ?? 50;
775
+ const preloadTimeout = import_react.default.useRef(null);
776
+ import_react.default.useEffect(() => {
777
+ if (preloadMode !== "render") {
778
+ return;
779
+ }
780
+ router.preloadRoute(navigation);
781
+ }, [navigation, preloadMode, router]);
782
+ const schedulePreload = import_react.default.useCallback(() => {
783
+ if (preloadMode !== "intent") {
784
+ return;
785
+ }
786
+ preloadTimeout.current = window.setTimeout(() => {
787
+ router.preloadRoute(navigation);
788
+ }, preloadDelay);
789
+ }, [navigation, preloadDelay, preloadMode, router]);
790
+ const cancelPreload = import_react.default.useCallback(() => {
791
+ if (preloadTimeout.current !== null) {
792
+ window.clearTimeout(preloadTimeout.current);
793
+ preloadTimeout.current = null;
794
+ }
795
+ }, []);
796
+ const renderedChildren = typeof children === "function" ? children({ isActive }) : children;
797
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("a", {
798
+ ...anchorProps,
799
+ ...isActive ? activeProps : undefined,
800
+ ref,
801
+ href,
802
+ onClick: (event) => {
803
+ onClick?.(event);
804
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) {
805
+ return;
806
+ }
807
+ event.preventDefault();
808
+ router.navigate(navigation);
809
+ },
810
+ onMouseEnter: (event) => {
811
+ onMouseEnter?.(event);
812
+ if (typeof window !== "undefined") {
813
+ schedulePreload();
814
+ }
815
+ },
816
+ onMouseLeave: cancelPreload,
817
+ onFocus: (event) => {
818
+ onFocus?.(event);
819
+ if (typeof window !== "undefined") {
820
+ schedulePreload();
821
+ }
822
+ },
823
+ onBlur: cancelPreload,
824
+ children: renderedChildren
825
+ }, undefined, false, undefined, this);
826
+ });
827
+ var Link = LinkComponent;
828
+ function createLink(Component) {
829
+ return function CreatedLink(props) {
830
+ const allProps = props;
831
+ const {
832
+ to,
833
+ from,
834
+ params,
835
+ search,
836
+ hash,
837
+ replace,
838
+ resetScroll,
839
+ state,
840
+ mask,
841
+ ignoreBlocker,
842
+ activeProps,
843
+ children,
844
+ preload,
845
+ ...componentProps
846
+ } = allProps;
847
+ const navigation = {
848
+ to,
849
+ from,
850
+ params,
851
+ search,
852
+ hash,
853
+ replace,
854
+ resetScroll,
855
+ state,
856
+ mask,
857
+ ignoreBlocker
858
+ };
859
+ const { href, isActive, router } = useResolvedLink(navigation);
860
+ const renderedChildren = typeof children === "function" ? children({ isActive }) : children;
861
+ import_react.default.useEffect(() => {
862
+ if (preload !== "render") {
863
+ return;
864
+ }
865
+ router.preloadRoute(navigation);
866
+ }, [navigation, preload, router]);
867
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Component, {
868
+ ...componentProps,
869
+ ...isActive ? activeProps : undefined,
870
+ href,
871
+ children: renderedChildren
872
+ }, undefined, false, undefined, this);
873
+ };
874
+ }
875
+ function linkOptions(options) {
876
+ return options;
877
+ }
878
+ function getRouteApi(to) {
879
+ return {
880
+ useParams: () => useParams({ from: to }),
881
+ useSearch: () => useSearch({ from: to }),
882
+ useMatch: () => useMatch({ from: to })
883
+ };
884
+ }
885
+ function createRouteMask(mask) {
886
+ return mask;
887
+ }