@flightdev/router 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,765 @@
1
+ // src/context.ts
2
+ var isBrowser = typeof window !== "undefined";
3
+ var currentContext = {
4
+ path: "/",
5
+ searchParams: new URLSearchParams(),
6
+ navigate: () => {
7
+ },
8
+ back: () => {
9
+ },
10
+ forward: () => {
11
+ }
12
+ };
13
+ var subscribers = /* @__PURE__ */ new Set();
14
+ function subscribe(callback) {
15
+ subscribers.add(callback);
16
+ return () => subscribers.delete(callback);
17
+ }
18
+ function getRouterContext() {
19
+ return currentContext;
20
+ }
21
+ function updateContext(updates) {
22
+ currentContext = { ...currentContext, ...updates };
23
+ subscribers.forEach((cb) => cb(currentContext));
24
+ }
25
+ function navigateTo(to, options = {}) {
26
+ if (!isBrowser) return;
27
+ const { replace = false, scroll = true, state } = options;
28
+ if (replace) {
29
+ window.history.replaceState(state ?? null, "", to);
30
+ } else {
31
+ window.history.pushState(state ?? null, "", to);
32
+ }
33
+ const url = new URL(to, window.location.origin);
34
+ updateContext({
35
+ path: url.pathname,
36
+ searchParams: url.searchParams
37
+ });
38
+ if (scroll) {
39
+ window.scrollTo({ top: 0, left: 0, behavior: "instant" });
40
+ }
41
+ }
42
+ function initRouter(options = {}) {
43
+ const { initialPath, basePath = "" } = options;
44
+ let path;
45
+ let searchParams;
46
+ if (isBrowser) {
47
+ path = window.location.pathname;
48
+ searchParams = new URLSearchParams(window.location.search);
49
+ } else {
50
+ path = initialPath || "/";
51
+ searchParams = new URLSearchParams();
52
+ }
53
+ if (basePath && path.startsWith(basePath)) {
54
+ path = path.slice(basePath.length) || "/";
55
+ }
56
+ currentContext = {
57
+ path,
58
+ searchParams,
59
+ navigate: navigateTo,
60
+ back: () => isBrowser && window.history.back(),
61
+ forward: () => isBrowser && window.history.forward()
62
+ };
63
+ if (isBrowser) {
64
+ window.addEventListener("popstate", () => {
65
+ updateContext({
66
+ path: window.location.pathname,
67
+ searchParams: new URLSearchParams(window.location.search)
68
+ });
69
+ });
70
+ const originalPushState = history.pushState.bind(history);
71
+ const originalReplaceState = history.replaceState.bind(history);
72
+ history.pushState = function(state, unused, url) {
73
+ originalPushState(state, unused, url);
74
+ if (url) {
75
+ const newUrl = new URL(url.toString(), window.location.origin);
76
+ updateContext({
77
+ path: newUrl.pathname,
78
+ searchParams: newUrl.searchParams
79
+ });
80
+ }
81
+ };
82
+ history.replaceState = function(state, unused, url) {
83
+ originalReplaceState(state, unused, url);
84
+ if (url) {
85
+ const newUrl = new URL(url.toString(), window.location.origin);
86
+ updateContext({
87
+ path: newUrl.pathname,
88
+ searchParams: newUrl.searchParams
89
+ });
90
+ }
91
+ };
92
+ }
93
+ }
94
+ var initialized = false;
95
+ if (isBrowser && !initialized) {
96
+ initialized = true;
97
+ initRouter();
98
+ }
99
+ var RouterContext = null;
100
+ var RouterProvider = null;
101
+ var useRouter = getRouterContext;
102
+ if (typeof globalThis !== "undefined") {
103
+ try {
104
+ const React = globalThis.React;
105
+ if (React?.createContext) {
106
+ const { createContext, useState, useEffect, useContext } = React;
107
+ const ReactRouterContext = createContext(currentContext);
108
+ RouterContext = ReactRouterContext;
109
+ RouterProvider = function FlightRouterProvider({
110
+ children,
111
+ initialPath,
112
+ basePath = ""
113
+ }) {
114
+ const [routerState, setRouterState] = useState(() => {
115
+ const path = isBrowser ? window.location.pathname : initialPath || "/";
116
+ const searchParams = isBrowser ? new URLSearchParams(window.location.search) : new URLSearchParams();
117
+ return {
118
+ path: basePath && path.startsWith(basePath) ? path.slice(basePath.length) || "/" : path,
119
+ searchParams,
120
+ navigate: navigateTo,
121
+ back: () => isBrowser && window.history.back(),
122
+ forward: () => isBrowser && window.history.forward()
123
+ };
124
+ });
125
+ useEffect(() => {
126
+ if (!isBrowser) return;
127
+ const handlePopState = () => {
128
+ let path = window.location.pathname;
129
+ if (basePath && path.startsWith(basePath)) {
130
+ path = path.slice(basePath.length) || "/";
131
+ }
132
+ setRouterState((prev) => ({
133
+ ...prev,
134
+ path,
135
+ searchParams: new URLSearchParams(window.location.search)
136
+ }));
137
+ };
138
+ window.addEventListener("popstate", handlePopState);
139
+ return () => window.removeEventListener("popstate", handlePopState);
140
+ }, [basePath]);
141
+ useEffect(() => {
142
+ return subscribe((ctx) => {
143
+ setRouterState((prev) => ({
144
+ ...prev,
145
+ path: ctx.path,
146
+ searchParams: ctx.searchParams
147
+ }));
148
+ });
149
+ }, []);
150
+ return React.createElement(
151
+ ReactRouterContext.Provider,
152
+ { value: routerState },
153
+ children
154
+ );
155
+ };
156
+ useRouter = function useFlightRouter() {
157
+ return useContext(ReactRouterContext);
158
+ };
159
+ }
160
+ } catch {
161
+ }
162
+ }
163
+
164
+ // src/prefetch.ts
165
+ var isBrowser2 = typeof window !== "undefined";
166
+ var supportsIntersectionObserver = isBrowser2 && "IntersectionObserver" in window;
167
+ var prefetchedUrls = /* @__PURE__ */ new Set();
168
+ var prefetchingUrls = /* @__PURE__ */ new Set();
169
+ var viewportObservers = /* @__PURE__ */ new Map();
170
+ function prefetch(href, options = {}) {
171
+ if (!isBrowser2) return;
172
+ const {
173
+ priority = "auto",
174
+ includeModules = true,
175
+ includeData = false
176
+ } = options;
177
+ const url = normalizeUrl(href);
178
+ if (prefetchedUrls.has(url) || prefetchingUrls.has(url)) {
179
+ return;
180
+ }
181
+ prefetchingUrls.add(url);
182
+ createPrefetchLink(url, "document", priority);
183
+ if (includeModules) {
184
+ prefetchModules(url, priority);
185
+ }
186
+ if (includeData) {
187
+ prefetchData(url, priority);
188
+ }
189
+ prefetchedUrls.add(url);
190
+ prefetchingUrls.delete(url);
191
+ }
192
+ function prefetchAll(hrefs, options = {}) {
193
+ for (const href of hrefs) {
194
+ prefetch(href, options);
195
+ }
196
+ }
197
+ function isPrefetched(href) {
198
+ return prefetchedUrls.has(normalizeUrl(href));
199
+ }
200
+ function clearPrefetchCache() {
201
+ prefetchedUrls.clear();
202
+ prefetchingUrls.clear();
203
+ }
204
+ function createPrefetchLink(href, as, priority) {
205
+ if (!isBrowser2) return null;
206
+ const existing = document.querySelector(
207
+ `link[rel="prefetch"][href="${href}"], link[rel="modulepreload"][href="${href}"]`
208
+ );
209
+ if (existing) return existing;
210
+ const link = document.createElement("link");
211
+ if (as === "script") {
212
+ link.rel = "modulepreload";
213
+ } else {
214
+ link.rel = "prefetch";
215
+ link.as = as;
216
+ }
217
+ link.href = href;
218
+ if (priority !== "auto" && "fetchPriority" in link) {
219
+ link.fetchPriority = priority;
220
+ }
221
+ if (priority === "low" && "requestIdleCallback" in window) {
222
+ window.requestIdleCallback(() => {
223
+ document.head.appendChild(link);
224
+ });
225
+ } else {
226
+ document.head.appendChild(link);
227
+ }
228
+ return link;
229
+ }
230
+ function prefetchModules(href, priority) {
231
+ const manifest = window.__FLIGHT_MANIFEST__;
232
+ if (!manifest?.routes) return;
233
+ const routeModules = manifest.routes[href];
234
+ if (!routeModules) return;
235
+ for (const module of routeModules) {
236
+ createPrefetchLink(module, "script", priority);
237
+ }
238
+ }
239
+ function prefetchData(href, priority) {
240
+ const dataUrl = `/_flight/data${href === "/" ? "/index" : href}.json`;
241
+ createPrefetchLink(dataUrl, "fetch", priority);
242
+ }
243
+ var sharedObserver = null;
244
+ var observerCallbacks = /* @__PURE__ */ new Map();
245
+ function getViewportObserver() {
246
+ if (!supportsIntersectionObserver) return null;
247
+ if (!sharedObserver) {
248
+ sharedObserver = new IntersectionObserver(
249
+ (entries) => {
250
+ for (const entry of entries) {
251
+ if (entry.isIntersecting) {
252
+ const callback = observerCallbacks.get(entry.target);
253
+ if (callback) {
254
+ callback();
255
+ sharedObserver?.unobserve(entry.target);
256
+ observerCallbacks.delete(entry.target);
257
+ }
258
+ }
259
+ }
260
+ },
261
+ {
262
+ // Start prefetching when link is 25% visible or within 100px of viewport
263
+ rootMargin: "100px",
264
+ threshold: 0.25
265
+ }
266
+ );
267
+ }
268
+ return sharedObserver;
269
+ }
270
+ function observeForPrefetch(element, href) {
271
+ if (!supportsIntersectionObserver) {
272
+ return () => {
273
+ };
274
+ }
275
+ const observer = getViewportObserver();
276
+ if (!observer) return () => {
277
+ };
278
+ const callback = () => {
279
+ prefetch(href, { priority: "low" });
280
+ };
281
+ observerCallbacks.set(element, callback);
282
+ observer.observe(element);
283
+ const cleanup = () => {
284
+ observer.unobserve(element);
285
+ observerCallbacks.delete(element);
286
+ viewportObservers.delete(element);
287
+ };
288
+ viewportObservers.set(element, cleanup);
289
+ return cleanup;
290
+ }
291
+ function setupIntentPrefetch(element, href) {
292
+ if (!isBrowser2) return () => {
293
+ };
294
+ let prefetchTriggered = false;
295
+ const handleIntent = () => {
296
+ if (!prefetchTriggered) {
297
+ prefetchTriggered = true;
298
+ prefetch(href, { priority: "auto" });
299
+ }
300
+ };
301
+ element.addEventListener("mouseenter", handleIntent, { passive: true });
302
+ element.addEventListener("focus", handleIntent, { passive: true });
303
+ element.addEventListener("touchstart", handleIntent, { passive: true });
304
+ return () => {
305
+ element.removeEventListener("mouseenter", handleIntent);
306
+ element.removeEventListener("focus", handleIntent);
307
+ element.removeEventListener("touchstart", handleIntent);
308
+ };
309
+ }
310
+ function normalizeUrl(href) {
311
+ if (isBrowser2 && !href.startsWith("http")) {
312
+ try {
313
+ const url = new URL(href, window.location.origin);
314
+ return url.pathname + url.search;
315
+ } catch {
316
+ return href;
317
+ }
318
+ }
319
+ return href;
320
+ }
321
+
322
+ // src/link.ts
323
+ var isBrowser3 = typeof window !== "undefined";
324
+ function handleLinkClick(href, options, event) {
325
+ if (event && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
326
+ return;
327
+ }
328
+ if (event && event.button !== 0) {
329
+ return;
330
+ }
331
+ event?.preventDefault();
332
+ const { navigate: navigate2 } = getRouterContext();
333
+ navigate2(href, options);
334
+ }
335
+ function isExternalUrl(href) {
336
+ if (!href) return false;
337
+ return href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//") || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("javascript:") || href.startsWith("#");
338
+ }
339
+ function normalizePrefetchStrategy(prefetchProp) {
340
+ if (prefetchProp === true) {
341
+ return "intent";
342
+ }
343
+ if (prefetchProp === false || prefetchProp === void 0) {
344
+ return "none";
345
+ }
346
+ return prefetchProp;
347
+ }
348
+ function prefetchRoute(href) {
349
+ prefetch(href);
350
+ }
351
+ var Link = null;
352
+ if (typeof globalThis !== "undefined") {
353
+ try {
354
+ const React = globalThis.React;
355
+ if (React?.createElement) {
356
+ const { useCallback, useEffect, useRef } = React;
357
+ Link = function FlightLink({
358
+ href,
359
+ children,
360
+ className,
361
+ target,
362
+ rel,
363
+ prefetch: prefetchProp = "none",
364
+ replace = false,
365
+ scroll = true,
366
+ onClick,
367
+ "aria-label": ariaLabel,
368
+ ...props
369
+ }) {
370
+ const linkRef = useRef(null);
371
+ const isExternal = isExternalUrl(href);
372
+ const prefetchStrategy = normalizePrefetchStrategy(prefetchProp);
373
+ const handleClick = useCallback(
374
+ (e) => {
375
+ if (onClick) {
376
+ onClick(e);
377
+ if (e.defaultPrevented) return;
378
+ }
379
+ if (isExternal || target === "_blank") return;
380
+ handleLinkClick(href, { replace, scroll }, e);
381
+ },
382
+ [href, isExternal, target, replace, scroll, onClick]
383
+ );
384
+ useEffect(() => {
385
+ if (isExternal || !isBrowser3 || prefetchStrategy === "none") {
386
+ return;
387
+ }
388
+ const link = linkRef.current;
389
+ if (!link) return;
390
+ switch (prefetchStrategy) {
391
+ case "render":
392
+ prefetch(href, { priority: "low" });
393
+ break;
394
+ case "viewport":
395
+ return observeForPrefetch(link, href);
396
+ case "intent":
397
+ default:
398
+ return setupIntentPrefetch(link, href);
399
+ }
400
+ }, [href, prefetchStrategy, isExternal]);
401
+ const computedRel = isExternal && target === "_blank" ? rel || "noopener noreferrer" : rel;
402
+ return React.createElement(
403
+ "a",
404
+ {
405
+ ref: linkRef,
406
+ href,
407
+ className,
408
+ target,
409
+ rel: computedRel,
410
+ "aria-label": ariaLabel,
411
+ onClick: handleClick,
412
+ ...props
413
+ },
414
+ children
415
+ );
416
+ };
417
+ }
418
+ } catch {
419
+ }
420
+ }
421
+ function createLink(props) {
422
+ const {
423
+ href,
424
+ children,
425
+ className,
426
+ target,
427
+ rel,
428
+ prefetch: prefetchProp = "none",
429
+ replace = false,
430
+ scroll = true
431
+ } = props;
432
+ const anchor = document.createElement("a");
433
+ anchor.href = href;
434
+ anchor.className = className || "";
435
+ if (target) anchor.target = target;
436
+ if (rel) anchor.rel = rel;
437
+ if (typeof children === "string") {
438
+ anchor.textContent = children;
439
+ }
440
+ const isExternal = isExternalUrl(href);
441
+ const prefetchStrategy = normalizePrefetchStrategy(prefetchProp);
442
+ if (!isExternal && target !== "_blank") {
443
+ anchor.addEventListener("click", (e) => {
444
+ handleLinkClick(href, { replace, scroll }, e);
445
+ });
446
+ }
447
+ if (!isExternal && prefetchStrategy !== "none") {
448
+ switch (prefetchStrategy) {
449
+ case "render":
450
+ prefetch(href, { priority: "low" });
451
+ break;
452
+ case "viewport":
453
+ observeForPrefetch(anchor, href);
454
+ break;
455
+ case "intent":
456
+ default:
457
+ setupIntentPrefetch(anchor, href);
458
+ break;
459
+ }
460
+ }
461
+ return anchor;
462
+ }
463
+ function useLinkProps(href, options = {}) {
464
+ const { replace = false, scroll = true, prefetch: prefetchProp = "none" } = options;
465
+ const isExternal = isExternalUrl(href);
466
+ const prefetchStrategy = normalizePrefetchStrategy(prefetchProp);
467
+ const result = {
468
+ href,
469
+ onClick: (e) => {
470
+ if (!isExternal) {
471
+ handleLinkClick(href, { replace, scroll }, e);
472
+ }
473
+ }
474
+ };
475
+ if (prefetchStrategy === "intent" && !isExternal) {
476
+ let prefetched = false;
477
+ const doPrefetch = () => {
478
+ if (!prefetched) {
479
+ prefetched = true;
480
+ prefetch(href);
481
+ }
482
+ };
483
+ result.onMouseenter = doPrefetch;
484
+ result.onFocus = doPrefetch;
485
+ }
486
+ if (prefetchStrategy === "render" && !isExternal && isBrowser3) {
487
+ prefetch(href, { priority: "low" });
488
+ }
489
+ return result;
490
+ }
491
+
492
+ // src/prefetch-links.ts
493
+ var isBrowser4 = typeof window !== "undefined";
494
+ var PrefetchPageLinks = null;
495
+ if (typeof globalThis !== "undefined") {
496
+ try {
497
+ const React = globalThis.React;
498
+ if (React?.createElement && "useEffect" in React) {
499
+ const { useEffect, useState } = React;
500
+ PrefetchPageLinks = function FlightPrefetchPageLinks({
501
+ page,
502
+ options = {}
503
+ }) {
504
+ const [shouldRender, setShouldRender] = useState(false);
505
+ useEffect(() => {
506
+ if (!isBrowser4) return;
507
+ if (isPrefetched(page)) {
508
+ return;
509
+ }
510
+ prefetch(page, {
511
+ priority: "low",
512
+ includeModules: true,
513
+ ...options
514
+ });
515
+ setShouldRender(false);
516
+ }, [page, options]);
517
+ return null;
518
+ };
519
+ }
520
+ } catch {
521
+ }
522
+ }
523
+ function prefetchPages(pages, options = {}) {
524
+ if (!isBrowser4) return;
525
+ for (const page of pages) {
526
+ if (!isPrefetched(page)) {
527
+ prefetch(page, {
528
+ priority: "low",
529
+ ...options
530
+ });
531
+ }
532
+ }
533
+ }
534
+ function prefetchWhenIdle(page, options = {}) {
535
+ if (!isBrowser4) return;
536
+ const doPrefetch = () => {
537
+ if (!isPrefetched(page)) {
538
+ prefetch(page, {
539
+ priority: "low",
540
+ ...options
541
+ });
542
+ }
543
+ };
544
+ if ("requestIdleCallback" in window) {
545
+ window.requestIdleCallback(doPrefetch, { timeout: 3e3 });
546
+ } else {
547
+ setTimeout(doPrefetch, 100);
548
+ }
549
+ }
550
+
551
+ // src/hooks.ts
552
+ var isBrowser5 = typeof window !== "undefined";
553
+ var pathSubscribers = /* @__PURE__ */ new Set();
554
+ function notifyPathChange() {
555
+ pathSubscribers.forEach((fn) => fn());
556
+ }
557
+ var historyIntercepted = false;
558
+ function interceptHistory() {
559
+ if (!isBrowser5 || historyIntercepted) return;
560
+ historyIntercepted = true;
561
+ const originalPushState = history.pushState.bind(history);
562
+ const originalReplaceState = history.replaceState.bind(history);
563
+ history.pushState = function(state, unused, url) {
564
+ originalPushState(state, unused, url);
565
+ notifyPathChange();
566
+ };
567
+ history.replaceState = function(state, unused, url) {
568
+ originalReplaceState(state, unused, url);
569
+ notifyPathChange();
570
+ };
571
+ window.addEventListener("popstate", notifyPathChange);
572
+ }
573
+ if (isBrowser5) {
574
+ interceptHistory();
575
+ }
576
+ function subscribeToPathname(callback) {
577
+ pathSubscribers.add(callback);
578
+ return () => pathSubscribers.delete(callback);
579
+ }
580
+ function getPathnameSnapshot() {
581
+ return isBrowser5 ? window.location.pathname : "/";
582
+ }
583
+ function getPathnameServerSnapshot() {
584
+ return "/";
585
+ }
586
+ function createUsePathname(useSyncExternalStore) {
587
+ return function usePathname2() {
588
+ return useSyncExternalStore(
589
+ subscribeToPathname,
590
+ getPathnameSnapshot,
591
+ getPathnameServerSnapshot
592
+ );
593
+ };
594
+ }
595
+ var useParams = () => ({});
596
+ var useSearchParams = () => [new URLSearchParams(), () => {
597
+ }];
598
+ var usePathname = () => {
599
+ if (isBrowser5) {
600
+ return window.location.pathname;
601
+ }
602
+ return "/";
603
+ };
604
+ if (typeof globalThis !== "undefined") {
605
+ try {
606
+ const React = globalThis.React;
607
+ if (React?.useSyncExternalStore) {
608
+ usePathname = createUsePathname(React.useSyncExternalStore);
609
+ }
610
+ if (React?.useState) {
611
+ const { useState, useEffect, useCallback } = React;
612
+ useParams = function useFlightParams() {
613
+ const [params, setParams] = useState({});
614
+ useEffect(() => {
615
+ const storedParams = window.__FLIGHT_PARAMS__;
616
+ if (storedParams) {
617
+ setParams(storedParams);
618
+ }
619
+ }, []);
620
+ return params;
621
+ };
622
+ useSearchParams = function useFlightSearchParams() {
623
+ const [searchParams, setSearchParamsState] = useState(
624
+ () => isBrowser5 ? new URLSearchParams(window.location.search) : new URLSearchParams()
625
+ );
626
+ useEffect(() => {
627
+ if (!isBrowser5) return;
628
+ const handleChange = () => {
629
+ setSearchParamsState(new URLSearchParams(window.location.search));
630
+ };
631
+ window.addEventListener("popstate", handleChange);
632
+ return () => window.removeEventListener("popstate", handleChange);
633
+ }, []);
634
+ const setSearchParams = useCallback((newParams) => {
635
+ if (!isBrowser5) return;
636
+ let params;
637
+ if (newParams instanceof URLSearchParams) {
638
+ params = newParams;
639
+ } else {
640
+ params = new URLSearchParams();
641
+ Object.entries(newParams).forEach(([key, value]) => {
642
+ if (Array.isArray(value)) {
643
+ value.forEach((v) => params.append(key, v));
644
+ } else {
645
+ params.set(key, value);
646
+ }
647
+ });
648
+ }
649
+ const newUrl = `${window.location.pathname}?${params.toString()}`;
650
+ window.history.pushState(null, "", newUrl);
651
+ setSearchParamsState(params);
652
+ }, []);
653
+ return [searchParams, setSearchParams];
654
+ };
655
+ }
656
+ } catch {
657
+ }
658
+ }
659
+
660
+ // src/navigate.ts
661
+ var isBrowser6 = typeof window !== "undefined";
662
+ function navigate(to, options = {}) {
663
+ const { navigate: routerNavigate } = getRouterContext();
664
+ routerNavigate(to, options);
665
+ }
666
+ function patternToRegex(pattern) {
667
+ const paramNames = [];
668
+ let regexStr = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\\\[\.\.\.(\w+)\\\]/g, (_, name) => {
669
+ paramNames.push(name);
670
+ return "(.+)";
671
+ }).replace(/\\\[(\w+)\\\]/g, (_, name) => {
672
+ paramNames.push(name);
673
+ return "([^/]+)";
674
+ }).replace(/:(\w+)/g, (_, name) => {
675
+ paramNames.push(name);
676
+ return "([^/]+)";
677
+ });
678
+ regexStr = `^${regexStr}$`;
679
+ return {
680
+ regex: new RegExp(regexStr),
681
+ paramNames
682
+ };
683
+ }
684
+ function matchRoute(pathname, pattern) {
685
+ const { regex, paramNames } = patternToRegex(pattern);
686
+ const match = pathname.match(regex);
687
+ if (!match) {
688
+ return { matched: false, params: {} };
689
+ }
690
+ const params = {};
691
+ paramNames.forEach((name, index) => {
692
+ params[name] = match[index + 1] || "";
693
+ });
694
+ return { matched: true, params };
695
+ }
696
+ function parseParams(pathname, pattern) {
697
+ const { params } = matchRoute(pathname, pattern);
698
+ return params;
699
+ }
700
+ function findRoute(pathname, routes) {
701
+ for (const route of routes) {
702
+ const { matched, params } = matchRoute(pathname, route.path);
703
+ if (matched) {
704
+ return {
705
+ route,
706
+ params,
707
+ pathname
708
+ };
709
+ }
710
+ }
711
+ return null;
712
+ }
713
+ function generatePath(pattern, params = {}) {
714
+ let path = pattern;
715
+ path = path.replace(/\[(\w+)\]/g, (_, name) => {
716
+ return params[name] || "";
717
+ });
718
+ path = path.replace(/:(\w+)/g, (_, name) => {
719
+ return params[name] || "";
720
+ });
721
+ return path;
722
+ }
723
+ function isActive(pattern) {
724
+ const { path } = getRouterContext();
725
+ const { matched } = matchRoute(path, pattern);
726
+ return matched;
727
+ }
728
+ function redirect(url) {
729
+ if (isBrowser6) {
730
+ window.location.href = url;
731
+ }
732
+ throw new Error(`Redirect to: ${url}`);
733
+ }
734
+ export {
735
+ Link,
736
+ PrefetchPageLinks,
737
+ RouterContext,
738
+ RouterProvider,
739
+ clearPrefetchCache,
740
+ createLink,
741
+ createUsePathname,
742
+ findRoute,
743
+ generatePath,
744
+ getPathnameServerSnapshot,
745
+ getPathnameSnapshot,
746
+ isActive,
747
+ isPrefetched,
748
+ matchRoute,
749
+ navigate,
750
+ observeForPrefetch,
751
+ parseParams,
752
+ prefetch,
753
+ prefetchAll,
754
+ prefetchPages,
755
+ prefetchRoute,
756
+ prefetchWhenIdle,
757
+ redirect,
758
+ setupIntentPrefetch,
759
+ subscribeToPathname,
760
+ useLinkProps,
761
+ useParams,
762
+ usePathname,
763
+ useRouter,
764
+ useSearchParams
765
+ };