@markless/router 0.4.0 → 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.
@@ -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 { debug_channel_exports as n, __exportAll as r, __marklessDebugBootstrapSource as t };
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,13 +151,18 @@ 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
- readonly prefetch?: boolean | 'intent' | 'viewport';
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
167
  readonly children?: __MarklessTypeService.Child;
152
168
  readonly class?: string;
@@ -155,37 +171,42 @@ type DefaultLinkProps = {
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 : DefaultLinkProps;
161
- interface AppLocals {}
162
- interface HttpResponse {
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: {
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: {
185
203
  readonly children?: __MarklessTypeService.Child;
186
204
  }) => unknown) & {
187
205
  renderSsr(props?: {
188
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 { type ApiRequestFileCache, type ApiRequestFileMethod, type ApiRequestFileRoute, AppLocals, DefaultLinkProps, EndpointHttpContext, Html, HttpContext, HttpResponse, Link, LinkNavigationProps, LinkProps, MARKLESS_ROUTER_ROUTE_EVENT, MarklessRouterGeneratedRoutes, MiddlewareHttpContext, PageProps, type RequestFileDefaultExport, type RequestFileDiagnostic, type RequestFileDiagnosticCode, type RequestFileParam, type RequestFileParseResult, type RequestFileTransformResult, type RouteDocumentModule, type RouteManifest, type RouteManifestMatch, type RouteManifestParam, type RouteManifestRoute, type RouteManifestStatusPages, type RoutePageModule, type RouteState, type RouteUpdate, __marklessCreateHttpContext, __marklessRouterStartSpaNavigation, buildRouteManifestFromFileIds, dispatchRouteUpdate, ensureNavigationRuntime, matchRouteManifest, normalizeRequestFileId, normalizeRequestPathname, normalizeRouteFileId, parseRequestFile, routePageProps, startRouteUpdateRenderer, transformRequestFileSource };
233
+ export type { ApiRequestFileCache, ApiRequestFileMethod, ApiRequestFileRoute, RequestFileDefaultExport, RequestFileDiagnostic, RequestFileDiagnosticCode, RequestFileParam, RequestFileParseResult, RequestFileTransformResult, RouteDocumentModule, RouteManifest, RouteManifestMatch, RouteManifestParam, RouteManifestRoute, RouteManifestStatusPages, RoutePageModule, RouteState, RouteUpdate };