@bleedingdev/modern-js-create 3.2.0-ultramodern.119 → 3.2.0-ultramodern.120

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.
@@ -60,23 +60,23 @@ const TANSTACK_ROUTER_VERSION = '1.170.15';
60
60
  const MODULE_FEDERATION_VERSION = '2.5.1';
61
61
  const ZEPHYR_RSPACK_PLUGIN_VERSION = '1.1.1';
62
62
  const ZEPHYR_AGENT_VERSION = '1.1.1';
63
- const WRANGLER_VERSION = '4.98.0';
63
+ const WRANGLER_VERSION = '4.99.0';
64
64
  const CLOUDFLARE_COMPATIBILITY_DATE = '2026-06-02';
65
65
  const TAILWIND_VERSION = '4.3.0';
66
66
  const TAILWIND_POSTCSS_VERSION = '4.3.0';
67
67
  const POSTCSS_VERSION = '8.5.15';
68
- const EFFECT_TSGO_VERSION = '0.14.0';
68
+ const EFFECT_TSGO_VERSION = '0.14.3';
69
69
  const TYPESCRIPT_VERSION = '6.0.3';
70
- const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260606.1';
71
- const OXLINT_VERSION = '1.68.0';
72
- const OXFMT_VERSION = '0.53.0';
73
- const ULTRACITE_VERSION = '7.8.1';
70
+ const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260610.1';
71
+ const OXLINT_VERSION = '1.69.0';
72
+ const OXFMT_VERSION = '0.54.0';
73
+ const ULTRACITE_VERSION = '7.8.3';
74
74
  const LEFTHOOK_VERSION = '^2.1.9';
75
75
  const I18NEXT_VERSION = '26.3.1';
76
76
  const REACT_VERSION = '^19.2.7';
77
77
  const REACT_DOM_VERSION = '^19.2.7';
78
78
  const REACT_ROUTER_DOM_VERSION = '7.17.0';
79
- const PNPM_VERSION = '11.5.2';
79
+ const PNPM_VERSION = '11.5.3';
80
80
  const GENERATED_CONTRACT_PATH = '.modernjs/ultramodern-generated-contract.json';
81
81
  const RSTACK_AGENT_SKILLS_COMMIT = '61c948b42512e223bad44b83af4080eba48b2677';
82
82
  const MODULE_FEDERATION_AGENT_SKILLS_COMMIT = '07bb5b6c43ad457609e00c081b72d4c42508ec76';
@@ -673,9 +673,67 @@ function createCloudflareSecurityContract() {
673
673
  }
674
674
  };
675
675
  }
676
+ const PUBLIC_WEBSITE_POLICY = {
677
+ qualityGates: {
678
+ publicRoutes: {
679
+ requireSitemapWhenPresent: true,
680
+ requireRobotsSitemapConsistency: true,
681
+ requireWebManifestWhenPresent: true
682
+ },
683
+ statusCodes: {
684
+ notFoundRoute: '/__ultramodern-smoke-missing',
685
+ unknownRouteStatus: 404
686
+ },
687
+ indexing: {
688
+ previewNoindex: true,
689
+ productionPublicRoutesIndexable: true
690
+ },
691
+ assets: {
692
+ cssPreloadRequired: true,
693
+ cssResponseRequired: true,
694
+ cacheControlRequiredForCss: true,
695
+ sourcemapsPubliclyReferenced: false
696
+ },
697
+ budgets: {
698
+ ssrHtmlMaxBytes: 250000,
699
+ mfManifestMaxBytes: 500000,
700
+ localeJsonMaxBytes: 100000,
701
+ sitemapXmlMaxBytes: 500000,
702
+ cssAssetMaxBytes: 750000
703
+ },
704
+ csp: {
705
+ finalMode: 'report-only-dogfood',
706
+ decision: "Report-only remains the generated final mode until public smoke proof records MF SSR script/style/connect compatibility for the deployed surface."
707
+ }
708
+ },
709
+ publicHead: {
710
+ indexableRobots: 'index, follow',
711
+ privateRouteRobots: 'noindex, nofollow'
712
+ },
713
+ publicSurface: {
714
+ defaultProviderFile: 'route.sitemap.mjs',
715
+ draftPolicy: 'omit-draft-by-default',
716
+ indexablePolicy: 'omit-indexable-false'
717
+ }
718
+ };
676
719
  function formatTsJsonValue(value, indent) {
677
720
  return JSON.stringify(value, null, 2).replaceAll('\n', `\n${' '.repeat(indent)}`);
678
721
  }
722
+ function formatIntegerCodeLiteral(value) {
723
+ return String(value).replace(/\B(?=(\d{3})+(?!\d))/gu, '_');
724
+ }
725
+ function createPublicWebsiteQualityGateContract() {
726
+ return PUBLIC_WEBSITE_POLICY.qualityGates;
727
+ }
728
+ function createPublicWebsiteBudgetFallback(budgetName) {
729
+ return formatIntegerCodeLiteral(PUBLIC_WEBSITE_POLICY.qualityGates.budgets[budgetName]);
730
+ }
731
+ function createPublicHeadRobotsPolicy() {
732
+ return PUBLIC_WEBSITE_POLICY.publicHead;
733
+ }
734
+ function createPublicSurfaceContentExpansionPolicy() {
735
+ return PUBLIC_WEBSITE_POLICY.publicSurface;
736
+ }
679
737
  function createCloudflareDeployContract(scope, app) {
680
738
  return {
681
739
  target: 'cloudflare',
@@ -689,6 +747,7 @@ function createCloudflareDeployContract(scope, app) {
689
747
  assetsBinding: 'ASSETS',
690
748
  routes: createCloudflareProofRoute(app),
691
749
  security: createCloudflareSecurityContract(),
750
+ qualityGates: createPublicWebsiteQualityGateContract(),
692
751
  evidence: {
693
752
  proofScript: "scripts/proof-cloudflare-version.mjs",
694
753
  reportDefault: '.codex/reports/cloudflare-version-proof/public-url-proof.json'
@@ -754,6 +813,9 @@ function createPackageTsConfig(packageDir, includeApi = false) {
754
813
  };
755
814
  }
756
815
  function createAppPackage(scope, app, packageSource, enableTailwind, remotes = []) {
816
+ const publicSurfaceBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
817
+ const publicSurfaceCloudflareBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
818
+ const publicSurfaceCloudflareOutputCommand = createPublicSurfaceGenerationCommand(app, 'cloudflare');
757
819
  const packageExports = Object.fromEntries(Object.entries(app.exposes ?? {}).map(([expose, source])=>[
758
820
  expose,
759
821
  source
@@ -764,8 +826,8 @@ function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [
764
826
  version: '0.1.0',
765
827
  scripts: {
766
828
  dev: 'modern dev',
767
- build: app.exposes ? `ULTRAMODERN_ZEPHYR=false modern build && node ${relativeRootFor(app.directory)}/scripts/assert-mf-types.mjs` : 'ULTRAMODERN_ZEPHYR=false modern build',
768
- 'cloudflare:build': 'ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy',
829
+ build: app.exposes ? `ULTRAMODERN_ZEPHYR=false modern build && ${publicSurfaceBuildCommand} && node ${relativeRootFor(app.directory)}/scripts/assert-mf-types.mjs` : `ULTRAMODERN_ZEPHYR=false modern build && ${publicSurfaceBuildCommand}`,
830
+ 'cloudflare:build': `ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build && ${publicSurfaceCloudflareBuildCommand} && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy && ${publicSurfaceCloudflareOutputCommand}`,
769
831
  'cloudflare:deploy': 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json',
770
832
  'cloudflare:preview': 'pnpm run cloudflare:build && wrangler dev --config .output/wrangler.json',
771
833
  'cloudflare:proof': `node ${relativeRootFor(app.directory)}/scripts/proof-cloudflare-version.mjs --app ${app.id}`,
@@ -822,6 +884,40 @@ function createSharedPackage(scope, id, description, packageSource) {
822
884
  };
823
885
  return packageJson;
824
886
  }
887
+ function createSharedContractsIndex() {
888
+ return `export type UltramodernPublicSitemapChangeFrequency =
889
+ | 'always'
890
+ | 'hourly'
891
+ | 'daily'
892
+ | 'weekly'
893
+ | 'monthly'
894
+ | 'yearly'
895
+ | 'never';
896
+
897
+ export type UltramodernPublicSitemapEntry = {
898
+ /**
899
+ * Params used to expand every localized route pattern, for example
900
+ * { slug: 'platform-story' } for /talks/:slug.
901
+ */
902
+ params: Record<string, string | number | boolean>;
903
+ /**
904
+ * Per-locale overrides when translated URLs use translated params.
905
+ */
906
+ localeParams?: Partial<Record<'en' | 'cs', Record<string, string | number | boolean>>>;
907
+ draft?: boolean;
908
+ indexable?: boolean;
909
+ lastModified?: string;
910
+ changeFrequency?: UltramodernPublicSitemapChangeFrequency;
911
+ priority?: number;
912
+ };
913
+
914
+ export const ultramodernWorkspaceContract = {
915
+ ownership: 'topology/ownership.json',
916
+ preset: 'presetUltramodern',
917
+ topology: 'topology/reference-topology.json',
918
+ } as const;
919
+ `;
920
+ }
825
921
  function createAppModernConfig(scope, app) {
826
922
  const bffImport = appHasEffectApi(app) ? "import { bffPlugin } from '@modern-js/plugin-bff';\n" : '';
827
923
  const bffConfig = appHasEffectApi(app) ? ` bff: {
@@ -887,11 +983,20 @@ const inferredCloudflareUrl =
887
983
  cloudflareDeployEnabled && cloudflareWorkersDevSubdomain !== undefined
888
984
  ? \`https://\${cloudflareWorkerName}.\${cloudflareWorkersDevSubdomain}.workers.dev\`
889
985
  : undefined;
986
+ // Site origin (SEO: canonical/hreflang URLs) prefers the site-wide public URL;
987
+ // the per-app deployment URL only fills in when no site origin is configured.
890
988
  const siteUrl =
891
- configuredCloudflareUrl ||
892
989
  configuredSiteUrl ||
990
+ configuredCloudflareUrl ||
893
991
  inferredCloudflareUrl ||
894
992
  \`http://localhost:\${port}\`;
993
+ // Asset origin prefers the per-app deployment URL (each MF app serves its own
994
+ // assets). Without an explicit public URL, assets must stay origin-relative so
995
+ // the app works behind tunnels and proxies (an absolute localhost assetPrefix
996
+ // makes pages served via e.g. ngrok fetch scripts from localhost, which Chrome
997
+ // blocks behind a Local Network Access permission prompt).
998
+ const assetPrefix =
999
+ configuredCloudflareUrl || configuredSiteUrl || inferredCloudflareUrl || '/';
895
1000
 
896
1001
  if (
897
1002
  cloudflareDeployEnabled &&
@@ -920,11 +1025,16 @@ ${bffConfig} ...(cloudflareDeployEnabled
920
1025
  },
921
1026
  }
922
1027
  : {}),
1028
+ dev: {
1029
+ // Keep dev assets origin-relative too; the default absolute
1030
+ // http://localhost:<port> prefix breaks pages served through tunnels.
1031
+ assetPrefix: '/',
1032
+ },
923
1033
  html: {
924
1034
  outputStructure: 'flat',
925
1035
  },
926
1036
  output: {
927
- assetPrefix: siteUrl,
1037
+ assetPrefix,
928
1038
  disableTsChecker: true,
929
1039
  distPath: {
930
1040
  html: './',
@@ -1149,7 +1259,7 @@ export default createModuleFederationConfig({
1149
1259
  dts: {
1150
1260
  displayErrorInTerminal: true,
1151
1261
  generateTypes: {
1152
- compilerInstance: '--package typescript -- tsc',
1262
+ compilerInstance: 'tsgo',
1153
1263
  },
1154
1264
  },
1155
1265
  filename: 'remoteEntry.js',
@@ -1207,7 +1317,7 @@ export default createModuleFederationConfig({
1207
1317
  dts: {
1208
1318
  displayErrorInTerminal: true,
1209
1319
  generateTypes: {
1210
- compilerInstance: '--package typescript -- tsc',
1320
+ compilerInstance: 'tsgo',
1211
1321
  },
1212
1322
  },
1213
1323
  exposes: ${exposes},
@@ -1229,6 +1339,7 @@ const privateAppRoutePublicness = {
1229
1339
  function createRouteOwnedI18nPaths(app) {
1230
1340
  const namespace = appI18nNamespace(app);
1231
1341
  const base = {
1342
+ descriptionKey: `${namespace}.seo.description`,
1232
1343
  mfBoundaryId: app.mfName,
1233
1344
  namespace,
1234
1345
  ownerAppId: app.id,
@@ -1430,6 +1541,7 @@ function createPublicRouteMetadata(app) {
1430
1541
  localisedPaths: route.localisedPaths,
1431
1542
  namespace: route.namespace,
1432
1543
  ownerAppId: route.ownerAppId,
1544
+ descriptionKey: route.descriptionKey,
1433
1545
  titleKey: route.titleKey
1434
1546
  }));
1435
1547
  }
@@ -1438,7 +1550,11 @@ function createRouteMetadataModule(app) {
1438
1550
  const localisedUrls = sortJsonValue(createLocalisedUrlsMap(app));
1439
1551
  const publicRoutes = sortJsonValue(createPublicRouteMetadata(app));
1440
1552
  const namespace = appI18nNamespace(app);
1441
- return `export const ultramodernRouteNamespace = '${namespace}' as const;
1553
+ return `// @generated by @modern-js/create.
1554
+ // Author route metadata in colocated src/routes/**/route.meta.ts files.
1555
+ // This compatibility manifest is regenerated from route-owned metadata.
1556
+
1557
+ export const ultramodernRouteNamespace = '${namespace}' as const;
1442
1558
 
1443
1559
  export const ultramodernRouteMetadata = ${JSON.stringify(routes, null, 2)} as const;
1444
1560
 
@@ -1447,6 +1563,8 @@ export const ultramodernLocalisedUrls = ${JSON.stringify(localisedUrls, null, 2)
1447
1563
  export const ultramodernPublicRoutes = ${JSON.stringify(publicRoutes, null, 2)} as const;
1448
1564
 
1449
1565
  export const ultramodernRouteConfig = {
1566
+ authoring: 'colocated-route-meta',
1567
+ generatedManifest: true,
1450
1568
  localisedUrls: ultramodernLocalisedUrls,
1451
1569
  namespace: ultramodernRouteNamespace,
1452
1570
  publicRoutes: ultramodernPublicRoutes,
@@ -1455,20 +1573,52 @@ export const ultramodernRouteConfig = {
1455
1573
  } as const;
1456
1574
  `;
1457
1575
  }
1576
+ function createRouteMetaModule(route) {
1577
+ return `const routeMeta = ${JSON.stringify(sortJsonValue(route), null, 2)} as const;
1578
+
1579
+ export default routeMeta;
1580
+ export { routeMeta };
1581
+ `;
1582
+ }
1583
+ function normalisePublicPath(pathname) {
1584
+ const normalised = pathname.trim().replaceAll(/\/+/gu, '/').replace(/\/+$/u, '');
1585
+ return normalised.length > 0 && normalised.startsWith('/') ? normalised : `/${normalised}`;
1586
+ }
1587
+ function splitPublicPathSegments(pathname) {
1588
+ return normalisePublicPath(pathname).split('/').filter(Boolean);
1589
+ }
1590
+ function routePathParamName(segment) {
1591
+ if (segment.startsWith(':')) return segment.slice(1).replace(/[?*+]$/u, '');
1592
+ if (segment.startsWith('[') && segment.endsWith(']')) return segment.slice(1, -1).replace(/^\.\.\./u, '').replace(/\$$/u, '');
1593
+ }
1594
+ function isDynamicPublicPathSegment(segment) {
1595
+ return void 0 !== routePathParamName(segment) || segment.includes('*') || segment.startsWith('[');
1596
+ }
1597
+ function isConcretePublicPath(pathname) {
1598
+ return !splitPublicPathSegments(pathname).some(isDynamicPublicPathSegment);
1599
+ }
1458
1600
  function routeSegmentToDirectory(segment) {
1459
- if (segment.startsWith(':')) {
1460
- const name = segment.slice(1).replace(/\?$/u, '');
1461
- return segment.endsWith('?') ? `[${name}$]` : `[${name}]`;
1462
- }
1601
+ const paramName = routePathParamName(segment);
1602
+ if (paramName && segment.startsWith(':')) return segment.endsWith('?') ? `[${paramName}$]` : `[${paramName}]`;
1463
1603
  return segment;
1464
1604
  }
1605
+ function routePathDirectorySegments(routePath) {
1606
+ return splitPublicPathSegments(routePath).map(routeSegmentToDirectory);
1607
+ }
1465
1608
  function createRoutePageFilePath(app, canonicalPath) {
1466
- const segments = canonicalPath.split('/').filter(Boolean).map(routeSegmentToDirectory);
1609
+ const segments = routePathDirectorySegments(canonicalPath);
1467
1610
  return `${app.directory}/src/routes/[lang]/${[
1468
1611
  ...segments,
1469
1612
  'page.tsx'
1470
1613
  ].join('/')}`;
1471
1614
  }
1615
+ function createRouteMetaFilePath(app, canonicalPath) {
1616
+ const segments = routePathDirectorySegments(canonicalPath);
1617
+ return `${app.directory}/src/routes/[lang]/${[
1618
+ ...segments,
1619
+ 'route.meta.ts'
1620
+ ].join('/')}`;
1621
+ }
1472
1622
  function createRouteAliasPage(canonicalPath) {
1473
1623
  const depth = canonicalPath.split('/').filter(Boolean).length;
1474
1624
  const rootPageImport = `${'../'.repeat(depth)}page`;
@@ -1626,24 +1776,22 @@ export default {} satisfies Config;
1626
1776
  function createTw(prefix) {
1627
1777
  return (classList)=>classList.split(/\s+/u).filter(Boolean).map((candidate)=>`${prefix}:${candidate.replace(/\[&&\]:/gu, '')}`).join(' ');
1628
1778
  }
1629
- const publicSurfaceRequiredAssetPaths = [
1630
- 'config/public/robots.txt'
1631
- ];
1632
- const publicSurfaceOptionalAssetPaths = [
1779
+ const publicSurfaceManagedSourceAssetPaths = [
1780
+ 'config/public/robots.txt',
1633
1781
  'config/public/sitemap.xml',
1634
1782
  'config/public/site.webmanifest'
1635
1783
  ];
1636
- function normalisePublicPath(pathname) {
1637
- const normalised = pathname.trim().replaceAll(/\/+/gu, '/').replace(/\/+$/u, '');
1638
- return normalised.length > 0 && normalised.startsWith('/') ? normalised : `/${normalised}`;
1639
- }
1784
+ const publicSurfaceBaseOutputFiles = [
1785
+ 'robots.txt'
1786
+ ];
1787
+ const publicSurfacePublicRouteOutputFiles = [
1788
+ 'sitemap.xml',
1789
+ 'site.webmanifest'
1790
+ ];
1640
1791
  function createLocalisedPublicPath(pathname, language) {
1641
1792
  const publicPath = normalisePublicPath(pathname);
1642
1793
  return '/' === publicPath ? `/${language}` : `/${language}${publicPath}`;
1643
1794
  }
1644
- function isConcretePublicPath(pathname) {
1645
- return !normalisePublicPath(pathname).split('/').some((segment)=>segment.startsWith(':') || segment.includes('*') || segment.startsWith('['));
1646
- }
1647
1795
  function uniqueSorted(values) {
1648
1796
  return Array.from(new Set(values)).sort((left, right)=>left.localeCompare(right));
1649
1797
  }
@@ -1661,94 +1809,45 @@ function createPublicSurfaceRouteEntries(app) {
1661
1809
  };
1662
1810
  }).filter((route)=>void 0 !== route).sort((left, right)=>left.canonicalUrlPath.localeCompare(right.canonicalUrlPath) || left.id.localeCompare(right.id));
1663
1811
  }
1812
+ function createPublicSurfaceContentSources(_app) {
1813
+ return [];
1814
+ }
1664
1815
  function createPublicSurfaceUrlPaths(app) {
1665
1816
  return uniqueSorted(createPublicSurfaceRouteEntries(app).flatMap((route)=>supportedWorkspaceLanguages.map((language)=>route.localeUrlPaths[language])));
1666
1817
  }
1667
- function createPublicSurfaceOrigin(app) {
1668
- return `http://localhost:${app.port}`;
1669
- }
1670
- function escapeXmlText(value) {
1671
- return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
1672
- }
1673
- function escapeXmlAttribute(value) {
1674
- return escapeXmlText(value).replaceAll('"', '&quot;');
1675
- }
1676
- function renderRobotsTxt(app) {
1677
- const urlPaths = createPublicSurfaceUrlPaths(app);
1678
- const lines = [
1679
- 'User-agent: *'
1680
- ];
1681
- if (0 === urlPaths.length) lines.push('Disallow: /');
1682
- else {
1683
- for (const urlPath of urlPaths)lines.push(`Allow: ${urlPath}$`);
1684
- lines.push('Disallow: /');
1685
- lines.push(`Sitemap: ${createPublicSurfaceOrigin(app)}/sitemap.xml`);
1686
- }
1687
- return `${lines.join('\n')}\n`;
1688
- }
1689
- function renderSitemapXml(app) {
1690
- const origin = createPublicSurfaceOrigin(app);
1691
- const routes = createPublicSurfaceRouteEntries(app);
1692
- const lines = [
1693
- '<?xml version="1.0" encoding="UTF-8"?>',
1694
- '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">'
1818
+ function createPublicSurfaceOutputFiles(app) {
1819
+ return [
1820
+ ...publicSurfaceBaseOutputFiles,
1821
+ ...createPublicRouteMetadata(app).length > 0 ? publicSurfacePublicRouteOutputFiles : []
1695
1822
  ];
1696
- for (const route of routes)for (const language of supportedWorkspaceLanguages){
1697
- lines.push(' <url>');
1698
- lines.push(` <loc>${escapeXmlText(`${origin}${route.localeUrlPaths[language]}`)}</loc>`);
1699
- for (const alternateLanguage of supportedWorkspaceLanguages)lines.push(` <xhtml:link rel="alternate" hreflang="${alternateLanguage}" href="${escapeXmlAttribute(`${origin}${route.localeUrlPaths[alternateLanguage]}`)}" />`);
1700
- lines.push(` <xhtml:link rel="alternate" hreflang="x-default" href="${escapeXmlAttribute(`${origin}${route.localeUrlPaths.en}`)}" />`);
1701
- lines.push(' </url>');
1702
- }
1703
- lines.push('</urlset>');
1704
- return `${lines.join('\n')}\n`;
1705
- }
1706
- function renderWebManifest(app) {
1707
- const startUrl = createPublicSurfaceUrlPaths(app)[0];
1708
- const manifest = {
1709
- name: app.displayName,
1710
- short_name: app.displayName,
1711
- display: 'standalone',
1712
- background_color: '#ffffff',
1713
- theme_color: '#133225',
1714
- lang: 'en',
1715
- categories: [
1716
- 'business',
1717
- 'productivity'
1718
- ],
1719
- icons: [],
1720
- ...startUrl ? {
1721
- scope: '/',
1722
- start_url: startUrl
1723
- } : {}
1724
- };
1725
- return `${JSON.stringify(sortJsonValue(manifest), null, 2)}\n`;
1726
1823
  }
1727
- function createPublicSurfaceAssets(app) {
1728
- const assets = {
1729
- 'config/public/robots.txt': renderRobotsTxt(app)
1730
- };
1731
- if (createPublicSurfaceRouteEntries(app).length > 0) {
1732
- assets['config/public/sitemap.xml'] = renderSitemapXml(app);
1733
- assets['config/public/site.webmanifest'] = renderWebManifest(app);
1734
- }
1735
- return assets;
1824
+ function createPublicSurfaceGenerationCommand(app, target, requirePublicOrigin = false) {
1825
+ return `node ${relativeRootFor(app.directory)}/scripts/generate-public-surface-assets.mjs --app ${app.id} --target ${target}${requirePublicOrigin ? ' --require-public-origin' : ''}`;
1736
1826
  }
1737
1827
  function workspaceAssetsForApp(app) {
1738
- return createPublicSurfaceAssets(app);
1828
+ return {};
1739
1829
  }
1740
1830
  function rewriteWorkspaceAssetsForApp(workspaceRoot, app) {
1831
+ for (const relativePath of publicSurfaceManagedSourceAssetPaths)external_node_fs_default().rmSync(external_node_path_default().join(workspaceRoot, app.directory, relativePath), {
1832
+ force: true
1833
+ });
1741
1834
  for (const [relativePath, content] of Object.entries(workspaceAssetsForApp(app)))writeFileReplacing(workspaceRoot, `${app.directory}/${relativePath}`, content);
1742
1835
  }
1743
- function createLocalizedHeadComponent() {
1744
- return `const fallbackLanguage = 'en';
1836
+ function createRouteHeadModule(app) {
1837
+ const robotsPolicy = createPublicHeadRobotsPolicy();
1838
+ return `import { useLocalizedLocation, useModernI18n } from '@modern-js/plugin-i18n/runtime';
1839
+ import { Helmet } from '@modern-js/runtime/head';
1840
+ import {
1841
+ ultramodernRouteMetadata,
1842
+ } from './ultramodern-route-metadata';
1843
+
1844
+ const appName = ${JSON.stringify(app.displayName)};
1845
+ const fallbackLanguage = 'en';
1745
1846
  const supportedLanguages = ['en', 'cs'] as const;
1746
1847
  type SupportedLanguage = (typeof supportedLanguages)[number];
1848
+ type RouteMetadata = (typeof ultramodernRouteMetadata)[number];
1747
1849
 
1748
- const localisedUrls = ultramodernLocalisedUrls as Record<
1749
- string,
1750
- Record<SupportedLanguage, string>
1751
- >;
1850
+ const routeMetadata = ultramodernRouteMetadata as readonly RouteMetadata[];
1752
1851
 
1753
1852
  const isSupportedLanguage = (value: string): value is SupportedLanguage =>
1754
1853
  supportedLanguages.includes(value as SupportedLanguage);
@@ -1799,55 +1898,24 @@ const matchPattern = (pathname: string, pattern: string) => {
1799
1898
  return params;
1800
1899
  };
1801
1900
 
1802
- const buildPath = (pattern: string, params: Record<string, string>) => {
1803
- const path = normalisePath(pattern)
1804
- .split('/')
1805
- .filter(Boolean)
1806
- .map(segment => {
1807
- if (!segment.startsWith(':')) {
1808
- return segment;
1809
- }
1810
- const value = params[paramName(segment)];
1811
- return value !== undefined && value.length > 0
1812
- ? encodeURIComponent(value)
1813
- : '';
1814
- })
1815
- .filter(Boolean)
1816
- .join('/');
1817
-
1818
- return \`/\${path}\`;
1819
- };
1820
-
1821
- const resolveLocalisedPath = (
1822
- pathname: string,
1823
- targetLanguage: SupportedLanguage,
1824
- ) => {
1901
+ const resolveRouteMetadata = (pathname: string) => {
1825
1902
  const pathWithoutLanguage = stripLanguagePrefix(pathname);
1826
1903
 
1827
- for (const entry of Object.values(localisedUrls)) {
1828
- const targetPattern = entry[targetLanguage];
1829
- if (targetPattern === undefined) {
1830
- continue;
1904
+ for (const route of routeMetadata) {
1905
+ const canonicalParams = matchPattern(pathWithoutLanguage, route.canonicalPath);
1906
+ if (canonicalParams !== undefined) {
1907
+ return route;
1831
1908
  }
1832
1909
 
1833
1910
  for (const language of supportedLanguages) {
1834
- const sourcePattern = entry[language];
1835
- const params =
1836
- sourcePattern === undefined
1837
- ? undefined
1838
- : matchPattern(pathWithoutLanguage, sourcePattern);
1911
+ const params = matchPattern(pathWithoutLanguage, route.localisedPaths[language]);
1839
1912
  if (params !== undefined) {
1840
- return buildPath(targetPattern, params);
1913
+ return route;
1841
1914
  }
1842
1915
  }
1843
1916
  }
1844
1917
 
1845
- return pathWithoutLanguage;
1846
- };
1847
-
1848
- const localizedPath = (pathname: string, language: SupportedLanguage) => {
1849
- const pathWithoutLanguage = resolveLocalisedPath(pathname, language);
1850
- return pathWithoutLanguage === '/' ? \`/\${language}\` : \`/\${language}\${pathWithoutLanguage}\`;
1918
+ return routeMetadata[0];
1851
1919
  };
1852
1920
 
1853
1921
  const absoluteUrl = (pathname: string) => {
@@ -1855,26 +1923,68 @@ const absoluteUrl = (pathname: string) => {
1855
1923
  return \`\${origin}\${pathname}\`;
1856
1924
  };
1857
1925
 
1858
- const LocalizedHead = () => {
1859
- const location = useLocation();
1860
- const canonicalPath = localizedPath(location.pathname, fallbackLanguage);
1926
+ const sanitiseJsonLd = (value: unknown) =>
1927
+ JSON.stringify(value).replaceAll('<', '\\\\u003c');
1928
+
1929
+ export const UltramodernRouteHead = () => {
1930
+ const { i18nInstance } = useModernI18n();
1931
+ const t = i18nInstance['t'].bind(i18nInstance);
1932
+ const { canonical, alternates } = useLocalizedLocation();
1933
+ const route = resolveRouteMetadata(canonical);
1934
+ const title = route ? t(route.titleKey) : appName;
1935
+ const description = route ? t(route.descriptionKey) : appName;
1936
+ const canonicalUrl = absoluteUrl(alternates[fallbackLanguage] ?? \`/\${fallbackLanguage}\`);
1937
+ const indexable = route?.public === true && route?.indexable === true;
1938
+ const jsonLd = indexable
1939
+ ? {
1940
+ '@context': 'https://schema.org',
1941
+ '@type': 'WebPage',
1942
+ description,
1943
+ inLanguage: supportedLanguages.join(','),
1944
+ isPartOf: {
1945
+ '@type': 'WebSite',
1946
+ name: appName,
1947
+ url: absoluteUrl('/'),
1948
+ },
1949
+ name: title,
1950
+ url: canonicalUrl,
1951
+ }
1952
+ : undefined;
1861
1953
 
1862
1954
  return (
1863
- <Helmet>
1864
- <link rel="canonical" href={absoluteUrl(canonicalPath)} />
1865
- {supportedLanguages.map(code => (
1866
- <link
1867
- href={absoluteUrl(localizedPath(location.pathname, code))}
1868
- hrefLang={code}
1869
- key={code}
1870
- rel="alternate"
1871
- />
1872
- ))}
1873
- <link
1874
- href={absoluteUrl(localizedPath(location.pathname, fallbackLanguage))}
1875
- hrefLang="x-default"
1876
- rel="alternate"
1877
- />
1955
+ <Helmet htmlAttributes={{ lang: i18nInstance.language ?? fallbackLanguage }}>
1956
+ <title>{title}</title>
1957
+ <meta content={description} name="description" />
1958
+ <meta content={indexable ? '${robotsPolicy.indexableRobots}' : '${robotsPolicy.privateRouteRobots}'} name="robots" />
1959
+ {indexable && (
1960
+ <>
1961
+ <link rel="canonical" href={canonicalUrl} />
1962
+ {supportedLanguages.map(code => (
1963
+ <link
1964
+ href={absoluteUrl(alternates[code] ?? \`/\${code}\`)}
1965
+ hrefLang={code}
1966
+ key={code}
1967
+ rel="alternate"
1968
+ />
1969
+ ))}
1970
+ <link
1971
+ href={absoluteUrl(alternates[fallbackLanguage] ?? \`/\${fallbackLanguage}\`)}
1972
+ hrefLang="x-default"
1973
+ rel="alternate"
1974
+ />
1975
+ <meta content={title} property="og:title" />
1976
+ <meta content={description} property="og:description" />
1977
+ <meta content={canonicalUrl} property="og:url" />
1978
+ <meta content="website" property="og:type" />
1979
+ <meta content={i18nInstance.language ?? fallbackLanguage} property="og:locale" />
1980
+ <meta content="summary_large_image" name="twitter:card" />
1981
+ <meta content={title} name="twitter:title" />
1982
+ <meta content={description} name="twitter:description" />
1983
+ {jsonLd && (
1984
+ <script type="application/ld+json">{sanitiseJsonLd(jsonLd)}</script>
1985
+ )}
1986
+ </>
1987
+ )}
1878
1988
  </Helmet>
1879
1989
  );
1880
1990
  };
@@ -1883,31 +1993,28 @@ const LocalizedHead = () => {
1883
1993
  function createShellPage(remotes = []) {
1884
1994
  const tw = createTw(tailwindPrefixForApp(shellApp));
1885
1995
  const remoteCount = String(remotes.length);
1886
- return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
1887
- import { Helmet } from '@modern-js/runtime/head';
1888
- import { useLocation } from '@modern-js/plugin-tanstack/runtime';
1996
+ return `import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
1889
1997
  import ShellFrame from '../shell-frame';
1998
+ import { UltramodernRouteHead } from '../ultramodern-route-head';
1890
1999
  import { VerticalShowcase } from '../vertical-components';
1891
- import { ultramodernLocalisedUrls } from '../ultramodern-route-metadata';
1892
2000
  import { ultramodernUiMarker } from '../../ultramodern-build';
1893
2001
 
1894
- ${createLocalizedHeadComponent()}
1895
2002
  export default function ShellHome() {
1896
2003
  const { i18nInstance } = useModernI18n();
1897
2004
  const t = i18nInstance['t'].bind(i18nInstance);
1898
2005
 
1899
2006
  return (
1900
2007
  <ShellFrame>
1901
- <LocalizedHead />
2008
+ <UltramodernRouteHead />
1902
2009
  <section className="${tw('mx-auto grid max-w-7xl items-center gap-8 py-8 md:grid-cols-[0.9fr_1.1fr] lg:gap-14')}">
1903
2010
  <div className="${tw('min-w-0')}">
1904
2011
  <p className="${tw('text-xs font-black uppercase tracking-[0.18em] text-emerald-800')}">{t('shell.hero.eyebrow')}</p>
1905
2012
  <h1 className="${tw('mt-3 max-w-3xl text-5xl font-black leading-none tracking-normal text-stone-950 md:text-7xl')}">{t('shell.title')}</h1>
1906
2013
  <p className="${tw('mt-5 max-w-2xl text-lg leading-8 text-stone-600')}">{t('shell.hero.lede')}</p>
1907
2014
  <div className="${tw('mt-7 flex flex-wrap gap-3')}">
1908
- <I18nLink className="${tw('inline-flex min-h-11 items-center justify-center rounded-full bg-emerald-800 px-5 font-bold text-white shadow-lg shadow-stone-900/10')}" to="/">
2015
+ <Link className="${tw('inline-flex min-h-11 items-center justify-center rounded-full bg-emerald-800 px-5 font-bold text-white shadow-lg shadow-stone-900/10')}" to="/">
1909
2016
  {t('shell.hero.primary')}
1910
- </I18nLink>
2017
+ </Link>
1911
2018
  <span className="${tw('inline-flex min-h-11 items-center justify-center rounded-full border border-stone-900/15 bg-white/90 px-5 font-bold text-stone-950 shadow-lg shadow-stone-900/10')}">
1912
2019
  {t('shell.hero.secondary')}
1913
2020
  </span>
@@ -1940,145 +2047,18 @@ export default function ShellHome() {
1940
2047
  }
1941
2048
  function createShellFrameComponent() {
1942
2049
  const tw = createTw(tailwindPrefixForApp(shellApp));
1943
- return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
1944
- import { useLocation } from '@modern-js/plugin-tanstack/runtime';
2050
+ return `import { useLocalizedLocation, useModernI18n } from '@modern-js/plugin-i18n/runtime';
1945
2051
  import type { ReactNode } from 'react';
1946
2052
  import { Header, StatusBadge } from './vertical-components';
1947
- import { ultramodernLocalisedUrls } from './ultramodern-route-metadata';
1948
-
1949
- const supportedLanguages = ['en', 'cs'] as const;
1950
- type SupportedLanguage = (typeof supportedLanguages)[number];
1951
2053
 
1952
2054
  interface ShellFrameProps {
1953
2055
  children: ReactNode;
1954
2056
  }
1955
2057
 
1956
- const localisedUrls = ultramodernLocalisedUrls as Record<
1957
- string,
1958
- Record<SupportedLanguage, string>
1959
- >;
1960
-
1961
- const isSupportedLanguage = (value: string): value is SupportedLanguage =>
1962
- supportedLanguages.includes(value as SupportedLanguage);
1963
-
1964
- const normalisePath = (pathname: string) => {
1965
- const normalised = pathname.replaceAll(/\\/+/gu, '/').replace(/\\/+$/u, '');
1966
- return normalised.length > 0 ? normalised : '/';
1967
- };
1968
-
1969
- const stripLanguagePrefix = (pathname: string) => {
1970
- const segments = normalisePath(pathname).split('/').filter(Boolean);
1971
- if (segments.length > 0 && isSupportedLanguage(segments[0] ?? '')) {
1972
- segments.shift();
1973
- }
1974
- return \`/\${segments.join('/')}\`;
1975
- };
1976
-
1977
- const escapeRegExp = (value: string) =>
1978
- value.replaceAll(/[.*+?^\${}()|[\\]\\\\]/gu, '\\\\$&');
1979
-
1980
- const paramName = (segment: string) => segment.slice(1).replace(/\\?$/u, '');
1981
-
1982
- const matchPattern = (pathname: string, pattern: string) => {
1983
- const names: string[] = [];
1984
- const source = normalisePath(pattern)
1985
- .split('/')
1986
- .filter(Boolean)
1987
- .map(segment => {
1988
- if (segment.startsWith(':')) {
1989
- names.push(paramName(segment));
1990
- return segment.endsWith('?') ? '(?:/([^/]+))?' : '/([^/]+)';
1991
- }
1992
- return \`/\${escapeRegExp(segment)}\`;
1993
- })
1994
- .join('');
1995
- const match = new RegExp(\`^\${source || '/'}$\`, 'u').exec(
1996
- normalisePath(pathname),
1997
- );
1998
-
1999
- if (match === null) {
2000
- return;
2001
- }
2002
-
2003
- const params: Record<string, string> = {};
2004
- for (const [index, name] of names.entries()) {
2005
- params[name] = decodeURIComponent(match[index + 1] ?? '');
2006
- }
2007
- return params;
2008
- };
2009
-
2010
- const buildPath = (pattern: string, params: Record<string, string>) => {
2011
- const path = normalisePath(pattern)
2012
- .split('/')
2013
- .filter(Boolean)
2014
- .map(segment => {
2015
- if (!segment.startsWith(':')) {
2016
- return segment;
2017
- }
2018
- const value = params[paramName(segment)];
2019
- return value !== undefined && value.length > 0
2020
- ? encodeURIComponent(value)
2021
- : '';
2022
- })
2023
- .filter(Boolean)
2024
- .join('/');
2025
-
2026
- return \`/\${path}\`;
2027
- };
2028
-
2029
- const resolveLocalisedPath = (
2030
- pathname: string,
2031
- targetLanguage: SupportedLanguage,
2032
- ) => {
2033
- const pathWithoutLanguage = stripLanguagePrefix(pathname);
2034
-
2035
- for (const entry of Object.values(localisedUrls)) {
2036
- const targetPattern = entry[targetLanguage];
2037
- if (targetPattern === undefined) {
2038
- continue;
2039
- }
2040
-
2041
- for (const language of supportedLanguages) {
2042
- const sourcePattern = entry[language];
2043
- const params =
2044
- sourcePattern === undefined
2045
- ? undefined
2046
- : matchPattern(pathWithoutLanguage, sourcePattern);
2047
- if (params !== undefined) {
2048
- return buildPath(targetPattern, params);
2049
- }
2050
- }
2051
- }
2052
-
2053
- return pathWithoutLanguage;
2054
- };
2055
-
2056
- const localizedPath = (pathname: string, language: SupportedLanguage) => {
2057
- const pathWithoutLanguage = resolveLocalisedPath(pathname, language);
2058
- return pathWithoutLanguage === '/' ? \`/\${language}\` : \`/\${language}\${pathWithoutLanguage}\`;
2059
- };
2060
-
2061
- const locationSuffix = (location: {
2062
- hash?: unknown;
2063
- search?: unknown;
2064
- searchStr?: unknown;
2065
- }) => {
2066
- let locationSearch = '';
2067
- if (typeof location.searchStr === 'string') {
2068
- locationSearch = location.searchStr;
2069
- } else if (typeof location.search === 'string') {
2070
- locationSearch = location.search;
2071
- }
2072
- const locationHash = typeof location.hash === 'string' ? location.hash : '';
2073
-
2074
- return \`\${locationSearch}\${locationHash}\`;
2075
- };
2076
-
2077
2058
  export default function ShellFrame({ children }: ShellFrameProps) {
2078
2059
  const { i18nInstance, language } = useModernI18n();
2079
2060
  const t = i18nInstance['t'].bind(i18nInstance);
2080
- const location = useLocation();
2081
- const suffix = locationSuffix(location);
2061
+ const { alternates } = useLocalizedLocation();
2082
2062
 
2083
2063
  return (
2084
2064
  <main className="${tw('min-h-screen bg-um-canvas px-4 py-5 text-um-foreground sm:px-6 lg:px-12')}">
@@ -2095,10 +2075,9 @@ export default function ShellFrame({ children }: ShellFrameProps) {
2095
2075
  name="language"
2096
2076
  onChange={event => {
2097
2077
  const nextLanguage = event.currentTarget.value;
2098
- if (isSupportedLanguage(nextLanguage)) {
2099
- window.location.assign(
2100
- \`\${localizedPath(location.pathname, nextLanguage)}\${suffix}\`,
2101
- );
2078
+ const targetHref = alternates[nextLanguage];
2079
+ if (targetHref !== undefined) {
2080
+ window.location.assign(targetHref);
2102
2081
  }
2103
2082
  }}
2104
2083
  value={language}
@@ -2189,7 +2168,7 @@ const createHydratedRemote =
2189
2168
  return ` <${componentName} key="${remote.id}" />`;
2190
2169
  }).join('\n');
2191
2170
  const remoteCount = String(widgetRemotes.length);
2192
- return `${federationImports}import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2171
+ return `${federationImports}import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2193
2172
 
2194
2173
  const widgetCount = Number('${remoteCount}');
2195
2174
 
@@ -2202,7 +2181,7 @@ const createHydratedRemote =
2202
2181
 
2203
2182
  return (
2204
2183
  <header className="${tw('flex min-w-0 flex-wrap items-center gap-x-8 gap-y-2 md:flex-1')}" data-modern-boundary-id="${shellApp.mfName}" data-modern-mf-expose="shell/Header">
2205
- <I18nLink className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('shell.title')}</I18nLink>
2184
+ <Link className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('shell.title')}</Link>
2206
2185
  </header>
2207
2186
  );
2208
2187
  };
@@ -2244,17 +2223,16 @@ function createRemotePage(app) {
2244
2223
  const tw = createTw(tailwindPrefixForApp(app));
2245
2224
  const listEffectItems = `list${toPascalCase(effectApiStem(app))}`;
2246
2225
  const effectBffImport = appHasEffectApi(app) ? `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
2247
- import { Helmet } from '@modern-js/runtime/head';
2248
- import { Link, useLocation } from '@modern-js/plugin-tanstack/runtime';
2226
+ import { Link } from '@modern-js/plugin-tanstack/runtime';
2249
2227
  import { useEffect, useState } from 'react';
2250
2228
  import {
2251
2229
  Effect,
2252
2230
  ${listEffectItems},
2253
2231
  runEffectRequest,
2254
2232
  } from '../../effect/${effectApiStem(app)}-client';
2255
- import { ultramodernLocalisedUrls } from '../ultramodern-route-metadata';
2233
+ import { UltramodernRouteHead } from '../ultramodern-route-head';
2256
2234
  import { ultramodernUiMarker } from '../../ultramodern-build';
2257
- ` : "import { useModernI18n } from '@modern-js/plugin-i18n/runtime';\nimport { Helmet } from '@modern-js/runtime/head';\nimport { Link, useLocation } from '@modern-js/plugin-tanstack/runtime';\nimport { ultramodernLocalisedUrls } from '../ultramodern-route-metadata';\nimport { ultramodernUiMarker } from '../../ultramodern-build';\n";
2235
+ ` : "import { useModernI18n } from '@modern-js/plugin-i18n/runtime';\nimport { Link } from '@modern-js/plugin-tanstack/runtime';\nimport { UltramodernRouteHead } from '../ultramodern-route-head';\nimport { ultramodernUiMarker } from '../../ultramodern-build';\n";
2258
2236
  const effectBffState = appHasEffectApi(app) ? ` const [effectApiStatus, setEffectApiStatus] = useState('pending');
2259
2237
 
2260
2238
  useEffect(() => {
@@ -2287,13 +2265,12 @@ import { ultramodernUiMarker } from '../../ultramodern-build';
2287
2265
  const effectBffMarkup = appHasEffectApi(app) ? ` <p data-testid="effect-bff-status">{effectApiStatus}</p>
2288
2266
  ` : '';
2289
2267
  return `${effectBffImport}
2290
- ${createLocalizedHeadComponent()}
2291
2268
  export default function ${toPascalCase(app.id)}Home() {
2292
2269
  const { i18nInstance, language } = useModernI18n();
2293
2270
  const t = i18nInstance['t'].bind(i18nInstance);
2294
2271
  ${effectBffState} return (
2295
2272
  <main className="${tw('min-h-screen bg-um-canvas px-4 py-6 text-um-foreground sm:px-8')}">
2296
- <LocalizedHead />
2273
+ <UltramodernRouteHead />
2297
2274
  <nav aria-label={t('${app.domain}.language.switcher')} className="${tw('flex gap-3')}">
2298
2275
  {supportedLanguages.map(code => (
2299
2276
  <Link
@@ -2373,7 +2350,7 @@ export default function ${componentName}() {
2373
2350
  }
2374
2351
  function createRemoteExposeComponent(app, expose) {
2375
2352
  const tw = createTw(tailwindPrefixForApp(app));
2376
- if ('workspace' === app.id && './Header' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2353
+ if ('workspace' === app.id && './Header' === expose) return `import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2377
2354
 
2378
2355
  export default function Header() {
2379
2356
  const { i18nInstance } = useModernI18n();
@@ -2381,16 +2358,16 @@ export default function Header() {
2381
2358
 
2382
2359
  return (
2383
2360
  <header className="${tw('flex min-w-0 flex-wrap items-center gap-x-8 gap-y-2 md:flex-1')}" data-modern-boundary-id="${app.mfName}" data-modern-mf-expose="${expose}">
2384
- <I18nLink className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('workspace.header.brand')}</I18nLink>
2361
+ <Link className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('workspace.header.brand')}</Link>
2385
2362
  <nav aria-label={t('workspace.header.navigation')} className="${tw('flex items-center gap-5')}">
2386
- <I18nLink className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/workspaces">{t('workspace.header.workspaces')}</I18nLink>
2387
- <I18nLink className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/directory">{t('workspace.header.directory')}</I18nLink>
2363
+ <Link className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/workspaces">{t('workspace.header.workspaces')}</Link>
2364
+ <Link className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/directory">{t('workspace.header.directory')}</Link>
2388
2365
  </nav>
2389
2366
  </header>
2390
2367
  );
2391
2368
  }
2392
2369
  `;
2393
- if ('workspace' === app.id && './Highlights' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2370
+ if ('workspace' === app.id && './Highlights' === expose) return `import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2394
2371
 
2395
2372
  const highlights = [
2396
2373
  { badge: 'workspace.highlights.shell', href: '/workspaces', name: 'workspace.highlights.shellTitle' },
@@ -2407,10 +2384,10 @@ export default function Highlights() {
2407
2384
  <h2 className="${tw('text-3xl font-black tracking-normal text-stone-950')}">{t('workspace.highlights.title')}</h2>
2408
2385
  <div className="${tw('mt-5 grid gap-4 md:grid-cols-3')}">
2409
2386
  {highlights.map(highlight => (
2410
- <I18nLink className="${tw('block rounded-2xl bg-white/90 p-5 text-stone-950 no-underline shadow-xl shadow-stone-900/10 transition hover:-translate-y-0.5 hover:shadow-2xl')}" key={highlight.href} to={highlight.href}>
2387
+ <Link className="${tw('block rounded-2xl bg-white/90 p-5 text-stone-950 no-underline shadow-xl shadow-stone-900/10 transition hover:-translate-y-0.5 hover:shadow-2xl')}" key={highlight.href} to={highlight.href}>
2411
2388
  <span className="${tw('text-xs font-black uppercase tracking-[0.16em] text-emerald-800')}">{t(highlight.badge)}</span>
2412
2389
  <strong className="${tw('mt-3 block text-xl font-black leading-tight')}">{t(highlight.name)}</strong>
2413
- </I18nLink>
2390
+ </Link>
2414
2391
  ))}
2415
2392
  </div>
2416
2393
  </section>
@@ -2487,7 +2464,7 @@ export default function ${componentName}() {
2487
2464
  );
2488
2465
  }
2489
2466
  `;
2490
- if ('actions' === app.id && './StartAction' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2467
+ if ('actions' === app.id && './StartAction' === expose) return `import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2491
2468
  import { useActionQueue } from '../action-queue-store';
2492
2469
 
2493
2470
  export default function ${componentName}() {
@@ -2500,9 +2477,9 @@ export default function ${componentName}() {
2500
2477
  <button className="${tw('inline-flex min-h-11 items-center justify-center rounded-full bg-emerald-800 px-5 font-bold text-white shadow-lg shadow-stone-900/10')}" onClick={queue.addStarterAction} type="button">
2501
2478
  {t('actions.controls.start')}
2502
2479
  </button>
2503
- <I18nLink className="${tw('inline-flex min-h-11 items-center justify-center rounded-full border border-stone-900/15 bg-white/90 px-5 font-bold text-stone-950 shadow-lg shadow-stone-900/10')}" to="/actions">
2480
+ <Link className="${tw('inline-flex min-h-11 items-center justify-center rounded-full border border-stone-900/15 bg-white/90 px-5 font-bold text-stone-950 shadow-lg shadow-stone-900/10')}" to="/actions">
2504
2481
  {t('actions.controls.viewQueue')}
2505
- </I18nLink>
2482
+ </Link>
2506
2483
  </div>
2507
2484
  );
2508
2485
  }
@@ -2667,6 +2644,9 @@ const commonLocaleMessages = {
2667
2644
  review: 'Revize akce',
2668
2645
  unavailable: 'Nedostupné',
2669
2646
  workspaces: 'Pracovní prostory'
2647
+ },
2648
+ seo: {
2649
+ description: 'Route-owned UltraModern plocha s lokalizovaným SSR a frameworkem řízenými public metadata.'
2670
2650
  }
2671
2651
  },
2672
2652
  en: {
@@ -2684,6 +2664,9 @@ const commonLocaleMessages = {
2684
2664
  review: 'Action review',
2685
2665
  unavailable: 'Unavailable',
2686
2666
  workspaces: 'Workspaces'
2667
+ },
2668
+ seo: {
2669
+ description: 'Route-owned UltraModern surface with localized SSR and framework-owned public metadata.'
2687
2670
  }
2688
2671
  }
2689
2672
  };
@@ -2762,6 +2745,9 @@ const generatedLocaleResources = {
2762
2745
  routes: {
2763
2746
  home: commonLocaleMessages.cs.routes.home
2764
2747
  },
2748
+ seo: {
2749
+ description: 'UltraModern shell SuperApp s lokalizovaným SSR, Module Federation a frameworkem řízenými public metadata.'
2750
+ },
2765
2751
  title: 'UltraModern Workspace'
2766
2752
  },
2767
2753
  workspace: {
@@ -2871,6 +2857,9 @@ const generatedLocaleResources = {
2871
2857
  routes: {
2872
2858
  home: commonLocaleMessages.en.routes.home
2873
2859
  },
2860
+ seo: {
2861
+ description: 'UltraModern shell SuperApp with localized SSR, Module Federation, and framework-owned public metadata.'
2862
+ },
2874
2863
  title: 'UltraModern Workspace'
2875
2864
  },
2876
2865
  workspace: {
@@ -3747,15 +3736,17 @@ function createAppConfigContract(app) {
3747
3736
  'moduleFederationPlugin',
3748
3737
  'zephyrRspackPlugin'
3749
3738
  ],
3739
+ dev: {
3740
+ assetPrefix: '/'
3741
+ },
3750
3742
  output: {
3751
3743
  assetPrefix: {
3752
3744
  envFallbackOrder: [
3753
3745
  createCloudflarePublicUrlEnv(app),
3754
3746
  'MODERN_PUBLIC_SITE_URL',
3755
- 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
3756
- app.portEnv
3747
+ 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN'
3757
3748
  ],
3758
- defaultLocalhostPort: app.port
3749
+ default: '/'
3759
3750
  },
3760
3751
  disableTsChecker: true,
3761
3752
  distPath: {
@@ -3781,6 +3772,15 @@ function createAppConfigContract(app) {
3781
3772
  },
3782
3773
  source: {
3783
3774
  mainEntryName: 'index',
3775
+ siteUrl: {
3776
+ envFallbackOrder: [
3777
+ 'MODERN_PUBLIC_SITE_URL',
3778
+ createCloudflarePublicUrlEnv(app),
3779
+ 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
3780
+ app.portEnv
3781
+ ],
3782
+ defaultLocalhostPort: app.port
3783
+ },
3784
3784
  siteUrlGlobal: 'ULTRAMODERN_SITE_URL'
3785
3785
  },
3786
3786
  ...appHasEffectApi(app) ? {
@@ -3942,11 +3942,17 @@ function createStylingContract(scope, app, enableTailwind) {
3942
3942
  };
3943
3943
  }
3944
3944
  function createPublicSurfaceContract(app) {
3945
- const files = Object.keys(createPublicSurfaceAssets(app)).sort().map((relativePath)=>relativePath.replace(/^config\/public\//u, ''));
3945
+ const files = createPublicSurfaceOutputFiles(app);
3946
+ const contentExpansionPolicy = createPublicSurfaceContentExpansionPolicy();
3946
3947
  return {
3948
+ authoring: 'colocated-route-meta',
3949
+ artifactLifecycle: 'build-and-deploy-output',
3950
+ generatedManifest: './src/routes/ultramodern-route-metadata',
3947
3951
  source: 'route-owned-public-routes',
3948
3952
  metadataExport: './src/routes/ultramodern-route-metadata',
3949
- staticRoot: 'config/public',
3953
+ generator: "scripts/generate-public-surface-assets.mjs",
3954
+ outputRoot: 'dist/public',
3955
+ cloudflareOutputRoot: '.output/public',
3950
3956
  privateRoutePolicy: 'omit-from-generated-public-surface',
3951
3957
  files,
3952
3958
  omittedByDefault: [
@@ -3954,15 +3960,103 @@ function createPublicSurfaceContract(app) {
3954
3960
  'llms.txt',
3955
3961
  'security.txt'
3956
3962
  ],
3963
+ languages: [
3964
+ ...supportedWorkspaceLanguages
3965
+ ],
3966
+ contentExpansion: {
3967
+ authoring: 'route-owned-esm-provider',
3968
+ defaultProviderFile: contentExpansionPolicy.defaultProviderFile,
3969
+ entryExport: 'default-or-entries',
3970
+ paramsSource: 'params-or-localeParams',
3971
+ draftPolicy: contentExpansionPolicy.draftPolicy,
3972
+ indexablePolicy: contentExpansionPolicy.indexablePolicy,
3973
+ lifecycle: 'executed-during-public-surface-generation'
3974
+ },
3975
+ contentSources: createPublicSurfaceContentSources(app),
3957
3976
  publicRoutes: createPublicRouteMetadata(app),
3977
+ routeEntries: createPublicSurfaceRouteEntries(app),
3958
3978
  concreteUrlPaths: createPublicSurfaceUrlPaths(app)
3959
3979
  };
3960
3980
  }
3981
+ function createPublicHeadContract() {
3982
+ const robotsPolicy = createPublicHeadRobotsPolicy();
3983
+ return {
3984
+ authoring: 'colocated-route-meta',
3985
+ generator: './src/routes/ultramodern-route-head',
3986
+ renderer: '@modern-js/runtime/head Helmet',
3987
+ ssr: true,
3988
+ title: {
3989
+ required: true,
3990
+ source: 'route.titleKey'
3991
+ },
3992
+ description: {
3993
+ required: true,
3994
+ source: "route.descriptionKey"
3995
+ },
3996
+ canonical: {
3997
+ publicIndexableOnly: true,
3998
+ source: 'localized canonical route URL'
3999
+ },
4000
+ alternates: {
4001
+ hreflang: [
4002
+ ...supportedWorkspaceLanguages
4003
+ ],
4004
+ xDefault: 'en'
4005
+ },
4006
+ openGraph: {
4007
+ publicIndexableOnly: true,
4008
+ required: [
4009
+ 'og:title',
4010
+ "og:description",
4011
+ 'og:url',
4012
+ 'og:type'
4013
+ ]
4014
+ },
4015
+ twitter: {
4016
+ publicIndexableOnly: true,
4017
+ required: [
4018
+ 'twitter:card',
4019
+ 'twitter:title',
4020
+ "twitter:description"
4021
+ ]
4022
+ },
4023
+ structuredData: {
4024
+ publicIndexableOnly: true,
4025
+ type: 'WebPage',
4026
+ sanitizesHtmlOpenBracket: true
4027
+ },
4028
+ privateRouteRobots: robotsPolicy.privateRouteRobots
4029
+ };
4030
+ }
4031
+ function createPublicWebAppArtifacts(app) {
4032
+ const routeMetadata = createRouteOwnedI18nPaths(app);
4033
+ return {
4034
+ routeMetadataFile: {
4035
+ path: `${app.directory}/src/routes/ultramodern-route-metadata.ts`,
4036
+ content: createRouteMetadataModule(app)
4037
+ },
4038
+ routeHeadFile: {
4039
+ path: `${app.directory}/src/routes/ultramodern-route-head.tsx`,
4040
+ content: createRouteHeadModule(app)
4041
+ },
4042
+ routeMetaFiles: routeMetadata.map((route)=>({
4043
+ path: createRouteMetaFilePath(app, route.canonicalPath),
4044
+ content: createRouteMetaModule(route)
4045
+ })),
4046
+ routeAliasFiles: routeMetadata.filter((route)=>'/' !== route.canonicalPath && 'shell' !== app.kind).map((route)=>({
4047
+ path: createRoutePageFilePath(app, route.canonicalPath),
4048
+ content: createRouteAliasPage(route.canonicalPath)
4049
+ })),
4050
+ publicHead: createPublicHeadContract(),
4051
+ publicSurface: createPublicSurfaceContract(app)
4052
+ };
4053
+ }
3961
4054
  function createAppGeneratedContract(scope, app, apps, enableTailwind) {
3962
4055
  const appWithResolvedRefs = 'shell' === app.kind ? {
3963
4056
  ...app,
3964
4057
  verticalRefs: apps.filter((candidate)=>'shell' !== candidate.kind).map((candidate)=>candidate.id)
3965
4058
  } : app;
4059
+ const publicWeb = createPublicWebAppArtifacts(app);
3966
4060
  const consumedRemotes = createModuleFederationRemoteContracts(appWithResolvedRefs, apps);
3967
4061
  return {
3968
4062
  id: app.id,
@@ -4019,6 +4113,8 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
4019
4113
  },
4020
4114
  routes: {
4021
4115
  source: 'route-owned',
4116
+ metadataAuthoring: 'colocated-route-meta',
4117
+ generatedManifest: true,
4022
4118
  metadataExport: './src/routes/ultramodern-route-metadata',
4023
4119
  localisedUrls: createLocalisedUrlsMap(app),
4024
4120
  owned: createRouteOwnedI18nPaths(app),
@@ -4027,7 +4123,8 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
4027
4123
  publicnessDefault: 'private-app-screen',
4028
4124
  generatedRouteMap: true,
4029
4125
  manualOverrides: [],
4030
- publicSurface: createPublicSurfaceContract(app)
4126
+ publicHead: publicWeb.publicHead,
4127
+ publicSurface: publicWeb.publicSurface
4031
4128
  },
4032
4129
  moduleFederation: {
4033
4130
  name: app.mfName,
@@ -4038,7 +4135,7 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
4038
4135
  exposes: Object.keys(app.exposes ?? {}),
4039
4136
  dts: {
4040
4137
  displayErrorInTerminal: true,
4041
- compilerInstance: "--package typescript -- tsc"
4138
+ compilerInstance: 'tsgo'
4042
4139
  },
4043
4140
  browserSafeExposesOnly: true,
4044
4141
  zephyrRspackPlugin: ZEPHYR_RSPACK_PLUGIN_VERSION
@@ -4263,7 +4360,7 @@ for (const appDir of appDirs) {
4263
4360
  if (
4264
4361
  contractEntry &&
4265
4362
  contractEntry.moduleFederation?.dts?.compilerInstance !==
4266
- '--package typescript -- tsc'
4363
+ 'tsgo'
4267
4364
  ) {
4268
4365
  throw new Error(
4269
4366
  \`Module Federation DTS must use the workspace TypeScript compiler: \${appDir}\`,
@@ -4302,53 +4399,588 @@ process.exitCode = runWorkspaceSourceCheck({
4302
4399
  });
4303
4400
  `;
4304
4401
  }
4305
- function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
4306
- const verticals = remotes.filter(appHasEffectApi).map((remote)=>({
4307
- id: remote.id,
4308
- domain: remote.domain,
4309
- stem: remote.effectApi.stem,
4310
- group: verticalEffectGroupName(remote),
4311
- path: remote.directory,
4312
- mfName: remote.mfName,
4313
- apiPrefix: remote.effectApi.prefix,
4314
- tailwindPrefix: tailwindPrefixForApp(remote),
4315
- zephyrAlias: remoteDependencyAlias(remote),
4316
- packageName: ultramodern_workspace_packageName(scope, remote.packageSuffix),
4317
- exposes: Object.keys(remote.exposes ?? {}),
4318
- componentPaths: Object.keys(remote.exposes ?? {}).map((expose)=>remoteComponentOutputPath(remote, expose)).filter((componentPath)=>Boolean(componentPath)),
4319
- namespace: appI18nNamespace(remote),
4320
- routePagePaths: createRouteOwnedI18nPaths(remote).filter((route)=>'/' !== route.canonicalPath).map((route)=>createRoutePageFilePath(remote, route.canonicalPath)),
4321
- localisedUrls: createLocalisedUrlsMap(remote),
4322
- verticalRefs: remote.verticalRefs ?? []
4323
- }));
4324
- const shellNamespace = appI18nNamespace(shellApp);
4325
- const oldRemotePaths = [
4326
- 'apps/remotes'
4327
- ];
4328
- const expectedBuildScript = remotes.length > 0 ? 'ULTRAMODERN_ZEPHYR=false pnpm -r --filter "./verticals/*" run build && ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm mf:types' : 'ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm mf:types';
4329
- const expectedCloudflareBuildScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm mf:types' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm mf:types';
4330
- const expectedCloudflareDeployScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && pnpm --filter "./apps/shell-super-app" run cloudflare:deploy' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:deploy';
4331
- const expectedCloudflareSecurity = createCloudflareSecurityContract();
4332
- return `import { execFileSync } from 'node:child_process';
4402
+ function createPublicSurfaceAssetsScript() {
4403
+ const contentExpansionPolicy = createPublicSurfaceContentExpansionPolicy();
4404
+ return `#!/usr/bin/env node
4333
4405
  import fs from 'node:fs';
4334
4406
  import path from 'node:path';
4407
+ import { fileURLToPath, pathToFileURL } from 'node:url';
4335
4408
 
4336
- const root = process.cwd();
4337
- const packageScope = '${scope}';
4338
- const expectedPnpmVersion = '${PNPM_VERSION}';
4339
- const tailwindEnabled = ${JSON.stringify(enableTailwind)};
4340
- const fullStackVerticals = ${JSON.stringify(verticals, null, 2)};
4341
- const shellNamespace = ${JSON.stringify(shellNamespace)};
4342
- const oldRemotePaths = ${JSON.stringify(oldRemotePaths, null, 2)};
4409
+ const workspaceRoot = path.resolve(
4410
+ path.dirname(fileURLToPath(import.meta.url)),
4411
+ '..',
4412
+ );
4413
+ const contractPath = path.join(
4414
+ workspaceRoot,
4415
+ '.modernjs/ultramodern-generated-contract.json',
4416
+ );
4417
+
4418
+ function readJson(filePath) {
4419
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
4420
+ }
4421
+
4422
+ function parseArgs(argv) {
4423
+ const parsed = {
4424
+ appId: undefined,
4425
+ target: 'dist',
4426
+ requirePublicOrigin: false,
4427
+ };
4428
+
4429
+ for (let index = 0; index < argv.length; index += 1) {
4430
+ const arg = argv[index];
4431
+ if (arg === '--app') {
4432
+ parsed.appId = argv[index + 1];
4433
+ index += 1;
4434
+ } else if (arg === '--target') {
4435
+ parsed.target = argv[index + 1];
4436
+ index += 1;
4437
+ } else if (arg === '--require-public-origin') {
4438
+ parsed.requirePublicOrigin = true;
4439
+ } else if (arg === '--help' || arg === '-h') {
4440
+ parsed.help = true;
4441
+ } else {
4442
+ throw new Error(\`Unknown argument: \${arg}\`);
4443
+ }
4444
+ }
4445
+
4446
+ if (!parsed.appId && !parsed.help) {
4447
+ throw new Error('Missing required --app argument');
4448
+ }
4449
+ if (!['dist', 'cloudflare'].includes(parsed.target)) {
4450
+ throw new Error(\`Unsupported public surface target: \${parsed.target}\`);
4451
+ }
4452
+
4453
+ return parsed;
4454
+ }
4455
+
4456
+ function printHelp() {
4457
+ process.stdout.write(\`Usage:
4458
+ node scripts/generate-public-surface-assets.mjs --app shell-super-app [--target dist|cloudflare] [--require-public-origin]
4459
+
4460
+ Set each app's production URL using the contract env key, for example:
4461
+ ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP=https://example.com
4462
+
4463
+ Dynamic public routes can opt into sitemap expansion by adding a route-owned
4464
+ ${contentExpansionPolicy.defaultProviderFile} provider beside route metadata, or by adding an
4465
+ explicit provider to routes.publicSurface.contentSources. Providers should export
4466
+ an entries array, entries() function, or default entries/loader returning
4467
+ UltramodernPublicSitemapEntry[].
4468
+ \`);
4469
+ }
4470
+
4471
+ function normalizeOrigin(value) {
4472
+ if (typeof value !== 'string' || value.trim() === '') {
4473
+ return undefined;
4474
+ }
4475
+ const url = new URL(value);
4476
+ return url.origin;
4477
+ }
4478
+
4479
+ function resolveOrigin(app, requirePublicOrigin) {
4480
+ const cloudflare = app.deploy?.cloudflare ?? {};
4481
+ const publicUrlEnv = cloudflare.publicUrlEnv;
4482
+ const fromAppEnv =
4483
+ typeof publicUrlEnv === 'string' ? normalizeOrigin(process.env[publicUrlEnv]) : undefined;
4484
+ const fromGlobalEnv = normalizeOrigin(process.env.MODERN_PUBLIC_SITE_URL);
4485
+ const workersDevSubdomain = process.env.ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN;
4486
+ const fromWorkersDev =
4487
+ typeof workersDevSubdomain === 'string' && workersDevSubdomain.trim() !== ''
4488
+ ? normalizeOrigin(\`https://\${cloudflare.workerName}.\${workersDevSubdomain}.workers.dev\`)
4489
+ : undefined;
4490
+
4491
+ // SEO output (sitemap <loc>, robots Sitemap:) uses the site-wide origin
4492
+ // first; the per-app deployment URL is only a fallback.
4493
+ const configuredOrigin = fromGlobalEnv ?? fromAppEnv ?? fromWorkersDev;
4494
+ if (configuredOrigin) {
4495
+ return configuredOrigin;
4496
+ }
4497
+ if (requirePublicOrigin) {
4498
+ throw new Error(
4499
+ \`\${app.id} has public routes but no production public URL. Set \${publicUrlEnv ?? 'ULTRAMODERN_PUBLIC_URL_<APP>'} or MODERN_PUBLIC_SITE_URL.\`,
4500
+ );
4501
+ }
4502
+ return undefined;
4503
+ }
4504
+
4505
+ function ensureOutputDir(app, target) {
4506
+ const relativeDir =
4507
+ target === 'cloudflare'
4508
+ ? app.routes?.publicSurface?.cloudflareOutputRoot
4509
+ : app.routes?.publicSurface?.outputRoot;
4510
+ if (typeof relativeDir !== 'string') {
4511
+ throw new Error(\`\${app.id} public surface contract is missing outputRoot for \${target}\`);
4512
+ }
4513
+ const outputDir = path.resolve(workspaceRoot, app.path, relativeDir);
4514
+ const appRoot = path.resolve(workspaceRoot, app.path);
4515
+ if (!outputDir.startsWith(appRoot + path.sep)) {
4516
+ throw new Error(\`\${app.id} public surface output escaped the app directory\`);
4517
+ }
4518
+ fs.mkdirSync(outputDir, { recursive: true });
4519
+ return outputDir;
4520
+ }
4521
+
4522
+ function resolveAppRelativePath(app, relativePath) {
4523
+ if (
4524
+ typeof relativePath !== 'string' ||
4525
+ relativePath.trim() === '' ||
4526
+ path.isAbsolute(relativePath) ||
4527
+ relativePath.split(/[\\\\/]+/).includes('..')
4528
+ ) {
4529
+ throw new Error(app.id + ' public content source has an unsafe module path');
4530
+ }
4531
+ const appRoot = path.resolve(workspaceRoot, app.path);
4532
+ const resolved = path.resolve(appRoot, relativePath);
4533
+ if (resolved !== appRoot && !resolved.startsWith(appRoot + path.sep)) {
4534
+ throw new Error(app.id + ' public content source escaped the app directory');
4535
+ }
4536
+ return resolved;
4537
+ }
4538
+
4539
+ function normalizePublicPath(pathname) {
4540
+ if (typeof pathname !== 'string') {
4541
+ throw new Error('Public route path must be a string');
4542
+ }
4543
+ const normalised = pathname
4544
+ .trim()
4545
+ .replaceAll(/\\/+/gu, '/')
4546
+ .replace(/\\/+$/u, '');
4547
+ return normalised.length > 0 && normalised.startsWith('/')
4548
+ ? normalised
4549
+ : '/' + normalised;
4550
+ }
4551
+
4552
+ function createLocalisedPublicPath(pathname, language) {
4553
+ const publicPath = normalizePublicPath(pathname);
4554
+ return publicPath === '/' ? '/' + language : '/' + language + publicPath;
4555
+ }
4556
+
4557
+ function splitPublicPathSegments(pathname) {
4558
+ return normalizePublicPath(pathname).split('/').filter(Boolean);
4559
+ }
4560
+
4561
+ function routePathParamName(segment) {
4562
+ if (segment.startsWith(':')) {
4563
+ return segment.slice(1).replace(/[?*+]$/u, '');
4564
+ }
4565
+ if (segment.startsWith('[') && segment.endsWith(']')) {
4566
+ return segment.slice(1, -1).replace(/^\\.\\.\\./u, '').replace(/\\$$/u, '');
4567
+ }
4568
+ return undefined;
4569
+ }
4570
+
4571
+ function routeSegmentToDirectory(segment) {
4572
+ const paramName = routePathParamName(segment);
4573
+ if (paramName && segment.startsWith(':')) {
4574
+ return segment.endsWith('?') ? '[' + paramName + '$]' : '[' + paramName + ']';
4575
+ }
4576
+ return segment;
4577
+ }
4578
+
4579
+ function assertParamValue(routeId, language, paramName, value) {
4580
+ if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
4581
+ throw new Error(routeId + ' ' + language + ' sitemap param ' + paramName + ' must be a string, number, or boolean');
4582
+ }
4583
+ const text = String(value).trim();
4584
+ if (text === '' || text.includes('/')) {
4585
+ throw new Error(routeId + ' ' + language + ' sitemap param ' + paramName + ' must be a non-empty path segment');
4586
+ }
4587
+ return encodeURIComponent(text);
4588
+ }
4589
+
4590
+ function expandPublicPathPattern(routeId, language, pattern, params) {
4591
+ const segments = splitPublicPathSegments(pattern);
4592
+ if (segments.length === 0) {
4593
+ return '/';
4594
+ }
4595
+ const expanded = segments.map(segment => {
4596
+ const paramName = routePathParamName(segment);
4597
+ if (!paramName) {
4598
+ if (segment.includes('*')) {
4599
+ throw new Error(routeId + ' ' + language + ' sitemap expansion does not support wildcard path segment ' + segment);
4600
+ }
4601
+ return segment;
4602
+ }
4603
+ if (!Object.prototype.hasOwnProperty.call(params, paramName)) {
4604
+ throw new Error(routeId + ' ' + language + ' sitemap entry is missing param ' + paramName);
4605
+ }
4606
+ return assertParamValue(routeId, language, paramName, params[paramName]);
4607
+ });
4608
+ return '/' + expanded.join('/');
4609
+ }
4610
+
4611
+ function assertPlainObject(value, label) {
4612
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
4613
+ throw new Error(label + ' must be an object');
4614
+ }
4615
+ return value;
4616
+ }
4617
+
4618
+ function normalizeSitemapFields(routeId, entry) {
4619
+ const normalized = {};
4620
+ if (entry.lastModified !== undefined) {
4621
+ const lastModified = String(entry.lastModified).trim();
4622
+ if (lastModified === '' || Number.isNaN(Date.parse(lastModified))) {
4623
+ throw new Error(routeId + ' sitemap entry has invalid lastModified');
4624
+ }
4625
+ normalized.lastModified = lastModified;
4626
+ }
4627
+ if (entry.changeFrequency !== undefined) {
4628
+ const allowed = new Set(['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never']);
4629
+ if (!allowed.has(entry.changeFrequency)) {
4630
+ throw new Error(routeId + ' sitemap entry has invalid changeFrequency');
4631
+ }
4632
+ normalized.changeFrequency = entry.changeFrequency;
4633
+ }
4634
+ if (entry.priority !== undefined) {
4635
+ if (typeof entry.priority !== 'number' || entry.priority < 0 || entry.priority > 1) {
4636
+ throw new Error(routeId + ' sitemap entry priority must be a number between 0 and 1');
4637
+ }
4638
+ normalized.priority = entry.priority;
4639
+ }
4640
+ return normalized;
4641
+ }
4642
+
4643
+ function routePathToProviderDirectory(routePath) {
4644
+ const segments = splitPublicPathSegments(routePath);
4645
+ if (segments.length === 0) {
4646
+ return 'src/routes/[lang]';
4647
+ }
4648
+ return path.posix.join(
4649
+ 'src/routes/[lang]',
4650
+ ...segments.map(routeSegmentToDirectory),
4651
+ );
4652
+ }
4653
+
4654
+ function createDiscoveredContentSources(app, publicSurface) {
4655
+ const explicitRouteIds = new Set(
4656
+ (publicSurface.contentSources ?? []).map(source => source.routeId),
4657
+ );
4658
+ const discovered = [];
4659
+ for (const route of publicSurface.publicRoutes ?? []) {
4660
+ if (
4661
+ explicitRouteIds.has(route.id) ||
4662
+ !Object.values(route.localisedPaths ?? {}).some(routePath =>
4663
+ /(?:^|\\/):[^/]+|\\[[^\\]]+\\]/u.test(routePath),
4664
+ )
4665
+ ) {
4666
+ continue;
4667
+ }
4668
+ const providerModule = path.posix.join(
4669
+ routePathToProviderDirectory(route.canonicalPath),
4670
+ '${contentExpansionPolicy.defaultProviderFile}',
4671
+ );
4672
+ if (fs.existsSync(resolveAppRelativePath(app, providerModule))) {
4673
+ discovered.push({
4674
+ entryExport: 'default-or-entries',
4675
+ module: providerModule,
4676
+ routeId: route.id,
4677
+ });
4678
+ }
4679
+ }
4680
+ return discovered;
4681
+ }
4682
+
4683
+ function resolveContentSources(app, publicSurface) {
4684
+ return [
4685
+ ...(publicSurface.contentSources ?? []),
4686
+ ...createDiscoveredContentSources(app, publicSurface),
4687
+ ];
4688
+ }
4689
+
4690
+ async function loadContentSourceEntries(app, contentSource, languages) {
4691
+ if (typeof contentSource?.routeId !== 'string' || contentSource.routeId.trim() === '') {
4692
+ throw new Error(app.id + ' public content source is missing routeId');
4693
+ }
4694
+ const modulePath = resolveAppRelativePath(app, contentSource.module);
4695
+ const moduleExports = await import(pathToFileURL(modulePath).href);
4696
+ const exported = moduleExports.default ?? moduleExports.entries;
4697
+ const rawEntries =
4698
+ typeof exported === 'function'
4699
+ ? await exported({
4700
+ appId: app.id,
4701
+ languages,
4702
+ routeId: contentSource.routeId,
4703
+ })
4704
+ : exported;
4705
+ if (!Array.isArray(rawEntries)) {
4706
+ throw new Error(app.id + ' public content source for ' + contentSource.routeId + ' must export an entries array or loader');
4707
+ }
4708
+ return rawEntries;
4709
+ }
4710
+
4711
+ async function expandContentSources(app, publicSurface, languages) {
4712
+ const routesById = new Map(
4713
+ (publicSurface.publicRoutes ?? []).map(route => [route.id, route]),
4714
+ );
4715
+ const expanded = [];
4716
+ for (const contentSource of resolveContentSources(app, publicSurface)) {
4717
+ const route = routesById.get(contentSource.routeId);
4718
+ if (!route) {
4719
+ throw new Error(app.id + ' public content source references unknown route ' + contentSource.routeId);
4720
+ }
4721
+ const rawEntries = await loadContentSourceEntries(app, contentSource, languages);
4722
+ for (const rawEntry of rawEntries) {
4723
+ const entry = assertPlainObject(rawEntry, route.id + ' sitemap entry');
4724
+ if (entry.draft === true || entry.indexable === false) {
4725
+ continue;
4726
+ }
4727
+ const baseParams = assertPlainObject(entry.params, route.id + ' sitemap entry params');
4728
+ const localeParams = entry.localeParams === undefined
4729
+ ? {}
4730
+ : assertPlainObject(entry.localeParams, route.id + ' sitemap entry localeParams');
4731
+ const localeUrlPaths = {};
4732
+ for (const language of languages) {
4733
+ const params = {
4734
+ ...baseParams,
4735
+ ...(localeParams[language] ?? {}),
4736
+ };
4737
+ localeUrlPaths[language] = createLocalisedPublicPath(
4738
+ expandPublicPathPattern(route.id, language, route.localisedPaths[language], params),
4739
+ language,
4740
+ );
4741
+ }
4742
+ expanded.push({
4743
+ ...route,
4744
+ ...normalizeSitemapFields(route.id, entry),
4745
+ canonicalUrlPath: localeUrlPaths.en,
4746
+ localeUrlPaths,
4747
+ });
4748
+ }
4749
+ }
4750
+ return expanded;
4751
+ }
4752
+
4753
+ function mergeRouteEntries(routeEntries, expandedRouteEntries, languages) {
4754
+ const byKey = new Map();
4755
+ const urlPathOwners = new Map();
4756
+ for (const route of [...routeEntries, ...expandedRouteEntries]) {
4757
+ const key = route.id + ':' + route.canonicalUrlPath;
4758
+ if (byKey.has(key)) {
4759
+ throw new Error('Duplicate public sitemap route entry ' + key);
4760
+ }
4761
+ for (const language of languages) {
4762
+ const urlPath = route.localeUrlPaths?.[language];
4763
+ if (typeof urlPath !== 'string') {
4764
+ throw new Error(route.id + ' public route entry is missing ' + language + ' locale URL path');
4765
+ }
4766
+ const existingOwner = urlPathOwners.get(urlPath);
4767
+ if (existingOwner && existingOwner !== route.id) {
4768
+ throw new Error('Duplicate public sitemap URL path ' + urlPath + ' from ' + existingOwner + ' and ' + route.id);
4769
+ }
4770
+ urlPathOwners.set(urlPath, route.id);
4771
+ }
4772
+ byKey.set(key, route);
4773
+ }
4774
+ return Array.from(byKey.values()).sort(
4775
+ (left, right) =>
4776
+ left.canonicalUrlPath.localeCompare(right.canonicalUrlPath) ||
4777
+ left.id.localeCompare(right.id),
4778
+ );
4779
+ }
4780
+
4781
+ function uniqueSorted(values) {
4782
+ return Array.from(new Set(values)).sort((left, right) =>
4783
+ left.localeCompare(right),
4784
+ );
4785
+ }
4786
+
4787
+ function createConcreteUrlPaths(routeEntries, languages) {
4788
+ return uniqueSorted(
4789
+ routeEntries.flatMap(route => languages.map(language => route.localeUrlPaths[language])),
4790
+ );
4791
+ }
4792
+
4793
+ function escapeXmlText(value) {
4794
+ return value
4795
+ .replaceAll('&', '&amp;')
4796
+ .replaceAll('<', '&lt;')
4797
+ .replaceAll('>', '&gt;');
4798
+ }
4799
+
4800
+ function escapeXmlAttribute(value) {
4801
+ return escapeXmlText(value).replaceAll('"', '&quot;');
4802
+ }
4803
+
4804
+ function renderRobotsTxt(urlPaths, sitemapUrl) {
4805
+ const lines = ['User-agent: *'];
4806
+ if (urlPaths.length === 0) {
4807
+ lines.push('Disallow: /');
4808
+ } else {
4809
+ for (const urlPath of urlPaths) {
4810
+ lines.push(\`Allow: \${urlPath}$\`);
4811
+ }
4812
+ lines.push('Disallow: /');
4813
+ if (sitemapUrl) {
4814
+ lines.push(\`Sitemap: \${sitemapUrl}\`);
4815
+ }
4816
+ }
4817
+ return \`\${lines.join('\\n')}\\n\`;
4818
+ }
4819
+
4820
+ function renderSitemapXml(origin, routeEntries, languages) {
4821
+ const lines = [
4822
+ '<?xml version="1.0" encoding="UTF-8"?>',
4823
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">',
4824
+ ];
4825
+
4826
+ for (const route of routeEntries) {
4827
+ for (const language of languages) {
4828
+ lines.push(' <url>');
4829
+ lines.push(\` <loc>\${escapeXmlText(\`\${origin}\${route.localeUrlPaths[language]}\`)}</loc>\`);
4830
+ for (const alternateLanguage of languages) {
4831
+ lines.push(
4832
+ \` <xhtml:link rel="alternate" hreflang="\${alternateLanguage}" href="\${escapeXmlAttribute(
4833
+ \`\${origin}\${route.localeUrlPaths[alternateLanguage]}\`,
4834
+ )}" />\`,
4835
+ );
4836
+ }
4837
+ lines.push(
4838
+ \` <xhtml:link rel="alternate" hreflang="x-default" href="\${escapeXmlAttribute(
4839
+ \`\${origin}\${route.localeUrlPaths.en}\`,
4840
+ )}" />\`,
4841
+ );
4842
+ if (route.lastModified) {
4843
+ lines.push(\` <lastmod>\${escapeXmlText(route.lastModified)}</lastmod>\`);
4844
+ }
4845
+ if (route.changeFrequency) {
4846
+ lines.push(\` <changefreq>\${escapeXmlText(route.changeFrequency)}</changefreq>\`);
4847
+ }
4848
+ if (route.priority !== undefined) {
4849
+ lines.push(\` <priority>\${route.priority.toFixed(1).replace(/\\.0$/u, '')}</priority>\`);
4850
+ }
4851
+ lines.push(' </url>');
4852
+ }
4853
+ }
4854
+
4855
+ lines.push('</urlset>');
4856
+ return \`\${lines.join('\\n')}\\n\`;
4857
+ }
4858
+
4859
+ function renderWebManifest(app, urlPaths) {
4860
+ const startUrl = urlPaths[0];
4861
+ const manifest = {
4862
+ background_color: '#ffffff',
4863
+ categories: ['business', 'productivity'],
4864
+ display: 'standalone',
4865
+ icons: [],
4866
+ lang: 'en',
4867
+ name: app.marker?.appId ?? app.id,
4868
+ short_name: app.marker?.appId ?? app.id,
4869
+ theme_color: '#133225',
4870
+ ...(startUrl ? { scope: '/', start_url: startUrl } : {}),
4871
+ };
4872
+ return \`\${JSON.stringify(manifest, null, 2)}\\n\`;
4873
+ }
4874
+
4875
+ function removeIfExists(outputDir, fileName) {
4876
+ fs.rmSync(path.join(outputDir, fileName), { force: true });
4877
+ }
4878
+
4879
+ function writeText(outputDir, fileName, content) {
4880
+ fs.writeFileSync(path.join(outputDir, fileName), content);
4881
+ }
4882
+
4883
+ async function generatePublicSurfaceAssets(app, target, requirePublicOrigin) {
4884
+ const publicSurface = app.routes?.publicSurface ?? {};
4885
+ const languages = publicSurface.languages ?? ['en', 'cs'];
4886
+ const outputDir = ensureOutputDir(app, target);
4887
+ const shouldRequirePublicOrigin =
4888
+ requirePublicOrigin ||
4889
+ process.env.ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS === 'true';
4890
+ const routeEntries = mergeRouteEntries(
4891
+ publicSurface.routeEntries ?? [],
4892
+ await expandContentSources(app, publicSurface, languages),
4893
+ languages,
4894
+ );
4895
+ const urlPaths = createConcreteUrlPaths(routeEntries, languages);
4896
+
4897
+ if (routeEntries.length === 0) {
4898
+ writeText(outputDir, 'robots.txt', renderRobotsTxt([], undefined));
4899
+ removeIfExists(outputDir, 'sitemap.xml');
4900
+ removeIfExists(outputDir, 'site.webmanifest');
4901
+ return;
4902
+ }
4903
+
4904
+ const origin = resolveOrigin(app, shouldRequirePublicOrigin);
4905
+ if (!origin) {
4906
+ writeText(outputDir, 'robots.txt', renderRobotsTxt([], undefined));
4907
+ removeIfExists(outputDir, 'sitemap.xml');
4908
+ removeIfExists(outputDir, 'site.webmanifest');
4909
+ return;
4910
+ }
4911
+
4912
+ writeText(outputDir, 'sitemap.xml', renderSitemapXml(origin, routeEntries, languages));
4913
+ writeText(outputDir, 'site.webmanifest', renderWebManifest(app, urlPaths));
4914
+ writeText(outputDir, 'robots.txt', renderRobotsTxt(urlPaths, \`\${origin}/sitemap.xml\`));
4915
+ }
4916
+
4917
+ try {
4918
+ const args = parseArgs(process.argv.slice(2));
4919
+ if (args.help) {
4920
+ printHelp();
4921
+ process.exit(0);
4922
+ }
4923
+ const contract = readJson(contractPath);
4924
+ const app = contract.apps?.find(candidate => candidate.id === args.appId);
4925
+ if (!app) {
4926
+ throw new Error(\`Unknown app in generated contract: \${args.appId}\`);
4927
+ }
4928
+ await generatePublicSurfaceAssets(app, args.target, args.requirePublicOrigin);
4929
+ } catch (error) {
4930
+ process.stderr.write(\`[public-surface] \${error.message}\\n\`);
4931
+ process.exitCode = 1;
4932
+ }
4933
+ `;
4934
+ }
4935
+ function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
4936
+ const verticals = remotes.filter(appHasEffectApi).map((remote)=>({
4937
+ id: remote.id,
4938
+ domain: remote.domain,
4939
+ stem: remote.effectApi.stem,
4940
+ group: verticalEffectGroupName(remote),
4941
+ path: remote.directory,
4942
+ mfName: remote.mfName,
4943
+ apiPrefix: remote.effectApi.prefix,
4944
+ tailwindPrefix: tailwindPrefixForApp(remote),
4945
+ zephyrAlias: remoteDependencyAlias(remote),
4946
+ packageName: ultramodern_workspace_packageName(scope, remote.packageSuffix),
4947
+ exposes: Object.keys(remote.exposes ?? {}),
4948
+ componentPaths: Object.keys(remote.exposes ?? {}).map((expose)=>remoteComponentOutputPath(remote, expose)).filter((componentPath)=>Boolean(componentPath)),
4949
+ namespace: appI18nNamespace(remote),
4950
+ routePagePaths: createRouteOwnedI18nPaths(remote).filter((route)=>'/' !== route.canonicalPath).map((route)=>createRoutePageFilePath(remote, route.canonicalPath)),
4951
+ routeMetaPaths: createRouteOwnedI18nPaths(remote).map((route)=>createRouteMetaFilePath(remote, route.canonicalPath)),
4952
+ localisedUrls: createLocalisedUrlsMap(remote),
4953
+ verticalRefs: remote.verticalRefs ?? []
4954
+ }));
4955
+ const shellRouteMetaPaths = createRouteOwnedI18nPaths(shellApp).map((route)=>createRouteMetaFilePath(shellApp, route.canonicalPath));
4956
+ const shellNamespace = appI18nNamespace(shellApp);
4957
+ const oldRemotePaths = [
4958
+ 'apps/remotes'
4959
+ ];
4960
+ const expectedBuildScript = remotes.length > 0 ? 'ULTRAMODERN_ZEPHYR=false pnpm -r --filter "./verticals/*" run build && ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm mf:types' : 'ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm mf:types';
4961
+ const expectedCloudflareBuildScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm mf:types' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm mf:types';
4962
+ const expectedCloudflareDeployScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && pnpm --filter "./apps/shell-super-app" run cloudflare:deploy' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:deploy';
4963
+ const expectedCloudflareSecurity = createCloudflareSecurityContract();
4964
+ const contentExpansionPolicy = createPublicSurfaceContentExpansionPolicy();
4965
+ const robotsPolicy = createPublicHeadRobotsPolicy();
4966
+ const qualityGates = createPublicWebsiteQualityGateContract();
4967
+ return `import { execFileSync } from 'node:child_process';
4968
+ import fs from 'node:fs';
4969
+ import path from 'node:path';
4970
+
4971
+ const root = process.cwd();
4972
+ const packageScope = '${scope}';
4973
+ const expectedPnpmVersion = '${PNPM_VERSION}';
4974
+ const tailwindEnabled = ${JSON.stringify(enableTailwind)};
4975
+ const fullStackVerticals = ${JSON.stringify(verticals, null, 2)};
4976
+ const shellNamespace = ${JSON.stringify(shellNamespace)};
4977
+ const oldRemotePaths = ${JSON.stringify(oldRemotePaths, null, 2)};
4343
4978
  const expectedBuildScript = ${JSON.stringify(expectedBuildScript)};
4344
4979
  const expectedCloudflareBuildScript = ${JSON.stringify(expectedCloudflareBuildScript)};
4345
4980
  const expectedCloudflareDeployScript = ${JSON.stringify(expectedCloudflareDeployScript)};
4346
4981
  const expectedCloudflareSecurity = ${JSON.stringify(expectedCloudflareSecurity, null, 2)};
4347
- const publicSurfaceRequiredAssetPaths = ${JSON.stringify([
4348
- ...publicSurfaceRequiredAssetPaths
4349
- ], null, 2)};
4350
- const publicSurfaceOptionalAssetPaths = ${JSON.stringify([
4351
- ...publicSurfaceOptionalAssetPaths
4982
+ const publicSurfaceManagedSourceAssetPaths = ${JSON.stringify([
4983
+ ...publicSurfaceManagedSourceAssetPaths
4352
4984
  ], null, 2)};
4353
4985
  const expectedModernPackageSpecifier = packageName => {
4354
4986
  if (packageSource.strategy === 'workspace') {
@@ -4374,19 +5006,67 @@ const assertNotExists = relativePath => {
4374
5006
  assert(!fs.existsSync(path.join(root, relativePath)), \`Unexpected \${relativePath}\`);
4375
5007
  };
4376
5008
  const assertPublicSurfaceAssets = (appPath, publicRoutes) => {
4377
- const robots = readText(\`\${appPath}/config/public/robots.txt\`);
4378
- if ((publicRoutes ?? []).length === 0) {
4379
- assert(robots.includes('Disallow: /'), \`\${appPath} robots.txt must disallow crawling when no public routes exist\`);
4380
- for (const relativePath of publicSurfaceOptionalAssetPaths) {
4381
- assertNotExists(\`\${appPath}/\${relativePath}\`);
4382
- }
4383
- return;
5009
+ for (const relativePath of publicSurfaceManagedSourceAssetPaths) {
5010
+ assertNotExists(\`\${appPath}/\${relativePath}\`);
5011
+ }
5012
+ void publicRoutes;
5013
+ };
5014
+ const assertPublicSurfaceContract = (appId, publicSurface) => {
5015
+ assert(publicSurface?.artifactLifecycle === 'build-and-deploy-output', \`\${appId} public surface artifacts must be build/deploy outputs\`);
5016
+ assert(publicSurface?.generator === 'scripts/generate-public-surface-assets.mjs', \`\${appId} public surface generator script is incorrect\`);
5017
+ assert(publicSurface?.outputRoot === 'dist/public', \`\${appId} public surface dist outputRoot is incorrect\`);
5018
+ assert(publicSurface?.cloudflareOutputRoot === '.output/public', \`\${appId} public surface Cloudflare outputRoot is incorrect\`);
5019
+ assert(!('staticRoot' in (publicSurface ?? {})), \`\${appId} public surface must not point at source config/public\`);
5020
+ assert((publicSurface?.files ?? []).includes('robots.txt'), \`\${appId} public surface must always emit robots.txt\`);
5021
+ assert(publicSurface?.contentExpansion?.authoring === 'route-owned-esm-provider', \`\${appId} public content expansion authoring is incorrect\`);
5022
+ assert(publicSurface?.contentExpansion?.defaultProviderFile === '${contentExpansionPolicy.defaultProviderFile}', \`\${appId} public content expansion provider file is incorrect\`);
5023
+ assert(publicSurface?.contentExpansion?.draftPolicy === '${contentExpansionPolicy.draftPolicy}', \`\${appId} public content expansion draft policy is incorrect\`);
5024
+ assert(publicSurface?.contentExpansion?.indexablePolicy === '${contentExpansionPolicy.indexablePolicy}', \`\${appId} public content expansion indexable policy is incorrect\`);
5025
+ assert(Array.isArray(publicSurface?.contentSources), \`\${appId} public content sources must be an array\`);
5026
+ if ((publicSurface?.publicRoutes ?? []).length === 0) {
5027
+ assert(!(publicSurface?.files ?? []).includes('sitemap.xml'), \`\${appId} private public surface must omit sitemap.xml\`);
5028
+ assert(!(publicSurface?.files ?? []).includes('site.webmanifest'), \`\${appId} private public surface must omit site.webmanifest\`);
5029
+ } else {
5030
+ assert((publicSurface?.files ?? []).includes('sitemap.xml'), \`\${appId} public surface must emit sitemap.xml when public routes exist\`);
5031
+ assert((publicSurface?.files ?? []).includes('site.webmanifest'), \`\${appId} public surface must emit site.webmanifest when public routes exist\`);
5032
+ }
5033
+ };
5034
+ const assertPublicHeadContract = (appId, publicHead, headModule) => {
5035
+ assert(publicHead?.generator === './src/routes/ultramodern-route-head', \`\${appId} public head generator is incorrect\`);
5036
+ assert(publicHead?.renderer === '@modern-js/runtime/head Helmet', \`\${appId} public head renderer is incorrect\`);
5037
+ assert(publicHead?.ssr === true, \`\${appId} public head must be SSR-rendered\`);
5038
+ assert(publicHead?.title?.source === 'route.titleKey', \`\${appId} public head title must come from route metadata\`);
5039
+ assert(publicHead?.description?.source === 'route.descriptionKey', \`\${appId} public head description must come from route metadata\`);
5040
+ assert(publicHead?.canonical?.publicIndexableOnly === true, \`\${appId} canonical links must be public/indexable only\`);
5041
+ assert(publicHead?.structuredData?.sanitizesHtmlOpenBracket === true, \`\${appId} structured data must sanitize HTML open brackets\`);
5042
+ assert(publicHead?.privateRouteRobots === '${robotsPolicy.privateRouteRobots}', \`\${appId} private route robots policy is incorrect\`);
5043
+ for (const snippet of [
5044
+ "from '@modern-js/runtime/head'",
5045
+ '<title>{title}</title>',
5046
+ 'name="description"',
5047
+ 'name="robots"',
5048
+ 'rel="canonical"',
5049
+ 'rel="alternate"',
5050
+ 'property="og:title"',
5051
+ 'property="og:description"',
5052
+ 'name="twitter:card"',
5053
+ 'application/ld+json',
5054
+ "replaceAll('<', '\\\\\\\\u003c')",
5055
+ ]) {
5056
+ assert(headModule.includes(snippet), \`\${appId} route head module is missing \${snippet}\`);
4384
5057
  }
4385
- const sitemap = readText(\`\${appPath}/config/public/sitemap.xml\`);
4386
- const manifest = readJson(\`\${appPath}/config/public/site.webmanifest\`);
4387
- assert(!sitemap.includes('<lastmod>'), \`\${appPath} sitemap must omit build-time lastmod values\`);
4388
- assert(typeof manifest.name === 'string' && manifest.name.length > 0, \`\${appPath} web manifest must include a safe app name\`);
4389
- assert(typeof manifest.start_url === 'string' && manifest.start_url.startsWith('/'), \`\${appPath} web manifest start_url must be a public route path\`);
5058
+ };
5059
+ const assertCloudflareQualityGates = (appId, qualityGates) => {
5060
+ assert(qualityGates?.publicRoutes?.requireSitemapWhenPresent === true, \`\${appId} quality gates must require sitemap for public routes\`);
5061
+ assert(qualityGates?.publicRoutes?.requireRobotsSitemapConsistency === true, \`\${appId} quality gates must require robots/sitemap consistency\`);
5062
+ assert(qualityGates?.statusCodes?.unknownRouteStatus === 404, \`\${appId} quality gates must require 404 unknown routes\`);
5063
+ assert(qualityGates?.indexing?.previewNoindex === true, \`\${appId} quality gates must require preview noindex\`);
5064
+ assert(qualityGates?.indexing?.productionPublicRoutesIndexable === true, \`\${appId} quality gates must require production public routes to be indexable\`);
5065
+ assert(qualityGates?.assets?.cssPreloadRequired === true, \`\${appId} quality gates must require CSS preload evidence\`);
5066
+ assert(qualityGates?.assets?.sourcemapsPubliclyReferenced === false, \`\${appId} quality gates must reject public sourcemap references\`);
5067
+ assert(typeof qualityGates?.budgets?.ssrHtmlMaxBytes === 'number', \`\${appId} quality gates must define SSR HTML byte budget\`);
5068
+ assert(typeof qualityGates?.budgets?.mfManifestMaxBytes === 'number', \`\${appId} quality gates must define MF manifest byte budget\`);
5069
+ assert(qualityGates?.csp?.finalMode === '${qualityGates.csp.finalMode}', \`\${appId} CSP final mode decision is missing\`);
4390
5070
  };
4391
5071
  const expectedWorkerName = packageSuffix => \`\${packageScope}-\${packageSuffix}\`.slice(0, 63);
4392
5072
  const expectedChunkLoadingGlobal = mfName =>
@@ -4448,6 +5128,7 @@ const requiredPaths = [
4448
5128
  'scripts/assert-mf-types.mjs',
4449
5129
  'scripts/bootstrap-agent-skills.mjs',
4450
5130
  'scripts/check-ultramodern-i18n-boundaries.mjs',
5131
+ 'scripts/generate-public-surface-assets.mjs',
4451
5132
  'scripts/proof-cloudflare-version.mjs',
4452
5133
  'scripts/setup-agent-reference-repos.mjs',
4453
5134
  'apps/shell-super-app/package.json',
@@ -4462,11 +5143,10 @@ const requiredPaths = [
4462
5143
  \`apps/shell-super-app/locales/cs/\${shellNamespace}.json\`,
4463
5144
  'apps/shell-super-app/src/routes/index.css',
4464
5145
  'apps/shell-super-app/src/routes/layout.tsx',
5146
+ 'apps/shell-super-app/src/routes/ultramodern-route-head.tsx',
4465
5147
  'apps/shell-super-app/src/routes/ultramodern-route-metadata.ts',
4466
5148
  'apps/shell-super-app/src/routes/[lang]/page.tsx',
4467
- ...publicSurfaceRequiredAssetPaths.map(
4468
- relativePath => \`apps/shell-super-app/\${relativePath}\`,
4469
- ),
5149
+ ...${JSON.stringify(shellRouteMetaPaths, null, 2)},
4470
5150
  'packages/shared-contracts/src/index.ts',
4471
5151
  'packages/shared-design-tokens/src/index.ts',
4472
5152
  'packages/shared-design-tokens/src/tokens.css',
@@ -4491,12 +5171,11 @@ for (const vertical of fullStackVerticals) {
4491
5171
  \`\${vertical.path}/locales/cs/\${vertical.namespace}.json\`,
4492
5172
  \`\${vertical.path}/src/routes/index.css\`,
4493
5173
  \`\${vertical.path}/src/routes/layout.tsx\`,
5174
+ \`\${vertical.path}/src/routes/ultramodern-route-head.tsx\`,
4494
5175
  \`\${vertical.path}/src/routes/ultramodern-route-metadata.ts\`,
4495
5176
  \`\${vertical.path}/src/routes/[lang]/page.tsx\`,
4496
- ...publicSurfaceRequiredAssetPaths.map(
4497
- relativePath => \`\${vertical.path}/\${relativePath}\`,
4498
- ),
4499
5177
  ...vertical.routePagePaths,
5178
+ ...vertical.routeMetaPaths,
4500
5179
  );
4501
5180
  }
4502
5181
 
@@ -4617,6 +5296,10 @@ assert(generatedContract.cssFederation?.sharedDesignTokens?.ssr?.firstPaintRequi
4617
5296
 
4618
5297
  const shellPackage = readJson('apps/shell-super-app/package.json');
4619
5298
  const shellModernConfig = readText('apps/shell-super-app/modern.config.ts');
5299
+ const shellRouteHead = readText('apps/shell-super-app/src/routes/ultramodern-route-head.tsx');
5300
+ const shellRouteMetadata = readText('apps/shell-super-app/src/routes/ultramodern-route-metadata.ts');
5301
+ assert(shellRouteMetadata.includes('@generated by @modern-js/create'), 'Shell route metadata compatibility manifest must be marked generated');
5302
+ assert(shellRouteMetadata.includes("authoring: 'colocated-route-meta'"), 'Shell route metadata manifest must advertise colocated authoring');
4620
5303
  const expectedZephyrDependencies = Object.fromEntries(
4621
5304
  fullStackVerticals.map(vertical => [
4622
5305
  vertical.zephyrAlias,
@@ -4639,10 +5322,18 @@ assert(shellContract?.deploy?.cloudflare?.publicUrlEnv === 'ULTRAMODERN_PUBLIC_U
4639
5322
  assert(shellContract?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell Cloudflare compatibilityDate is incorrect');
4640
5323
  assert(JSON.stringify(shellContract?.deploy?.cloudflare?.compatibilityFlags) === JSON.stringify(expectedCloudflareCompatibilityFlags), 'Shell Cloudflare compatibility flags are incorrect');
4641
5324
  assert(JSON.stringify(shellContract?.deploy?.cloudflare?.security) === JSON.stringify(expectedCloudflareSecurity), 'Shell Cloudflare security contract is incorrect');
5325
+ assertCloudflareQualityGates('shell-super-app', shellContract?.deploy?.cloudflare?.qualityGates);
4642
5326
  assert(shellContract?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell worker compatibilityDate is incorrect');
4643
5327
  assert(shellContract?.deploy?.worker?.name === expectedWorkerName('shell-super-app'), 'Shell worker name is incorrect');
4644
5328
  assert(shellModernConfig.includes("const cloudflareWorkerName = '" + expectedWorkerName('shell-super-app') + "'"), 'Shell modern.config.ts must define the Cloudflare worker name');
4645
5329
  assert(shellModernConfig.includes('name: cloudflareWorkerName'), 'Shell modern.config.ts must wire deploy.worker.name');
5330
+ assert(shellModernConfig.includes('const assetPrefix ='), 'Shell modern.config.ts must derive a dedicated asset prefix');
5331
+ assert(shellModernConfig.includes("assetPrefix: '/'"), 'Shell modern.config.ts must keep dev assets origin-relative');
5332
+ assert(shellModernConfig.includes('assetPrefix,'), 'Shell modern.config.ts must wire output.assetPrefix to the derived asset prefix');
5333
+ assert(shellContract?.config?.dev?.assetPrefix === '/', 'Shell dev asset prefix must stay origin-relative');
5334
+ assert(shellContract?.config?.output?.assetPrefix?.default === '/', 'Shell asset prefix must default to origin-relative paths');
5335
+ assert(JSON.stringify(shellContract?.config?.output?.assetPrefix?.envFallbackOrder) === JSON.stringify(['ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'MODERN_PUBLIC_SITE_URL', 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN']), 'Shell asset prefix env fallback order is incorrect');
5336
+ assert(JSON.stringify(shellContract?.config?.source?.siteUrl?.envFallbackOrder) === JSON.stringify(['MODERN_PUBLIC_SITE_URL', 'ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN', 'SHELL_SUPER_APP_PORT']), 'Shell site URL env fallback order is incorrect');
4646
5337
  assert(shellContract?.config?.rspack?.output?.uniqueName === 'shellSuperApp', 'Shell Rspack uniqueName is incorrect');
4647
5338
  assert(shellContract?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal('shellSuperApp'), 'Shell Rspack chunkLoadingGlobal is incorrect');
4648
5339
  assert(topology.shell?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell topology Cloudflare workerName is incorrect');
@@ -4657,11 +5348,15 @@ assert(shellContract?.styling?.federation?.assets?.shared?.some(asset => asset.e
4657
5348
  assert(shellContract?.styling?.federation?.dedupe?.duplicateBaseStylesAllowed === false, 'Shell CSS contract must forbid duplicated base styles');
4658
5349
  assert(shellContract?.styling?.federation?.ssr?.firstPaintRequired === true, 'Shell CSS must be required for SSR first paint');
4659
5350
  assert(shellContract?.routes?.privateByDefault === true, 'Shell routes must be private by default');
5351
+ assert(shellContract?.routes?.metadataAuthoring === 'colocated-route-meta', 'Shell route metadata authoring mode is incorrect');
5352
+ assert(shellContract?.routes?.generatedManifest === true, 'Shell route metadata manifest must be generated');
4660
5353
  assert(shellContract?.routes?.publicnessDefault === 'private-app-screen', 'Shell route publicness default is incorrect');
4661
5354
  assert(JSON.stringify(shellContract?.routes?.publicRoutes ?? []) === '[]', 'Shell must not expose generated public routes by default');
5355
+ assertPublicHeadContract('shell-super-app', shellContract?.routes?.publicHead, shellRouteHead);
5356
+ assertPublicSurfaceContract('shell-super-app', shellContract?.routes?.publicSurface);
4662
5357
  assert(
4663
- (shellContract?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen'),
4664
- 'Shell owned routes must be non-indexable private app screens by default',
5358
+ (shellContract?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen' && typeof route.descriptionKey === 'string'),
5359
+ 'Shell owned routes must be non-indexable private app screens by default and include description keys',
4665
5360
  );
4666
5361
  assertPublicSurfaceAssets('apps/shell-super-app', shellContract?.routes?.publicRoutes ?? []);
4667
5362
  assert(
@@ -4675,6 +5370,10 @@ assert(!('effectServices' in topology), 'Default APIs must be vertical-owned, no
4675
5370
  for (const vertical of fullStackVerticals) {
4676
5371
  const packageJson = readJson(\`\${vertical.path}/package.json\`);
4677
5372
  const modernConfig = readText(\`\${vertical.path}/modern.config.ts\`);
5373
+ const routeHead = readText(\`\${vertical.path}/src/routes/ultramodern-route-head.tsx\`);
5374
+ const routeMetadata = readText(\`\${vertical.path}/src/routes/ultramodern-route-metadata.ts\`);
5375
+ assert(routeMetadata.includes('@generated by @modern-js/create'), \`\${vertical.id} route metadata compatibility manifest must be marked generated\`);
5376
+ assert(routeMetadata.includes("authoring: 'colocated-route-meta'"), \`\${vertical.id} route metadata manifest must advertise colocated authoring\`);
4678
5377
  assert(packageJson.name === vertical.packageName, \`\${vertical.id} package name is incorrect\`);
4679
5378
  assert(packageJson.scripts?.['cloudflare:deploy'] === 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json', \`\${vertical.id} must expose cloudflare:deploy\`);
4680
5379
  assert(packageJson.scripts?.['cloudflare:proof']?.includes(\`--app \${vertical.id}\`), \`\${vertical.id} must expose cloudflare:proof\`);
@@ -4707,16 +5406,23 @@ for (const vertical of fullStackVerticals) {
4707
5406
  assert(contractEntry?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, \`\${vertical.id} Cloudflare compatibilityDate is incorrect\`);
4708
5407
  assert(JSON.stringify(contractEntry?.deploy?.cloudflare?.compatibilityFlags) === JSON.stringify(expectedCloudflareCompatibilityFlags), \`\${vertical.id} Cloudflare compatibility flags are incorrect\`);
4709
5408
  assert(JSON.stringify(contractEntry?.deploy?.cloudflare?.security) === JSON.stringify(expectedCloudflareSecurity), \`\${vertical.id} Cloudflare security contract is incorrect\`);
5409
+ assertCloudflareQualityGates(vertical.id, contractEntry?.deploy?.cloudflare?.qualityGates);
4710
5410
  assert(contractEntry?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, \`\${vertical.id} worker compatibilityDate is incorrect\`);
4711
5411
  assert(contractEntry?.deploy?.worker?.name === expectedWorkerName(vertical.id), \`\${vertical.id} worker name is incorrect\`);
4712
5412
  assert(modernConfig.includes("const cloudflareWorkerName = '" + expectedWorkerName(vertical.id) + "'"), \`\${vertical.id} modern.config.ts must define the Cloudflare worker name\`);
4713
5413
  assert(modernConfig.includes('name: cloudflareWorkerName'), \`\${vertical.id} modern.config.ts must wire deploy.worker.name\`);
5414
+ assert(modernConfig.includes('const assetPrefix ='), \`\${vertical.id} modern.config.ts must derive a dedicated asset prefix\`);
5415
+ assert(modernConfig.includes("assetPrefix: '/'"), \`\${vertical.id} modern.config.ts must keep dev assets origin-relative\`);
5416
+ assert(modernConfig.includes('assetPrefix,'), \`\${vertical.id} modern.config.ts must wire output.assetPrefix to the derived asset prefix\`);
5417
+ assert(contractEntry?.config?.dev?.assetPrefix === '/', \`\${vertical.id} dev asset prefix must stay origin-relative\`);
5418
+ assert(contractEntry?.config?.output?.assetPrefix?.default === '/', \`\${vertical.id} asset prefix must default to origin-relative paths\`);
5419
+ assert(JSON.stringify(contractEntry?.config?.output?.assetPrefix?.envFallbackOrder) === JSON.stringify([\`ULTRAMODERN_PUBLIC_URL_\${vertical.id.replace(/-/g, '_').toUpperCase()}\`, 'MODERN_PUBLIC_SITE_URL', 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN']), \`\${vertical.id} asset prefix env fallback order is incorrect\`);
4714
5420
  assert(contractEntry?.deploy?.cloudflare?.routes?.effectReadiness === \`\${vertical.apiPrefix}/effect/\${vertical.stem}/readiness\`, \`\${vertical.id} Cloudflare proof readiness route is incorrect\`);
4715
5421
  assert(contractEntry?.config?.rspack?.output?.uniqueName === vertical.mfName, \`\${vertical.id} Rspack uniqueName is incorrect\`);
4716
5422
  assert(contractEntry?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal(vertical.mfName), \`\${vertical.id} Rspack chunkLoadingGlobal is incorrect\`);
4717
5423
  assert(contractEntry?.moduleFederation?.name === vertical.mfName, \`\${vertical.id} MF name is incorrect\`);
4718
5424
  assert(JSON.stringify(contractEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), \`\${vertical.id} MF exposes are incorrect\`);
4719
- assert(contractEntry?.moduleFederation?.dts?.compilerInstance === '--package typescript -- tsc', \`\${vertical.id} must keep mandatory DTS compiler\`);
5425
+ assert(contractEntry?.moduleFederation?.dts?.compilerInstance === 'tsgo', \`\${vertical.id} must keep mandatory DTS compiler\`);
4720
5426
  assert(JSON.stringify(contractEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), \`\${vertical.id} MF verticalRefs are incorrect\`);
4721
5427
  assert(
4722
5428
  JSON.stringify((contractEntry?.moduleFederation?.remotes ?? []).map(remote => remote.id)) ===
@@ -4736,13 +5442,17 @@ for (const vertical of fullStackVerticals) {
4736
5442
  \`\${vertical.id} localisedUrls must come from route metadata\`,
4737
5443
  );
4738
5444
  assert(contractEntry?.routes?.source === 'route-owned', \`\${vertical.id} routes must be route-owned\`);
5445
+ assert(contractEntry?.routes?.metadataAuthoring === 'colocated-route-meta', \`\${vertical.id} route metadata authoring mode is incorrect\`);
5446
+ assert(contractEntry?.routes?.generatedManifest === true, \`\${vertical.id} route metadata manifest must be generated\`);
4739
5447
  assert(contractEntry?.routes?.metadataExport === './src/routes/ultramodern-route-metadata', \`\${vertical.id} route metadata export is incorrect\`);
4740
5448
  assert(contractEntry?.routes?.privateByDefault === true, \`\${vertical.id} routes must be private by default\`);
4741
5449
  assert(contractEntry?.routes?.publicnessDefault === 'private-app-screen', \`\${vertical.id} route publicness default is incorrect\`);
4742
5450
  assert(JSON.stringify(contractEntry?.routes?.publicRoutes ?? []) === '[]', \`\${vertical.id} must not expose generated public routes by default\`);
5451
+ assertPublicHeadContract(vertical.id, contractEntry?.routes?.publicHead, routeHead);
5452
+ assertPublicSurfaceContract(vertical.id, contractEntry?.routes?.publicSurface);
4743
5453
  assert(
4744
- (contractEntry?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen'),
4745
- \`\${vertical.id} owned routes must be non-indexable private app screens by default\`,
5454
+ (contractEntry?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen' && typeof route.descriptionKey === 'string'),
5455
+ \`\${vertical.id} owned routes must be non-indexable private app screens by default and include description keys\`,
4746
5456
  );
4747
5457
  assertPublicSurfaceAssets(vertical.path, contractEntry?.routes?.publicRoutes ?? []);
4748
5458
  assert(contractEntry?.styling?.federation?.owner?.id === vertical.id, \`\${vertical.id} CSS federation owner is missing\`);
@@ -4759,84 +5469,28 @@ for (const vertical of fullStackVerticals) {
4759
5469
  const topologyEntry = topology.verticals?.find(verticalEntry => verticalEntry.id === vertical.id);
4760
5470
  assert(topologyEntry?.kind === 'vertical', \`\${vertical.id} topology kind is incorrect\`);
4761
5471
  assert(topologyEntry?.package === vertical.packageName, \`\${vertical.id} topology package is incorrect\`);
4762
- assert(topologyEntry?.cloudflare?.workerName === expectedWorkerName(vertical.id), \`\${vertical.id} topology Cloudflare workerName is incorrect\`);
4763
- assert(topologyEntry?.moduleFederation?.name === vertical.mfName, \`\${vertical.id} topology MF name is incorrect\`);
4764
- assert(JSON.stringify(topologyEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), \`\${vertical.id} topology exposes are incorrect\`);
4765
- assert(JSON.stringify(topologyEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), \`\${vertical.id} topology verticalRefs are incorrect\`);
4766
- assert(topologyEntry?.api?.effect?.bff?.prefix === vertical.apiPrefix, \`\${vertical.id} topology API prefix is incorrect\`);
4767
- assert(topologyEntry?.api?.effect?.serverEntry === \`\${vertical.path}/api/effect/index.ts\`, \`\${vertical.id} topology server entry is incorrect\`);
4768
- assert(topologyEntry?.api?.effect?.readiness?.endpoint === \`/effect/\${vertical.stem}/readiness\`, \`\${vertical.id} topology readiness endpoint is incorrect\`);
4769
- assert(Object.keys(topologyEntry?.api?.effect?.domainOperations ?? {}).length >= 3, \`\${vertical.id} topology domain operations are missing\`);
4770
-
4771
- assert(ownership.owners?.some(owner => owner.id === vertical.id && owner.path === vertical.path), \`\${vertical.id} ownership entry is missing\`);
4772
- assert(overlay.ports?.[vertical.id], \`\${vertical.id} development port is missing\`);
4773
- assert(overlay.manifests?.[vertical.id]?.includes('/mf-manifest.json'), \`\${vertical.id} development manifest is missing\`);
4774
- assert(overlay.apis?.[vertical.id]?.endsWith(vertical.apiPrefix), \`\${vertical.id} development API URL is missing\`);
4775
- }
4776
-
4777
- console.log('UltraModern workspace scaffold validated');
4778
- `;
4779
- }
4780
- function createCloudflareVersionProofScript() {
4781
- return `#!/usr/bin/env node
4782
- import fs from 'node:fs';
4783
- import path from 'node:path';
4784
- import { fileURLToPath } from 'node:url';
4785
-
4786
- const workspaceRoot = path.resolve(
4787
- path.dirname(fileURLToPath(import.meta.url)),
4788
- '..',
4789
- );
4790
- const contractPath = path.join(
4791
- workspaceRoot,
4792
- '.modernjs/ultramodern-generated-contract.json',
4793
- );
4794
- const defaultOut = path.join(
4795
- workspaceRoot,
4796
- '.codex/reports/cloudflare-version-proof/public-url-proof.json',
4797
- );
4798
-
4799
- function readJson(filePath) {
4800
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
4801
- }
4802
-
4803
- function parseArgs(argv) {
4804
- const parsed = {
4805
- appId: undefined,
4806
- out: defaultOut,
4807
- requirePublicUrls: false,
4808
- };
4809
-
4810
- for (let index = 0; index < argv.length; index += 1) {
4811
- const arg = argv[index];
4812
- if (arg === '--app') {
4813
- parsed.appId = argv[index + 1];
4814
- index += 1;
4815
- } else if (arg === '--out') {
4816
- parsed.out = argv[index + 1];
4817
- index += 1;
4818
- } else if (arg === '--require-public-urls') {
4819
- parsed.requirePublicUrls = true;
4820
- } else if (arg === '--help' || arg === '-h') {
4821
- parsed.help = true;
4822
- } else {
4823
- throw new Error(\`Unknown argument: \${arg}\`);
4824
- }
4825
- }
5472
+ assert(topologyEntry?.cloudflare?.workerName === expectedWorkerName(vertical.id), \`\${vertical.id} topology Cloudflare workerName is incorrect\`);
5473
+ assert(topologyEntry?.moduleFederation?.name === vertical.mfName, \`\${vertical.id} topology MF name is incorrect\`);
5474
+ assert(JSON.stringify(topologyEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), \`\${vertical.id} topology exposes are incorrect\`);
5475
+ assert(JSON.stringify(topologyEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), \`\${vertical.id} topology verticalRefs are incorrect\`);
5476
+ assert(topologyEntry?.api?.effect?.bff?.prefix === vertical.apiPrefix, \`\${vertical.id} topology API prefix is incorrect\`);
5477
+ assert(topologyEntry?.api?.effect?.serverEntry === \`\${vertical.path}/api/effect/index.ts\`, \`\${vertical.id} topology server entry is incorrect\`);
5478
+ assert(topologyEntry?.api?.effect?.readiness?.endpoint === \`/effect/\${vertical.stem}/readiness\`, \`\${vertical.id} topology readiness endpoint is incorrect\`);
5479
+ assert(Object.keys(topologyEntry?.api?.effect?.domainOperations ?? {}).length >= 3, \`\${vertical.id} topology domain operations are missing\`);
4826
5480
 
4827
- return parsed;
5481
+ assert(ownership.owners?.some(owner => owner.id === vertical.id && owner.path === vertical.path), \`\${vertical.id} ownership entry is missing\`);
5482
+ assert(overlay.ports?.[vertical.id], \`\${vertical.id} development port is missing\`);
5483
+ assert(overlay.manifests?.[vertical.id]?.includes('/mf-manifest.json'), \`\${vertical.id} development manifest is missing\`);
5484
+ assert(overlay.apis?.[vertical.id]?.endsWith(vertical.apiPrefix), \`\${vertical.id} development API URL is missing\`);
4828
5485
  }
4829
5486
 
4830
- function printHelp() {
4831
- process.stdout.write(\`Usage:
4832
- node scripts/proof-cloudflare-version.mjs [--app workspace] [--out evidence.json] [--require-public-urls]
4833
-
4834
- Set each app's public URL using the contract env key, for example:
4835
- ULTRAMODERN_PUBLIC_URL_WORKSPACE=https://workspace.example.workers.dev
4836
- \`);
5487
+ console.log('UltraModern workspace scaffold validated');
5488
+ `;
4837
5489
  }
4838
-
4839
- function joinUrl(baseUrl, routePath) {
5490
+ function createCloudflareProofHelperScript() {
5491
+ const robotsPolicy = createPublicHeadRobotsPolicy();
5492
+ const qualityGates = createPublicWebsiteQualityGateContract();
5493
+ return `function joinUrl(baseUrl, routePath) {
4840
5494
  return new URL(routePath, baseUrl.endsWith('/') ? baseUrl : \`\${baseUrl}/\`);
4841
5495
  }
4842
5496
 
@@ -4850,6 +5504,8 @@ async function fetchText(url) {
4850
5504
  ok: response.ok,
4851
5505
  status: response.status,
4852
5506
  accessControlAllowOrigin: response.headers.get('access-control-allow-origin'),
5507
+ cacheControl: response.headers.get('cache-control'),
5508
+ contentLength: response.headers.get('content-length'),
4853
5509
  contentSecurityPolicy: response.headers.get('content-security-policy'),
4854
5510
  contentSecurityPolicyReportOnly: response.headers.get('content-security-policy-report-only'),
4855
5511
  contentType: response.headers.get('content-type'),
@@ -4908,6 +5564,52 @@ function assert(condition, message) {
4908
5564
  }
4909
5565
  }
4910
5566
 
5567
+ function responseByteLength(response) {
5568
+ return Buffer.byteLength(response.body, 'utf8');
5569
+ }
5570
+
5571
+ function assertByteBudget(evidence, app, response, options) {
5572
+ const bytes = responseByteLength(response);
5573
+ const passed = bytes <= options.maxBytes;
5574
+ evidence.assertions.push({
5575
+ type: 'byte-budget',
5576
+ label: options.label,
5577
+ route: options.route,
5578
+ actualBytes: bytes,
5579
+ maxBytes: options.maxBytes,
5580
+ status: passed ? 'pass' : 'fail',
5581
+ });
5582
+ assert(
5583
+ passed,
5584
+ app.id + ' ' + options.route + ' exceeds ' + options.label + ' byte budget: ' + bytes + ' > ' + options.maxBytes,
5585
+ );
5586
+ }
5587
+
5588
+ function assertContentType(evidence, app, response, options) {
5589
+ const actual = response.contentType ?? '';
5590
+ const passed = actual.toLowerCase().includes(options.includes);
5591
+ evidence.assertions.push({
5592
+ type: 'content-type',
5593
+ route: options.route,
5594
+ expectedIncludes: options.includes,
5595
+ actual,
5596
+ status: passed ? 'pass' : 'fail',
5597
+ });
5598
+ assert(passed, app.id + ' ' + options.route + ' content-type must include ' + options.includes);
5599
+ }
5600
+
5601
+ function assertCacheControl(evidence, app, response, options) {
5602
+ const actual = response.cacheControl ?? '';
5603
+ const passed = options.required === false || actual.trim() !== '';
5604
+ evidence.assertions.push({
5605
+ type: 'cache-control',
5606
+ route: options.route,
5607
+ actual,
5608
+ status: passed ? 'pass' : 'fail',
5609
+ });
5610
+ assert(passed, app.id + ' ' + options.route + ' is missing cache-control');
5611
+ }
5612
+
4911
5613
  function matchesPreviewHostname(hostname, pattern) {
4912
5614
  const normalizedHostname = hostname.toLowerCase();
4913
5615
  const normalizedPattern = String(pattern || '').toLowerCase();
@@ -5032,15 +5734,382 @@ function assertCloudflareSecurity(evidence, app, response, route, publicUrl, opt
5032
5734
  type: 'security-noindex',
5033
5735
  route,
5034
5736
  actual: response.xRobotsTag,
5035
- status: response.xRobotsTag === 'noindex, nofollow' ? 'pass' : 'fail',
5737
+ status: response.xRobotsTag === '${robotsPolicy.privateRouteRobots}' ? 'pass' : 'fail',
5036
5738
  });
5037
5739
  assert(
5038
- response.xRobotsTag === 'noindex, nofollow',
5740
+ response.xRobotsTag === '${robotsPolicy.privateRouteRobots}',
5039
5741
  \`\${app.id} \${route} is missing noindex X-Robots-Tag\`,
5040
5742
  );
5041
5743
  }
5042
5744
  }
5043
5745
 
5746
+ function collectStringValues(value, results = []) {
5747
+ if (typeof value === 'string') {
5748
+ results.push(value);
5749
+ return results;
5750
+ }
5751
+ if (Array.isArray(value)) {
5752
+ for (const item of value) {
5753
+ collectStringValues(item, results);
5754
+ }
5755
+ return results;
5756
+ }
5757
+ if (value && typeof value === 'object') {
5758
+ for (const item of Object.values(value)) {
5759
+ collectStringValues(item, results);
5760
+ }
5761
+ }
5762
+ return results;
5763
+ }
5764
+
5765
+ function assertNoPublicSourcemapRefs(evidence, app, manifestJson) {
5766
+ const sourcemapRefs = collectStringValues(manifestJson).filter(value =>
5767
+ /\\.map(?:$|[?#])/u.test(value),
5768
+ );
5769
+ evidence.assertions.push({
5770
+ type: 'sourcemap-policy',
5771
+ actual: sourcemapRefs,
5772
+ status: sourcemapRefs.length === 0 ? 'pass' : 'fail',
5773
+ });
5774
+ assert(
5775
+ sourcemapRefs.length === 0,
5776
+ app.id + ' MF manifest must not publicly reference sourcemaps',
5777
+ );
5778
+ }
5779
+
5780
+ function extractPreloadStyleUrls(linkHeader, publicUrl) {
5781
+ const urls = [];
5782
+ for (const match of String(linkHeader || '').matchAll(/<([^>]+)>\\s*;[^,]*rel=preload[^,]*as=style/giu)) {
5783
+ urls.push(String(joinUrl(publicUrl, match[1])));
5784
+ }
5785
+ return urls;
5786
+ }
5787
+
5788
+ function htmlHasRobotsDirective(html, expectedContent) {
5789
+ return htmlHasTagWithAttributes(html, 'meta', {
5790
+ name: 'robots',
5791
+ content: expectedContent,
5792
+ });
5793
+ }
5794
+
5795
+ function escapeRegExp(value) {
5796
+ return String(value).replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&');
5797
+ }
5798
+
5799
+ function htmlHasTagWithAttributes(html, tagName, attributes) {
5800
+ const tagPattern = new RegExp(\`<\${tagName}\\\\b[^>]*>\`, 'giu');
5801
+ const tags = html.match(tagPattern) || [];
5802
+ return tags.some(tag =>
5803
+ Object.entries(attributes).every(([name, value]) => {
5804
+ const attrPattern = new RegExp(
5805
+ \`\\\\b\${escapeRegExp(name)}=["']\${escapeRegExp(value)}["']\`,
5806
+ 'iu',
5807
+ );
5808
+ return attrPattern.test(tag);
5809
+ }),
5810
+ );
5811
+ }
5812
+
5813
+ function assertHeadTag(evidence, html, options) {
5814
+ const found = htmlHasTagWithAttributes(
5815
+ html,
5816
+ options.tag,
5817
+ options.attributes,
5818
+ );
5819
+ evidence.assertions.push({
5820
+ type: 'ssr-head',
5821
+ route: options.route,
5822
+ tag: options.tag,
5823
+ attributes: options.attributes,
5824
+ status: found ? 'pass' : 'fail',
5825
+ });
5826
+ assert(found, \`\${options.appId} \${options.route} SSR head is missing \${options.label}\`);
5827
+ }
5828
+
5829
+ async function validateSsrHead(evidence, app, publicUrl, ssrRoute, ssr) {
5830
+ const titleFound = /<title\\b[^>]*>[^<]+<\\/title>/iu.test(ssr.body);
5831
+ evidence.assertions.push({
5832
+ type: 'ssr-head',
5833
+ route: ssrRoute,
5834
+ tag: 'title',
5835
+ status: titleFound ? 'pass' : 'fail',
5836
+ });
5837
+ assert(titleFound, \`\${app.id} \${ssrRoute} SSR head is missing title\`);
5838
+ assertHeadTag(evidence, ssr.body, {
5839
+ appId: app.id,
5840
+ route: ssrRoute,
5841
+ tag: 'meta',
5842
+ attributes: { name: 'description' },
5843
+ label: 'description meta',
5844
+ });
5845
+ assertHeadTag(evidence, ssr.body, {
5846
+ appId: app.id,
5847
+ route: ssrRoute,
5848
+ tag: 'meta',
5849
+ attributes: { name: 'robots' },
5850
+ label: 'robots meta',
5851
+ });
5852
+
5853
+ const publicSurface = app.routes?.publicSurface ?? {};
5854
+ const routeEntry = (publicSurface.routeEntries ?? [])[0];
5855
+ if (!routeEntry) {
5856
+ const canonicalFound = htmlHasTagWithAttributes(ssr.body, 'link', {
5857
+ rel: 'canonical',
5858
+ });
5859
+ evidence.assertions.push({
5860
+ type: 'ssr-head-private-canonical',
5861
+ route: ssrRoute,
5862
+ status: canonicalFound ? 'fail' : 'pass',
5863
+ });
5864
+ assert(!canonicalFound, \`\${app.id} \${ssrRoute} private SSR head must not emit canonical links\`);
5865
+ return;
5866
+ }
5867
+
5868
+ const publicRoute = routeEntry.localeUrlPaths?.en ?? publicSurface.concreteUrlPaths?.[0];
5869
+ const headRoute = publicRoute || ssrRoute;
5870
+ const headResponse =
5871
+ headRoute === ssrRoute ? ssr : await fetchText(joinUrl(publicUrl, headRoute));
5872
+ if (headRoute !== ssrRoute) {
5873
+ evidence.assertions.push({
5874
+ type: 'ssr-head-route',
5875
+ route: headRoute,
5876
+ status: headResponse.ok ? 'pass' : 'fail',
5877
+ statusCode: headResponse.status,
5878
+ });
5879
+ assert(headResponse.ok, \`\${app.id} public head route returned HTTP \${headResponse.status}\`);
5880
+ assertCloudflareSecurity(evidence, app, headResponse, headRoute, publicUrl, {
5881
+ html: true,
5882
+ });
5883
+ }
5884
+ const isPreview = shouldNoindexUrl(publicUrl, app.deploy?.cloudflare?.security?.noindex);
5885
+ const robotsIndexable = htmlHasRobotsDirective(headResponse.body, '${robotsPolicy.indexableRobots}');
5886
+ evidence.assertions.push({
5887
+ type: 'indexing-policy',
5888
+ route: headRoute,
5889
+ mode: isPreview ? 'preview' : 'production',
5890
+ xRobotsTag: headResponse.xRobotsTag,
5891
+ htmlRobotsIndexable: robotsIndexable,
5892
+ status:
5893
+ isPreview || (headResponse.xRobotsTag !== '${robotsPolicy.privateRouteRobots}' && robotsIndexable)
5894
+ ? 'pass'
5895
+ : 'fail',
5896
+ });
5897
+ if (!isPreview) {
5898
+ assert(
5899
+ headResponse.xRobotsTag !== '${robotsPolicy.privateRouteRobots}' && robotsIndexable,
5900
+ \`\${app.id} \${headRoute} production public route must be indexable\`,
5901
+ );
5902
+ }
5903
+
5904
+ const canonicalUrl = String(joinUrl(publicUrl, headRoute));
5905
+ assertHeadTag(evidence, headResponse.body, {
5906
+ appId: app.id,
5907
+ route: headRoute,
5908
+ tag: 'link',
5909
+ attributes: { rel: 'canonical', href: canonicalUrl },
5910
+ label: 'canonical link',
5911
+ });
5912
+ for (const language of app.routes?.publicHead?.alternates?.hreflang ?? []) {
5913
+ const href = String(joinUrl(publicUrl, routeEntry.localeUrlPaths?.[language] ?? headRoute));
5914
+ assertHeadTag(evidence, headResponse.body, {
5915
+ appId: app.id,
5916
+ route: headRoute,
5917
+ tag: 'link',
5918
+ attributes: { rel: 'alternate', hreflang: language, href },
5919
+ label: \`hreflang \${language}\`,
5920
+ });
5921
+ }
5922
+ assertHeadTag(evidence, headResponse.body, {
5923
+ appId: app.id,
5924
+ route: headRoute,
5925
+ tag: 'link',
5926
+ attributes: { rel: 'alternate', hreflang: 'x-default' },
5927
+ label: 'x-default hreflang',
5928
+ });
5929
+ for (const property of ['og:title', 'og:description', 'og:url', 'og:type']) {
5930
+ assertHeadTag(evidence, headResponse.body, {
5931
+ appId: app.id,
5932
+ route: headRoute,
5933
+ tag: 'meta',
5934
+ attributes: { property },
5935
+ label: property,
5936
+ });
5937
+ }
5938
+ for (const name of ['twitter:card', 'twitter:title', 'twitter:description']) {
5939
+ assertHeadTag(evidence, headResponse.body, {
5940
+ appId: app.id,
5941
+ route: headRoute,
5942
+ tag: 'meta',
5943
+ attributes: { name },
5944
+ label: name,
5945
+ });
5946
+ }
5947
+ assertHeadTag(evidence, headResponse.body, {
5948
+ appId: app.id,
5949
+ route: headRoute,
5950
+ tag: 'script',
5951
+ attributes: { type: 'application/ld+json' },
5952
+ label: 'JSON-LD structured data',
5953
+ });
5954
+ }
5955
+
5956
+ async function validateNotFound(evidence, app, publicUrl) {
5957
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
5958
+ const notFoundRoute =
5959
+ qualityGates.statusCodes?.notFoundRoute ?? '${qualityGates.statusCodes.notFoundRoute}';
5960
+ const expectedStatus = qualityGates.statusCodes?.unknownRouteStatus ?? ${qualityGates.statusCodes.unknownRouteStatus};
5961
+ const response = await fetchText(joinUrl(publicUrl, notFoundRoute));
5962
+ evidence.assertions.push({
5963
+ type: 'status-code',
5964
+ route: notFoundRoute,
5965
+ expectedStatus,
5966
+ actualStatus: response.status,
5967
+ status: response.status === expectedStatus ? 'pass' : 'fail',
5968
+ });
5969
+ assert(
5970
+ response.status === expectedStatus,
5971
+ \`\${app.id} unknown route must return HTTP \${expectedStatus}, got \${response.status}\`,
5972
+ );
5973
+ assertCloudflareSecurity(evidence, app, response, notFoundRoute, publicUrl, {
5974
+ html: response.contentType?.includes('text/html'),
5975
+ });
5976
+ }
5977
+
5978
+ async function validateCssAsset(evidence, app, publicUrl, ssr) {
5979
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
5980
+ const budgets = qualityGates.budgets ?? {};
5981
+ const styleUrls = extractPreloadStyleUrls(ssr.link, publicUrl);
5982
+ evidence.assertions.push({
5983
+ type: 'css-preload-assets',
5984
+ actual: styleUrls,
5985
+ status: styleUrls.length > 0 ? 'pass' : 'fail',
5986
+ });
5987
+ assert(styleUrls.length > 0, \`\${app.id} SSR response did not expose preloadable CSS assets\`);
5988
+
5989
+ const styleUrl = styleUrls[0];
5990
+ const route = new URL(styleUrl).pathname;
5991
+ const css = await fetchText(styleUrl);
5992
+ evidence.assertions.push({
5993
+ type: 'css-asset',
5994
+ route,
5995
+ status: css.ok && css.body.trim() !== '' ? 'pass' : 'fail',
5996
+ statusCode: css.status,
5997
+ });
5998
+ assert(css.ok, \`\${app.id} CSS asset returned HTTP \${css.status}\`);
5999
+ assert(css.body.trim() !== '', \`\${app.id} CSS asset is empty\`);
6000
+ assertContentType(evidence, app, css, {
6001
+ route,
6002
+ includes: 'text/css',
6003
+ });
6004
+ assertCacheControl(evidence, app, css, {
6005
+ route,
6006
+ required: qualityGates.assets?.cacheControlRequiredForCss,
6007
+ });
6008
+ assertByteBudget(evidence, app, css, {
6009
+ label: 'cssAssetMaxBytes',
6010
+ maxBytes: budgets.cssAssetMaxBytes ?? ${createPublicWebsiteBudgetFallback('cssAssetMaxBytes')},
6011
+ route,
6012
+ });
6013
+ }
6014
+
6015
+ async function validatePublicSurface(evidence, app, publicUrl) {
6016
+ const publicSurface = app.routes?.publicSurface ?? {};
6017
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
6018
+ const budgets = qualityGates.budgets ?? {};
6019
+ const hasPublicRoutes =
6020
+ (publicSurface.publicRoutes ?? []).length > 0 ||
6021
+ (publicSurface.routeEntries ?? []).length > 0 ||
6022
+ (publicSurface.contentSources ?? []).length > 0;
6023
+
6024
+ const robotsRoute = '/robots.txt';
6025
+ const robots = await fetchText(joinUrl(publicUrl, robotsRoute));
6026
+ evidence.assertions.push({
6027
+ type: 'public-surface-robots',
6028
+ route: robotsRoute,
6029
+ status: robots.ok ? 'pass' : 'fail',
6030
+ statusCode: robots.status,
6031
+ });
6032
+ assert(robots.ok, \`\${app.id} robots.txt returned HTTP \${robots.status}\`);
6033
+ assertContentType(evidence, app, robots, {
6034
+ route: robotsRoute,
6035
+ includes: 'text/plain',
6036
+ });
6037
+ assertCloudflareSecurity(evidence, app, robots, robotsRoute, publicUrl);
6038
+
6039
+ if (!hasPublicRoutes) {
6040
+ const disallowsAll = robots.body.includes('Disallow: /');
6041
+ const referencesSitemap = /\\bSitemap:/iu.test(robots.body);
6042
+ evidence.assertions.push({
6043
+ type: 'public-surface-private-robots',
6044
+ route: robotsRoute,
6045
+ disallowsAll,
6046
+ referencesSitemap,
6047
+ status: disallowsAll && !referencesSitemap ? 'pass' : 'fail',
6048
+ });
6049
+ assert(disallowsAll, \`\${app.id} private public surface robots.txt must disallow crawling\`);
6050
+ assert(!referencesSitemap, \`\${app.id} private public surface robots.txt must not reference sitemap.xml\`);
6051
+ return;
6052
+ }
6053
+
6054
+ const sitemapRoute = '/sitemap.xml';
6055
+ const sitemap = await fetchText(joinUrl(publicUrl, sitemapRoute));
6056
+ evidence.assertions.push({
6057
+ type: 'public-surface-sitemap',
6058
+ route: sitemapRoute,
6059
+ status: sitemap.ok ? 'pass' : 'fail',
6060
+ statusCode: sitemap.status,
6061
+ });
6062
+ assert(sitemap.ok, \`\${app.id} sitemap.xml returned HTTP \${sitemap.status}\`);
6063
+ assertContentType(evidence, app, sitemap, {
6064
+ route: sitemapRoute,
6065
+ includes: 'xml',
6066
+ });
6067
+ assertByteBudget(evidence, app, sitemap, {
6068
+ label: 'sitemapXmlMaxBytes',
6069
+ maxBytes: budgets.sitemapXmlMaxBytes ?? ${createPublicWebsiteBudgetFallback('sitemapXmlMaxBytes')},
6070
+ route: sitemapRoute,
6071
+ });
6072
+
6073
+ const sitemapUrl = String(joinUrl(publicUrl, sitemapRoute));
6074
+ const robotsReferencesSitemap = robots.body.includes(\`Sitemap: \${sitemapUrl}\`);
6075
+ evidence.assertions.push({
6076
+ type: 'robots-sitemap-consistency',
6077
+ route: robotsRoute,
6078
+ sitemapUrl,
6079
+ status: robotsReferencesSitemap ? 'pass' : 'fail',
6080
+ });
6081
+ assert(
6082
+ robotsReferencesSitemap,
6083
+ \`\${app.id} robots.txt must reference generated sitemap.xml\`,
6084
+ );
6085
+
6086
+ for (const urlPath of publicSurface.concreteUrlPaths ?? []) {
6087
+ const loc = \`<loc>\${String(joinUrl(publicUrl, urlPath))}</loc>\`;
6088
+ evidence.assertions.push({
6089
+ type: 'sitemap-route',
6090
+ route: urlPath,
6091
+ status: sitemap.body.includes(loc) ? 'pass' : 'fail',
6092
+ });
6093
+ assert(sitemap.body.includes(loc), \`\${app.id} sitemap.xml is missing \${urlPath}\`);
6094
+ }
6095
+
6096
+ const manifestRoute = '/site.webmanifest';
6097
+ const webManifest = await fetchText(joinUrl(publicUrl, manifestRoute));
6098
+ const webManifestJson = parseMaybeJson(webManifest.body);
6099
+ evidence.assertions.push({
6100
+ type: 'public-surface-webmanifest',
6101
+ route: manifestRoute,
6102
+ status: webManifest.ok && webManifestJson ? 'pass' : 'fail',
6103
+ statusCode: webManifest.status,
6104
+ });
6105
+ assert(webManifest.ok, \`\${app.id} site.webmanifest returned HTTP \${webManifest.status}\`);
6106
+ assert(webManifestJson, \`\${app.id} site.webmanifest must be valid JSON\`);
6107
+ assertContentType(evidence, app, webManifest, {
6108
+ route: manifestRoute,
6109
+ includes: 'manifest',
6110
+ });
6111
+ }
6112
+
5044
6113
  async function validateApp(app, publicUrl) {
5045
6114
  const cloudflare = app.deploy?.cloudflare;
5046
6115
  const routes = cloudflare?.routes ?? {};
@@ -5054,6 +6123,8 @@ async function validateApp(app, publicUrl) {
5054
6123
 
5055
6124
  const ssrRoute = routes.ssr ?? '/en';
5056
6125
  const ssr = await fetchText(joinUrl(publicUrl, ssrRoute));
6126
+ const qualityGates = cloudflare?.qualityGates ?? {};
6127
+ const budgets = qualityGates.budgets ?? {};
5057
6128
  evidence.assertions.push({
5058
6129
  type: 'ssr',
5059
6130
  route: ssrRoute,
@@ -5064,6 +6135,18 @@ async function validateApp(app, publicUrl) {
5064
6135
  assertCloudflareSecurity(evidence, app, ssr, ssrRoute, publicUrl, {
5065
6136
  html: true,
5066
6137
  });
6138
+ assertContentType(evidence, app, ssr, {
6139
+ route: ssrRoute,
6140
+ includes: 'text/html',
6141
+ });
6142
+ assertByteBudget(evidence, app, ssr, {
6143
+ label: 'ssrHtmlMaxBytes',
6144
+ maxBytes: budgets.ssrHtmlMaxBytes ?? ${createPublicWebsiteBudgetFallback('ssrHtmlMaxBytes')},
6145
+ route: ssrRoute,
6146
+ });
6147
+ await validateSsrHead(evidence, app, publicUrl, ssrRoute, ssr);
6148
+ await validateNotFound(evidence, app, publicUrl);
6149
+ await validatePublicSurface(evidence, app, publicUrl);
5067
6150
 
5068
6151
  const uiMarker = extractUiMarker(ssr.body);
5069
6152
  evidence.assertions.push({
@@ -5103,6 +6186,7 @@ async function validateApp(app, publicUrl) {
5103
6186
  cssPreloadLinkHeader.includes('as=style'),
5104
6187
  \`\${app.id} SSR response is missing CSS preload Link headers\`,
5105
6188
  );
6189
+ await validateCssAsset(evidence, app, publicUrl, ssr);
5106
6190
 
5107
6191
  const manifestRoute = routes.mfManifest ?? '/mf-manifest.json';
5108
6192
  const manifest = await fetchText(joinUrl(publicUrl, manifestRoute));
@@ -5118,6 +6202,16 @@ async function validateApp(app, publicUrl) {
5118
6202
  \`\${app.id} MF manifest returned HTTP \${manifest.status}\`,
5119
6203
  );
5120
6204
  assertCloudflareSecurity(evidence, app, manifest, manifestRoute, publicUrl);
6205
+ assertContentType(evidence, app, manifest, {
6206
+ route: manifestRoute,
6207
+ includes: 'json',
6208
+ });
6209
+ assertByteBudget(evidence, app, manifest, {
6210
+ label: 'mfManifestMaxBytes',
6211
+ maxBytes: budgets.mfManifestMaxBytes ?? ${createPublicWebsiteBudgetFallback('mfManifestMaxBytes')},
6212
+ route: manifestRoute,
6213
+ });
6214
+ assertNoPublicSourcemapRefs(evidence, app, manifestJson);
5121
6215
  evidence.assertions.push({
5122
6216
  type: 'mf-manifest-cors',
5123
6217
  route: manifestRoute,
@@ -5158,6 +6252,15 @@ async function validateApp(app, publicUrl) {
5158
6252
  });
5159
6253
  assert(locale.ok, \`\${app.id} locale JSON returned HTTP \${locale.status}\`);
5160
6254
  assertCloudflareSecurity(evidence, app, locale, localeRoute, publicUrl);
6255
+ assertContentType(evidence, app, locale, {
6256
+ route: localeRoute,
6257
+ includes: 'json',
6258
+ });
6259
+ assertByteBudget(evidence, app, locale, {
6260
+ label: 'localeJsonMaxBytes',
6261
+ maxBytes: budgets.localeJsonMaxBytes ?? ${createPublicWebsiteBudgetFallback('localeJsonMaxBytes')},
6262
+ route: localeRoute,
6263
+ });
5161
6264
  evidence.assertions.push({
5162
6265
  type: 'i18n-cors',
5163
6266
  route: localeRoute,
@@ -5195,6 +6298,75 @@ async function validateApp(app, publicUrl) {
5195
6298
  return evidence;
5196
6299
  }
5197
6300
 
6301
+ export { validateApp };
6302
+ `;
6303
+ }
6304
+ function createCloudflareVersionProofScript() {
6305
+ return `#!/usr/bin/env node
6306
+ import fs from 'node:fs';
6307
+ import path from 'node:path';
6308
+ import { fileURLToPath } from 'node:url';
6309
+ import { validateApp } from './ultramodern-cloudflare-proof.mjs';
6310
+
6311
+ const workspaceRoot = path.resolve(
6312
+ path.dirname(fileURLToPath(import.meta.url)),
6313
+ '..',
6314
+ );
6315
+ const contractPath = path.join(
6316
+ workspaceRoot,
6317
+ '.modernjs/ultramodern-generated-contract.json',
6318
+ );
6319
+ const defaultOut = path.join(
6320
+ workspaceRoot,
6321
+ '.codex/reports/cloudflare-version-proof/public-url-proof.json',
6322
+ );
6323
+
6324
+ function readJson(filePath) {
6325
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
6326
+ }
6327
+
6328
+ function parseArgs(argv) {
6329
+ const parsed = {
6330
+ appId: undefined,
6331
+ out: defaultOut,
6332
+ requirePublicUrls: false,
6333
+ };
6334
+
6335
+ for (let index = 0; index < argv.length; index += 1) {
6336
+ const arg = argv[index];
6337
+ if (arg === '--app') {
6338
+ parsed.appId = argv[index + 1];
6339
+ index += 1;
6340
+ } else if (arg === '--out') {
6341
+ parsed.out = argv[index + 1];
6342
+ index += 1;
6343
+ } else if (arg === '--require-public-urls') {
6344
+ parsed.requirePublicUrls = true;
6345
+ } else if (arg === '--help' || arg === '-h') {
6346
+ parsed.help = true;
6347
+ } else {
6348
+ throw new Error(\`Unknown argument: \${arg}\`);
6349
+ }
6350
+ }
6351
+
6352
+ return parsed;
6353
+ }
6354
+
6355
+ function printHelp() {
6356
+ process.stdout.write(\`Usage:
6357
+ node scripts/proof-cloudflare-version.mjs [--app workspace] [--out evidence.json] [--require-public-urls]
6358
+
6359
+ Set each app's public URL using the contract env key, for example:
6360
+ ULTRAMODERN_PUBLIC_URL_WORKSPACE=https://workspace.example.workers.dev
6361
+ \`);
6362
+ }
6363
+
6364
+ function assert(condition, message) {
6365
+ if (!condition) {
6366
+ throw new Error(message);
6367
+ }
6368
+ }
6369
+
5198
6370
  async function main(argv = process.argv.slice(2)) {
5199
6371
  const args = parseArgs(argv);
5200
6372
  if (args.help) {
@@ -5261,10 +6433,13 @@ function writeGeneratedWorkspaceScripts(targetDir, scope, enableTailwind, remote
5261
6433
  writeFileReplacing(targetDir, "scripts/assert-mf-types.mjs", createAssertMfTypesScript(remotes));
5262
6434
  writeFileReplacing(targetDir, "scripts/validate-ultramodern-workspace.mjs", createWorkspaceValidationScript(scope, enableTailwind, remotes));
5263
6435
  writeFileReplacing(targetDir, "scripts/check-ultramodern-i18n-boundaries.mjs", createWorkspaceI18nBoundaryValidationScript());
6436
+ writeFileReplacing(targetDir, "scripts/generate-public-surface-assets.mjs", createPublicSurfaceAssetsScript());
6437
+ writeFileReplacing(targetDir, "scripts/ultramodern-cloudflare-proof.mjs", createCloudflareProofHelperScript());
5264
6438
  writeFileReplacing(targetDir, "scripts/proof-cloudflare-version.mjs", createCloudflareVersionProofScript());
5265
6439
  }
5266
6440
  function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes = []) {
5267
6441
  const resolvedApp = 'shell' === app.kind ? createShellHost(remotes) : app;
6442
+ const publicWeb = createPublicWebAppArtifacts(resolvedApp);
5268
6443
  const writeAppFile = (relativePath, content)=>{
5269
6444
  writeFile(targetDir, `${resolvedApp.directory}/${relativePath}`, content);
5270
6445
  };
@@ -5272,7 +6447,8 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes
5272
6447
  writeJson(targetDir, `${resolvedApp.directory}/tsconfig.json`, createPackageTsConfig(resolvedApp.directory, appHasEffectApi(resolvedApp)));
5273
6448
  writeFile(targetDir, `${resolvedApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(resolvedApp, remotes));
5274
6449
  writeFile(targetDir, `${resolvedApp.directory}/src/ultramodern-build.ts`, createUltramodernBuildModule(scope, resolvedApp));
5275
- writeFile(targetDir, `${resolvedApp.directory}/src/routes/ultramodern-route-metadata.ts`, createRouteMetadataModule(resolvedApp));
6450
+ writeFile(targetDir, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
6451
+ writeFile(targetDir, publicWeb.routeHeadFile.path, publicWeb.routeHeadFile.content);
5276
6452
  writeFile(targetDir, `${resolvedApp.directory}/modern.config.ts`, createAppModernConfig(scope, resolvedApp));
5277
6453
  writeFile(targetDir, `${resolvedApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(resolvedApp, scope, remotes));
5278
6454
  writeJson(targetDir, `${resolvedApp.directory}/locales/en/translation.json`, createAppPublicLocaleMessages(resolvedApp, 'en', remotes));
@@ -5288,7 +6464,8 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes
5288
6464
  writeAppFile('src/routes/layout.tsx', createLayout(resolvedApp.id));
5289
6465
  for (const [relativePath, content] of Object.entries(workspaceAssetsForApp(resolvedApp)))writeFile(targetDir, `${resolvedApp.directory}/${relativePath}`, content);
5290
6466
  writeAppFile('src/routes/[lang]/page.tsx', 'shell' === resolvedApp.kind ? createShellPage(remotes) : createRemotePage(resolvedApp));
5291
- for (const route of createRouteOwnedI18nPaths(resolvedApp))if ('/' !== route.canonicalPath && 'shell' !== resolvedApp.kind) writeFile(targetDir, createRoutePageFilePath(resolvedApp, route.canonicalPath), createRouteAliasPage(route.canonicalPath));
6467
+ for (const generatedFile of publicWeb.routeMetaFiles)writeFile(targetDir, generatedFile.path, generatedFile.content);
6468
+ for (const generatedFile of publicWeb.routeAliasFiles)writeFile(targetDir, generatedFile.path, generatedFile.content);
5292
6469
  if ('shell' === resolvedApp.kind) {
5293
6470
  writeAppFile('src/routes/vertical-components.tsx', createShellRemoteComponents(scope, remotes));
5294
6471
  writeAppFile('src/routes/shell-frame.tsx', createShellFrameComponent());
@@ -5319,12 +6496,7 @@ function writeSharedPackages(targetDir, scope, packageSource) {
5319
6496
  ]
5320
6497
  });
5321
6498
  }
5322
- writeFile(targetDir, 'packages/shared-contracts/src/index.ts', `export const ultramodernWorkspaceContract = {
5323
- ownership: 'topology/ownership.json',
5324
- preset: 'presetUltramodern',
5325
- topology: 'topology/reference-topology.json',
5326
- } as const;
5327
- `);
6499
+ writeFile(targetDir, 'packages/shared-contracts/src/index.ts', createSharedContractsIndex());
5328
6500
  writeFile(targetDir, 'packages/shared-design-tokens/src/index.ts', `export const sharedDesignTokens = {
5329
6501
  color: {
5330
6502
  accent: '#2f8f68',
@@ -5400,9 +6572,12 @@ function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes
5400
6572
  }
5401
6573
  function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes) {
5402
6574
  const shellHost = createShellHost(remotes);
6575
+ const publicWeb = createPublicWebAppArtifacts(shellHost);
5403
6576
  writeJsonFile(external_node_path_default().join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes));
5404
6577
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
5405
- writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/ultramodern-route-metadata.ts`, createRouteMetadataModule(shellHost));
6578
+ writeFileReplacing(workspaceRoot, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
6579
+ writeFileReplacing(workspaceRoot, publicWeb.routeHeadFile.path, publicWeb.routeHeadFile.content);
6580
+ for (const generatedFile of publicWeb.routeMetaFiles)writeFileReplacing(workspaceRoot, generatedFile.path, generatedFile.content);
5406
6581
  rewriteWorkspaceAssetsForApp(workspaceRoot, shellHost);
5407
6582
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(shellHost, scope, remotes));
5408
6583
  writeJsonFile(external_node_path_default().join(workspaceRoot, `${shellApp.directory}/locales/en/translation.json`), createAppPublicLocaleMessages(shellHost, 'en', remotes));