@flight-framework/router 0.0.6 → 0.0.7

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 CHANGED
@@ -1,326 +1,28 @@
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
- }
1
+ import {
2
+ PrefetchPageLinks,
3
+ RouterContext,
4
+ RouterProvider,
5
+ clearPrefetchCache,
6
+ findRoute,
7
+ generatePath,
8
+ getRouterContext,
9
+ isActive,
10
+ isPrefetched,
11
+ matchRoute,
12
+ navigate,
13
+ observeForPrefetch,
14
+ parseParams,
15
+ prefetch,
16
+ prefetchAll,
17
+ prefetchPages,
18
+ prefetchWhenIdle,
19
+ redirect,
20
+ setupIntentPrefetch,
21
+ useRouter
22
+ } from "./chunk-MO2HMSZH.js";
321
23
 
322
24
  // src/link.ts
323
- var isBrowser3 = typeof window !== "undefined";
25
+ var isBrowser = typeof window !== "undefined";
324
26
  function handleLinkClick(href, options, event) {
325
27
  if (event && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
326
28
  return;
@@ -382,7 +84,7 @@ if (typeof globalThis !== "undefined") {
382
84
  [href, isExternal, target, replace, scroll, onClick]
383
85
  );
384
86
  useEffect(() => {
385
- if (isExternal || !isBrowser3 || prefetchStrategy === "none") {
87
+ if (isExternal || !isBrowser || prefetchStrategy === "none") {
386
88
  return;
387
89
  }
388
90
  const link = linkRef.current;
@@ -483,80 +185,21 @@ function useLinkProps(href, options = {}) {
483
185
  result.onMouseenter = doPrefetch;
484
186
  result.onFocus = doPrefetch;
485
187
  }
486
- if (prefetchStrategy === "render" && !isExternal && isBrowser3) {
188
+ if (prefetchStrategy === "render" && !isExternal && isBrowser) {
487
189
  prefetch(href, { priority: "low" });
488
190
  }
489
191
  return result;
490
192
  }
491
193
 
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
194
  // src/hooks.ts
552
- var isBrowser5 = typeof window !== "undefined";
195
+ var isBrowser2 = typeof window !== "undefined";
553
196
  var pathSubscribers = /* @__PURE__ */ new Set();
554
197
  function notifyPathChange() {
555
198
  pathSubscribers.forEach((fn) => fn());
556
199
  }
557
200
  var historyIntercepted = false;
558
201
  function interceptHistory() {
559
- if (!isBrowser5 || historyIntercepted) return;
202
+ if (!isBrowser2 || historyIntercepted) return;
560
203
  historyIntercepted = true;
561
204
  const originalPushState = history.pushState.bind(history);
562
205
  const originalReplaceState = history.replaceState.bind(history);
@@ -570,7 +213,7 @@ function interceptHistory() {
570
213
  };
571
214
  window.addEventListener("popstate", notifyPathChange);
572
215
  }
573
- if (isBrowser5) {
216
+ if (isBrowser2) {
574
217
  interceptHistory();
575
218
  }
576
219
  function subscribeToPathname(callback) {
@@ -578,7 +221,7 @@ function subscribeToPathname(callback) {
578
221
  return () => pathSubscribers.delete(callback);
579
222
  }
580
223
  function getPathnameSnapshot() {
581
- return isBrowser5 ? window.location.pathname : "/";
224
+ return isBrowser2 ? window.location.pathname : "/";
582
225
  }
583
226
  function getPathnameServerSnapshot() {
584
227
  return "/";
@@ -596,7 +239,7 @@ var useParams = () => ({});
596
239
  var useSearchParams = () => [new URLSearchParams(), () => {
597
240
  }];
598
241
  var usePathname = () => {
599
- if (isBrowser5) {
242
+ if (isBrowser2) {
600
243
  return window.location.pathname;
601
244
  }
602
245
  return "/";
@@ -621,10 +264,10 @@ if (typeof globalThis !== "undefined") {
621
264
  };
622
265
  useSearchParams = function useFlightSearchParams() {
623
266
  const [searchParams, setSearchParamsState] = useState(
624
- () => isBrowser5 ? new URLSearchParams(window.location.search) : new URLSearchParams()
267
+ () => isBrowser2 ? new URLSearchParams(window.location.search) : new URLSearchParams()
625
268
  );
626
269
  useEffect(() => {
627
- if (!isBrowser5) return;
270
+ if (!isBrowser2) return;
628
271
  const handleChange = () => {
629
272
  setSearchParamsState(new URLSearchParams(window.location.search));
630
273
  };
@@ -632,7 +275,7 @@ if (typeof globalThis !== "undefined") {
632
275
  return () => window.removeEventListener("popstate", handleChange);
633
276
  }, []);
634
277
  const setSearchParams = useCallback((newParams) => {
635
- if (!isBrowser5) return;
278
+ if (!isBrowser2) return;
636
279
  let params;
637
280
  if (newParams instanceof URLSearchParams) {
638
281
  params = newParams;
@@ -656,81 +299,6 @@ if (typeof globalThis !== "undefined") {
656
299
  } catch {
657
300
  }
658
301
  }
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
302
  export {
735
303
  Link,
736
304
  PrefetchPageLinks,