@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/LICENSE +21 -0
- package/README.md +488 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +765 -0
- package/dist/preact/index.d.ts +77 -0
- package/dist/preact/index.js +654 -0
- package/dist/prefetch-DRp54Q7z.d.ts +373 -0
- package/dist/react/index.d.ts +116 -0
- package/dist/react/index.js +696 -0
- package/dist/solid/index.d.ts +107 -0
- package/dist/solid/index.js +625 -0
- package/dist/svelte/index.d.ts +118 -0
- package/dist/svelte/index.js +595 -0
- package/dist/vue/index.d.ts +147 -0
- package/dist/vue/index.js +658 -0
- package/package.json +95 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { P as PrefetchStrategy, a as RouterContextValue } from '../prefetch-DRp54Q7z.js';
|
|
2
|
+
export { L as LinkProps, N as NavigateOptions, d as PrefetchOptions, c as PrefetchPriority, R as RouteParams, b as RouterProviderProps, S as SearchParams, w as clearPrefetchCache, l as findRoute, o as generatePath, B as getRouterContext, D as initRouter, q as isActive, v as isPrefetched, m as matchRoute, n as navigate, x as observeForPrefetch, p as parseParams, s as prefetch, t as prefetchAll, z as prefetchPages, A as prefetchWhenIdle, r as redirect, y as setupIntentPrefetch, C as subscribe } from '../prefetch-DRp54Q7z.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Svelte Link Component
|
|
6
|
+
*
|
|
7
|
+
* Provides client-side navigation with prefetching support for Svelte 5.
|
|
8
|
+
* Uses Svelte 5 runes ($props) for reactive properties.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates a Link component action for Svelte.
|
|
13
|
+
* Use this with Svelte's `use:` directive for full control.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```svelte
|
|
17
|
+
* <script>
|
|
18
|
+
* import { linkAction } from '@flightdev/router/svelte';
|
|
19
|
+
* </script>
|
|
20
|
+
*
|
|
21
|
+
* <a href="/docs" use:linkAction={{ prefetch: 'intent' }}>Docs</a>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare function linkAction(node: HTMLAnchorElement, options?: {
|
|
25
|
+
prefetch?: boolean | PrefetchStrategy;
|
|
26
|
+
replace?: boolean;
|
|
27
|
+
scroll?: boolean;
|
|
28
|
+
}): {
|
|
29
|
+
destroy(): void;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Link component factory for Svelte.
|
|
33
|
+
* Returns props to spread on an anchor element.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```svelte
|
|
37
|
+
* <script>
|
|
38
|
+
* import { createLinkProps } from '@flightdev/router/svelte';
|
|
39
|
+
*
|
|
40
|
+
* const linkProps = createLinkProps('/docs', { prefetch: 'intent' });
|
|
41
|
+
* </script>
|
|
42
|
+
*
|
|
43
|
+
* <a {...linkProps}>Documentation</a>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
declare function createLinkProps(href: string, options?: {
|
|
47
|
+
prefetch?: boolean | PrefetchStrategy;
|
|
48
|
+
replace?: boolean;
|
|
49
|
+
scroll?: boolean;
|
|
50
|
+
}): {
|
|
51
|
+
href: string;
|
|
52
|
+
onclick: (e: MouseEvent) => void;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Simple Link function component.
|
|
56
|
+
* For Svelte 5, use $props() in your .svelte file instead.
|
|
57
|
+
* This provides the core logic that can be used in a .svelte wrapper.
|
|
58
|
+
*/
|
|
59
|
+
declare const Link: {
|
|
60
|
+
createProps: typeof createLinkProps;
|
|
61
|
+
action: typeof linkAction;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Svelte Stores for Router
|
|
66
|
+
*
|
|
67
|
+
* Provides Svelte stores for reactive router state.
|
|
68
|
+
* Compatible with Svelte 4 and Svelte 5.
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Router store - provides reactive access to router context
|
|
73
|
+
*/
|
|
74
|
+
declare const routerStore: {
|
|
75
|
+
subscribe(callback: (value: RouterContextValue) => void): () => boolean;
|
|
76
|
+
set(newValue: RouterContextValue): void;
|
|
77
|
+
get(): RouterContextValue;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Pathname store - provides reactive access to current pathname
|
|
81
|
+
*/
|
|
82
|
+
declare const pathnameStore: {
|
|
83
|
+
subscribe(callback: (value: string) => void): () => boolean;
|
|
84
|
+
set(newValue: string): void;
|
|
85
|
+
get(): string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Get current router context (non-reactive for SSR)
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```svelte
|
|
92
|
+
* <script>
|
|
93
|
+
* import { getRouter } from '@flightdev/router/svelte';
|
|
94
|
+
*
|
|
95
|
+
* const { navigate, back, forward } = getRouter();
|
|
96
|
+
* </script>
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
declare function getRouter(): RouterContextValue;
|
|
100
|
+
/**
|
|
101
|
+
* Get current pathname (non-reactive for SSR)
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```svelte
|
|
105
|
+
* <script>
|
|
106
|
+
* import { getPathname } from '@flightdev/router/svelte';
|
|
107
|
+
*
|
|
108
|
+
* const pathname = getPathname();
|
|
109
|
+
* </script>
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
declare function getPathname(): string;
|
|
113
|
+
/**
|
|
114
|
+
* Get current search params (non-reactive for SSR)
|
|
115
|
+
*/
|
|
116
|
+
declare function getSearchParams(): URLSearchParams;
|
|
117
|
+
|
|
118
|
+
export { Link, PrefetchStrategy, RouterContextValue, getPathname, getRouter, getSearchParams, pathnameStore, routerStore };
|
|
@@ -0,0 +1,595 @@
|
|
|
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/navigate.ts
|
|
165
|
+
var isBrowser2 = typeof window !== "undefined";
|
|
166
|
+
function navigate(to, options = {}) {
|
|
167
|
+
const { navigate: routerNavigate } = getRouterContext();
|
|
168
|
+
routerNavigate(to, options);
|
|
169
|
+
}
|
|
170
|
+
function patternToRegex(pattern) {
|
|
171
|
+
const paramNames = [];
|
|
172
|
+
let regexStr = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\\\[\.\.\.(\w+)\\\]/g, (_, name) => {
|
|
173
|
+
paramNames.push(name);
|
|
174
|
+
return "(.+)";
|
|
175
|
+
}).replace(/\\\[(\w+)\\\]/g, (_, name) => {
|
|
176
|
+
paramNames.push(name);
|
|
177
|
+
return "([^/]+)";
|
|
178
|
+
}).replace(/:(\w+)/g, (_, name) => {
|
|
179
|
+
paramNames.push(name);
|
|
180
|
+
return "([^/]+)";
|
|
181
|
+
});
|
|
182
|
+
regexStr = `^${regexStr}$`;
|
|
183
|
+
return {
|
|
184
|
+
regex: new RegExp(regexStr),
|
|
185
|
+
paramNames
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function matchRoute(pathname, pattern) {
|
|
189
|
+
const { regex, paramNames } = patternToRegex(pattern);
|
|
190
|
+
const match = pathname.match(regex);
|
|
191
|
+
if (!match) {
|
|
192
|
+
return { matched: false, params: {} };
|
|
193
|
+
}
|
|
194
|
+
const params = {};
|
|
195
|
+
paramNames.forEach((name, index) => {
|
|
196
|
+
params[name] = match[index + 1] || "";
|
|
197
|
+
});
|
|
198
|
+
return { matched: true, params };
|
|
199
|
+
}
|
|
200
|
+
function parseParams(pathname, pattern) {
|
|
201
|
+
const { params } = matchRoute(pathname, pattern);
|
|
202
|
+
return params;
|
|
203
|
+
}
|
|
204
|
+
function findRoute(pathname, routes) {
|
|
205
|
+
for (const route of routes) {
|
|
206
|
+
const { matched, params } = matchRoute(pathname, route.path);
|
|
207
|
+
if (matched) {
|
|
208
|
+
return {
|
|
209
|
+
route,
|
|
210
|
+
params,
|
|
211
|
+
pathname
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
function generatePath(pattern, params = {}) {
|
|
218
|
+
let path = pattern;
|
|
219
|
+
path = path.replace(/\[(\w+)\]/g, (_, name) => {
|
|
220
|
+
return params[name] || "";
|
|
221
|
+
});
|
|
222
|
+
path = path.replace(/:(\w+)/g, (_, name) => {
|
|
223
|
+
return params[name] || "";
|
|
224
|
+
});
|
|
225
|
+
return path;
|
|
226
|
+
}
|
|
227
|
+
function isActive(pattern) {
|
|
228
|
+
const { path } = getRouterContext();
|
|
229
|
+
const { matched } = matchRoute(path, pattern);
|
|
230
|
+
return matched;
|
|
231
|
+
}
|
|
232
|
+
function redirect(url) {
|
|
233
|
+
if (isBrowser2) {
|
|
234
|
+
window.location.href = url;
|
|
235
|
+
}
|
|
236
|
+
throw new Error(`Redirect to: ${url}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/prefetch.ts
|
|
240
|
+
var isBrowser3 = typeof window !== "undefined";
|
|
241
|
+
var supportsIntersectionObserver = isBrowser3 && "IntersectionObserver" in window;
|
|
242
|
+
var prefetchedUrls = /* @__PURE__ */ new Set();
|
|
243
|
+
var prefetchingUrls = /* @__PURE__ */ new Set();
|
|
244
|
+
var viewportObservers = /* @__PURE__ */ new Map();
|
|
245
|
+
function prefetch(href, options = {}) {
|
|
246
|
+
if (!isBrowser3) return;
|
|
247
|
+
const {
|
|
248
|
+
priority = "auto",
|
|
249
|
+
includeModules = true,
|
|
250
|
+
includeData = false
|
|
251
|
+
} = options;
|
|
252
|
+
const url = normalizeUrl(href);
|
|
253
|
+
if (prefetchedUrls.has(url) || prefetchingUrls.has(url)) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
prefetchingUrls.add(url);
|
|
257
|
+
createPrefetchLink(url, "document", priority);
|
|
258
|
+
if (includeModules) {
|
|
259
|
+
prefetchModules(url, priority);
|
|
260
|
+
}
|
|
261
|
+
if (includeData) {
|
|
262
|
+
prefetchData(url, priority);
|
|
263
|
+
}
|
|
264
|
+
prefetchedUrls.add(url);
|
|
265
|
+
prefetchingUrls.delete(url);
|
|
266
|
+
}
|
|
267
|
+
function prefetchAll(hrefs, options = {}) {
|
|
268
|
+
for (const href of hrefs) {
|
|
269
|
+
prefetch(href, options);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function isPrefetched(href) {
|
|
273
|
+
return prefetchedUrls.has(normalizeUrl(href));
|
|
274
|
+
}
|
|
275
|
+
function clearPrefetchCache() {
|
|
276
|
+
prefetchedUrls.clear();
|
|
277
|
+
prefetchingUrls.clear();
|
|
278
|
+
}
|
|
279
|
+
function createPrefetchLink(href, as, priority) {
|
|
280
|
+
if (!isBrowser3) return null;
|
|
281
|
+
const existing = document.querySelector(
|
|
282
|
+
`link[rel="prefetch"][href="${href}"], link[rel="modulepreload"][href="${href}"]`
|
|
283
|
+
);
|
|
284
|
+
if (existing) return existing;
|
|
285
|
+
const link = document.createElement("link");
|
|
286
|
+
if (as === "script") {
|
|
287
|
+
link.rel = "modulepreload";
|
|
288
|
+
} else {
|
|
289
|
+
link.rel = "prefetch";
|
|
290
|
+
link.as = as;
|
|
291
|
+
}
|
|
292
|
+
link.href = href;
|
|
293
|
+
if (priority !== "auto" && "fetchPriority" in link) {
|
|
294
|
+
link.fetchPriority = priority;
|
|
295
|
+
}
|
|
296
|
+
if (priority === "low" && "requestIdleCallback" in window) {
|
|
297
|
+
window.requestIdleCallback(() => {
|
|
298
|
+
document.head.appendChild(link);
|
|
299
|
+
});
|
|
300
|
+
} else {
|
|
301
|
+
document.head.appendChild(link);
|
|
302
|
+
}
|
|
303
|
+
return link;
|
|
304
|
+
}
|
|
305
|
+
function prefetchModules(href, priority) {
|
|
306
|
+
const manifest = window.__FLIGHT_MANIFEST__;
|
|
307
|
+
if (!manifest?.routes) return;
|
|
308
|
+
const routeModules = manifest.routes[href];
|
|
309
|
+
if (!routeModules) return;
|
|
310
|
+
for (const module of routeModules) {
|
|
311
|
+
createPrefetchLink(module, "script", priority);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function prefetchData(href, priority) {
|
|
315
|
+
const dataUrl = `/_flight/data${href === "/" ? "/index" : href}.json`;
|
|
316
|
+
createPrefetchLink(dataUrl, "fetch", priority);
|
|
317
|
+
}
|
|
318
|
+
var sharedObserver = null;
|
|
319
|
+
var observerCallbacks = /* @__PURE__ */ new Map();
|
|
320
|
+
function getViewportObserver() {
|
|
321
|
+
if (!supportsIntersectionObserver) return null;
|
|
322
|
+
if (!sharedObserver) {
|
|
323
|
+
sharedObserver = new IntersectionObserver(
|
|
324
|
+
(entries) => {
|
|
325
|
+
for (const entry of entries) {
|
|
326
|
+
if (entry.isIntersecting) {
|
|
327
|
+
const callback = observerCallbacks.get(entry.target);
|
|
328
|
+
if (callback) {
|
|
329
|
+
callback();
|
|
330
|
+
sharedObserver?.unobserve(entry.target);
|
|
331
|
+
observerCallbacks.delete(entry.target);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
// Start prefetching when link is 25% visible or within 100px of viewport
|
|
338
|
+
rootMargin: "100px",
|
|
339
|
+
threshold: 0.25
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
return sharedObserver;
|
|
344
|
+
}
|
|
345
|
+
function observeForPrefetch(element, href) {
|
|
346
|
+
if (!supportsIntersectionObserver) {
|
|
347
|
+
return () => {
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
const observer = getViewportObserver();
|
|
351
|
+
if (!observer) return () => {
|
|
352
|
+
};
|
|
353
|
+
const callback = () => {
|
|
354
|
+
prefetch(href, { priority: "low" });
|
|
355
|
+
};
|
|
356
|
+
observerCallbacks.set(element, callback);
|
|
357
|
+
observer.observe(element);
|
|
358
|
+
const cleanup = () => {
|
|
359
|
+
observer.unobserve(element);
|
|
360
|
+
observerCallbacks.delete(element);
|
|
361
|
+
viewportObservers.delete(element);
|
|
362
|
+
};
|
|
363
|
+
viewportObservers.set(element, cleanup);
|
|
364
|
+
return cleanup;
|
|
365
|
+
}
|
|
366
|
+
function setupIntentPrefetch(element, href) {
|
|
367
|
+
if (!isBrowser3) return () => {
|
|
368
|
+
};
|
|
369
|
+
let prefetchTriggered = false;
|
|
370
|
+
const handleIntent = () => {
|
|
371
|
+
if (!prefetchTriggered) {
|
|
372
|
+
prefetchTriggered = true;
|
|
373
|
+
prefetch(href, { priority: "auto" });
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
element.addEventListener("mouseenter", handleIntent, { passive: true });
|
|
377
|
+
element.addEventListener("focus", handleIntent, { passive: true });
|
|
378
|
+
element.addEventListener("touchstart", handleIntent, { passive: true });
|
|
379
|
+
return () => {
|
|
380
|
+
element.removeEventListener("mouseenter", handleIntent);
|
|
381
|
+
element.removeEventListener("focus", handleIntent);
|
|
382
|
+
element.removeEventListener("touchstart", handleIntent);
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
function normalizeUrl(href) {
|
|
386
|
+
if (isBrowser3 && !href.startsWith("http")) {
|
|
387
|
+
try {
|
|
388
|
+
const url = new URL(href, window.location.origin);
|
|
389
|
+
return url.pathname + url.search;
|
|
390
|
+
} catch {
|
|
391
|
+
return href;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return href;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/prefetch-links.ts
|
|
398
|
+
var isBrowser4 = typeof window !== "undefined";
|
|
399
|
+
var PrefetchPageLinks = null;
|
|
400
|
+
if (typeof globalThis !== "undefined") {
|
|
401
|
+
try {
|
|
402
|
+
const React = globalThis.React;
|
|
403
|
+
if (React?.createElement && "useEffect" in React) {
|
|
404
|
+
const { useEffect, useState } = React;
|
|
405
|
+
PrefetchPageLinks = function FlightPrefetchPageLinks({
|
|
406
|
+
page,
|
|
407
|
+
options = {}
|
|
408
|
+
}) {
|
|
409
|
+
const [shouldRender, setShouldRender] = useState(false);
|
|
410
|
+
useEffect(() => {
|
|
411
|
+
if (!isBrowser4) return;
|
|
412
|
+
if (isPrefetched(page)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
prefetch(page, {
|
|
416
|
+
priority: "low",
|
|
417
|
+
includeModules: true,
|
|
418
|
+
...options
|
|
419
|
+
});
|
|
420
|
+
setShouldRender(false);
|
|
421
|
+
}, [page, options]);
|
|
422
|
+
return null;
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
} catch {
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function prefetchPages(pages, options = {}) {
|
|
429
|
+
if (!isBrowser4) return;
|
|
430
|
+
for (const page of pages) {
|
|
431
|
+
if (!isPrefetched(page)) {
|
|
432
|
+
prefetch(page, {
|
|
433
|
+
priority: "low",
|
|
434
|
+
...options
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function prefetchWhenIdle(page, options = {}) {
|
|
440
|
+
if (!isBrowser4) return;
|
|
441
|
+
const doPrefetch = () => {
|
|
442
|
+
if (!isPrefetched(page)) {
|
|
443
|
+
prefetch(page, {
|
|
444
|
+
priority: "low",
|
|
445
|
+
...options
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
if ("requestIdleCallback" in window) {
|
|
450
|
+
window.requestIdleCallback(doPrefetch, { timeout: 3e3 });
|
|
451
|
+
} else {
|
|
452
|
+
setTimeout(doPrefetch, 100);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// src/svelte/Link.ts
|
|
457
|
+
var isBrowser5 = typeof window !== "undefined";
|
|
458
|
+
function isExternalUrl(href) {
|
|
459
|
+
if (!href) return false;
|
|
460
|
+
return href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//") || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("javascript:") || href.startsWith("#");
|
|
461
|
+
}
|
|
462
|
+
function normalizePrefetchStrategy(prefetchProp) {
|
|
463
|
+
if (prefetchProp === true) return "intent";
|
|
464
|
+
if (prefetchProp === false || prefetchProp === void 0) return "none";
|
|
465
|
+
return prefetchProp;
|
|
466
|
+
}
|
|
467
|
+
function linkAction(node, options = {}) {
|
|
468
|
+
const { prefetch: prefetchProp = "none", replace = false, scroll = true } = options;
|
|
469
|
+
const href = node.getAttribute("href") || "";
|
|
470
|
+
const isExternal = isExternalUrl(href);
|
|
471
|
+
const prefetchStrategy = normalizePrefetchStrategy(prefetchProp);
|
|
472
|
+
let cleanup;
|
|
473
|
+
const handleClick = (e) => {
|
|
474
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
475
|
+
if (e.button !== 0) return;
|
|
476
|
+
if (isExternal || node.target === "_blank") return;
|
|
477
|
+
e.preventDefault();
|
|
478
|
+
const { navigate: navigate2 } = getRouterContext();
|
|
479
|
+
navigate2(href, { replace, scroll });
|
|
480
|
+
};
|
|
481
|
+
node.addEventListener("click", handleClick);
|
|
482
|
+
if (!isExternal && prefetchStrategy !== "none" && isBrowser5) {
|
|
483
|
+
switch (prefetchStrategy) {
|
|
484
|
+
case "render":
|
|
485
|
+
prefetch(href, { priority: "low" });
|
|
486
|
+
break;
|
|
487
|
+
case "viewport":
|
|
488
|
+
cleanup = observeForPrefetch(node, href);
|
|
489
|
+
break;
|
|
490
|
+
case "intent":
|
|
491
|
+
default:
|
|
492
|
+
cleanup = setupIntentPrefetch(node, href);
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
destroy() {
|
|
498
|
+
node.removeEventListener("click", handleClick);
|
|
499
|
+
cleanup?.();
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function createLinkProps(href, options = {}) {
|
|
504
|
+
const { replace = false, scroll = true } = options;
|
|
505
|
+
const isExternal = isExternalUrl(href);
|
|
506
|
+
return {
|
|
507
|
+
href,
|
|
508
|
+
onclick: (e) => {
|
|
509
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
510
|
+
if (e.button !== 0) return;
|
|
511
|
+
if (isExternal) return;
|
|
512
|
+
e.preventDefault();
|
|
513
|
+
const { navigate: navigate2 } = getRouterContext();
|
|
514
|
+
navigate2(href, { replace, scroll });
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
var Link = {
|
|
519
|
+
createProps: createLinkProps,
|
|
520
|
+
action: linkAction
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// src/svelte/stores.ts
|
|
524
|
+
var isBrowser6 = typeof window !== "undefined";
|
|
525
|
+
function createStore(initialValue) {
|
|
526
|
+
let value = initialValue;
|
|
527
|
+
const subscribers2 = /* @__PURE__ */ new Set();
|
|
528
|
+
return {
|
|
529
|
+
subscribe(callback) {
|
|
530
|
+
subscribers2.add(callback);
|
|
531
|
+
callback(value);
|
|
532
|
+
return () => subscribers2.delete(callback);
|
|
533
|
+
},
|
|
534
|
+
set(newValue) {
|
|
535
|
+
value = newValue;
|
|
536
|
+
subscribers2.forEach((cb) => cb(value));
|
|
537
|
+
},
|
|
538
|
+
get() {
|
|
539
|
+
return value;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
var routerStore = createStore(getRouterContext());
|
|
544
|
+
var pathnameStore = createStore(
|
|
545
|
+
isBrowser6 ? window.location.pathname : "/"
|
|
546
|
+
);
|
|
547
|
+
var searchParamsStore = createStore(
|
|
548
|
+
isBrowser6 ? new URLSearchParams(window.location.search) : new URLSearchParams()
|
|
549
|
+
);
|
|
550
|
+
if (isBrowser6) {
|
|
551
|
+
subscribe((ctx) => {
|
|
552
|
+
routerStore.set(ctx);
|
|
553
|
+
pathnameStore.set(ctx.path);
|
|
554
|
+
searchParamsStore.set(ctx.searchParams);
|
|
555
|
+
});
|
|
556
|
+
window.addEventListener("popstate", () => {
|
|
557
|
+
pathnameStore.set(window.location.pathname);
|
|
558
|
+
searchParamsStore.set(new URLSearchParams(window.location.search));
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
function getRouter() {
|
|
562
|
+
return routerStore.get();
|
|
563
|
+
}
|
|
564
|
+
function getPathname() {
|
|
565
|
+
return pathnameStore.get();
|
|
566
|
+
}
|
|
567
|
+
function getSearchParams() {
|
|
568
|
+
return searchParamsStore.get();
|
|
569
|
+
}
|
|
570
|
+
export {
|
|
571
|
+
Link,
|
|
572
|
+
clearPrefetchCache,
|
|
573
|
+
findRoute,
|
|
574
|
+
generatePath,
|
|
575
|
+
getPathname,
|
|
576
|
+
getRouter,
|
|
577
|
+
getRouterContext,
|
|
578
|
+
getSearchParams,
|
|
579
|
+
initRouter,
|
|
580
|
+
isActive,
|
|
581
|
+
isPrefetched,
|
|
582
|
+
matchRoute,
|
|
583
|
+
navigate,
|
|
584
|
+
observeForPrefetch,
|
|
585
|
+
parseParams,
|
|
586
|
+
pathnameStore,
|
|
587
|
+
prefetch,
|
|
588
|
+
prefetchAll,
|
|
589
|
+
prefetchPages,
|
|
590
|
+
prefetchWhenIdle,
|
|
591
|
+
redirect,
|
|
592
|
+
routerStore,
|
|
593
|
+
setupIntentPrefetch,
|
|
594
|
+
subscribe
|
|
595
|
+
};
|