@vertz/ui-server 0.2.24 → 0.2.25

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
@@ -19,19 +19,21 @@ import {
19
19
  ssrDiscoverQueries,
20
20
  ssrRenderToString,
21
21
  streamToString
22
- } from "./shared/chunk-g0zqrb60.js";
22
+ } from "./shared/chunk-yr65qdge.js";
23
23
  import {
24
24
  clearGlobalSSRTimeout,
25
25
  createSSRAdapter,
26
26
  getGlobalSSRTimeout,
27
27
  getSSRQueries,
28
28
  getSSRUrl,
29
+ installDomShim,
29
30
  isInSSR,
30
31
  rawHtml,
31
32
  registerSSRQuery,
32
33
  setGlobalSSRTimeout,
33
- ssrStorage
34
- } from "./shared/chunk-zs75v8qj.js";
34
+ ssrStorage,
35
+ toVNode
36
+ } from "./shared/chunk-gcwqkynf.js";
35
37
 
36
38
  // src/asset-pipeline.ts
37
39
  function renderAssetTags(assets) {
@@ -425,6 +427,46 @@ async function renderToHTML(appOrOptions, maybeOptions) {
425
427
  }
426
428
  });
427
429
  }
430
+ // src/ssr-access-evaluator.ts
431
+ function toPrefetchSession(ssrAuth) {
432
+ if (!ssrAuth || ssrAuth.status !== "authenticated" || !ssrAuth.user) {
433
+ return { status: "unauthenticated" };
434
+ }
435
+ const roles = ssrAuth.user.role ? [ssrAuth.user.role] : undefined;
436
+ return {
437
+ status: "authenticated",
438
+ roles,
439
+ tenantId: ssrAuth.user.tenantId
440
+ };
441
+ }
442
+ function evaluateAccessRule(rule, session) {
443
+ switch (rule.type) {
444
+ case "public":
445
+ return true;
446
+ case "authenticated":
447
+ return session.status === "authenticated";
448
+ case "role":
449
+ if (session.status !== "authenticated")
450
+ return false;
451
+ return session.roles?.some((r) => rule.roles.includes(r)) === true;
452
+ case "entitlement":
453
+ if (session.status !== "authenticated")
454
+ return false;
455
+ return session.entitlements?.[rule.value] === true;
456
+ case "where":
457
+ return true;
458
+ case "fva":
459
+ return session.status === "authenticated";
460
+ case "deny":
461
+ return false;
462
+ case "all":
463
+ return rule.rules.every((r) => evaluateAccessRule(r, session));
464
+ case "any":
465
+ return rule.rules.some((r) => evaluateAccessRule(r, session));
466
+ default:
467
+ return false;
468
+ }
469
+ }
428
470
  // src/ssr-html.ts
429
471
  function generateSSRHtml(options) {
430
472
  const {
@@ -457,11 +499,545 @@ function generateSSRHtml(options) {
457
499
  </body>
458
500
  </html>`;
459
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);
523
+ }
524
+ }
525
+ return index;
526
+ }
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);
533
+ } else {
534
+ routeEntries[route.pattern] = { queries: [...route.queries] };
535
+ }
536
+ }
537
+ return {
538
+ routePatterns: [...new Set(manifest.routes.map((r) => r.pattern))],
539
+ routeEntries
540
+ };
541
+ }
542
+ function fullBuild(routerSourceOverride) {
543
+ const start = performance.now();
544
+ const routerSource = routerSourceOverride ?? readFile2(routerPath);
545
+ if (!routerSource) {
546
+ return;
547
+ }
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 {}
562
+ }
563
+ function incrementalUpdate(filePath, sourceText) {
564
+ if (!currentManifest)
565
+ return;
566
+ const indices = fileToRouteIndices.get(filePath);
567
+ if (!indices || indices.length === 0)
568
+ return;
569
+ 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
+ }
582
+ }
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 {}
594
+ }
595
+ return {
596
+ build() {
597
+ fullBuild();
598
+ },
599
+ onFileChange(filePath, sourceText) {
600
+ if (filePath === routerPath) {
601
+ fullBuild(sourceText);
602
+ } else {
603
+ incrementalUpdate(filePath, sourceText);
604
+ }
605
+ },
606
+ getSSRManifest() {
607
+ return currentSSRManifest;
608
+ },
609
+ getSnapshot() {
610
+ return {
611
+ manifest: currentManifest,
612
+ rebuildCount,
613
+ lastRebuildMs,
614
+ lastRebuildAt
615
+ };
616
+ }
617
+ };
618
+ }
619
+ // src/ssr-manifest-prefetch.ts
620
+ function reconstructDescriptors(queries, routeParams, apiClient) {
621
+ if (!apiClient)
622
+ return [];
623
+ const result = [];
624
+ for (const query of queries) {
625
+ const descriptor = reconstructSingle(query, routeParams, apiClient);
626
+ if (descriptor) {
627
+ result.push(descriptor);
628
+ }
629
+ }
630
+ return result;
631
+ }
632
+ function reconstructSingle(query, routeParams, apiClient) {
633
+ const { entity, operation } = query;
634
+ if (!entity || !operation)
635
+ return;
636
+ const entitySdk = apiClient[entity];
637
+ if (!entitySdk)
638
+ return;
639
+ const method = entitySdk[operation];
640
+ if (typeof method !== "function")
641
+ return;
642
+ const args = buildFactoryArgs(query, routeParams);
643
+ if (args === undefined)
644
+ return;
645
+ try {
646
+ const descriptor = method(...args);
647
+ if (!descriptor || typeof descriptor._key !== "string" || typeof descriptor._fetch !== "function") {
648
+ return;
649
+ }
650
+ return { key: descriptor._key, fetch: descriptor._fetch };
651
+ } catch {
652
+ return;
653
+ }
654
+ }
655
+ function buildFactoryArgs(query, routeParams) {
656
+ const { operation, idParam, queryBindings } = query;
657
+ if (operation === "get") {
658
+ if (idParam) {
659
+ const id = routeParams[idParam];
660
+ if (!id)
661
+ return;
662
+ const options = resolveQueryBindings(queryBindings, routeParams);
663
+ if (options === undefined && queryBindings)
664
+ return;
665
+ return options ? [id, options] : [id];
666
+ }
667
+ return;
668
+ }
669
+ if (!queryBindings)
670
+ return [];
671
+ const resolved = resolveQueryBindings(queryBindings, routeParams);
672
+ if (resolved === undefined)
673
+ return;
674
+ return [resolved];
675
+ }
676
+ function resolveQueryBindings(bindings, routeParams) {
677
+ if (!bindings)
678
+ return;
679
+ const resolved = {};
680
+ if (bindings.where) {
681
+ const where = {};
682
+ for (const [key, value] of Object.entries(bindings.where)) {
683
+ if (value === null)
684
+ return;
685
+ if (typeof value === "string" && value.startsWith("$")) {
686
+ const paramName = value.slice(1);
687
+ const paramValue = routeParams[paramName];
688
+ if (!paramValue)
689
+ return;
690
+ where[key] = paramValue;
691
+ } else {
692
+ where[key] = value;
693
+ }
694
+ }
695
+ resolved.where = where;
696
+ }
697
+ if (bindings.select)
698
+ resolved.select = bindings.select;
699
+ if (bindings.include)
700
+ resolved.include = bindings.include;
701
+ if (bindings.orderBy)
702
+ resolved.orderBy = bindings.orderBy;
703
+ if (bindings.limit !== undefined)
704
+ resolved.limit = bindings.limit;
705
+ return resolved;
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
+ // src/ssr-single-pass.ts
742
+ import { compileTheme as compileTheme2 } from "@vertz/ui";
743
+ async function ssrRenderSinglePass(module, url, options) {
744
+ if (options?.prefetch === false) {
745
+ return ssrRenderToString(module, url, options);
746
+ }
747
+ const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
748
+ const ssrTimeout = options?.ssrTimeout ?? 300;
749
+ ensureDomShim();
750
+ const zeroDiscoveryData = attemptZeroDiscovery(normalizedUrl, module, options, ssrTimeout);
751
+ if (zeroDiscoveryData) {
752
+ return renderWithPrefetchedData(module, normalizedUrl, zeroDiscoveryData, options);
753
+ }
754
+ const discoveryCtx = createRequestContext(normalizedUrl);
755
+ if (options?.ssrAuth) {
756
+ discoveryCtx.ssrAuth = options.ssrAuth;
757
+ }
758
+ const discoveredData = await ssrStorage.run(discoveryCtx, async () => {
759
+ try {
760
+ setGlobalSSRTimeout(ssrTimeout);
761
+ const createApp = resolveAppFactory(module);
762
+ createApp();
763
+ if (discoveryCtx.ssrRedirect) {
764
+ return { redirect: discoveryCtx.ssrRedirect };
765
+ }
766
+ if (discoveryCtx.pendingRouteComponents?.size) {
767
+ const entries = Array.from(discoveryCtx.pendingRouteComponents.entries());
768
+ const results = await Promise.allSettled(entries.map(([route, promise]) => Promise.race([
769
+ promise.then((mod) => ({ route, factory: mod.default })),
770
+ new Promise((_, reject) => setTimeout(() => reject(new Error("lazy route timeout")), ssrTimeout))
771
+ ])));
772
+ discoveryCtx.resolvedComponents = new Map;
773
+ for (const result of results) {
774
+ if (result.status === "fulfilled") {
775
+ const { route, factory } = result.value;
776
+ discoveryCtx.resolvedComponents.set(route, factory);
777
+ }
778
+ }
779
+ discoveryCtx.pendingRouteComponents = undefined;
780
+ }
781
+ const queries = getSSRQueries();
782
+ const eligibleQueries = filterByEntityAccess(queries, options?.manifest?.entityAccess, options?.prefetchSession);
783
+ const resolvedQueries = [];
784
+ if (eligibleQueries.length > 0) {
785
+ await Promise.allSettled(eligibleQueries.map(({ promise, timeout, resolve, key }) => Promise.race([
786
+ promise.then((data) => {
787
+ resolve(data);
788
+ resolvedQueries.push({ key, data });
789
+ return "resolved";
790
+ }),
791
+ new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
792
+ ])));
793
+ }
794
+ return {
795
+ resolvedQueries,
796
+ resolvedComponents: discoveryCtx.resolvedComponents
797
+ };
798
+ } finally {
799
+ clearGlobalSSRTimeout();
800
+ }
801
+ });
802
+ if ("redirect" in discoveredData) {
803
+ return {
804
+ html: "",
805
+ css: "",
806
+ ssrData: [],
807
+ headTags: "",
808
+ redirect: discoveredData.redirect
809
+ };
810
+ }
811
+ const renderCtx = createRequestContext(normalizedUrl);
812
+ if (options?.ssrAuth) {
813
+ renderCtx.ssrAuth = options.ssrAuth;
814
+ }
815
+ for (const { key, data } of discoveredData.resolvedQueries) {
816
+ renderCtx.queryCache.set(key, data);
817
+ }
818
+ renderCtx.resolvedComponents = discoveredData.resolvedComponents ?? new Map;
819
+ return ssrStorage.run(renderCtx, async () => {
820
+ try {
821
+ setGlobalSSRTimeout(ssrTimeout);
822
+ const createApp = resolveAppFactory(module);
823
+ let themeCss = "";
824
+ let themePreloadTags = "";
825
+ if (module.theme) {
826
+ try {
827
+ const compiled = compileTheme2(module.theme, {
828
+ fallbackMetrics: options?.fallbackMetrics
829
+ });
830
+ themeCss = compiled.css;
831
+ themePreloadTags = compiled.preloadTags;
832
+ } catch (e) {
833
+ console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
834
+ }
835
+ }
836
+ const app = createApp();
837
+ const vnode = toVNode(app);
838
+ const stream = renderToStream(vnode);
839
+ const html = await streamToString(stream);
840
+ const css = collectCSS(themeCss, module);
841
+ const ssrData = discoveredData.resolvedQueries.map(({ key, data }) => ({
842
+ key,
843
+ data: JSON.parse(JSON.stringify(data))
844
+ }));
845
+ return {
846
+ html,
847
+ css,
848
+ ssrData,
849
+ headTags: themePreloadTags,
850
+ discoveredRoutes: renderCtx.discoveredRoutes,
851
+ matchedRoutePatterns: renderCtx.matchedRoutePatterns
852
+ };
853
+ } finally {
854
+ clearGlobalSSRTimeout();
855
+ }
856
+ });
857
+ }
858
+ function attemptZeroDiscovery(url, module, options, ssrTimeout) {
859
+ const manifest = options?.manifest;
860
+ if (!manifest?.routeEntries || !module.api)
861
+ return null;
862
+ const matches = matchUrlToPatterns(url, manifest.routePatterns);
863
+ if (matches.length === 0)
864
+ return null;
865
+ const allQueries = [];
866
+ let mergedParams = {};
867
+ for (const match of matches) {
868
+ const entry = manifest.routeEntries[match.pattern];
869
+ if (entry) {
870
+ allQueries.push(...entry.queries);
871
+ }
872
+ mergedParams = { ...mergedParams, ...match.params };
873
+ }
874
+ if (allQueries.length === 0)
875
+ return null;
876
+ const descriptors = reconstructDescriptors(allQueries, mergedParams, module.api);
877
+ if (descriptors.length === 0)
878
+ return null;
879
+ return prefetchFromDescriptors(descriptors, ssrTimeout);
880
+ }
881
+ async function prefetchFromDescriptors(descriptors, ssrTimeout) {
882
+ const resolvedQueries = [];
883
+ await Promise.allSettled(descriptors.map(({ key, fetch: fetchFn }) => Promise.race([
884
+ fetchFn().then((result) => {
885
+ const data = unwrapResult(result);
886
+ resolvedQueries.push({ key, data });
887
+ return "resolved";
888
+ }),
889
+ new Promise((r) => setTimeout(r, ssrTimeout)).then(() => "timeout")
890
+ ])));
891
+ return { resolvedQueries };
892
+ }
893
+ function unwrapResult(result) {
894
+ if (result && typeof result === "object" && "ok" in result && "data" in result) {
895
+ const r = result;
896
+ if (r.ok)
897
+ return r.data;
898
+ }
899
+ return result;
900
+ }
901
+ async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, options) {
902
+ const data = await prefetchedData;
903
+ const ssrTimeout = options?.ssrTimeout ?? 300;
904
+ const renderCtx = createRequestContext(normalizedUrl);
905
+ if (options?.ssrAuth) {
906
+ renderCtx.ssrAuth = options.ssrAuth;
907
+ }
908
+ for (const { key, data: queryData } of data.resolvedQueries) {
909
+ renderCtx.queryCache.set(key, queryData);
910
+ }
911
+ renderCtx.resolvedComponents = new Map;
912
+ return ssrStorage.run(renderCtx, async () => {
913
+ try {
914
+ setGlobalSSRTimeout(ssrTimeout);
915
+ const createApp = resolveAppFactory(module);
916
+ let themeCss = "";
917
+ let themePreloadTags = "";
918
+ if (module.theme) {
919
+ try {
920
+ const compiled = compileTheme2(module.theme, {
921
+ fallbackMetrics: options?.fallbackMetrics
922
+ });
923
+ themeCss = compiled.css;
924
+ themePreloadTags = compiled.preloadTags;
925
+ } catch (e) {
926
+ console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
927
+ }
928
+ }
929
+ const app = createApp();
930
+ const vnode = toVNode(app);
931
+ const stream = renderToStream(vnode);
932
+ const html = await streamToString(stream);
933
+ if (renderCtx.ssrRedirect) {
934
+ return {
935
+ html: "",
936
+ css: "",
937
+ ssrData: [],
938
+ headTags: "",
939
+ redirect: renderCtx.ssrRedirect,
940
+ discoveredRoutes: renderCtx.discoveredRoutes,
941
+ matchedRoutePatterns: renderCtx.matchedRoutePatterns
942
+ };
943
+ }
944
+ const css = collectCSS(themeCss, module);
945
+ const ssrData = data.resolvedQueries.map(({ key, data: d }) => ({
946
+ key,
947
+ data: JSON.parse(JSON.stringify(d))
948
+ }));
949
+ return {
950
+ html,
951
+ css,
952
+ ssrData,
953
+ headTags: themePreloadTags,
954
+ discoveredRoutes: renderCtx.discoveredRoutes,
955
+ matchedRoutePatterns: renderCtx.matchedRoutePatterns
956
+ };
957
+ } finally {
958
+ clearGlobalSSRTimeout();
959
+ }
960
+ });
961
+ }
962
+ var domShimInstalled = false;
963
+ function ensureDomShim() {
964
+ if (domShimInstalled && typeof document !== "undefined")
965
+ return;
966
+ domShimInstalled = true;
967
+ installDomShim();
968
+ }
969
+ function resolveAppFactory(module) {
970
+ const createApp = module.default || module.App;
971
+ if (typeof createApp !== "function") {
972
+ throw new Error("App entry must export a default function or named App function");
973
+ }
974
+ return createApp;
975
+ }
976
+ function filterByEntityAccess(queries, entityAccess, session) {
977
+ if (!entityAccess || !session)
978
+ return queries;
979
+ return queries.filter(({ key }) => {
980
+ const entity = extractEntityFromKey(key);
981
+ const method = extractMethodFromKey(key);
982
+ if (!entity)
983
+ return true;
984
+ const entityRules = entityAccess[entity];
985
+ if (!entityRules)
986
+ return true;
987
+ const rule = entityRules[method];
988
+ if (!rule)
989
+ return true;
990
+ return evaluateAccessRule(rule, session);
991
+ });
992
+ }
993
+ function extractEntityFromKey(key) {
994
+ const pathStart = key.indexOf(":/");
995
+ if (pathStart === -1)
996
+ return;
997
+ const path = key.slice(pathStart + 2);
998
+ const firstSlash = path.indexOf("/");
999
+ const questionMark = path.indexOf("?");
1000
+ if (firstSlash === -1 && questionMark === -1)
1001
+ return path;
1002
+ if (firstSlash === -1)
1003
+ return path.slice(0, questionMark);
1004
+ if (questionMark === -1)
1005
+ return path.slice(0, firstSlash);
1006
+ return path.slice(0, Math.min(firstSlash, questionMark));
1007
+ }
1008
+ function extractMethodFromKey(key) {
1009
+ const pathStart = key.indexOf(":/");
1010
+ if (pathStart === -1)
1011
+ return "list";
1012
+ const path = key.slice(pathStart + 2);
1013
+ const cleanPath = path.split("?")[0] ?? "";
1014
+ const segments = cleanPath.split("/").filter(Boolean);
1015
+ return segments.length > 1 ? "get" : "list";
1016
+ }
1017
+ function collectCSS(themeCss, module) {
1018
+ const alreadyIncluded = new Set;
1019
+ if (themeCss)
1020
+ alreadyIncluded.add(themeCss);
1021
+ if (module.styles) {
1022
+ for (const s of module.styles)
1023
+ alreadyIncluded.add(s);
1024
+ }
1025
+ const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
1026
+ const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
1027
+ const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
1028
+ `)}</style>` : "";
1029
+ const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
1030
+ `)}</style>` : "";
1031
+ return [themeTag, globalTag, componentTag].filter(Boolean).join(`
1032
+ `);
1033
+ }
460
1034
  export {
461
1035
  wrapWithHydrationMarkers,
1036
+ toPrefetchSession,
462
1037
  streamToString,
463
1038
  ssrStorage,
464
1039
  ssrRenderToString,
1040
+ ssrRenderSinglePass,
465
1041
  ssrDiscoverQueries,
466
1042
  setGlobalSSRTimeout,
467
1043
  serializeToHtml,
@@ -474,7 +1050,9 @@ export {
474
1050
  renderHeadToHtml,
475
1051
  renderAssetTags,
476
1052
  registerSSRQuery,
1053
+ reconstructDescriptors,
477
1054
  rawHtml,
1055
+ matchUrlToPatterns,
478
1056
  isInSSR,
479
1057
  inlineCriticalCss,
480
1058
  getStreamingRuntimeScript,
@@ -484,6 +1062,7 @@ export {
484
1062
  getAccessSetForSSR,
485
1063
  generateSSRHtml,
486
1064
  extractFontMetrics,
1065
+ evaluateAccessRule,
487
1066
  encodeChunk,
488
1067
  detectFallbackFont,
489
1068
  createTemplateChunk,
@@ -492,6 +1071,7 @@ export {
492
1071
  createSSRHandler,
493
1072
  createSSRDataChunk,
494
1073
  createSSRAdapter,
1074
+ createPrefetchManifestManager,
495
1075
  createAccessSetScript,
496
1076
  collectStreamChunks,
497
1077
  clearGlobalSSRTimeout,
@@ -3,6 +3,7 @@ import { setAdapter } from "@vertz/ui/internals";
3
3
 
4
4
  // src/dom-shim/ssr-node.ts
5
5
  class SSRNode {
6
+ nodeType = 1;
6
7
  childNodes = [];
7
8
  parentNode = null;
8
9
  get firstChild() {
@@ -48,6 +49,7 @@ class SSRNode {
48
49
 
49
50
  // src/dom-shim/ssr-comment.ts
50
51
  class SSRComment extends SSRNode {
52
+ nodeType = 8;
51
53
  text;
52
54
  constructor(text) {
53
55
  super();
@@ -71,6 +73,7 @@ function rawHtml(html) {
71
73
 
72
74
  // src/dom-shim/ssr-text-node.ts
73
75
  class SSRTextNode extends SSRNode {
76
+ nodeType = 3;
74
77
  text;
75
78
  constructor(text) {
76
79
  super();
@@ -86,6 +89,7 @@ class SSRTextNode extends SSRNode {
86
89
 
87
90
  // src/dom-shim/ssr-fragment.ts
88
91
  class SSRDocumentFragment extends SSRNode {
92
+ nodeType = 11;
89
93
  children = [];
90
94
  appendChild(child) {
91
95
  if (child instanceof SSRTextNode) {
@@ -6,7 +6,7 @@ import {
6
6
  setGlobalSSRTimeout,
7
7
  ssrStorage,
8
8
  toVNode
9
- } from "./chunk-zs75v8qj.js";
9
+ } from "./chunk-gcwqkynf.js";
10
10
 
11
11
  // src/html-serializer.ts
12
12
  var VOID_ELEMENTS = new Set([
@@ -17,6 +17,8 @@ interface SSRModule {
17
17
  getInjectedCSS?: () => string[];
18
18
  /** Compiled routes exported from the app for build-time SSG with generateParams. */
19
19
  routes?: CompiledRoute[];
20
+ /** Code-generated API client for manifest-driven zero-discovery prefetching. */
21
+ api?: Record<string, Record<string, (...args: unknown[]) => unknown>>;
20
22
  }
21
23
  interface SSRRenderResult {
22
24
  html: string;
package/dist/ssr/index.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  injectIntoTemplate,
4
4
  ssrDiscoverQueries,
5
5
  ssrRenderToString
6
- } from "../shared/chunk-g0zqrb60.js";
7
- import"../shared/chunk-zs75v8qj.js";
6
+ } from "../shared/chunk-yr65qdge.js";
7
+ import"../shared/chunk-gcwqkynf.js";
8
8
 
9
9
  // src/prerender.ts
10
10
  async function discoverRoutes(module) {