@vertz/ui-server 0.2.30 → 0.2.32

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.
@@ -6,7 +6,7 @@ import {
6
6
  setGlobalSSRTimeout,
7
7
  ssrStorage,
8
8
  toVNode
9
- } from "./chunk-gcwqkynf.js";
9
+ } from "./chunk-ybftdw1r.js";
10
10
 
11
11
  // src/html-serializer.ts
12
12
  var VOID_ELEMENTS = new Set([
@@ -68,6 +68,43 @@ function serializeToHtml(node) {
68
68
  return `<${tag}${attrStr}>${childrenHtml}</${tag}>`;
69
69
  }
70
70
 
71
+ // src/ssr-access-set.ts
72
+ function getAccessSetForSSR(jwtPayload) {
73
+ if (!jwtPayload)
74
+ return null;
75
+ const acl = jwtPayload.acl;
76
+ if (!acl)
77
+ return null;
78
+ if (acl.overflow)
79
+ return null;
80
+ if (!acl.set)
81
+ return null;
82
+ return {
83
+ entitlements: Object.fromEntries(Object.entries(acl.set.entitlements).map(([name, check]) => [
84
+ name,
85
+ {
86
+ allowed: check.allowed,
87
+ reasons: check.reasons ?? [],
88
+ ...check.reason ? { reason: check.reason } : {},
89
+ ...check.meta ? { meta: check.meta } : {}
90
+ }
91
+ ])),
92
+ flags: acl.set.flags,
93
+ plan: acl.set.plan,
94
+ plans: acl.set.plans ?? {},
95
+ computedAt: acl.set.computedAt
96
+ };
97
+ }
98
+ function createAccessSetScript(accessSet, nonce) {
99
+ const json = JSON.stringify(accessSet);
100
+ const escaped = json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
101
+ const nonceAttr = nonce ? ` nonce="${escapeAttr2(nonce)}"` : "";
102
+ return `<script${nonceAttr}>window.__VERTZ_ACCESS_SET__=${escaped}</script>`;
103
+ }
104
+ function escapeAttr2(s) {
105
+ return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
106
+ }
107
+
71
108
  // src/slot-placeholder.ts
72
109
  var slotCounter = 0;
73
110
  function resetSlotCounter() {
@@ -493,6 +530,179 @@ data: ${safeSerialize(entry)}
493
530
  });
494
531
  }
495
532
 
533
+ // src/ssr-session.ts
534
+ function createSessionScript(session, nonce) {
535
+ const json = JSON.stringify(session);
536
+ const escaped = json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
537
+ const nonceAttr = nonce ? ` nonce="${escapeAttr3(nonce)}"` : "";
538
+ return `<script${nonceAttr}>window.__VERTZ_SESSION__=${escaped}</script>`;
539
+ }
540
+ function escapeAttr3(s) {
541
+ return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
542
+ }
543
+
544
+ // src/template-split.ts
545
+ function splitTemplate(template, options) {
546
+ let processed = template;
547
+ if (options?.inlineCSS) {
548
+ processed = inlineCSSAssets(processed, options.inlineCSS);
549
+ }
550
+ const outletMarker = "<!--ssr-outlet-->";
551
+ const outletIndex = processed.indexOf(outletMarker);
552
+ if (outletIndex !== -1) {
553
+ return {
554
+ headTemplate: processed.slice(0, outletIndex),
555
+ tailTemplate: processed.slice(outletIndex + outletMarker.length)
556
+ };
557
+ }
558
+ const appDivMatch = processed.match(/<div[^>]*id="app"[^>]*>/);
559
+ if (appDivMatch && appDivMatch.index != null) {
560
+ const openTag = appDivMatch[0];
561
+ const contentStart = appDivMatch.index + openTag.length;
562
+ const closingIndex = findMatchingDivClose(processed, contentStart);
563
+ return {
564
+ headTemplate: processed.slice(0, contentStart),
565
+ tailTemplate: processed.slice(closingIndex)
566
+ };
567
+ }
568
+ throw new Error('Could not find <!--ssr-outlet--> or <div id="app"> in the HTML template. ' + "The template must contain one of these markers for SSR content injection.");
569
+ }
570
+ function findMatchingDivClose(html, startAfterOpen) {
571
+ let depth = 1;
572
+ let i = startAfterOpen;
573
+ const len = html.length;
574
+ while (i < len && depth > 0) {
575
+ if (html[i] === "<") {
576
+ if (html.startsWith("</div>", i)) {
577
+ depth--;
578
+ if (depth === 0)
579
+ return i;
580
+ i += 6;
581
+ } else if (html.startsWith("<div", i) && /^<div[\s>]/.test(html.slice(i, i + 5))) {
582
+ depth++;
583
+ i += 4;
584
+ } else {
585
+ i++;
586
+ }
587
+ } else {
588
+ i++;
589
+ }
590
+ }
591
+ return len;
592
+ }
593
+ function inlineCSSAssets(html, inlineCSS) {
594
+ let result = html;
595
+ for (const [href, css] of Object.entries(inlineCSS)) {
596
+ const escapedHref = href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
597
+ const linkPattern = new RegExp(`<link[^>]*href=["']${escapedHref}["'][^>]*>`);
598
+ const safeCss = css.replace(/<\//g, "<\\/");
599
+ result = result.replace(linkPattern, `<style data-vertz-css>${safeCss}</style>`);
600
+ }
601
+ result = result.replace(/<link\s+rel="stylesheet"\s+href="([^"]+)"[^>]*>/g, (match, href) => `<link rel="stylesheet" href="${href}" media="print" onload="this.media='all'">
602
+ <noscript>${match}</noscript>`);
603
+ return result;
604
+ }
605
+
606
+ // src/ssr-handler-shared.ts
607
+ function sanitizeLinkHref(href) {
608
+ return href.replace(/[<>,;\s"']/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`);
609
+ }
610
+ function sanitizeLinkParam(value) {
611
+ return value.replace(/[^a-zA-Z0-9/_.-]/g, "");
612
+ }
613
+ function buildLinkHeader(items) {
614
+ return items.map((item) => {
615
+ const parts = [
616
+ `<${sanitizeLinkHref(item.href)}>`,
617
+ "rel=preload",
618
+ `as=${sanitizeLinkParam(item.as)}`
619
+ ];
620
+ if (item.type)
621
+ parts.push(`type=${sanitizeLinkParam(item.type)}`);
622
+ if (item.crossorigin)
623
+ parts.push("crossorigin");
624
+ return parts.join("; ");
625
+ }).join(", ");
626
+ }
627
+ function buildModulepreloadTags(paths) {
628
+ return paths.map((p) => `<link rel="modulepreload" href="${escapeAttr(p)}">`).join(`
629
+ `);
630
+ }
631
+ function resolveRouteModulepreload(routeChunkManifest, matchedPatterns, fallback) {
632
+ if (routeChunkManifest && matchedPatterns?.length) {
633
+ const chunkPaths = new Set;
634
+ for (const pattern of matchedPatterns) {
635
+ const chunks = routeChunkManifest.routes[pattern];
636
+ if (chunks) {
637
+ for (const chunk of chunks) {
638
+ chunkPaths.add(chunk);
639
+ }
640
+ }
641
+ }
642
+ if (chunkPaths.size > 0) {
643
+ return buildModulepreloadTags([...chunkPaths]);
644
+ }
645
+ }
646
+ return fallback;
647
+ }
648
+ function preprocessInlineCSS(template, inlineCSS) {
649
+ let result = template;
650
+ for (const [href, css] of Object.entries(inlineCSS)) {
651
+ const escapedHref = href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
652
+ const linkPattern = new RegExp(`<link[^>]*href=["']${escapedHref}["'][^>]*>`);
653
+ const safeCss = css.replace(/<\//g, "<\\/");
654
+ result = result.replace(linkPattern, `<style data-vertz-css>${safeCss}</style>`);
655
+ }
656
+ return result;
657
+ }
658
+ function precomputeHandlerState(options) {
659
+ const { module, inlineCSS, fallbackMetrics, modulepreload, progressiveHTML } = options;
660
+ let template = options.template;
661
+ if (inlineCSS) {
662
+ template = preprocessInlineCSS(template, inlineCSS);
663
+ }
664
+ let linkHeader;
665
+ if (module.theme) {
666
+ const compiled = compileThemeCached(module.theme, fallbackMetrics);
667
+ if (compiled.preloadItems.length > 0) {
668
+ linkHeader = buildLinkHeader(compiled.preloadItems);
669
+ }
670
+ }
671
+ const modulepreloadTags = modulepreload?.length ? buildModulepreloadTags(modulepreload) : undefined;
672
+ const splitResult = progressiveHTML ? splitTemplate(template) : undefined;
673
+ if (splitResult && module.theme) {
674
+ splitResult.headTemplate = splitResult.headTemplate.replace(/<link\s+rel="stylesheet"\s+href="([^"]+)"[^>]*>/g, (match, href) => `<link rel="stylesheet" href="${href}" media="print" onload="this.media='all'">
675
+ <noscript>${match}</noscript>`);
676
+ }
677
+ return { template, linkHeader, modulepreloadTags, splitResult };
678
+ }
679
+ async function resolveSession(request, sessionResolver, nonce) {
680
+ let sessionScript = "";
681
+ let ssrAuth;
682
+ try {
683
+ const sessionResult = await sessionResolver(request);
684
+ if (sessionResult) {
685
+ ssrAuth = {
686
+ status: "authenticated",
687
+ user: sessionResult.session.user,
688
+ expiresAt: sessionResult.session.expiresAt
689
+ };
690
+ const scripts = [];
691
+ scripts.push(createSessionScript(sessionResult.session, nonce));
692
+ if (sessionResult.accessSet != null) {
693
+ scripts.push(createAccessSetScript(sessionResult.accessSet, nonce));
694
+ }
695
+ sessionScript = scripts.join(`
696
+ `);
697
+ } else {
698
+ ssrAuth = { status: "unauthenticated" };
699
+ }
700
+ } catch (resolverErr) {
701
+ console.warn("[Server] Session resolver failed:", resolverErr instanceof Error ? resolverErr.message : resolverErr);
702
+ }
703
+ return { sessionScript, ssrAuth };
704
+ }
705
+
496
706
  // src/ssr-access-evaluator.ts
497
707
  function toPrefetchSession(ssrAuth, accessSet) {
498
708
  if (!ssrAuth || ssrAuth.status !== "authenticated" || !ssrAuth.user) {
@@ -536,78 +746,6 @@ function evaluateAccessRule(rule, session) {
536
746
  }
537
747
  }
538
748
 
539
- // src/ssr-access-set.ts
540
- function getAccessSetForSSR(jwtPayload) {
541
- if (!jwtPayload)
542
- return null;
543
- const acl = jwtPayload.acl;
544
- if (!acl)
545
- return null;
546
- if (acl.overflow)
547
- return null;
548
- if (!acl.set)
549
- return null;
550
- return {
551
- entitlements: Object.fromEntries(Object.entries(acl.set.entitlements).map(([name, check]) => [
552
- name,
553
- {
554
- allowed: check.allowed,
555
- reasons: check.reasons ?? [],
556
- ...check.reason ? { reason: check.reason } : {},
557
- ...check.meta ? { meta: check.meta } : {}
558
- }
559
- ])),
560
- flags: acl.set.flags,
561
- plan: acl.set.plan,
562
- plans: acl.set.plans ?? {},
563
- computedAt: acl.set.computedAt
564
- };
565
- }
566
- function createAccessSetScript(accessSet, nonce) {
567
- const json = JSON.stringify(accessSet);
568
- const escaped = json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
569
- const nonceAttr = nonce ? ` nonce="${escapeAttr2(nonce)}"` : "";
570
- return `<script${nonceAttr}>window.__VERTZ_ACCESS_SET__=${escaped}</script>`;
571
- }
572
- function escapeAttr2(s) {
573
- return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
574
- }
575
-
576
- // src/ssr-route-matcher.ts
577
- function matchUrlToPatterns(url, patterns) {
578
- const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
579
- const matches = [];
580
- for (const pattern of patterns) {
581
- const result = matchPattern(path, pattern);
582
- if (result) {
583
- matches.push(result);
584
- }
585
- }
586
- matches.sort((a, b) => {
587
- const aSegments = a.pattern.split("/").length;
588
- const bSegments = b.pattern.split("/").length;
589
- return aSegments - bSegments;
590
- });
591
- return matches;
592
- }
593
- function matchPattern(path, pattern) {
594
- const pathSegments = path.split("/").filter(Boolean);
595
- const patternSegments = pattern.split("/").filter(Boolean);
596
- if (patternSegments.length > pathSegments.length)
597
- return;
598
- const params = {};
599
- for (let i = 0;i < patternSegments.length; i++) {
600
- const seg = patternSegments[i];
601
- const val = pathSegments[i];
602
- if (seg.startsWith(":")) {
603
- params[seg.slice(1)] = val;
604
- } else if (seg !== val) {
605
- return;
606
- }
607
- }
608
- return { pattern, params };
609
- }
610
-
611
749
  // src/ssr-manifest-prefetch.ts
612
750
  function reconstructDescriptors(queries, routeParams, apiClient) {
613
751
  if (!apiClient)
@@ -697,6 +835,41 @@ function resolveQueryBindings(bindings, routeParams) {
697
835
  return resolved;
698
836
  }
699
837
 
838
+ // src/ssr-route-matcher.ts
839
+ function matchUrlToPatterns(url, patterns) {
840
+ const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
841
+ const matches = [];
842
+ for (const pattern of patterns) {
843
+ const result = matchPattern(path, pattern);
844
+ if (result) {
845
+ matches.push(result);
846
+ }
847
+ }
848
+ matches.sort((a, b) => {
849
+ const aSegments = a.pattern.split("/").length;
850
+ const bSegments = b.pattern.split("/").length;
851
+ return aSegments - bSegments;
852
+ });
853
+ return matches;
854
+ }
855
+ function matchPattern(path, pattern) {
856
+ const pathSegments = path.split("/").filter(Boolean);
857
+ const patternSegments = pattern.split("/").filter(Boolean);
858
+ if (patternSegments.length > pathSegments.length)
859
+ return;
860
+ const params = {};
861
+ for (let i = 0;i < patternSegments.length; i++) {
862
+ const seg = patternSegments[i];
863
+ const val = pathSegments[i];
864
+ if (seg.startsWith(":")) {
865
+ params[seg.slice(1)] = val;
866
+ } else if (seg !== val) {
867
+ return;
868
+ }
869
+ }
870
+ return { pattern, params };
871
+ }
872
+
700
873
  // src/ssr-single-pass.ts
701
874
  async function ssrRenderSinglePass(module, url, options) {
702
875
  if (options?.prefetch === false) {
@@ -1045,17 +1218,6 @@ function collectCSS2(themeCss, module) {
1045
1218
  `);
1046
1219
  }
1047
1220
 
1048
- // src/ssr-session.ts
1049
- function createSessionScript(session, nonce) {
1050
- const json = JSON.stringify(session);
1051
- const escaped = json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1052
- const nonceAttr = nonce ? ` nonce="${escapeAttr3(nonce)}"` : "";
1053
- return `<script${nonceAttr}>window.__VERTZ_SESSION__=${escaped}</script>`;
1054
- }
1055
- function escapeAttr3(s) {
1056
- return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
1057
- }
1058
-
1059
1221
  // src/template-inject.ts
1060
1222
  function injectIntoTemplate(options) {
1061
1223
  const { template, appHtml, appCss, ssrData, nonce, headTags, sessionScript } = options;
@@ -1119,350 +1281,4 @@ function replaceAppDivContent(template, appHtml) {
1119
1281
  return template.slice(0, contentStart) + appHtml + template.slice(i);
1120
1282
  }
1121
1283
 
1122
- // src/ssr-progressive-response.ts
1123
- function buildProgressiveResponse(options) {
1124
- const { headChunk, renderStream, tailChunk, ssrData, nonce, headers } = options;
1125
- const stream = new ReadableStream({
1126
- async start(controller) {
1127
- controller.enqueue(encodeChunk(headChunk));
1128
- const reader = renderStream.getReader();
1129
- let renderError;
1130
- try {
1131
- for (;; ) {
1132
- const { done, value } = await reader.read();
1133
- if (done)
1134
- break;
1135
- controller.enqueue(value);
1136
- }
1137
- } catch (err) {
1138
- renderError = err instanceof Error ? err : new Error(String(err));
1139
- }
1140
- if (renderError) {
1141
- console.error("[SSR] Render error after head sent:", renderError.message);
1142
- const nonceAttr = nonce != null ? ` nonce="${escapeAttr(nonce)}"` : "";
1143
- const errorScript = `<script${nonceAttr}>document.dispatchEvent(new CustomEvent('vertz:ssr-error',` + `{detail:{message:${safeSerialize(renderError.message)}}}))</script>`;
1144
- controller.enqueue(encodeChunk(errorScript));
1145
- }
1146
- let tail = "";
1147
- if (ssrData.length > 0) {
1148
- const nonceAttr = nonce != null ? ` nonce="${escapeAttr(nonce)}"` : "";
1149
- tail += `<script${nonceAttr}>window.__VERTZ_SSR_DATA__=${safeSerialize(ssrData)};</script>`;
1150
- }
1151
- tail += tailChunk;
1152
- controller.enqueue(encodeChunk(tail));
1153
- controller.close();
1154
- }
1155
- });
1156
- const responseHeaders = {
1157
- "Content-Type": "text/html; charset=utf-8",
1158
- ...headers
1159
- };
1160
- return new Response(stream, { status: 200, headers: responseHeaders });
1161
- }
1162
-
1163
- // src/template-split.ts
1164
- function splitTemplate(template, options) {
1165
- let processed = template;
1166
- if (options?.inlineCSS) {
1167
- processed = inlineCSSAssets(processed, options.inlineCSS);
1168
- }
1169
- const outletMarker = "<!--ssr-outlet-->";
1170
- const outletIndex = processed.indexOf(outletMarker);
1171
- if (outletIndex !== -1) {
1172
- return {
1173
- headTemplate: processed.slice(0, outletIndex),
1174
- tailTemplate: processed.slice(outletIndex + outletMarker.length)
1175
- };
1176
- }
1177
- const appDivMatch = processed.match(/<div[^>]*id="app"[^>]*>/);
1178
- if (appDivMatch && appDivMatch.index != null) {
1179
- const openTag = appDivMatch[0];
1180
- const contentStart = appDivMatch.index + openTag.length;
1181
- const closingIndex = findMatchingDivClose(processed, contentStart);
1182
- return {
1183
- headTemplate: processed.slice(0, contentStart),
1184
- tailTemplate: processed.slice(closingIndex)
1185
- };
1186
- }
1187
- throw new Error('Could not find <!--ssr-outlet--> or <div id="app"> in the HTML template. ' + "The template must contain one of these markers for SSR content injection.");
1188
- }
1189
- function findMatchingDivClose(html, startAfterOpen) {
1190
- let depth = 1;
1191
- let i = startAfterOpen;
1192
- const len = html.length;
1193
- while (i < len && depth > 0) {
1194
- if (html[i] === "<") {
1195
- if (html.startsWith("</div>", i)) {
1196
- depth--;
1197
- if (depth === 0)
1198
- return i;
1199
- i += 6;
1200
- } else if (html.startsWith("<div", i) && /^<div[\s>]/.test(html.slice(i, i + 5))) {
1201
- depth++;
1202
- i += 4;
1203
- } else {
1204
- i++;
1205
- }
1206
- } else {
1207
- i++;
1208
- }
1209
- }
1210
- return len;
1211
- }
1212
- function inlineCSSAssets(html, inlineCSS) {
1213
- let result = html;
1214
- for (const [href, css] of Object.entries(inlineCSS)) {
1215
- const escapedHref = href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1216
- const linkPattern = new RegExp(`<link[^>]*href=["']${escapedHref}["'][^>]*>`);
1217
- const safeCss = css.replace(/<\//g, "<\\/");
1218
- result = result.replace(linkPattern, `<style data-vertz-css>${safeCss}</style>`);
1219
- }
1220
- result = result.replace(/<link\s+rel="stylesheet"\s+href="([^"]+)"[^>]*>/g, (match, href) => `<link rel="stylesheet" href="${href}" media="print" onload="this.media='all'">
1221
- <noscript>${match}</noscript>`);
1222
- return result;
1223
- }
1224
-
1225
- // src/ssr-handler.ts
1226
- function sanitizeLinkHref(href) {
1227
- return href.replace(/[<>,;\s"']/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`);
1228
- }
1229
- function sanitizeLinkParam(value) {
1230
- return value.replace(/[^a-zA-Z0-9/_.-]/g, "");
1231
- }
1232
- function buildLinkHeader(items) {
1233
- return items.map((item) => {
1234
- const parts = [
1235
- `<${sanitizeLinkHref(item.href)}>`,
1236
- "rel=preload",
1237
- `as=${sanitizeLinkParam(item.as)}`
1238
- ];
1239
- if (item.type)
1240
- parts.push(`type=${sanitizeLinkParam(item.type)}`);
1241
- if (item.crossorigin)
1242
- parts.push("crossorigin");
1243
- return parts.join("; ");
1244
- }).join(", ");
1245
- }
1246
- function buildModulepreloadTags(paths) {
1247
- return paths.map((p) => `<link rel="modulepreload" href="${escapeAttr(p)}">`).join(`
1248
- `);
1249
- }
1250
- function resolveRouteModulepreload(routeChunkManifest, matchedPatterns, fallback) {
1251
- if (routeChunkManifest && matchedPatterns?.length) {
1252
- const chunkPaths = new Set;
1253
- for (const pattern of matchedPatterns) {
1254
- const chunks = routeChunkManifest.routes[pattern];
1255
- if (chunks) {
1256
- for (const chunk of chunks) {
1257
- chunkPaths.add(chunk);
1258
- }
1259
- }
1260
- }
1261
- if (chunkPaths.size > 0) {
1262
- return buildModulepreloadTags([...chunkPaths]);
1263
- }
1264
- }
1265
- return fallback;
1266
- }
1267
- function createSSRHandler(options) {
1268
- const {
1269
- module,
1270
- ssrTimeout,
1271
- inlineCSS,
1272
- nonce,
1273
- fallbackMetrics,
1274
- modulepreload,
1275
- routeChunkManifest,
1276
- cacheControl,
1277
- sessionResolver,
1278
- manifest,
1279
- progressiveHTML
1280
- } = options;
1281
- let template = options.template;
1282
- if (inlineCSS) {
1283
- for (const [href, css] of Object.entries(inlineCSS)) {
1284
- const escapedHref = href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1285
- const linkPattern = new RegExp(`<link[^>]*href=["']${escapedHref}["'][^>]*>`);
1286
- const safeCss = css.replace(/<\//g, "<\\/");
1287
- template = template.replace(linkPattern, `<style data-vertz-css>${safeCss}</style>`);
1288
- }
1289
- }
1290
- let linkHeader;
1291
- if (module.theme) {
1292
- const compiled = compileThemeCached(module.theme, fallbackMetrics);
1293
- if (compiled.preloadItems.length > 0) {
1294
- linkHeader = buildLinkHeader(compiled.preloadItems);
1295
- }
1296
- }
1297
- const modulepreloadTags = modulepreload?.length ? buildModulepreloadTags(modulepreload) : undefined;
1298
- const splitResult = progressiveHTML ? splitTemplate(template) : undefined;
1299
- if (splitResult && module.theme) {
1300
- splitResult.headTemplate = splitResult.headTemplate.replace(/<link\s+rel="stylesheet"\s+href="([^"]+)"[^>]*>/g, (match, href) => `<link rel="stylesheet" href="${href}" media="print" onload="this.media='all'">
1301
- <noscript>${match}</noscript>`);
1302
- }
1303
- return async (request) => {
1304
- const url = new URL(request.url);
1305
- const pathname = url.pathname;
1306
- if (request.headers.get("x-vertz-nav") === "1") {
1307
- return handleNavRequest(module, pathname, ssrTimeout);
1308
- }
1309
- let sessionScript = "";
1310
- let ssrAuth;
1311
- if (sessionResolver) {
1312
- try {
1313
- const sessionResult = await sessionResolver(request);
1314
- if (sessionResult) {
1315
- ssrAuth = {
1316
- status: "authenticated",
1317
- user: sessionResult.session.user,
1318
- expiresAt: sessionResult.session.expiresAt
1319
- };
1320
- const scripts = [];
1321
- scripts.push(createSessionScript(sessionResult.session, nonce));
1322
- if (sessionResult.accessSet != null) {
1323
- scripts.push(createAccessSetScript(sessionResult.accessSet, nonce));
1324
- }
1325
- sessionScript = scripts.join(`
1326
- `);
1327
- } else {
1328
- ssrAuth = { status: "unauthenticated" };
1329
- }
1330
- } catch (resolverErr) {
1331
- console.warn("[Server] Session resolver failed:", resolverErr instanceof Error ? resolverErr.message : resolverErr);
1332
- }
1333
- }
1334
- const useProgressive = progressiveHTML && splitResult && !(manifest?.routeEntries && Object.keys(manifest.routeEntries).length > 0);
1335
- if (useProgressive) {
1336
- return handleProgressiveHTMLRequest(module, splitResult, pathname + url.search, ssrTimeout, nonce, fallbackMetrics, linkHeader, modulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest);
1337
- }
1338
- return handleHTMLRequest(module, template, pathname + url.search, ssrTimeout, nonce, fallbackMetrics, linkHeader, modulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest);
1339
- };
1340
- }
1341
- async function handleNavRequest(module, url, ssrTimeout) {
1342
- try {
1343
- const stream = await ssrStreamNavQueries(module, url, { ssrTimeout });
1344
- return new Response(stream, {
1345
- status: 200,
1346
- headers: {
1347
- "Content-Type": "text/event-stream",
1348
- "Cache-Control": "no-cache"
1349
- }
1350
- });
1351
- } catch {
1352
- return new Response(`event: done
1353
- data: {}
1354
-
1355
- `, {
1356
- status: 200,
1357
- headers: {
1358
- "Content-Type": "text/event-stream",
1359
- "Cache-Control": "no-cache"
1360
- }
1361
- });
1362
- }
1363
- }
1364
- async function handleProgressiveHTMLRequest(module, split, url, ssrTimeout, nonce, fallbackMetrics, linkHeader, staticModulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest) {
1365
- try {
1366
- const result = await ssrRenderProgressive(module, url, {
1367
- ssrTimeout,
1368
- fallbackMetrics,
1369
- ssrAuth,
1370
- manifest
1371
- });
1372
- if (result.redirect) {
1373
- return new Response(null, {
1374
- status: 302,
1375
- headers: { Location: result.redirect.to }
1376
- });
1377
- }
1378
- const modulepreloadTags = resolveRouteModulepreload(routeChunkManifest, result.matchedRoutePatterns, staticModulepreloadTags);
1379
- let headChunk = split.headTemplate;
1380
- const headCloseIdx = headChunk.lastIndexOf("</head>");
1381
- if (headCloseIdx !== -1) {
1382
- const injections = [];
1383
- if (result.css)
1384
- injections.push(result.css);
1385
- if (result.headTags)
1386
- injections.push(result.headTags);
1387
- if (modulepreloadTags)
1388
- injections.push(modulepreloadTags);
1389
- if (sessionScript)
1390
- injections.push(sessionScript);
1391
- if (injections.length > 0) {
1392
- headChunk = headChunk.slice(0, headCloseIdx) + injections.join(`
1393
- `) + `
1394
- ` + headChunk.slice(headCloseIdx);
1395
- }
1396
- } else {
1397
- if (result.css)
1398
- headChunk += result.css;
1399
- if (result.headTags)
1400
- headChunk += result.headTags;
1401
- if (modulepreloadTags)
1402
- headChunk += modulepreloadTags;
1403
- if (sessionScript)
1404
- headChunk += sessionScript;
1405
- }
1406
- const headers = {};
1407
- if (linkHeader)
1408
- headers.Link = linkHeader;
1409
- if (cacheControl)
1410
- headers["Cache-Control"] = cacheControl;
1411
- return buildProgressiveResponse({
1412
- headChunk,
1413
- renderStream: result.renderStream,
1414
- tailChunk: split.tailTemplate,
1415
- ssrData: result.ssrData,
1416
- nonce,
1417
- headers
1418
- });
1419
- } catch (err) {
1420
- console.error("[SSR] Render failed:", err instanceof Error ? err.message : err);
1421
- return new Response("Internal Server Error", {
1422
- status: 500,
1423
- headers: { "Content-Type": "text/plain" }
1424
- });
1425
- }
1426
- }
1427
- async function handleHTMLRequest(module, template, url, ssrTimeout, nonce, fallbackMetrics, linkHeader, staticModulepreloadTags, routeChunkManifest, cacheControl, sessionScript, ssrAuth, manifest) {
1428
- try {
1429
- const result = await ssrRenderSinglePass(module, url, {
1430
- ssrTimeout,
1431
- fallbackMetrics,
1432
- ssrAuth,
1433
- manifest
1434
- });
1435
- if (result.redirect) {
1436
- return new Response(null, {
1437
- status: 302,
1438
- headers: { Location: result.redirect.to }
1439
- });
1440
- }
1441
- const modulepreloadTags = resolveRouteModulepreload(routeChunkManifest, result.matchedRoutePatterns, staticModulepreloadTags);
1442
- const allHeadTags = [result.headTags, modulepreloadTags].filter(Boolean).join(`
1443
- `);
1444
- const html = injectIntoTemplate({
1445
- template,
1446
- appHtml: result.html,
1447
- appCss: result.css,
1448
- ssrData: result.ssrData,
1449
- nonce,
1450
- headTags: allHeadTags || undefined,
1451
- sessionScript
1452
- });
1453
- const headers = { "Content-Type": "text/html; charset=utf-8" };
1454
- if (linkHeader)
1455
- headers.Link = linkHeader;
1456
- if (cacheControl)
1457
- headers["Cache-Control"] = cacheControl;
1458
- return new Response(html, { status: 200, headers });
1459
- } catch (err) {
1460
- console.error("[SSR] Render failed:", err instanceof Error ? err.message : err);
1461
- return new Response("Internal Server Error", {
1462
- status: 500,
1463
- headers: { "Content-Type": "text/plain" }
1464
- });
1465
- }
1466
- }
1467
-
1468
- export { escapeHtml, escapeAttr, serializeToHtml, resetSlotCounter, createSlotPlaceholder, encodeChunk, streamToString, collectStreamChunks, createTemplateChunk, renderToStream, safeSerialize, getStreamingRuntimeScript, createSSRDataChunk, compileThemeCached, createRequestContext, ssrRenderToString, ssrDiscoverQueries, toPrefetchSession, evaluateAccessRule, getAccessSetForSSR, createAccessSetScript, matchUrlToPatterns, reconstructDescriptors, ssrRenderSinglePass, createSessionScript, injectIntoTemplate, createSSRHandler };
1284
+ export { escapeHtml, escapeAttr, serializeToHtml, getAccessSetForSSR, createAccessSetScript, resetSlotCounter, createSlotPlaceholder, encodeChunk, streamToString, collectStreamChunks, createTemplateChunk, renderToStream, safeSerialize, getStreamingRuntimeScript, createSSRDataChunk, compileThemeCached, createRequestContext, ssrRenderToString, ssrDiscoverQueries, ssrStreamNavQueries, createSessionScript, resolveRouteModulepreload, precomputeHandlerState, resolveSession, toPrefetchSession, evaluateAccessRule, reconstructDescriptors, matchUrlToPatterns, ssrRenderSinglePass, ssrRenderProgressive, injectIntoTemplate };