@vertz/ui-server 0.2.25 → 0.2.28

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/index.js CHANGED
@@ -35,6 +35,62 @@ import {
35
35
  toVNode
36
36
  } from "./shared/chunk-gcwqkynf.js";
37
37
 
38
+ // src/aot-manifest-build.ts
39
+ import { readdirSync, readFileSync } from "node:fs";
40
+ import { join } from "node:path";
41
+ import { compileForSSRAot } from "@vertz/ui-compiler";
42
+ function generateAotBuildManifest(srcDir) {
43
+ const components = {};
44
+ const classificationLog = [];
45
+ const tsxFiles = collectTsxFiles(srcDir);
46
+ for (const filePath of tsxFiles) {
47
+ try {
48
+ const source = readFileSync(filePath, "utf-8");
49
+ const result = compileForSSRAot(source, { filename: filePath });
50
+ for (const comp of result.components) {
51
+ components[comp.name] = {
52
+ tier: comp.tier,
53
+ holes: comp.holes
54
+ };
55
+ }
56
+ } catch (e) {
57
+ classificationLog.push(`⚠ ${filePath}: ${e instanceof Error ? e.message : "compilation failed"}`);
58
+ }
59
+ }
60
+ let aotCount = 0;
61
+ let runtimeCount = 0;
62
+ for (const [name, entry] of Object.entries(components)) {
63
+ let line = `${name}: ${entry.tier}`;
64
+ if (entry.holes.length > 0) {
65
+ const holeLabel = entry.holes.length === 1 ? "hole" : "holes";
66
+ line += `, ${entry.holes.length} ${holeLabel} (${entry.holes.join(", ")})`;
67
+ }
68
+ classificationLog.push(line);
69
+ if (entry.tier === "runtime-fallback") {
70
+ runtimeCount++;
71
+ } else {
72
+ aotCount++;
73
+ }
74
+ }
75
+ const total = aotCount + runtimeCount;
76
+ if (total > 0) {
77
+ const pct = Math.round(aotCount / total * 100);
78
+ classificationLog.push(`Coverage: ${aotCount}/${total} components (${pct}%)`);
79
+ }
80
+ return { components, classificationLog };
81
+ }
82
+ function collectTsxFiles(dir) {
83
+ const files = [];
84
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
85
+ const fullPath = join(dir, entry.name);
86
+ if (entry.isDirectory()) {
87
+ files.push(...collectTsxFiles(fullPath));
88
+ } else if (entry.name.endsWith(".tsx")) {
89
+ files.push(fullPath);
90
+ }
91
+ }
92
+ return files;
93
+ }
38
94
  // src/asset-pipeline.ts
39
95
  function renderAssetTags(assets) {
40
96
  if (assets.length === 0)
@@ -63,7 +119,7 @@ function inlineCriticalCss(css) {
63
119
  // src/font-metrics.ts
64
120
  import { access as fsAccess } from "node:fs/promises";
65
121
  import { readFile } from "node:fs/promises";
66
- import { join } from "node:path";
122
+ import { join as join2 } from "node:path";
67
123
  import { fromBuffer } from "@capsizecss/unpack";
68
124
  var SYSTEM_FONT_METRICS = {
69
125
  Arial: {
@@ -132,12 +188,12 @@ function getPrimarySrcPath(descriptor) {
132
188
  }
133
189
  async function resolveFilePath(urlPath, rootDir) {
134
190
  const cleaned = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
135
- const direct = join(rootDir, cleaned);
191
+ const direct = join2(rootDir, cleaned);
136
192
  try {
137
193
  await fsAccess(direct);
138
194
  return direct;
139
195
  } catch {
140
- return join(rootDir, "public", cleaned);
196
+ return join2(rootDir, "public", cleaned);
141
197
  }
142
198
  }
143
199
  async function extractFontMetrics(fonts, rootDir) {
@@ -428,14 +484,16 @@ async function renderToHTML(appOrOptions, maybeOptions) {
428
484
  });
429
485
  }
430
486
  // src/ssr-access-evaluator.ts
431
- function toPrefetchSession(ssrAuth) {
487
+ function toPrefetchSession(ssrAuth, accessSet) {
432
488
  if (!ssrAuth || ssrAuth.status !== "authenticated" || !ssrAuth.user) {
433
489
  return { status: "unauthenticated" };
434
490
  }
435
491
  const roles = ssrAuth.user.role ? [ssrAuth.user.role] : undefined;
492
+ const entitlements = accessSet != null ? Object.fromEntries(Object.entries(accessSet.entitlements).map(([name, check]) => [name, check.allowed])) : undefined;
436
493
  return {
437
494
  status: "authenticated",
438
495
  roles,
496
+ entitlements,
439
497
  tenantId: ssrAuth.user.tenantId
440
498
  };
441
499
  }
@@ -467,144 +525,168 @@ function evaluateAccessRule(rule, session) {
467
525
  return false;
468
526
  }
469
527
  }
470
- // src/ssr-html.ts
471
- function generateSSRHtml(options) {
472
- const {
473
- appHtml,
474
- css,
475
- ssrData,
476
- clientEntry,
477
- title = "Vertz App",
478
- headTags: rawHeadTags = "",
479
- modulepreload
480
- } = options;
481
- const modulepreloadTags = modulepreload?.length ? modulepreload.map((p) => `<link rel="modulepreload" href="${escapeAttr(p)}">`).join(`
482
- `) : "";
483
- const headTags = [rawHeadTags, modulepreloadTags].filter(Boolean).join(`
484
- `);
485
- const ssrDataScript = ssrData.length > 0 ? `<script>window.__VERTZ_SSR_DATA__ = ${JSON.stringify(ssrData)};</script>` : "";
486
- return `<!doctype html>
487
- <html lang="en">
488
- <head>
489
- <meta charset="UTF-8" />
490
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
491
- <title>${escapeHtml(title)}</title>
492
- ${headTags}
493
- ${css}
494
- </head>
495
- <body>
496
- <div id="app">${appHtml}</div>
497
- ${ssrDataScript}
498
- <script type="module" src="${escapeAttr(clientEntry)}"></script>
499
- </body>
500
- </html>`;
501
- }
502
- // src/ssr-prefetch-dev.ts
503
- import {
504
- analyzeComponentQueries,
505
- generatePrefetchManifest
506
- } from "@vertz/ui-compiler";
507
- function createPrefetchManifestManager(options) {
508
- const { routerPath, readFile: readFile2, resolveImport } = options;
509
- let currentManifest = null;
510
- let currentSSRManifest;
511
- let rebuildCount = 0;
512
- let lastRebuildMs = null;
513
- let lastRebuildAt = null;
514
- let fileToRouteIndices = new Map;
515
- function buildFileIndex(routes) {
516
- const index = new Map;
517
- for (let i = 0;i < routes.length; i++) {
518
- const file = routes[i]?.file;
519
- if (file) {
520
- const existing = index.get(file) ?? [];
521
- existing.push(i);
522
- index.set(file, existing);
528
+ // src/ssr-aot-diagnostics.ts
529
+ var MAX_DIVERGENCES = 20;
530
+
531
+ class AotDiagnostics {
532
+ _components = new Map;
533
+ _divergences = [];
534
+ recordCompilation(components) {
535
+ for (const comp of components) {
536
+ this._components.set(comp.name, {
537
+ tier: comp.tier,
538
+ holes: comp.holes
539
+ });
540
+ }
541
+ }
542
+ recordDivergence(component, aotHtml, domHtml) {
543
+ this._divergences.push({
544
+ component,
545
+ aotHtml,
546
+ domHtml,
547
+ timestamp: new Date().toISOString()
548
+ });
549
+ if (this._divergences.length > MAX_DIVERGENCES) {
550
+ this._divergences = this._divergences.slice(this._divergences.length - MAX_DIVERGENCES);
551
+ }
552
+ }
553
+ clear() {
554
+ this._components.clear();
555
+ this._divergences = [];
556
+ }
557
+ clearComponents() {
558
+ this._components.clear();
559
+ }
560
+ getClassificationLog() {
561
+ const lines = [];
562
+ for (const [name, comp] of this._components) {
563
+ let line = `${name}: ${comp.tier}`;
564
+ if (comp.holes.length > 0) {
565
+ line += `, ${comp.holes.length} hole${comp.holes.length > 1 ? "s" : ""} (${comp.holes.join(", ")})`;
523
566
  }
567
+ lines.push(line);
524
568
  }
525
- return index;
569
+ const snapshot = this.getSnapshot();
570
+ const { total, aot, percentage } = snapshot.coverage;
571
+ if (total > 0) {
572
+ lines.push(`Coverage: ${aot}/${total} components (${percentage}%)`);
573
+ }
574
+ return lines;
526
575
  }
527
- function toSSRManifest(manifest) {
528
- const routeEntries = {};
529
- for (const route of manifest.routes) {
530
- const existing = routeEntries[route.pattern];
531
- if (existing) {
532
- existing.queries.push(...route.queries);
576
+ getSnapshot() {
577
+ let aot = 0;
578
+ let runtime = 0;
579
+ for (const comp of this._components.values()) {
580
+ if (comp.tier === "runtime-fallback") {
581
+ runtime++;
533
582
  } else {
534
- routeEntries[route.pattern] = { queries: [...route.queries] };
583
+ aot++;
535
584
  }
536
585
  }
586
+ const total = aot + runtime;
537
587
  return {
538
- routePatterns: [...new Set(manifest.routes.map((r) => r.pattern))],
539
- routeEntries
588
+ components: Object.fromEntries(this._components),
589
+ coverage: {
590
+ total,
591
+ aot,
592
+ runtime,
593
+ percentage: total === 0 ? 0 : Math.round(aot / total * 100)
594
+ },
595
+ divergences: [...this._divergences]
540
596
  };
541
597
  }
542
- function fullBuild(routerSourceOverride) {
598
+ }
599
+ // src/ssr-aot-manifest-dev.ts
600
+ import { compileForSSRAot as compileForSSRAot2 } from "@vertz/ui-compiler";
601
+ function createAotManifestManager(options) {
602
+ const { readFile: readFile2, listFiles } = options;
603
+ let currentManifest = null;
604
+ const diagnostics = new AotDiagnostics;
605
+ let rebuildCount = 0;
606
+ let lastRebuildMs = null;
607
+ let lastRebuildAt = null;
608
+ function compileFile(filePath, source) {
609
+ try {
610
+ const result = compileForSSRAot2(source, { filename: filePath });
611
+ return result.components.map((comp) => ({
612
+ name: comp.name,
613
+ entry: {
614
+ tier: comp.tier,
615
+ holes: comp.holes,
616
+ file: filePath
617
+ }
618
+ }));
619
+ } catch {
620
+ return [];
621
+ }
622
+ }
623
+ function updateDiagnostics(manifest, isFullBuild) {
624
+ if (isFullBuild) {
625
+ diagnostics.clear();
626
+ } else {
627
+ diagnostics.clearComponents();
628
+ }
629
+ const entries = Object.entries(manifest.components).map(([name, entry]) => ({
630
+ name,
631
+ tier: entry.tier,
632
+ holes: entry.holes
633
+ }));
634
+ diagnostics.recordCompilation(entries);
635
+ }
636
+ function fullBuild() {
543
637
  const start = performance.now();
544
- const routerSource = routerSourceOverride ?? readFile2(routerPath);
545
- if (!routerSource) {
546
- return;
638
+ const files = listFiles();
639
+ const components = {};
640
+ for (const filePath of files) {
641
+ if (!filePath.endsWith(".tsx"))
642
+ continue;
643
+ const source = readFile2(filePath);
644
+ if (!source)
645
+ continue;
646
+ const entries = compileFile(filePath, source);
647
+ for (const { name, entry } of entries) {
648
+ components[name] = entry;
649
+ }
547
650
  }
548
- try {
549
- const manifest = generatePrefetchManifest({
550
- routerSource,
551
- routerPath,
552
- readFile: readFile2,
553
- resolveImport
554
- });
555
- currentManifest = manifest;
556
- currentSSRManifest = toSSRManifest(manifest);
557
- fileToRouteIndices = buildFileIndex(manifest.routes);
558
- rebuildCount++;
559
- lastRebuildMs = Math.round(performance.now() - start);
560
- lastRebuildAt = new Date().toISOString();
561
- } catch {}
651
+ currentManifest = { components };
652
+ updateDiagnostics(currentManifest, true);
653
+ rebuildCount++;
654
+ lastRebuildMs = Math.round(performance.now() - start);
655
+ lastRebuildAt = new Date().toISOString();
562
656
  }
563
657
  function incrementalUpdate(filePath, sourceText) {
564
658
  if (!currentManifest)
565
659
  return;
566
- const indices = fileToRouteIndices.get(filePath);
567
- if (!indices || indices.length === 0)
568
- return;
569
660
  const start = performance.now();
570
- try {
571
- const analysis = analyzeComponentQueries(sourceText, filePath);
572
- const newRoutes = [...currentManifest.routes];
573
- for (const idx of indices) {
574
- const existing = newRoutes[idx];
575
- if (existing) {
576
- newRoutes[idx] = {
577
- ...existing,
578
- queries: analysis.queries,
579
- params: analysis.params
580
- };
581
- }
661
+ const newComponents = { ...currentManifest.components };
662
+ for (const [name, entry] of Object.entries(newComponents)) {
663
+ if (entry.file === filePath) {
664
+ delete newComponents[name];
582
665
  }
583
- const newManifest = {
584
- ...currentManifest,
585
- routes: newRoutes,
586
- generatedAt: new Date().toISOString()
587
- };
588
- currentManifest = newManifest;
589
- currentSSRManifest = toSSRManifest(newManifest);
590
- rebuildCount++;
591
- lastRebuildMs = Math.round(performance.now() - start);
592
- lastRebuildAt = new Date().toISOString();
593
- } catch {}
666
+ }
667
+ if (sourceText.trim()) {
668
+ const entries = compileFile(filePath, sourceText);
669
+ for (const { name, entry } of entries) {
670
+ newComponents[name] = entry;
671
+ }
672
+ }
673
+ currentManifest = { components: newComponents };
674
+ updateDiagnostics(currentManifest, false);
675
+ rebuildCount++;
676
+ lastRebuildMs = Math.round(performance.now() - start);
677
+ lastRebuildAt = new Date().toISOString();
594
678
  }
595
679
  return {
596
680
  build() {
597
681
  fullBuild();
598
682
  },
599
683
  onFileChange(filePath, sourceText) {
600
- if (filePath === routerPath) {
601
- fullBuild(sourceText);
602
- } else {
603
- incrementalUpdate(filePath, sourceText);
604
- }
684
+ if (!filePath.endsWith(".tsx"))
685
+ return;
686
+ incrementalUpdate(filePath, sourceText);
605
687
  },
606
- getSSRManifest() {
607
- return currentSSRManifest;
688
+ getManifest() {
689
+ return currentManifest;
608
690
  },
609
691
  getSnapshot() {
610
692
  return {
@@ -613,9 +695,53 @@ function createPrefetchManifestManager(options) {
613
695
  lastRebuildMs,
614
696
  lastRebuildAt
615
697
  };
698
+ },
699
+ getDiagnostics() {
700
+ return diagnostics;
616
701
  }
617
702
  };
618
703
  }
704
+ // src/ssr-aot-pipeline.ts
705
+ import { compileTheme as compileTheme3 } from "@vertz/ui";
706
+
707
+ // src/ssr-route-matcher.ts
708
+ function matchUrlToPatterns(url, patterns) {
709
+ const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
710
+ const matches = [];
711
+ for (const pattern of patterns) {
712
+ const result = matchPattern(path, pattern);
713
+ if (result) {
714
+ matches.push(result);
715
+ }
716
+ }
717
+ matches.sort((a, b) => {
718
+ const aSegments = a.pattern.split("/").length;
719
+ const bSegments = b.pattern.split("/").length;
720
+ return aSegments - bSegments;
721
+ });
722
+ return matches;
723
+ }
724
+ function matchPattern(path, pattern) {
725
+ const pathSegments = path.split("/").filter(Boolean);
726
+ const patternSegments = pattern.split("/").filter(Boolean);
727
+ if (patternSegments.length > pathSegments.length)
728
+ return;
729
+ const params = {};
730
+ for (let i = 0;i < patternSegments.length; i++) {
731
+ const seg = patternSegments[i];
732
+ const val = pathSegments[i];
733
+ if (seg.startsWith(":")) {
734
+ params[seg.slice(1)] = val;
735
+ } else if (seg !== val) {
736
+ return;
737
+ }
738
+ }
739
+ return { pattern, params };
740
+ }
741
+
742
+ // src/ssr-single-pass.ts
743
+ import { compileTheme as compileTheme2 } from "@vertz/ui";
744
+
619
745
  // src/ssr-manifest-prefetch.ts
620
746
  function reconstructDescriptors(queries, routeParams, apiClient) {
621
747
  if (!apiClient)
@@ -704,42 +830,8 @@ function resolveQueryBindings(bindings, routeParams) {
704
830
  resolved.limit = bindings.limit;
705
831
  return resolved;
706
832
  }
707
- // src/ssr-route-matcher.ts
708
- function matchUrlToPatterns(url, patterns) {
709
- const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
710
- const matches = [];
711
- for (const pattern of patterns) {
712
- const result = matchPattern(path, pattern);
713
- if (result) {
714
- matches.push(result);
715
- }
716
- }
717
- matches.sort((a, b) => {
718
- const aSegments = a.pattern.split("/").length;
719
- const bSegments = b.pattern.split("/").length;
720
- return aSegments - bSegments;
721
- });
722
- return matches;
723
- }
724
- function matchPattern(path, pattern) {
725
- const pathSegments = path.split("/").filter(Boolean);
726
- const patternSegments = pattern.split("/").filter(Boolean);
727
- if (patternSegments.length > pathSegments.length)
728
- return;
729
- const params = {};
730
- for (let i = 0;i < patternSegments.length; i++) {
731
- const seg = patternSegments[i];
732
- const val = pathSegments[i];
733
- if (seg.startsWith(":")) {
734
- params[seg.slice(1)] = val;
735
- } else if (seg !== val) {
736
- return;
737
- }
738
- }
739
- return { pattern, params };
740
- }
833
+
741
834
  // src/ssr-single-pass.ts
742
- import { compileTheme as compileTheme2 } from "@vertz/ui";
743
835
  async function ssrRenderSinglePass(module, url, options) {
744
836
  if (options?.prefetch === false) {
745
837
  return ssrRenderToString(module, url, options);
@@ -1031,6 +1123,408 @@ function collectCSS(themeCss, module) {
1031
1123
  return [themeTag, globalTag, componentTag].filter(Boolean).join(`
1032
1124
  `);
1033
1125
  }
1126
+
1127
+ // src/ssr-aot-pipeline.ts
1128
+ function createHoles(holeNames, module, url, queryCache, ssrAuth) {
1129
+ if (holeNames.length === 0)
1130
+ return {};
1131
+ const holes = {};
1132
+ for (const name of holeNames) {
1133
+ holes[name] = () => {
1134
+ const holeCtx = createRequestContext(url);
1135
+ for (const [key, data] of queryCache) {
1136
+ holeCtx.queryCache.set(key, data);
1137
+ }
1138
+ if (ssrAuth) {
1139
+ holeCtx.ssrAuth = ssrAuth;
1140
+ }
1141
+ holeCtx.resolvedComponents = new Map;
1142
+ return ssrStorage.run(holeCtx, () => {
1143
+ ensureDomShim2();
1144
+ const factory = resolveHoleComponent(module, name);
1145
+ if (!factory) {
1146
+ return `<!-- AOT hole: ${name} not found -->`;
1147
+ }
1148
+ const node = factory();
1149
+ const vnode = toVNode(node);
1150
+ return serializeToHtml(vnode);
1151
+ });
1152
+ };
1153
+ }
1154
+ return holes;
1155
+ }
1156
+ function resolveHoleComponent(module, name) {
1157
+ const moduleRecord = module;
1158
+ const exported = moduleRecord[name];
1159
+ if (typeof exported === "function") {
1160
+ return exported;
1161
+ }
1162
+ return;
1163
+ }
1164
+ async function ssrRenderAot(module, url, options) {
1165
+ const { aotManifest, manifest } = options;
1166
+ const ssrTimeout = options.ssrTimeout ?? 300;
1167
+ const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
1168
+ const fallbackOptions = {
1169
+ ssrTimeout,
1170
+ fallbackMetrics: options.fallbackMetrics,
1171
+ ssrAuth: options.ssrAuth,
1172
+ manifest,
1173
+ prefetchSession: options.prefetchSession
1174
+ };
1175
+ const aotPatterns = Object.keys(aotManifest.routes);
1176
+ const matches = matchUrlToPatterns(normalizedUrl, aotPatterns);
1177
+ if (matches.length === 0) {
1178
+ return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
1179
+ }
1180
+ const match = matches[matches.length - 1];
1181
+ if (!match) {
1182
+ return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
1183
+ }
1184
+ const aotEntry = aotManifest.routes[match.pattern];
1185
+ if (!aotEntry) {
1186
+ return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
1187
+ }
1188
+ const queryCache = new Map;
1189
+ try {
1190
+ setGlobalSSRTimeout(ssrTimeout);
1191
+ const holes = createHoles(aotEntry.holes, module, normalizedUrl, queryCache, options.ssrAuth);
1192
+ const ctx = {
1193
+ holes,
1194
+ getData: (key) => queryCache.get(key),
1195
+ session: options.prefetchSession,
1196
+ params: match.params
1197
+ };
1198
+ const data = {};
1199
+ for (const [key, value] of queryCache) {
1200
+ data[key] = value;
1201
+ }
1202
+ const html = aotEntry.render(data, ctx);
1203
+ if (options.diagnostics && isAotDebugEnabled()) {
1204
+ try {
1205
+ const domResult = await ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
1206
+ if (domResult.html !== html) {
1207
+ options.diagnostics.recordDivergence(match.pattern, html, domResult.html);
1208
+ }
1209
+ } catch {}
1210
+ }
1211
+ const css = collectCSSFromModule(module, options.fallbackMetrics);
1212
+ const ssrData = [];
1213
+ for (const [key, data2] of queryCache) {
1214
+ ssrData.push({ key, data: JSON.parse(JSON.stringify(data2)) });
1215
+ }
1216
+ return {
1217
+ html,
1218
+ css: css.cssString,
1219
+ ssrData,
1220
+ headTags: css.preloadTags
1221
+ };
1222
+ } finally {
1223
+ clearGlobalSSRTimeout();
1224
+ }
1225
+ }
1226
+ function isAotDebugEnabled() {
1227
+ const env = process.env.VERTZ_DEBUG;
1228
+ if (!env)
1229
+ return false;
1230
+ return env === "1" || env.split(",").includes("aot");
1231
+ }
1232
+ var domShimInstalled2 = false;
1233
+ function ensureDomShim2() {
1234
+ if (domShimInstalled2 && typeof document !== "undefined")
1235
+ return;
1236
+ domShimInstalled2 = true;
1237
+ installDomShim();
1238
+ }
1239
+ function collectCSSFromModule(module, fallbackMetrics) {
1240
+ let themeCss = "";
1241
+ let preloadTags = "";
1242
+ if (module.theme) {
1243
+ try {
1244
+ const compiled = compileTheme3(module.theme, { fallbackMetrics });
1245
+ themeCss = compiled.css;
1246
+ preloadTags = compiled.preloadTags;
1247
+ } catch (e) {
1248
+ console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
1249
+ }
1250
+ }
1251
+ const alreadyIncluded = new Set;
1252
+ if (themeCss)
1253
+ alreadyIncluded.add(themeCss);
1254
+ if (module.styles) {
1255
+ for (const s of module.styles)
1256
+ alreadyIncluded.add(s);
1257
+ }
1258
+ const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
1259
+ const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
1260
+ const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
1261
+ `)}</style>` : "";
1262
+ const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
1263
+ `)}</style>` : "";
1264
+ const cssString = [themeTag, globalTag, componentTag].filter(Boolean).join(`
1265
+ `);
1266
+ return { cssString, preloadTags };
1267
+ }
1268
+ // src/ssr-aot-runtime.ts
1269
+ var UNITLESS_PROPERTIES = new Set([
1270
+ "animationIterationCount",
1271
+ "aspectRatio",
1272
+ "borderImageOutset",
1273
+ "borderImageSlice",
1274
+ "borderImageWidth",
1275
+ "boxFlex",
1276
+ "boxFlexGroup",
1277
+ "boxOrdinalGroup",
1278
+ "columnCount",
1279
+ "columns",
1280
+ "flex",
1281
+ "flexGrow",
1282
+ "flexPositive",
1283
+ "flexShrink",
1284
+ "flexNegative",
1285
+ "flexOrder",
1286
+ "fontWeight",
1287
+ "gridArea",
1288
+ "gridColumn",
1289
+ "gridColumnEnd",
1290
+ "gridColumnSpan",
1291
+ "gridColumnStart",
1292
+ "gridRow",
1293
+ "gridRowEnd",
1294
+ "gridRowSpan",
1295
+ "gridRowStart",
1296
+ "lineClamp",
1297
+ "lineHeight",
1298
+ "opacity",
1299
+ "order",
1300
+ "orphans",
1301
+ "scale",
1302
+ "tabSize",
1303
+ "widows",
1304
+ "zIndex",
1305
+ "zoom"
1306
+ ]);
1307
+ function __esc(value) {
1308
+ if (value == null || value === false)
1309
+ return "";
1310
+ if (value === true)
1311
+ return "true";
1312
+ if (typeof value === "number")
1313
+ return String(value);
1314
+ if (Array.isArray(value))
1315
+ return value.map(__esc).join("");
1316
+ return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1317
+ }
1318
+ function __esc_attr(value) {
1319
+ const str = typeof value === "string" ? value : String(value);
1320
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
1321
+ }
1322
+ function __ssr_spread(props) {
1323
+ const parts = [];
1324
+ for (const key in props) {
1325
+ const value = props[key];
1326
+ if (value == null || value === false)
1327
+ continue;
1328
+ const third = key.charAt(2);
1329
+ if (key.length > 2 && key.charAt(0) === "o" && key.charAt(1) === "n" && third >= "A" && third <= "Z") {
1330
+ continue;
1331
+ }
1332
+ if (typeof value === "function")
1333
+ continue;
1334
+ if (key === "key" || key === "ref" || key === "children")
1335
+ continue;
1336
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
1337
+ if (key === "style" && typeof value === "object") {
1338
+ const css = __ssr_style_object(value);
1339
+ if (css) {
1340
+ parts.push(` style="${__esc_attr(css)}"`);
1341
+ }
1342
+ continue;
1343
+ }
1344
+ if (value === true) {
1345
+ parts.push(` ${attrName}`);
1346
+ continue;
1347
+ }
1348
+ parts.push(` ${attrName}="${__esc_attr(value)}"`);
1349
+ }
1350
+ return parts.join("");
1351
+ }
1352
+ function camelToKebab(prop) {
1353
+ if (prop.startsWith("--"))
1354
+ return prop;
1355
+ if (prop.startsWith("ms")) {
1356
+ return `-${prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`;
1357
+ }
1358
+ const first = prop.charAt(0);
1359
+ if (first >= "A" && first <= "Z") {
1360
+ return `-${first.toLowerCase()}${prop.slice(1).replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`;
1361
+ }
1362
+ return prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
1363
+ }
1364
+ function __ssr_style_object(style) {
1365
+ const parts = [];
1366
+ for (const prop in style) {
1367
+ const value = style[prop];
1368
+ if (value == null || value === "")
1369
+ continue;
1370
+ const cssProp = camelToKebab(prop);
1371
+ if (typeof value === "number" && value !== 0 && !UNITLESS_PROPERTIES.has(prop)) {
1372
+ parts.push(`${cssProp}: ${value}px`);
1373
+ } else {
1374
+ parts.push(`${cssProp}: ${value}`);
1375
+ }
1376
+ }
1377
+ return parts.join("; ");
1378
+ }
1379
+ // src/ssr-html.ts
1380
+ function generateSSRHtml(options) {
1381
+ const {
1382
+ appHtml,
1383
+ css,
1384
+ ssrData,
1385
+ clientEntry,
1386
+ title = "Vertz App",
1387
+ headTags: rawHeadTags = "",
1388
+ modulepreload
1389
+ } = options;
1390
+ const modulepreloadTags = modulepreload?.length ? modulepreload.map((p) => `<link rel="modulepreload" href="${escapeAttr(p)}">`).join(`
1391
+ `) : "";
1392
+ const headTags = [rawHeadTags, modulepreloadTags].filter(Boolean).join(`
1393
+ `);
1394
+ const ssrDataScript = ssrData.length > 0 ? `<script>window.__VERTZ_SSR_DATA__ = ${JSON.stringify(ssrData)};</script>` : "";
1395
+ return `<!doctype html>
1396
+ <html lang="en">
1397
+ <head>
1398
+ <meta charset="UTF-8" />
1399
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1400
+ <title>${escapeHtml(title)}</title>
1401
+ ${headTags}
1402
+ ${css}
1403
+ </head>
1404
+ <body>
1405
+ <div id="app">${appHtml}</div>
1406
+ ${ssrDataScript}
1407
+ <script type="module" src="${escapeAttr(clientEntry)}"></script>
1408
+ </body>
1409
+ </html>`;
1410
+ }
1411
+ // src/ssr-prefetch-dev.ts
1412
+ import {
1413
+ analyzeComponentQueries,
1414
+ generatePrefetchManifest
1415
+ } from "@vertz/ui-compiler";
1416
+ function createPrefetchManifestManager(options) {
1417
+ const { routerPath, readFile: readFile2, resolveImport } = options;
1418
+ let currentManifest = null;
1419
+ let currentSSRManifest;
1420
+ let rebuildCount = 0;
1421
+ let lastRebuildMs = null;
1422
+ let lastRebuildAt = null;
1423
+ let fileToRouteIndices = new Map;
1424
+ function buildFileIndex(routes) {
1425
+ const index = new Map;
1426
+ for (let i = 0;i < routes.length; i++) {
1427
+ const file = routes[i]?.file;
1428
+ if (file) {
1429
+ const existing = index.get(file) ?? [];
1430
+ existing.push(i);
1431
+ index.set(file, existing);
1432
+ }
1433
+ }
1434
+ return index;
1435
+ }
1436
+ function toSSRManifest(manifest) {
1437
+ const routeEntries = {};
1438
+ for (const route of manifest.routes) {
1439
+ const existing = routeEntries[route.pattern];
1440
+ if (existing) {
1441
+ existing.queries.push(...route.queries);
1442
+ } else {
1443
+ routeEntries[route.pattern] = { queries: [...route.queries] };
1444
+ }
1445
+ }
1446
+ return {
1447
+ routePatterns: [...new Set(manifest.routes.map((r) => r.pattern))],
1448
+ routeEntries
1449
+ };
1450
+ }
1451
+ function fullBuild(routerSourceOverride) {
1452
+ const start = performance.now();
1453
+ const routerSource = routerSourceOverride ?? readFile2(routerPath);
1454
+ if (!routerSource) {
1455
+ return;
1456
+ }
1457
+ try {
1458
+ const manifest = generatePrefetchManifest({
1459
+ routerSource,
1460
+ routerPath,
1461
+ readFile: readFile2,
1462
+ resolveImport
1463
+ });
1464
+ currentManifest = manifest;
1465
+ currentSSRManifest = toSSRManifest(manifest);
1466
+ fileToRouteIndices = buildFileIndex(manifest.routes);
1467
+ rebuildCount++;
1468
+ lastRebuildMs = Math.round(performance.now() - start);
1469
+ lastRebuildAt = new Date().toISOString();
1470
+ } catch {}
1471
+ }
1472
+ function incrementalUpdate(filePath, sourceText) {
1473
+ if (!currentManifest)
1474
+ return;
1475
+ const indices = fileToRouteIndices.get(filePath);
1476
+ if (!indices || indices.length === 0)
1477
+ return;
1478
+ const start = performance.now();
1479
+ try {
1480
+ const analysis = analyzeComponentQueries(sourceText, filePath);
1481
+ const newRoutes = [...currentManifest.routes];
1482
+ for (const idx of indices) {
1483
+ const existing = newRoutes[idx];
1484
+ if (existing) {
1485
+ newRoutes[idx] = {
1486
+ ...existing,
1487
+ queries: analysis.queries,
1488
+ params: analysis.params
1489
+ };
1490
+ }
1491
+ }
1492
+ const newManifest = {
1493
+ ...currentManifest,
1494
+ routes: newRoutes,
1495
+ generatedAt: new Date().toISOString()
1496
+ };
1497
+ currentManifest = newManifest;
1498
+ currentSSRManifest = toSSRManifest(newManifest);
1499
+ rebuildCount++;
1500
+ lastRebuildMs = Math.round(performance.now() - start);
1501
+ lastRebuildAt = new Date().toISOString();
1502
+ } catch {}
1503
+ }
1504
+ return {
1505
+ build() {
1506
+ fullBuild();
1507
+ },
1508
+ onFileChange(filePath, sourceText) {
1509
+ if (filePath === routerPath) {
1510
+ fullBuild(sourceText);
1511
+ } else {
1512
+ incrementalUpdate(filePath, sourceText);
1513
+ }
1514
+ },
1515
+ getSSRManifest() {
1516
+ return currentSSRManifest;
1517
+ },
1518
+ getSnapshot() {
1519
+ return {
1520
+ manifest: currentManifest,
1521
+ rebuildCount,
1522
+ lastRebuildMs,
1523
+ lastRebuildAt
1524
+ };
1525
+ }
1526
+ };
1527
+ }
1034
1528
  export {
1035
1529
  wrapWithHydrationMarkers,
1036
1530
  toPrefetchSession,
@@ -1038,6 +1532,7 @@ export {
1038
1532
  ssrStorage,
1039
1533
  ssrRenderToString,
1040
1534
  ssrRenderSinglePass,
1535
+ ssrRenderAot,
1041
1536
  ssrDiscoverQueries,
1042
1537
  setGlobalSSRTimeout,
1043
1538
  serializeToHtml,
@@ -1054,6 +1549,7 @@ export {
1054
1549
  rawHtml,
1055
1550
  matchUrlToPatterns,
1056
1551
  isInSSR,
1552
+ isAotDebugEnabled,
1057
1553
  inlineCriticalCss,
1058
1554
  getStreamingRuntimeScript,
1059
1555
  getSSRUrl,
@@ -1061,6 +1557,7 @@ export {
1061
1557
  getGlobalSSRTimeout,
1062
1558
  getAccessSetForSSR,
1063
1559
  generateSSRHtml,
1560
+ generateAotBuildManifest,
1064
1561
  extractFontMetrics,
1065
1562
  evaluateAccessRule,
1066
1563
  encodeChunk,
@@ -1072,8 +1569,15 @@ export {
1072
1569
  createSSRDataChunk,
1073
1570
  createSSRAdapter,
1074
1571
  createPrefetchManifestManager,
1572
+ createHoles,
1573
+ createAotManifestManager,
1075
1574
  createAccessSetScript,
1076
1575
  collectStreamChunks,
1077
1576
  clearGlobalSSRTimeout,
1078
- HeadCollector
1577
+ __ssr_style_object,
1578
+ __ssr_spread,
1579
+ __esc_attr,
1580
+ __esc,
1581
+ HeadCollector,
1582
+ AotDiagnostics
1079
1583
  };