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

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.
Files changed (118) hide show
  1. package/README.md +35 -12
  2. package/dist/cjs/create-package-root.cjs +7 -9
  3. package/dist/cjs/index.cjs +74 -44
  4. package/dist/cjs/locale/en.cjs +6 -7
  5. package/dist/cjs/locale/zh.cjs +6 -7
  6. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +337 -0
  7. package/dist/cjs/ultramodern-workspace/app-files.cjs +223 -0
  8. package/dist/cjs/ultramodern-workspace/contracts.cjs +836 -0
  9. package/dist/cjs/ultramodern-workspace/demo-components.cjs +422 -0
  10. package/dist/cjs/ultramodern-workspace/descriptors.cjs +222 -0
  11. package/dist/cjs/ultramodern-workspace/effect-api.cjs +952 -0
  12. package/dist/cjs/ultramodern-workspace/fs-io.cjs +191 -0
  13. package/dist/cjs/ultramodern-workspace/index.cjs +48 -0
  14. package/dist/cjs/ultramodern-workspace/locales.cjs +173 -0
  15. package/dist/cjs/ultramodern-workspace/module-federation.cjs +487 -0
  16. package/dist/cjs/ultramodern-workspace/naming.cjs +161 -0
  17. package/dist/cjs/ultramodern-workspace/package-json.cjs +406 -0
  18. package/dist/cjs/ultramodern-workspace/package-source.cjs +59 -0
  19. package/dist/cjs/ultramodern-workspace/policy.cjs +248 -0
  20. package/dist/cjs/ultramodern-workspace/public-surface.cjs +268 -0
  21. package/dist/cjs/ultramodern-workspace/routes.cjs +375 -0
  22. package/dist/cjs/ultramodern-workspace/types.cjs +61 -0
  23. package/dist/cjs/ultramodern-workspace/versions.cjs +153 -0
  24. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +153 -0
  25. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +175 -0
  26. package/dist/esm/create-package-root.js +7 -9
  27. package/dist/esm/index.js +72 -42
  28. package/dist/esm/locale/en.js +6 -7
  29. package/dist/esm/locale/zh.js +6 -7
  30. package/dist/esm/ultramodern-workspace/add-vertical.js +252 -0
  31. package/dist/esm/ultramodern-workspace/app-files.js +149 -0
  32. package/dist/esm/ultramodern-workspace/contracts.js +741 -0
  33. package/dist/esm/ultramodern-workspace/demo-components.js +363 -0
  34. package/dist/esm/ultramodern-workspace/descriptors.js +133 -0
  35. package/dist/esm/ultramodern-workspace/effect-api.js +854 -0
  36. package/dist/esm/ultramodern-workspace/fs-io.js +90 -0
  37. package/dist/esm/ultramodern-workspace/index.js +3 -0
  38. package/dist/esm/ultramodern-workspace/locales.js +122 -0
  39. package/dist/esm/ultramodern-workspace/module-federation.js +415 -0
  40. package/dist/esm/ultramodern-workspace/naming.js +71 -0
  41. package/dist/esm/ultramodern-workspace/package-json.js +338 -0
  42. package/dist/esm/ultramodern-workspace/package-source.js +21 -0
  43. package/dist/esm/ultramodern-workspace/policy.js +183 -0
  44. package/dist/esm/ultramodern-workspace/public-surface.js +183 -0
  45. package/dist/esm/ultramodern-workspace/routes.js +280 -0
  46. package/dist/esm/ultramodern-workspace/types.js +16 -0
  47. package/dist/esm/ultramodern-workspace/versions.js +34 -0
  48. package/dist/esm/ultramodern-workspace/workspace-scripts.js +91 -0
  49. package/dist/esm/ultramodern-workspace/write-workspace.js +121 -0
  50. package/dist/esm-node/create-package-root.js +7 -9
  51. package/dist/esm-node/index.js +72 -42
  52. package/dist/esm-node/locale/en.js +6 -7
  53. package/dist/esm-node/locale/zh.js +6 -7
  54. package/dist/esm-node/ultramodern-workspace/add-vertical.js +253 -0
  55. package/dist/esm-node/ultramodern-workspace/app-files.js +150 -0
  56. package/dist/esm-node/ultramodern-workspace/contracts.js +742 -0
  57. package/dist/esm-node/ultramodern-workspace/demo-components.js +364 -0
  58. package/dist/esm-node/ultramodern-workspace/descriptors.js +134 -0
  59. package/dist/esm-node/ultramodern-workspace/effect-api.js +855 -0
  60. package/dist/esm-node/ultramodern-workspace/fs-io.js +91 -0
  61. package/dist/esm-node/ultramodern-workspace/index.js +4 -0
  62. package/dist/esm-node/ultramodern-workspace/locales.js +123 -0
  63. package/dist/esm-node/ultramodern-workspace/module-federation.js +416 -0
  64. package/dist/esm-node/ultramodern-workspace/naming.js +72 -0
  65. package/dist/esm-node/ultramodern-workspace/package-json.js +339 -0
  66. package/dist/esm-node/ultramodern-workspace/package-source.js +22 -0
  67. package/dist/esm-node/ultramodern-workspace/policy.js +184 -0
  68. package/dist/esm-node/ultramodern-workspace/public-surface.js +184 -0
  69. package/dist/esm-node/ultramodern-workspace/routes.js +281 -0
  70. package/dist/esm-node/ultramodern-workspace/types.js +17 -0
  71. package/dist/esm-node/ultramodern-workspace/versions.js +35 -0
  72. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +92 -0
  73. package/dist/esm-node/ultramodern-workspace/write-workspace.js +122 -0
  74. package/dist/types/locale/en.d.ts +4 -5
  75. package/dist/types/locale/index.d.ts +8 -10
  76. package/dist/types/locale/zh.d.ts +4 -5
  77. package/dist/types/ultramodern-workspace/add-vertical.d.ts +19 -0
  78. package/dist/types/ultramodern-workspace/app-files.d.ts +14 -0
  79. package/dist/types/ultramodern-workspace/contracts.d.ts +21 -0
  80. package/dist/types/ultramodern-workspace/demo-components.d.ts +9 -0
  81. package/dist/types/ultramodern-workspace/descriptors.d.ts +39 -0
  82. package/dist/types/ultramodern-workspace/effect-api.d.ts +73 -0
  83. package/dist/types/ultramodern-workspace/fs-io.d.ts +18 -0
  84. package/dist/types/ultramodern-workspace/index.d.ts +4 -0
  85. package/dist/types/ultramodern-workspace/locales.d.ts +183 -0
  86. package/dist/types/ultramodern-workspace/module-federation.d.ts +16 -0
  87. package/dist/types/ultramodern-workspace/naming.d.ts +16 -0
  88. package/dist/types/ultramodern-workspace/package-json.d.ts +12 -0
  89. package/dist/types/ultramodern-workspace/package-source.d.ts +2 -0
  90. package/dist/types/ultramodern-workspace/policy.d.ts +60 -0
  91. package/dist/types/ultramodern-workspace/public-surface.d.ts +37 -0
  92. package/dist/types/ultramodern-workspace/routes.d.ts +25 -0
  93. package/dist/types/ultramodern-workspace/types.d.ts +95 -0
  94. package/dist/types/ultramodern-workspace/versions.d.ts +38 -0
  95. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
  96. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -0
  97. package/package.json +4 -3
  98. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +1 -4
  99. package/template-workspace/.mise.toml.handlebars +1 -0
  100. package/template-workspace/{AGENTS.md → AGENTS.md.handlebars} +12 -7
  101. package/template-workspace/README.md.handlebars +40 -24
  102. package/template-workspace/{pnpm-workspace.yaml → pnpm-workspace.yaml.handlebars} +2 -2
  103. package/template-workspace/scripts/bootstrap-agent-skills.mjs +31 -51
  104. package/templates/app/shell-frame.tsx +49 -0
  105. package/templates/app/ultramodern-route-head.tsx.handlebars +142 -0
  106. package/templates/packages/shared-contracts-index.ts +466 -0
  107. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +69 -0
  108. package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mjs +9 -0
  109. package/templates/workspace-scripts/generate-public-surface-assets.mjs +529 -0
  110. package/templates/workspace-scripts/proof-cloudflare-version.mjs +125 -0
  111. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +851 -0
  112. package/templates/workspace-scripts/ultramodern-performance-readiness.config.mjs +7 -0
  113. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +223 -0
  114. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +656 -0
  115. package/dist/cjs/ultramodern-workspace.cjs +0 -6797
  116. package/dist/esm/ultramodern-workspace.js +0 -6738
  117. package/dist/esm-node/ultramodern-workspace.js +0 -6739
  118. package/dist/types/ultramodern-workspace.d.ts +0 -29
@@ -0,0 +1,851 @@
1
+ function joinUrl(baseUrl, routePath) {
2
+ return new URL(routePath, baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`);
3
+ }
4
+
5
+ function normalizeUrlWithTrailingSlash(url) {
6
+ return url.endsWith('/') ? url : `${url}/`;
7
+ }
8
+
9
+ async function fetchText(url) {
10
+ const response = await fetch(url);
11
+ return {
12
+ ok: response.ok,
13
+ status: response.status,
14
+ accessControlAllowOrigin: response.headers.get('access-control-allow-origin'),
15
+ cacheControl: response.headers.get('cache-control'),
16
+ contentLength: response.headers.get('content-length'),
17
+ contentSecurityPolicy: response.headers.get('content-security-policy'),
18
+ contentSecurityPolicyReportOnly: response.headers.get('content-security-policy-report-only'),
19
+ contentType: response.headers.get('content-type'),
20
+ link: response.headers.get('link'),
21
+ permissionsPolicy: response.headers.get('permissions-policy'),
22
+ referrerPolicy: response.headers.get('referrer-policy'),
23
+ xContentTypeOptions: response.headers.get('x-content-type-options'),
24
+ xRobotsTag: response.headers.get('x-robots-tag'),
25
+ body: await response.text(),
26
+ };
27
+ }
28
+
29
+ function parseMaybeJson(body) {
30
+ try {
31
+ return JSON.parse(body);
32
+ } catch {
33
+ return undefined;
34
+ }
35
+ }
36
+
37
+ function markerFromJson(value) {
38
+ if (!value || typeof value !== 'object') {
39
+ return undefined;
40
+ }
41
+ if (value.marker && typeof value.marker.build === 'string') {
42
+ return value.marker.build;
43
+ }
44
+ if (typeof value.build === 'string') {
45
+ return value.build;
46
+ }
47
+ for (const nested of Object.values(value)) {
48
+ if (Array.isArray(nested)) {
49
+ for (const item of nested) {
50
+ const marker = markerFromJson(item);
51
+ if (marker) {
52
+ return marker;
53
+ }
54
+ }
55
+ } else {
56
+ const marker = markerFromJson(nested);
57
+ if (marker) {
58
+ return marker;
59
+ }
60
+ }
61
+ }
62
+ return undefined;
63
+ }
64
+
65
+ function extractUiMarker(html) {
66
+ return html.match(/data-build-marker=["']([^"']+)["']/u)?.[1];
67
+ }
68
+
69
+ function assert(condition, message) {
70
+ if (!condition) {
71
+ throw new Error(message);
72
+ }
73
+ }
74
+
75
+ function responseByteLength(response) {
76
+ return Buffer.byteLength(response.body, 'utf8');
77
+ }
78
+
79
+ function assertByteBudget(evidence, app, response, options) {
80
+ const bytes = responseByteLength(response);
81
+ const passed = bytes <= options.maxBytes;
82
+ evidence.assertions.push({
83
+ type: 'byte-budget',
84
+ label: options.label,
85
+ route: options.route,
86
+ actualBytes: bytes,
87
+ maxBytes: options.maxBytes,
88
+ status: passed ? 'pass' : 'fail',
89
+ });
90
+ assert(
91
+ passed,
92
+ app.id + ' ' + options.route + ' exceeds ' + options.label + ' byte budget: ' + bytes + ' > ' + options.maxBytes,
93
+ );
94
+ }
95
+
96
+ function assertContentType(evidence, app, response, options) {
97
+ const actual = response.contentType ?? '';
98
+ const passed = actual.toLowerCase().includes(options.includes);
99
+ evidence.assertions.push({
100
+ type: 'content-type',
101
+ route: options.route,
102
+ expectedIncludes: options.includes,
103
+ actual,
104
+ status: passed ? 'pass' : 'fail',
105
+ });
106
+ assert(passed, app.id + ' ' + options.route + ' content-type must include ' + options.includes);
107
+ }
108
+
109
+ function assertCacheControl(evidence, app, response, options) {
110
+ const actual = response.cacheControl ?? '';
111
+ const passed = options.required === false || actual.trim() !== '';
112
+ evidence.assertions.push({
113
+ type: 'cache-control',
114
+ route: options.route,
115
+ actual,
116
+ status: passed ? 'pass' : 'fail',
117
+ });
118
+ assert(passed, app.id + ' ' + options.route + ' is missing cache-control');
119
+ }
120
+
121
+ function matchesPreviewHostname(hostname, pattern) {
122
+ const normalizedHostname = hostname.toLowerCase();
123
+ const normalizedPattern = String(pattern || '').toLowerCase();
124
+
125
+ if (!normalizedPattern) {
126
+ return false;
127
+ }
128
+
129
+ if (normalizedPattern.startsWith('*.')) {
130
+ return normalizedHostname.endsWith(normalizedPattern.slice(1));
131
+ }
132
+
133
+ return normalizedHostname === normalizedPattern;
134
+ }
135
+
136
+ function shouldNoindexUrl(publicUrl, noindex) {
137
+ if (!noindex || noindex === false) {
138
+ return false;
139
+ }
140
+
141
+ const { hostname } = new URL(publicUrl);
142
+ const normalizedHostname = hostname.toLowerCase();
143
+
144
+ if (
145
+ noindex.localhost !== false &&
146
+ (normalizedHostname === 'localhost' ||
147
+ normalizedHostname === '127.0.0.1' ||
148
+ normalizedHostname === '[::1]')
149
+ ) {
150
+ return true;
151
+ }
152
+
153
+ if (
154
+ noindex.workersDev !== false &&
155
+ normalizedHostname.endsWith('.workers.dev')
156
+ ) {
157
+ return true;
158
+ }
159
+
160
+ return (noindex.previewHostnames || []).some(pattern =>
161
+ matchesPreviewHostname(normalizedHostname, pattern),
162
+ );
163
+ }
164
+
165
+ function assertHeader(evidence, response, expected, options) {
166
+ if (expected === false || expected === undefined) {
167
+ return;
168
+ }
169
+
170
+ const actual = response[options.field];
171
+ evidence.assertions.push({
172
+ type: 'security-header',
173
+ header: options.header,
174
+ route: options.route,
175
+ expected,
176
+ actual,
177
+ status: actual === expected ? 'pass' : 'fail',
178
+ });
179
+ assert(actual === expected, `${options.appId} ${options.route} is missing ${options.header}`);
180
+ }
181
+
182
+ function assertCloudflareSecurity(evidence, app, response, route, publicUrl, options = {}) {
183
+ const security = app.deploy?.cloudflare?.security;
184
+
185
+ if (!security || security.enabled === false) {
186
+ return;
187
+ }
188
+
189
+ const headers = security.headers || {};
190
+ assertHeader(evidence, response, headers.referrerPolicy, {
191
+ appId: app.id,
192
+ field: 'referrerPolicy',
193
+ header: 'referrer-policy',
194
+ route,
195
+ });
196
+ assertHeader(evidence, response, headers.contentTypeOptions, {
197
+ appId: app.id,
198
+ field: 'xContentTypeOptions',
199
+ header: 'x-content-type-options',
200
+ route,
201
+ });
202
+ assertHeader(evidence, response, headers.permissionsPolicy, {
203
+ appId: app.id,
204
+ field: 'permissionsPolicy',
205
+ header: 'permissions-policy',
206
+ route,
207
+ });
208
+
209
+ const csp = security.contentSecurityPolicy;
210
+ if (options.html && csp?.mode !== 'off') {
211
+ const header =
212
+ csp?.mode === 'enforce'
213
+ ? 'content-security-policy'
214
+ : 'content-security-policy-report-only';
215
+ const actual =
216
+ csp?.mode === 'enforce'
217
+ ? response.contentSecurityPolicy
218
+ : response.contentSecurityPolicyReportOnly;
219
+ const expectedDirectives = ['script-src', 'style-src', 'connect-src'];
220
+ const missingDirectives = expectedDirectives.filter(
221
+ directive => !actual?.includes(directive),
222
+ );
223
+
224
+ evidence.assertions.push({
225
+ type: 'security-csp',
226
+ header,
227
+ route,
228
+ mode: csp?.mode ?? 'report-only',
229
+ actual,
230
+ missingDirectives,
231
+ status: actual && missingDirectives.length === 0 ? 'pass' : 'fail',
232
+ });
233
+ assert(actual, `${app.id} ${route} is missing ${header}`);
234
+ assert(
235
+ missingDirectives.length === 0,
236
+ `${app.id} ${route} CSP is missing ${missingDirectives.join(', ')}`,
237
+ );
238
+ }
239
+
240
+ if (shouldNoindexUrl(publicUrl, security.noindex)) {
241
+ evidence.assertions.push({
242
+ type: 'security-noindex',
243
+ route,
244
+ actual: response.xRobotsTag,
245
+ status: response.xRobotsTag === 'noindex, nofollow' ? 'pass' : 'fail',
246
+ });
247
+ assert(
248
+ response.xRobotsTag === 'noindex, nofollow',
249
+ `${app.id} ${route} is missing noindex X-Robots-Tag`,
250
+ );
251
+ }
252
+ }
253
+
254
+ function collectStringValues(value, results = []) {
255
+ if (typeof value === 'string') {
256
+ results.push(value);
257
+ return results;
258
+ }
259
+ if (Array.isArray(value)) {
260
+ for (const item of value) {
261
+ collectStringValues(item, results);
262
+ }
263
+ return results;
264
+ }
265
+ if (value && typeof value === 'object') {
266
+ for (const item of Object.values(value)) {
267
+ collectStringValues(item, results);
268
+ }
269
+ }
270
+ return results;
271
+ }
272
+
273
+ function assertNoPublicSourcemapRefs(evidence, app, manifestJson) {
274
+ const sourcemapRefs = collectStringValues(manifestJson).filter(value =>
275
+ /\.map(?:$|[?#])/u.test(value),
276
+ );
277
+ evidence.assertions.push({
278
+ type: 'sourcemap-policy',
279
+ actual: sourcemapRefs,
280
+ status: sourcemapRefs.length === 0 ? 'pass' : 'fail',
281
+ });
282
+ assert(
283
+ sourcemapRefs.length === 0,
284
+ app.id + ' MF manifest must not publicly reference sourcemaps',
285
+ );
286
+ }
287
+
288
+ function extractPreloadStyleUrls(linkHeader, publicUrl) {
289
+ const urls = [];
290
+ for (const match of String(linkHeader || '').matchAll(/<([^>]+)>\s*;[^,]*rel=preload[^,]*as=style/giu)) {
291
+ urls.push(String(joinUrl(publicUrl, match[1])));
292
+ }
293
+ return urls;
294
+ }
295
+
296
+ function htmlHasRobotsDirective(html, expectedContent) {
297
+ return htmlHasTagWithAttributes(html, 'meta', {
298
+ name: 'robots',
299
+ content: expectedContent,
300
+ });
301
+ }
302
+
303
+ function escapeRegExp(value) {
304
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
305
+ }
306
+
307
+ function htmlHasTagWithAttributes(html, tagName, attributes) {
308
+ const tagPattern = new RegExp(`<${tagName}\\b[^>]*>`, 'giu');
309
+ const tags = html.match(tagPattern) || [];
310
+ return tags.some(tag =>
311
+ Object.entries(attributes).every(([name, value]) => {
312
+ const attrPattern = new RegExp(
313
+ `\\b${escapeRegExp(name)}=["']${escapeRegExp(value)}["']`,
314
+ 'iu',
315
+ );
316
+ return attrPattern.test(tag);
317
+ }),
318
+ );
319
+ }
320
+
321
+ function assertHeadTag(evidence, html, options) {
322
+ const found = htmlHasTagWithAttributes(
323
+ html,
324
+ options.tag,
325
+ options.attributes,
326
+ );
327
+ evidence.assertions.push({
328
+ type: 'ssr-head',
329
+ route: options.route,
330
+ tag: options.tag,
331
+ attributes: options.attributes,
332
+ status: found ? 'pass' : 'fail',
333
+ });
334
+ assert(found, `${options.appId} ${options.route} SSR head is missing ${options.label}`);
335
+ }
336
+
337
+ async function validateSsrHead(evidence, app, publicUrl, ssrRoute, ssr) {
338
+ const titleFound = /<title\b[^>]*>[^<]+<\/title>/iu.test(ssr.body);
339
+ evidence.assertions.push({
340
+ type: 'ssr-head',
341
+ route: ssrRoute,
342
+ tag: 'title',
343
+ status: titleFound ? 'pass' : 'fail',
344
+ });
345
+ assert(titleFound, `${app.id} ${ssrRoute} SSR head is missing title`);
346
+ assertHeadTag(evidence, ssr.body, {
347
+ appId: app.id,
348
+ route: ssrRoute,
349
+ tag: 'meta',
350
+ attributes: { name: 'description' },
351
+ label: 'description meta',
352
+ });
353
+ assertHeadTag(evidence, ssr.body, {
354
+ appId: app.id,
355
+ route: ssrRoute,
356
+ tag: 'meta',
357
+ attributes: { name: 'robots' },
358
+ label: 'robots meta',
359
+ });
360
+
361
+ const publicSurface = app.routes?.publicSurface ?? {};
362
+ const routeEntry = (publicSurface.routeEntries ?? [])[0];
363
+ if (!routeEntry) {
364
+ const canonicalFound = htmlHasTagWithAttributes(ssr.body, 'link', {
365
+ rel: 'canonical',
366
+ });
367
+ evidence.assertions.push({
368
+ type: 'ssr-head-private-canonical',
369
+ route: ssrRoute,
370
+ status: canonicalFound ? 'fail' : 'pass',
371
+ });
372
+ assert(!canonicalFound, `${app.id} ${ssrRoute} private SSR head must not emit canonical links`);
373
+ return;
374
+ }
375
+
376
+ const publicRoute = routeEntry.localeUrlPaths?.en ?? publicSurface.concreteUrlPaths?.[0];
377
+ const headRoute = publicRoute || ssrRoute;
378
+ const headResponse =
379
+ headRoute === ssrRoute ? ssr : await fetchText(joinUrl(publicUrl, headRoute));
380
+ if (headRoute !== ssrRoute) {
381
+ evidence.assertions.push({
382
+ type: 'ssr-head-route',
383
+ route: headRoute,
384
+ status: headResponse.ok ? 'pass' : 'fail',
385
+ statusCode: headResponse.status,
386
+ });
387
+ assert(headResponse.ok, `${app.id} public head route returned HTTP ${headResponse.status}`);
388
+ assertCloudflareSecurity(evidence, app, headResponse, headRoute, publicUrl, {
389
+ html: true,
390
+ });
391
+ }
392
+ const isPreview = shouldNoindexUrl(publicUrl, app.deploy?.cloudflare?.security?.noindex);
393
+ const robotsIndexable = htmlHasRobotsDirective(headResponse.body, 'index, follow');
394
+ evidence.assertions.push({
395
+ type: 'indexing-policy',
396
+ route: headRoute,
397
+ mode: isPreview ? 'preview' : 'production',
398
+ xRobotsTag: headResponse.xRobotsTag,
399
+ htmlRobotsIndexable: robotsIndexable,
400
+ status:
401
+ isPreview || (headResponse.xRobotsTag !== 'noindex, nofollow' && robotsIndexable)
402
+ ? 'pass'
403
+ : 'fail',
404
+ });
405
+ if (!isPreview) {
406
+ assert(
407
+ headResponse.xRobotsTag !== 'noindex, nofollow' && robotsIndexable,
408
+ `${app.id} ${headRoute} production public route must be indexable`,
409
+ );
410
+ }
411
+
412
+ const canonicalUrl = String(joinUrl(publicUrl, headRoute));
413
+ assertHeadTag(evidence, headResponse.body, {
414
+ appId: app.id,
415
+ route: headRoute,
416
+ tag: 'link',
417
+ attributes: { rel: 'canonical', href: canonicalUrl },
418
+ label: 'canonical link',
419
+ });
420
+ for (const language of app.routes?.publicHead?.alternates?.hreflang ?? []) {
421
+ const href = String(joinUrl(publicUrl, routeEntry.localeUrlPaths?.[language] ?? headRoute));
422
+ assertHeadTag(evidence, headResponse.body, {
423
+ appId: app.id,
424
+ route: headRoute,
425
+ tag: 'link',
426
+ attributes: { rel: 'alternate', hreflang: language, href },
427
+ label: `hreflang ${language}`,
428
+ });
429
+ }
430
+ assertHeadTag(evidence, headResponse.body, {
431
+ appId: app.id,
432
+ route: headRoute,
433
+ tag: 'link',
434
+ attributes: { rel: 'alternate', hreflang: 'x-default' },
435
+ label: 'x-default hreflang',
436
+ });
437
+ for (const property of ['og:title', 'og:description', 'og:url', 'og:type']) {
438
+ assertHeadTag(evidence, headResponse.body, {
439
+ appId: app.id,
440
+ route: headRoute,
441
+ tag: 'meta',
442
+ attributes: { property },
443
+ label: property,
444
+ });
445
+ }
446
+ for (const name of ['twitter:card', 'twitter:title', 'twitter:description']) {
447
+ assertHeadTag(evidence, headResponse.body, {
448
+ appId: app.id,
449
+ route: headRoute,
450
+ tag: 'meta',
451
+ attributes: { name },
452
+ label: name,
453
+ });
454
+ }
455
+ assertHeadTag(evidence, headResponse.body, {
456
+ appId: app.id,
457
+ route: headRoute,
458
+ tag: 'script',
459
+ attributes: { type: 'application/ld+json' },
460
+ label: 'JSON-LD structured data',
461
+ });
462
+ }
463
+
464
+ async function validateNotFound(evidence, app, publicUrl) {
465
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
466
+ const notFoundRoute =
467
+ qualityGates.statusCodes?.notFoundRoute ?? '/__ultramodern-smoke-missing';
468
+ const expectedStatus = qualityGates.statusCodes?.unknownRouteStatus ?? 404;
469
+ const response = await fetchText(joinUrl(publicUrl, notFoundRoute));
470
+ evidence.assertions.push({
471
+ type: 'status-code',
472
+ route: notFoundRoute,
473
+ expectedStatus,
474
+ actualStatus: response.status,
475
+ status: response.status === expectedStatus ? 'pass' : 'fail',
476
+ });
477
+ assert(
478
+ response.status === expectedStatus,
479
+ `${app.id} unknown route must return HTTP ${expectedStatus}, got ${response.status}`,
480
+ );
481
+ assertCloudflareSecurity(evidence, app, response, notFoundRoute, publicUrl, {
482
+ html: response.contentType?.includes('text/html'),
483
+ });
484
+ }
485
+
486
+ async function validateCssAsset(evidence, app, publicUrl, ssr) {
487
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
488
+ const budgets = qualityGates.budgets ?? {};
489
+ const styleUrls = extractPreloadStyleUrls(ssr.link, publicUrl);
490
+ evidence.assertions.push({
491
+ type: 'css-preload-assets',
492
+ actual: styleUrls,
493
+ status: styleUrls.length > 0 ? 'pass' : 'fail',
494
+ });
495
+ assert(styleUrls.length > 0, `${app.id} SSR response did not expose preloadable CSS assets`);
496
+
497
+ const styleUrl = styleUrls[0];
498
+ const route = new URL(styleUrl).pathname;
499
+ const css = await fetchText(styleUrl);
500
+ evidence.assertions.push({
501
+ type: 'css-asset',
502
+ route,
503
+ status: css.ok && css.body.trim() !== '' ? 'pass' : 'fail',
504
+ statusCode: css.status,
505
+ });
506
+ assert(css.ok, `${app.id} CSS asset returned HTTP ${css.status}`);
507
+ assert(css.body.trim() !== '', `${app.id} CSS asset is empty`);
508
+ assertContentType(evidence, app, css, {
509
+ route,
510
+ includes: 'text/css',
511
+ });
512
+ assertCacheControl(evidence, app, css, {
513
+ route,
514
+ required: qualityGates.assets?.cacheControlRequiredForCss,
515
+ });
516
+ assertByteBudget(evidence, app, css, {
517
+ label: 'cssAssetMaxBytes',
518
+ maxBytes: budgets.cssAssetMaxBytes ?? 750_000,
519
+ route,
520
+ });
521
+ }
522
+
523
+ async function validatePublicSurface(evidence, app, publicUrl) {
524
+ const publicSurface = app.routes?.publicSurface ?? {};
525
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
526
+ const budgets = qualityGates.budgets ?? {};
527
+ const hasPublicRoutes =
528
+ (publicSurface.publicRoutes ?? []).length > 0 ||
529
+ (publicSurface.routeEntries ?? []).length > 0 ||
530
+ (publicSurface.contentSources ?? []).length > 0;
531
+
532
+ const robotsRoute = '/robots.txt';
533
+ const robots = await fetchText(joinUrl(publicUrl, robotsRoute));
534
+ evidence.assertions.push({
535
+ type: 'public-surface-robots',
536
+ route: robotsRoute,
537
+ status: robots.ok ? 'pass' : 'fail',
538
+ statusCode: robots.status,
539
+ });
540
+ assert(robots.ok, `${app.id} robots.txt returned HTTP ${robots.status}`);
541
+ assertContentType(evidence, app, robots, {
542
+ route: robotsRoute,
543
+ includes: 'text/plain',
544
+ });
545
+ assertCloudflareSecurity(evidence, app, robots, robotsRoute, publicUrl);
546
+
547
+ if (!hasPublicRoutes) {
548
+ const disallowsAll = robots.body.includes('Disallow: /');
549
+ const referencesSitemap = /\bSitemap:/iu.test(robots.body);
550
+ evidence.assertions.push({
551
+ type: 'public-surface-private-robots',
552
+ route: robotsRoute,
553
+ disallowsAll,
554
+ referencesSitemap,
555
+ status: disallowsAll && !referencesSitemap ? 'pass' : 'fail',
556
+ });
557
+ assert(disallowsAll, `${app.id} private public surface robots.txt must disallow crawling`);
558
+ assert(!referencesSitemap, `${app.id} private public surface robots.txt must not reference sitemap.xml`);
559
+ return;
560
+ }
561
+
562
+ const sitemapRoute = '/sitemap.xml';
563
+ const sitemap = await fetchText(joinUrl(publicUrl, sitemapRoute));
564
+ evidence.assertions.push({
565
+ type: 'public-surface-sitemap',
566
+ route: sitemapRoute,
567
+ status: sitemap.ok ? 'pass' : 'fail',
568
+ statusCode: sitemap.status,
569
+ });
570
+ assert(sitemap.ok, `${app.id} sitemap.xml returned HTTP ${sitemap.status}`);
571
+ assertContentType(evidence, app, sitemap, {
572
+ route: sitemapRoute,
573
+ includes: 'xml',
574
+ });
575
+ assertByteBudget(evidence, app, sitemap, {
576
+ label: 'sitemapXmlMaxBytes',
577
+ maxBytes: budgets.sitemapXmlMaxBytes ?? 500_000,
578
+ route: sitemapRoute,
579
+ });
580
+
581
+ const sitemapUrl = String(joinUrl(publicUrl, sitemapRoute));
582
+ const robotsReferencesSitemap = robots.body.includes(`Sitemap: ${sitemapUrl}`);
583
+ evidence.assertions.push({
584
+ type: 'robots-sitemap-consistency',
585
+ route: robotsRoute,
586
+ sitemapUrl,
587
+ status: robotsReferencesSitemap ? 'pass' : 'fail',
588
+ });
589
+ assert(
590
+ robotsReferencesSitemap,
591
+ `${app.id} robots.txt must reference generated sitemap.xml`,
592
+ );
593
+
594
+ for (const urlPath of publicSurface.concreteUrlPaths ?? []) {
595
+ const loc = `<loc>${String(joinUrl(publicUrl, urlPath))}</loc>`;
596
+ evidence.assertions.push({
597
+ type: 'sitemap-route',
598
+ route: urlPath,
599
+ status: sitemap.body.includes(loc) ? 'pass' : 'fail',
600
+ });
601
+ assert(sitemap.body.includes(loc), `${app.id} sitemap.xml is missing ${urlPath}`);
602
+ }
603
+
604
+ const manifestRoute = '/site.webmanifest';
605
+ const webManifest = await fetchText(joinUrl(publicUrl, manifestRoute));
606
+ const webManifestJson = parseMaybeJson(webManifest.body);
607
+ evidence.assertions.push({
608
+ type: 'public-surface-webmanifest',
609
+ route: manifestRoute,
610
+ status: webManifest.ok && webManifestJson ? 'pass' : 'fail',
611
+ statusCode: webManifest.status,
612
+ });
613
+ assert(webManifest.ok, `${app.id} site.webmanifest returned HTTP ${webManifest.status}`);
614
+ assert(webManifestJson, `${app.id} site.webmanifest must be valid JSON`);
615
+ assertContentType(evidence, app, webManifest, {
616
+ route: manifestRoute,
617
+ includes: 'manifest',
618
+ });
619
+ }
620
+
621
+ function createAppEvidence(app, publicUrl) {
622
+ const cloudflare = app.deploy?.cloudflare;
623
+
624
+ return {
625
+ appId: app.id,
626
+ publicUrl,
627
+ workerName: cloudflare?.workerName,
628
+ publicUrlEnv: cloudflare?.publicUrlEnv,
629
+ assertions: [],
630
+ };
631
+ }
632
+
633
+ async function validateSsrEvidence(evidence, app, publicUrl, routes) {
634
+ const cloudflare = app.deploy?.cloudflare;
635
+ const qualityGates = cloudflare?.qualityGates ?? {};
636
+ const budgets = qualityGates.budgets ?? {};
637
+
638
+ const ssrRoute = routes.ssr ?? '/en';
639
+ const ssr = await fetchText(joinUrl(publicUrl, ssrRoute));
640
+ evidence.assertions.push({
641
+ type: 'ssr',
642
+ route: ssrRoute,
643
+ status: ssr.ok ? 'pass' : 'fail',
644
+ statusCode: ssr.status,
645
+ });
646
+ assert(ssr.ok, `${app.id} SSR route returned HTTP ${ssr.status}`);
647
+ assertCloudflareSecurity(evidence, app, ssr, ssrRoute, publicUrl, {
648
+ html: true,
649
+ });
650
+ assertContentType(evidence, app, ssr, {
651
+ route: ssrRoute,
652
+ includes: 'text/html',
653
+ });
654
+ assertByteBudget(evidence, app, ssr, {
655
+ label: 'ssrHtmlMaxBytes',
656
+ maxBytes: budgets.ssrHtmlMaxBytes ?? 250_000,
657
+ route: ssrRoute,
658
+ });
659
+ await validateSsrHead(evidence, app, publicUrl, ssrRoute, ssr);
660
+ await validateNotFound(evidence, app, publicUrl);
661
+ await validatePublicSurface(evidence, app, publicUrl);
662
+
663
+ return ssr;
664
+ }
665
+
666
+ function validateUiMarkerEvidence(evidence, app, ssr) {
667
+ const uiMarker = extractUiMarker(ssr.body);
668
+ evidence.assertions.push({
669
+ type: 'ui-marker',
670
+ expected: app.marker?.build,
671
+ actual: uiMarker,
672
+ status: uiMarker === app.marker?.build ? 'pass' : 'fail',
673
+ });
674
+ assert(uiMarker === app.marker?.build, `${app.id} UI marker mismatch`);
675
+ }
676
+
677
+ function validateCssRootMarkerEvidence(evidence, app, ssr) {
678
+ const cssRootSelector = app.styling?.federation?.rootSelector;
679
+ const expectedAppId = cssRootSelector?.match(/data-app-id="([^"]+)"/u)?.[1];
680
+ evidence.assertions.push({
681
+ type: 'css-root-marker',
682
+ expected: cssRootSelector,
683
+ status:
684
+ expectedAppId && ssr.body.includes(`data-app-id="${expectedAppId}"`)
685
+ ? 'pass'
686
+ : 'fail',
687
+ });
688
+ assert(
689
+ expectedAppId && ssr.body.includes(`data-app-id="${expectedAppId}"`),
690
+ `${app.id} SSR response is missing CSS root marker ${cssRootSelector}`,
691
+ );
692
+ }
693
+
694
+ function validateCssPreloadLinkEvidence(evidence, app, ssr) {
695
+ const cssPreloadLinkHeader = ssr.link ?? '';
696
+ evidence.assertions.push({
697
+ type: 'css-preload-link-header',
698
+ actual: cssPreloadLinkHeader,
699
+ status:
700
+ cssPreloadLinkHeader.includes('rel=preload') &&
701
+ cssPreloadLinkHeader.includes('as=style')
702
+ ? 'pass'
703
+ : 'fail',
704
+ });
705
+ assert(
706
+ cssPreloadLinkHeader.includes('rel=preload') &&
707
+ cssPreloadLinkHeader.includes('as=style'),
708
+ `${app.id} SSR response is missing CSS preload Link headers`,
709
+ );
710
+ }
711
+
712
+ async function validateRenderedAssetEvidence(evidence, app, publicUrl, ssr) {
713
+ validateUiMarkerEvidence(evidence, app, ssr);
714
+ validateCssRootMarkerEvidence(evidence, app, ssr);
715
+ validateCssPreloadLinkEvidence(evidence, app, ssr);
716
+ await validateCssAsset(evidence, app, publicUrl, ssr);
717
+ }
718
+
719
+ async function validateModuleFederationManifestEvidence(evidence, app, publicUrl, routes) {
720
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
721
+ const budgets = qualityGates.budgets ?? {};
722
+
723
+ const manifestRoute = routes.mfManifest ?? '/mf-manifest.json';
724
+ const manifest = await fetchText(joinUrl(publicUrl, manifestRoute));
725
+ const manifestJson = parseMaybeJson(manifest.body);
726
+ evidence.assertions.push({
727
+ type: 'mf-manifest',
728
+ route: manifestRoute,
729
+ status: manifest.ok ? 'pass' : 'fail',
730
+ statusCode: manifest.status,
731
+ });
732
+ assert(
733
+ manifest.ok,
734
+ `${app.id} MF manifest returned HTTP ${manifest.status}`,
735
+ );
736
+ assertCloudflareSecurity(evidence, app, manifest, manifestRoute, publicUrl);
737
+ assertContentType(evidence, app, manifest, {
738
+ route: manifestRoute,
739
+ includes: 'json',
740
+ });
741
+ assertByteBudget(evidence, app, manifest, {
742
+ label: 'mfManifestMaxBytes',
743
+ maxBytes: budgets.mfManifestMaxBytes ?? 500_000,
744
+ route: manifestRoute,
745
+ });
746
+ assertNoPublicSourcemapRefs(evidence, app, manifestJson);
747
+ evidence.assertions.push({
748
+ type: 'mf-manifest-cors',
749
+ route: manifestRoute,
750
+ actual: manifest.accessControlAllowOrigin,
751
+ status: manifest.accessControlAllowOrigin === '*' ? 'pass' : 'fail',
752
+ });
753
+ assert(
754
+ manifest.accessControlAllowOrigin === '*',
755
+ `${app.id} MF manifest is missing Cloudflare CORS headers`,
756
+ );
757
+ const expectedPublicPath = normalizeUrlWithTrailingSlash(publicUrl);
758
+ const manifestPublicPath = manifestJson?.metaData?.publicPath;
759
+ evidence.assertions.push({
760
+ type: 'mf-manifest-public-path',
761
+ expected: expectedPublicPath,
762
+ actual: manifestPublicPath,
763
+ status: manifestPublicPath === expectedPublicPath ? 'pass' : 'fail',
764
+ });
765
+ assert(
766
+ manifestPublicPath === expectedPublicPath,
767
+ `${app.id} MF manifest publicPath must resolve remote assets from ${expectedPublicPath}`,
768
+ );
769
+ }
770
+
771
+ async function validateI18nEvidence(evidence, app, publicUrl, routes) {
772
+ const qualityGates = app.deploy?.cloudflare?.qualityGates ?? {};
773
+ const budgets = qualityGates.budgets ?? {};
774
+
775
+ const localeRoute = routes.locale ?? `/locales/en/${app.i18n?.namespace}.json`;
776
+ const locale = await fetchText(joinUrl(publicUrl, localeRoute));
777
+ const localeJson = parseMaybeJson(locale.body);
778
+ evidence.assertions.push({
779
+ type: 'i18n-marker',
780
+ namespace: app.i18n?.namespace,
781
+ route: localeRoute,
782
+ status:
783
+ locale.ok &&
784
+ localeJson &&
785
+ Object.hasOwn(localeJson, app.i18n?.namespace)
786
+ ? 'pass'
787
+ : 'fail',
788
+ statusCode: locale.status,
789
+ });
790
+ assert(locale.ok, `${app.id} locale JSON returned HTTP ${locale.status}`);
791
+ assertCloudflareSecurity(evidence, app, locale, localeRoute, publicUrl);
792
+ assertContentType(evidence, app, locale, {
793
+ route: localeRoute,
794
+ includes: 'json',
795
+ });
796
+ assertByteBudget(evidence, app, locale, {
797
+ label: 'localeJsonMaxBytes',
798
+ maxBytes: budgets.localeJsonMaxBytes ?? 100_000,
799
+ route: localeRoute,
800
+ });
801
+ evidence.assertions.push({
802
+ type: 'i18n-cors',
803
+ route: localeRoute,
804
+ actual: locale.accessControlAllowOrigin,
805
+ status: locale.accessControlAllowOrigin === '*' ? 'pass' : 'fail',
806
+ });
807
+ assert(
808
+ locale.accessControlAllowOrigin === '*',
809
+ `${app.id} locale JSON is missing Cloudflare CORS headers`,
810
+ );
811
+ assert(
812
+ localeJson && Object.hasOwn(localeJson, app.i18n?.namespace),
813
+ `${app.id} locale JSON is missing namespace ${app.i18n?.namespace}`,
814
+ );
815
+ }
816
+
817
+ async function validateReadinessEvidence(evidence, app, publicUrl, routes) {
818
+ if (routes.effectReadiness) {
819
+ const readiness = await fetchText(joinUrl(publicUrl, routes.effectReadiness));
820
+ const readinessJson = parseMaybeJson(readiness.body);
821
+ const apiMarker = markerFromJson(readinessJson);
822
+ evidence.assertions.push({
823
+ type: 'api-marker',
824
+ route: routes.effectReadiness,
825
+ expected: app.marker?.build,
826
+ actual: apiMarker,
827
+ status: readiness.ok && apiMarker === app.marker?.build ? 'pass' : 'fail',
828
+ statusCode: readiness.status,
829
+ });
830
+ assert(
831
+ readiness.ok,
832
+ `${app.id} Effect readiness returned HTTP ${readiness.status}`,
833
+ );
834
+ assert(apiMarker === app.marker?.build, `${app.id} API marker mismatch`);
835
+ }
836
+ }
837
+
838
+ async function validateApp(app, publicUrl) {
839
+ const routes = app.deploy?.cloudflare?.routes ?? {};
840
+ const evidence = createAppEvidence(app, publicUrl);
841
+
842
+ const ssr = await validateSsrEvidence(evidence, app, publicUrl, routes);
843
+ await validateRenderedAssetEvidence(evidence, app, publicUrl, ssr);
844
+ await validateModuleFederationManifestEvidence(evidence, app, publicUrl, routes);
845
+ await validateI18nEvidence(evidence, app, publicUrl, routes);
846
+ await validateReadinessEvidence(evidence, app, publicUrl, routes);
847
+
848
+ return evidence;
849
+ }
850
+
851
+ export { validateApp };