@markless/router 0.5.0 → 0.6.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.
@@ -1,658 +1,2 @@
1
- import { i as SCROLL_ATTRIBUTE, n as PREFETCH_ATTRIBUTE, r as REPLACE_ATTRIBUTE, t as LINK_ATTRIBUTE } from "../../link-attributes-nY1Qkmx2.js";
2
- import { i as ROUTE_SCRIPT_TYPE, n as requestLocals, r as HTML_ATTRIBUTES_SLOT } from "../../request-locals-BFPF6Vdm.js";
3
- import { a as normalizeRouteFileId, r as matchRouteManifest, t as buildRouteManifestFromFileIds } from "../../route-manifest-Bw_nFohL.js";
4
- import { a as startFragmentNavigation, i as FRAGMENT_REQUEST_HEADER, n as FRAGMENT_PREFETCH_TTL_MS, o as appendModulePreloads, r as FRAGMENT_REGION_END, s as listenForLinkIntent, t as FRAGMENT_APPEND_END } from "../../fragment-navigation-CIiNnFFc.js";
5
- import { n as __marklessDebugBootstrapSource } from "../../debug-channel-Di6SppZv.js";
6
- import { CONTAINER_RETIRE_PROPERTY, renderToString } from "@markless/web/render-to-string";
7
- import { STREAMED_ARM_SELECTOR, STREAM_ARM_EXECUTOR_GLOBAL, renderToStream } from "@markless/web/render-to-stream";
8
- import { normalizeMarklessDevError, renderMarklessDevErrorDocument } from "@markless/bundler/dev-error";
9
- //#region src/vite/runtime/link-bridge-module.ts
10
- /** Marks the page's link bridge script: inline in dev, a module stub importing the shared bridge in a build. */
11
- const LINK_BRIDGE_SCRIPT_ATTRIBUTE = "data-markless-router-link-resumer";
12
- function linkBridgeModulePath(base, hash) {
13
- return `${base.replace(/\/?$/, "/")}_markless/links.${hash}.js`;
14
- }
15
- //#endregion
16
- //#region src/vite/runtime/page-http.ts
17
- const RESPONSE_COMMITTED_CODE = "MARKLESS_ROUTER_RESPONSE_COMMITTED";
18
- function createPageHttp(request, url) {
19
- let committed;
20
- let status;
21
- let statusText;
22
- const refuse = (write) => {
23
- throw new Error(`${RESPONSE_COMMITTED_CODE}: ${write} ran after the page's response started streaming to the browser, so its status and headers were already sent. Set http.response before the first byte goes out: in the page body, before any await that outlives the first flush.`);
24
- };
25
- const headers = new PageHeaders((write, unchanged) => {
26
- if (committed && !unchanged(committed.headers)) refuse(write);
27
- });
28
- const response = {
29
- headers,
30
- get status() {
31
- return status;
32
- },
33
- set status(value) {
34
- if (committed && value !== committed.status) refuse(`http.response.status = ${value}`);
35
- status = value;
36
- },
37
- get statusText() {
38
- return statusText;
39
- },
40
- set statusText(value) {
41
- if (committed && value !== committed.statusText) refuse(`http.response.statusText = ${JSON.stringify(value)}`);
42
- statusText = value;
43
- }
44
- };
45
- return {
46
- context: {
47
- locals: requestLocals(request) ?? {},
48
- request,
49
- response,
50
- url
51
- },
52
- commit(fallbackStatus, routerHeaders) {
53
- const sent = new Headers(routerHeaders);
54
- for (const [name, value] of headers) if (name !== "set-cookie") sent.set(name, value);
55
- for (const cookie of headers.getSetCookie()) sent.append("set-cookie", cookie);
56
- committed = {
57
- status: status ?? fallbackStatus,
58
- statusText,
59
- headers: sent
60
- };
61
- return {
62
- status: committed.status,
63
- ...statusText === void 0 ? {} : { statusText },
64
- headers: sent
65
- };
66
- }
67
- };
68
- }
69
- var PageHeaders = class extends Headers {
70
- #guard;
71
- constructor(guard) {
72
- super();
73
- this.#guard = guard;
74
- }
75
- set(name, value) {
76
- this.#guard(`http.response.headers.set(${JSON.stringify(name)})`, (sent) => name.toLowerCase() === "set-cookie" ? sent.getSetCookie().includes(value) : sent.get(name) === value);
77
- super.set(name, value);
78
- }
79
- append(name, value) {
80
- this.#guard(`http.response.headers.append(${JSON.stringify(name)})`, (sent) => name.toLowerCase() === "set-cookie" ? sent.getSetCookie().includes(value) : (sent.get(name)?.split(", ") ?? []).includes(value));
81
- if (!headerValues(this, name).includes(value)) super.append(name, value);
82
- }
83
- delete(name) {
84
- this.#guard(`http.response.headers.delete(${JSON.stringify(name)})`, (sent) => !sent.has(name));
85
- super.delete(name);
86
- }
87
- };
88
- function headerValues(headers, name) {
89
- return name.toLowerCase() === "set-cookie" ? headers.getSetCookie() : headers.get(name)?.split(", ") ?? [];
90
- }
91
- //#endregion
92
- //#region src/vite/runtime/create-server-entry.ts
93
- const DOCUMENT_CHILDREN_PLACEHOLDER = "__markless_router_document_children__";
94
- function createServerEntry(options) {
95
- const manifest = buildRouteManifestFromFileIds(options.routeFileIds);
96
- assertCurrentRouteAssets(options);
97
- const fragmentBridge = options.fragmentEntryPath !== void 0 && options.navigation !== "render" ? {
98
- entry: options.fragmentEntryPath,
99
- source: options.fragmentNavigationSource ?? startFragmentNavigation.toString(),
100
- prefetch: options.prefetch !== false,
101
- pages: manifest.routes.map((route) => route.pathname.replace(/:[^/]+/g, ":"))
102
- } : void 0;
103
- const fragmentNavigation = fragmentBridge !== void 0;
104
- const linkBridges = !options.dev && options.navigationEntryPath ? linkBridgeModules(options.base ?? "/", options.navigationEntryPath, fragmentNavigation || options.prefetch !== false, options.documentNavigation === "document", fragmentBridge) : void 0;
105
- async function fetch(request) {
106
- const url = new URL(request.url);
107
- const linkBridge = linkBridges?.find((module) => module.path === url.pathname);
108
- if (linkBridge) return new Response(linkBridge.source, { headers: {
109
- "content-type": "text/javascript;charset=utf-8",
110
- "cache-control": "public, max-age=31536000, s-maxage=31536000, immutable"
111
- } });
112
- const fragment = fragmentNavigation && request.headers.get("x-markless-fragment") === "1";
113
- if (isNitroApiPathname(url.pathname)) return new Response("Not found", { status: 404 });
114
- const match = matchRouteManifest(url.pathname, manifest);
115
- if (!match) return renderStatusPage(url, manifest.statusPages.notFound, 404, "Not found", fragment, request);
116
- try {
117
- return await renderPage(url, match.route.file, match.params, 200, fragment, request);
118
- } catch (error) {
119
- console.error("[markless-router] page render failed:", error);
120
- if (options.dev) return renderDevelopmentError(url, error);
121
- try {
122
- return await renderStatusPage(url, manifest.statusPages.error, 500, "Internal Server Error", fragment, request);
123
- } catch (errorPageError) {
124
- console.error("[markless-router] error page render failed:", errorPageError);
125
- return new Response("Internal Server Error", {
126
- status: 500,
127
- headers: { "content-type": "text/plain;charset=utf-8" }
128
- });
129
- }
130
- }
131
- }
132
- function renderDevelopmentError(url, error) {
133
- const headers = {
134
- "cache-control": "no-store",
135
- "content-type": "text/html;charset=utf-8"
136
- };
137
- try {
138
- const payload = normalizeMarklessDevError(error, { id: `navigation:${url.pathname}` });
139
- return new Response(renderMarklessDevErrorDocument(payload), {
140
- status: 500,
141
- headers
142
- });
143
- } catch {
144
- return new Response(escapeHtml(flattenError(error)), {
145
- status: 500,
146
- headers: {
147
- ...headers,
148
- "content-type": "text/plain;charset=utf-8"
149
- }
150
- });
151
- }
152
- }
153
- async function renderStatusPage(url, file, status, fallbackText, fragment, request) {
154
- if (!file) return new Response(fallbackText, { status });
155
- return renderPage(url, file, {}, status, fragment, request);
156
- }
157
- async function renderPage(url, file, params, status, fragment, request) {
158
- const loadPageModule = options.pageModuleLoaders[file];
159
- if (!loadPageModule) return new Response(`Page module not found: ${file}`, { status: 500 });
160
- const cookie = request.headers.get("cookie");
161
- const pageModule = await loadPageModule();
162
- const http = createPageHttp(request, url);
163
- const pageProps = {
164
- http: http.context,
165
- params,
166
- url: {
167
- href: url.href,
168
- pathname: url.pathname,
169
- search: url.search
170
- },
171
- status
172
- };
173
- const documentModule = options.documentModuleLoader ? await options.documentModuleLoader() : void 0;
174
- const headers = { "content-type": "text/html;charset=utf-8" };
175
- if (fragmentNavigation) headers.vary = FRAGMENT_REQUEST_HEADER;
176
- const baseArtifact = pageModule.default;
177
- const renderSsr = baseArtifact?.renderSsr ?? pageModule.marklessRenderSsr;
178
- if (!renderSsr) {
179
- const shell = await renderDocumentShell(documentModule, pageProps, "");
180
- const message = `Page module must export an Markless compiled artifact: ${escapeHtml(file)}`;
181
- return new Response(fillDocumentChildren(shell, message), http.commit(status, headers));
182
- }
183
- const documentLoads = options.documentNavigation === "document";
184
- const linkIntent = fragmentNavigation || options.prefetch !== false;
185
- const destinationPreloads = documentLoads || fragmentNavigation ? options.routeSsrModulePreloads : options.routeModulePreloads;
186
- const pageArtifact = routedPageArtifact(renderSsr, baseArtifact, pageProps, file, options.navigationEntryPath, pageStylesheets(options, file), documentModule?.default?.headInjections ?? [], linkIntent ? (html) => linkIntentPreloads(html, pageProps.url.href, manifest, destinationPreloads, fragmentNavigation) : void 0, documentLoads, fragmentBridge, linkBridges);
187
- let rendersStorage = false;
188
- const routedArtifact = {
189
- ...pageArtifact,
190
- ...options.prerenderWakeEntryPath ? { prerenderWakeModuleUrl: options.prerenderWakeEntryPath } : {},
191
- async renderSsr(renderProps, renderContext) {
192
- const output = await pageArtifact.renderSsr(renderProps, renderContext);
193
- if (documentStorageRecords(output.state).length > 0) rendersStorage = true;
194
- return output;
195
- }
196
- };
197
- const privateToVisitor = () => {
198
- if (!rendersStorage) return;
199
- headers["cache-control"] = "private";
200
- headers.vary = headers.vary ? `${headers.vary}, cookie` : "cookie";
201
- };
202
- const renderOptions = {
203
- cookie,
204
- props: pageProps,
205
- resumeModuleUrl: options.resumeEntryPath ?? baseArtifact?.resumeModuleUrl,
206
- modulePreloads: (html) => landingModulePreloads(options.routeSsrModulePreloads?.[file], linkBridges?.[html.includes("href=\"#/") ? 1 : 0].path)
207
- };
208
- if (options.render === "blocking") {
209
- const pageHtml = splitLeadingHeadHtml(await renderToString(routedArtifact, renderOptions));
210
- privateToVisitor();
211
- const shell = await renderDocumentShell(documentModule, pageProps, pageHtml.headHtml, options.importMap);
212
- const html = fragment ? fragmentDocument(shell, pageHtml.bodyHtml, documentLoads) + FRAGMENT_REGION_END : fillDocumentChildren(shell, pageHtml.bodyHtml);
213
- return new Response(html, http.commit(status, headers));
214
- }
215
- const stream = await renderToStream(routedArtifact, renderOptions);
216
- privateToVisitor();
217
- const pageHtml = splitLeadingHeadHtml(stream.shell);
218
- const shell = await renderDocumentShell(documentModule, pageProps, pageHtml.headHtml, options.importMap);
219
- if (stream.pendingArmCount === 0) {
220
- const html = fragment ? fragmentDocument(shell, pageHtml.bodyHtml, documentLoads) + FRAGMENT_REGION_END : fillDocumentChildren(shell, pageHtml.bodyHtml);
221
- return new Response(html, http.commit(status, headers));
222
- }
223
- const placeholderAt = shell.indexOf(DOCUMENT_CHILDREN_PLACEHOLDER);
224
- const prefix = fragment ? fragmentDocument(shell, pageHtml.bodyHtml, documentLoads) + FRAGMENT_REGION_END : placeholderAt === -1 ? shell : shell.slice(0, placeholderAt) + pageHtml.bodyHtml;
225
- const suffix = fragment || placeholderAt === -1 ? "" : shell.slice(placeholderAt + 37);
226
- const appendEnd = fragment ? FRAGMENT_APPEND_END : "";
227
- const encoder = new TextEncoder();
228
- const sent = http.commit(status, headers);
229
- let cancelled = false;
230
- let completed = false;
231
- const body = new ReadableStream({
232
- async start(controller) {
233
- controller.enqueue(encoder.encode(prefix));
234
- try {
235
- for await (const chunk of stream.appends()) {
236
- if (cancelled) return;
237
- controller.enqueue(encoder.encode(chunk + appendEnd));
238
- }
239
- } catch (error) {
240
- if (cancelled) return;
241
- console.error("[markless-router] streaming settle failed:", error);
242
- controller.error(error);
243
- completed = true;
244
- return;
245
- }
246
- if (cancelled) return;
247
- if (suffix) controller.enqueue(encoder.encode(suffix));
248
- controller.close();
249
- completed = true;
250
- },
251
- cancel(reason) {
252
- if (completed) return;
253
- cancelled = true;
254
- stream.cancel(reason);
255
- }
256
- });
257
- return new Response(body, sent);
258
- }
259
- return { fetch };
260
- }
261
- function flattenError(error) {
262
- if (error instanceof Error) return error.message;
263
- if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") return error.message;
264
- return String(error);
265
- }
266
- function assertCurrentRouteAssets(options) {
267
- if (options.routeStylesheets === void 0) return;
268
- const routeMaps = [
269
- ["navigation", options.routeModulePreloads ?? {}],
270
- ["SSR", options.routeSsrModulePreloads ?? {}],
271
- ["style", options.routeStylesheets]
272
- ];
273
- const expected = [...new Set(options.routeFileIds.map(normalizeRouteFileId))].filter((file) => /^pages\/.+\.(?:tsrx|mdx)$/.test(file)).toSorted();
274
- for (const [label, routes] of routeMaps) {
275
- const actual = Object.keys(routes).toSorted();
276
- if (JSON.stringify(actual) !== JSON.stringify(expected)) throw new Error(`Markless Router ${label} client-asset routes are stale; expected ${JSON.stringify(expected)}, received ${JSON.stringify(actual)}. Rebuild the client environment before the server environment.`);
277
- }
278
- }
279
- function dedupeHeadLinks(injections) {
280
- const seen = /* @__PURE__ */ new Set();
281
- return injections.filter((injection) => {
282
- const href = injection.tag === "link" ? injection.attributes?.href : void 0;
283
- if (typeof href !== "string") return true;
284
- if (seen.has(href)) return false;
285
- seen.add(href);
286
- return true;
287
- });
288
- }
289
- function pageStylesheets(options, file) {
290
- const documentHrefs = options.documentStylesheets ?? [];
291
- const routeHrefs = options.routeStylesheets?.[file];
292
- if (documentHrefs.length === 0) return routeHrefs;
293
- return [.../* @__PURE__ */ new Set([...documentHrefs, ...routeHrefs ?? []])];
294
- }
295
- function routedPageArtifact(renderSsr, baseArtifact, pageProps, file, navigationEntryPath, stylesheetHrefs, documentHeadInjections = [], intentPreloads, documentLoads = false, fragment, linkBridgeModules) {
296
- const headInjections = dedupeHeadLinks([
297
- ...documentHeadInjections,
298
- ...baseArtifact?.headInjections ?? [],
299
- ...(stylesheetHrefs ?? []).map((href) => ({
300
- tag: "link",
301
- location: "head",
302
- attributes: {
303
- rel: "stylesheet",
304
- href
305
- }
306
- }))
307
- ]);
308
- return {
309
- resumeModuleUrl: baseArtifact?.resumeModuleUrl,
310
- inlineResumerSources: baseArtifact?.inlineResumerSources,
311
- storageSeeds: baseArtifact?.storageSeeds,
312
- ...headInjections.length > 0 ? { headInjections } : {},
313
- async renderSsr(renderProps, renderContext) {
314
- const output = await renderSsr(renderProps ?? pageProps, renderContext);
315
- if (!output) return { html: "" };
316
- const routeScript = output.state || output.view ? renderRouteScript(file) : "";
317
- const linkBridge = navigationEntryPath ? renderLinkBridgeScript(navigationEntryPath, intentPreloads?.(output.html), documentLoads, fragment, output.html.includes("href=\"#/"), linkBridgeModules) : "";
318
- const stateWithProps = withPagePropsCell(output.state, pageProps);
319
- return routeScript || linkBridge || stateWithProps !== output.state ? {
320
- ...output,
321
- state: stateWithProps,
322
- html: `${output.html}${routeScript}${linkBridge}`
323
- } : output;
324
- }
325
- };
326
- }
327
- function withPagePropsCell(state, props) {
328
- if (!state || typeof state !== "object") return state;
329
- const { http: _serverOnly, ...browserProps } = props;
330
- const cells = state.cells;
331
- if (!Array.isArray(cells) || cells.some((cell) => cell?.graphNodeId === "prop:props")) return state;
332
- return {
333
- ...state,
334
- cells: [...cells, {
335
- graphNodeId: "prop:props",
336
- name: "props",
337
- valueKind: "object",
338
- directValue: browserProps
339
- }]
340
- };
341
- }
342
- function landingModulePreloads(routePreloads, linkBridgePath) {
343
- const preloads = [];
344
- addModulePreloads(preloads, /* @__PURE__ */ new Set(), routePreloads);
345
- if (linkBridgePath) preloads.push(linkBridgePath);
346
- return preloads.length > 0 ? preloads : void 0;
347
- }
348
- function routerLinkHrefs(html) {
349
- return [...html.matchAll(/<a\b(?=[^>]*\bdata-markless-router-link(?:[\s=>]|$))(?=[^>]*\bhref="([^"]*)")[^>]*>/g)].map((match) => unescapeHtmlAttribute(match[1] ?? ""));
350
- }
351
- function anchorHrefs(html) {
352
- return [...html.matchAll(/<a\b(?=[^>]*\bhref="([^"]*)")[^>]*>/g)].map((match) => unescapeHtmlAttribute(match[1] ?? ""));
353
- }
354
- function linkIntentPreloads(html, baseHref, manifest, routes, plainAnchors = false) {
355
- const destinations = Object.create(null);
356
- for (const href of plainAnchors ? anchorHrefs(html) : routerLinkHrefs(html)) {
357
- const url = parseSameOriginUrl(href, baseHref);
358
- const match = url && matchRouteManifest(url.pathname, manifest);
359
- const preloads = match && routes?.[match.route.file];
360
- if (url && preloads?.length) destinations[url.pathname] = preloads;
361
- }
362
- return destinations;
363
- }
364
- function indexedPreloads(intentPreloads) {
365
- const preloads = [];
366
- const indexes = /* @__PURE__ */ new Map();
367
- const destinations = Object.create(null);
368
- for (const [pathname, list] of Object.entries(intentPreloads)) destinations[pathname] = list.map((preload) => {
369
- const key = JSON.stringify(preload);
370
- let index = indexes.get(key);
371
- if (index === void 0) {
372
- index = preloads.push(preload) - 1;
373
- indexes.set(key, index);
374
- }
375
- return index;
376
- });
377
- return {
378
- preloads,
379
- destinations
380
- };
381
- }
382
- function parseSameOriginUrl(href, base) {
383
- try {
384
- const current = new URL(base);
385
- const url = new URL(href, current);
386
- return url.origin === current.origin ? url : void 0;
387
- } catch {
388
- return;
389
- }
390
- }
391
- function unescapeHtmlAttribute(value) {
392
- return value.replaceAll("&quot;", "\"").replaceAll("&gt;", ">").replaceAll("&lt;", "<").replaceAll("&amp;", "&");
393
- }
394
- function addModulePreloads(preloads, seen, items) {
395
- for (const preload of items ?? []) {
396
- const preloadHref = typeof preload === "string" ? preload : preload.href;
397
- if (!preloadHref || seen.has(preloadHref)) continue;
398
- seen.add(preloadHref);
399
- preloads.push(preload);
400
- }
401
- }
402
- function splitLeadingHeadHtml(html) {
403
- const containerStart = html.indexOf("<div data-async-container");
404
- if (containerStart <= 0) return {
405
- bodyHtml: html,
406
- headHtml: ""
407
- };
408
- return {
409
- bodyHtml: html.slice(containerStart),
410
- headHtml: html.slice(0, containerStart)
411
- };
412
- }
413
- async function renderDocumentShell(documentModule, pageProps, headHtml, importMap = "") {
414
- const written = {};
415
- const documentHtml = await renderDocumentModule(documentModule, {
416
- ...pageProps,
417
- children: DOCUMENT_CHILDREN_PLACEHOLDER
418
- }, written);
419
- const attributes = htmlAttributes(documentModule, pageProps, written);
420
- if (documentHtml !== void 0) return [
421
- "<!doctype html>",
422
- `<html${renderAttributes(attributes)}>`,
423
- insertImportMap(insertHeadHtml(documentHtml, headHtml), importMap),
424
- "</html>"
425
- ].join("");
426
- return [
427
- "<!doctype html>",
428
- `<html${renderAttributes(attributes)}>`,
429
- "<head>",
430
- "<meta charset=\"utf-8\">",
431
- importMap,
432
- "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
433
- headHtml,
434
- "</head>",
435
- "<body>",
436
- DOCUMENT_CHILDREN_PLACEHOLDER,
437
- "</body>",
438
- "</html>"
439
- ].join("");
440
- }
441
- function fillDocumentChildren(documentShell, children) {
442
- const at = documentShell.indexOf(DOCUMENT_CHILDREN_PLACEHOLDER);
443
- if (at === -1) return documentShell;
444
- return documentShell.slice(0, at) + children + documentShell.slice(at + 37);
445
- }
446
- function fragmentDocument(shell, bodyHtml, documentLoads) {
447
- const bodyAt = documentLoads ? -1 : shell.search(/<body[\s>]/i);
448
- const bodyOpenEnd = bodyAt === -1 ? -1 : shell.indexOf(">", bodyAt);
449
- if (bodyOpenEnd === -1) return fillDocumentChildren(shell, bodyHtml);
450
- return `${shell.slice(0, bodyOpenEnd + 1)}${bodyHtml}</body></html>`;
451
- }
452
- async function renderDocumentModule(documentModule, props, htmlAttributes) {
453
- const output = await documentModule?.default?.renderSsr?.(props, { [HTML_ATTRIBUTES_SLOT]: htmlAttributes });
454
- assertDocumentStorageFree(output);
455
- return output?.html;
456
- }
457
- function assertDocumentStorageFree(output) {
458
- const storage = documentStorageRecords(output?.state);
459
- if (storage.length === 0) return;
460
- const keys = storage.map((record) => record.key).join(", ");
461
- throw new Error(`MARKLESS_ROUTER_DOCUMENT_STORAGE_UNSUPPORTED: the document declares storage cells (${keys}), but the router serves only the document's HTML, so their state payload never reaches the browser and they can never resume. Declare storage() at module scope in a component the page renders instead.`);
462
- }
463
- function documentStorageRecords(state) {
464
- if (!state || typeof state !== "object") return [];
465
- const storage = state.storage;
466
- return Array.isArray(storage) ? storage : [];
467
- }
468
- function insertHeadHtml(documentHtml, headHtml) {
469
- if (!headHtml) return documentHtml;
470
- const headEnd = documentHtml.indexOf("</head>");
471
- if (headEnd === -1) return `${headHtml}${documentHtml}`;
472
- return `${documentHtml.slice(0, headEnd)}${headHtml}${documentHtml.slice(headEnd)}`;
473
- }
474
- function renderRouteScript(file) {
475
- return `<script type="${ROUTE_SCRIPT_TYPE}">${escapeScriptJson({ file })}<\/script>`;
476
- }
477
- function linkBridgeBody(resumeEntryPath, linkIntent, documentLoads, fragment, page) {
478
- const inline = !("module" in page);
479
- const fragmentNavigation = fragment !== void 0;
480
- const fragmentSource = fragment ? `
481
- (${fragment.source})(d,()=>(${escapeScriptJson(fragmentConfig(fragment, documentLoads))}),${inline ? escapeScriptJson(page.intentPreloads ?? {}) : "r"},(href)=>import(href));` : "";
482
- const indexed = !fragmentNavigation && linkIntent ? inline ? page.intentPreloads && indexedPreloads(page.intentPreloads) : {
483
- preloads: "preloads",
484
- destinations: "destinations"
485
- } : void 0;
486
- const intentSource = indexed ? `
487
- const preloads = ${inline ? escapeScriptJson(indexed.preloads) : "p"};
488
- const destinations = ${inline ? escapeScriptJson(indexed.destinations) : "q"};
489
- const destination = (url) => destinations[url.pathname]?.map((index) => preloads[index]);
490
- const append = (${appendModulePreloads.toString()});
491
- (${listenForLinkIntent.toString()})(d, linkAttr, (url) => {
492
- append(d, destination(url));
493
- }, prefetchAttr);` : "";
494
- const hashClickSource = fragment && !page.hashLinks ? "" : `
495
- if (d.__marklessRouterHashClickStarted) return;
496
- d.__marklessRouterHashClickStarted = true;
497
- const anchorFrom = (event) => {
498
- const target = event.composedPath && event.composedPath()[0] || event.target;
499
- return target && target.closest ? target.closest('a[href]') : target && target.parentElement && target.parentElement.closest ? target.parentElement.closest('a[href]') : null;
500
- };
501
- const sameOrigin = (href) => {
502
- try {
503
- const url = new URL(href, location.href);
504
- return url.origin === location.origin ? url : null;
505
- } catch {
506
- return null;
507
- }
508
- };
509
- d.addEventListener('click', async (event) => {
510
- if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return;
511
- const anchor = anchorFrom(event);
512
- if (!anchor || !anchor.hasAttribute(linkAttr) || anchor.hasAttribute('download')) return;
513
- const target = anchor.getAttribute('target');
514
- if (target && target !== '_self') return;
515
- if (anchor.relList && anchor.relList.contains('external')) return;
516
- const url = sameOrigin(anchor.href);
517
- if (!url${documentLoads || fragmentNavigation ? " || !url.hash.startsWith('#/')" : ""}) return;
518
- event.preventDefault();
519
- try {
520
- const mod = await import(${JSON.stringify(resumeEntryPath)});
521
- const navigate = mod.navigateMarklessRouterLink;
522
- if (typeof navigate === 'function') {
523
- await navigate({
524
- href: url.href,
525
- replace: anchor.hasAttribute(replaceAttr),
526
- scroll: anchor.getAttribute(scrollAttr) === 'manual' ? 'manual' : undefined,
527
- });
528
- } else {
529
- location.assign(url.href);
530
- }
531
- } catch (error) {
532
- setTimeout(() => { throw error; });
533
- location.assign(url.href);
534
- }
535
- }, true);`;
536
- const debugRegistration = typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__ ? `
537
- try {
538
- const key = Symbol.for('markless.debug.channel.v1.bootstrap');
539
- const md = globalThis[key];
540
- if (md) { md.router('ssr-link-bridge'); md.activate(); }
541
- } catch {}` : "";
542
- return `${fragmentSource}${fragment && !hashClickSource ? "" : `
543
- if(d.__marklessRouterLinkResumerStarted${fragment ? "&&d.__marklessRouterHashClickStarted" : ""})return;d.__marklessRouterLinkResumerStarted=true;`}${intentSource || hashClickSource ? `
544
- const linkAttr = ${JSON.stringify(LINK_ATTRIBUTE)};` : ""}${intentSource ? `
545
- const prefetchAttr = ${JSON.stringify(PREFETCH_ATTRIBUTE)};` : ""}${hashClickSource ? `
546
- const replaceAttr = ${JSON.stringify(REPLACE_ATTRIBUTE)};
547
- const scrollAttr = ${JSON.stringify(SCROLL_ATTRIBUTE)};` : ""}${intentSource}
548
- if(location.hash&&location.hash.startsWith('#/'))import(${JSON.stringify(resumeEntryPath)}).catch(()=>{});${hashClickSource}${debugRegistration}`;
549
- }
550
- function linkBridgeModules(base, resumeEntryPath, linkIntent, documentLoads, fragment) {
551
- const variant = (hashLinks) => {
552
- const source = `export default(r,p,q)=>{const d=document;${linkBridgeBody(resumeEntryPath, linkIntent, documentLoads, fragment, {
553
- module: true,
554
- hashLinks
555
- })}\n};`;
556
- return {
557
- path: linkBridgeModulePath(base, contentHash(source)),
558
- source
559
- };
560
- };
561
- const withoutHashLinks = variant(false);
562
- return [withoutHashLinks, fragment ? variant(true) : withoutHashLinks];
563
- }
564
- function contentHash(text) {
565
- let hash = 2166136261;
566
- for (let at = 0; at < text.length; at++) hash = Math.imul(hash ^ text.charCodeAt(at), 16777619);
567
- return (hash >>> 0).toString(36);
568
- }
569
- function renderLinkBridgeScript(resumeEntryPath, intentPreloads, documentLoads = false, fragment, hashLinks = false, modules) {
570
- const debugBootstrap = typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__ ? `<script data-markless-router-debug-bootstrap>${escapeInlineScript(`(() => {
571
- const d = document;
572
- const s = d.currentScript;
573
- const r = s && s.closest('[data-async-container]');
574
- if (!r) return;
575
- try { globalThis[Symbol.for('markless.debug.channel.v1.bootstrap')] = ${__marklessDebugBootstrapSource()}(r, 'ssr-inline', false); } catch {}
576
- })();`)}<\/script>` : "";
577
- const module = modules?.[hashLinks ? 1 : 0];
578
- if (module) {
579
- const indexed = !fragment && intentPreloads ? indexedPreloads(intentPreloads) : void 0;
580
- const args = fragment ? [escapeScriptJson(intentPreloads ?? {})] : indexed ? [
581
- "0",
582
- escapeScriptJson(indexed.preloads),
583
- escapeScriptJson(indexed.destinations)
584
- ] : [];
585
- return `${debugBootstrap}<script type="module" ${LINK_BRIDGE_SCRIPT_ATTRIBUTE}>${escapeInlineScript(`import b from${JSON.stringify(module.path)};b(${args.join(",")})`)}<\/script>`;
586
- }
587
- const body = linkBridgeBody(resumeEntryPath, intentPreloads !== void 0, documentLoads, fragment, {
588
- intentPreloads,
589
- hashLinks
590
- });
591
- return `${debugBootstrap}<script ${LINK_BRIDGE_SCRIPT_ATTRIBUTE}>${escapeInlineScript(`(()=>{const d=document,s=d.currentScript,r=s&&s.closest('[data-async-container]');if(!r)return;${body}
592
- })();`)}<\/script>`;
593
- }
594
- function fragmentConfig(fragment, documentLoads) {
595
- return {
596
- entry: fragment.entry,
597
- linkAttribute: LINK_ATTRIBUTE,
598
- prefetchAttribute: PREFETCH_ATTRIBUTE,
599
- replaceAttribute: REPLACE_ATTRIBUTE,
600
- scrollAttribute: SCROLL_ATTRIBUTE,
601
- routeScriptType: ROUTE_SCRIPT_TYPE,
602
- retireProperty: CONTAINER_RETIRE_PROPERTY,
603
- streamExecutorGlobal: STREAM_ARM_EXECUTOR_GLOBAL,
604
- streamedArmSelector: STREAMED_ARM_SELECTOR,
605
- requestHeader: FRAGMENT_REQUEST_HEADER,
606
- regionEndMarker: FRAGMENT_REGION_END,
607
- appendMarker: FRAGMENT_APPEND_END,
608
- documentLoads,
609
- hoverDwellMs: 20,
610
- prefetchTtlMs: FRAGMENT_PREFETCH_TTL_MS,
611
- prefetchConnections: 2,
612
- endpointPrefixes: [API_PATHNAME_PREFIX],
613
- idleFragments: 2,
614
- idleWithoutConnectionInfo: true,
615
- prefetch: fragment.prefetch,
616
- pages: fragment.pages
617
- };
618
- }
619
- function htmlAttributes(documentModule, pageProps, written) {
620
- const attributes = documentModule?.__marklessRouterHtmlAttributes?.(pageProps) ?? {
621
- lang: "en",
622
- ...written
623
- };
624
- const normalized = {};
625
- for (const [name, value] of Object.entries(attributes)) {
626
- if (value === false || value === null || value === void 0) continue;
627
- normalized[name] = String(value);
628
- }
629
- return normalized;
630
- }
631
- function renderAttributes(attributes) {
632
- return Object.entries(attributes).map(([name, value]) => ` ${name}="${escapeHtml(value)}"`).join("");
633
- }
634
- function escapeHtml(value) {
635
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
636
- }
637
- function escapeScriptJson(value) {
638
- return JSON.stringify(value).replaceAll("<", "\\u003c");
639
- }
640
- function escapeInlineScript(value) {
641
- return value.replace(/<\/script/gi, "<\\/script");
642
- }
643
- const API_PATHNAME_PREFIX = "/api";
644
- function isNitroApiPathname(pathname) {
645
- return pathname === API_PATHNAME_PREFIX || pathname.startsWith(`${API_PATHNAME_PREFIX}/`);
646
- }
647
- function insertImportMap(html, importMap) {
648
- if (!importMap) return html;
649
- const head = /<head(?:\s[^>]*)?>/i.exec(html);
650
- if (!head) return `${importMap}${html}`;
651
- const afterHead = head.index + head[0].length;
652
- const headEnd = html.indexOf("</head>", afterHead);
653
- const charset = /<meta\s[^>]*charset[^>]*>(?:<\/meta>)?/i.exec(html.slice(afterHead));
654
- const at = charset && (headEnd === -1 || afterHead + charset.index < headEnd) ? afterHead + charset.index + charset[0].length : afterHead;
655
- return `${html.slice(0, at)}${importMap}${html.slice(at)}`;
656
- }
657
- //#endregion
658
- export { createServerEntry };
1
+ import { n as routerLinkBridgeModules, t as createServerEntry } from "../../create-server-entry-DaYssO9x.js";
2
+ export { createServerEntry, routerLinkBridgeModules };