@bleedingdev/modern-js-create 3.2.0-ultramodern.101 → 3.2.0-ultramodern.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -566,6 +566,7 @@ const MODULE_FEDERATION_VERSION = '2.5.0';
566
566
  const ZEPHYR_RSPACK_PLUGIN_VERSION = '1.1.1';
567
567
  const ZEPHYR_AGENT_VERSION = '1.1.1';
568
568
  const WRANGLER_VERSION = '4.95.0';
569
+ const CLOUDFLARE_COMPATIBILITY_DATE = '2026-06-02';
569
570
  const TAILWIND_VERSION = '4.3.0';
570
571
  const TAILWIND_POSTCSS_VERSION = '4.3.0';
571
572
  const EFFECT_TSGO_VERSION = '0.13.0';
@@ -900,6 +901,12 @@ function toCamelCase(value) {
900
901
  function toEnvSegment(value) {
901
902
  return toKebabCase(value).replace(/-/g, '_').toUpperCase();
902
903
  }
904
+ function createRspackUniqueName(app) {
905
+ return app.mfName;
906
+ }
907
+ function createRspackChunkLoadingGlobal(app) {
908
+ return `__ULTRAMODERN_${toEnvSegment(app.mfName)}_LOADED_CHUNKS__`;
909
+ }
903
910
  function ultramodern_workspace_packageName(scope, suffix) {
904
911
  return `@${scope}/${suffix}`;
905
912
  }
@@ -1095,17 +1102,105 @@ function createCloudflareProofRoute(app) {
1095
1102
  } : {}
1096
1103
  };
1097
1104
  }
1105
+ function createCloudflareSecurityContract() {
1106
+ return {
1107
+ enabled: true,
1108
+ headers: {
1109
+ referrerPolicy: 'strict-origin-when-cross-origin',
1110
+ contentTypeOptions: 'nosniff',
1111
+ permissionsPolicy: 'camera=(), geolocation=(), microphone=(), payment=(), usb=()'
1112
+ },
1113
+ contentSecurityPolicy: {
1114
+ mode: 'report-only',
1115
+ directives: {
1116
+ 'base-uri': [
1117
+ "'self'"
1118
+ ],
1119
+ 'connect-src': [
1120
+ "'self'",
1121
+ 'https:',
1122
+ 'http:',
1123
+ 'wss:',
1124
+ 'ws:'
1125
+ ],
1126
+ 'default-src': [
1127
+ "'self'"
1128
+ ],
1129
+ 'font-src': [
1130
+ "'self'",
1131
+ 'data:',
1132
+ 'https:',
1133
+ 'http:'
1134
+ ],
1135
+ 'form-action': [
1136
+ "'self'"
1137
+ ],
1138
+ 'frame-ancestors': [
1139
+ "'self'"
1140
+ ],
1141
+ 'img-src': [
1142
+ "'self'",
1143
+ 'data:',
1144
+ 'blob:',
1145
+ 'https:',
1146
+ 'http:'
1147
+ ],
1148
+ 'manifest-src': [
1149
+ "'self'",
1150
+ 'https:',
1151
+ 'http:'
1152
+ ],
1153
+ 'object-src': [
1154
+ "'none'"
1155
+ ],
1156
+ "script-src": [
1157
+ "'self'",
1158
+ "'unsafe-inline'",
1159
+ "'unsafe-eval'",
1160
+ 'https:',
1161
+ 'http:',
1162
+ 'blob:'
1163
+ ],
1164
+ 'style-src': [
1165
+ "'self'",
1166
+ "'unsafe-inline'",
1167
+ 'https:',
1168
+ 'http:'
1169
+ ],
1170
+ 'worker-src': [
1171
+ "'self'",
1172
+ 'blob:'
1173
+ ]
1174
+ },
1175
+ reason: "Report-only by default so Cloudflare Module Federation SSR can prove remote script, style, and connect compatibility before enforcement."
1176
+ },
1177
+ noindex: {
1178
+ workersDev: true,
1179
+ localhost: true,
1180
+ previewHostnames: []
1181
+ },
1182
+ cookies: {
1183
+ mutateSetCookie: false,
1184
+ reason: 'Generated Cloudflare worker does not own application Set-Cookie headers.'
1185
+ }
1186
+ };
1187
+ }
1188
+ function formatTsJsonValue(value, indent) {
1189
+ return JSON.stringify(value, null, 2).replaceAll('\n', `\n${' '.repeat(indent)}`);
1190
+ }
1098
1191
  function createCloudflareDeployContract(scope, app) {
1099
1192
  return {
1100
1193
  target: 'cloudflare',
1101
1194
  workerName: createCloudflareWorkerName(scope, app),
1102
1195
  publicUrlEnv: createCloudflarePublicUrlEnv(app),
1196
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
1103
1197
  compatibilityFlags: [
1104
1198
  'nodejs_compat',
1105
1199
  'global_fetch_strictly_public'
1106
1200
  ],
1107
1201
  assetsBinding: 'ASSETS',
1108
1202
  routes: createCloudflareProofRoute(app),
1203
+ security: createCloudflareSecurityContract(),
1109
1204
  evidence: {
1110
1205
  proofScript: "scripts/proof-cloudflare-version.mjs",
1111
1206
  reportDefault: '.codex/reports/cloudflare-version-proof/public-url-proof.json'
@@ -1379,6 +1474,9 @@ ${bffPluginEntry} moduleFederationPlugin(),
1379
1474
  overrideBrowserslist: ['defaults'],
1380
1475
  },
1381
1476
  bundlerChain: chain => {
1477
+ chain.output
1478
+ .uniqueName('${createRspackUniqueName(app)}')
1479
+ .chunkLoadingGlobal('${createRspackChunkLoadingGlobal(app)}');
1382
1480
  chain.ignoreWarnings([
1383
1481
  {
1384
1482
  message: /the request of a dependency is an expression/u,
@@ -1391,6 +1489,8 @@ ${bffPluginEntry} moduleFederationPlugin(),
1391
1489
  ? {
1392
1490
  deploy: {
1393
1491
  worker: {
1492
+ compatibilityDate: '${CLOUDFLARE_COMPATIBILITY_DATE}',
1493
+ security: ${formatTsJsonValue(createCloudflareSecurityContract(), 16)},
1394
1494
  ssr: true,
1395
1495
  },
1396
1496
  },
@@ -1629,12 +1729,18 @@ ${createModuleFederationRemotesConfig(scope, app, remotes)}${createSharedModuleF
1629
1729
  function appI18nNamespace(app) {
1630
1730
  return 'shell' === app.kind ? 'shell' : app.domain ?? app.id;
1631
1731
  }
1732
+ const privateAppRoutePublicness = {
1733
+ indexable: false,
1734
+ public: false,
1735
+ publicSurface: 'private-app-screen'
1736
+ };
1632
1737
  function createRouteOwnedI18nPaths(app) {
1633
1738
  const namespace = appI18nNamespace(app);
1634
1739
  const base = {
1635
1740
  mfBoundaryId: app.mfName,
1636
1741
  namespace,
1637
- ownerAppId: app.id
1742
+ ownerAppId: app.id,
1743
+ ...privateAppRoutePublicness
1638
1744
  };
1639
1745
  if ('shell' === app.kind) return [
1640
1746
  {
@@ -1807,8 +1913,11 @@ function createRouteOwnedI18nPaths(app) {
1807
1913
  }
1808
1914
  ];
1809
1915
  }
1810
- function createLocalisedUrlsMap(app) {
1811
- return Object.fromEntries(createRouteOwnedI18nPaths(app).flatMap((route)=>{
1916
+ function isPublicIndexableRoute(route) {
1917
+ return route.public && route.indexable;
1918
+ }
1919
+ function createLocalisedUrlsMapFromRoutes(routes) {
1920
+ return Object.fromEntries(routes.flatMap((route)=>{
1812
1921
  if ('/' === route.canonicalPath) return [];
1813
1922
  return Array.from(new Set([
1814
1923
  route.canonicalPath,
@@ -1819,9 +1928,23 @@ function createLocalisedUrlsMap(app) {
1819
1928
  ]);
1820
1929
  }));
1821
1930
  }
1931
+ function createLocalisedUrlsMap(app) {
1932
+ return createLocalisedUrlsMapFromRoutes(createRouteOwnedI18nPaths(app));
1933
+ }
1934
+ function createPublicRouteMetadata(app) {
1935
+ return createRouteOwnedI18nPaths(app).filter(isPublicIndexableRoute).map((route)=>({
1936
+ canonicalPath: route.canonicalPath,
1937
+ id: route.id,
1938
+ localisedPaths: route.localisedPaths,
1939
+ namespace: route.namespace,
1940
+ ownerAppId: route.ownerAppId,
1941
+ titleKey: route.titleKey
1942
+ }));
1943
+ }
1822
1944
  function createRouteMetadataModule(app) {
1823
1945
  const routes = sortJsonValue(createRouteOwnedI18nPaths(app));
1824
1946
  const localisedUrls = sortJsonValue(createLocalisedUrlsMap(app));
1947
+ const publicRoutes = sortJsonValue(createPublicRouteMetadata(app));
1825
1948
  const namespace = appI18nNamespace(app);
1826
1949
  return `export const ultramodernRouteNamespace = '${namespace}' as const;
1827
1950
 
@@ -1829,9 +1952,12 @@ export const ultramodernRouteMetadata = ${JSON.stringify(routes, null, 2)} as co
1829
1952
 
1830
1953
  export const ultramodernLocalisedUrls = ${JSON.stringify(localisedUrls, null, 2)} as const;
1831
1954
 
1955
+ export const ultramodernPublicRoutes = ${JSON.stringify(publicRoutes, null, 2)} as const;
1956
+
1832
1957
  export const ultramodernRouteConfig = {
1833
1958
  localisedUrls: ultramodernLocalisedUrls,
1834
1959
  namespace: ultramodernRouteNamespace,
1960
+ publicRoutes: ultramodernPublicRoutes,
1835
1961
  routes: ultramodernRouteMetadata,
1836
1962
  source: 'route-owned',
1837
1963
  } as const;
@@ -4051,6 +4177,12 @@ function createAppConfigContract(app) {
4051
4177
  disableClientServer: true
4052
4178
  }
4053
4179
  },
4180
+ rspack: {
4181
+ output: {
4182
+ uniqueName: createRspackUniqueName(app),
4183
+ chunkLoadingGlobal: createRspackChunkLoadingGlobal(app)
4184
+ }
4185
+ },
4054
4186
  html: {
4055
4187
  outputStructure: 'flat'
4056
4188
  },
@@ -4233,7 +4365,9 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
4233
4365
  target: 'cloudflare',
4234
4366
  cloudflare: createCloudflareDeployContract(scope, app),
4235
4367
  worker: {
4368
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
4236
4369
  name: createCloudflareWorkerName(scope, app),
4370
+ security: createCloudflareSecurityContract(),
4237
4371
  ssr: true
4238
4372
  },
4239
4373
  output: {
@@ -4278,6 +4412,9 @@ function createAppGeneratedContract(scope, app, apps, enableTailwind) {
4278
4412
  metadataExport: './src/routes/ultramodern-route-metadata',
4279
4413
  localisedUrls: createLocalisedUrlsMap(app),
4280
4414
  owned: createRouteOwnedI18nPaths(app),
4415
+ publicRoutes: createPublicRouteMetadata(app),
4416
+ privateByDefault: true,
4417
+ publicnessDefault: 'private-app-screen',
4281
4418
  generatedRouteMap: true,
4282
4419
  manualOverrides: []
4283
4420
  },
@@ -4755,6 +4892,7 @@ function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
4755
4892
  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 ultramodern:assert-mf-types' : 'ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm ultramodern:assert-mf-types';
4756
4893
  const expectedCloudflareBuildScript = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm ultramodern:assert-mf-types' : 'pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm ultramodern:assert-mf-types';
4757
4894
  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';
4895
+ const expectedCloudflareSecurity = createCloudflareSecurityContract();
4758
4896
  return `import { execFileSync } from 'node:child_process';
4759
4897
  import fs from 'node:fs';
4760
4898
  import path from 'node:path';
@@ -4769,6 +4907,7 @@ const oldRemotePaths = ${JSON.stringify(oldRemotePaths, null, 2)};
4769
4907
  const expectedBuildScript = ${JSON.stringify(expectedBuildScript)};
4770
4908
  const expectedCloudflareBuildScript = ${JSON.stringify(expectedCloudflareBuildScript)};
4771
4909
  const expectedCloudflareDeployScript = ${JSON.stringify(expectedCloudflareDeployScript)};
4910
+ const expectedCloudflareSecurity = ${JSON.stringify(expectedCloudflareSecurity, null, 2)};
4772
4911
 
4773
4912
  const readText = relativePath => fs.readFileSync(path.join(root, relativePath), 'utf-8');
4774
4913
  const readJson = relativePath => JSON.parse(readText(relativePath));
@@ -4784,6 +4923,12 @@ const assertNotExists = relativePath => {
4784
4923
  assert(!fs.existsSync(path.join(root, relativePath)), \`Unexpected \${relativePath}\`);
4785
4924
  };
4786
4925
  const expectedWorkerName = packageSuffix => \`\${packageScope}-\${packageSuffix}\`.slice(0, 63);
4926
+ const expectedChunkLoadingGlobal = mfName =>
4927
+ \`__ULTRAMODERN_\${mfName
4928
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
4929
+ .replace(/[^a-zA-Z0-9]+/g, '_')
4930
+ .replace(/^_+|_+$/g, '')
4931
+ .toUpperCase()}_LOADED_CHUNKS__\`;
4787
4932
  const parseSemver = version => {
4788
4933
  const match = /^(\\d+)\\.(\\d+)\\.(\\d+)/u.exec(version);
4789
4934
  assert(match, \`Unable to parse pnpm version: \${version}\`);
@@ -4929,6 +5074,7 @@ assert(rootPackage.scripts?.['skills:check'] === 'node ./scripts/bootstrap-agent
4929
5074
  assert(rootPackage.scripts?.postinstall === "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mjs && node ./scripts/setup-agent-reference-repos.mjs", 'Root postinstall must format, bootstrap agent skills, initialize git/hooks, and install reference repositories');
4930
5075
 
4931
5076
  const expectedAppIds = ['shell-super-app', ...fullStackVerticals.map(vertical => vertical.id)];
5077
+ const expectedCloudflareCompatibilityDate = '${CLOUDFLARE_COMPATIBILITY_DATE}';
4932
5078
  const expectedCloudflareCompatibilityFlags = ['nodejs_compat', 'global_fetch_strictly_public'];
4933
5079
  assert(
4934
5080
  JSON.stringify(generatedContract.apps?.map(app => app.id)) === JSON.stringify(expectedAppIds),
@@ -4959,7 +5105,12 @@ assert(
4959
5105
  const shellContract = generatedContract.apps?.find(app => app.id === 'shell-super-app');
4960
5106
  assert(shellContract?.deploy?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell Cloudflare workerName is incorrect');
4961
5107
  assert(shellContract?.deploy?.cloudflare?.publicUrlEnv === 'ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'Shell Cloudflare public URL env is incorrect');
5108
+ assert(shellContract?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell Cloudflare compatibilityDate is incorrect');
4962
5109
  assert(JSON.stringify(shellContract?.deploy?.cloudflare?.compatibilityFlags) === JSON.stringify(expectedCloudflareCompatibilityFlags), 'Shell Cloudflare compatibility flags are incorrect');
5110
+ assert(JSON.stringify(shellContract?.deploy?.cloudflare?.security) === JSON.stringify(expectedCloudflareSecurity), 'Shell Cloudflare security contract is incorrect');
5111
+ assert(shellContract?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell worker compatibilityDate is incorrect');
5112
+ assert(shellContract?.config?.rspack?.output?.uniqueName === 'shellSuperApp', 'Shell Rspack uniqueName is incorrect');
5113
+ assert(shellContract?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal('shellSuperApp'), 'Shell Rspack chunkLoadingGlobal is incorrect');
4963
5114
  assert(topology.shell?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell topology Cloudflare workerName is incorrect');
4964
5115
  assert(shellContract?.styling?.federation?.owner?.id === 'shell-super-app', 'Shell CSS federation owner is missing');
4965
5116
  assert(shellContract?.styling?.federation?.role === 'shell-base-overlay', 'Shell must own base and overlay CSS');
@@ -4971,6 +5122,13 @@ assert(shellContract?.styling?.federation?.entrypoints?.css?.includes('src/route
4971
5122
  assert(shellContract?.styling?.federation?.assets?.shared?.some(asset => asset.endsWith('/shared-design-tokens/tokens.css')), 'Shell must import the shared design token CSS asset');
4972
5123
  assert(shellContract?.styling?.federation?.dedupe?.duplicateBaseStylesAllowed === false, 'Shell CSS contract must forbid duplicated base styles');
4973
5124
  assert(shellContract?.styling?.federation?.ssr?.firstPaintRequired === true, 'Shell CSS must be required for SSR first paint');
5125
+ assert(shellContract?.routes?.privateByDefault === true, 'Shell routes must be private by default');
5126
+ assert(shellContract?.routes?.publicnessDefault === 'private-app-screen', 'Shell route publicness default is incorrect');
5127
+ assert(JSON.stringify(shellContract?.routes?.publicRoutes ?? []) === '[]', 'Shell must not expose generated public routes by default');
5128
+ assert(
5129
+ (shellContract?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen'),
5130
+ 'Shell owned routes must be non-indexable private app screens by default',
5131
+ );
4974
5132
  assert(
4975
5133
  topology.shell?.verticalRefs?.join(',') === fullStackVerticals.map(vertical => vertical.id).join(','),
4976
5134
  'Topology shell verticalRefs must match generated verticals',
@@ -5006,8 +5164,13 @@ for (const vertical of fullStackVerticals) {
5006
5164
  assert(contractEntry?.kind === 'vertical', \`\${vertical.id} generated contract kind is incorrect\`);
5007
5165
  assert(contractEntry?.deploy?.cloudflare?.workerName === expectedWorkerName(vertical.id), \`\${vertical.id} Cloudflare workerName is incorrect\`);
5008
5166
  assert(contractEntry?.deploy?.cloudflare?.publicUrlEnv === \`ULTRAMODERN_PUBLIC_URL_\${vertical.id.replace(/-/g, '_').toUpperCase()}\`, \`\${vertical.id} Cloudflare public URL env is incorrect\`);
5167
+ assert(contractEntry?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, \`\${vertical.id} Cloudflare compatibilityDate is incorrect\`);
5009
5168
  assert(JSON.stringify(contractEntry?.deploy?.cloudflare?.compatibilityFlags) === JSON.stringify(expectedCloudflareCompatibilityFlags), \`\${vertical.id} Cloudflare compatibility flags are incorrect\`);
5169
+ assert(JSON.stringify(contractEntry?.deploy?.cloudflare?.security) === JSON.stringify(expectedCloudflareSecurity), \`\${vertical.id} Cloudflare security contract is incorrect\`);
5170
+ assert(contractEntry?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, \`\${vertical.id} worker compatibilityDate is incorrect\`);
5010
5171
  assert(contractEntry?.deploy?.cloudflare?.routes?.effectReadiness === \`\${vertical.apiPrefix}/effect/\${vertical.stem}/readiness\`, \`\${vertical.id} Cloudflare proof readiness route is incorrect\`);
5172
+ assert(contractEntry?.config?.rspack?.output?.uniqueName === vertical.mfName, \`\${vertical.id} Rspack uniqueName is incorrect\`);
5173
+ assert(contractEntry?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal(vertical.mfName), \`\${vertical.id} Rspack chunkLoadingGlobal is incorrect\`);
5011
5174
  assert(contractEntry?.moduleFederation?.name === vertical.mfName, \`\${vertical.id} MF name is incorrect\`);
5012
5175
  assert(JSON.stringify(contractEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), \`\${vertical.id} MF exposes are incorrect\`);
5013
5176
  assert(contractEntry?.moduleFederation?.dts?.compilerInstance === '--package typescript -- tsc', \`\${vertical.id} must keep mandatory DTS compiler\`);
@@ -5031,6 +5194,13 @@ for (const vertical of fullStackVerticals) {
5031
5194
  );
5032
5195
  assert(contractEntry?.routes?.source === 'route-owned', \`\${vertical.id} routes must be route-owned\`);
5033
5196
  assert(contractEntry?.routes?.metadataExport === './src/routes/ultramodern-route-metadata', \`\${vertical.id} route metadata export is incorrect\`);
5197
+ assert(contractEntry?.routes?.privateByDefault === true, \`\${vertical.id} routes must be private by default\`);
5198
+ assert(contractEntry?.routes?.publicnessDefault === 'private-app-screen', \`\${vertical.id} route publicness default is incorrect\`);
5199
+ assert(JSON.stringify(contractEntry?.routes?.publicRoutes ?? []) === '[]', \`\${vertical.id} must not expose generated public routes by default\`);
5200
+ assert(
5201
+ (contractEntry?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen'),
5202
+ \`\${vertical.id} owned routes must be non-indexable private app screens by default\`,
5203
+ );
5034
5204
  assert(contractEntry?.styling?.federation?.owner?.id === vertical.id, \`\${vertical.id} CSS federation owner is missing\`);
5035
5205
  assert(contractEntry?.styling?.federation?.role === 'vertical-css', \`\${vertical.id} must own only vertical CSS\`);
5036
5206
  assert(contractEntry?.styling?.federation?.rootSelector === \`[data-app-id="\${vertical.id}"]\`, \`\${vertical.id} CSS root selector is incorrect\`);
@@ -5136,8 +5306,14 @@ async function fetchText(url) {
5136
5306
  ok: response.ok,
5137
5307
  status: response.status,
5138
5308
  accessControlAllowOrigin: response.headers.get('access-control-allow-origin'),
5309
+ contentSecurityPolicy: response.headers.get('content-security-policy'),
5310
+ contentSecurityPolicyReportOnly: response.headers.get('content-security-policy-report-only'),
5139
5311
  contentType: response.headers.get('content-type'),
5140
5312
  link: response.headers.get('link'),
5313
+ permissionsPolicy: response.headers.get('permissions-policy'),
5314
+ referrerPolicy: response.headers.get('referrer-policy'),
5315
+ xContentTypeOptions: response.headers.get('x-content-type-options'),
5316
+ xRobotsTag: response.headers.get('x-robots-tag'),
5141
5317
  body: await response.text(),
5142
5318
  };
5143
5319
  }
@@ -5188,6 +5364,139 @@ function assert(condition, message) {
5188
5364
  }
5189
5365
  }
5190
5366
 
5367
+ function matchesPreviewHostname(hostname, pattern) {
5368
+ const normalizedHostname = hostname.toLowerCase();
5369
+ const normalizedPattern = String(pattern || '').toLowerCase();
5370
+
5371
+ if (!normalizedPattern) {
5372
+ return false;
5373
+ }
5374
+
5375
+ if (normalizedPattern.startsWith('*.')) {
5376
+ return normalizedHostname.endsWith(normalizedPattern.slice(1));
5377
+ }
5378
+
5379
+ return normalizedHostname === normalizedPattern;
5380
+ }
5381
+
5382
+ function shouldNoindexUrl(publicUrl, noindex) {
5383
+ if (!noindex || noindex === false) {
5384
+ return false;
5385
+ }
5386
+
5387
+ const { hostname } = new URL(publicUrl);
5388
+ const normalizedHostname = hostname.toLowerCase();
5389
+
5390
+ if (
5391
+ noindex.localhost !== false &&
5392
+ (normalizedHostname === 'localhost' ||
5393
+ normalizedHostname === '127.0.0.1' ||
5394
+ normalizedHostname === '[::1]')
5395
+ ) {
5396
+ return true;
5397
+ }
5398
+
5399
+ if (
5400
+ noindex.workersDev !== false &&
5401
+ normalizedHostname.endsWith('.workers.dev')
5402
+ ) {
5403
+ return true;
5404
+ }
5405
+
5406
+ return (noindex.previewHostnames || []).some(pattern =>
5407
+ matchesPreviewHostname(normalizedHostname, pattern),
5408
+ );
5409
+ }
5410
+
5411
+ function assertHeader(evidence, response, expected, options) {
5412
+ if (expected === false || expected === undefined) {
5413
+ return;
5414
+ }
5415
+
5416
+ const actual = response[options.field];
5417
+ evidence.assertions.push({
5418
+ type: 'security-header',
5419
+ header: options.header,
5420
+ route: options.route,
5421
+ expected,
5422
+ actual,
5423
+ status: actual === expected ? 'pass' : 'fail',
5424
+ });
5425
+ assert(actual === expected, \`\${options.appId} \${options.route} is missing \${options.header}\`);
5426
+ }
5427
+
5428
+ function assertCloudflareSecurity(evidence, app, response, route, publicUrl, options = {}) {
5429
+ const security = app.deploy?.cloudflare?.security;
5430
+
5431
+ if (!security || security.enabled === false) {
5432
+ return;
5433
+ }
5434
+
5435
+ const headers = security.headers || {};
5436
+ assertHeader(evidence, response, headers.referrerPolicy, {
5437
+ appId: app.id,
5438
+ field: 'referrerPolicy',
5439
+ header: 'referrer-policy',
5440
+ route,
5441
+ });
5442
+ assertHeader(evidence, response, headers.contentTypeOptions, {
5443
+ appId: app.id,
5444
+ field: 'xContentTypeOptions',
5445
+ header: 'x-content-type-options',
5446
+ route,
5447
+ });
5448
+ assertHeader(evidence, response, headers.permissionsPolicy, {
5449
+ appId: app.id,
5450
+ field: 'permissionsPolicy',
5451
+ header: 'permissions-policy',
5452
+ route,
5453
+ });
5454
+
5455
+ const csp = security.contentSecurityPolicy;
5456
+ if (options.html && csp?.mode !== 'off') {
5457
+ const header =
5458
+ csp?.mode === 'enforce'
5459
+ ? 'content-security-policy'
5460
+ : 'content-security-policy-report-only';
5461
+ const actual =
5462
+ csp?.mode === 'enforce'
5463
+ ? response.contentSecurityPolicy
5464
+ : response.contentSecurityPolicyReportOnly;
5465
+ const expectedDirectives = ['script-src', 'style-src', 'connect-src'];
5466
+ const missingDirectives = expectedDirectives.filter(
5467
+ directive => !actual?.includes(directive),
5468
+ );
5469
+
5470
+ evidence.assertions.push({
5471
+ type: 'security-csp',
5472
+ header,
5473
+ route,
5474
+ mode: csp?.mode ?? 'report-only',
5475
+ actual,
5476
+ missingDirectives,
5477
+ status: actual && missingDirectives.length === 0 ? 'pass' : 'fail',
5478
+ });
5479
+ assert(actual, \`\${app.id} \${route} is missing \${header}\`);
5480
+ assert(
5481
+ missingDirectives.length === 0,
5482
+ \`\${app.id} \${route} CSP is missing \${missingDirectives.join(', ')}\`,
5483
+ );
5484
+ }
5485
+
5486
+ if (shouldNoindexUrl(publicUrl, security.noindex)) {
5487
+ evidence.assertions.push({
5488
+ type: 'security-noindex',
5489
+ route,
5490
+ actual: response.xRobotsTag,
5491
+ status: response.xRobotsTag === 'noindex, nofollow' ? 'pass' : 'fail',
5492
+ });
5493
+ assert(
5494
+ response.xRobotsTag === 'noindex, nofollow',
5495
+ \`\${app.id} \${route} is missing noindex X-Robots-Tag\`,
5496
+ );
5497
+ }
5498
+ }
5499
+
5191
5500
  async function validateApp(app, publicUrl) {
5192
5501
  const cloudflare = app.deploy?.cloudflare;
5193
5502
  const routes = cloudflare?.routes ?? {};
@@ -5208,6 +5517,9 @@ async function validateApp(app, publicUrl) {
5208
5517
  statusCode: ssr.status,
5209
5518
  });
5210
5519
  assert(ssr.ok, \`\${app.id} SSR route returned HTTP \${ssr.status}\`);
5520
+ assertCloudflareSecurity(evidence, app, ssr, ssrRoute, publicUrl, {
5521
+ html: true,
5522
+ });
5211
5523
 
5212
5524
  const uiMarker = extractUiMarker(ssr.body);
5213
5525
  evidence.assertions.push({
@@ -5261,6 +5573,7 @@ async function validateApp(app, publicUrl) {
5261
5573
  manifest.ok,
5262
5574
  \`\${app.id} MF manifest returned HTTP \${manifest.status}\`,
5263
5575
  );
5576
+ assertCloudflareSecurity(evidence, app, manifest, manifestRoute, publicUrl);
5264
5577
  evidence.assertions.push({
5265
5578
  type: 'mf-manifest-cors',
5266
5579
  route: manifestRoute,
@@ -5300,6 +5613,7 @@ async function validateApp(app, publicUrl) {
5300
5613
  statusCode: locale.status,
5301
5614
  });
5302
5615
  assert(locale.ok, \`\${app.id} locale JSON returned HTTP \${locale.status}\`);
5616
+ assertCloudflareSecurity(evidence, app, locale, localeRoute, publicUrl);
5303
5617
  evidence.assertions.push({
5304
5618
  type: 'i18n-cors',
5305
5619
  route: localeRoute,
@@ -1,3 +1,118 @@
1
- declare const i18n: any;
2
- declare const localeKeys: any;
1
+ import { I18n } from '@modern-js/i18n-utils';
2
+ declare const i18n: I18n;
3
+ declare const localeKeys: {
4
+ prompt: {
5
+ projectName: string;
6
+ };
7
+ error: {
8
+ projectNameEmpty: string;
9
+ directoryExists: string;
10
+ invalidRouter: string;
11
+ invalidBffRuntime: string;
12
+ createFailed: string;
13
+ };
14
+ message: {
15
+ welcome: string;
16
+ success: string;
17
+ nextSteps: string;
18
+ step1: string;
19
+ step2: string;
20
+ step3: string;
21
+ };
22
+ help: {
23
+ title: string;
24
+ description: string;
25
+ usage: string;
26
+ usageExample: string;
27
+ options: string;
28
+ optionHelp: string;
29
+ optionVersion: string;
30
+ optionLang: string;
31
+ optionRouter: string;
32
+ optionBff: string;
33
+ optionBffRuntime: string;
34
+ optionTailwind: string;
35
+ optionWorkspace: string;
36
+ optionUltramodernWorkspace: string;
37
+ optionUltramodernPackageSource: string;
38
+ optionUltramodernPackageScope: string;
39
+ optionUltramodernPackageNamePrefix: string;
40
+ optionVertical: string;
41
+ optionSub: string;
42
+ examples: string;
43
+ example1: string;
44
+ example2: string;
45
+ example3: string;
46
+ example4: string;
47
+ example5: string;
48
+ example6: string;
49
+ example7: string;
50
+ example8: string;
51
+ example9: string;
52
+ example10: string;
53
+ example11: string;
54
+ example12: string;
55
+ moreInfo: string;
56
+ };
57
+ version: {
58
+ message: string;
59
+ };
60
+ } | {
61
+ prompt: {
62
+ projectName: string;
63
+ };
64
+ error: {
65
+ projectNameEmpty: string;
66
+ directoryExists: string;
67
+ invalidRouter: string;
68
+ invalidBffRuntime: string;
69
+ createFailed: string;
70
+ };
71
+ message: {
72
+ welcome: string;
73
+ success: string;
74
+ nextSteps: string;
75
+ step1: string;
76
+ step2: string;
77
+ step3: string;
78
+ };
79
+ help: {
80
+ title: string;
81
+ description: string;
82
+ usage: string;
83
+ usageExample: string;
84
+ options: string;
85
+ optionHelp: string;
86
+ optionVersion: string;
87
+ optionLang: string;
88
+ optionRouter: string;
89
+ optionBff: string;
90
+ optionBffRuntime: string;
91
+ optionTailwind: string;
92
+ optionWorkspace: string;
93
+ optionUltramodernWorkspace: string;
94
+ optionUltramodernPackageSource: string;
95
+ optionUltramodernPackageScope: string;
96
+ optionUltramodernPackageNamePrefix: string;
97
+ optionVertical: string;
98
+ optionSub: string;
99
+ examples: string;
100
+ example1: string;
101
+ example2: string;
102
+ example3: string;
103
+ example4: string;
104
+ example5: string;
105
+ example6: string;
106
+ example7: string;
107
+ example8: string;
108
+ example9: string;
109
+ example10: string;
110
+ example11: string;
111
+ example12: string;
112
+ moreInfo: string;
113
+ };
114
+ version: {
115
+ message: string;
116
+ };
117
+ };
3
118
  export { i18n, localeKeys };
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.2.0-ultramodern.101",
24
+ "version": "3.2.0-ultramodern.103",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/index.js",
27
27
  "bin": {
@@ -41,7 +41,7 @@
41
41
  "@types/node": "^25.9.1",
42
42
  "@typescript/native-preview": "7.0.0-dev.20260527.2",
43
43
  "tsx": "^4.22.3",
44
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.101"
44
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.103"
45
45
  },
46
46
  "publishConfig": {
47
47
  "registry": "https://registry.npmjs.org/",
@@ -54,6 +54,6 @@
54
54
  "start": "node ./dist/index.js"
55
55
  },
56
56
  "ultramodern": {
57
- "frameworkVersion": "3.2.0-ultramodern.101"
57
+ "frameworkVersion": "3.2.0-ultramodern.103"
58
58
  }
59
59
  }
@@ -49,6 +49,7 @@ The default app is intentionally monolith-friendly:
49
49
  | --- | --- |
50
50
  | App routes | Locale-prefixed pages under `src/routes/[lang]` |
51
51
  | Copy | English and Czech resources in `config/public/locales` |
52
+ | Web defaults | Local favicon/logo assets, localized metadata, semantic starter markup |
52
53
  | Styling | App-local CSS, with Tailwind files only when selected |
53
54
  | Server logic | Optional BFF entrypoints under `api/` |
54
55
  | Tests | Rstest smoke coverage in `tests/` |
@@ -66,6 +67,11 @@ real routes, actions, and API calls. Put user-visible text in
66
67
  `config/public/locales/<lang>/translation.json`, then render it through
67
68
  `react-i18next` or `@modern-js/plugin-i18n/runtime`.
68
69
 
70
+ The starter keeps favicon and logo assets local in `config/favicon.svg` and
71
+ `config/public/assets/ultramodern-logo.svg`. Replace those files when your app
72
+ has product branding. The localized page title and description live in the same
73
+ translation resources as the visible UI copy.
74
+
69
75
  Tune the preset in `modern.config.ts`. Production builds require
70
76
  `MODERN_PUBLIC_SITE_URL` so canonical and `hreflang` URLs use your deployed
71
77
  origin. The local fallback is `http://localhost:8080`.