@vertz/ui-server 0.2.29 → 0.2.30

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
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  collectStreamChunks,
3
+ compileThemeCached,
3
4
  createAccessSetScript,
4
5
  createRequestContext,
5
6
  createSSRDataChunk,
@@ -10,16 +11,21 @@ import {
10
11
  encodeChunk,
11
12
  escapeAttr,
12
13
  escapeHtml,
14
+ evaluateAccessRule,
13
15
  getAccessSetForSSR,
14
16
  getStreamingRuntimeScript,
17
+ matchUrlToPatterns,
18
+ reconstructDescriptors,
15
19
  renderToStream,
16
20
  resetSlotCounter,
17
21
  safeSerialize,
18
22
  serializeToHtml,
19
23
  ssrDiscoverQueries,
24
+ ssrRenderSinglePass,
20
25
  ssrRenderToString,
21
- streamToString
22
- } from "./shared/chunk-yr65qdge.js";
26
+ streamToString,
27
+ toPrefetchSession
28
+ } from "./shared/chunk-bd0sgykf.js";
23
29
  import {
24
30
  clearGlobalSSRTimeout,
25
31
  createSSRAdapter,
@@ -384,7 +390,7 @@ ${options.head}` : headHtml;
384
390
  });
385
391
  }
386
392
  // src/render-to-html.ts
387
- import { compileTheme, getInjectedCSS } from "@vertz/ui";
393
+ import { getInjectedCSS } from "@vertz/ui";
388
394
  async function twoPassRender(options) {
389
395
  options.app();
390
396
  const queries = getSSRQueries();
@@ -403,7 +409,7 @@ async function twoPassRender(options) {
403
409
  const pendingQueries = queries.filter((q) => !q.resolved);
404
410
  const vnode = options.app();
405
411
  const collectedCSS = getInjectedCSS();
406
- const themeCss = options.theme ? compileTheme(options.theme, { fallbackMetrics: options.fallbackMetrics }).css : "";
412
+ const themeCss = options.theme ? compileThemeCached(options.theme, options.fallbackMetrics).css : "";
407
413
  const allStyles = [themeCss, ...options.styles ?? [], ...collectedCSS].filter(Boolean);
408
414
  const styleTags = allStyles.length > 0 ? `<style>${allStyles.join(`
409
415
  `)}</style>` : "";
@@ -483,48 +489,6 @@ async function renderToHTML(appOrOptions, maybeOptions) {
483
489
  }
484
490
  });
485
491
  }
486
- // src/ssr-access-evaluator.ts
487
- function toPrefetchSession(ssrAuth, accessSet) {
488
- if (!ssrAuth || ssrAuth.status !== "authenticated" || !ssrAuth.user) {
489
- return { status: "unauthenticated" };
490
- }
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;
493
- return {
494
- status: "authenticated",
495
- roles,
496
- entitlements,
497
- tenantId: ssrAuth.user.tenantId
498
- };
499
- }
500
- function evaluateAccessRule(rule, session) {
501
- switch (rule.type) {
502
- case "public":
503
- return true;
504
- case "authenticated":
505
- return session.status === "authenticated";
506
- case "role":
507
- if (session.status !== "authenticated")
508
- return false;
509
- return session.roles?.some((r) => rule.roles.includes(r)) === true;
510
- case "entitlement":
511
- if (session.status !== "authenticated")
512
- return false;
513
- return session.entitlements?.[rule.value] === true;
514
- case "where":
515
- return true;
516
- case "fva":
517
- return session.status === "authenticated";
518
- case "deny":
519
- return false;
520
- case "all":
521
- return rule.rules.every((r) => evaluateAccessRule(r, session));
522
- case "any":
523
- return rule.rules.some((r) => evaluateAccessRule(r, session));
524
- default:
525
- return false;
526
- }
527
- }
528
492
  // src/ssr-aot-diagnostics.ts
529
493
  var MAX_DIVERGENCES = 20;
530
494
 
@@ -701,429 +665,6 @@ function createAotManifestManager(options) {
701
665
  }
702
666
  };
703
667
  }
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
-
745
- // src/ssr-manifest-prefetch.ts
746
- function reconstructDescriptors(queries, routeParams, apiClient) {
747
- if (!apiClient)
748
- return [];
749
- const result = [];
750
- for (const query of queries) {
751
- const descriptor = reconstructSingle(query, routeParams, apiClient);
752
- if (descriptor) {
753
- result.push(descriptor);
754
- }
755
- }
756
- return result;
757
- }
758
- function reconstructSingle(query, routeParams, apiClient) {
759
- const { entity, operation } = query;
760
- if (!entity || !operation)
761
- return;
762
- const entitySdk = apiClient[entity];
763
- if (!entitySdk)
764
- return;
765
- const method = entitySdk[operation];
766
- if (typeof method !== "function")
767
- return;
768
- const args = buildFactoryArgs(query, routeParams);
769
- if (args === undefined)
770
- return;
771
- try {
772
- const descriptor = method(...args);
773
- if (!descriptor || typeof descriptor._key !== "string" || typeof descriptor._fetch !== "function") {
774
- return;
775
- }
776
- return { key: descriptor._key, fetch: descriptor._fetch };
777
- } catch {
778
- return;
779
- }
780
- }
781
- function buildFactoryArgs(query, routeParams) {
782
- const { operation, idParam, queryBindings } = query;
783
- if (operation === "get") {
784
- if (idParam) {
785
- const id = routeParams[idParam];
786
- if (!id)
787
- return;
788
- const options = resolveQueryBindings(queryBindings, routeParams);
789
- if (options === undefined && queryBindings)
790
- return;
791
- return options ? [id, options] : [id];
792
- }
793
- return;
794
- }
795
- if (!queryBindings)
796
- return [];
797
- const resolved = resolveQueryBindings(queryBindings, routeParams);
798
- if (resolved === undefined)
799
- return;
800
- return [resolved];
801
- }
802
- function resolveQueryBindings(bindings, routeParams) {
803
- if (!bindings)
804
- return;
805
- const resolved = {};
806
- if (bindings.where) {
807
- const where = {};
808
- for (const [key, value] of Object.entries(bindings.where)) {
809
- if (value === null)
810
- return;
811
- if (typeof value === "string" && value.startsWith("$")) {
812
- const paramName = value.slice(1);
813
- const paramValue = routeParams[paramName];
814
- if (!paramValue)
815
- return;
816
- where[key] = paramValue;
817
- } else {
818
- where[key] = value;
819
- }
820
- }
821
- resolved.where = where;
822
- }
823
- if (bindings.select)
824
- resolved.select = bindings.select;
825
- if (bindings.include)
826
- resolved.include = bindings.include;
827
- if (bindings.orderBy)
828
- resolved.orderBy = bindings.orderBy;
829
- if (bindings.limit !== undefined)
830
- resolved.limit = bindings.limit;
831
- return resolved;
832
- }
833
-
834
- // src/ssr-single-pass.ts
835
- async function ssrRenderSinglePass(module, url, options) {
836
- if (options?.prefetch === false) {
837
- return ssrRenderToString(module, url, options);
838
- }
839
- const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
840
- const ssrTimeout = options?.ssrTimeout ?? 300;
841
- ensureDomShim();
842
- const zeroDiscoveryData = attemptZeroDiscovery(normalizedUrl, module, options, ssrTimeout);
843
- if (zeroDiscoveryData) {
844
- return renderWithPrefetchedData(module, normalizedUrl, zeroDiscoveryData, options);
845
- }
846
- const discoveryCtx = createRequestContext(normalizedUrl);
847
- if (options?.ssrAuth) {
848
- discoveryCtx.ssrAuth = options.ssrAuth;
849
- }
850
- const discoveredData = await ssrStorage.run(discoveryCtx, async () => {
851
- try {
852
- setGlobalSSRTimeout(ssrTimeout);
853
- const createApp = resolveAppFactory(module);
854
- createApp();
855
- if (discoveryCtx.ssrRedirect) {
856
- return { redirect: discoveryCtx.ssrRedirect };
857
- }
858
- if (discoveryCtx.pendingRouteComponents?.size) {
859
- const entries = Array.from(discoveryCtx.pendingRouteComponents.entries());
860
- const results = await Promise.allSettled(entries.map(([route, promise]) => Promise.race([
861
- promise.then((mod) => ({ route, factory: mod.default })),
862
- new Promise((_, reject) => setTimeout(() => reject(new Error("lazy route timeout")), ssrTimeout))
863
- ])));
864
- discoveryCtx.resolvedComponents = new Map;
865
- for (const result of results) {
866
- if (result.status === "fulfilled") {
867
- const { route, factory } = result.value;
868
- discoveryCtx.resolvedComponents.set(route, factory);
869
- }
870
- }
871
- discoveryCtx.pendingRouteComponents = undefined;
872
- }
873
- const queries = getSSRQueries();
874
- const eligibleQueries = filterByEntityAccess(queries, options?.manifest?.entityAccess, options?.prefetchSession);
875
- const resolvedQueries = [];
876
- if (eligibleQueries.length > 0) {
877
- await Promise.allSettled(eligibleQueries.map(({ promise, timeout, resolve, key }) => Promise.race([
878
- promise.then((data) => {
879
- resolve(data);
880
- resolvedQueries.push({ key, data });
881
- return "resolved";
882
- }),
883
- new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
884
- ])));
885
- }
886
- return {
887
- resolvedQueries,
888
- resolvedComponents: discoveryCtx.resolvedComponents
889
- };
890
- } finally {
891
- clearGlobalSSRTimeout();
892
- }
893
- });
894
- if ("redirect" in discoveredData) {
895
- return {
896
- html: "",
897
- css: "",
898
- ssrData: [],
899
- headTags: "",
900
- redirect: discoveredData.redirect
901
- };
902
- }
903
- const renderCtx = createRequestContext(normalizedUrl);
904
- if (options?.ssrAuth) {
905
- renderCtx.ssrAuth = options.ssrAuth;
906
- }
907
- for (const { key, data } of discoveredData.resolvedQueries) {
908
- renderCtx.queryCache.set(key, data);
909
- }
910
- renderCtx.resolvedComponents = discoveredData.resolvedComponents ?? new Map;
911
- return ssrStorage.run(renderCtx, async () => {
912
- try {
913
- setGlobalSSRTimeout(ssrTimeout);
914
- const createApp = resolveAppFactory(module);
915
- let themeCss = "";
916
- let themePreloadTags = "";
917
- if (module.theme) {
918
- try {
919
- const compiled = compileTheme2(module.theme, {
920
- fallbackMetrics: options?.fallbackMetrics
921
- });
922
- themeCss = compiled.css;
923
- themePreloadTags = compiled.preloadTags;
924
- } catch (e) {
925
- console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
926
- }
927
- }
928
- const app = createApp();
929
- const vnode = toVNode(app);
930
- const stream = renderToStream(vnode);
931
- const html = await streamToString(stream);
932
- const css = collectCSS(themeCss, module);
933
- const ssrData = discoveredData.resolvedQueries.map(({ key, data }) => ({
934
- key,
935
- data: JSON.parse(JSON.stringify(data))
936
- }));
937
- return {
938
- html,
939
- css,
940
- ssrData,
941
- headTags: themePreloadTags,
942
- discoveredRoutes: renderCtx.discoveredRoutes,
943
- matchedRoutePatterns: renderCtx.matchedRoutePatterns
944
- };
945
- } finally {
946
- clearGlobalSSRTimeout();
947
- }
948
- });
949
- }
950
- function attemptZeroDiscovery(url, module, options, ssrTimeout) {
951
- const manifest = options?.manifest;
952
- if (!manifest?.routeEntries || !module.api)
953
- return null;
954
- const matches = matchUrlToPatterns(url, manifest.routePatterns);
955
- if (matches.length === 0)
956
- return null;
957
- const allQueries = [];
958
- let mergedParams = {};
959
- for (const match of matches) {
960
- const entry = manifest.routeEntries[match.pattern];
961
- if (entry) {
962
- allQueries.push(...entry.queries);
963
- }
964
- mergedParams = { ...mergedParams, ...match.params };
965
- }
966
- if (allQueries.length === 0)
967
- return null;
968
- const descriptors = reconstructDescriptors(allQueries, mergedParams, module.api);
969
- if (descriptors.length === 0)
970
- return null;
971
- return prefetchFromDescriptors(descriptors, ssrTimeout);
972
- }
973
- async function prefetchFromDescriptors(descriptors, ssrTimeout) {
974
- const resolvedQueries = [];
975
- await Promise.allSettled(descriptors.map(({ key, fetch: fetchFn }) => Promise.race([
976
- fetchFn().then((result) => {
977
- const data = unwrapResult(result);
978
- resolvedQueries.push({ key, data });
979
- return "resolved";
980
- }),
981
- new Promise((r) => setTimeout(r, ssrTimeout)).then(() => "timeout")
982
- ])));
983
- return { resolvedQueries };
984
- }
985
- function unwrapResult(result) {
986
- if (result && typeof result === "object" && "ok" in result && "data" in result) {
987
- const r = result;
988
- if (r.ok)
989
- return r.data;
990
- }
991
- return result;
992
- }
993
- async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, options) {
994
- const data = await prefetchedData;
995
- const ssrTimeout = options?.ssrTimeout ?? 300;
996
- const renderCtx = createRequestContext(normalizedUrl);
997
- if (options?.ssrAuth) {
998
- renderCtx.ssrAuth = options.ssrAuth;
999
- }
1000
- for (const { key, data: queryData } of data.resolvedQueries) {
1001
- renderCtx.queryCache.set(key, queryData);
1002
- }
1003
- renderCtx.resolvedComponents = new Map;
1004
- return ssrStorage.run(renderCtx, async () => {
1005
- try {
1006
- setGlobalSSRTimeout(ssrTimeout);
1007
- const createApp = resolveAppFactory(module);
1008
- let themeCss = "";
1009
- let themePreloadTags = "";
1010
- if (module.theme) {
1011
- try {
1012
- const compiled = compileTheme2(module.theme, {
1013
- fallbackMetrics: options?.fallbackMetrics
1014
- });
1015
- themeCss = compiled.css;
1016
- themePreloadTags = compiled.preloadTags;
1017
- } catch (e) {
1018
- console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
1019
- }
1020
- }
1021
- const app = createApp();
1022
- const vnode = toVNode(app);
1023
- const stream = renderToStream(vnode);
1024
- const html = await streamToString(stream);
1025
- if (renderCtx.ssrRedirect) {
1026
- return {
1027
- html: "",
1028
- css: "",
1029
- ssrData: [],
1030
- headTags: "",
1031
- redirect: renderCtx.ssrRedirect,
1032
- discoveredRoutes: renderCtx.discoveredRoutes,
1033
- matchedRoutePatterns: renderCtx.matchedRoutePatterns
1034
- };
1035
- }
1036
- const css = collectCSS(themeCss, module);
1037
- const ssrData = data.resolvedQueries.map(({ key, data: d }) => ({
1038
- key,
1039
- data: JSON.parse(JSON.stringify(d))
1040
- }));
1041
- return {
1042
- html,
1043
- css,
1044
- ssrData,
1045
- headTags: themePreloadTags,
1046
- discoveredRoutes: renderCtx.discoveredRoutes,
1047
- matchedRoutePatterns: renderCtx.matchedRoutePatterns
1048
- };
1049
- } finally {
1050
- clearGlobalSSRTimeout();
1051
- }
1052
- });
1053
- }
1054
- var domShimInstalled = false;
1055
- function ensureDomShim() {
1056
- if (domShimInstalled && typeof document !== "undefined")
1057
- return;
1058
- domShimInstalled = true;
1059
- installDomShim();
1060
- }
1061
- function resolveAppFactory(module) {
1062
- const createApp = module.default || module.App;
1063
- if (typeof createApp !== "function") {
1064
- throw new Error("App entry must export a default function or named App function");
1065
- }
1066
- return createApp;
1067
- }
1068
- function filterByEntityAccess(queries, entityAccess, session) {
1069
- if (!entityAccess || !session)
1070
- return queries;
1071
- return queries.filter(({ key }) => {
1072
- const entity = extractEntityFromKey(key);
1073
- const method = extractMethodFromKey(key);
1074
- if (!entity)
1075
- return true;
1076
- const entityRules = entityAccess[entity];
1077
- if (!entityRules)
1078
- return true;
1079
- const rule = entityRules[method];
1080
- if (!rule)
1081
- return true;
1082
- return evaluateAccessRule(rule, session);
1083
- });
1084
- }
1085
- function extractEntityFromKey(key) {
1086
- const pathStart = key.indexOf(":/");
1087
- if (pathStart === -1)
1088
- return;
1089
- const path = key.slice(pathStart + 2);
1090
- const firstSlash = path.indexOf("/");
1091
- const questionMark = path.indexOf("?");
1092
- if (firstSlash === -1 && questionMark === -1)
1093
- return path;
1094
- if (firstSlash === -1)
1095
- return path.slice(0, questionMark);
1096
- if (questionMark === -1)
1097
- return path.slice(0, firstSlash);
1098
- return path.slice(0, Math.min(firstSlash, questionMark));
1099
- }
1100
- function extractMethodFromKey(key) {
1101
- const pathStart = key.indexOf(":/");
1102
- if (pathStart === -1)
1103
- return "list";
1104
- const path = key.slice(pathStart + 2);
1105
- const cleanPath = path.split("?")[0] ?? "";
1106
- const segments = cleanPath.split("/").filter(Boolean);
1107
- return segments.length > 1 ? "get" : "list";
1108
- }
1109
- function collectCSS(themeCss, module) {
1110
- const alreadyIncluded = new Set;
1111
- if (themeCss)
1112
- alreadyIncluded.add(themeCss);
1113
- if (module.styles) {
1114
- for (const s of module.styles)
1115
- alreadyIncluded.add(s);
1116
- }
1117
- const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
1118
- const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
1119
- const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
1120
- `)}</style>` : "";
1121
- const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
1122
- `)}</style>` : "";
1123
- return [themeTag, globalTag, componentTag].filter(Boolean).join(`
1124
- `);
1125
- }
1126
-
1127
668
  // src/ssr-aot-pipeline.ts
1128
669
  function createHoles(holeNames, module, url, queryCache, ssrAuth) {
1129
670
  if (holeNames.length === 0)
@@ -1140,7 +681,7 @@ function createHoles(holeNames, module, url, queryCache, ssrAuth) {
1140
681
  }
1141
682
  holeCtx.resolvedComponents = new Map;
1142
683
  return ssrStorage.run(holeCtx, () => {
1143
- ensureDomShim2();
684
+ ensureDomShim();
1144
685
  const factory = resolveHoleComponent(module, name);
1145
686
  if (!factory) {
1146
687
  return `<!-- AOT hole: ${name} not found -->`;
@@ -1211,7 +752,7 @@ async function ssrRenderAot(module, url, options) {
1211
752
  const css = collectCSSFromModule(module, options.fallbackMetrics);
1212
753
  const ssrData = [];
1213
754
  for (const [key, data2] of queryCache) {
1214
- ssrData.push({ key, data: JSON.parse(JSON.stringify(data2)) });
755
+ ssrData.push({ key, data: data2 });
1215
756
  }
1216
757
  return {
1217
758
  html,
@@ -1229,11 +770,11 @@ function isAotDebugEnabled() {
1229
770
  return false;
1230
771
  return env === "1" || env.split(",").includes("aot");
1231
772
  }
1232
- var domShimInstalled2 = false;
1233
- function ensureDomShim2() {
1234
- if (domShimInstalled2 && typeof document !== "undefined")
773
+ var domShimInstalled = false;
774
+ function ensureDomShim() {
775
+ if (domShimInstalled && typeof document !== "undefined")
1235
776
  return;
1236
- domShimInstalled2 = true;
777
+ domShimInstalled = true;
1237
778
  installDomShim();
1238
779
  }
1239
780
  function collectCSSFromModule(module, fallbackMetrics) {
@@ -1241,7 +782,7 @@ function collectCSSFromModule(module, fallbackMetrics) {
1241
782
  let preloadTags = "";
1242
783
  if (module.theme) {
1243
784
  try {
1244
- const compiled = compileTheme3(module.theme, { fallbackMetrics });
785
+ const compiled = compileThemeCached(module.theme, fallbackMetrics);
1245
786
  themeCss = compiled.css;
1246
787
  preloadTags = compiled.preloadTags;
1247
788
  } catch (e) {