@markless/router 0.3.3 → 0.5.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/debug-channel-CewnC9fH.js +2 -0
- package/dist/{debug-channel-CSzo-Zj8.js → debug-channel-Di6SppZv.js} +36 -17
- package/dist/fragment-navigation-CIiNnFFc.js +323 -0
- package/dist/index.d.ts +54 -33
- package/dist/index.js +50 -31
- package/dist/link-attributes-nY1Qkmx2.js +7 -0
- package/dist/protocol-constants-B4WxrZAS.js +26 -0
- package/dist/{request-files-C-0LLSsx.js → request-files-Ca8E0FOo.js} +3 -2
- package/dist/request-locals-BFPF6Vdm.js +28 -0
- package/dist/route-href-CGooO_g6.js +18 -0
- package/dist/{route-manifest-DPgGIyrS.js → route-manifest-Bw_nFohL.js} +9 -6
- package/dist/typescript-plugin.cjs +8 -5
- package/dist/typescript-plugin.d.ts +3 -11194
- package/dist/typescript-plugin.js +8 -5
- package/dist/vite/route-href.d.ts +2 -3
- package/dist/vite/route-href.js +1 -17
- package/dist/vite/runtime/create-route-discovery.d.ts +3 -4
- package/dist/vite/runtime/create-route-discovery.js +1 -1
- package/dist/vite/runtime/create-server-entry.d.ts +11 -5
- package/dist/vite/runtime/create-server-entry.js +354 -96
- package/dist/vite/runtime/mdx-route.d.ts +1097 -17
- package/dist/vite/runtime/mdx-route.js +1094 -31
- package/dist/vite.d.ts +10 -6
- package/dist/vite.js +782 -188
- package/package.json +7 -6
- package/src/vite/entries/client-entry.ts +10 -1
- package/src/vite/entries/fragment-entry.ts +543 -0
- package/src/vite/entries/resume-entry.ts +41 -7
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as __marklessDebugInvalidateElement, c as __marklessDebugRegisterDelegatedEvents, d as __marklessDebugSetBoundaries, f as __marklessDebugStartContainer, i as __marklessDebugDisposeContainer, l as __marklessDebugRegisterRouter, n as __marklessDebugBootstrapSource, o as __marklessDebugRecordInteraction, r as __marklessDebugChannelForTest, s as __marklessDebugRecordViolation, t as __marklessDebugActivateContainer, u as __marklessDebugResetForTest } from "./debug-channel-Di6SppZv.js";
|
|
2
|
+
export { __marklessDebugActivateContainer, __marklessDebugBootstrapSource, __marklessDebugChannelForTest, __marklessDebugDisposeContainer, __marklessDebugInvalidateElement, __marklessDebugRecordInteraction, __marklessDebugRecordViolation, __marklessDebugRegisterDelegatedEvents, __marklessDebugRegisterRouter, __marklessDebugResetForTest, __marklessDebugSetBoundaries, __marklessDebugStartContainer };
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
//#region \0rolldown/runtime.js
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __exportAll = (all, no_symbols) => {
|
|
4
|
-
let target = {};
|
|
5
|
-
for (var name in all) __defProp(target, name, {
|
|
6
|
-
get: all[name],
|
|
7
|
-
enumerable: true
|
|
8
|
-
});
|
|
9
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
-
return target;
|
|
11
|
-
};
|
|
12
|
-
//#endregion
|
|
13
1
|
//#region ../web/src/debug-channel.ts
|
|
14
|
-
var debug_channel_exports = /* @__PURE__ */ __exportAll({
|
|
15
|
-
__marklessDebugBootstrapSource: () => __marklessDebugBootstrapSource,
|
|
16
|
-
__marklessDebugRegisterRouter: () => __marklessDebugRegisterRouter
|
|
17
|
-
});
|
|
18
2
|
function installDebugChannelLayer(root, initialPhase, initiallyActive = true) {
|
|
19
3
|
const global = globalThis;
|
|
20
4
|
const previous = global.__MARKLESS_DEBUG__?.version === 1 ? global.__MARKLESS_DEBUG__ : void 0;
|
|
@@ -247,9 +231,33 @@ function safely(run) {
|
|
|
247
231
|
return;
|
|
248
232
|
}
|
|
249
233
|
}
|
|
234
|
+
function __marklessDebugRegisterDelegatedEvents(root, eventNames) {
|
|
235
|
+
safely(() => moduleRoots.get(root)?.delegated(eventNames));
|
|
236
|
+
}
|
|
250
237
|
function __marklessDebugBootstrapSource() {
|
|
251
238
|
return `((root,phase,active)=>{const controls=(${installDebugChannelLayer.toString()})(root,phase,active);return Object.freeze({record:(element,eventName,input)=>controls.record(element,eventName,input),delegated:(eventNames)=>controls.delegated(eventNames),router:(source)=>controls.router(source),activate:()=>controls.activate()})})`;
|
|
252
239
|
}
|
|
240
|
+
function __marklessDebugChannelForTest() {
|
|
241
|
+
return globalThis.__MARKLESS_DEBUG__;
|
|
242
|
+
}
|
|
243
|
+
function __marklessDebugStartContainer(root, phase, active = true) {
|
|
244
|
+
return safely(() => controlsFor(root, phase, active).start(phase, active)) ?? "";
|
|
245
|
+
}
|
|
246
|
+
function __marklessDebugActivateContainer(root) {
|
|
247
|
+
safely(() => controlsFor(root, "ssr-resume").activate());
|
|
248
|
+
}
|
|
249
|
+
function __marklessDebugDisposeContainer(root) {
|
|
250
|
+
safely(() => moduleRoots.get(root)?.dispose());
|
|
251
|
+
}
|
|
252
|
+
function __marklessDebugInvalidateElement(root, element) {
|
|
253
|
+
safely(() => moduleRoots.get(root)?.invalidate(element));
|
|
254
|
+
}
|
|
255
|
+
function __marklessDebugRecordInteraction(root, element, eventName, input) {
|
|
256
|
+
safely(() => (moduleRoots.get(root) ?? controlsFor(root, "csr")).record(element, eventName, input));
|
|
257
|
+
}
|
|
258
|
+
function __marklessDebugRecordViolation(input) {
|
|
259
|
+
safely(() => latestControls?.violation(input));
|
|
260
|
+
}
|
|
253
261
|
function __marklessDebugRegisterRouter(root, source) {
|
|
254
262
|
if (root) safely(() => (moduleRoots.get(root) ?? controlsFor(root, "ssr-resume")).router(source));
|
|
255
263
|
else safely(() => {
|
|
@@ -258,5 +266,16 @@ function __marklessDebugRegisterRouter(root, source) {
|
|
|
258
266
|
(latestControls ?? inlineControls)?.router(source);
|
|
259
267
|
});
|
|
260
268
|
}
|
|
269
|
+
function __marklessDebugSetBoundaries(root, boundaries) {
|
|
270
|
+
safely(() => moduleRoots.get(root)?.boundaries(boundaries));
|
|
271
|
+
}
|
|
272
|
+
function __marklessDebugResetForTest() {
|
|
273
|
+
const global = globalThis;
|
|
274
|
+
moduleRoots = /* @__PURE__ */ new WeakMap();
|
|
275
|
+
latestControls = void 0;
|
|
276
|
+
delete global.__MARKLESS_DEBUG__;
|
|
277
|
+
delete global[Symbol.for("markless.debug.channel.v1.bootstrap")];
|
|
278
|
+
delete global[Symbol.for("markless.debug.channel.v1.router-sources")];
|
|
279
|
+
}
|
|
261
280
|
//#endregion
|
|
262
|
-
export {
|
|
281
|
+
export { __marklessDebugInvalidateElement as a, __marklessDebugRegisterDelegatedEvents as c, __marklessDebugSetBoundaries as d, __marklessDebugStartContainer as f, __marklessDebugDisposeContainer as i, __marklessDebugRegisterRouter as l, __marklessDebugBootstrapSource as n, __marklessDebugRecordInteraction as o, __marklessDebugChannelForTest as r, __marklessDebugRecordViolation as s, __marklessDebugActivateContainer as t, __marklessDebugResetForTest as u };
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
//#region src/vite/link-intent.ts
|
|
2
|
+
function listenForLinkIntent(root, linkAttribute, preload, prefetchAttribute) {
|
|
3
|
+
const listener = (event) => {
|
|
4
|
+
if (event.defaultPrevented) return;
|
|
5
|
+
const target = event.composedPath()[0] ?? event.target;
|
|
6
|
+
const anchor = (target instanceof Element ? target : target?.parentElement)?.closest("a[href]");
|
|
7
|
+
if (!anchor?.hasAttribute(linkAttribute) || anchor.hasAttribute("download")) return;
|
|
8
|
+
if (prefetchAttribute && anchor.getAttribute(prefetchAttribute) === "none") return;
|
|
9
|
+
if (anchor.target && anchor.target !== "_self") return;
|
|
10
|
+
if (anchor.relList.contains("external")) return;
|
|
11
|
+
const url = new URL(anchor.href, anchor.ownerDocument.baseURI);
|
|
12
|
+
if (url.origin !== new URL(anchor.ownerDocument.URL).origin) return;
|
|
13
|
+
preload(url);
|
|
14
|
+
};
|
|
15
|
+
const events = [
|
|
16
|
+
"pointerover",
|
|
17
|
+
"focusin",
|
|
18
|
+
"pointerdown"
|
|
19
|
+
];
|
|
20
|
+
for (const event of events) root.addEventListener(event, listener, { passive: true });
|
|
21
|
+
return () => {
|
|
22
|
+
for (const event of events) root.removeEventListener(event, listener);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function appendModulePreloads(document, items) {
|
|
26
|
+
if (!document?.head || !items?.length) return [];
|
|
27
|
+
const absolute = (href) => new URL(href, document.baseURI).href;
|
|
28
|
+
const seen = new Set([...document.querySelectorAll("link[rel=\"modulepreload\"]")].map((link) => absolute(link.getAttribute("href") || link.href)));
|
|
29
|
+
const appended = [];
|
|
30
|
+
for (const item of items) {
|
|
31
|
+
const href = typeof item === "string" ? item : item.href;
|
|
32
|
+
if (!href || seen.has(absolute(href))) continue;
|
|
33
|
+
const link = document.createElement("link");
|
|
34
|
+
link.rel = "modulepreload";
|
|
35
|
+
link.href = href;
|
|
36
|
+
link.crossOrigin = typeof item === "string" ? "anonymous" : item.crossOrigin ?? "anonymous";
|
|
37
|
+
if (typeof item !== "string" && item.fetchPriority) link.fetchPriority = item.fetchPriority;
|
|
38
|
+
document.head.appendChild(link);
|
|
39
|
+
seen.add(absolute(href));
|
|
40
|
+
appended.push(href);
|
|
41
|
+
}
|
|
42
|
+
return appended;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/vite/fragment-navigation.ts
|
|
46
|
+
function startFragmentNavigation(d, configOf, routes, importModule) {
|
|
47
|
+
const w = d.defaultView;
|
|
48
|
+
if (!w) return;
|
|
49
|
+
const host = d;
|
|
50
|
+
const known = host.__marklessFragmentNav?.routes ?? host.__marklessFragmentWake;
|
|
51
|
+
if (known) return void Object.assign(known, routes);
|
|
52
|
+
host.__marklessFragmentWake = routes;
|
|
53
|
+
const scrolled = /* @__PURE__ */ new Set();
|
|
54
|
+
let traverse;
|
|
55
|
+
const wake = () => traverse ??= start();
|
|
56
|
+
const listen = (event) => {
|
|
57
|
+
const target = event.composedPath()[0] ?? event.target;
|
|
58
|
+
if (event.type === "scroll") {
|
|
59
|
+
if (target !== d) scrolled.add(target);
|
|
60
|
+
} else if (event.type === "popstate") wake()(event);
|
|
61
|
+
else if (!traverse && target.closest?.("a[href]")) wake();
|
|
62
|
+
};
|
|
63
|
+
for (const type of [
|
|
64
|
+
"scroll",
|
|
65
|
+
"pointerover",
|
|
66
|
+
"pointerdown",
|
|
67
|
+
"touchstart",
|
|
68
|
+
"focusin",
|
|
69
|
+
"click",
|
|
70
|
+
"popstate"
|
|
71
|
+
]) w.addEventListener(type, listen, {
|
|
72
|
+
capture: true,
|
|
73
|
+
passive: true
|
|
74
|
+
});
|
|
75
|
+
if (Object.keys(routes).length) {
|
|
76
|
+
const idle = () => w.requestIdleCallback ? w.requestIdleCallback(wake, { timeout: 3e3 }) : w.setTimeout(wake, 500);
|
|
77
|
+
if (d.readyState === "complete") idle();
|
|
78
|
+
else w.addEventListener("load", idle, { once: true });
|
|
79
|
+
}
|
|
80
|
+
const start = () => {
|
|
81
|
+
delete host.__marklessFragmentWake;
|
|
82
|
+
const config = configOf();
|
|
83
|
+
const location = w.location;
|
|
84
|
+
const connection = w.navigator.connection;
|
|
85
|
+
const pageKey = (url) => url.pathname + url.search;
|
|
86
|
+
let module;
|
|
87
|
+
const load = () => {
|
|
88
|
+
module ??= importModule(config.entry);
|
|
89
|
+
module.catch(() => module = void 0);
|
|
90
|
+
return module;
|
|
91
|
+
};
|
|
92
|
+
const pages = config.pages.map((page) => page.split("/").filter(Boolean));
|
|
93
|
+
const isPage = (pathname) => {
|
|
94
|
+
if (config.endpointPrefixes.some((prefix) => (pathname + "/").startsWith(prefix + "/"))) return false;
|
|
95
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
96
|
+
return pages.some((page) => {
|
|
97
|
+
const rest = page.indexOf("**");
|
|
98
|
+
if (rest === -1 ? page.length !== parts.length : parts.length <= rest) return false;
|
|
99
|
+
return page.every((segment, index) => segment === "**" || segment === ":" || segment === parts[index]);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
const eligible = (anchor, forClick = false) => {
|
|
103
|
+
if (!anchor || anchor.localName !== "a") return;
|
|
104
|
+
const link = anchor;
|
|
105
|
+
if (link.hasAttribute("download") || link.relList.contains("external")) return;
|
|
106
|
+
if (link.target && link.target !== "_self") return;
|
|
107
|
+
if (!forClick && link.getAttribute(config.prefetchAttribute) === "none") return;
|
|
108
|
+
let url;
|
|
109
|
+
try {
|
|
110
|
+
url = new URL(link.href, d.baseURI);
|
|
111
|
+
} catch {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (url.origin !== location.origin || url.hash.startsWith("#/")) return;
|
|
115
|
+
if (!isPage(url.pathname)) return;
|
|
116
|
+
const here = forClick ? pageKey(new URL(location.href)) : state.shown;
|
|
117
|
+
if (pageKey(url) === here && (!forClick || url.href.includes("#"))) return;
|
|
118
|
+
return url;
|
|
119
|
+
};
|
|
120
|
+
const anchorOf = (event) => {
|
|
121
|
+
const target = event.composedPath?.()[0] ?? event.target;
|
|
122
|
+
return (target && target.nodeType === 1 ? target : target?.parentElement)?.closest("a[href]");
|
|
123
|
+
};
|
|
124
|
+
const preloadCode = (url, low) => {
|
|
125
|
+
const present = new Set([...d.querySelectorAll("link[rel=\"modulepreload\"]")].map((link) => link.href));
|
|
126
|
+
for (const item of [config.entry, ...state.routes[url.pathname] ?? []]) {
|
|
127
|
+
const entry = typeof item === "string" ? { href: item } : item;
|
|
128
|
+
const href = new URL(entry.href, d.baseURI).href;
|
|
129
|
+
if (present.has(href)) continue;
|
|
130
|
+
present.add(href);
|
|
131
|
+
const link = d.createElement("link");
|
|
132
|
+
link.rel = "modulepreload";
|
|
133
|
+
link.href = href;
|
|
134
|
+
link.crossOrigin = entry.crossOrigin ?? "anonymous";
|
|
135
|
+
if (low) link.fetchPriority = "low";
|
|
136
|
+
d.head.appendChild(link);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const parse = (url, text) => {
|
|
140
|
+
const at = text.indexOf(config.regionEndMarker);
|
|
141
|
+
const prefix = at === -1 ? text : text.slice(0, at);
|
|
142
|
+
return {
|
|
143
|
+
url,
|
|
144
|
+
prefix,
|
|
145
|
+
doc: new DOMParser().parseFromString(prefix, "text/html"),
|
|
146
|
+
tail: at === -1 ? "" : text.slice(at + config.regionEndMarker.length)
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
const request = (url, speculative) => {
|
|
150
|
+
const controller = new AbortController();
|
|
151
|
+
const read = async () => {
|
|
152
|
+
const response = await w.fetch(url.href, {
|
|
153
|
+
credentials: "same-origin",
|
|
154
|
+
headers: speculative ? {
|
|
155
|
+
[config.requestHeader]: "1",
|
|
156
|
+
purpose: "prefetch"
|
|
157
|
+
} : { [config.requestHeader]: "1" },
|
|
158
|
+
priority: speculative ? "auto" : "high",
|
|
159
|
+
signal: controller.signal
|
|
160
|
+
});
|
|
161
|
+
const type = response.headers.get("content-type") ?? "";
|
|
162
|
+
if (!response.ok || !type.includes("text/html") || !response.body) throw new Error(`MARKLESS_ROUTER_FRAGMENT_UNAVAILABLE: ${response.status}`);
|
|
163
|
+
const finalUrl = new URL(response.url || url.href);
|
|
164
|
+
if (finalUrl.origin !== location.origin) throw new Error("MARKLESS_ROUTER_FRAGMENT_OFF_ORIGIN");
|
|
165
|
+
finalUrl.hash = url.hash;
|
|
166
|
+
const reader = response.body.getReader();
|
|
167
|
+
const decoder = new TextDecoder();
|
|
168
|
+
let text = "";
|
|
169
|
+
for (;;) {
|
|
170
|
+
if (text.includes(config.regionEndMarker)) return {
|
|
171
|
+
...parse(finalUrl.href, text),
|
|
172
|
+
rest: reader
|
|
173
|
+
};
|
|
174
|
+
const chunk = await reader.read();
|
|
175
|
+
if (chunk.done) {
|
|
176
|
+
entry.open = false;
|
|
177
|
+
return parse(finalUrl.href, text + decoder.decode());
|
|
178
|
+
}
|
|
179
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const entry = {
|
|
183
|
+
at: w.performance.now(),
|
|
184
|
+
result: read(),
|
|
185
|
+
open: true,
|
|
186
|
+
abort: () => {
|
|
187
|
+
entry.open = false;
|
|
188
|
+
controller.abort();
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
entry.result.catch(() => entry.open = false);
|
|
192
|
+
return entry;
|
|
193
|
+
};
|
|
194
|
+
const prefetch = (url) => {
|
|
195
|
+
const key = pageKey(url);
|
|
196
|
+
if (!config.prefetch || key === state.shown) return;
|
|
197
|
+
const existing = state.prefetches.get(key);
|
|
198
|
+
if (existing && w.performance.now() - existing.at < config.prefetchTtlMs) return;
|
|
199
|
+
existing?.abort();
|
|
200
|
+
const open = [...state.prefetches].filter(([, entry]) => entry.open);
|
|
201
|
+
for (const [stale, entry] of open.slice(0, Math.max(0, open.length + 1 - config.prefetchConnections))) {
|
|
202
|
+
state.prefetches.delete(stale);
|
|
203
|
+
entry.abort();
|
|
204
|
+
}
|
|
205
|
+
const entry = request(url, true);
|
|
206
|
+
state.prefetches.set(key, entry);
|
|
207
|
+
w.setTimeout(() => {
|
|
208
|
+
if (state.prefetches.get(key) !== entry) return;
|
|
209
|
+
state.prefetches.delete(key);
|
|
210
|
+
entry.abort();
|
|
211
|
+
}, config.prefetchTtlMs);
|
|
212
|
+
preloadCode(url, true);
|
|
213
|
+
};
|
|
214
|
+
let scanning = false;
|
|
215
|
+
const rescan = () => {
|
|
216
|
+
if (!state.idleDownloads || scanning) return;
|
|
217
|
+
scanning = true;
|
|
218
|
+
const run = () => {
|
|
219
|
+
scanning = false;
|
|
220
|
+
const visible = /* @__PURE__ */ new Map();
|
|
221
|
+
for (const anchor of d.querySelectorAll("a[href]")) {
|
|
222
|
+
const url = eligible(anchor);
|
|
223
|
+
if (!url || !state.routes[url.pathname]) continue;
|
|
224
|
+
const box = anchor.getBoundingClientRect();
|
|
225
|
+
if (!box.width || box.bottom < 0 || box.top > w.innerHeight) continue;
|
|
226
|
+
if (box.right < 0 || box.left > w.innerWidth) continue;
|
|
227
|
+
const hit = d.elementFromPoint(box.left + box.width / 2, box.top + box.height / 2);
|
|
228
|
+
if (!hit || hit !== anchor && !anchor.contains(hit)) continue;
|
|
229
|
+
visible.set(pageKey(url), url);
|
|
230
|
+
}
|
|
231
|
+
const fragments = visible.size <= config.idleFragments * 2 ? config.idleFragments : 0;
|
|
232
|
+
let fetched = 0;
|
|
233
|
+
for (const [key, url] of visible) {
|
|
234
|
+
preloadCode(url, true);
|
|
235
|
+
if (fetched >= fragments || state.prefetches.has(key)) continue;
|
|
236
|
+
fetched += 1;
|
|
237
|
+
state.prefetch(url);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const idle = () => w.requestIdleCallback ? w.requestIdleCallback(run, { timeout: 3e3 }) : w.setTimeout(run, 500);
|
|
241
|
+
if (d.readyState === "complete") idle();
|
|
242
|
+
else w.addEventListener("load", idle, { once: true });
|
|
243
|
+
};
|
|
244
|
+
const state = {
|
|
245
|
+
config,
|
|
246
|
+
routes,
|
|
247
|
+
prefetches: /* @__PURE__ */ new Map(),
|
|
248
|
+
visited: /* @__PURE__ */ new Map(),
|
|
249
|
+
shown: pageKey(new URL(location.href)),
|
|
250
|
+
eligible,
|
|
251
|
+
preloadCode,
|
|
252
|
+
request,
|
|
253
|
+
prefetch,
|
|
254
|
+
parse,
|
|
255
|
+
idleDownloads: config.prefetch && (connection ? connection.effectiveType === "4g" && connection.saveData !== true : config.idleWithoutConnectionInfo),
|
|
256
|
+
rescan,
|
|
257
|
+
scrolled
|
|
258
|
+
};
|
|
259
|
+
host.__marklessFragmentNav = state;
|
|
260
|
+
let restTimer = 0;
|
|
261
|
+
let previous;
|
|
262
|
+
d.addEventListener("pointermove", (event) => {
|
|
263
|
+
const pointer = event;
|
|
264
|
+
if (pointer.pointerType !== "mouse") return;
|
|
265
|
+
const point = {
|
|
266
|
+
x: pointer.clientX,
|
|
267
|
+
y: pointer.clientY,
|
|
268
|
+
t: pointer.timeStamp
|
|
269
|
+
};
|
|
270
|
+
const gap = previous ? point.t - previous.t : 0;
|
|
271
|
+
const creeping = !!previous && gap > 0 && Math.hypot(point.x - previous.x, point.y - previous.y) / gap < .1;
|
|
272
|
+
previous = point;
|
|
273
|
+
w.clearTimeout(restTimer);
|
|
274
|
+
const url = eligible(anchorOf(event));
|
|
275
|
+
if (!url) return;
|
|
276
|
+
if (creeping) prefetch(url);
|
|
277
|
+
else restTimer = w.setTimeout(() => prefetch(url), Math.max(config.hoverDwellMs, gap * 1.5));
|
|
278
|
+
}, { passive: true });
|
|
279
|
+
d.addEventListener("pointerout", () => w.clearTimeout(restTimer), { passive: true });
|
|
280
|
+
for (const type of [
|
|
281
|
+
"pointerdown",
|
|
282
|
+
"touchstart",
|
|
283
|
+
"focusin"
|
|
284
|
+
]) d.addEventListener(type, (event) => {
|
|
285
|
+
w.clearTimeout(restTimer);
|
|
286
|
+
const url = eligible(anchorOf(event));
|
|
287
|
+
if (url) prefetch(url);
|
|
288
|
+
}, { passive: true });
|
|
289
|
+
const follow = (routedPhase) => (event) => {
|
|
290
|
+
const mouse = event;
|
|
291
|
+
if (mouse.defaultPrevented || mouse.button !== 0) return;
|
|
292
|
+
if (mouse.metaKey || mouse.altKey || mouse.ctrlKey || mouse.shiftKey) return;
|
|
293
|
+
const anchor = anchorOf(event);
|
|
294
|
+
if (!anchor || anchor.hasAttribute(config.linkAttribute) !== routedPhase) return;
|
|
295
|
+
const url = eligible(anchor, true);
|
|
296
|
+
if (!url) return;
|
|
297
|
+
const current = new URL(location.href);
|
|
298
|
+
w.clearTimeout(restTimer);
|
|
299
|
+
event.preventDefault();
|
|
300
|
+
const replace = anchor.hasAttribute(config.replaceAttribute) || url.href === current.href;
|
|
301
|
+
const manual = anchor.getAttribute(config.scrollAttribute) === "manual";
|
|
302
|
+
load().then((entry) => entry.navigate(d, url, replace ? "replace" : "push", manual ? "manual" : "reset"), () => location.assign(url.href));
|
|
303
|
+
};
|
|
304
|
+
d.addEventListener("click", follow(true), true);
|
|
305
|
+
d.addEventListener("click", follow(false));
|
|
306
|
+
rescan();
|
|
307
|
+
return (event) => {
|
|
308
|
+
const url = new URL(location.href);
|
|
309
|
+
if (url.hash.startsWith("#/") || pageKey(url) === state.shown) return;
|
|
310
|
+
const saved = event.state?.__marklessFragment;
|
|
311
|
+
load().then((entry) => entry.navigate(d, url, "traverse", saved ?? {
|
|
312
|
+
x: 0,
|
|
313
|
+
y: 0
|
|
314
|
+
}), () => location.reload());
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
const FRAGMENT_REQUEST_HEADER = "x-markless-fragment";
|
|
319
|
+
const FRAGMENT_REGION_END = "<!--markless:region-end-->";
|
|
320
|
+
const FRAGMENT_APPEND_END = "<!--markless:append-end-->";
|
|
321
|
+
const FRAGMENT_PREFETCH_TTL_MS = 3e4;
|
|
322
|
+
//#endregion
|
|
323
|
+
export { startFragmentNavigation as a, FRAGMENT_REQUEST_HEADER as i, FRAGMENT_PREFETCH_TTL_MS as n, appendModulePreloads as o, FRAGMENT_REGION_END as r, listenForLinkIntent as s, FRAGMENT_APPEND_END as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createExternalDelegateStructureTokens, createExternalDelegateView } from "@markless/web/fns/external-delegate";
|
|
2
|
-
|
|
2
|
+
//#region src/route-dom.d.ts
|
|
3
|
+
declare const HTML_ATTRIBUTES_SLOT = "__marklessRouterHtmlAttributes";
|
|
4
|
+
//#endregion
|
|
3
5
|
//#region src/route-manifest.d.ts
|
|
4
6
|
interface RouteManifestRoute {
|
|
5
7
|
readonly pathname: string;
|
|
@@ -23,10 +25,10 @@ interface RouteManifestMatch {
|
|
|
23
25
|
readonly route: RouteManifestRoute;
|
|
24
26
|
readonly params: Readonly<Record<string, string>>;
|
|
25
27
|
}
|
|
26
|
-
declare function buildRouteManifestFromFileIds(fileIds: readonly string[]): RouteManifest;
|
|
27
|
-
declare function normalizeRequestPathname(pathname: string): string;
|
|
28
|
-
declare function normalizeRouteFileId(fileId: string): string;
|
|
29
|
-
declare function matchRouteManifest(pathname: string, manifest: RouteManifest): RouteManifestMatch | undefined;
|
|
28
|
+
export declare function buildRouteManifestFromFileIds(fileIds: readonly string[]): RouteManifest;
|
|
29
|
+
export declare function normalizeRequestPathname(pathname: string): string;
|
|
30
|
+
export declare function normalizeRouteFileId(fileId: string): string;
|
|
31
|
+
export declare function matchRouteManifest(pathname: string, manifest: RouteManifest): RouteManifestMatch | undefined;
|
|
30
32
|
//#endregion
|
|
31
33
|
//#region src/request-files.d.ts
|
|
32
34
|
type RequestFileDiagnosticCode = 'http-method-export' | 'invalid-cache-metadata' | 'missing-default-function';
|
|
@@ -74,12 +76,12 @@ type RequestFileParseResult = {
|
|
|
74
76
|
readonly file: string;
|
|
75
77
|
readonly kind: 'middleware';
|
|
76
78
|
};
|
|
77
|
-
declare function parseRequestFile(fileId: string, sourceText: string): RequestFileParseResult;
|
|
78
|
-
declare function normalizeRequestFileId(fileId: string): string;
|
|
79
|
-
declare function transformRequestFileSource(fileId: string, sourceText: string): RequestFileTransformResult | undefined;
|
|
79
|
+
export declare function parseRequestFile(fileId: string, sourceText: string): RequestFileParseResult;
|
|
80
|
+
export declare function normalizeRequestFileId(fileId: string): string;
|
|
81
|
+
export declare function transformRequestFileSource(fileId: string, sourceText: string): RequestFileTransformResult | undefined;
|
|
80
82
|
//#endregion
|
|
81
83
|
//#region src/route-renderer.d.ts
|
|
82
|
-
declare function startRouteUpdateRenderer(document?: Document): void;
|
|
84
|
+
export declare function startRouteUpdateRenderer(document?: Document): void;
|
|
83
85
|
//#endregion
|
|
84
86
|
//#region src/spa-navigation.d.ts
|
|
85
87
|
type MarklessRouterNavigationRuntime = Pick<Navigation, 'addEventListener' | 'navigate'>;
|
|
@@ -97,17 +99,19 @@ interface MarklessRouterNavigationPolyfillModule {
|
|
|
97
99
|
}
|
|
98
100
|
interface StartSpaNavigationOptions {
|
|
99
101
|
readonly documentModuleLoader?: () => Promise<unknown>;
|
|
102
|
+
readonly documentNavigation?: 'client' | 'document';
|
|
100
103
|
readonly loadPolyfill?: () => Promise<MarklessRouterNavigationPolyfillModule>;
|
|
101
104
|
readonly pageModuleLoaders: Record<string, () => Promise<unknown>>;
|
|
102
105
|
readonly preloadRouteModule?: (file: string) => unknown;
|
|
103
106
|
readonly routeFileIds: readonly string[];
|
|
104
107
|
readonly window?: MarklessRouterNavigationWindow;
|
|
105
108
|
}
|
|
106
|
-
declare function __marklessRouterStartSpaNavigation(options: StartSpaNavigationOptions): Promise<void>;
|
|
107
|
-
declare function ensureNavigationRuntime(runtimeWindow?: MarklessRouterNavigationWindow, loadPolyfill?: () => Promise<MarklessRouterNavigationPolyfillModule>): Promise<MarklessRouterNavigationRuntime>;
|
|
109
|
+
export declare function __marklessRouterStartSpaNavigation(options: StartSpaNavigationOptions): Promise<void>;
|
|
110
|
+
export declare function ensureNavigationRuntime(runtimeWindow?: MarklessRouterNavigationWindow, loadPolyfill?: () => Promise<MarklessRouterNavigationPolyfillModule>): Promise<MarklessRouterNavigationRuntime>;
|
|
108
111
|
//#endregion
|
|
109
112
|
//#region src/route-state.d.ts
|
|
110
|
-
declare const MARKLESS_ROUTER_ROUTE_EVENT = "marklessrouternavigate";
|
|
113
|
+
export declare const MARKLESS_ROUTER_ROUTE_EVENT = "marklessrouternavigate";
|
|
114
|
+
export declare const MARKLESS_ROUTER_RENDERED_EVENT = "marklessrouterrendered";
|
|
111
115
|
interface RouteState {
|
|
112
116
|
readonly file: string;
|
|
113
117
|
readonly params: Readonly<Record<string, string>>;
|
|
@@ -127,11 +131,18 @@ interface RouteUpdate {
|
|
|
127
131
|
readonly signal?: AbortSignal;
|
|
128
132
|
readonly onRendered?: () => void;
|
|
129
133
|
}
|
|
130
|
-
declare function routePageProps(route: RouteState): PageProps
|
|
131
|
-
declare function dispatchRouteUpdate(document: Document, update: RouteUpdate): void;
|
|
134
|
+
export declare function routePageProps(route: RouteState): Omit<PageProps, 'http'>;
|
|
135
|
+
export declare function dispatchRouteUpdate(document: Document, update: RouteUpdate): void;
|
|
132
136
|
//#endregion
|
|
133
137
|
//#region src/index.d.ts
|
|
134
|
-
interface PageProps<Params extends object = Readonly<Record<string, string
|
|
138
|
+
export interface PageProps<Params extends object = Readonly<Record<string, string>>, Locals extends object = AppLocals> {
|
|
139
|
+
/**
|
|
140
|
+
* The request and the response being written, the same object API routes and middleware get.
|
|
141
|
+
* It exists only while the page renders on the server: the compiler refuses any read of it from
|
|
142
|
+
* code that runs in the browser. Set `http.response.status` and `http.response.headers` before the
|
|
143
|
+
* first byte is sent; a later write that changes them throws `MARKLESS_ROUTER_RESPONSE_COMMITTED`.
|
|
144
|
+
*/
|
|
145
|
+
readonly http: HttpContext<Locals>;
|
|
135
146
|
readonly params: Readonly<Params>;
|
|
136
147
|
readonly url: {
|
|
137
148
|
readonly href: string;
|
|
@@ -140,52 +151,62 @@ interface PageProps<Params extends object = Readonly<Record<string, string>>> {
|
|
|
140
151
|
};
|
|
141
152
|
readonly status: number;
|
|
142
153
|
}
|
|
143
|
-
interface MarklessRouterGeneratedRoutes {}
|
|
144
|
-
interface LinkNavigationProps {
|
|
145
|
-
|
|
154
|
+
export interface MarklessRouterGeneratedRoutes {}
|
|
155
|
+
export interface LinkNavigationProps {
|
|
156
|
+
/**
|
|
157
|
+
* `false`: nothing for this link downloads before it is clicked. Only page routes are ever
|
|
158
|
+
* prefetched (with a `Purpose: prefetch` header), never `/api` endpoints; set `false` on a link to
|
|
159
|
+
* a page whose GET has side effects (a one-time token, a counter) so only the click requests it.
|
|
160
|
+
*/
|
|
161
|
+
readonly prefetch?: boolean;
|
|
146
162
|
readonly replace?: boolean;
|
|
147
163
|
readonly scroll?: boolean;
|
|
148
164
|
}
|
|
149
|
-
type DefaultLinkProps = {
|
|
165
|
+
export type DefaultLinkProps = {
|
|
150
166
|
readonly href?: string;
|
|
151
|
-
readonly children?:
|
|
167
|
+
readonly children?: __MarklessTypeService.Child;
|
|
152
168
|
readonly class?: string;
|
|
153
169
|
readonly id?: string;
|
|
154
170
|
readonly target?: string;
|
|
155
171
|
readonly rel?: string;
|
|
156
172
|
readonly [prop: string]: unknown;
|
|
157
173
|
} & LinkNavigationProps;
|
|
158
|
-
type LinkProps = MarklessRouterGeneratedRoutes extends {
|
|
174
|
+
export type LinkProps = MarklessRouterGeneratedRoutes extends {
|
|
159
175
|
readonly link: infer Props;
|
|
160
|
-
} ? Props
|
|
161
|
-
|
|
162
|
-
|
|
176
|
+
} ? Props & Pick<DefaultLinkProps, 'children' | 'class' | 'id' | 'target' | 'rel'> & {
|
|
177
|
+
readonly [prop: string]: unknown;
|
|
178
|
+
} : DefaultLinkProps;
|
|
179
|
+
export interface AppLocals {}
|
|
180
|
+
export interface HttpResponse {
|
|
163
181
|
readonly headers: Headers;
|
|
164
182
|
status?: number;
|
|
165
183
|
statusText?: string;
|
|
166
184
|
}
|
|
167
|
-
interface HttpContext<Locals extends object = AppLocals> {
|
|
185
|
+
export interface HttpContext<Locals extends object = AppLocals> {
|
|
168
186
|
readonly locals: Locals;
|
|
169
187
|
readonly request: Request;
|
|
170
188
|
readonly response: HttpResponse;
|
|
171
189
|
readonly url: URL;
|
|
172
190
|
}
|
|
173
|
-
interface EndpointHttpContext<Params extends object = Readonly<Record<string, string>>, Locals extends object = AppLocals> extends HttpContext<Locals> {
|
|
191
|
+
export interface EndpointHttpContext<Params extends object = Readonly<Record<string, string>>, Locals extends object = AppLocals> extends HttpContext<Locals> {
|
|
174
192
|
readonly params: Readonly<Params>;
|
|
175
193
|
}
|
|
176
|
-
interface MiddlewareHttpContext<Locals extends object = AppLocals> extends HttpContext<Locals> {}
|
|
194
|
+
export interface MiddlewareHttpContext<Locals extends object = AppLocals> extends HttpContext<Locals> {}
|
|
177
195
|
type RuntimeHttpEvent = {
|
|
178
196
|
readonly context?: Record<string, unknown>;
|
|
179
197
|
readonly req?: Request;
|
|
180
198
|
readonly res?: HttpResponse;
|
|
181
199
|
readonly url?: URL;
|
|
182
200
|
};
|
|
183
|
-
declare function __marklessCreateHttpContext<Params extends object = Readonly<Record<string, string>>, Locals extends object = AppLocals>(event: RuntimeHttpEvent): EndpointHttpContext<Params, Locals>;
|
|
184
|
-
declare const Html: ((props: {
|
|
185
|
-
readonly children?:
|
|
201
|
+
export declare function __marklessCreateHttpContext<Params extends object = Readonly<Record<string, string>>, Locals extends object = AppLocals>(event: RuntimeHttpEvent): EndpointHttpContext<Params, Locals>;
|
|
202
|
+
export declare const Html: ((props: {
|
|
203
|
+
readonly children?: __MarklessTypeService.Child;
|
|
186
204
|
}) => unknown) & {
|
|
187
205
|
renderSsr(props?: {
|
|
188
|
-
readonly children?:
|
|
206
|
+
readonly children?: __MarklessTypeService.Child;
|
|
207
|
+
readonly [name: string]: unknown;
|
|
208
|
+
}, renderContext?: {
|
|
209
|
+
readonly [HTML_ATTRIBUTES_SLOT]?: Record<string, unknown>;
|
|
189
210
|
}): {
|
|
190
211
|
html: string;
|
|
191
212
|
elementCount: number;
|
|
@@ -207,6 +228,6 @@ type LinkComponent = {
|
|
|
207
228
|
view: ReturnType<typeof createExternalDelegateView>;
|
|
208
229
|
};
|
|
209
230
|
};
|
|
210
|
-
declare const Link: LinkComponent;
|
|
231
|
+
export declare const Link: LinkComponent;
|
|
211
232
|
//#endregion
|
|
212
|
-
export {
|
|
233
|
+
export type { ApiRequestFileCache, ApiRequestFileMethod, ApiRequestFileRoute, RequestFileDefaultExport, RequestFileDiagnostic, RequestFileDiagnosticCode, RequestFileParam, RequestFileParseResult, RequestFileTransformResult, RouteDocumentModule, RouteManifest, RouteManifestMatch, RouteManifestParam, RouteManifestRoute, RouteManifestStatusPages, RoutePageModule, RouteState, RouteUpdate };
|