@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.
@@ -12,23 +12,23 @@ const TANSTACK_ROUTER_VERSION = '1.170.15';
12
12
  const MODULE_FEDERATION_VERSION = '2.5.1';
13
13
  const ZEPHYR_RSPACK_PLUGIN_VERSION = '1.1.1';
14
14
  const ZEPHYR_AGENT_VERSION = '1.1.1';
15
- const WRANGLER_VERSION = '4.98.0';
15
+ const WRANGLER_VERSION = '4.99.0';
16
16
  const CLOUDFLARE_COMPATIBILITY_DATE = '2026-06-02';
17
17
  const TAILWIND_VERSION = '4.3.0';
18
18
  const TAILWIND_POSTCSS_VERSION = '4.3.0';
19
19
  const POSTCSS_VERSION = '8.5.15';
20
- const EFFECT_TSGO_VERSION = '0.14.0';
20
+ const EFFECT_TSGO_VERSION = '0.14.3';
21
21
  const TYPESCRIPT_VERSION = '6.0.3';
22
- const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260606.1';
23
- const OXLINT_VERSION = '1.68.0';
24
- const OXFMT_VERSION = '0.53.0';
25
- const ULTRACITE_VERSION = '7.8.1';
22
+ const TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260610.1';
23
+ const OXLINT_VERSION = '1.69.0';
24
+ const OXFMT_VERSION = '0.54.0';
25
+ const ULTRACITE_VERSION = '7.8.3';
26
26
  const LEFTHOOK_VERSION = '^2.1.9';
27
27
  const I18NEXT_VERSION = '26.3.1';
28
28
  const REACT_VERSION = '^19.2.7';
29
29
  const REACT_DOM_VERSION = '^19.2.7';
30
30
  const REACT_ROUTER_DOM_VERSION = '7.17.0';
31
- const PNPM_VERSION = '11.5.2';
31
+ const PNPM_VERSION = '11.5.3';
32
32
  const GENERATED_CONTRACT_PATH = '.modernjs/ultramodern-generated-contract.json';
33
33
  const RSTACK_AGENT_SKILLS_COMMIT = '61c948b42512e223bad44b83af4080eba48b2677';
34
34
  const MODULE_FEDERATION_AGENT_SKILLS_COMMIT = '07bb5b6c43ad457609e00c081b72d4c42508ec76';
@@ -625,9 +625,67 @@ function createCloudflareSecurityContract() {
625
625
  }
626
626
  };
627
627
  }
628
+ const PUBLIC_WEBSITE_POLICY = {
629
+ qualityGates: {
630
+ publicRoutes: {
631
+ requireSitemapWhenPresent: true,
632
+ requireRobotsSitemapConsistency: true,
633
+ requireWebManifestWhenPresent: true
634
+ },
635
+ statusCodes: {
636
+ notFoundRoute: '/__ultramodern-smoke-missing',
637
+ unknownRouteStatus: 404
638
+ },
639
+ indexing: {
640
+ previewNoindex: true,
641
+ productionPublicRoutesIndexable: true
642
+ },
643
+ assets: {
644
+ cssPreloadRequired: true,
645
+ cssResponseRequired: true,
646
+ cacheControlRequiredForCss: true,
647
+ sourcemapsPubliclyReferenced: false
648
+ },
649
+ budgets: {
650
+ ssrHtmlMaxBytes: 250000,
651
+ mfManifestMaxBytes: 500000,
652
+ localeJsonMaxBytes: 100000,
653
+ sitemapXmlMaxBytes: 500000,
654
+ cssAssetMaxBytes: 750000
655
+ },
656
+ csp: {
657
+ finalMode: 'report-only-dogfood',
658
+ decision: "Report-only remains the generated final mode until public smoke proof records MF SSR script/style/connect compatibility for the deployed surface."
659
+ }
660
+ },
661
+ publicHead: {
662
+ indexableRobots: 'index, follow',
663
+ privateRouteRobots: 'noindex, nofollow'
664
+ },
665
+ publicSurface: {
666
+ defaultProviderFile: 'route.sitemap.mjs',
667
+ draftPolicy: 'omit-draft-by-default',
668
+ indexablePolicy: 'omit-indexable-false'
669
+ }
670
+ };
628
671
  function formatTsJsonValue(value, indent) {
629
672
  return JSON.stringify(value, null, 2).replaceAll('\n', `\n${' '.repeat(indent)}`);
630
673
  }
674
+ function formatIntegerCodeLiteral(value) {
675
+ return String(value).replace(/\B(?=(\d{3})+(?!\d))/gu, '_');
676
+ }
677
+ function createPublicWebsiteQualityGateContract() {
678
+ return PUBLIC_WEBSITE_POLICY.qualityGates;
679
+ }
680
+ function createPublicWebsiteBudgetFallback(budgetName) {
681
+ return formatIntegerCodeLiteral(PUBLIC_WEBSITE_POLICY.qualityGates.budgets[budgetName]);
682
+ }
683
+ function createPublicHeadRobotsPolicy() {
684
+ return PUBLIC_WEBSITE_POLICY.publicHead;
685
+ }
686
+ function createPublicSurfaceContentExpansionPolicy() {
687
+ return PUBLIC_WEBSITE_POLICY.publicSurface;
688
+ }
631
689
  function createCloudflareDeployContract(scope, app) {
632
690
  return {
633
691
  target: 'cloudflare',
@@ -641,6 +699,7 @@ function createCloudflareDeployContract(scope, app) {
641
699
  assetsBinding: 'ASSETS',
642
700
  routes: createCloudflareProofRoute(app),
643
701
  security: createCloudflareSecurityContract(),
702
+ qualityGates: createPublicWebsiteQualityGateContract(),
644
703
  evidence: {
645
704
  proofScript: "scripts/proof-cloudflare-version.mjs",
646
705
  reportDefault: '.codex/reports/cloudflare-version-proof/public-url-proof.json'
@@ -706,6 +765,9 @@ function createPackageTsConfig(packageDir, includeApi = false) {
706
765
  };
707
766
  }
708
767
  function createAppPackage(scope, app, packageSource, enableTailwind, remotes = []) {
768
+ const publicSurfaceBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
769
+ const publicSurfaceCloudflareBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
770
+ const publicSurfaceCloudflareOutputCommand = createPublicSurfaceGenerationCommand(app, 'cloudflare');
709
771
  const packageExports = Object.fromEntries(Object.entries(app.exposes ?? {}).map(([expose, source])=>[
710
772
  expose,
711
773
  source
@@ -716,8 +778,8 @@ function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [
716
778
  version: '0.1.0',
717
779
  scripts: {
718
780
  dev: 'modern dev',
719
- build: app.exposes ? `ULTRAMODERN_ZEPHYR=false modern build && node ${relativeRootFor(app.directory)}/scripts/assert-mf-types.mjs` : 'ULTRAMODERN_ZEPHYR=false modern build',
720
- 'cloudflare:build': 'ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy',
781
+ build: app.exposes ? `ULTRAMODERN_ZEPHYR=false modern build && ${publicSurfaceBuildCommand} && node ${relativeRootFor(app.directory)}/scripts/assert-mf-types.mjs` : `ULTRAMODERN_ZEPHYR=false modern build && ${publicSurfaceBuildCommand}`,
782
+ 'cloudflare:build': `ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build && ${publicSurfaceCloudflareBuildCommand} && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy && ${publicSurfaceCloudflareOutputCommand}`,
721
783
  'cloudflare:deploy': 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json',
722
784
  'cloudflare:preview': 'pnpm run cloudflare:build && wrangler dev --config .output/wrangler.json',
723
785
  'cloudflare:proof': `node ${relativeRootFor(app.directory)}/scripts/proof-cloudflare-version.mjs --app ${app.id}`,
@@ -774,6 +836,40 @@ function createSharedPackage(scope, id, description, packageSource) {
774
836
  };
775
837
  return packageJson;
776
838
  }
839
+ function createSharedContractsIndex() {
840
+ return `export type UltramodernPublicSitemapChangeFrequency =
841
+ | 'always'
842
+ | 'hourly'
843
+ | 'daily'
844
+ | 'weekly'
845
+ | 'monthly'
846
+ | 'yearly'
847
+ | 'never';
848
+
849
+ export type UltramodernPublicSitemapEntry = {
850
+ /**
851
+ * Params used to expand every localized route pattern, for example
852
+ * { slug: 'platform-story' } for /talks/:slug.
853
+ */
854
+ params: Record<string, string | number | boolean>;
855
+ /**
856
+ * Per-locale overrides when translated URLs use translated params.
857
+ */
858
+ localeParams?: Partial<Record<'en' | 'cs', Record<string, string | number | boolean>>>;
859
+ draft?: boolean;
860
+ indexable?: boolean;
861
+ lastModified?: string;
862
+ changeFrequency?: UltramodernPublicSitemapChangeFrequency;
863
+ priority?: number;
864
+ };
865
+
866
+ export const ultramodernWorkspaceContract = {
867
+ ownership: 'topology/ownership.json',
868
+ preset: 'presetUltramodern',
869
+ topology: 'topology/reference-topology.json',
870
+ } as const;
871
+ `;
872
+ }
777
873
  function createAppModernConfig(scope, app) {
778
874
  const bffImport = appHasEffectApi(app) ? "import { bffPlugin } from '@modern-js/plugin-bff';\n" : '';
779
875
  const bffConfig = appHasEffectApi(app) ? ` bff: {
@@ -839,11 +935,20 @@ const inferredCloudflareUrl =
839
935
  cloudflareDeployEnabled && cloudflareWorkersDevSubdomain !== undefined
840
936
  ? \`https://\${cloudflareWorkerName}.\${cloudflareWorkersDevSubdomain}.workers.dev\`
841
937
  : undefined;
938
+ // Site origin (SEO: canonical/hreflang URLs) prefers the site-wide public URL;
939
+ // the per-app deployment URL only fills in when no site origin is configured.
842
940
  const siteUrl =
843
- configuredCloudflareUrl ||
844
941
  configuredSiteUrl ||
942
+ configuredCloudflareUrl ||
845
943
  inferredCloudflareUrl ||
846
944
  \`http://localhost:\${port}\`;
945
+ // Asset origin prefers the per-app deployment URL (each MF app serves its own
946
+ // assets). Without an explicit public URL, assets must stay origin-relative so
947
+ // the app works behind tunnels and proxies (an absolute localhost assetPrefix
948
+ // makes pages served via e.g. ngrok fetch scripts from localhost, which Chrome
949
+ // blocks behind a Local Network Access permission prompt).
950
+ const assetPrefix =
951
+ configuredCloudflareUrl || configuredSiteUrl || inferredCloudflareUrl || '/';
847
952
 
848
953
  if (
849
954
  cloudflareDeployEnabled &&
@@ -872,11 +977,16 @@ ${bffConfig} ...(cloudflareDeployEnabled
872
977
  },
873
978
  }
874
979
  : {}),
980
+ dev: {
981
+ // Keep dev assets origin-relative too; the default absolute
982
+ // http://localhost:<port> prefix breaks pages served through tunnels.
983
+ assetPrefix: '/',
984
+ },
875
985
  html: {
876
986
  outputStructure: 'flat',
877
987
  },
878
988
  output: {
879
- assetPrefix: siteUrl,
989
+ assetPrefix,
880
990
  disableTsChecker: true,
881
991
  distPath: {
882
992
  html: './',
@@ -1101,7 +1211,7 @@ export default createModuleFederationConfig({
1101
1211
  dts: {
1102
1212
  displayErrorInTerminal: true,
1103
1213
  generateTypes: {
1104
- compilerInstance: '--package typescript -- tsc',
1214
+ compilerInstance: 'tsgo',
1105
1215
  },
1106
1216
  },
1107
1217
  filename: 'remoteEntry.js',
@@ -1159,7 +1269,7 @@ export default createModuleFederationConfig({
1159
1269
  dts: {
1160
1270
  displayErrorInTerminal: true,
1161
1271
  generateTypes: {
1162
- compilerInstance: '--package typescript -- tsc',
1272
+ compilerInstance: 'tsgo',
1163
1273
  },
1164
1274
  },
1165
1275
  exposes: ${exposes},
@@ -1181,6 +1291,7 @@ const privateAppRoutePublicness = {
1181
1291
  function createRouteOwnedI18nPaths(app) {
1182
1292
  const namespace = appI18nNamespace(app);
1183
1293
  const base = {
1294
+ descriptionKey: `${namespace}.seo.description`,
1184
1295
  mfBoundaryId: app.mfName,
1185
1296
  namespace,
1186
1297
  ownerAppId: app.id,
@@ -1382,6 +1493,7 @@ function createPublicRouteMetadata(app) {
1382
1493
  localisedPaths: route.localisedPaths,
1383
1494
  namespace: route.namespace,
1384
1495
  ownerAppId: route.ownerAppId,
1496
+ descriptionKey: route.descriptionKey,
1385
1497
  titleKey: route.titleKey
1386
1498
  }));
1387
1499
  }
@@ -1390,7 +1502,11 @@ function createRouteMetadataModule(app) {
1390
1502
  const localisedUrls = sortJsonValue(createLocalisedUrlsMap(app));
1391
1503
  const publicRoutes = sortJsonValue(createPublicRouteMetadata(app));
1392
1504
  const namespace = appI18nNamespace(app);
1393
- return `export const ultramodernRouteNamespace = '${namespace}' as const;
1505
+ return `// @generated by @modern-js/create.
1506
+ // Author route metadata in colocated src/routes/**/route.meta.ts files.
1507
+ // This compatibility manifest is regenerated from route-owned metadata.
1508
+
1509
+ export const ultramodernRouteNamespace = '${namespace}' as const;
1394
1510
 
1395
1511
  export const ultramodernRouteMetadata = ${JSON.stringify(routes, null, 2)} as const;
1396
1512
 
@@ -1399,6 +1515,8 @@ export const ultramodernLocalisedUrls = ${JSON.stringify(localisedUrls, null, 2)
1399
1515
  export const ultramodernPublicRoutes = ${JSON.stringify(publicRoutes, null, 2)} as const;
1400
1516
 
1401
1517
  export const ultramodernRouteConfig = {
1518
+ authoring: 'colocated-route-meta',
1519
+ generatedManifest: true,
1402
1520
  localisedUrls: ultramodernLocalisedUrls,
1403
1521
  namespace: ultramodernRouteNamespace,
1404
1522
  publicRoutes: ultramodernPublicRoutes,
@@ -1407,20 +1525,52 @@ export const ultramodernRouteConfig = {
1407
1525
  } as const;
1408
1526
  `;
1409
1527
  }
1528
+ function createRouteMetaModule(route) {
1529
+ return `const routeMeta = ${JSON.stringify(sortJsonValue(route), null, 2)} as const;
1530
+
1531
+ export default routeMeta;
1532
+ export { routeMeta };
1533
+ `;
1534
+ }
1535
+ function normalisePublicPath(pathname) {
1536
+ const normalised = pathname.trim().replaceAll(/\/+/gu, '/').replace(/\/+$/u, '');
1537
+ return normalised.length > 0 && normalised.startsWith('/') ? normalised : `/${normalised}`;
1538
+ }
1539
+ function splitPublicPathSegments(pathname) {
1540
+ return normalisePublicPath(pathname).split('/').filter(Boolean);
1541
+ }
1542
+ function routePathParamName(segment) {
1543
+ if (segment.startsWith(':')) return segment.slice(1).replace(/[?*+]$/u, '');
1544
+ if (segment.startsWith('[') && segment.endsWith(']')) return segment.slice(1, -1).replace(/^\.\.\./u, '').replace(/\$$/u, '');
1545
+ }
1546
+ function isDynamicPublicPathSegment(segment) {
1547
+ return void 0 !== routePathParamName(segment) || segment.includes('*') || segment.startsWith('[');
1548
+ }
1549
+ function isConcretePublicPath(pathname) {
1550
+ return !splitPublicPathSegments(pathname).some(isDynamicPublicPathSegment);
1551
+ }
1410
1552
  function routeSegmentToDirectory(segment) {
1411
- if (segment.startsWith(':')) {
1412
- const name = segment.slice(1).replace(/\?$/u, '');
1413
- return segment.endsWith('?') ? `[${name}$]` : `[${name}]`;
1414
- }
1553
+ const paramName = routePathParamName(segment);
1554
+ if (paramName && segment.startsWith(':')) return segment.endsWith('?') ? `[${paramName}$]` : `[${paramName}]`;
1415
1555
  return segment;
1416
1556
  }
1557
+ function routePathDirectorySegments(routePath) {
1558
+ return splitPublicPathSegments(routePath).map(routeSegmentToDirectory);
1559
+ }
1417
1560
  function createRoutePageFilePath(app, canonicalPath) {
1418
- const segments = canonicalPath.split('/').filter(Boolean).map(routeSegmentToDirectory);
1561
+ const segments = routePathDirectorySegments(canonicalPath);
1419
1562
  return `${app.directory}/src/routes/[lang]/${[
1420
1563
  ...segments,
1421
1564
  'page.tsx'
1422
1565
  ].join('/')}`;
1423
1566
  }
1567
+ function createRouteMetaFilePath(app, canonicalPath) {
1568
+ const segments = routePathDirectorySegments(canonicalPath);
1569
+ return `${app.directory}/src/routes/[lang]/${[
1570
+ ...segments,
1571
+ 'route.meta.ts'
1572
+ ].join('/')}`;
1573
+ }
1424
1574
  function createRouteAliasPage(canonicalPath) {
1425
1575
  const depth = canonicalPath.split('/').filter(Boolean).length;
1426
1576
  const rootPageImport = `${'../'.repeat(depth)}page`;
@@ -1578,24 +1728,22 @@ export default {} satisfies Config;
1578
1728
  function createTw(prefix) {
1579
1729
  return (classList)=>classList.split(/\s+/u).filter(Boolean).map((candidate)=>`${prefix}:${candidate.replace(/\[&&\]:/gu, '')}`).join(' ');
1580
1730
  }
1581
- const publicSurfaceRequiredAssetPaths = [
1582
- 'config/public/robots.txt'
1583
- ];
1584
- const publicSurfaceOptionalAssetPaths = [
1731
+ const publicSurfaceManagedSourceAssetPaths = [
1732
+ 'config/public/robots.txt',
1585
1733
  'config/public/sitemap.xml',
1586
1734
  'config/public/site.webmanifest'
1587
1735
  ];
1588
- function normalisePublicPath(pathname) {
1589
- const normalised = pathname.trim().replaceAll(/\/+/gu, '/').replace(/\/+$/u, '');
1590
- return normalised.length > 0 && normalised.startsWith('/') ? normalised : `/${normalised}`;
1591
- }
1736
+ const publicSurfaceBaseOutputFiles = [
1737
+ 'robots.txt'
1738
+ ];
1739
+ const publicSurfacePublicRouteOutputFiles = [
1740
+ 'sitemap.xml',
1741
+ 'site.webmanifest'
1742
+ ];
1592
1743
  function createLocalisedPublicPath(pathname, language) {
1593
1744
  const publicPath = normalisePublicPath(pathname);
1594
1745
  return '/' === publicPath ? `/${language}` : `/${language}${publicPath}`;
1595
1746
  }
1596
- function isConcretePublicPath(pathname) {
1597
- return !normalisePublicPath(pathname).split('/').some((segment)=>segment.startsWith(':') || segment.includes('*') || segment.startsWith('['));
1598
- }
1599
1747
  function uniqueSorted(values) {
1600
1748
  return Array.from(new Set(values)).sort((left, right)=>left.localeCompare(right));
1601
1749
  }
@@ -1613,94 +1761,45 @@ function createPublicSurfaceRouteEntries(app) {
1613
1761
  };
1614
1762
  }).filter((route)=>void 0 !== route).sort((left, right)=>left.canonicalUrlPath.localeCompare(right.canonicalUrlPath) || left.id.localeCompare(right.id));
1615
1763
  }
1764
+ function createPublicSurfaceContentSources(_app) {
1765
+ return [];
1766
+ }
1616
1767
  function createPublicSurfaceUrlPaths(app) {
1617
1768
  return uniqueSorted(createPublicSurfaceRouteEntries(app).flatMap((route)=>supportedWorkspaceLanguages.map((language)=>route.localeUrlPaths[language])));
1618
1769
  }
1619
- function createPublicSurfaceOrigin(app) {
1620
- return `http://localhost:${app.port}`;
1621
- }
1622
- function escapeXmlText(value) {
1623
- return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
1624
- }
1625
- function escapeXmlAttribute(value) {
1626
- return escapeXmlText(value).replaceAll('"', '&quot;');
1627
- }
1628
- function renderRobotsTxt(app) {
1629
- const urlPaths = createPublicSurfaceUrlPaths(app);
1630
- const lines = [
1631
- 'User-agent: *'
1632
- ];
1633
- if (0 === urlPaths.length) lines.push('Disallow: /');
1634
- else {
1635
- for (const urlPath of urlPaths)lines.push(`Allow: ${urlPath}$`);
1636
- lines.push('Disallow: /');
1637
- lines.push(`Sitemap: ${createPublicSurfaceOrigin(app)}/sitemap.xml`);
1638
- }
1639
- return `${lines.join('\n')}\n`;
1640
- }
1641
- function renderSitemapXml(app) {
1642
- const origin = createPublicSurfaceOrigin(app);
1643
- const routes = createPublicSurfaceRouteEntries(app);
1644
- const lines = [
1645
- '<?xml version="1.0" encoding="UTF-8"?>',
1646
- '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">'
1770
+ function createPublicSurfaceOutputFiles(app) {
1771
+ return [
1772
+ ...publicSurfaceBaseOutputFiles,
1773
+ ...createPublicRouteMetadata(app).length > 0 ? publicSurfacePublicRouteOutputFiles : []
1647
1774
  ];
1648
- for (const route of routes)for (const language of supportedWorkspaceLanguages){
1649
- lines.push(' <url>');
1650
- lines.push(` <loc>${escapeXmlText(`${origin}${route.localeUrlPaths[language]}`)}</loc>`);
1651
- for (const alternateLanguage of supportedWorkspaceLanguages)lines.push(` <xhtml:link rel="alternate" hreflang="${alternateLanguage}" href="${escapeXmlAttribute(`${origin}${route.localeUrlPaths[alternateLanguage]}`)}" />`);
1652
- lines.push(` <xhtml:link rel="alternate" hreflang="x-default" href="${escapeXmlAttribute(`${origin}${route.localeUrlPaths.en}`)}" />`);
1653
- lines.push(' </url>');
1654
- }
1655
- lines.push('</urlset>');
1656
- return `${lines.join('\n')}\n`;
1657
- }
1658
- function renderWebManifest(app) {
1659
- const startUrl = createPublicSurfaceUrlPaths(app)[0];
1660
- const manifest = {
1661
- name: app.displayName,
1662
- short_name: app.displayName,
1663
- display: 'standalone',
1664
- background_color: '#ffffff',
1665
- theme_color: '#133225',
1666
- lang: 'en',
1667
- categories: [
1668
- 'business',
1669
- 'productivity'
1670
- ],
1671
- icons: [],
1672
- ...startUrl ? {
1673
- scope: '/',
1674
- start_url: startUrl
1675
- } : {}
1676
- };
1677
- return `${JSON.stringify(sortJsonValue(manifest), null, 2)}\n`;
1678
1775
  }
1679
- function createPublicSurfaceAssets(app) {
1680
- const assets = {
1681
- 'config/public/robots.txt': renderRobotsTxt(app)
1682
- };
1683
- if (createPublicSurfaceRouteEntries(app).length > 0) {
1684
- assets['config/public/sitemap.xml'] = renderSitemapXml(app);
1685
- assets['config/public/site.webmanifest'] = renderWebManifest(app);
1686
- }
1687
- return assets;
1776
+ function createPublicSurfaceGenerationCommand(app, target, requirePublicOrigin = false) {
1777
+ return `node ${relativeRootFor(app.directory)}/scripts/generate-public-surface-assets.mjs --app ${app.id} --target ${target}${requirePublicOrigin ? ' --require-public-origin' : ''}`;
1688
1778
  }
1689
1779
  function workspaceAssetsForApp(app) {
1690
- return createPublicSurfaceAssets(app);
1780
+ return {};
1691
1781
  }
1692
1782
  function rewriteWorkspaceAssetsForApp(workspaceRoot, app) {
1783
+ for (const relativePath of publicSurfaceManagedSourceAssetPaths)node_fs.rmSync(node_path.join(workspaceRoot, app.directory, relativePath), {
1784
+ force: true
1785
+ });
1693
1786
  for (const [relativePath, content] of Object.entries(workspaceAssetsForApp(app)))writeFileReplacing(workspaceRoot, `${app.directory}/${relativePath}`, content);
1694
1787
  }
1695
- function createLocalizedHeadComponent() {
1696
- return `const fallbackLanguage = 'en';
1788
+ function createRouteHeadModule(app) {
1789
+ const robotsPolicy = createPublicHeadRobotsPolicy();
1790
+ return `import { useLocalizedLocation, useModernI18n } from '@modern-js/plugin-i18n/runtime';
1791
+ import { Helmet } from '@modern-js/runtime/head';
1792
+ import {
1793
+ ultramodernRouteMetadata,
1794
+ } from './ultramodern-route-metadata';
1795
+
1796
+ const appName = ${JSON.stringify(app.displayName)};
1797
+ const fallbackLanguage = 'en';
1697
1798
  const supportedLanguages = ['en', 'cs'] as const;
1698
1799
  type SupportedLanguage = (typeof supportedLanguages)[number];
1800
+ type RouteMetadata = (typeof ultramodernRouteMetadata)[number];
1699
1801
 
1700
- const localisedUrls = ultramodernLocalisedUrls as Record<
1701
- string,
1702
- Record<SupportedLanguage, string>
1703
- >;
1802
+ const routeMetadata = ultramodernRouteMetadata as readonly RouteMetadata[];
1704
1803
 
1705
1804
  const isSupportedLanguage = (value: string): value is SupportedLanguage =>
1706
1805
  supportedLanguages.includes(value as SupportedLanguage);
@@ -1751,55 +1850,24 @@ const matchPattern = (pathname: string, pattern: string) => {
1751
1850
  return params;
1752
1851
  };
1753
1852
 
1754
- const buildPath = (pattern: string, params: Record<string, string>) => {
1755
- const path = normalisePath(pattern)
1756
- .split('/')
1757
- .filter(Boolean)
1758
- .map(segment => {
1759
- if (!segment.startsWith(':')) {
1760
- return segment;
1761
- }
1762
- const value = params[paramName(segment)];
1763
- return value !== undefined && value.length > 0
1764
- ? encodeURIComponent(value)
1765
- : '';
1766
- })
1767
- .filter(Boolean)
1768
- .join('/');
1769
-
1770
- return \`/\${path}\`;
1771
- };
1772
-
1773
- const resolveLocalisedPath = (
1774
- pathname: string,
1775
- targetLanguage: SupportedLanguage,
1776
- ) => {
1853
+ const resolveRouteMetadata = (pathname: string) => {
1777
1854
  const pathWithoutLanguage = stripLanguagePrefix(pathname);
1778
1855
 
1779
- for (const entry of Object.values(localisedUrls)) {
1780
- const targetPattern = entry[targetLanguage];
1781
- if (targetPattern === undefined) {
1782
- continue;
1856
+ for (const route of routeMetadata) {
1857
+ const canonicalParams = matchPattern(pathWithoutLanguage, route.canonicalPath);
1858
+ if (canonicalParams !== undefined) {
1859
+ return route;
1783
1860
  }
1784
1861
 
1785
1862
  for (const language of supportedLanguages) {
1786
- const sourcePattern = entry[language];
1787
- const params =
1788
- sourcePattern === undefined
1789
- ? undefined
1790
- : matchPattern(pathWithoutLanguage, sourcePattern);
1863
+ const params = matchPattern(pathWithoutLanguage, route.localisedPaths[language]);
1791
1864
  if (params !== undefined) {
1792
- return buildPath(targetPattern, params);
1865
+ return route;
1793
1866
  }
1794
1867
  }
1795
1868
  }
1796
1869
 
1797
- return pathWithoutLanguage;
1798
- };
1799
-
1800
- const localizedPath = (pathname: string, language: SupportedLanguage) => {
1801
- const pathWithoutLanguage = resolveLocalisedPath(pathname, language);
1802
- return pathWithoutLanguage === '/' ? \`/\${language}\` : \`/\${language}\${pathWithoutLanguage}\`;
1870
+ return routeMetadata[0];
1803
1871
  };
1804
1872
 
1805
1873
  const absoluteUrl = (pathname: string) => {
@@ -1807,26 +1875,68 @@ const absoluteUrl = (pathname: string) => {
1807
1875
  return \`\${origin}\${pathname}\`;
1808
1876
  };
1809
1877
 
1810
- const LocalizedHead = () => {
1811
- const location = useLocation();
1812
- const canonicalPath = localizedPath(location.pathname, fallbackLanguage);
1878
+ const sanitiseJsonLd = (value: unknown) =>
1879
+ JSON.stringify(value).replaceAll('<', '\\\\u003c');
1880
+
1881
+ export const UltramodernRouteHead = () => {
1882
+ const { i18nInstance } = useModernI18n();
1883
+ const t = i18nInstance['t'].bind(i18nInstance);
1884
+ const { canonical, alternates } = useLocalizedLocation();
1885
+ const route = resolveRouteMetadata(canonical);
1886
+ const title = route ? t(route.titleKey) : appName;
1887
+ const description = route ? t(route.descriptionKey) : appName;
1888
+ const canonicalUrl = absoluteUrl(alternates[fallbackLanguage] ?? \`/\${fallbackLanguage}\`);
1889
+ const indexable = route?.public === true && route?.indexable === true;
1890
+ const jsonLd = indexable
1891
+ ? {
1892
+ '@context': 'https://schema.org',
1893
+ '@type': 'WebPage',
1894
+ description,
1895
+ inLanguage: supportedLanguages.join(','),
1896
+ isPartOf: {
1897
+ '@type': 'WebSite',
1898
+ name: appName,
1899
+ url: absoluteUrl('/'),
1900
+ },
1901
+ name: title,
1902
+ url: canonicalUrl,
1903
+ }
1904
+ : undefined;
1813
1905
 
1814
1906
  return (
1815
- <Helmet>
1816
- <link rel="canonical" href={absoluteUrl(canonicalPath)} />
1817
- {supportedLanguages.map(code => (
1818
- <link
1819
- href={absoluteUrl(localizedPath(location.pathname, code))}
1820
- hrefLang={code}
1821
- key={code}
1822
- rel="alternate"
1823
- />
1824
- ))}
1825
- <link
1826
- href={absoluteUrl(localizedPath(location.pathname, fallbackLanguage))}
1827
- hrefLang="x-default"
1828
- rel="alternate"
1829
- />
1907
+ <Helmet htmlAttributes={{ lang: i18nInstance.language ?? fallbackLanguage }}>
1908
+ <title>{title}</title>
1909
+ <meta content={description} name="description" />
1910
+ <meta content={indexable ? '${robotsPolicy.indexableRobots}' : '${robotsPolicy.privateRouteRobots}'} name="robots" />
1911
+ {indexable && (
1912
+ <>
1913
+ <link rel="canonical" href={canonicalUrl} />
1914
+ {supportedLanguages.map(code => (
1915
+ <link
1916
+ href={absoluteUrl(alternates[code] ?? \`/\${code}\`)}
1917
+ hrefLang={code}
1918
+ key={code}
1919
+ rel="alternate"
1920
+ />
1921
+ ))}
1922
+ <link
1923
+ href={absoluteUrl(alternates[fallbackLanguage] ?? \`/\${fallbackLanguage}\`)}
1924
+ hrefLang="x-default"
1925
+ rel="alternate"
1926
+ />
1927
+ <meta content={title} property="og:title" />
1928
+ <meta content={description} property="og:description" />
1929
+ <meta content={canonicalUrl} property="og:url" />
1930
+ <meta content="website" property="og:type" />
1931
+ <meta content={i18nInstance.language ?? fallbackLanguage} property="og:locale" />
1932
+ <meta content="summary_large_image" name="twitter:card" />
1933
+ <meta content={title} name="twitter:title" />
1934
+ <meta content={description} name="twitter:description" />
1935
+ {jsonLd && (
1936
+ <script type="application/ld+json">{sanitiseJsonLd(jsonLd)}</script>
1937
+ )}
1938
+ </>
1939
+ )}
1830
1940
  </Helmet>
1831
1941
  );
1832
1942
  };
@@ -1835,31 +1945,28 @@ const LocalizedHead = () => {
1835
1945
  function createShellPage(remotes = []) {
1836
1946
  const tw = createTw(tailwindPrefixForApp(shellApp));
1837
1947
  const remoteCount = String(remotes.length);
1838
- return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
1839
- import { Helmet } from '@modern-js/runtime/head';
1840
- import { useLocation } from '@modern-js/plugin-tanstack/runtime';
1948
+ return `import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
1841
1949
  import ShellFrame from '../shell-frame';
1950
+ import { UltramodernRouteHead } from '../ultramodern-route-head';
1842
1951
  import { VerticalShowcase } from '../vertical-components';
1843
- import { ultramodernLocalisedUrls } from '../ultramodern-route-metadata';
1844
1952
  import { ultramodernUiMarker } from '../../ultramodern-build';
1845
1953
 
1846
- ${createLocalizedHeadComponent()}
1847
1954
  export default function ShellHome() {
1848
1955
  const { i18nInstance } = useModernI18n();
1849
1956
  const t = i18nInstance['t'].bind(i18nInstance);
1850
1957
 
1851
1958
  return (
1852
1959
  <ShellFrame>
1853
- <LocalizedHead />
1960
+ <UltramodernRouteHead />
1854
1961
  <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')}">
1855
1962
  <div className="${tw('min-w-0')}">
1856
1963
  <p className="${tw('text-xs font-black uppercase tracking-[0.18em] text-emerald-800')}">{t('shell.hero.eyebrow')}</p>
1857
1964
  <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>
1858
1965
  <p className="${tw('mt-5 max-w-2xl text-lg leading-8 text-stone-600')}">{t('shell.hero.lede')}</p>
1859
1966
  <div className="${tw('mt-7 flex flex-wrap gap-3')}">
1860
- <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="/">
1967
+ <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="/">
1861
1968
  {t('shell.hero.primary')}
1862
- </I18nLink>
1969
+ </Link>
1863
1970
  <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')}">
1864
1971
  {t('shell.hero.secondary')}
1865
1972
  </span>
@@ -1892,145 +1999,18 @@ export default function ShellHome() {
1892
1999
  }
1893
2000
  function createShellFrameComponent() {
1894
2001
  const tw = createTw(tailwindPrefixForApp(shellApp));
1895
- return `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
1896
- import { useLocation } from '@modern-js/plugin-tanstack/runtime';
2002
+ return `import { useLocalizedLocation, useModernI18n } from '@modern-js/plugin-i18n/runtime';
1897
2003
  import type { ReactNode } from 'react';
1898
2004
  import { Header, StatusBadge } from './vertical-components';
1899
- import { ultramodernLocalisedUrls } from './ultramodern-route-metadata';
1900
-
1901
- const supportedLanguages = ['en', 'cs'] as const;
1902
- type SupportedLanguage = (typeof supportedLanguages)[number];
1903
2005
 
1904
2006
  interface ShellFrameProps {
1905
2007
  children: ReactNode;
1906
2008
  }
1907
2009
 
1908
- const localisedUrls = ultramodernLocalisedUrls as Record<
1909
- string,
1910
- Record<SupportedLanguage, string>
1911
- >;
1912
-
1913
- const isSupportedLanguage = (value: string): value is SupportedLanguage =>
1914
- supportedLanguages.includes(value as SupportedLanguage);
1915
-
1916
- const normalisePath = (pathname: string) => {
1917
- const normalised = pathname.replaceAll(/\\/+/gu, '/').replace(/\\/+$/u, '');
1918
- return normalised.length > 0 ? normalised : '/';
1919
- };
1920
-
1921
- const stripLanguagePrefix = (pathname: string) => {
1922
- const segments = normalisePath(pathname).split('/').filter(Boolean);
1923
- if (segments.length > 0 && isSupportedLanguage(segments[0] ?? '')) {
1924
- segments.shift();
1925
- }
1926
- return \`/\${segments.join('/')}\`;
1927
- };
1928
-
1929
- const escapeRegExp = (value: string) =>
1930
- value.replaceAll(/[.*+?^\${}()|[\\]\\\\]/gu, '\\\\$&');
1931
-
1932
- const paramName = (segment: string) => segment.slice(1).replace(/\\?$/u, '');
1933
-
1934
- const matchPattern = (pathname: string, pattern: string) => {
1935
- const names: string[] = [];
1936
- const source = normalisePath(pattern)
1937
- .split('/')
1938
- .filter(Boolean)
1939
- .map(segment => {
1940
- if (segment.startsWith(':')) {
1941
- names.push(paramName(segment));
1942
- return segment.endsWith('?') ? '(?:/([^/]+))?' : '/([^/]+)';
1943
- }
1944
- return \`/\${escapeRegExp(segment)}\`;
1945
- })
1946
- .join('');
1947
- const match = new RegExp(\`^\${source || '/'}$\`, 'u').exec(
1948
- normalisePath(pathname),
1949
- );
1950
-
1951
- if (match === null) {
1952
- return;
1953
- }
1954
-
1955
- const params: Record<string, string> = {};
1956
- for (const [index, name] of names.entries()) {
1957
- params[name] = decodeURIComponent(match[index + 1] ?? '');
1958
- }
1959
- return params;
1960
- };
1961
-
1962
- const buildPath = (pattern: string, params: Record<string, string>) => {
1963
- const path = normalisePath(pattern)
1964
- .split('/')
1965
- .filter(Boolean)
1966
- .map(segment => {
1967
- if (!segment.startsWith(':')) {
1968
- return segment;
1969
- }
1970
- const value = params[paramName(segment)];
1971
- return value !== undefined && value.length > 0
1972
- ? encodeURIComponent(value)
1973
- : '';
1974
- })
1975
- .filter(Boolean)
1976
- .join('/');
1977
-
1978
- return \`/\${path}\`;
1979
- };
1980
-
1981
- const resolveLocalisedPath = (
1982
- pathname: string,
1983
- targetLanguage: SupportedLanguage,
1984
- ) => {
1985
- const pathWithoutLanguage = stripLanguagePrefix(pathname);
1986
-
1987
- for (const entry of Object.values(localisedUrls)) {
1988
- const targetPattern = entry[targetLanguage];
1989
- if (targetPattern === undefined) {
1990
- continue;
1991
- }
1992
-
1993
- for (const language of supportedLanguages) {
1994
- const sourcePattern = entry[language];
1995
- const params =
1996
- sourcePattern === undefined
1997
- ? undefined
1998
- : matchPattern(pathWithoutLanguage, sourcePattern);
1999
- if (params !== undefined) {
2000
- return buildPath(targetPattern, params);
2001
- }
2002
- }
2003
- }
2004
-
2005
- return pathWithoutLanguage;
2006
- };
2007
-
2008
- const localizedPath = (pathname: string, language: SupportedLanguage) => {
2009
- const pathWithoutLanguage = resolveLocalisedPath(pathname, language);
2010
- return pathWithoutLanguage === '/' ? \`/\${language}\` : \`/\${language}\${pathWithoutLanguage}\`;
2011
- };
2012
-
2013
- const locationSuffix = (location: {
2014
- hash?: unknown;
2015
- search?: unknown;
2016
- searchStr?: unknown;
2017
- }) => {
2018
- let locationSearch = '';
2019
- if (typeof location.searchStr === 'string') {
2020
- locationSearch = location.searchStr;
2021
- } else if (typeof location.search === 'string') {
2022
- locationSearch = location.search;
2023
- }
2024
- const locationHash = typeof location.hash === 'string' ? location.hash : '';
2025
-
2026
- return \`\${locationSearch}\${locationHash}\`;
2027
- };
2028
-
2029
2010
  export default function ShellFrame({ children }: ShellFrameProps) {
2030
2011
  const { i18nInstance, language } = useModernI18n();
2031
2012
  const t = i18nInstance['t'].bind(i18nInstance);
2032
- const location = useLocation();
2033
- const suffix = locationSuffix(location);
2013
+ const { alternates } = useLocalizedLocation();
2034
2014
 
2035
2015
  return (
2036
2016
  <main className="${tw('min-h-screen bg-um-canvas px-4 py-5 text-um-foreground sm:px-6 lg:px-12')}">
@@ -2047,10 +2027,9 @@ export default function ShellFrame({ children }: ShellFrameProps) {
2047
2027
  name="language"
2048
2028
  onChange={event => {
2049
2029
  const nextLanguage = event.currentTarget.value;
2050
- if (isSupportedLanguage(nextLanguage)) {
2051
- window.location.assign(
2052
- \`\${localizedPath(location.pathname, nextLanguage)}\${suffix}\`,
2053
- );
2030
+ const targetHref = alternates[nextLanguage];
2031
+ if (targetHref !== undefined) {
2032
+ window.location.assign(targetHref);
2054
2033
  }
2055
2034
  }}
2056
2035
  value={language}
@@ -2141,7 +2120,7 @@ const createHydratedRemote =
2141
2120
  return ` <${componentName} key="${remote.id}" />`;
2142
2121
  }).join('\n');
2143
2122
  const remoteCount = String(widgetRemotes.length);
2144
- return `${federationImports}import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2123
+ return `${federationImports}import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2145
2124
 
2146
2125
  const widgetCount = Number('${remoteCount}');
2147
2126
 
@@ -2154,7 +2133,7 @@ const createHydratedRemote =
2154
2133
 
2155
2134
  return (
2156
2135
  <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">
2157
- <I18nLink className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('shell.title')}</I18nLink>
2136
+ <Link className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('shell.title')}</Link>
2158
2137
  </header>
2159
2138
  );
2160
2139
  };
@@ -2196,17 +2175,16 @@ function createRemotePage(app) {
2196
2175
  const tw = createTw(tailwindPrefixForApp(app));
2197
2176
  const listEffectItems = `list${toPascalCase(effectApiStem(app))}`;
2198
2177
  const effectBffImport = appHasEffectApi(app) ? `import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
2199
- import { Helmet } from '@modern-js/runtime/head';
2200
- import { Link, useLocation } from '@modern-js/plugin-tanstack/runtime';
2178
+ import { Link } from '@modern-js/plugin-tanstack/runtime';
2201
2179
  import { useEffect, useState } from 'react';
2202
2180
  import {
2203
2181
  Effect,
2204
2182
  ${listEffectItems},
2205
2183
  runEffectRequest,
2206
2184
  } from '../../effect/${effectApiStem(app)}-client';
2207
- import { ultramodernLocalisedUrls } from '../ultramodern-route-metadata';
2185
+ import { UltramodernRouteHead } from '../ultramodern-route-head';
2208
2186
  import { ultramodernUiMarker } from '../../ultramodern-build';
2209
- ` : "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";
2187
+ ` : "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";
2210
2188
  const effectBffState = appHasEffectApi(app) ? ` const [effectApiStatus, setEffectApiStatus] = useState('pending');
2211
2189
 
2212
2190
  useEffect(() => {
@@ -2239,13 +2217,12 @@ import { ultramodernUiMarker } from '../../ultramodern-build';
2239
2217
  const effectBffMarkup = appHasEffectApi(app) ? ` <p data-testid="effect-bff-status">{effectApiStatus}</p>
2240
2218
  ` : '';
2241
2219
  return `${effectBffImport}
2242
- ${createLocalizedHeadComponent()}
2243
2220
  export default function ${toPascalCase(app.id)}Home() {
2244
2221
  const { i18nInstance, language } = useModernI18n();
2245
2222
  const t = i18nInstance['t'].bind(i18nInstance);
2246
2223
  ${effectBffState} return (
2247
2224
  <main className="${tw('min-h-screen bg-um-canvas px-4 py-6 text-um-foreground sm:px-8')}">
2248
- <LocalizedHead />
2225
+ <UltramodernRouteHead />
2249
2226
  <nav aria-label={t('${app.domain}.language.switcher')} className="${tw('flex gap-3')}">
2250
2227
  {supportedLanguages.map(code => (
2251
2228
  <Link
@@ -2325,7 +2302,7 @@ export default function ${componentName}() {
2325
2302
  }
2326
2303
  function createRemoteExposeComponent(app, expose) {
2327
2304
  const tw = createTw(tailwindPrefixForApp(app));
2328
- if ('workspace' === app.id && './Header' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2305
+ if ('workspace' === app.id && './Header' === expose) return `import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2329
2306
 
2330
2307
  export default function Header() {
2331
2308
  const { i18nInstance } = useModernI18n();
@@ -2333,16 +2310,16 @@ export default function Header() {
2333
2310
 
2334
2311
  return (
2335
2312
  <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}">
2336
- <I18nLink className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('workspace.header.brand')}</I18nLink>
2313
+ <Link className="${tw('whitespace-nowrap text-xl font-black tracking-normal text-stone-950 no-underline')}" to="/">{t('workspace.header.brand')}</Link>
2337
2314
  <nav aria-label={t('workspace.header.navigation')} className="${tw('flex items-center gap-5')}">
2338
- <I18nLink className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/workspaces">{t('workspace.header.workspaces')}</I18nLink>
2339
- <I18nLink className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/directory">{t('workspace.header.directory')}</I18nLink>
2315
+ <Link className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/workspaces">{t('workspace.header.workspaces')}</Link>
2316
+ <Link className="${tw('text-sm font-extrabold text-stone-900 no-underline')}" to="/directory">{t('workspace.header.directory')}</Link>
2340
2317
  </nav>
2341
2318
  </header>
2342
2319
  );
2343
2320
  }
2344
2321
  `;
2345
- if ('workspace' === app.id && './Highlights' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2322
+ if ('workspace' === app.id && './Highlights' === expose) return `import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2346
2323
 
2347
2324
  const highlights = [
2348
2325
  { badge: 'workspace.highlights.shell', href: '/workspaces', name: 'workspace.highlights.shellTitle' },
@@ -2359,10 +2336,10 @@ export default function Highlights() {
2359
2336
  <h2 className="${tw('text-3xl font-black tracking-normal text-stone-950')}">{t('workspace.highlights.title')}</h2>
2360
2337
  <div className="${tw('mt-5 grid gap-4 md:grid-cols-3')}">
2361
2338
  {highlights.map(highlight => (
2362
- <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}>
2339
+ <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}>
2363
2340
  <span className="${tw('text-xs font-black uppercase tracking-[0.16em] text-emerald-800')}">{t(highlight.badge)}</span>
2364
2341
  <strong className="${tw('mt-3 block text-xl font-black leading-tight')}">{t(highlight.name)}</strong>
2365
- </I18nLink>
2342
+ </Link>
2366
2343
  ))}
2367
2344
  </div>
2368
2345
  </section>
@@ -2439,7 +2416,7 @@ export default function ${componentName}() {
2439
2416
  );
2440
2417
  }
2441
2418
  `;
2442
- if ('actions' === app.id && './StartAction' === expose) return `import { I18nLink, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2419
+ if ('actions' === app.id && './StartAction' === expose) return `import { Link, useModernI18n } from '@modern-js/plugin-i18n/runtime';
2443
2420
  import { useActionQueue } from '../action-queue-store';
2444
2421
 
2445
2422
  export default function ${componentName}() {
@@ -2452,9 +2429,9 @@ export default function ${componentName}() {
2452
2429
  <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">
2453
2430
  {t('actions.controls.start')}
2454
2431
  </button>
2455
- <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">
2432
+ <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">
2456
2433
  {t('actions.controls.viewQueue')}
2457
- </I18nLink>
2434
+ </Link>
2458
2435
  </div>
2459
2436
  );
2460
2437
  }
@@ -2619,6 +2596,9 @@ const commonLocaleMessages = {
2619
2596
  review: 'Revize akce',
2620
2597
  unavailable: 'Nedostupné',
2621
2598
  workspaces: 'Pracovní prostory'
2599
+ },
2600
+ seo: {
2601
+ description: 'Route-owned UltraModern plocha s lokalizovaným SSR a frameworkem řízenými public metadata.'
2622
2602
  }
2623
2603
  },
2624
2604
  en: {
@@ -2636,6 +2616,9 @@ const commonLocaleMessages = {
2636
2616
  review: 'Action review',
2637
2617
  unavailable: 'Unavailable',
2638
2618
  workspaces: 'Workspaces'
2619
+ },
2620
+ seo: {
2621
+ description: 'Route-owned UltraModern surface with localized SSR and framework-owned public metadata.'
2639
2622
  }
2640
2623
  }
2641
2624
  };
@@ -2714,6 +2697,9 @@ const generatedLocaleResources = {
2714
2697
  routes: {
2715
2698
  home: commonLocaleMessages.cs.routes.home
2716
2699
  },
2700
+ seo: {
2701
+ description: 'UltraModern shell SuperApp s lokalizovaným SSR, Module Federation a frameworkem řízenými public metadata.'
2702
+ },
2717
2703
  title: 'UltraModern Workspace'
2718
2704
  },
2719
2705
  workspace: {
@@ -2823,6 +2809,9 @@ const generatedLocaleResources = {
2823
2809
  routes: {
2824
2810
  home: commonLocaleMessages.en.routes.home
2825
2811
  },
2812
+ seo: {
2813
+ description: 'UltraModern shell SuperApp with localized SSR, Module Federation, and framework-owned public metadata.'
2814
+ },
2826
2815
  title: 'UltraModern Workspace'
2827
2816
  },
2828
2817
  workspace: {
@@ -3699,15 +3688,17 @@ function createAppConfigContract(app) {
3699
3688
  'moduleFederationPlugin',
3700
3689
  'zephyrRspackPlugin'
3701
3690
  ],
3691
+ dev: {
3692
+ assetPrefix: '/'
3693
+ },
3702
3694
  output: {
3703
3695
  assetPrefix: {
3704
3696
  envFallbackOrder: [
3705
3697
  createCloudflarePublicUrlEnv(app),
3706
3698
  'MODERN_PUBLIC_SITE_URL',
3707
- 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
3708
- app.portEnv
3699
+ 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN'
3709
3700
  ],
3710
- defaultLocalhostPort: app.port
3701
+ default: '/'
3711
3702
  },
3712
3703
  disableTsChecker: true,
3713
3704
  distPath: {
@@ -3733,6 +3724,15 @@ function createAppConfigContract(app) {
3733
3724
  },
3734
3725
  source: {
3735
3726
  mainEntryName: 'index',
3727
+ siteUrl: {
3728
+ envFallbackOrder: [
3729
+ 'MODERN_PUBLIC_SITE_URL',
3730
+ createCloudflarePublicUrlEnv(app),
3731
+ 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
3732
+ app.portEnv
3733
+ ],
3734
+ defaultLocalhostPort: app.port
3735
+ },
3736
3736
  siteUrlGlobal: 'ULTRAMODERN_SITE_URL'
3737
3737
  },
3738
3738
  ...appHasEffectApi(app) ? {
@@ -3894,11 +3894,17 @@ function createStylingContract(scope, app, enableTailwind) {
3894
3894
  };
3895
3895
  }
3896
3896
  function createPublicSurfaceContract(app) {
3897
- const files = Object.keys(createPublicSurfaceAssets(app)).sort().map((relativePath)=>relativePath.replace(/^config\/public\//u, ''));
3897
+ const files = createPublicSurfaceOutputFiles(app);
3898
+ const contentExpansionPolicy = createPublicSurfaceContentExpansionPolicy();
3898
3899
  return {
3900
+ authoring: 'colocated-route-meta',
3901
+ artifactLifecycle: 'build-and-deploy-output',
3902
+ generatedManifest: './src/routes/ultramodern-route-metadata',
3899
3903
  source: 'route-owned-public-routes',
3900
3904
  metadataExport: './src/routes/ultramodern-route-metadata',
3901
- staticRoot: 'config/public',
3905
+ generator: "scripts/generate-public-surface-assets.mjs",
3906
+ outputRoot: 'dist/public',
3907
+ cloudflareOutputRoot: '.output/public',
3902
3908
  privateRoutePolicy: 'omit-from-generated-public-surface',
3903
3909
  files,
3904
3910
  omittedByDefault: [
@@ -3906,15 +3912,103 @@ function createPublicSurfaceContract(app) {
3906
3912
  'llms.txt',
3907
3913
  'security.txt'
3908
3914
  ],
3915
+ languages: [
3916
+ ...supportedWorkspaceLanguages
3917
+ ],
3918
+ contentExpansion: {
3919
+ authoring: 'route-owned-esm-provider',
3920
+ defaultProviderFile: contentExpansionPolicy.defaultProviderFile,
3921
+ entryExport: 'default-or-entries',
3922
+ paramsSource: 'params-or-localeParams',
3923
+ draftPolicy: contentExpansionPolicy.draftPolicy,
3924
+ indexablePolicy: contentExpansionPolicy.indexablePolicy,
3925
+ lifecycle: 'executed-during-public-surface-generation'
3926
+ },
3927
+ contentSources: createPublicSurfaceContentSources(app),
3909
3928
  publicRoutes: createPublicRouteMetadata(app),
3929
+ routeEntries: createPublicSurfaceRouteEntries(app),
3910
3930
  concreteUrlPaths: createPublicSurfaceUrlPaths(app)
3911
3931
  };
3912
3932
  }
3933
+ function createPublicHeadContract() {
3934
+ const robotsPolicy = createPublicHeadRobotsPolicy();
3935
+ return {
3936
+ authoring: 'colocated-route-meta',
3937
+ generator: './src/routes/ultramodern-route-head',
3938
+ renderer: '@modern-js/runtime/head Helmet',
3939
+ ssr: true,
3940
+ title: {
3941
+ required: true,
3942
+ source: 'route.titleKey'
3943
+ },
3944
+ description: {
3945
+ required: true,
3946
+ source: "route.descriptionKey"
3947
+ },
3948
+ canonical: {
3949
+ publicIndexableOnly: true,
3950
+ source: 'localized canonical route URL'
3951
+ },
3952
+ alternates: {
3953
+ hreflang: [
3954
+ ...supportedWorkspaceLanguages
3955
+ ],
3956
+ xDefault: 'en'
3957
+ },
3958
+ openGraph: {
3959
+ publicIndexableOnly: true,
3960
+ required: [
3961
+ 'og:title',
3962
+ "og:description",
3963
+ 'og:url',
3964
+ 'og:type'
3965
+ ]
3966
+ },
3967
+ twitter: {
3968
+ publicIndexableOnly: true,
3969
+ required: [
3970
+ 'twitter:card',
3971
+ 'twitter:title',
3972
+ "twitter:description"
3973
+ ]
3974
+ },
3975
+ structuredData: {
3976
+ publicIndexableOnly: true,
3977
+ type: 'WebPage',
3978
+ sanitizesHtmlOpenBracket: true
3979
+ },
3980
+ privateRouteRobots: robotsPolicy.privateRouteRobots
3981
+ };
3982
+ }
3983
+ function createPublicWebAppArtifacts(app) {
3984
+ const routeMetadata = createRouteOwnedI18nPaths(app);
3985
+ return {
3986
+ routeMetadataFile: {
3987
+ path: `${app.directory}/src/routes/ultramodern-route-metadata.ts`,
3988
+ content: createRouteMetadataModule(app)
3989
+ },
3990
+ routeHeadFile: {
3991
+ path: `${app.directory}/src/routes/ultramodern-route-head.tsx`,
3992
+ content: createRouteHeadModule(app)
3993
+ },
3994
+ routeMetaFiles: routeMetadata.map((route)=>({
3995
+ path: createRouteMetaFilePath(app, route.canonicalPath),
3996
+ content: createRouteMetaModule(route)
3997
+ })),
3998
+ routeAliasFiles: routeMetadata.filter((route)=>'/' !== route.canonicalPath && 'shell' !== app.kind).map((route)=>({
3999
+ path: createRoutePageFilePath(app, route.canonicalPath),
4000
+ content: createRouteAliasPage(route.canonicalPath)
4001
+ })),
4002
+ publicHead: createPublicHeadContract(),
4003
+ publicSurface: createPublicSurfaceContract(app)
4004
+ };
4005
+ }
3913
4006
  function createAppGeneratedContract(scope, app, apps, enableTailwind) {
3914
4007
  const appWithResolvedRefs = 'shell' === app.kind ? {
3915
4008
  ...app,
3916
4009
  verticalRefs: apps.filter((candidate)=>'shell' !== candidate.kind).map((candidate)=>candidate.id)
3917
4010
  } : app;
4011
+ const publicWeb = createPublicWebAppArtifacts(app);
3918
4012
  const consumedRemotes = createModuleFederationRemoteContracts(appWithResolvedRefs, apps);
3919
4013
  return {
3920
4014
  id: app.id,
@@ -3971,6 +4065,8 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
3971
4065
  },
3972
4066
  routes: {
3973
4067
  source: 'route-owned',
4068
+ metadataAuthoring: 'colocated-route-meta',
4069
+ generatedManifest: true,
3974
4070
  metadataExport: './src/routes/ultramodern-route-metadata',
3975
4071
  localisedUrls: createLocalisedUrlsMap(app),
3976
4072
  owned: createRouteOwnedI18nPaths(app),
@@ -3979,7 +4075,8 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
3979
4075
  publicnessDefault: 'private-app-screen',
3980
4076
  generatedRouteMap: true,
3981
4077
  manualOverrides: [],
3982
- publicSurface: createPublicSurfaceContract(app)
4078
+ publicHead: publicWeb.publicHead,
4079
+ publicSurface: publicWeb.publicSurface
3983
4080
  },
3984
4081
  moduleFederation: {
3985
4082
  name: app.mfName,
@@ -3990,7 +4087,7 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
3990
4087
  exposes: Object.keys(app.exposes ?? {}),
3991
4088
  dts: {
3992
4089
  displayErrorInTerminal: true,
3993
- compilerInstance: "--package typescript -- tsc"
4090
+ compilerInstance: 'tsgo'
3994
4091
  },
3995
4092
  browserSafeExposesOnly: true,
3996
4093
  zephyrRspackPlugin: ZEPHYR_RSPACK_PLUGIN_VERSION
@@ -4215,7 +4312,7 @@ for (const appDir of appDirs) {
4215
4312
  if (
4216
4313
  contractEntry &&
4217
4314
  contractEntry.moduleFederation?.dts?.compilerInstance !==
4218
- '--package typescript -- tsc'
4315
+ 'tsgo'
4219
4316
  ) {
4220
4317
  throw new Error(
4221
4318
  \`Module Federation DTS must use the workspace TypeScript compiler: \${appDir}\`,
@@ -4254,53 +4351,588 @@ process.exitCode = runWorkspaceSourceCheck({
4254
4351
  });
4255
4352
  `;
4256
4353
  }
4257
- function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
4258
- const verticals = remotes.filter(appHasEffectApi).map((remote)=>({
4259
- id: remote.id,
4260
- domain: remote.domain,
4261
- stem: remote.effectApi.stem,
4262
- group: verticalEffectGroupName(remote),
4263
- path: remote.directory,
4264
- mfName: remote.mfName,
4265
- apiPrefix: remote.effectApi.prefix,
4266
- tailwindPrefix: tailwindPrefixForApp(remote),
4267
- zephyrAlias: remoteDependencyAlias(remote),
4268
- packageName: ultramodern_workspace_packageName(scope, remote.packageSuffix),
4269
- exposes: Object.keys(remote.exposes ?? {}),
4270
- componentPaths: Object.keys(remote.exposes ?? {}).map((expose)=>remoteComponentOutputPath(remote, expose)).filter((componentPath)=>Boolean(componentPath)),
4271
- namespace: appI18nNamespace(remote),
4272
- routePagePaths: createRouteOwnedI18nPaths(remote).filter((route)=>'/' !== route.canonicalPath).map((route)=>createRoutePageFilePath(remote, route.canonicalPath)),
4273
- localisedUrls: createLocalisedUrlsMap(remote),
4274
- verticalRefs: remote.verticalRefs ?? []
4275
- }));
4276
- const shellNamespace = appI18nNamespace(shellApp);
4277
- const oldRemotePaths = [
4278
- 'apps/remotes'
4279
- ];
4280
- 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';
4281
- 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';
4282
- 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';
4283
- const expectedCloudflareSecurity = createCloudflareSecurityContract();
4284
- return `import { execFileSync } from 'node:child_process';
4354
+ function createPublicSurfaceAssetsScript() {
4355
+ const contentExpansionPolicy = createPublicSurfaceContentExpansionPolicy();
4356
+ return `#!/usr/bin/env node
4285
4357
  import fs from 'node:fs';
4286
4358
  import path from 'node:path';
4359
+ import { fileURLToPath, pathToFileURL } from 'node:url';
4287
4360
 
4288
- const root = process.cwd();
4289
- const packageScope = '${scope}';
4290
- const expectedPnpmVersion = '${PNPM_VERSION}';
4291
- const tailwindEnabled = ${JSON.stringify(enableTailwind)};
4292
- const fullStackVerticals = ${JSON.stringify(verticals, null, 2)};
4293
- const shellNamespace = ${JSON.stringify(shellNamespace)};
4294
- const oldRemotePaths = ${JSON.stringify(oldRemotePaths, null, 2)};
4361
+ const workspaceRoot = path.resolve(
4362
+ path.dirname(fileURLToPath(import.meta.url)),
4363
+ '..',
4364
+ );
4365
+ const contractPath = path.join(
4366
+ workspaceRoot,
4367
+ '.modernjs/ultramodern-generated-contract.json',
4368
+ );
4369
+
4370
+ function readJson(filePath) {
4371
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
4372
+ }
4373
+
4374
+ function parseArgs(argv) {
4375
+ const parsed = {
4376
+ appId: undefined,
4377
+ target: 'dist',
4378
+ requirePublicOrigin: false,
4379
+ };
4380
+
4381
+ for (let index = 0; index < argv.length; index += 1) {
4382
+ const arg = argv[index];
4383
+ if (arg === '--app') {
4384
+ parsed.appId = argv[index + 1];
4385
+ index += 1;
4386
+ } else if (arg === '--target') {
4387
+ parsed.target = argv[index + 1];
4388
+ index += 1;
4389
+ } else if (arg === '--require-public-origin') {
4390
+ parsed.requirePublicOrigin = true;
4391
+ } else if (arg === '--help' || arg === '-h') {
4392
+ parsed.help = true;
4393
+ } else {
4394
+ throw new Error(\`Unknown argument: \${arg}\`);
4395
+ }
4396
+ }
4397
+
4398
+ if (!parsed.appId && !parsed.help) {
4399
+ throw new Error('Missing required --app argument');
4400
+ }
4401
+ if (!['dist', 'cloudflare'].includes(parsed.target)) {
4402
+ throw new Error(\`Unsupported public surface target: \${parsed.target}\`);
4403
+ }
4404
+
4405
+ return parsed;
4406
+ }
4407
+
4408
+ function printHelp() {
4409
+ process.stdout.write(\`Usage:
4410
+ node scripts/generate-public-surface-assets.mjs --app shell-super-app [--target dist|cloudflare] [--require-public-origin]
4411
+
4412
+ Set each app's production URL using the contract env key, for example:
4413
+ ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP=https://example.com
4414
+
4415
+ Dynamic public routes can opt into sitemap expansion by adding a route-owned
4416
+ ${contentExpansionPolicy.defaultProviderFile} provider beside route metadata, or by adding an
4417
+ explicit provider to routes.publicSurface.contentSources. Providers should export
4418
+ an entries array, entries() function, or default entries/loader returning
4419
+ UltramodernPublicSitemapEntry[].
4420
+ \`);
4421
+ }
4422
+
4423
+ function normalizeOrigin(value) {
4424
+ if (typeof value !== 'string' || value.trim() === '') {
4425
+ return undefined;
4426
+ }
4427
+ const url = new URL(value);
4428
+ return url.origin;
4429
+ }
4430
+
4431
+ function resolveOrigin(app, requirePublicOrigin) {
4432
+ const cloudflare = app.deploy?.cloudflare ?? {};
4433
+ const publicUrlEnv = cloudflare.publicUrlEnv;
4434
+ const fromAppEnv =
4435
+ typeof publicUrlEnv === 'string' ? normalizeOrigin(process.env[publicUrlEnv]) : undefined;
4436
+ const fromGlobalEnv = normalizeOrigin(process.env.MODERN_PUBLIC_SITE_URL);
4437
+ const workersDevSubdomain = process.env.ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN;
4438
+ const fromWorkersDev =
4439
+ typeof workersDevSubdomain === 'string' && workersDevSubdomain.trim() !== ''
4440
+ ? normalizeOrigin(\`https://\${cloudflare.workerName}.\${workersDevSubdomain}.workers.dev\`)
4441
+ : undefined;
4442
+
4443
+ // SEO output (sitemap <loc>, robots Sitemap:) uses the site-wide origin
4444
+ // first; the per-app deployment URL is only a fallback.
4445
+ const configuredOrigin = fromGlobalEnv ?? fromAppEnv ?? fromWorkersDev;
4446
+ if (configuredOrigin) {
4447
+ return configuredOrigin;
4448
+ }
4449
+ if (requirePublicOrigin) {
4450
+ throw new Error(
4451
+ \`\${app.id} has public routes but no production public URL. Set \${publicUrlEnv ?? 'ULTRAMODERN_PUBLIC_URL_<APP>'} or MODERN_PUBLIC_SITE_URL.\`,
4452
+ );
4453
+ }
4454
+ return undefined;
4455
+ }
4456
+
4457
+ function ensureOutputDir(app, target) {
4458
+ const relativeDir =
4459
+ target === 'cloudflare'
4460
+ ? app.routes?.publicSurface?.cloudflareOutputRoot
4461
+ : app.routes?.publicSurface?.outputRoot;
4462
+ if (typeof relativeDir !== 'string') {
4463
+ throw new Error(\`\${app.id} public surface contract is missing outputRoot for \${target}\`);
4464
+ }
4465
+ const outputDir = path.resolve(workspaceRoot, app.path, relativeDir);
4466
+ const appRoot = path.resolve(workspaceRoot, app.path);
4467
+ if (!outputDir.startsWith(appRoot + path.sep)) {
4468
+ throw new Error(\`\${app.id} public surface output escaped the app directory\`);
4469
+ }
4470
+ fs.mkdirSync(outputDir, { recursive: true });
4471
+ return outputDir;
4472
+ }
4473
+
4474
+ function resolveAppRelativePath(app, relativePath) {
4475
+ if (
4476
+ typeof relativePath !== 'string' ||
4477
+ relativePath.trim() === '' ||
4478
+ path.isAbsolute(relativePath) ||
4479
+ relativePath.split(/[\\\\/]+/).includes('..')
4480
+ ) {
4481
+ throw new Error(app.id + ' public content source has an unsafe module path');
4482
+ }
4483
+ const appRoot = path.resolve(workspaceRoot, app.path);
4484
+ const resolved = path.resolve(appRoot, relativePath);
4485
+ if (resolved !== appRoot && !resolved.startsWith(appRoot + path.sep)) {
4486
+ throw new Error(app.id + ' public content source escaped the app directory');
4487
+ }
4488
+ return resolved;
4489
+ }
4490
+
4491
+ function normalizePublicPath(pathname) {
4492
+ if (typeof pathname !== 'string') {
4493
+ throw new Error('Public route path must be a string');
4494
+ }
4495
+ const normalised = pathname
4496
+ .trim()
4497
+ .replaceAll(/\\/+/gu, '/')
4498
+ .replace(/\\/+$/u, '');
4499
+ return normalised.length > 0 && normalised.startsWith('/')
4500
+ ? normalised
4501
+ : '/' + normalised;
4502
+ }
4503
+
4504
+ function createLocalisedPublicPath(pathname, language) {
4505
+ const publicPath = normalizePublicPath(pathname);
4506
+ return publicPath === '/' ? '/' + language : '/' + language + publicPath;
4507
+ }
4508
+
4509
+ function splitPublicPathSegments(pathname) {
4510
+ return normalizePublicPath(pathname).split('/').filter(Boolean);
4511
+ }
4512
+
4513
+ function routePathParamName(segment) {
4514
+ if (segment.startsWith(':')) {
4515
+ return segment.slice(1).replace(/[?*+]$/u, '');
4516
+ }
4517
+ if (segment.startsWith('[') && segment.endsWith(']')) {
4518
+ return segment.slice(1, -1).replace(/^\\.\\.\\./u, '').replace(/\\$$/u, '');
4519
+ }
4520
+ return undefined;
4521
+ }
4522
+
4523
+ function routeSegmentToDirectory(segment) {
4524
+ const paramName = routePathParamName(segment);
4525
+ if (paramName && segment.startsWith(':')) {
4526
+ return segment.endsWith('?') ? '[' + paramName + '$]' : '[' + paramName + ']';
4527
+ }
4528
+ return segment;
4529
+ }
4530
+
4531
+ function assertParamValue(routeId, language, paramName, value) {
4532
+ if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
4533
+ throw new Error(routeId + ' ' + language + ' sitemap param ' + paramName + ' must be a string, number, or boolean');
4534
+ }
4535
+ const text = String(value).trim();
4536
+ if (text === '' || text.includes('/')) {
4537
+ throw new Error(routeId + ' ' + language + ' sitemap param ' + paramName + ' must be a non-empty path segment');
4538
+ }
4539
+ return encodeURIComponent(text);
4540
+ }
4541
+
4542
+ function expandPublicPathPattern(routeId, language, pattern, params) {
4543
+ const segments = splitPublicPathSegments(pattern);
4544
+ if (segments.length === 0) {
4545
+ return '/';
4546
+ }
4547
+ const expanded = segments.map(segment => {
4548
+ const paramName = routePathParamName(segment);
4549
+ if (!paramName) {
4550
+ if (segment.includes('*')) {
4551
+ throw new Error(routeId + ' ' + language + ' sitemap expansion does not support wildcard path segment ' + segment);
4552
+ }
4553
+ return segment;
4554
+ }
4555
+ if (!Object.prototype.hasOwnProperty.call(params, paramName)) {
4556
+ throw new Error(routeId + ' ' + language + ' sitemap entry is missing param ' + paramName);
4557
+ }
4558
+ return assertParamValue(routeId, language, paramName, params[paramName]);
4559
+ });
4560
+ return '/' + expanded.join('/');
4561
+ }
4562
+
4563
+ function assertPlainObject(value, label) {
4564
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
4565
+ throw new Error(label + ' must be an object');
4566
+ }
4567
+ return value;
4568
+ }
4569
+
4570
+ function normalizeSitemapFields(routeId, entry) {
4571
+ const normalized = {};
4572
+ if (entry.lastModified !== undefined) {
4573
+ const lastModified = String(entry.lastModified).trim();
4574
+ if (lastModified === '' || Number.isNaN(Date.parse(lastModified))) {
4575
+ throw new Error(routeId + ' sitemap entry has invalid lastModified');
4576
+ }
4577
+ normalized.lastModified = lastModified;
4578
+ }
4579
+ if (entry.changeFrequency !== undefined) {
4580
+ const allowed = new Set(['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never']);
4581
+ if (!allowed.has(entry.changeFrequency)) {
4582
+ throw new Error(routeId + ' sitemap entry has invalid changeFrequency');
4583
+ }
4584
+ normalized.changeFrequency = entry.changeFrequency;
4585
+ }
4586
+ if (entry.priority !== undefined) {
4587
+ if (typeof entry.priority !== 'number' || entry.priority < 0 || entry.priority > 1) {
4588
+ throw new Error(routeId + ' sitemap entry priority must be a number between 0 and 1');
4589
+ }
4590
+ normalized.priority = entry.priority;
4591
+ }
4592
+ return normalized;
4593
+ }
4594
+
4595
+ function routePathToProviderDirectory(routePath) {
4596
+ const segments = splitPublicPathSegments(routePath);
4597
+ if (segments.length === 0) {
4598
+ return 'src/routes/[lang]';
4599
+ }
4600
+ return path.posix.join(
4601
+ 'src/routes/[lang]',
4602
+ ...segments.map(routeSegmentToDirectory),
4603
+ );
4604
+ }
4605
+
4606
+ function createDiscoveredContentSources(app, publicSurface) {
4607
+ const explicitRouteIds = new Set(
4608
+ (publicSurface.contentSources ?? []).map(source => source.routeId),
4609
+ );
4610
+ const discovered = [];
4611
+ for (const route of publicSurface.publicRoutes ?? []) {
4612
+ if (
4613
+ explicitRouteIds.has(route.id) ||
4614
+ !Object.values(route.localisedPaths ?? {}).some(routePath =>
4615
+ /(?:^|\\/):[^/]+|\\[[^\\]]+\\]/u.test(routePath),
4616
+ )
4617
+ ) {
4618
+ continue;
4619
+ }
4620
+ const providerModule = path.posix.join(
4621
+ routePathToProviderDirectory(route.canonicalPath),
4622
+ '${contentExpansionPolicy.defaultProviderFile}',
4623
+ );
4624
+ if (fs.existsSync(resolveAppRelativePath(app, providerModule))) {
4625
+ discovered.push({
4626
+ entryExport: 'default-or-entries',
4627
+ module: providerModule,
4628
+ routeId: route.id,
4629
+ });
4630
+ }
4631
+ }
4632
+ return discovered;
4633
+ }
4634
+
4635
+ function resolveContentSources(app, publicSurface) {
4636
+ return [
4637
+ ...(publicSurface.contentSources ?? []),
4638
+ ...createDiscoveredContentSources(app, publicSurface),
4639
+ ];
4640
+ }
4641
+
4642
+ async function loadContentSourceEntries(app, contentSource, languages) {
4643
+ if (typeof contentSource?.routeId !== 'string' || contentSource.routeId.trim() === '') {
4644
+ throw new Error(app.id + ' public content source is missing routeId');
4645
+ }
4646
+ const modulePath = resolveAppRelativePath(app, contentSource.module);
4647
+ const moduleExports = await import(pathToFileURL(modulePath).href);
4648
+ const exported = moduleExports.default ?? moduleExports.entries;
4649
+ const rawEntries =
4650
+ typeof exported === 'function'
4651
+ ? await exported({
4652
+ appId: app.id,
4653
+ languages,
4654
+ routeId: contentSource.routeId,
4655
+ })
4656
+ : exported;
4657
+ if (!Array.isArray(rawEntries)) {
4658
+ throw new Error(app.id + ' public content source for ' + contentSource.routeId + ' must export an entries array or loader');
4659
+ }
4660
+ return rawEntries;
4661
+ }
4662
+
4663
+ async function expandContentSources(app, publicSurface, languages) {
4664
+ const routesById = new Map(
4665
+ (publicSurface.publicRoutes ?? []).map(route => [route.id, route]),
4666
+ );
4667
+ const expanded = [];
4668
+ for (const contentSource of resolveContentSources(app, publicSurface)) {
4669
+ const route = routesById.get(contentSource.routeId);
4670
+ if (!route) {
4671
+ throw new Error(app.id + ' public content source references unknown route ' + contentSource.routeId);
4672
+ }
4673
+ const rawEntries = await loadContentSourceEntries(app, contentSource, languages);
4674
+ for (const rawEntry of rawEntries) {
4675
+ const entry = assertPlainObject(rawEntry, route.id + ' sitemap entry');
4676
+ if (entry.draft === true || entry.indexable === false) {
4677
+ continue;
4678
+ }
4679
+ const baseParams = assertPlainObject(entry.params, route.id + ' sitemap entry params');
4680
+ const localeParams = entry.localeParams === undefined
4681
+ ? {}
4682
+ : assertPlainObject(entry.localeParams, route.id + ' sitemap entry localeParams');
4683
+ const localeUrlPaths = {};
4684
+ for (const language of languages) {
4685
+ const params = {
4686
+ ...baseParams,
4687
+ ...(localeParams[language] ?? {}),
4688
+ };
4689
+ localeUrlPaths[language] = createLocalisedPublicPath(
4690
+ expandPublicPathPattern(route.id, language, route.localisedPaths[language], params),
4691
+ language,
4692
+ );
4693
+ }
4694
+ expanded.push({
4695
+ ...route,
4696
+ ...normalizeSitemapFields(route.id, entry),
4697
+ canonicalUrlPath: localeUrlPaths.en,
4698
+ localeUrlPaths,
4699
+ });
4700
+ }
4701
+ }
4702
+ return expanded;
4703
+ }
4704
+
4705
+ function mergeRouteEntries(routeEntries, expandedRouteEntries, languages) {
4706
+ const byKey = new Map();
4707
+ const urlPathOwners = new Map();
4708
+ for (const route of [...routeEntries, ...expandedRouteEntries]) {
4709
+ const key = route.id + ':' + route.canonicalUrlPath;
4710
+ if (byKey.has(key)) {
4711
+ throw new Error('Duplicate public sitemap route entry ' + key);
4712
+ }
4713
+ for (const language of languages) {
4714
+ const urlPath = route.localeUrlPaths?.[language];
4715
+ if (typeof urlPath !== 'string') {
4716
+ throw new Error(route.id + ' public route entry is missing ' + language + ' locale URL path');
4717
+ }
4718
+ const existingOwner = urlPathOwners.get(urlPath);
4719
+ if (existingOwner && existingOwner !== route.id) {
4720
+ throw new Error('Duplicate public sitemap URL path ' + urlPath + ' from ' + existingOwner + ' and ' + route.id);
4721
+ }
4722
+ urlPathOwners.set(urlPath, route.id);
4723
+ }
4724
+ byKey.set(key, route);
4725
+ }
4726
+ return Array.from(byKey.values()).sort(
4727
+ (left, right) =>
4728
+ left.canonicalUrlPath.localeCompare(right.canonicalUrlPath) ||
4729
+ left.id.localeCompare(right.id),
4730
+ );
4731
+ }
4732
+
4733
+ function uniqueSorted(values) {
4734
+ return Array.from(new Set(values)).sort((left, right) =>
4735
+ left.localeCompare(right),
4736
+ );
4737
+ }
4738
+
4739
+ function createConcreteUrlPaths(routeEntries, languages) {
4740
+ return uniqueSorted(
4741
+ routeEntries.flatMap(route => languages.map(language => route.localeUrlPaths[language])),
4742
+ );
4743
+ }
4744
+
4745
+ function escapeXmlText(value) {
4746
+ return value
4747
+ .replaceAll('&', '&amp;')
4748
+ .replaceAll('<', '&lt;')
4749
+ .replaceAll('>', '&gt;');
4750
+ }
4751
+
4752
+ function escapeXmlAttribute(value) {
4753
+ return escapeXmlText(value).replaceAll('"', '&quot;');
4754
+ }
4755
+
4756
+ function renderRobotsTxt(urlPaths, sitemapUrl) {
4757
+ const lines = ['User-agent: *'];
4758
+ if (urlPaths.length === 0) {
4759
+ lines.push('Disallow: /');
4760
+ } else {
4761
+ for (const urlPath of urlPaths) {
4762
+ lines.push(\`Allow: \${urlPath}$\`);
4763
+ }
4764
+ lines.push('Disallow: /');
4765
+ if (sitemapUrl) {
4766
+ lines.push(\`Sitemap: \${sitemapUrl}\`);
4767
+ }
4768
+ }
4769
+ return \`\${lines.join('\\n')}\\n\`;
4770
+ }
4771
+
4772
+ function renderSitemapXml(origin, routeEntries, languages) {
4773
+ const lines = [
4774
+ '<?xml version="1.0" encoding="UTF-8"?>',
4775
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">',
4776
+ ];
4777
+
4778
+ for (const route of routeEntries) {
4779
+ for (const language of languages) {
4780
+ lines.push(' <url>');
4781
+ lines.push(\` <loc>\${escapeXmlText(\`\${origin}\${route.localeUrlPaths[language]}\`)}</loc>\`);
4782
+ for (const alternateLanguage of languages) {
4783
+ lines.push(
4784
+ \` <xhtml:link rel="alternate" hreflang="\${alternateLanguage}" href="\${escapeXmlAttribute(
4785
+ \`\${origin}\${route.localeUrlPaths[alternateLanguage]}\`,
4786
+ )}" />\`,
4787
+ );
4788
+ }
4789
+ lines.push(
4790
+ \` <xhtml:link rel="alternate" hreflang="x-default" href="\${escapeXmlAttribute(
4791
+ \`\${origin}\${route.localeUrlPaths.en}\`,
4792
+ )}" />\`,
4793
+ );
4794
+ if (route.lastModified) {
4795
+ lines.push(\` <lastmod>\${escapeXmlText(route.lastModified)}</lastmod>\`);
4796
+ }
4797
+ if (route.changeFrequency) {
4798
+ lines.push(\` <changefreq>\${escapeXmlText(route.changeFrequency)}</changefreq>\`);
4799
+ }
4800
+ if (route.priority !== undefined) {
4801
+ lines.push(\` <priority>\${route.priority.toFixed(1).replace(/\\.0$/u, '')}</priority>\`);
4802
+ }
4803
+ lines.push(' </url>');
4804
+ }
4805
+ }
4806
+
4807
+ lines.push('</urlset>');
4808
+ return \`\${lines.join('\\n')}\\n\`;
4809
+ }
4810
+
4811
+ function renderWebManifest(app, urlPaths) {
4812
+ const startUrl = urlPaths[0];
4813
+ const manifest = {
4814
+ background_color: '#ffffff',
4815
+ categories: ['business', 'productivity'],
4816
+ display: 'standalone',
4817
+ icons: [],
4818
+ lang: 'en',
4819
+ name: app.marker?.appId ?? app.id,
4820
+ short_name: app.marker?.appId ?? app.id,
4821
+ theme_color: '#133225',
4822
+ ...(startUrl ? { scope: '/', start_url: startUrl } : {}),
4823
+ };
4824
+ return \`\${JSON.stringify(manifest, null, 2)}\\n\`;
4825
+ }
4826
+
4827
+ function removeIfExists(outputDir, fileName) {
4828
+ fs.rmSync(path.join(outputDir, fileName), { force: true });
4829
+ }
4830
+
4831
+ function writeText(outputDir, fileName, content) {
4832
+ fs.writeFileSync(path.join(outputDir, fileName), content);
4833
+ }
4834
+
4835
+ async function generatePublicSurfaceAssets(app, target, requirePublicOrigin) {
4836
+ const publicSurface = app.routes?.publicSurface ?? {};
4837
+ const languages = publicSurface.languages ?? ['en', 'cs'];
4838
+ const outputDir = ensureOutputDir(app, target);
4839
+ const shouldRequirePublicOrigin =
4840
+ requirePublicOrigin ||
4841
+ process.env.ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS === 'true';
4842
+ const routeEntries = mergeRouteEntries(
4843
+ publicSurface.routeEntries ?? [],
4844
+ await expandContentSources(app, publicSurface, languages),
4845
+ languages,
4846
+ );
4847
+ const urlPaths = createConcreteUrlPaths(routeEntries, languages);
4848
+
4849
+ if (routeEntries.length === 0) {
4850
+ writeText(outputDir, 'robots.txt', renderRobotsTxt([], undefined));
4851
+ removeIfExists(outputDir, 'sitemap.xml');
4852
+ removeIfExists(outputDir, 'site.webmanifest');
4853
+ return;
4854
+ }
4855
+
4856
+ const origin = resolveOrigin(app, shouldRequirePublicOrigin);
4857
+ if (!origin) {
4858
+ writeText(outputDir, 'robots.txt', renderRobotsTxt([], undefined));
4859
+ removeIfExists(outputDir, 'sitemap.xml');
4860
+ removeIfExists(outputDir, 'site.webmanifest');
4861
+ return;
4862
+ }
4863
+
4864
+ writeText(outputDir, 'sitemap.xml', renderSitemapXml(origin, routeEntries, languages));
4865
+ writeText(outputDir, 'site.webmanifest', renderWebManifest(app, urlPaths));
4866
+ writeText(outputDir, 'robots.txt', renderRobotsTxt(urlPaths, \`\${origin}/sitemap.xml\`));
4867
+ }
4868
+
4869
+ try {
4870
+ const args = parseArgs(process.argv.slice(2));
4871
+ if (args.help) {
4872
+ printHelp();
4873
+ process.exit(0);
4874
+ }
4875
+ const contract = readJson(contractPath);
4876
+ const app = contract.apps?.find(candidate => candidate.id === args.appId);
4877
+ if (!app) {
4878
+ throw new Error(\`Unknown app in generated contract: \${args.appId}\`);
4879
+ }
4880
+ await generatePublicSurfaceAssets(app, args.target, args.requirePublicOrigin);
4881
+ } catch (error) {
4882
+ process.stderr.write(\`[public-surface] \${error.message}\\n\`);
4883
+ process.exitCode = 1;
4884
+ }
4885
+ `;
4886
+ }
4887
+ function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
4888
+ const verticals = remotes.filter(appHasEffectApi).map((remote)=>({
4889
+ id: remote.id,
4890
+ domain: remote.domain,
4891
+ stem: remote.effectApi.stem,
4892
+ group: verticalEffectGroupName(remote),
4893
+ path: remote.directory,
4894
+ mfName: remote.mfName,
4895
+ apiPrefix: remote.effectApi.prefix,
4896
+ tailwindPrefix: tailwindPrefixForApp(remote),
4897
+ zephyrAlias: remoteDependencyAlias(remote),
4898
+ packageName: ultramodern_workspace_packageName(scope, remote.packageSuffix),
4899
+ exposes: Object.keys(remote.exposes ?? {}),
4900
+ componentPaths: Object.keys(remote.exposes ?? {}).map((expose)=>remoteComponentOutputPath(remote, expose)).filter((componentPath)=>Boolean(componentPath)),
4901
+ namespace: appI18nNamespace(remote),
4902
+ routePagePaths: createRouteOwnedI18nPaths(remote).filter((route)=>'/' !== route.canonicalPath).map((route)=>createRoutePageFilePath(remote, route.canonicalPath)),
4903
+ routeMetaPaths: createRouteOwnedI18nPaths(remote).map((route)=>createRouteMetaFilePath(remote, route.canonicalPath)),
4904
+ localisedUrls: createLocalisedUrlsMap(remote),
4905
+ verticalRefs: remote.verticalRefs ?? []
4906
+ }));
4907
+ const shellRouteMetaPaths = createRouteOwnedI18nPaths(shellApp).map((route)=>createRouteMetaFilePath(shellApp, route.canonicalPath));
4908
+ const shellNamespace = appI18nNamespace(shellApp);
4909
+ const oldRemotePaths = [
4910
+ 'apps/remotes'
4911
+ ];
4912
+ 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';
4913
+ 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';
4914
+ 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';
4915
+ const expectedCloudflareSecurity = createCloudflareSecurityContract();
4916
+ const contentExpansionPolicy = createPublicSurfaceContentExpansionPolicy();
4917
+ const robotsPolicy = createPublicHeadRobotsPolicy();
4918
+ const qualityGates = createPublicWebsiteQualityGateContract();
4919
+ return `import { execFileSync } from 'node:child_process';
4920
+ import fs from 'node:fs';
4921
+ import path from 'node:path';
4922
+
4923
+ const root = process.cwd();
4924
+ const packageScope = '${scope}';
4925
+ const expectedPnpmVersion = '${PNPM_VERSION}';
4926
+ const tailwindEnabled = ${JSON.stringify(enableTailwind)};
4927
+ const fullStackVerticals = ${JSON.stringify(verticals, null, 2)};
4928
+ const shellNamespace = ${JSON.stringify(shellNamespace)};
4929
+ const oldRemotePaths = ${JSON.stringify(oldRemotePaths, null, 2)};
4295
4930
  const expectedBuildScript = ${JSON.stringify(expectedBuildScript)};
4296
4931
  const expectedCloudflareBuildScript = ${JSON.stringify(expectedCloudflareBuildScript)};
4297
4932
  const expectedCloudflareDeployScript = ${JSON.stringify(expectedCloudflareDeployScript)};
4298
4933
  const expectedCloudflareSecurity = ${JSON.stringify(expectedCloudflareSecurity, null, 2)};
4299
- const publicSurfaceRequiredAssetPaths = ${JSON.stringify([
4300
- ...publicSurfaceRequiredAssetPaths
4301
- ], null, 2)};
4302
- const publicSurfaceOptionalAssetPaths = ${JSON.stringify([
4303
- ...publicSurfaceOptionalAssetPaths
4934
+ const publicSurfaceManagedSourceAssetPaths = ${JSON.stringify([
4935
+ ...publicSurfaceManagedSourceAssetPaths
4304
4936
  ], null, 2)};
4305
4937
  const expectedModernPackageSpecifier = packageName => {
4306
4938
  if (packageSource.strategy === 'workspace') {
@@ -4326,19 +4958,67 @@ const assertNotExists = relativePath => {
4326
4958
  assert(!fs.existsSync(path.join(root, relativePath)), \`Unexpected \${relativePath}\`);
4327
4959
  };
4328
4960
  const assertPublicSurfaceAssets = (appPath, publicRoutes) => {
4329
- const robots = readText(\`\${appPath}/config/public/robots.txt\`);
4330
- if ((publicRoutes ?? []).length === 0) {
4331
- assert(robots.includes('Disallow: /'), \`\${appPath} robots.txt must disallow crawling when no public routes exist\`);
4332
- for (const relativePath of publicSurfaceOptionalAssetPaths) {
4333
- assertNotExists(\`\${appPath}/\${relativePath}\`);
4334
- }
4335
- return;
4961
+ for (const relativePath of publicSurfaceManagedSourceAssetPaths) {
4962
+ assertNotExists(\`\${appPath}/\${relativePath}\`);
4963
+ }
4964
+ void publicRoutes;
4965
+ };
4966
+ const assertPublicSurfaceContract = (appId, publicSurface) => {
4967
+ assert(publicSurface?.artifactLifecycle === 'build-and-deploy-output', \`\${appId} public surface artifacts must be build/deploy outputs\`);
4968
+ assert(publicSurface?.generator === 'scripts/generate-public-surface-assets.mjs', \`\${appId} public surface generator script is incorrect\`);
4969
+ assert(publicSurface?.outputRoot === 'dist/public', \`\${appId} public surface dist outputRoot is incorrect\`);
4970
+ assert(publicSurface?.cloudflareOutputRoot === '.output/public', \`\${appId} public surface Cloudflare outputRoot is incorrect\`);
4971
+ assert(!('staticRoot' in (publicSurface ?? {})), \`\${appId} public surface must not point at source config/public\`);
4972
+ assert((publicSurface?.files ?? []).includes('robots.txt'), \`\${appId} public surface must always emit robots.txt\`);
4973
+ assert(publicSurface?.contentExpansion?.authoring === 'route-owned-esm-provider', \`\${appId} public content expansion authoring is incorrect\`);
4974
+ assert(publicSurface?.contentExpansion?.defaultProviderFile === '${contentExpansionPolicy.defaultProviderFile}', \`\${appId} public content expansion provider file is incorrect\`);
4975
+ assert(publicSurface?.contentExpansion?.draftPolicy === '${contentExpansionPolicy.draftPolicy}', \`\${appId} public content expansion draft policy is incorrect\`);
4976
+ assert(publicSurface?.contentExpansion?.indexablePolicy === '${contentExpansionPolicy.indexablePolicy}', \`\${appId} public content expansion indexable policy is incorrect\`);
4977
+ assert(Array.isArray(publicSurface?.contentSources), \`\${appId} public content sources must be an array\`);
4978
+ if ((publicSurface?.publicRoutes ?? []).length === 0) {
4979
+ assert(!(publicSurface?.files ?? []).includes('sitemap.xml'), \`\${appId} private public surface must omit sitemap.xml\`);
4980
+ assert(!(publicSurface?.files ?? []).includes('site.webmanifest'), \`\${appId} private public surface must omit site.webmanifest\`);
4981
+ } else {
4982
+ assert((publicSurface?.files ?? []).includes('sitemap.xml'), \`\${appId} public surface must emit sitemap.xml when public routes exist\`);
4983
+ assert((publicSurface?.files ?? []).includes('site.webmanifest'), \`\${appId} public surface must emit site.webmanifest when public routes exist\`);
4984
+ }
4985
+ };
4986
+ const assertPublicHeadContract = (appId, publicHead, headModule) => {
4987
+ assert(publicHead?.generator === './src/routes/ultramodern-route-head', \`\${appId} public head generator is incorrect\`);
4988
+ assert(publicHead?.renderer === '@modern-js/runtime/head Helmet', \`\${appId} public head renderer is incorrect\`);
4989
+ assert(publicHead?.ssr === true, \`\${appId} public head must be SSR-rendered\`);
4990
+ assert(publicHead?.title?.source === 'route.titleKey', \`\${appId} public head title must come from route metadata\`);
4991
+ assert(publicHead?.description?.source === 'route.descriptionKey', \`\${appId} public head description must come from route metadata\`);
4992
+ assert(publicHead?.canonical?.publicIndexableOnly === true, \`\${appId} canonical links must be public/indexable only\`);
4993
+ assert(publicHead?.structuredData?.sanitizesHtmlOpenBracket === true, \`\${appId} structured data must sanitize HTML open brackets\`);
4994
+ assert(publicHead?.privateRouteRobots === '${robotsPolicy.privateRouteRobots}', \`\${appId} private route robots policy is incorrect\`);
4995
+ for (const snippet of [
4996
+ "from '@modern-js/runtime/head'",
4997
+ '<title>{title}</title>',
4998
+ 'name="description"',
4999
+ 'name="robots"',
5000
+ 'rel="canonical"',
5001
+ 'rel="alternate"',
5002
+ 'property="og:title"',
5003
+ 'property="og:description"',
5004
+ 'name="twitter:card"',
5005
+ 'application/ld+json',
5006
+ "replaceAll('<', '\\\\\\\\u003c')",
5007
+ ]) {
5008
+ assert(headModule.includes(snippet), \`\${appId} route head module is missing \${snippet}\`);
4336
5009
  }
4337
- const sitemap = readText(\`\${appPath}/config/public/sitemap.xml\`);
4338
- const manifest = readJson(\`\${appPath}/config/public/site.webmanifest\`);
4339
- assert(!sitemap.includes('<lastmod>'), \`\${appPath} sitemap must omit build-time lastmod values\`);
4340
- assert(typeof manifest.name === 'string' && manifest.name.length > 0, \`\${appPath} web manifest must include a safe app name\`);
4341
- assert(typeof manifest.start_url === 'string' && manifest.start_url.startsWith('/'), \`\${appPath} web manifest start_url must be a public route path\`);
5010
+ };
5011
+ const assertCloudflareQualityGates = (appId, qualityGates) => {
5012
+ assert(qualityGates?.publicRoutes?.requireSitemapWhenPresent === true, \`\${appId} quality gates must require sitemap for public routes\`);
5013
+ assert(qualityGates?.publicRoutes?.requireRobotsSitemapConsistency === true, \`\${appId} quality gates must require robots/sitemap consistency\`);
5014
+ assert(qualityGates?.statusCodes?.unknownRouteStatus === 404, \`\${appId} quality gates must require 404 unknown routes\`);
5015
+ assert(qualityGates?.indexing?.previewNoindex === true, \`\${appId} quality gates must require preview noindex\`);
5016
+ assert(qualityGates?.indexing?.productionPublicRoutesIndexable === true, \`\${appId} quality gates must require production public routes to be indexable\`);
5017
+ assert(qualityGates?.assets?.cssPreloadRequired === true, \`\${appId} quality gates must require CSS preload evidence\`);
5018
+ assert(qualityGates?.assets?.sourcemapsPubliclyReferenced === false, \`\${appId} quality gates must reject public sourcemap references\`);
5019
+ assert(typeof qualityGates?.budgets?.ssrHtmlMaxBytes === 'number', \`\${appId} quality gates must define SSR HTML byte budget\`);
5020
+ assert(typeof qualityGates?.budgets?.mfManifestMaxBytes === 'number', \`\${appId} quality gates must define MF manifest byte budget\`);
5021
+ assert(qualityGates?.csp?.finalMode === '${qualityGates.csp.finalMode}', \`\${appId} CSP final mode decision is missing\`);
4342
5022
  };
4343
5023
  const expectedWorkerName = packageSuffix => \`\${packageScope}-\${packageSuffix}\`.slice(0, 63);
4344
5024
  const expectedChunkLoadingGlobal = mfName =>
@@ -4400,6 +5080,7 @@ const requiredPaths = [
4400
5080
  'scripts/assert-mf-types.mjs',
4401
5081
  'scripts/bootstrap-agent-skills.mjs',
4402
5082
  'scripts/check-ultramodern-i18n-boundaries.mjs',
5083
+ 'scripts/generate-public-surface-assets.mjs',
4403
5084
  'scripts/proof-cloudflare-version.mjs',
4404
5085
  'scripts/setup-agent-reference-repos.mjs',
4405
5086
  'apps/shell-super-app/package.json',
@@ -4414,11 +5095,10 @@ const requiredPaths = [
4414
5095
  \`apps/shell-super-app/locales/cs/\${shellNamespace}.json\`,
4415
5096
  'apps/shell-super-app/src/routes/index.css',
4416
5097
  'apps/shell-super-app/src/routes/layout.tsx',
5098
+ 'apps/shell-super-app/src/routes/ultramodern-route-head.tsx',
4417
5099
  'apps/shell-super-app/src/routes/ultramodern-route-metadata.ts',
4418
5100
  'apps/shell-super-app/src/routes/[lang]/page.tsx',
4419
- ...publicSurfaceRequiredAssetPaths.map(
4420
- relativePath => \`apps/shell-super-app/\${relativePath}\`,
4421
- ),
5101
+ ...${JSON.stringify(shellRouteMetaPaths, null, 2)},
4422
5102
  'packages/shared-contracts/src/index.ts',
4423
5103
  'packages/shared-design-tokens/src/index.ts',
4424
5104
  'packages/shared-design-tokens/src/tokens.css',
@@ -4443,12 +5123,11 @@ for (const vertical of fullStackVerticals) {
4443
5123
  \`\${vertical.path}/locales/cs/\${vertical.namespace}.json\`,
4444
5124
  \`\${vertical.path}/src/routes/index.css\`,
4445
5125
  \`\${vertical.path}/src/routes/layout.tsx\`,
5126
+ \`\${vertical.path}/src/routes/ultramodern-route-head.tsx\`,
4446
5127
  \`\${vertical.path}/src/routes/ultramodern-route-metadata.ts\`,
4447
5128
  \`\${vertical.path}/src/routes/[lang]/page.tsx\`,
4448
- ...publicSurfaceRequiredAssetPaths.map(
4449
- relativePath => \`\${vertical.path}/\${relativePath}\`,
4450
- ),
4451
5129
  ...vertical.routePagePaths,
5130
+ ...vertical.routeMetaPaths,
4452
5131
  );
4453
5132
  }
4454
5133
 
@@ -4569,6 +5248,10 @@ assert(generatedContract.cssFederation?.sharedDesignTokens?.ssr?.firstPaintRequi
4569
5248
 
4570
5249
  const shellPackage = readJson('apps/shell-super-app/package.json');
4571
5250
  const shellModernConfig = readText('apps/shell-super-app/modern.config.ts');
5251
+ const shellRouteHead = readText('apps/shell-super-app/src/routes/ultramodern-route-head.tsx');
5252
+ const shellRouteMetadata = readText('apps/shell-super-app/src/routes/ultramodern-route-metadata.ts');
5253
+ assert(shellRouteMetadata.includes('@generated by @modern-js/create'), 'Shell route metadata compatibility manifest must be marked generated');
5254
+ assert(shellRouteMetadata.includes("authoring: 'colocated-route-meta'"), 'Shell route metadata manifest must advertise colocated authoring');
4572
5255
  const expectedZephyrDependencies = Object.fromEntries(
4573
5256
  fullStackVerticals.map(vertical => [
4574
5257
  vertical.zephyrAlias,
@@ -4591,10 +5274,18 @@ assert(shellContract?.deploy?.cloudflare?.publicUrlEnv === 'ULTRAMODERN_PUBLIC_U
4591
5274
  assert(shellContract?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell Cloudflare compatibilityDate is incorrect');
4592
5275
  assert(JSON.stringify(shellContract?.deploy?.cloudflare?.compatibilityFlags) === JSON.stringify(expectedCloudflareCompatibilityFlags), 'Shell Cloudflare compatibility flags are incorrect');
4593
5276
  assert(JSON.stringify(shellContract?.deploy?.cloudflare?.security) === JSON.stringify(expectedCloudflareSecurity), 'Shell Cloudflare security contract is incorrect');
5277
+ assertCloudflareQualityGates('shell-super-app', shellContract?.deploy?.cloudflare?.qualityGates);
4594
5278
  assert(shellContract?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell worker compatibilityDate is incorrect');
4595
5279
  assert(shellContract?.deploy?.worker?.name === expectedWorkerName('shell-super-app'), 'Shell worker name is incorrect');
4596
5280
  assert(shellModernConfig.includes("const cloudflareWorkerName = '" + expectedWorkerName('shell-super-app') + "'"), 'Shell modern.config.ts must define the Cloudflare worker name');
4597
5281
  assert(shellModernConfig.includes('name: cloudflareWorkerName'), 'Shell modern.config.ts must wire deploy.worker.name');
5282
+ assert(shellModernConfig.includes('const assetPrefix ='), 'Shell modern.config.ts must derive a dedicated asset prefix');
5283
+ assert(shellModernConfig.includes("assetPrefix: '/'"), 'Shell modern.config.ts must keep dev assets origin-relative');
5284
+ assert(shellModernConfig.includes('assetPrefix,'), 'Shell modern.config.ts must wire output.assetPrefix to the derived asset prefix');
5285
+ assert(shellContract?.config?.dev?.assetPrefix === '/', 'Shell dev asset prefix must stay origin-relative');
5286
+ assert(shellContract?.config?.output?.assetPrefix?.default === '/', 'Shell asset prefix must default to origin-relative paths');
5287
+ 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');
5288
+ 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');
4598
5289
  assert(shellContract?.config?.rspack?.output?.uniqueName === 'shellSuperApp', 'Shell Rspack uniqueName is incorrect');
4599
5290
  assert(shellContract?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal('shellSuperApp'), 'Shell Rspack chunkLoadingGlobal is incorrect');
4600
5291
  assert(topology.shell?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell topology Cloudflare workerName is incorrect');
@@ -4609,11 +5300,15 @@ assert(shellContract?.styling?.federation?.assets?.shared?.some(asset => asset.e
4609
5300
  assert(shellContract?.styling?.federation?.dedupe?.duplicateBaseStylesAllowed === false, 'Shell CSS contract must forbid duplicated base styles');
4610
5301
  assert(shellContract?.styling?.federation?.ssr?.firstPaintRequired === true, 'Shell CSS must be required for SSR first paint');
4611
5302
  assert(shellContract?.routes?.privateByDefault === true, 'Shell routes must be private by default');
5303
+ assert(shellContract?.routes?.metadataAuthoring === 'colocated-route-meta', 'Shell route metadata authoring mode is incorrect');
5304
+ assert(shellContract?.routes?.generatedManifest === true, 'Shell route metadata manifest must be generated');
4612
5305
  assert(shellContract?.routes?.publicnessDefault === 'private-app-screen', 'Shell route publicness default is incorrect');
4613
5306
  assert(JSON.stringify(shellContract?.routes?.publicRoutes ?? []) === '[]', 'Shell must not expose generated public routes by default');
5307
+ assertPublicHeadContract('shell-super-app', shellContract?.routes?.publicHead, shellRouteHead);
5308
+ assertPublicSurfaceContract('shell-super-app', shellContract?.routes?.publicSurface);
4614
5309
  assert(
4615
- (shellContract?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen'),
4616
- 'Shell owned routes must be non-indexable private app screens by default',
5310
+ (shellContract?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen' && typeof route.descriptionKey === 'string'),
5311
+ 'Shell owned routes must be non-indexable private app screens by default and include description keys',
4617
5312
  );
4618
5313
  assertPublicSurfaceAssets('apps/shell-super-app', shellContract?.routes?.publicRoutes ?? []);
4619
5314
  assert(
@@ -4627,6 +5322,10 @@ assert(!('effectServices' in topology), 'Default APIs must be vertical-owned, no
4627
5322
  for (const vertical of fullStackVerticals) {
4628
5323
  const packageJson = readJson(\`\${vertical.path}/package.json\`);
4629
5324
  const modernConfig = readText(\`\${vertical.path}/modern.config.ts\`);
5325
+ const routeHead = readText(\`\${vertical.path}/src/routes/ultramodern-route-head.tsx\`);
5326
+ const routeMetadata = readText(\`\${vertical.path}/src/routes/ultramodern-route-metadata.ts\`);
5327
+ assert(routeMetadata.includes('@generated by @modern-js/create'), \`\${vertical.id} route metadata compatibility manifest must be marked generated\`);
5328
+ assert(routeMetadata.includes("authoring: 'colocated-route-meta'"), \`\${vertical.id} route metadata manifest must advertise colocated authoring\`);
4630
5329
  assert(packageJson.name === vertical.packageName, \`\${vertical.id} package name is incorrect\`);
4631
5330
  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\`);
4632
5331
  assert(packageJson.scripts?.['cloudflare:proof']?.includes(\`--app \${vertical.id}\`), \`\${vertical.id} must expose cloudflare:proof\`);
@@ -4659,16 +5358,23 @@ for (const vertical of fullStackVerticals) {
4659
5358
  assert(contractEntry?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, \`\${vertical.id} Cloudflare compatibilityDate is incorrect\`);
4660
5359
  assert(JSON.stringify(contractEntry?.deploy?.cloudflare?.compatibilityFlags) === JSON.stringify(expectedCloudflareCompatibilityFlags), \`\${vertical.id} Cloudflare compatibility flags are incorrect\`);
4661
5360
  assert(JSON.stringify(contractEntry?.deploy?.cloudflare?.security) === JSON.stringify(expectedCloudflareSecurity), \`\${vertical.id} Cloudflare security contract is incorrect\`);
5361
+ assertCloudflareQualityGates(vertical.id, contractEntry?.deploy?.cloudflare?.qualityGates);
4662
5362
  assert(contractEntry?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, \`\${vertical.id} worker compatibilityDate is incorrect\`);
4663
5363
  assert(contractEntry?.deploy?.worker?.name === expectedWorkerName(vertical.id), \`\${vertical.id} worker name is incorrect\`);
4664
5364
  assert(modernConfig.includes("const cloudflareWorkerName = '" + expectedWorkerName(vertical.id) + "'"), \`\${vertical.id} modern.config.ts must define the Cloudflare worker name\`);
4665
5365
  assert(modernConfig.includes('name: cloudflareWorkerName'), \`\${vertical.id} modern.config.ts must wire deploy.worker.name\`);
5366
+ assert(modernConfig.includes('const assetPrefix ='), \`\${vertical.id} modern.config.ts must derive a dedicated asset prefix\`);
5367
+ assert(modernConfig.includes("assetPrefix: '/'"), \`\${vertical.id} modern.config.ts must keep dev assets origin-relative\`);
5368
+ assert(modernConfig.includes('assetPrefix,'), \`\${vertical.id} modern.config.ts must wire output.assetPrefix to the derived asset prefix\`);
5369
+ assert(contractEntry?.config?.dev?.assetPrefix === '/', \`\${vertical.id} dev asset prefix must stay origin-relative\`);
5370
+ assert(contractEntry?.config?.output?.assetPrefix?.default === '/', \`\${vertical.id} asset prefix must default to origin-relative paths\`);
5371
+ 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\`);
4666
5372
  assert(contractEntry?.deploy?.cloudflare?.routes?.effectReadiness === \`\${vertical.apiPrefix}/effect/\${vertical.stem}/readiness\`, \`\${vertical.id} Cloudflare proof readiness route is incorrect\`);
4667
5373
  assert(contractEntry?.config?.rspack?.output?.uniqueName === vertical.mfName, \`\${vertical.id} Rspack uniqueName is incorrect\`);
4668
5374
  assert(contractEntry?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal(vertical.mfName), \`\${vertical.id} Rspack chunkLoadingGlobal is incorrect\`);
4669
5375
  assert(contractEntry?.moduleFederation?.name === vertical.mfName, \`\${vertical.id} MF name is incorrect\`);
4670
5376
  assert(JSON.stringify(contractEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), \`\${vertical.id} MF exposes are incorrect\`);
4671
- assert(contractEntry?.moduleFederation?.dts?.compilerInstance === '--package typescript -- tsc', \`\${vertical.id} must keep mandatory DTS compiler\`);
5377
+ assert(contractEntry?.moduleFederation?.dts?.compilerInstance === 'tsgo', \`\${vertical.id} must keep mandatory DTS compiler\`);
4672
5378
  assert(JSON.stringify(contractEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), \`\${vertical.id} MF verticalRefs are incorrect\`);
4673
5379
  assert(
4674
5380
  JSON.stringify((contractEntry?.moduleFederation?.remotes ?? []).map(remote => remote.id)) ===
@@ -4688,13 +5394,17 @@ for (const vertical of fullStackVerticals) {
4688
5394
  \`\${vertical.id} localisedUrls must come from route metadata\`,
4689
5395
  );
4690
5396
  assert(contractEntry?.routes?.source === 'route-owned', \`\${vertical.id} routes must be route-owned\`);
5397
+ assert(contractEntry?.routes?.metadataAuthoring === 'colocated-route-meta', \`\${vertical.id} route metadata authoring mode is incorrect\`);
5398
+ assert(contractEntry?.routes?.generatedManifest === true, \`\${vertical.id} route metadata manifest must be generated\`);
4691
5399
  assert(contractEntry?.routes?.metadataExport === './src/routes/ultramodern-route-metadata', \`\${vertical.id} route metadata export is incorrect\`);
4692
5400
  assert(contractEntry?.routes?.privateByDefault === true, \`\${vertical.id} routes must be private by default\`);
4693
5401
  assert(contractEntry?.routes?.publicnessDefault === 'private-app-screen', \`\${vertical.id} route publicness default is incorrect\`);
4694
5402
  assert(JSON.stringify(contractEntry?.routes?.publicRoutes ?? []) === '[]', \`\${vertical.id} must not expose generated public routes by default\`);
5403
+ assertPublicHeadContract(vertical.id, contractEntry?.routes?.publicHead, routeHead);
5404
+ assertPublicSurfaceContract(vertical.id, contractEntry?.routes?.publicSurface);
4695
5405
  assert(
4696
- (contractEntry?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen'),
4697
- \`\${vertical.id} owned routes must be non-indexable private app screens by default\`,
5406
+ (contractEntry?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen' && typeof route.descriptionKey === 'string'),
5407
+ \`\${vertical.id} owned routes must be non-indexable private app screens by default and include description keys\`,
4698
5408
  );
4699
5409
  assertPublicSurfaceAssets(vertical.path, contractEntry?.routes?.publicRoutes ?? []);
4700
5410
  assert(contractEntry?.styling?.federation?.owner?.id === vertical.id, \`\${vertical.id} CSS federation owner is missing\`);
@@ -4711,84 +5421,28 @@ for (const vertical of fullStackVerticals) {
4711
5421
  const topologyEntry = topology.verticals?.find(verticalEntry => verticalEntry.id === vertical.id);
4712
5422
  assert(topologyEntry?.kind === 'vertical', \`\${vertical.id} topology kind is incorrect\`);
4713
5423
  assert(topologyEntry?.package === vertical.packageName, \`\${vertical.id} topology package is incorrect\`);
4714
- assert(topologyEntry?.cloudflare?.workerName === expectedWorkerName(vertical.id), \`\${vertical.id} topology Cloudflare workerName is incorrect\`);
4715
- assert(topologyEntry?.moduleFederation?.name === vertical.mfName, \`\${vertical.id} topology MF name is incorrect\`);
4716
- assert(JSON.stringify(topologyEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), \`\${vertical.id} topology exposes are incorrect\`);
4717
- assert(JSON.stringify(topologyEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), \`\${vertical.id} topology verticalRefs are incorrect\`);
4718
- assert(topologyEntry?.api?.effect?.bff?.prefix === vertical.apiPrefix, \`\${vertical.id} topology API prefix is incorrect\`);
4719
- assert(topologyEntry?.api?.effect?.serverEntry === \`\${vertical.path}/api/effect/index.ts\`, \`\${vertical.id} topology server entry is incorrect\`);
4720
- assert(topologyEntry?.api?.effect?.readiness?.endpoint === \`/effect/\${vertical.stem}/readiness\`, \`\${vertical.id} topology readiness endpoint is incorrect\`);
4721
- assert(Object.keys(topologyEntry?.api?.effect?.domainOperations ?? {}).length >= 3, \`\${vertical.id} topology domain operations are missing\`);
4722
-
4723
- assert(ownership.owners?.some(owner => owner.id === vertical.id && owner.path === vertical.path), \`\${vertical.id} ownership entry is missing\`);
4724
- assert(overlay.ports?.[vertical.id], \`\${vertical.id} development port is missing\`);
4725
- assert(overlay.manifests?.[vertical.id]?.includes('/mf-manifest.json'), \`\${vertical.id} development manifest is missing\`);
4726
- assert(overlay.apis?.[vertical.id]?.endsWith(vertical.apiPrefix), \`\${vertical.id} development API URL is missing\`);
4727
- }
4728
-
4729
- console.log('UltraModern workspace scaffold validated');
4730
- `;
4731
- }
4732
- function createCloudflareVersionProofScript() {
4733
- return `#!/usr/bin/env node
4734
- import fs from 'node:fs';
4735
- import path from 'node:path';
4736
- import { fileURLToPath } from 'node:url';
4737
-
4738
- const workspaceRoot = path.resolve(
4739
- path.dirname(fileURLToPath(import.meta.url)),
4740
- '..',
4741
- );
4742
- const contractPath = path.join(
4743
- workspaceRoot,
4744
- '.modernjs/ultramodern-generated-contract.json',
4745
- );
4746
- const defaultOut = path.join(
4747
- workspaceRoot,
4748
- '.codex/reports/cloudflare-version-proof/public-url-proof.json',
4749
- );
4750
-
4751
- function readJson(filePath) {
4752
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
4753
- }
4754
-
4755
- function parseArgs(argv) {
4756
- const parsed = {
4757
- appId: undefined,
4758
- out: defaultOut,
4759
- requirePublicUrls: false,
4760
- };
4761
-
4762
- for (let index = 0; index < argv.length; index += 1) {
4763
- const arg = argv[index];
4764
- if (arg === '--app') {
4765
- parsed.appId = argv[index + 1];
4766
- index += 1;
4767
- } else if (arg === '--out') {
4768
- parsed.out = argv[index + 1];
4769
- index += 1;
4770
- } else if (arg === '--require-public-urls') {
4771
- parsed.requirePublicUrls = true;
4772
- } else if (arg === '--help' || arg === '-h') {
4773
- parsed.help = true;
4774
- } else {
4775
- throw new Error(\`Unknown argument: \${arg}\`);
4776
- }
4777
- }
5424
+ assert(topologyEntry?.cloudflare?.workerName === expectedWorkerName(vertical.id), \`\${vertical.id} topology Cloudflare workerName is incorrect\`);
5425
+ assert(topologyEntry?.moduleFederation?.name === vertical.mfName, \`\${vertical.id} topology MF name is incorrect\`);
5426
+ assert(JSON.stringify(topologyEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), \`\${vertical.id} topology exposes are incorrect\`);
5427
+ assert(JSON.stringify(topologyEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), \`\${vertical.id} topology verticalRefs are incorrect\`);
5428
+ assert(topologyEntry?.api?.effect?.bff?.prefix === vertical.apiPrefix, \`\${vertical.id} topology API prefix is incorrect\`);
5429
+ assert(topologyEntry?.api?.effect?.serverEntry === \`\${vertical.path}/api/effect/index.ts\`, \`\${vertical.id} topology server entry is incorrect\`);
5430
+ assert(topologyEntry?.api?.effect?.readiness?.endpoint === \`/effect/\${vertical.stem}/readiness\`, \`\${vertical.id} topology readiness endpoint is incorrect\`);
5431
+ assert(Object.keys(topologyEntry?.api?.effect?.domainOperations ?? {}).length >= 3, \`\${vertical.id} topology domain operations are missing\`);
4778
5432
 
4779
- return parsed;
5433
+ assert(ownership.owners?.some(owner => owner.id === vertical.id && owner.path === vertical.path), \`\${vertical.id} ownership entry is missing\`);
5434
+ assert(overlay.ports?.[vertical.id], \`\${vertical.id} development port is missing\`);
5435
+ assert(overlay.manifests?.[vertical.id]?.includes('/mf-manifest.json'), \`\${vertical.id} development manifest is missing\`);
5436
+ assert(overlay.apis?.[vertical.id]?.endsWith(vertical.apiPrefix), \`\${vertical.id} development API URL is missing\`);
4780
5437
  }
4781
5438
 
4782
- function printHelp() {
4783
- process.stdout.write(\`Usage:
4784
- node scripts/proof-cloudflare-version.mjs [--app workspace] [--out evidence.json] [--require-public-urls]
4785
-
4786
- Set each app's public URL using the contract env key, for example:
4787
- ULTRAMODERN_PUBLIC_URL_WORKSPACE=https://workspace.example.workers.dev
4788
- \`);
5439
+ console.log('UltraModern workspace scaffold validated');
5440
+ `;
4789
5441
  }
4790
-
4791
- function joinUrl(baseUrl, routePath) {
5442
+ function createCloudflareProofHelperScript() {
5443
+ const robotsPolicy = createPublicHeadRobotsPolicy();
5444
+ const qualityGates = createPublicWebsiteQualityGateContract();
5445
+ return `function joinUrl(baseUrl, routePath) {
4792
5446
  return new URL(routePath, baseUrl.endsWith('/') ? baseUrl : \`\${baseUrl}/\`);
4793
5447
  }
4794
5448
 
@@ -4802,6 +5456,8 @@ async function fetchText(url) {
4802
5456
  ok: response.ok,
4803
5457
  status: response.status,
4804
5458
  accessControlAllowOrigin: response.headers.get('access-control-allow-origin'),
5459
+ cacheControl: response.headers.get('cache-control'),
5460
+ contentLength: response.headers.get('content-length'),
4805
5461
  contentSecurityPolicy: response.headers.get('content-security-policy'),
4806
5462
  contentSecurityPolicyReportOnly: response.headers.get('content-security-policy-report-only'),
4807
5463
  contentType: response.headers.get('content-type'),
@@ -4860,6 +5516,52 @@ function assert(condition, message) {
4860
5516
  }
4861
5517
  }
4862
5518
 
5519
+ function responseByteLength(response) {
5520
+ return Buffer.byteLength(response.body, 'utf8');
5521
+ }
5522
+
5523
+ function assertByteBudget(evidence, app, response, options) {
5524
+ const bytes = responseByteLength(response);
5525
+ const passed = bytes <= options.maxBytes;
5526
+ evidence.assertions.push({
5527
+ type: 'byte-budget',
5528
+ label: options.label,
5529
+ route: options.route,
5530
+ actualBytes: bytes,
5531
+ maxBytes: options.maxBytes,
5532
+ status: passed ? 'pass' : 'fail',
5533
+ });
5534
+ assert(
5535
+ passed,
5536
+ app.id + ' ' + options.route + ' exceeds ' + options.label + ' byte budget: ' + bytes + ' > ' + options.maxBytes,
5537
+ );
5538
+ }
5539
+
5540
+ function assertContentType(evidence, app, response, options) {
5541
+ const actual = response.contentType ?? '';
5542
+ const passed = actual.toLowerCase().includes(options.includes);
5543
+ evidence.assertions.push({
5544
+ type: 'content-type',
5545
+ route: options.route,
5546
+ expectedIncludes: options.includes,
5547
+ actual,
5548
+ status: passed ? 'pass' : 'fail',
5549
+ });
5550
+ assert(passed, app.id + ' ' + options.route + ' content-type must include ' + options.includes);
5551
+ }
5552
+
5553
+ function assertCacheControl(evidence, app, response, options) {
5554
+ const actual = response.cacheControl ?? '';
5555
+ const passed = options.required === false || actual.trim() !== '';
5556
+ evidence.assertions.push({
5557
+ type: 'cache-control',
5558
+ route: options.route,
5559
+ actual,
5560
+ status: passed ? 'pass' : 'fail',
5561
+ });
5562
+ assert(passed, app.id + ' ' + options.route + ' is missing cache-control');
5563
+ }
5564
+
4863
5565
  function matchesPreviewHostname(hostname, pattern) {
4864
5566
  const normalizedHostname = hostname.toLowerCase();
4865
5567
  const normalizedPattern = String(pattern || '').toLowerCase();
@@ -4984,15 +5686,382 @@ function assertCloudflareSecurity(evidence, app, response, route, publicUrl, opt
4984
5686
  type: 'security-noindex',
4985
5687
  route,
4986
5688
  actual: response.xRobotsTag,
4987
- status: response.xRobotsTag === 'noindex, nofollow' ? 'pass' : 'fail',
5689
+ status: response.xRobotsTag === '${robotsPolicy.privateRouteRobots}' ? 'pass' : 'fail',
4988
5690
  });
4989
5691
  assert(
4990
- response.xRobotsTag === 'noindex, nofollow',
5692
+ response.xRobotsTag === '${robotsPolicy.privateRouteRobots}',
4991
5693
  \`\${app.id} \${route} is missing noindex X-Robots-Tag\`,
4992
5694
  );
4993
5695
  }
4994
5696
  }
4995
5697
 
5698
+ function collectStringValues(value, results = []) {
5699
+ if (typeof value === 'string') {
5700
+ results.push(value);
5701
+ return results;
5702
+ }
5703
+ if (Array.isArray(value)) {
5704
+ for (const item of value) {
5705
+ collectStringValues(item, results);
5706
+ }
5707
+ return results;
5708
+ }
5709
+ if (value && typeof value === 'object') {
5710
+ for (const item of Object.values(value)) {
5711
+ collectStringValues(item, results);
5712
+ }
5713
+ }
5714
+ return results;
5715
+ }
5716
+
5717
+ function assertNoPublicSourcemapRefs(evidence, app, manifestJson) {
5718
+ const sourcemapRefs = collectStringValues(manifestJson).filter(value =>
5719
+ /\\.map(?:$|[?#])/u.test(value),
5720
+ );
5721
+ evidence.assertions.push({
5722
+ type: 'sourcemap-policy',
5723
+ actual: sourcemapRefs,
5724
+ status: sourcemapRefs.length === 0 ? 'pass' : 'fail',
5725
+ });
5726
+ assert(
5727
+ sourcemapRefs.length === 0,
5728
+ app.id + ' MF manifest must not publicly reference sourcemaps',
5729
+ );
5730
+ }
5731
+
5732
+ function extractPreloadStyleUrls(linkHeader, publicUrl) {
5733
+ const urls = [];
5734
+ for (const match of String(linkHeader || '').matchAll(/<([^>]+)>\\s*;[^,]*rel=preload[^,]*as=style/giu)) {
5735
+ urls.push(String(joinUrl(publicUrl, match[1])));
5736
+ }
5737
+ return urls;
5738
+ }
5739
+
5740
+ function htmlHasRobotsDirective(html, expectedContent) {
5741
+ return htmlHasTagWithAttributes(html, 'meta', {
5742
+ name: 'robots',
5743
+ content: expectedContent,
5744
+ });
5745
+ }
5746
+
5747
+ function escapeRegExp(value) {
5748
+ return String(value).replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&');
5749
+ }
5750
+
5751
+ function htmlHasTagWithAttributes(html, tagName, attributes) {
5752
+ const tagPattern = new RegExp(\`<\${tagName}\\\\b[^>]*>\`, 'giu');
5753
+ const tags = html.match(tagPattern) || [];
5754
+ return tags.some(tag =>
5755
+ Object.entries(attributes).every(([name, value]) => {
5756
+ const attrPattern = new RegExp(
5757
+ \`\\\\b\${escapeRegExp(name)}=["']\${escapeRegExp(value)}["']\`,
5758
+ 'iu',
5759
+ );
5760
+ return attrPattern.test(tag);
5761
+ }),
5762
+ );
5763
+ }
5764
+
5765
+ function assertHeadTag(evidence, html, options) {
5766
+ const found = htmlHasTagWithAttributes(
5767
+ html,
5768
+ options.tag,
5769
+ options.attributes,
5770
+ );
5771
+ evidence.assertions.push({
5772
+ type: 'ssr-head',
5773
+ route: options.route,
5774
+ tag: options.tag,
5775
+ attributes: options.attributes,
5776
+ status: found ? 'pass' : 'fail',
5777
+ });
5778
+ assert(found, \`\${options.appId} \${options.route} SSR head is missing \${options.label}\`);
5779
+ }
5780
+
5781
+ async function validateSsrHead(evidence, app, publicUrl, ssrRoute, ssr) {
5782
+ const titleFound = /<title\\b[^>]*>[^<]+<\\/title>/iu.test(ssr.body);
5783
+ evidence.assertions.push({
5784
+ type: 'ssr-head',
5785
+ route: ssrRoute,
5786
+ tag: 'title',
5787
+ status: titleFound ? 'pass' : 'fail',
5788
+ });
5789
+ assert(titleFound, \`\${app.id} \${ssrRoute} SSR head is missing title\`);
5790
+ assertHeadTag(evidence, ssr.body, {
5791
+ appId: app.id,
5792
+ route: ssrRoute,
5793
+ tag: 'meta',
5794
+ attributes: { name: 'description' },
5795
+ label: 'description meta',
5796
+ });
5797
+ assertHeadTag(evidence, ssr.body, {
5798
+ appId: app.id,
5799
+ route: ssrRoute,
5800
+ tag: 'meta',
5801
+ attributes: { name: 'robots' },
5802
+ label: 'robots meta',
5803
+ });
5804
+
5805
+ const publicSurface = app.routes?.publicSurface ?? {};
5806
+ const routeEntry = (publicSurface.routeEntries ?? [])[0];
5807
+ if (!routeEntry) {
5808
+ const canonicalFound = htmlHasTagWithAttributes(ssr.body, 'link', {
5809
+ rel: 'canonical',
5810
+ });
5811
+ evidence.assertions.push({
5812
+ type: 'ssr-head-private-canonical',
5813
+ route: ssrRoute,
5814
+ status: canonicalFound ? 'fail' : 'pass',
5815
+ });
5816
+ assert(!canonicalFound, \`\${app.id} \${ssrRoute} private SSR head must not emit canonical links\`);
5817
+ return;
5818
+ }
5819
+
5820
+ const publicRoute = routeEntry.localeUrlPaths?.en ?? publicSurface.concreteUrlPaths?.[0];
5821
+ const headRoute = publicRoute || ssrRoute;
5822
+ const headResponse =
5823
+ headRoute === ssrRoute ? ssr : await fetchText(joinUrl(publicUrl, headRoute));
5824
+ if (headRoute !== ssrRoute) {
5825
+ evidence.assertions.push({
5826
+ type: 'ssr-head-route',
5827
+ route: headRoute,
5828
+ status: headResponse.ok ? 'pass' : 'fail',
5829
+ statusCode: headResponse.status,
5830
+ });
5831
+ assert(headResponse.ok, \`\${app.id} public head route returned HTTP \${headResponse.status}\`);
5832
+ assertCloudflareSecurity(evidence, app, headResponse, headRoute, publicUrl, {
5833
+ html: true,
5834
+ });
5835
+ }
5836
+ const isPreview = shouldNoindexUrl(publicUrl, app.deploy?.cloudflare?.security?.noindex);
5837
+ const robotsIndexable = htmlHasRobotsDirective(headResponse.body, '${robotsPolicy.indexableRobots}');
5838
+ evidence.assertions.push({
5839
+ type: 'indexing-policy',
5840
+ route: headRoute,
5841
+ mode: isPreview ? 'preview' : 'production',
5842
+ xRobotsTag: headResponse.xRobotsTag,
5843
+ htmlRobotsIndexable: robotsIndexable,
5844
+ status:
5845
+ isPreview || (headResponse.xRobotsTag !== '${robotsPolicy.privateRouteRobots}' && robotsIndexable)
5846
+ ? 'pass'
5847
+ : 'fail',
5848
+ });
5849
+ if (!isPreview) {
5850
+ assert(
5851
+ headResponse.xRobotsTag !== '${robotsPolicy.privateRouteRobots}' && robotsIndexable,
5852
+ \`\${app.id} \${headRoute} production public route must be indexable\`,
5853
+ );
5854
+ }
5855
+
5856
+ const canonicalUrl = String(joinUrl(publicUrl, headRoute));
5857
+ assertHeadTag(evidence, headResponse.body, {
5858
+ appId: app.id,
5859
+ route: headRoute,
5860
+ tag: 'link',
5861
+ attributes: { rel: 'canonical', href: canonicalUrl },
5862
+ label: 'canonical link',
5863
+ });
5864
+ for (const language of app.routes?.publicHead?.alternates?.hreflang ?? []) {
5865
+ const href = String(joinUrl(publicUrl, routeEntry.localeUrlPaths?.[language] ?? headRoute));
5866
+ assertHeadTag(evidence, headResponse.body, {
5867
+ appId: app.id,
5868
+ route: headRoute,
5869
+ tag: 'link',
5870
+ attributes: { rel: 'alternate', hreflang: language, href },
5871
+ label: \`hreflang \${language}\`,
5872
+ });
5873
+ }
5874
+ assertHeadTag(evidence, headResponse.body, {
5875
+ appId: app.id,
5876
+ route: headRoute,
5877
+ tag: 'link',
5878
+ attributes: { rel: 'alternate', hreflang: 'x-default' },
5879
+ label: 'x-default hreflang',
5880
+ });
5881
+ for (const property of ['og:title', 'og:description', 'og:url', 'og:type']) {
5882
+ assertHeadTag(evidence, headResponse.body, {
5883
+ appId: app.id,
5884
+ route: headRoute,
5885
+ tag: 'meta',
5886
+ attributes: { property },
5887
+ label: property,
5888
+ });
5889
+ }
5890
+ for (const name of ['twitter:card', 'twitter:title', 'twitter:description']) {
5891
+ assertHeadTag(evidence, headResponse.body, {
5892
+ appId: app.id,
5893
+ route: headRoute,
5894
+ tag: 'meta',
5895
+ attributes: { name },
5896
+ label: name,
5897
+ });
5898
+ }
5899
+ assertHeadTag(evidence, headResponse.body, {
5900
+ appId: app.id,
5901
+ route: headRoute,
5902
+ tag: 'script',
5903
+ attributes: { type: 'application/ld+json' },
5904
+ label: 'JSON-LD structured data',
5905
+ });
5906
+ }
5907
+
5908
+ async function validateNotFound(evidence, app, publicUrl) {
5909
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
5910
+ const notFoundRoute =
5911
+ qualityGates.statusCodes?.notFoundRoute ?? '${qualityGates.statusCodes.notFoundRoute}';
5912
+ const expectedStatus = qualityGates.statusCodes?.unknownRouteStatus ?? ${qualityGates.statusCodes.unknownRouteStatus};
5913
+ const response = await fetchText(joinUrl(publicUrl, notFoundRoute));
5914
+ evidence.assertions.push({
5915
+ type: 'status-code',
5916
+ route: notFoundRoute,
5917
+ expectedStatus,
5918
+ actualStatus: response.status,
5919
+ status: response.status === expectedStatus ? 'pass' : 'fail',
5920
+ });
5921
+ assert(
5922
+ response.status === expectedStatus,
5923
+ \`\${app.id} unknown route must return HTTP \${expectedStatus}, got \${response.status}\`,
5924
+ );
5925
+ assertCloudflareSecurity(evidence, app, response, notFoundRoute, publicUrl, {
5926
+ html: response.contentType?.includes('text/html'),
5927
+ });
5928
+ }
5929
+
5930
+ async function validateCssAsset(evidence, app, publicUrl, ssr) {
5931
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
5932
+ const budgets = qualityGates.budgets ?? {};
5933
+ const styleUrls = extractPreloadStyleUrls(ssr.link, publicUrl);
5934
+ evidence.assertions.push({
5935
+ type: 'css-preload-assets',
5936
+ actual: styleUrls,
5937
+ status: styleUrls.length > 0 ? 'pass' : 'fail',
5938
+ });
5939
+ assert(styleUrls.length > 0, \`\${app.id} SSR response did not expose preloadable CSS assets\`);
5940
+
5941
+ const styleUrl = styleUrls[0];
5942
+ const route = new URL(styleUrl).pathname;
5943
+ const css = await fetchText(styleUrl);
5944
+ evidence.assertions.push({
5945
+ type: 'css-asset',
5946
+ route,
5947
+ status: css.ok && css.body.trim() !== '' ? 'pass' : 'fail',
5948
+ statusCode: css.status,
5949
+ });
5950
+ assert(css.ok, \`\${app.id} CSS asset returned HTTP \${css.status}\`);
5951
+ assert(css.body.trim() !== '', \`\${app.id} CSS asset is empty\`);
5952
+ assertContentType(evidence, app, css, {
5953
+ route,
5954
+ includes: 'text/css',
5955
+ });
5956
+ assertCacheControl(evidence, app, css, {
5957
+ route,
5958
+ required: qualityGates.assets?.cacheControlRequiredForCss,
5959
+ });
5960
+ assertByteBudget(evidence, app, css, {
5961
+ label: 'cssAssetMaxBytes',
5962
+ maxBytes: budgets.cssAssetMaxBytes ?? ${createPublicWebsiteBudgetFallback('cssAssetMaxBytes')},
5963
+ route,
5964
+ });
5965
+ }
5966
+
5967
+ async function validatePublicSurface(evidence, app, publicUrl) {
5968
+ const publicSurface = app.routes?.publicSurface ?? {};
5969
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
5970
+ const budgets = qualityGates.budgets ?? {};
5971
+ const hasPublicRoutes =
5972
+ (publicSurface.publicRoutes ?? []).length > 0 ||
5973
+ (publicSurface.routeEntries ?? []).length > 0 ||
5974
+ (publicSurface.contentSources ?? []).length > 0;
5975
+
5976
+ const robotsRoute = '/robots.txt';
5977
+ const robots = await fetchText(joinUrl(publicUrl, robotsRoute));
5978
+ evidence.assertions.push({
5979
+ type: 'public-surface-robots',
5980
+ route: robotsRoute,
5981
+ status: robots.ok ? 'pass' : 'fail',
5982
+ statusCode: robots.status,
5983
+ });
5984
+ assert(robots.ok, \`\${app.id} robots.txt returned HTTP \${robots.status}\`);
5985
+ assertContentType(evidence, app, robots, {
5986
+ route: robotsRoute,
5987
+ includes: 'text/plain',
5988
+ });
5989
+ assertCloudflareSecurity(evidence, app, robots, robotsRoute, publicUrl);
5990
+
5991
+ if (!hasPublicRoutes) {
5992
+ const disallowsAll = robots.body.includes('Disallow: /');
5993
+ const referencesSitemap = /\\bSitemap:/iu.test(robots.body);
5994
+ evidence.assertions.push({
5995
+ type: 'public-surface-private-robots',
5996
+ route: robotsRoute,
5997
+ disallowsAll,
5998
+ referencesSitemap,
5999
+ status: disallowsAll && !referencesSitemap ? 'pass' : 'fail',
6000
+ });
6001
+ assert(disallowsAll, \`\${app.id} private public surface robots.txt must disallow crawling\`);
6002
+ assert(!referencesSitemap, \`\${app.id} private public surface robots.txt must not reference sitemap.xml\`);
6003
+ return;
6004
+ }
6005
+
6006
+ const sitemapRoute = '/sitemap.xml';
6007
+ const sitemap = await fetchText(joinUrl(publicUrl, sitemapRoute));
6008
+ evidence.assertions.push({
6009
+ type: 'public-surface-sitemap',
6010
+ route: sitemapRoute,
6011
+ status: sitemap.ok ? 'pass' : 'fail',
6012
+ statusCode: sitemap.status,
6013
+ });
6014
+ assert(sitemap.ok, \`\${app.id} sitemap.xml returned HTTP \${sitemap.status}\`);
6015
+ assertContentType(evidence, app, sitemap, {
6016
+ route: sitemapRoute,
6017
+ includes: 'xml',
6018
+ });
6019
+ assertByteBudget(evidence, app, sitemap, {
6020
+ label: 'sitemapXmlMaxBytes',
6021
+ maxBytes: budgets.sitemapXmlMaxBytes ?? ${createPublicWebsiteBudgetFallback('sitemapXmlMaxBytes')},
6022
+ route: sitemapRoute,
6023
+ });
6024
+
6025
+ const sitemapUrl = String(joinUrl(publicUrl, sitemapRoute));
6026
+ const robotsReferencesSitemap = robots.body.includes(\`Sitemap: \${sitemapUrl}\`);
6027
+ evidence.assertions.push({
6028
+ type: 'robots-sitemap-consistency',
6029
+ route: robotsRoute,
6030
+ sitemapUrl,
6031
+ status: robotsReferencesSitemap ? 'pass' : 'fail',
6032
+ });
6033
+ assert(
6034
+ robotsReferencesSitemap,
6035
+ \`\${app.id} robots.txt must reference generated sitemap.xml\`,
6036
+ );
6037
+
6038
+ for (const urlPath of publicSurface.concreteUrlPaths ?? []) {
6039
+ const loc = \`<loc>\${String(joinUrl(publicUrl, urlPath))}</loc>\`;
6040
+ evidence.assertions.push({
6041
+ type: 'sitemap-route',
6042
+ route: urlPath,
6043
+ status: sitemap.body.includes(loc) ? 'pass' : 'fail',
6044
+ });
6045
+ assert(sitemap.body.includes(loc), \`\${app.id} sitemap.xml is missing \${urlPath}\`);
6046
+ }
6047
+
6048
+ const manifestRoute = '/site.webmanifest';
6049
+ const webManifest = await fetchText(joinUrl(publicUrl, manifestRoute));
6050
+ const webManifestJson = parseMaybeJson(webManifest.body);
6051
+ evidence.assertions.push({
6052
+ type: 'public-surface-webmanifest',
6053
+ route: manifestRoute,
6054
+ status: webManifest.ok && webManifestJson ? 'pass' : 'fail',
6055
+ statusCode: webManifest.status,
6056
+ });
6057
+ assert(webManifest.ok, \`\${app.id} site.webmanifest returned HTTP \${webManifest.status}\`);
6058
+ assert(webManifestJson, \`\${app.id} site.webmanifest must be valid JSON\`);
6059
+ assertContentType(evidence, app, webManifest, {
6060
+ route: manifestRoute,
6061
+ includes: 'manifest',
6062
+ });
6063
+ }
6064
+
4996
6065
  async function validateApp(app, publicUrl) {
4997
6066
  const cloudflare = app.deploy?.cloudflare;
4998
6067
  const routes = cloudflare?.routes ?? {};
@@ -5006,6 +6075,8 @@ async function validateApp(app, publicUrl) {
5006
6075
 
5007
6076
  const ssrRoute = routes.ssr ?? '/en';
5008
6077
  const ssr = await fetchText(joinUrl(publicUrl, ssrRoute));
6078
+ const qualityGates = cloudflare?.qualityGates ?? {};
6079
+ const budgets = qualityGates.budgets ?? {};
5009
6080
  evidence.assertions.push({
5010
6081
  type: 'ssr',
5011
6082
  route: ssrRoute,
@@ -5016,6 +6087,18 @@ async function validateApp(app, publicUrl) {
5016
6087
  assertCloudflareSecurity(evidence, app, ssr, ssrRoute, publicUrl, {
5017
6088
  html: true,
5018
6089
  });
6090
+ assertContentType(evidence, app, ssr, {
6091
+ route: ssrRoute,
6092
+ includes: 'text/html',
6093
+ });
6094
+ assertByteBudget(evidence, app, ssr, {
6095
+ label: 'ssrHtmlMaxBytes',
6096
+ maxBytes: budgets.ssrHtmlMaxBytes ?? ${createPublicWebsiteBudgetFallback('ssrHtmlMaxBytes')},
6097
+ route: ssrRoute,
6098
+ });
6099
+ await validateSsrHead(evidence, app, publicUrl, ssrRoute, ssr);
6100
+ await validateNotFound(evidence, app, publicUrl);
6101
+ await validatePublicSurface(evidence, app, publicUrl);
5019
6102
 
5020
6103
  const uiMarker = extractUiMarker(ssr.body);
5021
6104
  evidence.assertions.push({
@@ -5055,6 +6138,7 @@ async function validateApp(app, publicUrl) {
5055
6138
  cssPreloadLinkHeader.includes('as=style'),
5056
6139
  \`\${app.id} SSR response is missing CSS preload Link headers\`,
5057
6140
  );
6141
+ await validateCssAsset(evidence, app, publicUrl, ssr);
5058
6142
 
5059
6143
  const manifestRoute = routes.mfManifest ?? '/mf-manifest.json';
5060
6144
  const manifest = await fetchText(joinUrl(publicUrl, manifestRoute));
@@ -5070,6 +6154,16 @@ async function validateApp(app, publicUrl) {
5070
6154
  \`\${app.id} MF manifest returned HTTP \${manifest.status}\`,
5071
6155
  );
5072
6156
  assertCloudflareSecurity(evidence, app, manifest, manifestRoute, publicUrl);
6157
+ assertContentType(evidence, app, manifest, {
6158
+ route: manifestRoute,
6159
+ includes: 'json',
6160
+ });
6161
+ assertByteBudget(evidence, app, manifest, {
6162
+ label: 'mfManifestMaxBytes',
6163
+ maxBytes: budgets.mfManifestMaxBytes ?? ${createPublicWebsiteBudgetFallback('mfManifestMaxBytes')},
6164
+ route: manifestRoute,
6165
+ });
6166
+ assertNoPublicSourcemapRefs(evidence, app, manifestJson);
5073
6167
  evidence.assertions.push({
5074
6168
  type: 'mf-manifest-cors',
5075
6169
  route: manifestRoute,
@@ -5110,6 +6204,15 @@ async function validateApp(app, publicUrl) {
5110
6204
  });
5111
6205
  assert(locale.ok, \`\${app.id} locale JSON returned HTTP \${locale.status}\`);
5112
6206
  assertCloudflareSecurity(evidence, app, locale, localeRoute, publicUrl);
6207
+ assertContentType(evidence, app, locale, {
6208
+ route: localeRoute,
6209
+ includes: 'json',
6210
+ });
6211
+ assertByteBudget(evidence, app, locale, {
6212
+ label: 'localeJsonMaxBytes',
6213
+ maxBytes: budgets.localeJsonMaxBytes ?? ${createPublicWebsiteBudgetFallback('localeJsonMaxBytes')},
6214
+ route: localeRoute,
6215
+ });
5113
6216
  evidence.assertions.push({
5114
6217
  type: 'i18n-cors',
5115
6218
  route: localeRoute,
@@ -5147,6 +6250,75 @@ async function validateApp(app, publicUrl) {
5147
6250
  return evidence;
5148
6251
  }
5149
6252
 
6253
+ export { validateApp };
6254
+ `;
6255
+ }
6256
+ function createCloudflareVersionProofScript() {
6257
+ return `#!/usr/bin/env node
6258
+ import fs from 'node:fs';
6259
+ import path from 'node:path';
6260
+ import { fileURLToPath } from 'node:url';
6261
+ import { validateApp } from './ultramodern-cloudflare-proof.mjs';
6262
+
6263
+ const workspaceRoot = path.resolve(
6264
+ path.dirname(fileURLToPath(import.meta.url)),
6265
+ '..',
6266
+ );
6267
+ const contractPath = path.join(
6268
+ workspaceRoot,
6269
+ '.modernjs/ultramodern-generated-contract.json',
6270
+ );
6271
+ const defaultOut = path.join(
6272
+ workspaceRoot,
6273
+ '.codex/reports/cloudflare-version-proof/public-url-proof.json',
6274
+ );
6275
+
6276
+ function readJson(filePath) {
6277
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
6278
+ }
6279
+
6280
+ function parseArgs(argv) {
6281
+ const parsed = {
6282
+ appId: undefined,
6283
+ out: defaultOut,
6284
+ requirePublicUrls: false,
6285
+ };
6286
+
6287
+ for (let index = 0; index < argv.length; index += 1) {
6288
+ const arg = argv[index];
6289
+ if (arg === '--app') {
6290
+ parsed.appId = argv[index + 1];
6291
+ index += 1;
6292
+ } else if (arg === '--out') {
6293
+ parsed.out = argv[index + 1];
6294
+ index += 1;
6295
+ } else if (arg === '--require-public-urls') {
6296
+ parsed.requirePublicUrls = true;
6297
+ } else if (arg === '--help' || arg === '-h') {
6298
+ parsed.help = true;
6299
+ } else {
6300
+ throw new Error(\`Unknown argument: \${arg}\`);
6301
+ }
6302
+ }
6303
+
6304
+ return parsed;
6305
+ }
6306
+
6307
+ function printHelp() {
6308
+ process.stdout.write(\`Usage:
6309
+ node scripts/proof-cloudflare-version.mjs [--app workspace] [--out evidence.json] [--require-public-urls]
6310
+
6311
+ Set each app's public URL using the contract env key, for example:
6312
+ ULTRAMODERN_PUBLIC_URL_WORKSPACE=https://workspace.example.workers.dev
6313
+ \`);
6314
+ }
6315
+
6316
+ function assert(condition, message) {
6317
+ if (!condition) {
6318
+ throw new Error(message);
6319
+ }
6320
+ }
6321
+
5150
6322
  async function main(argv = process.argv.slice(2)) {
5151
6323
  const args = parseArgs(argv);
5152
6324
  if (args.help) {
@@ -5213,10 +6385,13 @@ function writeGeneratedWorkspaceScripts(targetDir, scope, enableTailwind, remote
5213
6385
  writeFileReplacing(targetDir, "scripts/assert-mf-types.mjs", createAssertMfTypesScript(remotes));
5214
6386
  writeFileReplacing(targetDir, "scripts/validate-ultramodern-workspace.mjs", createWorkspaceValidationScript(scope, enableTailwind, remotes));
5215
6387
  writeFileReplacing(targetDir, "scripts/check-ultramodern-i18n-boundaries.mjs", createWorkspaceI18nBoundaryValidationScript());
6388
+ writeFileReplacing(targetDir, "scripts/generate-public-surface-assets.mjs", createPublicSurfaceAssetsScript());
6389
+ writeFileReplacing(targetDir, "scripts/ultramodern-cloudflare-proof.mjs", createCloudflareProofHelperScript());
5216
6390
  writeFileReplacing(targetDir, "scripts/proof-cloudflare-version.mjs", createCloudflareVersionProofScript());
5217
6391
  }
5218
6392
  function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes = []) {
5219
6393
  const resolvedApp = 'shell' === app.kind ? createShellHost(remotes) : app;
6394
+ const publicWeb = createPublicWebAppArtifacts(resolvedApp);
5220
6395
  const writeAppFile = (relativePath, content)=>{
5221
6396
  writeFile(targetDir, `${resolvedApp.directory}/${relativePath}`, content);
5222
6397
  };
@@ -5224,7 +6399,8 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes
5224
6399
  writeJson(targetDir, `${resolvedApp.directory}/tsconfig.json`, createPackageTsConfig(resolvedApp.directory, appHasEffectApi(resolvedApp)));
5225
6400
  writeFile(targetDir, `${resolvedApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(resolvedApp, remotes));
5226
6401
  writeFile(targetDir, `${resolvedApp.directory}/src/ultramodern-build.ts`, createUltramodernBuildModule(scope, resolvedApp));
5227
- writeFile(targetDir, `${resolvedApp.directory}/src/routes/ultramodern-route-metadata.ts`, createRouteMetadataModule(resolvedApp));
6402
+ writeFile(targetDir, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
6403
+ writeFile(targetDir, publicWeb.routeHeadFile.path, publicWeb.routeHeadFile.content);
5228
6404
  writeFile(targetDir, `${resolvedApp.directory}/modern.config.ts`, createAppModernConfig(scope, resolvedApp));
5229
6405
  writeFile(targetDir, `${resolvedApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(resolvedApp, scope, remotes));
5230
6406
  writeJson(targetDir, `${resolvedApp.directory}/locales/en/translation.json`, createAppPublicLocaleMessages(resolvedApp, 'en', remotes));
@@ -5240,7 +6416,8 @@ function writeApp(targetDir, scope, app, packageSource, enableTailwind, remotes
5240
6416
  writeAppFile('src/routes/layout.tsx', createLayout(resolvedApp.id));
5241
6417
  for (const [relativePath, content] of Object.entries(workspaceAssetsForApp(resolvedApp)))writeFile(targetDir, `${resolvedApp.directory}/${relativePath}`, content);
5242
6418
  writeAppFile('src/routes/[lang]/page.tsx', 'shell' === resolvedApp.kind ? createShellPage(remotes) : createRemotePage(resolvedApp));
5243
- for (const route of createRouteOwnedI18nPaths(resolvedApp))if ('/' !== route.canonicalPath && 'shell' !== resolvedApp.kind) writeFile(targetDir, createRoutePageFilePath(resolvedApp, route.canonicalPath), createRouteAliasPage(route.canonicalPath));
6419
+ for (const generatedFile of publicWeb.routeMetaFiles)writeFile(targetDir, generatedFile.path, generatedFile.content);
6420
+ for (const generatedFile of publicWeb.routeAliasFiles)writeFile(targetDir, generatedFile.path, generatedFile.content);
5244
6421
  if ('shell' === resolvedApp.kind) {
5245
6422
  writeAppFile('src/routes/vertical-components.tsx', createShellRemoteComponents(scope, remotes));
5246
6423
  writeAppFile('src/routes/shell-frame.tsx', createShellFrameComponent());
@@ -5271,12 +6448,7 @@ function writeSharedPackages(targetDir, scope, packageSource) {
5271
6448
  ]
5272
6449
  });
5273
6450
  }
5274
- writeFile(targetDir, 'packages/shared-contracts/src/index.ts', `export const ultramodernWorkspaceContract = {
5275
- ownership: 'topology/ownership.json',
5276
- preset: 'presetUltramodern',
5277
- topology: 'topology/reference-topology.json',
5278
- } as const;
5279
- `);
6451
+ writeFile(targetDir, 'packages/shared-contracts/src/index.ts', createSharedContractsIndex());
5280
6452
  writeFile(targetDir, 'packages/shared-design-tokens/src/index.ts', `export const sharedDesignTokens = {
5281
6453
  color: {
5282
6454
  accent: '#2f8f68',
@@ -5352,9 +6524,12 @@ function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes
5352
6524
  }
5353
6525
  function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes) {
5354
6526
  const shellHost = createShellHost(remotes);
6527
+ const publicWeb = createPublicWebAppArtifacts(shellHost);
5355
6528
  writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes));
5356
6529
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
5357
- writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/ultramodern-route-metadata.ts`, createRouteMetadataModule(shellHost));
6530
+ writeFileReplacing(workspaceRoot, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
6531
+ writeFileReplacing(workspaceRoot, publicWeb.routeHeadFile.path, publicWeb.routeHeadFile.content);
6532
+ for (const generatedFile of publicWeb.routeMetaFiles)writeFileReplacing(workspaceRoot, generatedFile.path, generatedFile.content);
5358
6533
  rewriteWorkspaceAssetsForApp(workspaceRoot, shellHost);
5359
6534
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern.runtime.ts`, createAppRuntimeConfig(shellHost, scope, remotes));
5360
6535
  writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/locales/en/translation.json`), createAppPublicLocaleMessages(shellHost, 'en', remotes));