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