@bleedingdev/modern-js-ultramodern-create 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21

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 (442) hide show
  1. package/README.md +43 -74
  2. package/dist/cjs/cli/package-source.cjs +2 -2
  3. package/dist/cjs/cli/project-setup.cjs +2 -22
  4. package/dist/cjs/index.cjs +9 -1
  5. package/dist/cjs/locale/en.cjs +2 -1
  6. package/dist/cjs/locale/zh.cjs +2 -1
  7. package/dist/cjs/ultramodern-package-source.cjs +13 -9
  8. package/dist/cjs/ultramodern-release-cohort.cjs +6 -21
  9. package/dist/cjs/ultramodern-tooling/commands/cloudflare-output-verify.cjs +49 -56
  10. package/dist/cjs/ultramodern-tooling/commands/context.cjs +11 -29
  11. package/dist/cjs/ultramodern-tooling/commands/routes-generate-app.cjs +67 -0
  12. package/dist/cjs/ultramodern-tooling/commands/routes-generate.cjs +24 -53
  13. package/dist/cjs/ultramodern-tooling/commands/validate.cjs +5 -11
  14. package/dist/cjs/ultramodern-tooling/commands.cjs +15 -8
  15. package/dist/cjs/ultramodern-tooling/config/index.cjs +10 -24
  16. package/dist/cjs/ultramodern-tooling/config/load.cjs +12 -10
  17. package/dist/cjs/ultramodern-tooling/config/metadata.cjs +34 -32
  18. package/dist/cjs/ultramodern-tooling/config/normalize.cjs +165 -164
  19. package/dist/cjs/ultramodern-workspace/add-shell.cjs +118 -118
  20. package/dist/cjs/ultramodern-workspace/add-vertical/execute.cjs +121 -25
  21. package/dist/cjs/ultramodern-workspace/add-vertical/plan.cjs +14 -160
  22. package/dist/cjs/ultramodern-workspace/add-vertical/preflight.cjs +16 -48
  23. package/dist/cjs/ultramodern-workspace/add-vertical/preview.cjs +78 -0
  24. package/dist/cjs/ultramodern-workspace/add-vertical/shell-files.cjs +87 -24
  25. package/dist/cjs/ultramodern-workspace/add-vertical/topology.cjs +4 -1
  26. package/dist/cjs/ultramodern-workspace/add-vertical/transaction.cjs +378 -45
  27. package/dist/cjs/ultramodern-workspace/add-vertical/workspace-state.cjs +21 -44
  28. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +0 -9
  29. package/dist/cjs/ultramodern-workspace/api/client.cjs +5 -5
  30. package/dist/cjs/ultramodern-workspace/api/contracts.cjs +2 -2
  31. package/dist/cjs/ultramodern-workspace/api/rpc.cjs +20 -18
  32. package/dist/cjs/ultramodern-workspace/api/service.cjs +49 -24
  33. package/dist/cjs/ultramodern-workspace/api/shared.cjs +28 -18
  34. package/dist/cjs/ultramodern-workspace/app-files.cjs +3 -23
  35. package/dist/cjs/ultramodern-workspace/backend-federation.cjs +27 -27
  36. package/dist/cjs/ultramodern-workspace/contracts.cjs +14 -183
  37. package/dist/cjs/ultramodern-workspace/delivery-unit-schema/surfaces.cjs +85 -0
  38. package/dist/cjs/ultramodern-workspace/delivery-unit-schema/types.cjs +8 -85
  39. package/dist/cjs/ultramodern-workspace/delivery-unit-stamp.cjs +16 -4
  40. package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +27 -17
  41. package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +16 -10
  42. package/dist/cjs/ultramodern-workspace/demo-components.cjs +66 -433
  43. package/dist/cjs/ultramodern-workspace/descriptors.cjs +0 -4
  44. package/dist/cjs/ultramodern-workspace/fs-io.cjs +37 -2
  45. package/dist/cjs/ultramodern-workspace/generation-result.cjs +4 -4
  46. package/dist/cjs/ultramodern-workspace/index.cjs +2 -5
  47. package/dist/cjs/ultramodern-workspace/mf-validation/constants.cjs +0 -6
  48. package/dist/cjs/ultramodern-workspace/mf-validation/discovery.cjs +6 -6
  49. package/dist/cjs/{ultramodern-tooling/commands/migrate-strict-effect/generated-patches.cjs → ultramodern-workspace/mf-validation/exposes.cjs} +13 -17
  50. package/dist/cjs/ultramodern-workspace/mf-validation/inspect.cjs +26 -7
  51. package/dist/cjs/ultramodern-workspace/mf-validation/object-literal.cjs +21 -122
  52. package/dist/cjs/ultramodern-workspace/mf-validation/path-utils.cjs +37 -38
  53. package/dist/cjs/ultramodern-workspace/mf-validation/syntax.cjs +93 -153
  54. package/dist/cjs/ultramodern-workspace/mf-validation.cjs +4 -0
  55. package/dist/cjs/ultramodern-workspace/module-federation/config.cjs +24 -29
  56. package/dist/cjs/ultramodern-workspace/module-federation/reexport-module.cjs +39 -58
  57. package/dist/cjs/ultramodern-workspace/module-federation/remote-refs.cjs +1 -43
  58. package/dist/cjs/ultramodern-workspace/package-json.cjs +63 -37
  59. package/dist/cjs/ultramodern-workspace/patch-inventory.cjs +104 -0
  60. package/dist/cjs/ultramodern-workspace/policy.cjs +3 -577
  61. package/dist/cjs/ultramodern-workspace/public-surface.cjs +3 -3
  62. package/dist/cjs/ultramodern-workspace/routes.cjs +2 -11
  63. package/dist/cjs/ultramodern-workspace/shared-patches.cjs +12 -2
  64. package/dist/cjs/ultramodern-workspace/shells.cjs +0 -70
  65. package/dist/cjs/ultramodern-workspace/tooling-command-catalog.cjs +27 -41
  66. package/dist/cjs/ultramodern-workspace/tsconfigs.cjs +8 -2
  67. package/dist/cjs/ultramodern-workspace/validation/architecture.cjs +397 -0
  68. package/dist/cjs/ultramodern-workspace/validation/assertions.cjs +140 -0
  69. package/dist/cjs/ultramodern-workspace/validation/types.cjs +18 -0
  70. package/dist/cjs/ultramodern-workspace/validation/workspace.cjs +344 -0
  71. package/dist/cjs/ultramodern-workspace/versions.cjs +81 -45
  72. package/dist/cjs/ultramodern-workspace/workspace-artifact-ownership.cjs +183 -0
  73. package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +36 -25
  74. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +11 -200
  75. package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +62 -373
  76. package/dist/cjs/ultramodern-workspace/write-app.cjs +19 -11
  77. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +20 -50
  78. package/dist/cjs/ultramodern-workspace/zerops.cjs +2 -0
  79. package/dist/esm/cli/package-source.js +3 -3
  80. package/dist/esm/cli/project-setup.js +2 -22
  81. package/dist/esm/index.js +10 -2
  82. package/dist/esm/locale/en.js +2 -1
  83. package/dist/esm/locale/zh.js +2 -1
  84. package/dist/esm/ultramodern-package-source.js +14 -4
  85. package/dist/esm/ultramodern-release-cohort.js +5 -14
  86. package/dist/esm/ultramodern-tooling/commands/cloudflare-output-verify.js +49 -56
  87. package/dist/esm/ultramodern-tooling/commands/context.js +10 -26
  88. package/dist/esm/ultramodern-tooling/commands/routes-generate-app.js +35 -0
  89. package/dist/esm/ultramodern-tooling/commands/routes-generate.js +25 -54
  90. package/dist/esm/ultramodern-tooling/commands/validate.js +6 -12
  91. package/dist/esm/ultramodern-tooling/commands.js +15 -9
  92. package/dist/esm/ultramodern-tooling/config/index.js +0 -2
  93. package/dist/esm/ultramodern-tooling/config/load.js +9 -9
  94. package/dist/esm/ultramodern-tooling/config/metadata.js +33 -28
  95. package/dist/esm/ultramodern-tooling/config/normalize.js +159 -156
  96. package/dist/esm/ultramodern-workspace/add-shell.js +124 -123
  97. package/dist/esm/ultramodern-workspace/add-vertical/execute.js +119 -27
  98. package/dist/esm/ultramodern-workspace/add-vertical/plan.js +16 -150
  99. package/dist/esm/ultramodern-workspace/add-vertical/preflight.js +15 -47
  100. package/dist/esm/ultramodern-workspace/add-vertical/preview.js +40 -0
  101. package/dist/esm/ultramodern-workspace/add-vertical/shell-files.js +87 -25
  102. package/dist/esm/ultramodern-workspace/add-vertical/topology.js +4 -1
  103. package/dist/esm/ultramodern-workspace/add-vertical/transaction.js +373 -46
  104. package/dist/esm/ultramodern-workspace/add-vertical/workspace-state.js +17 -34
  105. package/dist/esm/ultramodern-workspace/add-vertical.js +1 -1
  106. package/dist/esm/ultramodern-workspace/api/client.js +5 -5
  107. package/dist/esm/ultramodern-workspace/api/contracts.js +2 -2
  108. package/dist/esm/ultramodern-workspace/api/rpc.js +20 -18
  109. package/dist/esm/ultramodern-workspace/api/service.js +49 -24
  110. package/dist/esm/ultramodern-workspace/api/shared.js +28 -18
  111. package/dist/esm/ultramodern-workspace/app-files.js +3 -23
  112. package/dist/esm/ultramodern-workspace/backend-federation.js +5 -5
  113. package/dist/esm/ultramodern-workspace/contracts.js +14 -180
  114. package/dist/esm/ultramodern-workspace/delivery-unit-schema/surfaces.js +41 -0
  115. package/dist/esm/ultramodern-workspace/delivery-unit-schema/types.js +1 -75
  116. package/dist/esm/ultramodern-workspace/delivery-unit-stamp.js +16 -4
  117. package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +28 -18
  118. package/dist/esm/ultramodern-workspace/delivery-unit.js +12 -6
  119. package/dist/esm/ultramodern-workspace/demo-components.js +67 -428
  120. package/dist/esm/ultramodern-workspace/descriptors.js +1 -2
  121. package/dist/esm/ultramodern-workspace/fs-io.js +37 -2
  122. package/dist/esm/ultramodern-workspace/generation-result.js +3 -3
  123. package/dist/esm/ultramodern-workspace/index.js +1 -1
  124. package/dist/esm/ultramodern-workspace/mf-validation/constants.js +1 -4
  125. package/dist/esm/ultramodern-workspace/mf-validation/discovery.js +7 -7
  126. package/dist/esm/ultramodern-workspace/mf-validation/exposes.js +14 -0
  127. package/dist/esm/ultramodern-workspace/mf-validation/inspect.js +27 -8
  128. package/dist/esm/ultramodern-workspace/mf-validation/object-literal.js +22 -123
  129. package/dist/esm/ultramodern-workspace/mf-validation/path-utils.js +36 -34
  130. package/dist/esm/ultramodern-workspace/mf-validation/syntax.js +83 -143
  131. package/dist/esm/ultramodern-workspace/mf-validation.js +1 -0
  132. package/dist/esm/ultramodern-workspace/module-federation/config.js +25 -30
  133. package/dist/esm/ultramodern-workspace/module-federation/reexport-module.js +39 -58
  134. package/dist/esm/ultramodern-workspace/module-federation/remote-refs.js +1 -43
  135. package/dist/esm/ultramodern-workspace/package-json.js +58 -32
  136. package/dist/esm/ultramodern-workspace/patch-inventory.js +66 -0
  137. package/dist/esm/ultramodern-workspace/policy.js +2 -564
  138. package/dist/esm/ultramodern-workspace/public-surface.js +3 -3
  139. package/dist/esm/ultramodern-workspace/routes.js +2 -11
  140. package/dist/esm/ultramodern-workspace/shared-patches.js +2 -2
  141. package/dist/esm/ultramodern-workspace/shells.js +3 -67
  142. package/dist/esm/ultramodern-workspace/tooling-command-catalog.js +22 -37
  143. package/dist/esm/ultramodern-workspace/tsconfigs.js +9 -3
  144. package/dist/esm/ultramodern-workspace/validation/architecture.js +348 -0
  145. package/dist/esm/ultramodern-workspace/validation/assertions.js +65 -0
  146. package/dist/esm/ultramodern-workspace/validation/types.js +0 -0
  147. package/dist/esm/ultramodern-workspace/validation/workspace.js +286 -0
  148. package/dist/esm/ultramodern-workspace/versions.js +74 -29
  149. package/dist/esm/ultramodern-workspace/workspace-artifact-ownership.js +128 -0
  150. package/dist/esm/ultramodern-workspace/workspace-script-plan.js +35 -27
  151. package/dist/esm/ultramodern-workspace/workspace-scripts.js +10 -190
  152. package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +65 -376
  153. package/dist/esm/ultramodern-workspace/write-app.js +18 -10
  154. package/dist/esm/ultramodern-workspace/write-workspace.js +25 -52
  155. package/dist/esm/ultramodern-workspace/zerops.js +2 -0
  156. package/dist/esm-node/cli/package-source.js +3 -3
  157. package/dist/esm-node/cli/project-setup.js +2 -22
  158. package/dist/esm-node/index.js +10 -2
  159. package/dist/esm-node/locale/en.js +2 -1
  160. package/dist/esm-node/locale/zh.js +2 -1
  161. package/dist/esm-node/ultramodern-package-source.js +14 -4
  162. package/dist/esm-node/ultramodern-release-cohort.js +5 -14
  163. package/dist/esm-node/ultramodern-tooling/commands/cloudflare-output-verify.js +49 -56
  164. package/dist/esm-node/ultramodern-tooling/commands/context.js +10 -26
  165. package/dist/esm-node/ultramodern-tooling/commands/routes-generate-app.js +36 -0
  166. package/dist/esm-node/ultramodern-tooling/commands/routes-generate.js +25 -54
  167. package/dist/esm-node/ultramodern-tooling/commands/validate.js +6 -12
  168. package/dist/esm-node/ultramodern-tooling/commands.js +15 -9
  169. package/dist/esm-node/ultramodern-tooling/config/index.js +0 -2
  170. package/dist/esm-node/ultramodern-tooling/config/load.js +9 -9
  171. package/dist/esm-node/ultramodern-tooling/config/metadata.js +33 -28
  172. package/dist/esm-node/ultramodern-tooling/config/normalize.js +159 -156
  173. package/dist/esm-node/ultramodern-workspace/add-shell.js +124 -123
  174. package/dist/esm-node/ultramodern-workspace/add-vertical/execute.js +119 -27
  175. package/dist/esm-node/ultramodern-workspace/add-vertical/plan.js +16 -150
  176. package/dist/esm-node/ultramodern-workspace/add-vertical/preflight.js +15 -47
  177. package/dist/esm-node/ultramodern-workspace/add-vertical/preview.js +41 -0
  178. package/dist/esm-node/ultramodern-workspace/add-vertical/shell-files.js +87 -25
  179. package/dist/esm-node/ultramodern-workspace/add-vertical/topology.js +4 -1
  180. package/dist/esm-node/ultramodern-workspace/add-vertical/transaction.js +373 -46
  181. package/dist/esm-node/ultramodern-workspace/add-vertical/workspace-state.js +17 -34
  182. package/dist/esm-node/ultramodern-workspace/add-vertical.js +1 -1
  183. package/dist/esm-node/ultramodern-workspace/api/client.js +5 -5
  184. package/dist/esm-node/ultramodern-workspace/api/contracts.js +2 -2
  185. package/dist/esm-node/ultramodern-workspace/api/rpc.js +20 -18
  186. package/dist/esm-node/ultramodern-workspace/api/service.js +49 -24
  187. package/dist/esm-node/ultramodern-workspace/api/shared.js +28 -18
  188. package/dist/esm-node/ultramodern-workspace/app-files.js +3 -23
  189. package/dist/esm-node/ultramodern-workspace/backend-federation.js +5 -5
  190. package/dist/esm-node/ultramodern-workspace/contracts.js +14 -180
  191. package/dist/esm-node/ultramodern-workspace/delivery-unit-schema/surfaces.js +42 -0
  192. package/dist/esm-node/ultramodern-workspace/delivery-unit-schema/types.js +1 -75
  193. package/dist/esm-node/ultramodern-workspace/delivery-unit-stamp.js +16 -4
  194. package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +28 -18
  195. package/dist/esm-node/ultramodern-workspace/delivery-unit.js +12 -6
  196. package/dist/esm-node/ultramodern-workspace/demo-components.js +67 -428
  197. package/dist/esm-node/ultramodern-workspace/descriptors.js +1 -2
  198. package/dist/esm-node/ultramodern-workspace/fs-io.js +37 -2
  199. package/dist/esm-node/ultramodern-workspace/generation-result.js +3 -3
  200. package/dist/esm-node/ultramodern-workspace/index.js +1 -1
  201. package/dist/esm-node/ultramodern-workspace/mf-validation/constants.js +1 -4
  202. package/dist/esm-node/ultramodern-workspace/mf-validation/discovery.js +7 -7
  203. package/dist/esm-node/ultramodern-workspace/mf-validation/exposes.js +15 -0
  204. package/dist/esm-node/ultramodern-workspace/mf-validation/inspect.js +27 -8
  205. package/dist/esm-node/ultramodern-workspace/mf-validation/object-literal.js +22 -123
  206. package/dist/esm-node/ultramodern-workspace/mf-validation/path-utils.js +36 -34
  207. package/dist/esm-node/ultramodern-workspace/mf-validation/syntax.js +83 -143
  208. package/dist/esm-node/ultramodern-workspace/mf-validation.js +1 -0
  209. package/dist/esm-node/ultramodern-workspace/module-federation/config.js +25 -30
  210. package/dist/esm-node/ultramodern-workspace/module-federation/reexport-module.js +39 -58
  211. package/dist/esm-node/ultramodern-workspace/module-federation/remote-refs.js +1 -43
  212. package/dist/esm-node/ultramodern-workspace/package-json.js +58 -32
  213. package/dist/esm-node/ultramodern-workspace/patch-inventory.js +67 -0
  214. package/dist/esm-node/ultramodern-workspace/policy.js +2 -564
  215. package/dist/esm-node/ultramodern-workspace/public-surface.js +3 -3
  216. package/dist/esm-node/ultramodern-workspace/routes.js +2 -11
  217. package/dist/esm-node/ultramodern-workspace/shared-patches.js +2 -2
  218. package/dist/esm-node/ultramodern-workspace/shells.js +3 -67
  219. package/dist/esm-node/ultramodern-workspace/tooling-command-catalog.js +22 -37
  220. package/dist/esm-node/ultramodern-workspace/tsconfigs.js +9 -3
  221. package/dist/esm-node/ultramodern-workspace/validation/architecture.js +349 -0
  222. package/dist/esm-node/ultramodern-workspace/validation/assertions.js +66 -0
  223. package/dist/esm-node/ultramodern-workspace/validation/types.js +1 -0
  224. package/dist/esm-node/ultramodern-workspace/validation/workspace.js +287 -0
  225. package/dist/esm-node/ultramodern-workspace/versions.js +74 -29
  226. package/dist/esm-node/ultramodern-workspace/workspace-artifact-ownership.js +129 -0
  227. package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +35 -27
  228. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +10 -190
  229. package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +65 -376
  230. package/dist/esm-node/ultramodern-workspace/write-app.js +18 -10
  231. package/dist/esm-node/ultramodern-workspace/write-workspace.js +25 -52
  232. package/dist/esm-node/ultramodern-workspace/zerops.js +2 -0
  233. package/dist/types/cli/project-setup.d.ts +1 -1
  234. package/dist/types/locale/en.d.ts +1 -0
  235. package/dist/types/locale/index.d.ts +2 -0
  236. package/dist/types/locale/zh.d.ts +1 -0
  237. package/dist/types/ultramodern-package-source.d.ts +2 -4
  238. package/dist/types/ultramodern-release-cohort.d.ts +1 -3
  239. package/dist/types/ultramodern-tooling/commands/cloudflare-output-verify.d.ts +2 -2
  240. package/dist/types/ultramodern-tooling/commands/context.d.ts +4 -1
  241. package/dist/types/ultramodern-tooling/commands/routes-generate-app.d.ts +1 -0
  242. package/dist/types/ultramodern-tooling/commands/routes-generate.d.ts +1 -1
  243. package/dist/types/ultramodern-tooling/commands/validate.d.ts +1 -1
  244. package/dist/types/ultramodern-tooling/config/index.d.ts +0 -2
  245. package/dist/types/ultramodern-tooling/config/load.d.ts +10 -2
  246. package/dist/types/ultramodern-tooling/config/metadata.d.ts +2 -2
  247. package/dist/types/ultramodern-tooling/config/normalize.d.ts +19 -19
  248. package/dist/types/ultramodern-tooling/config/types.d.ts +2 -9
  249. package/dist/types/ultramodern-workspace/add-shell.d.ts +1 -1
  250. package/dist/types/ultramodern-workspace/add-vertical/execute.d.ts +1 -0
  251. package/dist/types/ultramodern-workspace/add-vertical/preflight.d.ts +4 -3
  252. package/dist/types/ultramodern-workspace/add-vertical/preview.d.ts +4 -0
  253. package/dist/types/ultramodern-workspace/add-vertical/shell-files.d.ts +7 -2
  254. package/dist/types/ultramodern-workspace/add-vertical/transaction.d.ts +32 -6
  255. package/dist/types/ultramodern-workspace/add-vertical/workspace-state.d.ts +10 -7
  256. package/dist/types/ultramodern-workspace/add-vertical.d.ts +1 -1
  257. package/dist/types/ultramodern-workspace/api/client.d.ts +3 -1
  258. package/dist/types/ultramodern-workspace/api/rpc.d.ts +1 -1
  259. package/dist/types/ultramodern-workspace/api/service.d.ts +3 -1
  260. package/dist/types/ultramodern-workspace/api/shared.d.ts +2 -0
  261. package/dist/types/ultramodern-workspace/backend-federation.d.ts +1 -1
  262. package/dist/types/ultramodern-workspace/contracts.d.ts +1 -3
  263. package/dist/types/ultramodern-workspace/delivery-unit-schema/surfaces.d.ts +17 -0
  264. package/dist/types/ultramodern-workspace/delivery-unit-schema/types.d.ts +3 -68
  265. package/dist/types/ultramodern-workspace/delivery-unit-stamp.d.ts +2 -2
  266. package/dist/types/ultramodern-workspace/delivery-unit.d.ts +3 -3
  267. package/dist/types/ultramodern-workspace/demo-components.d.ts +0 -3
  268. package/dist/types/ultramodern-workspace/descriptors.d.ts +0 -1
  269. package/dist/types/ultramodern-workspace/fs-io.d.ts +1 -1
  270. package/dist/types/ultramodern-workspace/index.d.ts +1 -1
  271. package/dist/types/ultramodern-workspace/mf-validation/constants.d.ts +0 -1
  272. package/dist/types/ultramodern-workspace/mf-validation/exposes.d.ts +11 -0
  273. package/dist/types/ultramodern-workspace/mf-validation/path-utils.d.ts +2 -3
  274. package/dist/types/ultramodern-workspace/mf-validation/syntax.d.ts +4 -3
  275. package/dist/types/ultramodern-workspace/mf-validation/types.d.ts +1 -0
  276. package/dist/types/ultramodern-workspace/mf-validation.d.ts +1 -0
  277. package/dist/types/ultramodern-workspace/module-federation/config.d.ts +1 -1
  278. package/dist/types/ultramodern-workspace/module-federation/reexport-module.d.ts +2 -2
  279. package/dist/types/ultramodern-workspace/package-json.d.ts +1 -1
  280. package/dist/types/ultramodern-workspace/patch-inventory.d.ts +10 -0
  281. package/dist/types/ultramodern-workspace/policy.d.ts +31 -209
  282. package/dist/types/ultramodern-workspace/routes.d.ts +6 -1
  283. package/dist/types/ultramodern-workspace/shells.d.ts +2 -15
  284. package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +8 -7
  285. package/dist/types/ultramodern-workspace/types.d.ts +9 -9
  286. package/dist/types/ultramodern-workspace/validation/architecture.d.ts +2 -0
  287. package/dist/types/ultramodern-workspace/validation/assertions.d.ts +14 -0
  288. package/dist/types/ultramodern-workspace/validation/types.d.ts +9 -0
  289. package/dist/types/ultramodern-workspace/validation/workspace.d.ts +6 -0
  290. package/dist/types/ultramodern-workspace/versions.d.ts +73 -28
  291. package/dist/types/ultramodern-workspace/workspace-artifact-ownership.d.ts +18 -0
  292. package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +4 -5
  293. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +8 -13
  294. package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +22 -188
  295. package/dist/types/ultramodern-workspace/write-workspace.d.ts +1 -3
  296. package/package.json +18 -11
  297. package/{template-workspace/.modernjs/release-cohort.json → release-cohort.json} +88 -40
  298. package/template-workspace/.codex/skills/rspack-tracing/scripts/analyze_trace.js +1 -1
  299. package/template-workspace/README.md.handlebars +20 -35
  300. package/template-workspace/oxfmt.config.ts +3 -0
  301. package/template-workspace/pnpm-workspace.yaml.handlebars +0 -24
  302. package/template-workspace/scripts/bootstrap-agent-skills.mjs +4 -4
  303. package/templates/packages/shared-contracts-index.ts +4 -332
  304. package/templates/workspace/apps/modern.config.shared-module-federation.ts.handlebars +10 -0
  305. package/templates/workspace/apps/modern.config.ts.handlebars +13 -27
  306. package/templates/workspace/apps/shared/src/routes/ultramodern-route-metadata.ts.handlebars +21 -11
  307. package/templates/workspace/apps/shell-super-app/src/routes/[lang]/page.tsx.handlebars +20 -20
  308. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.helpers.tsx.handlebars +4 -6
  309. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.imports.tsx.handlebars +2 -2
  310. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.tsx.handlebars +11 -11
  311. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.worker.helpers.tsx.handlebars +1 -1
  312. package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.worker.imports.tsx.handlebars +1 -1
  313. package/templates/workspace-scripts/generate-node-backend-federation.mjs +68 -77
  314. package/templates/workspace-scripts/generate-public-surface-assets.mjs +107 -207
  315. package/templates/workspace-scripts/materialize-zerops-runtime.mjs +12 -19
  316. package/templates/workspace-scripts/proof-cloudflare-version.mjs +53 -444
  317. package/templates/workspace-scripts/proof-node-backend-federation.mjs +212 -91
  318. package/templates/workspace-scripts/{proof-workerd-ssr.mts → proof-workerd-ssr.mjs} +434 -239
  319. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +25 -7
  320. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +45 -147
  321. package/templates/workspace-scripts/ultramodern-typecheck.mjs +1 -1
  322. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.cjs +0 -212
  323. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.cjs +0 -47
  324. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.cjs +0 -112
  325. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.cjs +0 -55
  326. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.cjs +0 -102
  327. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.cjs +0 -195
  328. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.cjs +0 -146
  329. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxlint.cjs +0 -110
  330. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.cjs +0 -67
  331. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.cjs +0 -307
  332. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.cjs +0 -58
  333. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.cjs +0 -71
  334. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.cjs +0 -151
  335. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/help.cjs +0 -67
  336. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/install.cjs +0 -52
  337. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/io.cjs +0 -447
  338. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.cjs +0 -323
  339. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-source.cjs +0 -87
  340. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.cjs +0 -612
  341. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.cjs +0 -376
  342. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.cjs +0 -73
  343. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/react-router-retirement.cjs +0 -215
  344. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.cjs +0 -135
  345. package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect.cjs +0 -454
  346. package/dist/cjs/ultramodern-tooling/config/constants.cjs +0 -48
  347. package/dist/cjs/ultramodern-tooling/config/synthesize.cjs +0 -157
  348. package/dist/cjs/ultramodern-workspace/delivery-unit-schema/up-projection.cjs +0 -188
  349. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.js +0 -154
  350. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.js +0 -9
  351. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.js +0 -63
  352. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.js +0 -7
  353. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.js +0 -47
  354. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +0 -146
  355. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.js +0 -97
  356. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxlint.js +0 -61
  357. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.js +0 -16
  358. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +0 -258
  359. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.js +0 -10
  360. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.js +0 -8
  361. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.js +0 -15
  362. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.js +0 -100
  363. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/help.js +0 -29
  364. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/install.js +0 -14
  365. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/io.js +0 -385
  366. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +0 -276
  367. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/package-source.js +0 -49
  368. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.js +0 -559
  369. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.js +0 -329
  370. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.js +0 -19
  371. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/react-router-retirement.js +0 -148
  372. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.js +0 -80
  373. package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect.js +0 -405
  374. package/dist/esm/ultramodern-tooling/config/constants.js +0 -4
  375. package/dist/esm/ultramodern-tooling/config/synthesize.js +0 -108
  376. package/dist/esm/ultramodern-workspace/delivery-unit-schema/up-projection.js +0 -129
  377. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.js +0 -155
  378. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.js +0 -10
  379. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.js +0 -64
  380. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.js +0 -8
  381. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.js +0 -48
  382. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +0 -147
  383. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.js +0 -98
  384. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxlint.js +0 -62
  385. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.js +0 -17
  386. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +0 -259
  387. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.js +0 -11
  388. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.js +0 -9
  389. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.js +0 -16
  390. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.js +0 -101
  391. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/help.js +0 -30
  392. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/install.js +0 -15
  393. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/io.js +0 -386
  394. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +0 -277
  395. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/package-source.js +0 -50
  396. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.js +0 -560
  397. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.js +0 -330
  398. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.js +0 -20
  399. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/react-router-retirement.js +0 -149
  400. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.js +0 -81
  401. package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect.js +0 -406
  402. package/dist/esm-node/ultramodern-tooling/config/constants.js +0 -5
  403. package/dist/esm-node/ultramodern-tooling/config/synthesize.js +0 -109
  404. package/dist/esm-node/ultramodern-workspace/delivery-unit-schema/up-projection.js +0 -130
  405. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.d.ts +0 -12
  406. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.d.ts +0 -4
  407. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.d.ts +0 -1
  408. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.d.ts +0 -3
  409. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.d.ts +0 -5
  410. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.d.ts +0 -3
  411. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.d.ts +0 -2
  412. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxlint.d.ts +0 -2
  413. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.d.ts +0 -4
  414. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.d.ts +0 -3
  415. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.d.ts +0 -3
  416. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.d.ts +0 -8
  417. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.d.ts +0 -3
  418. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.d.ts +0 -3
  419. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/help.d.ts +0 -1
  420. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/install.d.ts +0 -2
  421. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/io.d.ts +0 -26
  422. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.d.ts +0 -25
  423. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/package-source.d.ts +0 -3
  424. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.d.ts +0 -26
  425. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.d.ts +0 -26
  426. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.d.ts +0 -6
  427. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/react-router-retirement.d.ts +0 -37
  428. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.d.ts +0 -4
  429. package/dist/types/ultramodern-tooling/commands/migrate-strict-effect.d.ts +0 -2
  430. package/dist/types/ultramodern-tooling/config/constants.d.ts +0 -3
  431. package/dist/types/ultramodern-tooling/config/synthesize.d.ts +0 -5
  432. package/dist/types/ultramodern-workspace/delivery-unit-schema/up-projection.d.ts +0 -189
  433. package/template-workspace/patches/@module-federation__bridge-react@2.9.0.patch +0 -47
  434. package/template-workspace/patches/@module-federation__dts-plugin@2.9.0.patch +0 -26
  435. package/template-workspace/patches/@module-federation__modern-js-v3@2.9.0.patch +0 -268
  436. package/template-workspace/patches/@module-federation__runtime-core@2.9.0.patch +0 -13
  437. package/template-workspace/patches/drizzle-orm-ts7-strict-declarations.patch +0 -471
  438. package/template-workspace/patches/msgpackr@2.1.0.patch +0 -30
  439. package/template-workspace/patches/zod@4.5.4.patch +0 -56
  440. package/templates/workspace-scripts/check-ultramodern-api-boundaries.mts +0 -779
  441. package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mts +0 -9
  442. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +0 -4109
@@ -1,4109 +0,0 @@
1
- import { execFileSync } from 'node:child_process';
2
- import crypto from 'node:crypto';
3
- import fs from 'node:fs';
4
- import { createRequire } from 'node:module';
5
- import path from 'node:path';
6
-
7
- // Generated by modern-js-create with an immutable expected proof contract.
8
- const root = process.cwd();
9
- const workspaceValidationContract = {{workspaceValidationContractJson}};
10
- const packageScope = workspaceValidationContract.packageScope;
11
- const expectedNodeVersion = workspaceValidationContract.versions.node;
12
- const expectedCrossEnvVersion =
13
- workspaceValidationContract.versions.crossEnv;
14
- const expectedEffectVersion = workspaceValidationContract.versions.effect;
15
- const expectedEffectVitestVersion =
16
- workspaceValidationContract.versions.effectVitest;
17
- const expectedModuleFederationVersion =
18
- workspaceValidationContract.versions.moduleFederation;
19
- const expectedTanstackHistoryVersion =
20
- workspaceValidationContract.versions.tanstackHistory;
21
- const expectedCloudflareCompatibilityDate =
22
- workspaceValidationContract.versions.cloudflareCompatibilityDate;
23
- const tailwindEnabled = workspaceValidationContract.tailwindEnabled;
24
- const fullStackVerticals = workspaceValidationContract.fullStackVerticals;
25
- // Backend-federation and Zerops runtime surfaces only exist when the workspace
26
- // exposes API-bearing verticals. Shell-only workspaces skip their
27
- // materialization during migrate, so the contract must not require them.
28
- const hasBackendSurfaces = fullStackVerticals.some(
29
- vertical => vertical.emitsApi,
30
- );
31
- // Every vertical (ui-only and horizontal-remote included) is a delivery unit
32
- // and deploys via Zerops; only the BACKEND proof/generation surfaces depend on
33
- // an API-bearing unit existing (split gating).
34
- const hasDeliveryUnits = fullStackVerticals.length > 0;
35
- const shellNamespace = workspaceValidationContract.shellNamespace;
36
- const oldRemotePaths = workspaceValidationContract.oldRemotePaths;
37
- const expectedBuildScript = workspaceValidationContract.scripts.build;
38
- const expectedCloudflareBuildScript =
39
- workspaceValidationContract.scripts.cloudflareBuild;
40
- const expectedCloudflareDeployScript =
41
- workspaceValidationContract.scripts.cloudflareDeploy;
42
- const expectedCloudflareSecurity =
43
- workspaceValidationContract.cloudflareSecurity;
44
- const publicSurfaceManagedSourceAssetPaths =
45
- workspaceValidationContract.publicSurfaceManagedSourceAssetPaths;
46
- const shellRouteMetaPaths = workspaceValidationContract.shellRouteMetaPaths;
47
- const compactConfigPath =
48
- workspaceValidationContract.metadata.compactConfig.path;
49
- const retiredMetadataPaths =
50
- workspaceValidationContract.legacy.retiredMetadataPaths;
51
- const modernPackageCohort =
52
- workspaceValidationContract.cohort.modernPackages;
53
- const expectedAdditionalShellIds =
54
- workspaceValidationContract.cohort.additionalShellIds ?? [];
55
- const expectedAdditionalShells =
56
- workspaceValidationContract.additionalShells ?? [];
57
- const expectedPrimaryShellVerticalIds =
58
- workspaceValidationContract.topology?.referenceTopology?.shell
59
- ?.verticalRefs ?? workspaceValidationContract.cohort.verticalIds;
60
- const expectedReleaseCohort =
61
- workspaceValidationContract.cohort.releaseCohort;
62
- const expectedModernPackageSpecifier = packageName => {
63
- if (packageSource.strategy === 'workspace') {
64
- return 'workspace:*';
65
- }
66
- const aliases = packageSource.modernPackages?.aliases ?? {};
67
- const alias = aliases[packageName];
68
- const specifier = packageSource.modernPackages?.specifier;
69
- return typeof alias === 'string' ? `npm:${alias}@${specifier}` : specifier;
70
- };
71
-
72
- const readText = relativePath => fs.readFileSync(path.join(root, relativePath), 'utf-8');
73
- const readJson = relativePath => JSON.parse(readText(relativePath));
74
- const dependencyRequireCandidates = [
75
- path.join(root, 'package.json'),
76
- ...(typeof process.env.npm_package_json === 'string'
77
- ? [process.env.npm_package_json]
78
- : []),
79
- ...(typeof process.env.npm_config_local_prefix === 'string'
80
- ? [path.join(process.env.npm_config_local_prefix, 'package.json')]
81
- : []),
82
- ...(typeof process.env.INIT_CWD === 'string'
83
- ? [
84
- path.join(process.env.INIT_CWD, 'package.json'),
85
- path.join(
86
- process.env.INIT_CWD,
87
- 'packages/toolkit/ultramodern-create/package.json',
88
- ),
89
- path.join(process.env.INIT_CWD, 'packages/toolkit/create/package.json'),
90
- ]
91
- : []),
92
- ];
93
- const loadDependency = packageName => {
94
- const errors = [];
95
- for (const requireFrom of dependencyRequireCandidates) {
96
- if (!fs.existsSync(requireFrom)) {
97
- continue;
98
- }
99
- try {
100
- return createRequire(requireFrom)(packageName);
101
- } catch (error) {
102
- errors.push(`${requireFrom}: ${error.message}`);
103
- }
104
- }
105
- throw new Error(
106
- `Unable to load ${packageName}; install the generated workspace dependencies. ${errors.join(' | ')}`,
107
- );
108
- };
109
- const loadFirstDependency = packageNames => {
110
- const errors = [];
111
- for (const packageName of packageNames) {
112
- try {
113
- return loadDependency(packageName);
114
- } catch (error) {
115
- errors.push(error.message);
116
- }
117
- }
118
- throw new Error(errors.join(' | '));
119
- };
120
- const tryLoadFirstDependency = packageNames => {
121
- try {
122
- return loadFirstDependency(packageNames);
123
- } catch {
124
- return undefined;
125
- }
126
- };
127
- const typescript = tryLoadFirstDependency([
128
- '@typescript/native/unstable/ast',
129
- 'typescript/unstable/ast',
130
- ]);
131
- const typescriptApi = tryLoadFirstDependency([
132
- '@typescript/native/unstable/sync',
133
- 'typescript/unstable/sync',
134
- ]);
135
- let compilerSnapshot;
136
- const assert = (condition, message) => {
137
- if (!condition) {
138
- throw new Error(message);
139
- }
140
- };
141
- const assertPortableGeneratedEnvironmentScripts = (packageJson, label) => {
142
- const build = packageJson.scripts?.build ?? '';
143
- const cloudflareBuild = packageJson.scripts?.['cloudflare:build'] ?? '';
144
- assert(build.includes('cross-env MODERNJS_DEPLOY=node modern deploy --skip-build'), `${label} build must use cross-env for its Node deploy`);
145
- assert(cloudflareBuild.includes('cross-env MODERNJS_DEPLOY=cloudflare modern build'), `${label} cloudflare:build must use cross-env for Modern build`);
146
- assert(cloudflareBuild.includes('cross-env MODERNJS_DEPLOY=cloudflare modern deploy --skip-build'), `${label} cloudflare:build must use cross-env for Modern deploy`);
147
- assert(!/(?:^|\s+&&\s+)MODERNJS_DEPLOY=/u.test(build), `${label} build must not use a POSIX-only environment prefix`);
148
- assert(!/(?:^|\s+&&\s+)MODERNJS_DEPLOY=/u.test(cloudflareBuild), `${label} cloudflare:build must not use a POSIX-only environment prefix`);
149
- };
150
- const assertExists = relativePath => {
151
- assert(fs.existsSync(path.join(root, relativePath)), `Missing ${relativePath}`);
152
- };
153
- const assertNotExists = relativePath => {
154
- assert(!fs.existsSync(path.join(root, relativePath)), `Unexpected ${relativePath}`);
155
- };
156
- const assertAnyOf = relativePaths => {
157
- assert(
158
- relativePaths.some(relativePath =>
159
- fs.existsSync(path.join(root, relativePath)),
160
- ),
161
- `Missing one of: ${relativePaths.join(', ')}`,
162
- );
163
- };
164
- const requiredShellWorkerCompositionPath = shellPath => {
165
- const candidates = [
166
- `${shellPath}/src/routes/vertical-components.worker.tsx`,
167
- `${shellPath}/src/federated-components.worker.tsx`,
168
- ];
169
- const existingPath = candidates.find(candidate =>
170
- fs.existsSync(path.join(root, candidate)),
171
- );
172
- if (existingPath !== undefined) {
173
- return existingPath;
174
- }
175
- throw new Error(`Missing one of: ${candidates.join(', ')}`);
176
- };
177
- const canonicalizeJson = value => {
178
- if (Array.isArray(value)) {
179
- const entries = value.map(canonicalizeJson);
180
- const keyedEntries = entries.every(
181
- entry =>
182
- entry !== null &&
183
- typeof entry === 'object' &&
184
- !Array.isArray(entry) &&
185
- typeof entry.id === 'string',
186
- );
187
- if (keyedEntries) {
188
- const ids = entries.map(entry => entry.id);
189
- if (new Set(ids).size === ids.length) {
190
- return entries.toSorted((left, right) => left.id.localeCompare(right.id));
191
- }
192
- }
193
- return entries;
194
- }
195
- if (value !== null && typeof value === 'object') {
196
- return Object.fromEntries(
197
- Object.keys(value)
198
- .toSorted()
199
- .map(key => [key, canonicalizeJson(value[key])]),
200
- );
201
- }
202
- return value;
203
- };
204
- const sameJson = (actual, expected) =>
205
- JSON.stringify(canonicalizeJson(actual)) ===
206
- JSON.stringify(canonicalizeJson(expected));
207
- const formatJson = value =>
208
- value === undefined ? 'undefined' : JSON.stringify(canonicalizeJson(value));
209
- const quoteYamlString = value => `'${String(value).replace(/'/gu, "''")}'`;
210
- const quoteShellValue = value => `'${String(value).replace(/'/gu, "'\\''")}'`;
211
- const selfCheckFailure = (contract, message, fixArea) =>
212
- `MicroVertical contract self-check failed: ${contract}. ${message}. Fix area: ${fixArea}.`;
213
- const assertSelfCheck = (condition, contract, message, fixArea) => {
214
- assert(condition, selfCheckFailure(contract, message, fixArea));
215
- };
216
- const assertSameJson = (actual, expected, contract, fixArea) => {
217
- assertSelfCheck(
218
- sameJson(actual, expected),
219
- contract,
220
- `Expected ${formatJson(expected)}, found ${formatJson(actual)}`,
221
- fixArea,
222
- );
223
- };
224
- const assertObject = (value, contract, fixArea) => {
225
- assertSelfCheck(
226
- value !== null && typeof value === 'object' && !Array.isArray(value),
227
- contract,
228
- `Expected JSON object, found ${formatJson(value)}`,
229
- fixArea,
230
- );
231
- };
232
- const assertArray = (value, contract, fixArea) => {
233
- assertSelfCheck(
234
- Array.isArray(value),
235
- contract,
236
- `Expected JSON array, found ${formatJson(value)}`,
237
- fixArea,
238
- );
239
- };
240
- const assertUniqueStrings = (values, contract) => {
241
- assert(Array.isArray(values), `${contract} must be an array`);
242
- const seen = new Set();
243
- for (const value of values) {
244
- assert(
245
- typeof value === 'string' && value.length > 0,
246
- `${contract} must contain non-empty strings`,
247
- );
248
- assert(!seen.has(value), `Duplicate value "${value}" in ${contract}`);
249
- seen.add(value);
250
- }
251
- };
252
- const assertUniqueIdEntries = (entries, contract) => {
253
- assert(Array.isArray(entries), `${contract} must be an array`);
254
- const seen = new Set();
255
- for (const entry of entries) {
256
- const id = entry?.id;
257
- assert(
258
- typeof id === 'string' && id.length > 0,
259
- `${contract} entries must have non-empty string ids`,
260
- );
261
- assert(!seen.has(id), `Duplicate id "${id}" in ${contract}`);
262
- seen.add(id);
263
- }
264
- };
265
- const assertSameIdCohort = (entries, expectedIds, contract, fixArea) => {
266
- assertUniqueIdEntries(entries, contract);
267
- assertSameJson(
268
- entries.map(entry => entry.id).toSorted(),
269
- [...expectedIds].toSorted(),
270
- `${contract} cohort`,
271
- fixArea,
272
- );
273
- };
274
- const assertWorkspaceValidationContract = contract => {
275
- assert(
276
- contract !== null &&
277
- typeof contract === 'object' &&
278
- !Array.isArray(contract),
279
- 'Workspace validation contract must be a JSON object',
280
- );
281
- assert(
282
- contract.schemaVersion === 2,
283
- `Unsupported workspace validation contract schemaVersion ${formatJson(contract.schemaVersion)}; expected 2`,
284
- );
285
- assert(
286
- contract.kind === 'modernjs.ultramodern-workspace-validation-contract',
287
- `Unsupported workspace validation contract kind ${formatJson(contract.kind)}`,
288
- );
289
-
290
- const metadataEntries = Object.entries(contract.metadata ?? {});
291
- assert(
292
- metadataEntries.length === (contract.cohort?.releaseCohort ? 5 : 4),
293
- 'Workspace validation contract must declare every structured metadata input',
294
- );
295
- for (const [name, metadata] of metadataEntries) {
296
- assert(
297
- typeof metadata?.path === 'string' && metadata.path.length > 0,
298
- `Workspace validation contract metadata.${name}.path is required`,
299
- );
300
- assert(
301
- metadata.schemaVersion === 1,
302
- `Unsupported expected metadata schemaVersion ${formatJson(metadata.schemaVersion)} for ${name}`,
303
- );
304
- }
305
-
306
- assertUniqueStrings(
307
- contract.cohort?.modernPackages,
308
- 'workspace validation contract Modern package cohort',
309
- );
310
- assertUniqueStrings(
311
- contract.cohort?.appIds,
312
- 'workspace validation contract app cohort',
313
- );
314
- assertUniqueStrings(
315
- contract.cohort?.additionalShellIds ?? [],
316
- 'workspace validation contract additional-shell cohort',
317
- );
318
- for (const [field, label] of [
319
- [
320
- 'additionalShellOwnerIds',
321
- 'workspace validation contract additional-shell owner cohort',
322
- ],
323
- [
324
- 'additionalShellDeliveryUnitIds',
325
- 'workspace validation contract additional-shell delivery-unit cohort',
326
- ],
327
- [
328
- 'additionalShellDegradedStateIds',
329
- 'workspace validation contract additional-shell degraded-state cohort',
330
- ],
331
- [
332
- 'additionalShellBuildMarkerIds',
333
- 'workspace validation contract additional-shell build-marker cohort',
334
- ],
335
- ]) {
336
- assertUniqueStrings(contract.cohort?.[field] ?? [], label);
337
- }
338
- assertUniqueStrings(
339
- contract.cohort?.backendAppIds,
340
- 'workspace validation contract backend app cohort',
341
- );
342
- assertUniqueStrings(
343
- contract.cohort?.verticalIds,
344
- 'workspace validation contract vertical cohort',
345
- );
346
- assertUniqueStrings(
347
- contract.cohort?.sharedPackageIds,
348
- 'workspace validation contract shared package cohort',
349
- );
350
- assertUniqueStrings(
351
- contract.cohort?.ownerIds,
352
- 'workspace validation contract owner cohort',
353
- );
354
- assertUniqueIdEntries(
355
- contract.cohort?.packageManifests,
356
- 'workspace validation contract package manifests',
357
- );
358
- assertUniqueStrings(
359
- (contract.cohort?.packageManifests ?? []).map(manifest => manifest.path),
360
- 'workspace validation contract package manifest paths',
361
- );
362
-
363
- const evidencePolicy = contract.validationEvidencePolicy;
364
- assert(
365
- evidencePolicy?.schemaVersion === 1,
366
- `Unsupported validation evidence policy schemaVersion ${formatJson(evidencePolicy?.schemaVersion)}; expected 1`,
367
- );
368
- assertUniqueIdEntries(
369
- evidencePolicy?.required,
370
- 'workspace validation contract evidence requirements',
371
- );
372
- const requiredEvidence = new Map([
373
- ['typescript-compiler', 'compiler'],
374
- ['architecture-compiler', 'compiler'],
375
- ['executable-modern-config', 'runtime'],
376
- ['executable-runtime-config', 'runtime'],
377
- ['executable-module-federation-config', 'runtime'],
378
- ['executable-build-facade', 'runtime'],
379
- ['structured-package-config', 'structured'],
380
- ['structured-deploy-config', 'structured'],
381
- ['public-behavior-gates', 'behavior'],
382
- ]);
383
- assertSameJson(
384
- evidencePolicy.required,
385
- [...requiredEvidence].map(([id, kind]) => ({ id, kind })),
386
- 'workspace validation contract evidence requirements',
387
- 'restore the complete compiler, runtime, structured-data, and behavior evidence set',
388
- );
389
- for (const evidence of evidencePolicy.required) {
390
- assert(
391
- requiredEvidence.get(evidence.id) === evidence.kind,
392
- `Unsupported validation evidence ${formatJson(evidence)}`,
393
- );
394
- }
395
- };
396
- const assertAdditionalShellContractShape = () => {
397
- if ((workspaceValidationContract.cohort?.additionalShellIds ?? []).length > 0) {
398
- for (const field of [
399
- 'additionalShellOwnerIds',
400
- 'additionalShellDeliveryUnitIds',
401
- 'additionalShellDegradedStateIds',
402
- 'additionalShellBuildMarkerIds',
403
- ]) {
404
- assertSameJson(
405
- workspaceValidationContract.cohort?.[field],
406
- workspaceValidationContract.cohort.additionalShellIds,
407
- `workspace validation contract ${field}`,
408
- 'restore every generated additional-shell cohort',
409
- );
410
- }
411
- assertSameIdCohort(
412
- workspaceValidationContract.cohort?.additionalShellManifests,
413
- workspaceValidationContract.cohort.additionalShellIds,
414
- 'workspace validation contract additional-shell manifests',
415
- 'restore every generated additional-shell package manifest',
416
- );
417
- assertSameIdCohort(
418
- workspaceValidationContract.additionalShells,
419
- workspaceValidationContract.cohort.additionalShellIds,
420
- 'workspace validation contract additional-shell records',
421
- 'restore every generated additional-shell contract record',
422
- );
423
- } else {
424
- assert(
425
- workspaceValidationContract.cohort?.additionalShellManifests === undefined,
426
- 'Single-shell workspace must not declare additional-shell manifests',
427
- );
428
- assert(
429
- workspaceValidationContract.additionalShells === undefined,
430
- 'Single-shell workspace must not declare additional-shell records',
431
- );
432
- for (const field of [
433
- 'additionalShellOwnerIds',
434
- 'additionalShellDeliveryUnitIds',
435
- 'additionalShellDegradedStateIds',
436
- 'additionalShellBuildMarkerIds',
437
- ]) {
438
- assert(
439
- workspaceValidationContract.cohort?.[field] === undefined,
440
- `Single-shell workspace must not declare ${field}`,
441
- );
442
- }
443
- }
444
- };
445
- const sourceExtensions = new Set([
446
- '.cjs',
447
- '.cts',
448
- '.js',
449
- '.jsx',
450
- '.mjs',
451
- '.mts',
452
- '.ts',
453
- '.tsx',
454
- ]);
455
- const collectCompilerInputs = relativeDirectory => {
456
- const absoluteDirectory = path.join(root, relativeDirectory);
457
- if (!fs.existsSync(absoluteDirectory)) {
458
- return [];
459
- }
460
- const files = [];
461
- const queue = [absoluteDirectory];
462
- while (queue.length > 0) {
463
- const current = queue.shift();
464
- for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
465
- const absoluteEntry = path.join(current, entry.name);
466
- if (entry.isDirectory()) {
467
- queue.push(absoluteEntry);
468
- } else if (sourceExtensions.has(path.extname(entry.name))) {
469
- files.push(absoluteEntry);
470
- }
471
- }
472
- }
473
- return files.toSorted();
474
- };
475
- const initializeCompilerSnapshot = absolutePaths => {
476
- if (typescript === undefined || typescriptApi === undefined) {
477
- return;
478
- }
479
- const compiler = new typescriptApi.API();
480
- compilerSnapshot = compiler.updateSnapshot({
481
- openFiles: [...absolutePaths],
482
- });
483
- };
484
- const parseCompilerInput = absolutePath => {
485
- const project = compilerSnapshot?.getDefaultProjectForFile(absolutePath);
486
- const sourceFile = project?.program.getSourceFile(absolutePath);
487
- assert(
488
- sourceFile !== undefined,
489
- `Native TypeScript compiler could not parse ${path.relative(root, absolutePath)}`,
490
- );
491
- return sourceFile;
492
- };
493
- const nodeName = node => {
494
- if (
495
- typescript.isIdentifier(node) ||
496
- typescript.isPrivateIdentifier(node) ||
497
- typescript.isStringLiteralLikeNode(node) ||
498
- typescript.isNumericLiteral(node)
499
- ) {
500
- return node.text;
501
- }
502
- return undefined;
503
- };
504
- const propertyAccessPath = node => {
505
- if (typescript.isIdentifier(node)) {
506
- return [node.text];
507
- }
508
- if (
509
- typescript.isPropertyAccessExpression(node) ||
510
- typescript.isPropertyAccessChain?.(node)
511
- ) {
512
- const left = propertyAccessPath(node.expression);
513
- return left === undefined ? undefined : [...left, node.name.text];
514
- }
515
- if (
516
- typescript.isElementAccessExpression(node) &&
517
- typescript.isStringLiteralLikeNode(node.argumentExpression)
518
- ) {
519
- const left = propertyAccessPath(node.expression);
520
- return left === undefined
521
- ? undefined
522
- : [...left, node.argumentExpression.text];
523
- }
524
- return undefined;
525
- };
526
- const compilerFailure = (sourceFile, node, contract, diagnostic, fixArea) => {
527
- const position = sourceFile.getLineAndCharacterOfPosition(
528
- node.getStart(sourceFile),
529
- );
530
- const relativePath = path.relative(root, sourceFile.fileName).split(path.sep).join('/');
531
- throw new Error(
532
- selfCheckFailure(
533
- contract,
534
- `${diagnostic} Compiler evidence: ${relativePath}:${position.line + 1}:${position.character + 1}`,
535
- fixArea,
536
- ),
537
- );
538
- };
539
- const runtimeModuleReferences = sourceFile => {
540
- const references = [];
541
- const record = (node, specifier, kind, typeOnly = false) => {
542
- if (!typeOnly) {
543
- references.push({ kind, node, specifier });
544
- }
545
- };
546
- const visit = node => {
547
- if (
548
- typescript.isImportDeclaration(node) &&
549
- typescript.isStringLiteralLikeNode(node.moduleSpecifier)
550
- ) {
551
- const importClause = node.importClause;
552
- const namedBindings = importClause?.namedBindings;
553
- const namedBindingsAreTypeOnly =
554
- namedBindings !== undefined &&
555
- typescript.isNamedImports(namedBindings) &&
556
- namedBindings.elements.length > 0 &&
557
- namedBindings.elements.every(element => element.isTypeOnly);
558
- record(
559
- node,
560
- node.moduleSpecifier.text,
561
- importClause === undefined ? 'side-effect-import' : 'static-import',
562
- importClause?.isTypeOnly === true ||
563
- (importClause?.name === undefined && namedBindingsAreTypeOnly),
564
- );
565
- } else if (
566
- typescript.isExportDeclaration(node) &&
567
- node.moduleSpecifier !== undefined &&
568
- typescript.isStringLiteralLikeNode(node.moduleSpecifier)
569
- ) {
570
- record(
571
- node,
572
- node.moduleSpecifier.text,
573
- 'static-import',
574
- node.isTypeOnly,
575
- );
576
- } else if (
577
- typescript.isCallExpression(node) &&
578
- node.arguments.length > 0 &&
579
- typescript.isStringLiteralLikeNode(node.arguments[0])
580
- ) {
581
- if (node.expression.kind === typescript.SyntaxKind.ImportKeyword) {
582
- record(node, node.arguments[0].text, 'dynamic-import');
583
- } else if (
584
- typescript.isIdentifier(node.expression) &&
585
- node.expression.text === 'require'
586
- ) {
587
- record(node, node.arguments[0].text, 'require');
588
- }
589
- }
590
- node.forEachChild(visit);
591
- };
592
- visit(sourceFile);
593
- return references;
594
- };
595
- const importDiagnosticId = (prefix, kind) => {
596
- switch (kind) {
597
- case 'side-effect-import':
598
- return `${prefix}-side-effect-import`;
599
- case 'dynamic-import':
600
- return `${prefix}-dynamic-import`;
601
- case 'require':
602
- return `${prefix}-require`;
603
- default:
604
- return prefix === 'vertical-directory'
605
- ? 'vertical-directory-deep-import'
606
- : 'workspace-package-source-import';
607
- }
608
- };
609
- const isWorkspaceSourceSpecifier = specifier => {
610
- const segments = specifier.split('/');
611
- return (
612
- segments.length > 3 &&
613
- segments[0].startsWith('@') &&
614
- segments[2] === 'src'
615
- );
616
- };
617
- const resolvesInsideVerticalDirectory = (sourceFile, specifier) => {
618
- if (!specifier.startsWith('.')) {
619
- return false;
620
- }
621
- const specifierSegments = specifier.replaceAll('\\', '/').split('/');
622
- if (
623
- specifierSegments.some(
624
- (segment, index) =>
625
- segment === 'verticals' && specifierSegments[index + 1] !== undefined,
626
- )
627
- ) {
628
- return true;
629
- }
630
- const absoluteTarget = path.resolve(path.dirname(sourceFile.fileName), specifier);
631
- const verticalRoot = path.join(root, 'verticals') + path.sep;
632
- if (!absoluteTarget.startsWith(verticalRoot)) {
633
- return false;
634
- }
635
- const relativeSegments = path.relative(verticalRoot, absoluteTarget).split(path.sep);
636
- return relativeSegments.length > 1;
637
- };
638
- const jsxTagName = tagName =>
639
- typescript.isIdentifier(tagName) ? tagName.text : undefined;
640
- const jsxAttribute = (attributes, name) =>
641
- attributes.properties.find(
642
- property =>
643
- typescript.isJsxAttribute(property) &&
644
- nodeName(property.name) === name,
645
- );
646
- const isBooleanLiteral = (node, value) =>
647
- node?.kind ===
648
- (value ? typescript.SyntaxKind.TrueKeyword : typescript.SyntaxKind.FalseKeyword);
649
- const isCallNamed = (node, names) => {
650
- if (node === undefined || !typescript.isCallExpression(node)) {
651
- return false;
652
- }
653
- const accessPath = propertyAccessPath(node.expression);
654
- return accessPath !== undefined && names.has(accessPath.at(-1));
655
- };
656
- const objectLiteralProperty = (objectLiteral, name) =>
657
- objectLiteral.properties.find(
658
- property =>
659
- typescript.isPropertyAssignment(property) &&
660
- nodeName(property.name) === name,
661
- );
662
- const moduleFederationConfigArgument = sourceFile => {
663
- let argument;
664
- const visit = node => {
665
- if (argument !== undefined) {
666
- return;
667
- }
668
- if (
669
- typescript.isCallExpression(node) &&
670
- typescript.isIdentifier(node.expression) &&
671
- node.expression.text === 'createModuleFederationConfig' &&
672
- node.arguments.length === 1 &&
673
- typescript.isObjectLiteralExpression(node.arguments[0])
674
- ) {
675
- argument = node.arguments[0];
676
- return;
677
- }
678
- node.forEachChild(visit);
679
- };
680
- visit(sourceFile);
681
- return argument;
682
- };
683
- const appDeclaresReactRouter = appPath => {
684
- const relativePath = `${appPath}/package.json`;
685
- if (!fs.existsSync(path.join(root, relativePath))) {
686
- return false;
687
- }
688
- const packageJson = readJson(relativePath);
689
- return ['react-router', 'react-router-dom'].some(dependency =>
690
- ['dependencies', 'devDependencies'].some(field =>
691
- Object.hasOwn(packageJson[field] ?? {}, dependency),
692
- ),
693
- );
694
- };
695
- // TanStack Router is the frontend router of every generated workspace, and
696
- // `@module-federation/rspack` decides the bridge router purely from this flag
697
- // and `@module-federation/bridge-react` — it never inspects react-router. The
698
- // flag therefore has to mirror the app's own manifest: `false` (the router-free
699
- // bridge-react base entry) unless the app declares React Router itself.
700
- const bridgeRouterFixArea =
701
- 'declare bridge.enableBridgeRouter: false, or true only in an app that declares react-router or react-router-dom';
702
- const assertBridgeRouterCapability = (sourceFile, appPath) => {
703
- const contract = 'module federation bridge capability';
704
- const configArgument = moduleFederationConfigArgument(sourceFile);
705
- if (configArgument === undefined) {
706
- compilerFailure(
707
- sourceFile,
708
- sourceFile,
709
- contract,
710
- 'Generated Module Federation must pass a literal config object to createModuleFederationConfig.',
711
- bridgeRouterFixArea,
712
- );
713
- }
714
- const bridge = objectLiteralProperty(configArgument, 'bridge');
715
- const declaration =
716
- bridge !== undefined &&
717
- typescript.isObjectLiteralExpression(bridge.initializer)
718
- ? objectLiteralProperty(bridge.initializer, 'enableBridgeRouter')
719
- : undefined;
720
- if (declaration === undefined) {
721
- compilerFailure(
722
- sourceFile,
723
- bridge ?? configArgument,
724
- contract,
725
- 'Generated Module Federation must declare bridge.enableBridgeRouter in its config object.',
726
- bridgeRouterFixArea,
727
- );
728
- }
729
- if (isBooleanLiteral(declaration.initializer, false)) {
730
- return;
731
- }
732
- if (!isBooleanLiteral(declaration.initializer, true)) {
733
- compilerFailure(
734
- sourceFile,
735
- declaration,
736
- contract,
737
- 'Generated Module Federation must declare bridge.enableBridgeRouter as a boolean literal.',
738
- bridgeRouterFixArea,
739
- );
740
- }
741
- if (!appDeclaresReactRouter(appPath)) {
742
- compilerFailure(
743
- sourceFile,
744
- declaration,
745
- contract,
746
- `Generated Module Federation enables the React bridge router while ${appPath}/package.json declares neither react-router nor react-router-dom.`,
747
- bridgeRouterFixArea,
748
- );
749
- }
750
- };
751
- const assertCompilerArchitecture = () => {
752
- const structuralPolicy = workspaceValidationContract.structuralShellPolicy;
753
- assert(
754
- structuralPolicy?.schemaVersion === 1,
755
- 'Structural thin-shell compiler policy must use schemaVersion 1',
756
- );
757
- const compositionPolicy = workspaceValidationContract.federatedCompositionPolicy;
758
- assert(
759
- compositionPolicy?.schemaVersion === 1,
760
- 'Federated composition compiler policy must use schemaVersion 1',
761
- );
762
- const compilerInputs = new Set([
763
- ...(structuralPolicy.shells ?? []).flatMap(shell =>
764
- collectCompilerInputs(shell.srcDir),
765
- ),
766
- ...(compositionPolicy.hosts ?? []).flatMap(host =>
767
- collectCompilerInputs(host.srcDir),
768
- ),
769
- ...workspaceValidationContract.topology.compactConfig.apps.flatMap(app =>
770
- collectCompilerInputs(app.path),
771
- ),
772
- ]);
773
- initializeCompilerSnapshot(compilerInputs);
774
- for (const shell of structuralPolicy.shells ?? []) {
775
- for (const forbidden of structuralPolicy.forbiddenPathClasses ?? []) {
776
- assert(
777
- !fs.existsSync(path.join(root, shell.packageDir, forbidden.path)),
778
- selfCheckFailure(
779
- `structural thin-shell ${forbidden.id}`,
780
- `${forbidden.diagnostic} Forbidden artifact ${shell.packageDir}/${forbidden.path} exists`,
781
- `remove forbidden thin-shell artifact ${shell.packageDir}/${forbidden.path}`,
782
- ),
783
- );
784
- }
785
- if (compilerSnapshot === undefined) {
786
- continue;
787
- }
788
- for (const absolutePath of collectCompilerInputs(shell.srcDir)) {
789
- const sourceFile = parseCompilerInput(absolutePath);
790
- for (const reference of runtimeModuleReferences(sourceFile)) {
791
- if (resolvesInsideVerticalDirectory(sourceFile, reference.specifier)) {
792
- const id = importDiagnosticId('vertical-directory', reference.kind);
793
- compilerFailure(
794
- sourceFile,
795
- reference.node,
796
- `structural thin-shell ${id}`,
797
- 'A thin Shell must consume only published vertical surfaces, never a vertical implementation directory.',
798
- 'consume only published surfaces from the thin shell',
799
- );
800
- }
801
- if (isWorkspaceSourceSpecifier(reference.specifier)) {
802
- const id = importDiagnosticId('workspace-package', reference.kind);
803
- compilerFailure(
804
- sourceFile,
805
- reference.node,
806
- `structural thin-shell ${id}`,
807
- 'A thin Shell must consume only package exports, never another package raw src directory.',
808
- 'consume only published surfaces from the thin shell',
809
- );
810
- }
811
- }
812
- }
813
- }
814
-
815
- if (compilerSnapshot === undefined) {
816
- const packageJson = readJson('package.json');
817
- assert(
818
- typeof packageJson.devDependencies?.['@typescript/native'] === 'string',
819
- 'Generated workspaces must declare the native TypeScript compiler used by architecture evidence',
820
- );
821
- assert(
822
- packageJson.scripts?.check?.split('&&').some(
823
- stage => stage.trim() === 'pnpm typecheck',
824
- ),
825
- 'Generated workspace check must execute compiler evidence before contract validation',
826
- );
827
- return;
828
- }
829
-
830
- for (const host of compositionPolicy.hosts ?? []) {
831
- for (const absolutePath of collectCompilerInputs(host.srcDir)) {
832
- const sourceFile = parseCompilerInput(absolutePath);
833
- if (sourceFile.isDeclarationFile) {
834
- continue;
835
- }
836
- const remoteImplementation = specifier =>
837
- (host.remotes ?? []).find(remote => {
838
- if (specifier === remote.packageName) {
839
- return true;
840
- }
841
- if (specifier.startsWith(`${remote.packageName}/`)) {
842
- const subpath = specifier.slice(remote.packageName.length + 1);
843
- return subpath !== 'api' && !subpath.startsWith('api/');
844
- }
845
- if (!specifier.startsWith('.')) {
846
- return false;
847
- }
848
- const absoluteTarget = path.resolve(
849
- path.dirname(sourceFile.fileName),
850
- specifier,
851
- );
852
- const remoteDirectory = path.join(root, remote.directory);
853
- return (
854
- absoluteTarget === remoteDirectory ||
855
- absoluteTarget.startsWith(remoteDirectory + path.sep)
856
- );
857
- });
858
- for (const reference of runtimeModuleReferences(sourceFile)) {
859
- const remote = remoteImplementation(reference.specifier);
860
- if (remote !== undefined) {
861
- compilerFailure(
862
- sourceFile,
863
- reference.node,
864
- 'federated composition remote-runtime-package-import',
865
- `Host ${host.id} imports the ${remote.id} render implementation through ${reference.specifier}.`,
866
- 'use import type for contracts or compose the implementation through Module Federation',
867
- );
868
- }
869
- }
870
-
871
- const hydrationStateSetters = new Set();
872
- const visit = node => {
873
- if (
874
- typescript.isVariableDeclaration(node) &&
875
- typescript.isArrayBindingPattern(node.name) &&
876
- node.name.elements.length >= 2 &&
877
- isCallNamed(node.initializer, new Set(['useState'])) &&
878
- isBooleanLiteral(node.initializer.arguments[0], false)
879
- ) {
880
- const stateName = nodeName(node.name.elements[0].name)?.toLowerCase();
881
- const setterName = nodeName(node.name.elements[1].name);
882
- if (
883
- (stateName === 'hydrated' || stateName === 'ishydrated') &&
884
- setterName !== undefined
885
- ) {
886
- hydrationStateSetters.add(setterName);
887
- }
888
- }
889
- if (
890
- (typescript.isVariableDeclaration(node) ||
891
- typescript.isFunctionDeclaration(node)) &&
892
- nodeName(node.name) === 'createHydratedRemote'
893
- ) {
894
- compilerFailure(
895
- sourceFile,
896
- node,
897
- 'federated composition hydrated-remote-factory',
898
- 'Federated hosts must use the distributed SSR boundary directly; hydration-time remote factories are forbidden.',
899
- 'compose remote rendering through framework Module Federation primitives',
900
- );
901
- }
902
- if (
903
- (typescript.isPropertyAssignment(node) ||
904
- typescript.isJsxAttribute(node)) &&
905
- (nodeName(node.name) === 'loading' || nodeName(node.name) === 'fallback')
906
- ) {
907
- const value = typescript.isPropertyAssignment(node)
908
- ? node.initializer
909
- : node.initializer !== undefined &&
910
- typescript.isJsxExpression(node.initializer)
911
- ? node.initializer.expression
912
- : undefined;
913
- if (
914
- value !== undefined &&
915
- (typescript.isJsxElement(value) ||
916
- typescript.isJsxSelfClosingElement(value))
917
- ) {
918
- const tagName = typescript.isJsxElement(value)
919
- ? jsxTagName(value.openingElement.tagName)
920
- : jsxTagName(value.tagName);
921
- if (tagName === 'ServerComponent' || tagName === 'LocalComponent') {
922
- compilerFailure(
923
- sourceFile,
924
- node,
925
- 'federated composition local-loading-copy',
926
- 'Federated hosts must not render a local component copy while loading a remote implementation.',
927
- 'use an explicit unavailable-state component instead of a local implementation copy',
928
- );
929
- }
930
- }
931
- }
932
- node.forEachChild(visit);
933
- };
934
- visit(sourceFile);
935
- if (hydrationStateSetters.size > 0) {
936
- const findHydrationSetter = node => {
937
- if (
938
- typescript.isCallExpression(node) &&
939
- typescript.isIdentifier(node.expression) &&
940
- hydrationStateSetters.has(node.expression.text) &&
941
- isBooleanLiteral(node.arguments[0], true)
942
- ) {
943
- compilerFailure(
944
- sourceFile,
945
- node,
946
- 'federated composition hydration-flag',
947
- 'Federated hosts must hydrate the server DOM directly; hydrated-state component switching is forbidden.',
948
- 'compose remote rendering through framework Module Federation primitives',
949
- );
950
- }
951
- node.forEachChild(findHydrationSetter);
952
- };
953
- findHydrationSetter(sourceFile);
954
- }
955
- }
956
- }
957
-
958
- const shellRouteDirectories = (structuralPolicy.shells ?? []).map(
959
- shell => `${shell.packageDir}/src/routes`,
960
- );
961
- const appPaths = workspaceValidationContract.topology.compactConfig.apps.map(
962
- app => app.path,
963
- );
964
- const configFiles = appPaths.flatMap(appPath => [
965
- `${appPath}/modern.config.ts`,
966
- `${appPath}/module-federation.config.ts`,
967
- ]);
968
- const moduleFederationConfigAppPaths = new Map(
969
- appPaths
970
- .map(appPath => [
971
- path.join(root, `${appPath}/module-federation.config.ts`),
972
- appPath,
973
- ])
974
- .filter(entry => fs.existsSync(entry[0])),
975
- );
976
- const checkedFiles = new Set([
977
- ...shellRouteDirectories.flatMap(collectCompilerInputs),
978
- ...configFiles
979
- .map(relativePath => path.join(root, relativePath))
980
- .filter(absolutePath => fs.existsSync(absolutePath)),
981
- ]);
982
- for (const absolutePath of checkedFiles) {
983
- const sourceFile = parseCompilerInput(absolutePath);
984
- const isShellRoute = shellRouteDirectories.some(relativeDirectory => {
985
- const absoluteDirectory = path.join(root, relativeDirectory);
986
- return absolutePath.startsWith(absoluteDirectory + path.sep);
987
- });
988
- const moduleFederationAppPath =
989
- moduleFederationConfigAppPaths.get(absolutePath);
990
- const visit = node => {
991
- if (
992
- typescript.isImportDeclaration(node) &&
993
- typescript.isStringLiteralLikeNode(node.moduleSpecifier) &&
994
- node.moduleSpecifier.text === 'node:child_process'
995
- ) {
996
- compilerFailure(
997
- sourceFile,
998
- node,
999
- 'framework config API node-child-process-access',
1000
- 'Generated config must not invoke node:child_process directly.',
1001
- 'use the framework config environment API',
1002
- );
1003
- }
1004
- const accessPath = propertyAccessPath(node);
1005
- if (sameJson(accessPath, ['process', 'env'])) {
1006
- compilerFailure(
1007
- sourceFile,
1008
- node,
1009
- 'framework config API direct-process-env-access',
1010
- 'Generated config must use the framework config environment API instead of process.env.',
1011
- 'use getBuildConfigEnvironment',
1012
- );
1013
- }
1014
- if (
1015
- typescript.isIdentifier(node) &&
1016
- node.text === 'ULTRAMODERN_ZEPHYR'
1017
- ) {
1018
- compilerFailure(
1019
- sourceFile,
1020
- node,
1021
- 'zephyr gating ultramodern-zephyr-environment-gate',
1022
- 'Generated Zephyr integration must not be gated through ULTRAMODERN_ZEPHYR.',
1023
- 'use Zephyr native deploy-token behavior',
1024
- );
1025
- }
1026
- if (typescript.isPropertyAssignment(node)) {
1027
- const property = nodeName(node.name);
1028
- if (
1029
- // Inside a Module Federation config the flag is required evidence
1030
- // checked positionally after this walk; anywhere else it is a
1031
- // deviation, whatever it is set to.
1032
- (property === 'enableBridgeRouter' &&
1033
- moduleFederationAppPath === undefined) ||
1034
- (property === 'disableDynamicRemoteTypeHints' &&
1035
- isBooleanLiteral(node.initializer, true)) ||
1036
- property === 'treeShakingSharedExcludePlugins'
1037
- ) {
1038
- compilerFailure(
1039
- sourceFile,
1040
- node,
1041
- 'module federation bridge capability',
1042
- `Generated Module Federation carries forbidden option ${property}.`,
1043
- 'use framework-owned Module Federation defaults',
1044
- );
1045
- }
1046
- }
1047
- if (isShellRoute) {
1048
- if (
1049
- typescript.isJsxOpeningElement(node) &&
1050
- jsxTagName(node.tagName) === 'a' &&
1051
- jsxAttribute(node.attributes, 'onClick') !== undefined
1052
- ) {
1053
- compilerFailure(
1054
- sourceFile,
1055
- node,
1056
- 'shell routing synthetic-anchor-click-interception',
1057
- 'Shell routing must not intercept anchor clicks synthetically.',
1058
- 'use the router Link primitive',
1059
- );
1060
- }
1061
- if (
1062
- isCallNamed(node, new Set(['hydrateRoot', 'loadRemote', 'loadShare']))
1063
- ) {
1064
- compilerFailure(
1065
- sourceFile,
1066
- node,
1067
- 'module federation native loading',
1068
- 'Shell routing must use framework Module Federation loading primitives.',
1069
- 'remove manual hydration or remote-loading wrappers',
1070
- );
1071
- }
1072
- if (
1073
- (typescript.isBinaryExpression(node) &&
1074
- node.operatorToken.kind === typescript.SyntaxKind.EqualsToken &&
1075
- propertyAccessPath(node.left)?.slice(0, 2).join('.') ===
1076
- 'window.location') ||
1077
- (typescript.isCallExpression(node) &&
1078
- propertyAccessPath(node.expression)?.slice(0, 2).join('.') ===
1079
- 'window.location')
1080
- ) {
1081
- compilerFailure(
1082
- sourceFile,
1083
- node,
1084
- 'shell routing window-location-navigation',
1085
- 'Shell routing must use router navigation instead of window.location.',
1086
- 'use the router navigation primitive',
1087
- );
1088
- }
1089
- }
1090
- node.forEachChild(visit);
1091
- };
1092
- visit(sourceFile);
1093
- if (moduleFederationAppPath !== undefined) {
1094
- assertBridgeRouterCapability(sourceFile, moduleFederationAppPath);
1095
- }
1096
- }
1097
- };
1098
- const compactConfigPolicyView = config => ({
1099
- schemaVersion: config.schemaVersion,
1100
- profile: config.profile,
1101
- workspace: config.workspace,
1102
- features: config.features,
1103
- deploy: config.deploy,
1104
- moduleFederation: config.moduleFederation,
1105
- backendFederation: config.backendFederation,
1106
- agentSkills: config.agentSkills,
1107
- tooling: config.tooling,
1108
- });
1109
- const assertStructuredWorkspaceMetadata = ({
1110
- ultramodernConfig,
1111
- topology,
1112
- ownership,
1113
- overlay,
1114
- }) => {
1115
- const observedMetadata = [
1116
- {
1117
- contract: workspaceValidationContract.metadata.compactConfig,
1118
- value: ultramodernConfig,
1119
- },
1120
- {
1121
- contract: workspaceValidationContract.metadata.referenceTopology,
1122
- value: topology,
1123
- },
1124
- {
1125
- contract: workspaceValidationContract.metadata.ownership,
1126
- value: ownership,
1127
- },
1128
- {
1129
- contract: workspaceValidationContract.metadata.developmentOverlay,
1130
- value: overlay,
1131
- },
1132
- ];
1133
-
1134
- for (const entry of observedMetadata) {
1135
- assert(
1136
- entry.value !== null &&
1137
- typeof entry.value === 'object' &&
1138
- !Array.isArray(entry.value),
1139
- `${entry.contract.path} must contain a JSON object`,
1140
- );
1141
- assert(
1142
- Number.isInteger(entry.value.schemaVersion),
1143
- `${entry.contract.path} must declare an integer schemaVersion`,
1144
- );
1145
- }
1146
-
1147
- const observedSchemaVersions = new Set(
1148
- observedMetadata.map(entry => entry.value.schemaVersion),
1149
- );
1150
- assert(
1151
- observedSchemaVersions.size === 1,
1152
- `Mixed workspace metadata schema versions: ${observedMetadata
1153
- .map(entry => `${entry.contract.path}=${entry.value.schemaVersion}`)
1154
- .join(', ')}`,
1155
- );
1156
- for (const entry of observedMetadata) {
1157
- assert(
1158
- entry.value.schemaVersion === entry.contract.schemaVersion,
1159
- `Unsupported workspace metadata schemaVersion ${entry.value.schemaVersion} at ${entry.contract.path}; expected ${entry.contract.schemaVersion}`,
1160
- );
1161
- }
1162
-
1163
- assertObject(
1164
- ultramodernConfig.packageSource,
1165
- `${compactConfigPath} packageSource`,
1166
- 'restore generated compact package-source metadata',
1167
- );
1168
- for (const field of
1169
- workspaceValidationContract.legacy.forbiddenCompactConfigFields) {
1170
- assert(
1171
- !Object.hasOwn(ultramodernConfig, field),
1172
- `Stale legacy field ${compactConfigPath}.${field} is forbidden`,
1173
- );
1174
- }
1175
- for (const field of
1176
- workspaceValidationContract.legacy.forbiddenPackageSourceFields) {
1177
- assert(
1178
- !Object.hasOwn(ultramodernConfig.packageSource, field),
1179
- `Stale legacy field ${compactConfigPath}.packageSource.${field} is forbidden`,
1180
- );
1181
- }
1182
- for (const field of workspaceValidationContract.legacy.forbiddenTopologyFields) {
1183
- assert(
1184
- !Object.hasOwn(topology, field),
1185
- `Stale legacy field ${workspaceValidationContract.metadata.referenceTopology.path}.${field} is forbidden`,
1186
- );
1187
- }
1188
-
1189
- assertSameIdCohort(
1190
- ultramodernConfig.topology?.apps,
1191
- workspaceValidationContract.cohort.appIds,
1192
- `${compactConfigPath} topology.apps`,
1193
- 'restore the complete generated app cohort',
1194
- );
1195
- assertSameIdCohort(
1196
- ultramodernConfig.moduleFederation?.apps,
1197
- workspaceValidationContract.cohort.appIds,
1198
- `${compactConfigPath} moduleFederation.apps`,
1199
- 'restore the complete generated Module Federation app cohort',
1200
- );
1201
- assertSameIdCohort(
1202
- ultramodernConfig.backendFederation?.apps,
1203
- workspaceValidationContract.cohort.backendAppIds,
1204
- `${compactConfigPath} backendFederation.apps`,
1205
- 'restore the complete generated backend app cohort',
1206
- );
1207
- assertSameIdCohort(
1208
- topology.verticals,
1209
- workspaceValidationContract.cohort.verticalIds,
1210
- `${workspaceValidationContract.metadata.referenceTopology.path} verticals`,
1211
- 'restore the complete generated vertical cohort',
1212
- );
1213
- assertSameIdCohort(
1214
- topology.sharedPackages,
1215
- workspaceValidationContract.cohort.sharedPackageIds,
1216
- `${workspaceValidationContract.metadata.referenceTopology.path} sharedPackages`,
1217
- 'restore the complete generated shared package cohort',
1218
- );
1219
- assertSameIdCohort(
1220
- topology.shell?.moduleFederation?.remotes,
1221
- workspaceValidationContract.topology.referenceTopology.shell.moduleFederation.remotes.map(
1222
- remote => remote.id,
1223
- ),
1224
- `${workspaceValidationContract.metadata.referenceTopology.path} shell.moduleFederation.remotes`,
1225
- 'restore the complete generated shell remote cohort',
1226
- );
1227
- assertSameIdCohort(
1228
- ownership.owners,
1229
- workspaceValidationContract.cohort.ownerIds,
1230
- `${workspaceValidationContract.metadata.ownership.path} owners`,
1231
- 'restore the complete generated ownership cohort',
1232
- );
1233
-
1234
- for (const manifest of workspaceValidationContract.cohort.packageManifests) {
1235
- assertExists(manifest.path);
1236
- const packageJson = readJson(manifest.path);
1237
- assert(
1238
- packageJson.name === manifest.packageName,
1239
- `${manifest.path} package name must be ${manifest.packageName}`,
1240
- );
1241
- if (manifest.role === 'shell' || manifest.role === 'vertical') {
1242
- assert(
1243
- packageJson.modernjs?.appId === manifest.id,
1244
- `${manifest.path} modernjs.appId must be ${manifest.id}`,
1245
- );
1246
- }
1247
- }
1248
- if (expectedReleaseCohort) {
1249
- const releaseCohortContract = workspaceValidationContract.metadata.releaseCohort;
1250
- assertSelfCheck(
1251
- releaseCohortContract?.path === '.modernjs/release-cohort.json',
1252
- 'authenticated release cohort projection',
1253
- 'Expected release-cohort metadata path is missing or invalid',
1254
- '.modernjs/release-cohort.json',
1255
- );
1256
- assertSameJson(
1257
- readJson(releaseCohortContract.path),
1258
- expectedReleaseCohort,
1259
- 'authenticated release cohort projection',
1260
- releaseCohortContract.path,
1261
- );
1262
- }
1263
- };
1264
- const assertStructuredWorkspaceMetadataSemantics = ({
1265
- ultramodernConfig,
1266
- topology,
1267
- ownership,
1268
- overlay,
1269
- }) => {
1270
- assertSameJson(
1271
- compactConfigPolicyView(ultramodernConfig),
1272
- workspaceValidationContract.policy.compactConfig,
1273
- `${compactConfigPath} policy`,
1274
- 'restore generated compact workspace policy metadata',
1275
- );
1276
- assertSameJson(
1277
- ultramodernConfig.topology,
1278
- workspaceValidationContract.topology.compactConfig,
1279
- `${compactConfigPath} topology`,
1280
- 'restore the complete generated compact topology cohort',
1281
- );
1282
- assertSameJson(
1283
- topology,
1284
- workspaceValidationContract.topology.referenceTopology,
1285
- workspaceValidationContract.metadata.referenceTopology.path,
1286
- 'restore the complete generated reference topology',
1287
- );
1288
- assertSameJson(
1289
- ownership,
1290
- workspaceValidationContract.topology.ownership,
1291
- workspaceValidationContract.metadata.ownership.path,
1292
- 'restore the complete generated ownership topology',
1293
- );
1294
- assertSameJson(
1295
- overlay,
1296
- workspaceValidationContract.topology.developmentOverlay,
1297
- workspaceValidationContract.metadata.developmentOverlay.path,
1298
- 'restore the complete generated development topology',
1299
- );
1300
- };
1301
- const findById = (entries, id) =>
1302
- Array.isArray(entries) ? entries.find(entry => entry?.id === id) : undefined;
1303
- const generatedContractLabel = compactConfigPath;
1304
- const toKebabCase = value =>
1305
- String(value)
1306
- .trim()
1307
- .replace(/([a-z0-9])([A-Z])/gu, '$1-$2')
1308
- .replace(/[^a-zA-Z0-9._-]+/gu, '-')
1309
- .replace(/[._]+/gu, '-')
1310
- .toLowerCase()
1311
- .replace(/-+/gu, '-')
1312
- .replace(/^-+|-+$/gu, '');
1313
- const toPascalCase = value =>
1314
- toKebabCase(value)
1315
- .split('-')
1316
- .filter(Boolean)
1317
- .map(part => part.charAt(0).toUpperCase() + part.slice(1))
1318
- .join('');
1319
- const toCamelCase = value => {
1320
- const pascal = toPascalCase(value);
1321
- return `${pascal.charAt(0).toLowerCase()}${pascal.slice(1)}`;
1322
- };
1323
- const toEnvSegment = value => toKebabCase(value).replace(/-/gu, '_').toUpperCase();
1324
- const packageNameFor = (scope, suffix) => `@${scope}/${suffix}`;
1325
- const normalizeRelativePath = value =>
1326
- String(value ?? '').replace(/\\/gu, '/').replace(/^\.\/+/u, '');
1327
- const appNamespace = app => (app.kind === 'shell' ? 'shell' : (app.domain ?? app.id));
1328
- const tailwindPrefixDigitWords = [
1329
- 'zero',
1330
- 'one',
1331
- 'two',
1332
- 'three',
1333
- 'four',
1334
- 'five',
1335
- 'six',
1336
- 'seven',
1337
- 'eight',
1338
- 'nine',
1339
- ];
1340
- const tailwindPrefixFor = app =>
1341
- app.kind === 'shell'
1342
- ? 'shell'
1343
- : String(app.domain ?? app.id)
1344
- .toLowerCase()
1345
- .replace(/[^a-z0-9]/gu, '')
1346
- .replace(/[0-9]/gu, digit => tailwindPrefixDigitWords[Number(digit)]);
1347
- const buildMarkerFor = app =>
1348
- crypto
1349
- .createHash('sha256')
1350
- .update(`${packageScope}:${app.packageSuffix}:${app.id}:0.1.0`)
1351
- .digest('hex')
1352
- .slice(0, 16);
1353
- const deliveryUnitIdentityFixArea =
1354
- 'regenerate vertical identity from delivery-unit record; do not hand-edit surface markers';
1355
- const deliveryUnitBlock = record => ({
1356
- schemaVersion: record?.schemaVersion,
1357
- kind: record?.kind,
1358
- unitId: record?.unitId,
1359
- packageName: record?.packageName,
1360
- version: record?.version,
1361
- buildMarker: record?.buildMarker,
1362
- sourceRevision: record?.sourceRevision,
1363
- });
1364
- const expectedCompactAppFor = id =>
1365
- workspaceValidationContract.topology.compactConfig?.apps?.find(
1366
- entry => entry?.id === id,
1367
- );
1368
- const expectedDeliveryUnitFor = vertical => {
1369
- const expectedApp = expectedCompactAppFor(vertical.id);
1370
- return (
1371
- expectedApp?.backendFederation?.deliveryUnit ??
1372
- expectedApp?.deliveryUnit ??
1373
- vertical.deliveryUnit
1374
- );
1375
- };
1376
- const normalizeCompactApp = rawApp => {
1377
- const id = String(rawApp.id);
1378
- const kind = rawApp.kind === 'vertical' ? 'vertical' : 'shell';
1379
- const appPath =
1380
- typeof rawApp.path === 'string'
1381
- ? normalizeRelativePath(rawApp.path)
1382
- : kind === 'shell'
1383
- ? 'apps/shell-super-app'
1384
- : `verticals/${toKebabCase(id)}`;
1385
- const packageSuffix =
1386
- typeof rawApp.packageSuffix === 'string'
1387
- ? rawApp.packageSuffix
1388
- : appPath.split('/').at(-1) ?? id;
1389
- const domain =
1390
- typeof rawApp.domain === 'string'
1391
- ? rawApp.domain
1392
- : kind === 'vertical'
1393
- ? packageSuffix
1394
- : undefined;
1395
- const moduleFederation =
1396
- rawApp.moduleFederation && typeof rawApp.moduleFederation === 'object'
1397
- ? rawApp.moduleFederation
1398
- : {};
1399
- const api =
1400
- rawApp.api && typeof rawApp.api === 'object'
1401
- ? {
1402
- stem:
1403
- typeof rawApp.api.stem === 'string'
1404
- ? rawApp.api.stem
1405
- : domain ?? id,
1406
- prefix:
1407
- typeof rawApp.api.prefix === 'string'
1408
- ? rawApp.api.prefix
1409
- : `/${domain ?? id}-api`,
1410
- consumedBy: Array.isArray(rawApp.api.consumedBy)
1411
- ? rawApp.api.consumedBy.filter(
1412
- consumer => typeof consumer === 'string',
1413
- )
1414
- : ['shell-super-app', id],
1415
- // Preserve the API protocol so the synthesized generated contract can
1416
- // branch between the REST (`shared/api.ts` + `./api/client`) and RPC
1417
- // (`shared/rpc.ts` + `./api/rpc-client`) surfaces.
1418
- protocol: rawApp.api.protocol === 'rpc' ? 'rpc' : 'rest',
1419
- }
1420
- : undefined;
1421
-
1422
- return {
1423
- id,
1424
- kind,
1425
- path: appPath,
1426
- package: typeof rawApp.package === 'string' ? rawApp.package : undefined,
1427
- packageSuffix,
1428
- domain,
1429
- port:
1430
- typeof rawApp.port === 'number'
1431
- ? rawApp.port
1432
- : kind === 'shell'
1433
- ? 3020
1434
- : 3030,
1435
- portEnv:
1436
- typeof rawApp.portEnv === 'string'
1437
- ? rawApp.portEnv
1438
- : kind === 'shell'
1439
- ? 'SHELL_SUPER_APP_PORT'
1440
- : `VERTICAL_${toEnvSegment(domain ?? id)}_PORT`,
1441
- mfName:
1442
- typeof moduleFederation.name === 'string'
1443
- ? moduleFederation.name
1444
- : kind === 'shell'
1445
- ? 'shellSuperApp'
1446
- : `vertical${toPascalCase(domain ?? id)}`,
1447
- exposes: Array.isArray(moduleFederation.exposes)
1448
- ? moduleFederation.exposes.filter(expose => typeof expose === 'string')
1449
- : [],
1450
- moduleFederationSsr: moduleFederation.ssr !== false,
1451
- verticalRefs: Array.isArray(moduleFederation.verticalRefs)
1452
- ? moduleFederation.verticalRefs.filter(ref => typeof ref === 'string')
1453
- : [],
1454
- deliveryUnit:
1455
- rawApp.deliveryUnit && typeof rawApp.deliveryUnit === 'object'
1456
- ? rawApp.deliveryUnit
1457
- : undefined,
1458
- backendFederation:
1459
- rawApp.backendFederation && typeof rawApp.backendFederation === 'object'
1460
- ? rawApp.backendFederation
1461
- : undefined,
1462
- api,
1463
- };
1464
- };
1465
- const compactAppsFromConfig = config =>
1466
- Array.isArray(config.topology?.apps)
1467
- ? config.topology.apps.map(normalizeCompactApp)
1468
- : [];
1469
- const remoteDependencyAliasFor = app => toCamelCase(app.domain ?? app.id.replace(/^remote-/u, ''));
1470
- const remoteContractsFor = (app, apps) =>
1471
- (app.verticalRefs ?? [])
1472
- .map(ref => apps.find(candidate => candidate.id === ref))
1473
- .filter(Boolean)
1474
- .map(remote => ({
1475
- id: remote.id,
1476
- alias: remoteDependencyAliasFor(remote),
1477
- name: remote.mfName,
1478
- manifestEnv: `VERTICAL_${toEnvSegment(remote.domain ?? remote.id)}_MF_MANIFEST`,
1479
- manifestUrl: `http://localhost:${remote.port}/mf-manifest.json`,
1480
- }));
1481
- const createRouteOwnedEntries = app => {
1482
- const namespace = appNamespace(app);
1483
- const base = {
1484
- descriptionKey: `${namespace}.seo.description`,
1485
- mfBoundaryId: app.mfName,
1486
- namespace,
1487
- ownerAppId: app.id,
1488
- public: false,
1489
- indexable: false,
1490
- publicSurface: 'private-app-screen',
1491
- };
1492
-
1493
- return [
1494
- {
1495
- ...base,
1496
- canonicalPath: '/',
1497
- id: app.kind === 'shell' ? 'shell-home' : `${app.id}-home`,
1498
- localisedPaths: {
1499
- cs: '/',
1500
- en: '/',
1501
- },
1502
- titleKey: app.kind === 'shell' ? 'shell.title' : `${namespace}.title`,
1503
- },
1504
- ];
1505
- };
1506
- const createPublicRoutes = app =>
1507
- createRouteOwnedEntries(app)
1508
- .filter(route => route.public && route.indexable)
1509
- .map(route => ({
1510
- canonicalPath: route.canonicalPath,
1511
- id: route.id,
1512
- localisedPaths: route.localisedPaths,
1513
- namespace: route.namespace,
1514
- ownerAppId: route.ownerAppId,
1515
- descriptionKey: route.descriptionKey,
1516
- titleKey: route.titleKey,
1517
- }));
1518
- const createLocalisedUrls = app =>
1519
- Object.fromEntries(
1520
- createRouteOwnedEntries(app).flatMap(route => {
1521
- if (route.canonicalPath === '/') {
1522
- return [];
1523
- }
1524
- return Array.from(
1525
- new Set([route.canonicalPath, ...Object.values(route.localisedPaths)]),
1526
- ).map(pathname => [pathname, route.localisedPaths]);
1527
- }),
1528
- );
1529
- const createPublicSurface = app => {
1530
- const publicRoutes = createPublicRoutes(app);
1531
- return {
1532
- authoring: 'colocated-route-meta',
1533
- artifactLifecycle: 'build-and-deploy-output',
1534
- generatedManifest: './src/routes/ultramodern-route-metadata',
1535
- source: 'route-owned-public-routes',
1536
- metadataExport: './src/routes/ultramodern-route-metadata',
1537
- generator: 'scripts/generate-public-surface-assets.mts',
1538
- outputRoot: 'dist/public',
1539
- cloudflareBuildOutputRoot: 'dist-cloudflare/public',
1540
- privateRoutePolicy: 'omit-from-generated-public-surface',
1541
- files:
1542
- publicRoutes.length > 0
1543
- ? ['robots.txt', 'sitemap.xml', 'site.webmanifest']
1544
- : ['robots.txt'],
1545
- omittedByDefault: ['api-catalog.json', 'llms.txt', 'security.txt'],
1546
- languages: ['en', 'cs'],
1547
- contentExpansion: {
1548
- authoring: 'route-owned-esm-provider',
1549
- defaultProviderFile: 'route.sitemap.mjs',
1550
- entryExport: 'default-or-entries',
1551
- paramsSource: 'params-or-localeParams',
1552
- draftPolicy: 'omit-draft-by-default',
1553
- indexablePolicy: 'omit-indexable-false',
1554
- lifecycle: 'executed-during-public-surface-generation',
1555
- },
1556
- contentSources: [],
1557
- publicRoutes,
1558
- routeEntries: [],
1559
- concreteUrlPaths: [],
1560
- };
1561
- };
1562
- const createPublicHead = () => ({
1563
- authoring: 'colocated-route-meta',
1564
- generator: './src/routes/ultramodern-route-head',
1565
- renderer: '@modern-js/runtime/head Helmet',
1566
- ssr: true,
1567
- title: {
1568
- required: true,
1569
- source: 'route.titleKey',
1570
- },
1571
- description: {
1572
- required: true,
1573
- source: 'route.descriptionKey',
1574
- },
1575
- canonical: {
1576
- publicIndexableOnly: true,
1577
- source: 'localized canonical route URL',
1578
- },
1579
- alternates: {
1580
- hreflang: ['en', 'cs'],
1581
- xDefault: 'en',
1582
- },
1583
- openGraph: {
1584
- publicIndexableOnly: true,
1585
- required: ['og:title', 'og:description', 'og:url', 'og:type'],
1586
- },
1587
- twitter: {
1588
- publicIndexableOnly: true,
1589
- required: ['twitter:card', 'twitter:title', 'twitter:description'],
1590
- },
1591
- structuredData: {
1592
- publicIndexableOnly: true,
1593
- optional: true,
1594
- source: 'route.jsonLd',
1595
- inference: false,
1596
- helperModule: './src/routes/ultramodern-jsonld',
1597
- helperTypes: [
1598
- 'WebPage',
1599
- 'WebApplication',
1600
- 'SoftwareApplication',
1601
- 'BreadcrumbList',
1602
- 'FAQPage',
1603
- 'Organization',
1604
- ],
1605
- sanitizesHtmlOpenBracket: true,
1606
- },
1607
- privateRouteRobots: 'noindex, nofollow',
1608
- });
1609
- const createQualityGates = () => ({
1610
- publicRoutes: {
1611
- requireSitemapWhenPresent: true,
1612
- requireRobotsSitemapConsistency: true,
1613
- requireWebManifestWhenPresent: true,
1614
- },
1615
- statusCodes: {
1616
- notFoundRoute: '/__ultramodern-smoke-missing/nope',
1617
- unknownRouteStatus: 404,
1618
- },
1619
- indexing: {
1620
- previewNoindex: true,
1621
- productionPublicRoutesIndexable: true,
1622
- },
1623
- assets: {
1624
- cssPreloadRequired: true,
1625
- cssResponseRequired: true,
1626
- cacheControlRequiredForCss: true,
1627
- sourcemapsPubliclyReferenced: false,
1628
- },
1629
- budgets: {
1630
- ssrHtmlMaxBytes: 250_000,
1631
- mfManifestMaxBytes: 500_000,
1632
- localeJsonMaxBytes: 100_000,
1633
- sitemapXmlMaxBytes: 500_000,
1634
- cssAssetMaxBytes: 750_000,
1635
- },
1636
- csp: {
1637
- finalMode: 'report-only-dogfood',
1638
- decision:
1639
- 'Report-only remains the generated final mode until public smoke proof records MF SSR script/style/connect compatibility for the deployed surface.',
1640
- },
1641
- });
1642
- const createCloudflareRoutes = app => ({
1643
- ssr: '/en',
1644
- mfManifest: '/mf-manifest.json',
1645
- locale: `/locales/en/${appNamespace(app)}.json`,
1646
- // Mirrors policy.ts:76 — the Cloudflare proof carries a REST readiness route
1647
- // only for `rest` units. An `rpc` unit exposes its `/rpc` route instead and
1648
- // must not emit (or require) a REST readiness path.
1649
- ...(app.api && app.api.protocol === 'rest'
1650
- ? {
1651
- apiReadiness: `${app.api.prefix}/${app.api.stem}/readiness`,
1652
- }
1653
- : {}),
1654
- });
1655
- const createCloudflareDeploy = app => ({
1656
- target: 'cloudflare',
1657
- workerName: expectedWorkerName(app.packageSuffix),
1658
- publicUrlEnv: `ULTRAMODERN_PUBLIC_URL_${toEnvSegment(app.id)}`,
1659
- compatibilityDate: expectedCloudflareCompatibilityDate,
1660
- compatibilityFlags: ['nodejs_compat', 'global_fetch_strictly_public'],
1661
- assetsBinding: 'ASSETS',
1662
- routes: createCloudflareRoutes(app),
1663
- security: expectedCloudflareSecurity,
1664
- qualityGates: createQualityGates(),
1665
- evidence: {
1666
- proofScript: 'scripts/proof-cloudflare-version.mts',
1667
- reportDefault:
1668
- '.codex/reports/cloudflare-version-proof/public-url-proof.json',
1669
- },
1670
- });
1671
- const createEffectReadiness = app => ({
1672
- endpoint: `/${app.api.stem}/readiness`,
1673
- marker: {
1674
- ui: 'ultramodernUiMarker',
1675
- api: 'ultramodernApiMarker',
1676
- skew: 'none',
1677
- },
1678
- checks: ['moduleFederation', 'ssr', 'translations', 'api'],
1679
- });
1680
- const createEffectRequestContext = () => ({
1681
- propagatedHeaders: [
1682
- 'accept-language',
1683
- 'authorization',
1684
- 'traceparent',
1685
- 'x-correlation-id',
1686
- 'x-tenant-id',
1687
- 'x-ultramodern-env',
1688
- 'x-vertical-version-id',
1689
- ],
1690
- source: 'shell-to-vertical-api-client',
1691
- });
1692
- const createEffectDomainOperations = app => {
1693
- const stem = app.api.stem;
1694
- const group = toCamelCase(stem);
1695
- const basePath = `/${stem}`;
1696
- return {
1697
- workspaceFeed: {
1698
- client: `list${toPascalCase(stem)}`,
1699
- method: 'GET',
1700
- path: basePath,
1701
- resource: 'workspace-items',
1702
- owner: app.id,
1703
- },
1704
- workspaceDetail: {
1705
- client: `get${toPascalCase(stem)}`,
1706
- method: 'GET',
1707
- path: `${basePath}/:id`,
1708
- resource: 'workspace-item',
1709
- owner: app.id,
1710
- },
1711
- workspaceCreate: {
1712
- client: `create${toPascalCase(stem)}`,
1713
- method: 'POST',
1714
- path: basePath,
1715
- resource: group,
1716
- owner: app.id,
1717
- },
1718
- };
1719
- };
1720
- const createEffectOperationContract = app => ({
1721
- group: toCamelCase(app.api.stem),
1722
- operations: {
1723
- readiness: {
1724
- method: 'GET',
1725
- path: `/${app.api.stem}/readiness`,
1726
- source: 'generated-client',
1727
- },
1728
- },
1729
- });
1730
- const createAppConfigContract = app => ({
1731
- preset: 'presetUltramodern',
1732
- plugins: [
1733
- 'appTools',
1734
- 'ultramodernReleaseEnvelopePlugin',
1735
- 'tanstackRouterPlugin',
1736
- 'i18nPlugin',
1737
- ...(app.api ? ['bffPlugin'] : []),
1738
- 'moduleFederationPlugin',
1739
- 'zephyrRspackPlugin',
1740
- ],
1741
- dev: {
1742
- assetPrefix: app.kind === 'shell' ? '/' : 'app-public-origin',
1743
- },
1744
- output: {
1745
- assetPrefix: {
1746
- envFallbackOrder: ['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX'],
1747
- default: app.kind === 'shell' ? '/' : 'app-public-origin',
1748
- },
1749
- disableTsChecker: false,
1750
- },
1751
- performance: {
1752
- readinessDiagnostics: {
1753
- default: 'enabled',
1754
- optOut: {
1755
- env: 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false',
1756
- config: 'scripts/ultramodern-performance-readiness.config.mjs',
1757
- },
1758
- failOn: 'framework-invariant',
1759
- },
1760
- },
1761
- rspack: {
1762
- output: {
1763
- uniqueName: app.mfName,
1764
- chunkLoadingGlobal: expectedChunkLoadingGlobal(app.mfName),
1765
- },
1766
- },
1767
- source: {
1768
- siteUrl: {
1769
- envFallbackOrder: [
1770
- 'MODERN_PUBLIC_SITE_URL',
1771
- `ULTRAMODERN_PUBLIC_URL_${toEnvSegment(app.id)}`,
1772
- 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN',
1773
- app.portEnv,
1774
- ],
1775
- defaultLocalhostPort: app.port,
1776
- },
1777
- },
1778
- });
1779
- const cssDedupe = () => ({
1780
- strategy: 'shared-token-package-plus-css-content-hash',
1781
- sharedPackage: packageNameFor(packageScope, 'shared-design-tokens'),
1782
- sharedLayers: ['ultramodern-shared-tokens'],
1783
- runtimeLoad: 'once-per-content-hash',
1784
- duplicateBaseStylesAllowed: false,
1785
- });
1786
- const createStylingContract = app => {
1787
- const sharedTokenPackage = packageNameFor(packageScope, 'shared-design-tokens');
1788
- const ownedLayers =
1789
- app.kind === 'shell'
1790
- ? ['ultramodern-shell-base', 'ultramodern-shell-overlay']
1791
- : [`ultramodern-vertical-${app.domain ?? app.id}`];
1792
-
1793
- return {
1794
- tailwind: tailwindEnabled,
1795
- federation: {
1796
- owner: {
1797
- id: app.id,
1798
- package: app.package ?? packageNameFor(packageScope, app.packageSuffix),
1799
- },
1800
- role: app.kind === 'shell' ? 'shell-base-overlay' : 'vertical-css',
1801
- rootSelector: `[data-app-id="${app.id}"]`,
1802
- classPrefix: `${tailwindPrefixFor(app)}:`,
1803
- layers: {
1804
- shared: ['ultramodern-shared-tokens'],
1805
- owned: ownedLayers,
1806
- },
1807
- entrypoints: {
1808
- layoutImport: 'src/routes/layout.tsx',
1809
- css: ['src/routes/index.css'],
1810
- ...(app.kind === 'shell'
1811
- ? {}
1812
- : { federationEntry: 'src/federation-entry.tsx' }),
1813
- },
1814
- assets: {
1815
- shared: [`${sharedTokenPackage}/tokens.css`],
1816
- owned: ['src/routes/index.css'],
1817
- },
1818
- dedupe: cssDedupe(),
1819
- ssr: {
1820
- cloudflare: true,
1821
- firstPaintRequired: true,
1822
- verticalCss:
1823
- app.kind === 'shell'
1824
- ? 'host-preloads-shell-and-shared-css'
1825
- : 'federated-manifest-owned-css',
1826
- },
1827
- },
1828
- };
1829
- };
1830
- const createAppContract = (app, apps) => ({
1831
- id: app.id,
1832
- package: app.package ?? packageNameFor(packageScope, app.packageSuffix),
1833
- path: app.path,
1834
- kind: app.kind,
1835
- config: createAppConfigContract(app),
1836
- styling: createStylingContract(app),
1837
- deploy: {
1838
- target: 'cloudflare',
1839
- cloudflare: createCloudflareDeploy(app),
1840
- worker: {
1841
- compatibilityDate: expectedCloudflareCompatibilityDate,
1842
- name: expectedWorkerName(app.packageSuffix),
1843
- security: expectedCloudflareSecurity,
1844
- ssr: true,
1845
- },
1846
- },
1847
- ssr: app.moduleFederationSsr
1848
- ? {
1849
- mode: 'stream',
1850
- moduleFederationAppSSR: true,
1851
- }
1852
- : undefined,
1853
- i18n: {
1854
- languages: ['en', 'cs'],
1855
- namespace: appNamespace(app),
1856
- localisedUrls: createLocalisedUrls(app),
1857
- },
1858
- routes: {
1859
- source: 'route-owned',
1860
- metadataAuthoring: 'colocated-route-meta',
1861
- generatedManifest: true,
1862
- metadataExport: './src/routes/ultramodern-route-metadata',
1863
- localisedUrls: createLocalisedUrls(app),
1864
- owned: createRouteOwnedEntries(app),
1865
- publicRoutes: createPublicRoutes(app),
1866
- privateByDefault: true,
1867
- publicnessDefault: 'private-app-screen',
1868
- publicHead: createPublicHead(),
1869
- publicSurface: createPublicSurface(app),
1870
- },
1871
- moduleFederation: {
1872
- name: app.mfName,
1873
- ...(app.verticalRefs?.length
1874
- ? {
1875
- verticalRefs: app.verticalRefs,
1876
- remotes: remoteContractsFor(app, apps),
1877
- }
1878
- : {}),
1879
- exposes: app.exposes,
1880
- dts: {
1881
- compilerInstance: 'effect-tsgo',
1882
- displayErrorInTerminal: true,
1883
- tsConfigPath: './tsconfig.mf-types.json',
1884
- },
1885
- browserSafeExposesOnly: true,
1886
- },
1887
- marker: {
1888
- appId: app.id,
1889
- packageName: app.package ?? packageNameFor(packageScope, app.packageSuffix),
1890
- version: '0.1.0',
1891
- build: app.deliveryUnit?.buildMarker ?? buildMarkerFor(app),
1892
- deployProfile: 'cloudflare-ssr-mf-effect-v1',
1893
- uiSurface: 'ui',
1894
- ...(app.api ? { apiSurface: 'api' } : {}),
1895
- },
1896
- ...(app.api
1897
- ? {
1898
- // An `rpc` unit exposes the Effect RpcGroup contract/client and the
1899
- // `/rpc` route instead of the REST `shared/api.ts` + `./api/client`
1900
- // surface, so it carries no OpenAPI/readiness/domain-operation metadata.
1901
- api:
1902
- (app.api.protocol ?? 'rest') === 'rpc'
1903
- ? {
1904
- runtime: 'effect',
1905
- import: '@modern-js/plugin-bff/effect-edge',
1906
- prefix: app.api.prefix,
1907
- protocol: 'rpc',
1908
- strictEffectApproach: true,
1909
- workerEntry: 'worker/__modern_bff_effect.js',
1910
- contract: './api',
1911
- client: './api/rpc-client',
1912
- group: toCamelCase(app.api.stem),
1913
- rpc: { path: '/rpc', serialization: 'json' },
1914
- rpcPath: `${app.api.prefix}/rpc`,
1915
- }
1916
- : {
1917
- runtime: 'effect',
1918
- import: '@modern-js/plugin-bff/effect-edge',
1919
- prefix: app.api.prefix,
1920
- openapi: '/openapi.json',
1921
- strictEffectApproach: true,
1922
- workerEntry: 'worker/__modern_bff_effect.js',
1923
- contract: './api',
1924
- client: './api/client',
1925
- readiness: createEffectReadiness(app),
1926
- requestContext: createEffectRequestContext(),
1927
- domainOperations: createEffectDomainOperations(app),
1928
- ...createEffectOperationContract(app),
1929
- },
1930
- }
1931
- : {}),
1932
- });
1933
- const createCssFederationContract = () => ({
1934
- sharedDesignTokens: {
1935
- owner: {
1936
- id: 'shared-design-tokens',
1937
- package: packageNameFor(packageScope, 'shared-design-tokens'),
1938
- },
1939
- role: 'shared-design-tokens',
1940
- rootSelector: ':root',
1941
- classPrefix: '--um-',
1942
- layers: {
1943
- owned: ['ultramodern-shared-tokens'],
1944
- },
1945
- entrypoints: {
1946
- css: ['packages/shared-design-tokens/src/tokens.css'],
1947
- },
1948
- assets: {
1949
- exports: ['./tokens.css'],
1950
- },
1951
- dedupe: cssDedupe(),
1952
- ssr: {
1953
- firstPaintRequired: true,
1954
- },
1955
- },
1956
- });
1957
- const createPerformanceReadinessContract = () => ({
1958
- default: 'enabled',
1959
- mode: 'diagnostic',
1960
- scope: 'ultramodern-generated-and-framework-owned',
1961
- report: {
1962
- script: 'scripts/ultramodern-performance-readiness.mts',
1963
- config: 'scripts/ultramodern-performance-readiness.config.mjs',
1964
- deterministic: true,
1965
- },
1966
- optOut: {
1967
- env: 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false',
1968
- },
1969
- signals: [
1970
- 'bfcache',
1971
- 'core-web-vitals-rum',
1972
- 'duplicate-prefetch-warmup',
1973
- 'cache-policy-sanity',
1974
- 'save-data-behavior',
1975
- 'cloudflare-ssr-cache-hints',
1976
- ].map(id => ({ id })),
1977
- });
1978
- const createModernPackageAliases = packageSourceConfig => {
1979
- if (typeof packageSourceConfig?.aliasScope !== 'string') {
1980
- return undefined;
1981
- }
1982
- const scope = packageSourceConfig.aliasScope.replace(/^@/u, '');
1983
- const prefix =
1984
- typeof packageSourceConfig.aliasPackageNamePrefix === 'string'
1985
- ? packageSourceConfig.aliasPackageNamePrefix
1986
- : '';
1987
- return Object.fromEntries(
1988
- modernPackageCohort.map(packageName => [
1989
- packageName,
1990
- `@${scope}/${prefix}${packageName.split('/').at(-1)}`,
1991
- ]),
1992
- );
1993
- };
1994
- const createPackageSourceView = config => {
1995
- const source = config.packageSource;
1996
- assert(
1997
- source !== null && typeof source === 'object' && !Array.isArray(source),
1998
- `${compactConfigPath} packageSource must be a JSON object`,
1999
- );
2000
- assert(
2001
- source.strategy === 'workspace' || source.strategy === 'install',
2002
- `${compactConfigPath} packageSource.strategy must be workspace or install`,
2003
- );
2004
- if (source.strategy === 'install') {
2005
- assert(
2006
- typeof source.modernPackageVersion === 'string' &&
2007
- source.modernPackageVersion.length > 0,
2008
- `${compactConfigPath} install package source must declare modernPackageVersion`,
2009
- );
2010
- }
2011
- const strategy = source.strategy;
2012
- const specifier =
2013
- strategy === 'install' ? source.modernPackageVersion : 'workspace:*';
2014
- const aliases = createModernPackageAliases(source);
2015
- return {
2016
- schemaVersion: 1,
2017
- strategy,
2018
- modernPackages: {
2019
- packages: modernPackageCohort,
2020
- specifier,
2021
- ...(typeof source.registry === 'string'
2022
- ? { registry: source.registry }
2023
- : {}),
2024
- ...(aliases ? { aliases } : {}),
2025
- },
2026
- generatedWorkspacePackages: {
2027
- packages: [
2028
- packageNameFor(packageScope, 'shared-contracts'),
2029
- packageNameFor(packageScope, 'shared-design-tokens'),
2030
- ],
2031
- specifier: 'workspace:*',
2032
- },
2033
- };
2034
- };
2035
- const synthesizeGeneratedContractFromCompact = config => {
2036
- const apps = compactAppsFromConfig(config);
2037
- return {
2038
- schemaVersion: 1,
2039
- profile: config.profile ?? 'cloudflare-ssr-mf-effect-v1',
2040
- node: {
2041
- version: config.workspace?.node?.version ?? expectedNodeVersion,
2042
- engineRange: '>=26',
2043
- },
2044
- performanceReadiness: createPerformanceReadinessContract(),
2045
- cssFederation: createCssFederationContract(),
2046
- apps: apps.map(app => createAppContract(app, apps)),
2047
- };
2048
- };
2049
- const readGeneratedContractView = config => {
2050
- return synthesizeGeneratedContractFromCompact(config);
2051
- };
2052
- const expectedManifestUrl = vertical =>
2053
- `http://localhost:${vertical.port}/mf-manifest.json`;
2054
- const expectedApiUrl = vertical =>
2055
- `http://localhost:${vertical.port}${vertical.apiPrefix}${
2056
- vertical.apiProtocol === 'rpc' ? '/rpc' : ''
2057
- }`;
2058
- const expectedBackendFederationName = vertical => `${vertical.mfName}Backend`;
2059
- const expectedBackendManifestUrl = vertical =>
2060
- `http://localhost:${vertical.port}/backend-mf-manifest.json`;
2061
- const expectedBackendContainerEntry = vertical =>
2062
- `http://localhost:${vertical.port}/backendRemoteEntry.cjs`;
2063
- const expectedBackendManifestEnv = vertical =>
2064
- `VERTICAL_${toEnvSegment(vertical.domain ?? vertical.id)}_BACKEND_MF_MANIFEST`;
2065
- const expectedPublicUrlEnv = vertical =>
2066
- `ULTRAMODERN_PUBLIC_URL_${toEnvSegment(vertical.id)}`;
2067
- const expectedCloudflareWorkerName = vertical =>
2068
- toKebabCase(`${packageScope}-${vertical.packageSuffix ?? vertical.id}`).slice(
2069
- 0,
2070
- 63,
2071
- );
2072
- const backendFederationSubset = backendFederation => ({
2073
- role: backendFederation?.role,
2074
- name: backendFederation?.name,
2075
- runtimeFramework: backendFederation?.runtimeFramework,
2076
- strictEffectApproach: backendFederation?.strictEffectApproach,
2077
- exposeRuntime: backendFederation?.exposes?.['./effect-api']?.runtime,
2078
- exposeReadiness: backendFederation?.exposes?.['./effect-api']?.readiness,
2079
- versionBoundary: {
2080
- invariant: backendFederation?.versionBoundary?.invariant,
2081
- hasUi: Object.hasOwn(backendFederation?.versionBoundary ?? {}, 'ui'),
2082
- uiManifestUrl: backendFederation?.versionBoundary?.ui?.manifestUrl,
2083
- apiReadiness: backendFederation?.versionBoundary?.api?.readiness,
2084
- },
2085
- cloudflare: {
2086
- kind: backendFederation?.executionSurfaces?.cloudflare?.kind,
2087
- hasApi: Object.hasOwn(
2088
- backendFederation?.executionSurfaces?.cloudflare ?? {},
2089
- 'api',
2090
- ),
2091
- hasSsr: Object.hasOwn(
2092
- backendFederation?.executionSurfaces?.cloudflare ?? {},
2093
- 'ssr',
2094
- ),
2095
- workerName: backendFederation?.executionSurfaces?.cloudflare?.workerName,
2096
- publicUrlEnv:
2097
- backendFederation?.executionSurfaces?.cloudflare?.publicUrlEnv,
2098
- zephyrRuntime:
2099
- backendFederation?.executionSurfaces?.cloudflare?.zephyr?.runtime,
2100
- },
2101
- node: {
2102
- kind: backendFederation?.executionSurfaces?.node?.kind,
2103
- remoteName: backendFederation?.executionSurfaces?.node?.remoteName,
2104
- manifestEnv: backendFederation?.executionSurfaces?.node?.manifestEnv,
2105
- manifestUrl: backendFederation?.executionSurfaces?.node?.manifestUrl,
2106
- containerEntry:
2107
- backendFederation?.executionSurfaces?.node?.containerEntry,
2108
- remoteType: backendFederation?.executionSurfaces?.node?.remoteType,
2109
- expose: backendFederation?.executionSurfaces?.node?.expose,
2110
- },
2111
- compatibility: {
2112
- contractVersion: backendFederation?.compatibility?.contractVersion,
2113
- },
2114
- topLevelManifestUrl: backendFederation?.manifestUrl,
2115
- topLevelContainerEntry: backendFederation?.containerEntry,
2116
- });
2117
- const expectedBackendFederationSubset = vertical => ({
2118
- role: 'microvertical-server',
2119
- name: expectedBackendFederationName(vertical),
2120
- runtimeFramework: 'effect',
2121
- strictEffectApproach: true,
2122
- exposeRuntime: `${vertical.path}/api/index.ts`,
2123
- // The RPC surface exposes no REST readiness endpoint, so its backend
2124
- // federation contract omits the readiness probes (G7a).
2125
- exposeReadiness:
2126
- vertical.apiProtocol === 'rpc'
2127
- ? undefined
2128
- : `${vertical.apiPrefix}/${vertical.stem}/readiness`,
2129
- versionBoundary: {
2130
- invariant: 'web-and-api-same-build',
2131
- hasUi: vertical.emitsUi,
2132
- uiManifestUrl: vertical.emitsUi
2133
- ? expectedManifestUrl(vertical)
2134
- : undefined,
2135
- apiReadiness:
2136
- vertical.apiProtocol === 'rpc'
2137
- ? undefined
2138
- : `${vertical.apiPrefix}/${vertical.stem}/readiness`,
2139
- },
2140
- cloudflare: {
2141
- kind: 'cloudflare-worker-snapshot',
2142
- hasApi: !vertical.emitsUi,
2143
- hasSsr: vertical.emitsUi,
2144
- workerName: expectedCloudflareWorkerName(vertical),
2145
- publicUrlEnv: expectedPublicUrlEnv(vertical),
2146
- zephyrRuntime: vertical.emitsUi ? 'ssr-worker' : 'api-worker',
2147
- },
2148
- node: {
2149
- kind: 'node-mf-runtime',
2150
- remoteName: expectedBackendFederationName(vertical),
2151
- manifestEnv: expectedBackendManifestEnv(vertical),
2152
- manifestUrl: expectedBackendManifestUrl(vertical),
2153
- containerEntry: expectedBackendContainerEntry(vertical),
2154
- remoteType: 'commonjs-module',
2155
- expose: './effect-api',
2156
- },
2157
- compatibility: {
2158
- contractVersion: 'microvertical-server-effect-v1',
2159
- },
2160
- topLevelManifestUrl: undefined,
2161
- topLevelContainerEntry: undefined,
2162
- });
2163
- const serverExecutionSubset = serverExecution => ({
2164
- apiBaseUrl: serverExecution?.apiBaseUrl,
2165
- versionBoundary: serverExecution?.versionBoundary,
2166
- cloudflareKind: serverExecution?.cloudflare?.kind,
2167
- cloudflareWorkerName: serverExecution?.cloudflare?.workerName,
2168
- nodeKind: serverExecution?.node?.kind,
2169
- nodeManifestUrl: serverExecution?.node?.manifestUrl,
2170
- nodeContainerEntry: serverExecution?.node?.containerEntry,
2171
- });
2172
- const expectedServerExecutionSubset = vertical => ({
2173
- apiBaseUrl: expectedApiUrl(vertical),
2174
- versionBoundary: 'web-and-api-same-build',
2175
- cloudflareKind: 'cloudflare-worker-snapshot',
2176
- cloudflareWorkerName: expectedCloudflareWorkerName(vertical),
2177
- nodeKind: 'node-mf-runtime',
2178
- nodeManifestUrl: expectedBackendManifestUrl(vertical),
2179
- nodeContainerEntry: expectedBackendContainerEntry(vertical),
2180
- });
2181
- const remoteContractSubset = remote => ({
2182
- id: remote?.id,
2183
- name: remote?.name,
2184
- manifestUrl: remote?.manifestUrl,
2185
- });
2186
- const expectedRemoteContractSubset = vertical => ({
2187
- id: vertical.id,
2188
- name: vertical.mfName,
2189
- manifestUrl: expectedManifestUrl(vertical),
2190
- });
2191
- const expectedRemoteSubsetsForRefs = refs =>
2192
- refs
2193
- .map(ref => fullStackVerticals.find(vertical => vertical.id === ref))
2194
- .filter(Boolean)
2195
- .map(expectedRemoteContractSubset);
2196
- const requiredMicroVerticalPaths = vertical => [
2197
- `${vertical.path}/package.json`,
2198
- `${vertical.path}/tsconfig.json`,
2199
- `${vertical.path}/tsconfig.mf-types.json`,
2200
- `${vertical.path}/modern.config.ts`,
2201
- `${vertical.path}/src/modern-app-env.d.ts`,
2202
- `${vertical.path}/src/modern.runtime.ts`,
2203
- `${vertical.path}/locales/en/translation.json`,
2204
- `${vertical.path}/locales/en/${vertical.namespace}.json`,
2205
- `${vertical.path}/locales/cs/translation.json`,
2206
- `${vertical.path}/locales/cs/${vertical.namespace}.json`,
2207
- ...(vertical.emitsUi
2208
- ? [
2209
- `${vertical.path}/module-federation.config.ts`,
2210
- `${vertical.path}/src/federation-entry.tsx`,
2211
- ...vertical.componentPaths,
2212
- `${vertical.path}/src/routes/index.css`,
2213
- `${vertical.path}/src/routes/layout.tsx`,
2214
- `${vertical.path}/src/routes/ultramodern-route-head.tsx`,
2215
- `${vertical.path}/src/routes/ultramodern-route-metadata.ts`,
2216
- `${vertical.path}/src/routes/[lang]/page.tsx`,
2217
- ...(vertical.exposes.includes('./Widget')
2218
- ? [
2219
- `${vertical.path}/src/routes/[lang]/_mf/fragment/widget/page.tsx`,
2220
- ]
2221
- : []),
2222
- ...vertical.routePagePaths,
2223
- ...vertical.routeMetaPaths,
2224
- ]
2225
- : []),
2226
- ...(vertical.emitsApi
2227
- ? [
2228
- `${vertical.path}/backend-federation.config.ts`,
2229
- `${vertical.path}/api/effect-api.ts`,
2230
- `${vertical.path}/api/index.ts`,
2231
- `${vertical.path}/${vertical.apiContractPath}`,
2232
- `${vertical.path}/${vertical.apiClientPath}`,
2233
- ]
2234
- : []),
2235
- ];
2236
- // UI/MF artifacts an `api-only` unit must NOT emit (headless invariant), and
2237
- // API/BFF artifacts a `ui-only`/Horizontal Remote unit must NOT emit.
2238
- const forbiddenMicroVerticalPaths = vertical => [
2239
- ...(vertical.emitsUi
2240
- ? []
2241
- : [
2242
- `${vertical.path}/module-federation.config.ts`,
2243
- `${vertical.path}/src/federation-entry.tsx`,
2244
- // The federated demo `./Widget` component (descriptors.ts:127 ->
2245
- // remoteComponentOutputPath) is a UI-only artifact; a headless api-only
2246
- // unit exposes no browser component and must not ship it.
2247
- `${vertical.path}/src/components/${vertical.domain ?? vertical.id}-widget.tsx`,
2248
- `${vertical.path}/src/routes/layout.tsx`,
2249
- `${vertical.path}/src/routes/[lang]/page.tsx`,
2250
- `${vertical.path}/src/routes/[lang]/_mf/fragment/widget/page.tsx`,
2251
- // A headless api-only unit renders no browser surface, so it must not
2252
- // ship route components, the colocated route-metadata/head modules, or
2253
- // the colocated `[lang]/route.meta.ts` route-meta file.
2254
- `${vertical.path}/src/routes/[lang]/route.meta.ts`,
2255
- `${vertical.path}/src/routes/ultramodern-route-head.tsx`,
2256
- `${vertical.path}/src/routes/ultramodern-route-metadata.ts`,
2257
- `${vertical.path}/src/routes/index.css`,
2258
- ]),
2259
- ...(vertical.emitsApi
2260
- ? []
2261
- : [
2262
- `${vertical.path}/shared/api.ts`,
2263
- // A ui-only/Horizontal Remote unit carries no API contract in either
2264
- // protocol (neither the REST `shared/api.ts` nor the RPC `shared/rpc.ts`)
2265
- // and no generated API client.
2266
- `${vertical.path}/shared/rpc.ts`,
2267
- `${vertical.path}/backend-federation.config.ts`,
2268
- `${vertical.path}/api/index.ts`,
2269
- `${vertical.path}/api/effect-api.ts`,
2270
- `${vertical.path}/src/api/${vertical.domain ?? vertical.id}-client.ts`,
2271
- `${vertical.path}/src/api/${vertical.domain ?? vertical.id}-rpc-client.ts`,
2272
- ]),
2273
- ];
2274
- const assertRequiredVerticalFile = vertical => relativePath => {
2275
- assertSelfCheck(
2276
- fs.existsSync(path.join(root, relativePath)),
2277
- `required files for ${vertical.id}`,
2278
- `Missing ${relativePath}`,
2279
- 'restore the generated MicroVertical files or rerun the MicroVertical generator',
2280
- );
2281
- };
2282
- const assertForbiddenVerticalFile = vertical => relativePath => {
2283
- assertSelfCheck(
2284
- !fs.existsSync(path.join(root, relativePath)),
2285
- `forbidden files for ${vertical.id}`,
2286
- `Unexpected ${relativePath} for a ${vertical.surfaceProfile} unit`,
2287
- `remove ${relativePath}; a ${vertical.surfaceProfile} unit does not emit this surface`,
2288
- );
2289
- };
2290
- const assertMicroVerticalContractGraph = ({
2291
- generatedContract,
2292
- topology,
2293
- ownership,
2294
- overlay,
2295
- shellPackage,
2296
- }) => {
2297
- const expectedVerticalIds = fullStackVerticals.map(vertical => vertical.id);
2298
- const expectedAppIds = ['shell-super-app', ...expectedVerticalIds];
2299
- const expectedShellVerticalIds = expectedPrimaryShellVerticalIds;
2300
- const expectedShellRemotes = expectedShellVerticalIds.flatMap(
2301
- verticalId => {
2302
- const vertical = fullStackVerticals.find(
2303
- candidate => candidate.id === verticalId,
2304
- );
2305
- return vertical === undefined
2306
- ? []
2307
- : [expectedRemoteContractSubset(vertical)];
2308
- },
2309
- );
2310
-
2311
- assertObject(
2312
- topology.shell,
2313
- 'topology/reference-topology.json shell',
2314
- 'restore generated topology shell metadata',
2315
- );
2316
- assertArray(
2317
- topology.verticals,
2318
- 'topology/reference-topology.json verticals',
2319
- 'restore generated topology vertical entries',
2320
- );
2321
- assertObject(
2322
- topology.shell?.moduleFederation,
2323
- 'topology/reference-topology.json shell.moduleFederation',
2324
- 'restore generated shell Module Federation metadata',
2325
- );
2326
- assertArray(
2327
- topology.shell?.moduleFederation?.remotes,
2328
- 'topology/reference-topology.json shell.moduleFederation.remotes',
2329
- 'restore generated shell Module Federation remotes',
2330
- );
2331
- assertArray(
2332
- ownership.owners,
2333
- 'topology/ownership.json owners',
2334
- 'restore generated ownership entries',
2335
- );
2336
- assertObject(
2337
- overlay.ports,
2338
- 'topology/local-overlays/development.json ports',
2339
- 'restore generated local development port overlays',
2340
- );
2341
- assertObject(
2342
- overlay.manifests,
2343
- 'topology/local-overlays/development.json manifests',
2344
- 'restore generated local Module Federation manifest overlays',
2345
- );
2346
- assertObject(
2347
- overlay.apis,
2348
- 'topology/local-overlays/development.json apis',
2349
- 'restore generated local API overlays',
2350
- );
2351
- assertArray(
2352
- generatedContract.apps,
2353
- `${generatedContractLabel} apps`,
2354
- 'regenerate the generated contract from the workspace topology',
2355
- );
2356
-
2357
- assertSameJson(
2358
- topology.shell.verticalRefs ?? [],
2359
- expectedShellVerticalIds,
2360
- 'topology/reference-topology.json shell.verticalRefs',
2361
- 'restore generated topology shell references',
2362
- );
2363
- assertSameJson(
2364
- topology.verticals.map(vertical => vertical?.id),
2365
- expectedVerticalIds,
2366
- 'topology/reference-topology.json verticals',
2367
- 'restore generated topology vertical entries',
2368
- );
2369
- assertSameJson(
2370
- topology.shell.moduleFederation.remotes.map(remoteContractSubset),
2371
- expectedShellRemotes,
2372
- 'topology/reference-topology.json shell.moduleFederation.remotes',
2373
- 'restore generated shell Module Federation remotes',
2374
- );
2375
- assertSameJson(
2376
- generatedContract.apps.map(app => app?.id),
2377
- expectedAppIds,
2378
- `${generatedContractLabel} apps`,
2379
- 'regenerate the generated contract after topology changes',
2380
- );
2381
-
2382
- const shellContract = findById(generatedContract.apps, 'shell-super-app');
2383
- assertObject(
2384
- shellContract,
2385
- `${generatedContractLabel} apps.shell-super-app`,
2386
- 'regenerate the generated shell contract entry',
2387
- );
2388
- assertSameJson(
2389
- shellContract.moduleFederation?.verticalRefs ?? [],
2390
- expectedShellVerticalIds,
2391
- `${generatedContractLabel} shell moduleFederation.verticalRefs`,
2392
- 'regenerate the generated shell Module Federation contract',
2393
- );
2394
- assertSameJson(
2395
- (shellContract.moduleFederation?.remotes ?? []).map(remoteContractSubset),
2396
- expectedShellRemotes,
2397
- `${generatedContractLabel} shell moduleFederation.remotes`,
2398
- 'regenerate the generated shell Module Federation contract',
2399
- );
2400
- assertSameJson(
2401
- shellContract.ssr,
2402
- {
2403
- mode: 'stream',
2404
- moduleFederationAppSSR: true,
2405
- },
2406
- `${generatedContractLabel} shell SSR contract`,
2407
- 'restore generated streaming SSR Module Federation settings',
2408
- );
2409
-
2410
- for (const vertical of fullStackVerticals) {
2411
- const expectedRefs = vertical.verticalRefs ?? [];
2412
- const topologyEntry = findById(topology.verticals, vertical.id);
2413
- const ownershipEntry = findById(ownership.owners, vertical.id);
2414
- const contractEntry = findById(generatedContract.apps, vertical.id);
2415
- const expectedExposes = Array.isArray(vertical.exposes)
2416
- ? vertical.exposes
2417
- : Object.keys(vertical.exposes ?? {});
2418
-
2419
- requiredMicroVerticalPaths(vertical).forEach(
2420
- assertRequiredVerticalFile(vertical),
2421
- );
2422
-
2423
- assertObject(
2424
- topologyEntry,
2425
- `topology/reference-topology.json verticals.${vertical.id}`,
2426
- 'restore generated topology vertical entries',
2427
- );
2428
- assertSameJson(
2429
- {
2430
- kind: topologyEntry.kind,
2431
- package: topologyEntry.package,
2432
- path: topologyEntry.path,
2433
- moduleFederation: {
2434
- name: topologyEntry.moduleFederation?.name,
2435
- manifestUrl: topologyEntry.moduleFederation?.manifestUrl,
2436
- exposes: topologyEntry.moduleFederation?.exposes ?? [],
2437
- verticalRefs: topologyEntry.moduleFederation?.verticalRefs ?? [],
2438
- remotes: (topologyEntry.moduleFederation?.remotes ?? []).map(
2439
- remoteContractSubset,
2440
- ),
2441
- },
2442
- ...(vertical.emitsApi
2443
- ? {
2444
- api: {
2445
- prefix: topologyEntry.api?.bff?.prefix,
2446
- serverEntry: topologyEntry.api?.serverEntry,
2447
- },
2448
- }
2449
- : {}),
2450
- },
2451
- {
2452
- kind: 'vertical',
2453
- package: vertical.packageName,
2454
- path: vertical.path,
2455
- moduleFederation: {
2456
- name: vertical.mfName,
2457
- manifestUrl: expectedManifestUrl(vertical),
2458
- exposes: expectedExposes,
2459
- verticalRefs: expectedRefs,
2460
- remotes: expectedRemoteSubsetsForRefs(expectedRefs),
2461
- },
2462
- ...(vertical.emitsApi
2463
- ? {
2464
- api: {
2465
- prefix: vertical.apiPrefix,
2466
- serverEntry: `${vertical.path}/api/index.ts`,
2467
- },
2468
- }
2469
- : {}),
2470
- },
2471
- `topology/reference-topology.json verticals.${vertical.id}`,
2472
- 'restore generated topology vertical entries',
2473
- );
2474
- if (vertical.emitsApi) {
2475
- assertSameJson(
2476
- backendFederationSubset(topologyEntry.backendFederation),
2477
- expectedBackendFederationSubset(vertical),
2478
- `topology/reference-topology.json verticals.${vertical.id}.backendFederation`,
2479
- 'restore generated MicroVertical server execution contract',
2480
- );
2481
- }
2482
-
2483
- if (vertical.deliveryUnit) {
2484
- const compactApp = findById(
2485
- ultramodernConfig.topology?.apps,
2486
- vertical.id,
2487
- );
2488
- const expectedDeliveryUnit = deliveryUnitBlock(
2489
- expectedDeliveryUnitFor(vertical),
2490
- );
2491
- assertSameJson(
2492
- deliveryUnitBlock(compactApp?.deliveryUnit),
2493
- expectedDeliveryUnit,
2494
- `${generatedContractLabel} topology.apps.${vertical.id}.deliveryUnit`,
2495
- deliveryUnitIdentityFixArea,
2496
- );
2497
- if (vertical.emitsApi) {
2498
- assertSameJson(
2499
- deliveryUnitBlock(compactApp?.backendFederation?.deliveryUnit),
2500
- expectedDeliveryUnit,
2501
- `${generatedContractLabel} topology.apps.${vertical.id}.backendFederation.deliveryUnit`,
2502
- deliveryUnitIdentityFixArea,
2503
- );
2504
- }
2505
- assertSameJson(
2506
- deliveryUnitBlock(topologyEntry.deliveryUnit),
2507
- expectedDeliveryUnit,
2508
- `topology/reference-topology.json verticals.${vertical.id}.deliveryUnit`,
2509
- deliveryUnitIdentityFixArea,
2510
- );
2511
- if (vertical.emitsApi) {
2512
- assertSameJson(
2513
- deliveryUnitBlock(topologyEntry.backendFederation?.deliveryUnit),
2514
- expectedDeliveryUnit,
2515
- `topology/reference-topology.json verticals.${vertical.id}.backendFederation.deliveryUnit`,
2516
- deliveryUnitIdentityFixArea,
2517
- );
2518
- assertSelfCheck(
2519
- topologyEntry.backendFederation?.versionBoundary?.identityRoot === 'deliveryUnit',
2520
- `topology/reference-topology.json verticals.${vertical.id}.backendFederation.versionBoundary.identityRoot`,
2521
- `Expected "deliveryUnit", found ${formatJson(topologyEntry.backendFederation?.versionBoundary?.identityRoot)}`,
2522
- deliveryUnitIdentityFixArea,
2523
- );
2524
- }
2525
- }
2526
-
2527
- assertObject(
2528
- ownershipEntry,
2529
- `topology/ownership.json owners.${vertical.id}`,
2530
- 'restore generated ownership entries',
2531
- );
2532
- assertSameJson(
2533
- {
2534
- package: ownershipEntry.package,
2535
- path: ownershipEntry.path,
2536
- },
2537
- {
2538
- package: vertical.packageName,
2539
- path: vertical.path,
2540
- },
2541
- `topology/ownership.json owners.${vertical.id}`,
2542
- 'restore generated ownership entries',
2543
- );
2544
-
2545
- assertSameJson(
2546
- overlay.ports[vertical.id],
2547
- vertical.port,
2548
- `topology/local-overlays/development.json ports.${vertical.id}`,
2549
- 'restore generated local development port overlay',
2550
- );
2551
- if (vertical.emitsUi) {
2552
- assertSameJson(
2553
- overlay.manifests[vertical.id],
2554
- expectedManifestUrl(vertical),
2555
- `topology/local-overlays/development.json manifests.${vertical.id}`,
2556
- 'restore generated local Module Federation manifest overlay',
2557
- );
2558
- }
2559
- if (vertical.emitsApi) {
2560
- assertSameJson(
2561
- overlay.apis[vertical.id],
2562
- expectedApiUrl(vertical),
2563
- `topology/local-overlays/development.json apis.${vertical.id}`,
2564
- 'restore generated local API overlay',
2565
- );
2566
- assertSameJson(
2567
- serverExecutionSubset(overlay.serverExecution?.[vertical.id]),
2568
- expectedServerExecutionSubset(vertical),
2569
- `topology/local-overlays/development.json serverExecution.${vertical.id}`,
2570
- 'restore generated local MicroVertical server execution overlay',
2571
- );
2572
- }
2573
-
2574
-
2575
- // Plain workspace dependencies and Zephyr metadata are gated
2576
- // INDEPENDENTLY (G2a/G28): every shell depends on each API-emitting
2577
- // vertical (its vertical-clients.ts re-exports the client) regardless of
2578
- // UI composition, while Zephyr metadata follows UI composition refs only.
2579
- const shellPackagesForDependencyChecks = [
2580
- {
2581
- id: 'shell-super-app',
2582
- path: 'apps/shell-super-app',
2583
- pkg: shellPackage,
2584
- uiRefs: expectedShellVerticalIds,
2585
- },
2586
- ...expectedAdditionalShells.map(additionalShell => ({
2587
- id: additionalShell.id,
2588
- path: additionalShell.path,
2589
- pkg: readJson(`${additionalShell.path}/package.json`),
2590
- uiRefs: additionalShell.verticalRefs ?? [],
2591
- })),
2592
- ];
2593
- for (const shellEntry of shellPackagesForDependencyChecks) {
2594
- const composed = shellEntry.uiRefs.includes(vertical.id);
2595
- if (vertical.emitsApi || composed) {
2596
- assertSameJson(
2597
- shellEntry.pkg.dependencies?.[vertical.packageName],
2598
- 'workspace:*',
2599
- `${shellEntry.path}/package.json dependencies.${vertical.packageName}`,
2600
- 'restore shell dependency for the MicroVertical consumer',
2601
- );
2602
- }
2603
- if (composed) {
2604
- assertSameJson(
2605
- shellEntry.pkg['zephyr:dependencies']?.[vertical.zephyrAlias],
2606
- `${vertical.packageName}@workspace:*`,
2607
- `${shellEntry.path}/package.json zephyr:dependencies.${vertical.zephyrAlias}`,
2608
- 'restore shell Zephyr dependency metadata for the MicroVertical',
2609
- );
2610
- }
2611
- }
2612
-
2613
- assertObject(
2614
- contractEntry,
2615
- `${generatedContractLabel} apps.${vertical.id}`,
2616
- 'regenerate the generated MicroVertical contract entry',
2617
- );
2618
- assertSameJson(
2619
- {
2620
- kind: contractEntry.kind,
2621
- package: contractEntry.package,
2622
- path: contractEntry.path,
2623
- moduleFederation: {
2624
- name: contractEntry.moduleFederation?.name,
2625
- exposes: contractEntry.moduleFederation?.exposes ?? [],
2626
- verticalRefs: contractEntry.moduleFederation?.verticalRefs ?? [],
2627
- remotes: (contractEntry.moduleFederation?.remotes ?? []).map(
2628
- remoteContractSubset,
2629
- ),
2630
- },
2631
- ...(vertical.emitsApi
2632
- ? {
2633
- api: {
2634
- prefix: contractEntry.api?.prefix,
2635
- contract: contractEntry.api?.contract,
2636
- client: contractEntry.api?.client,
2637
- },
2638
- }
2639
- : {}),
2640
- ssr: contractEntry.ssr,
2641
- },
2642
- {
2643
- kind: 'vertical',
2644
- package: vertical.packageName,
2645
- path: vertical.path,
2646
- moduleFederation: {
2647
- name: vertical.mfName,
2648
- exposes: expectedExposes,
2649
- verticalRefs: expectedRefs,
2650
- remotes: expectedRemoteSubsetsForRefs(expectedRefs),
2651
- },
2652
- ...(vertical.emitsApi
2653
- ? {
2654
- api: {
2655
- prefix: vertical.apiPrefix,
2656
- // Protocol-aware exports: `rest` -> `./api`/`./api/client`,
2657
- // `rpc` -> `./api`/`./api/rpc-client`.
2658
- contract: vertical.apiContractExport,
2659
- client: vertical.apiClientExport,
2660
- },
2661
- }
2662
- : {}),
2663
- ssr: {
2664
- mode: 'stream',
2665
- moduleFederationAppSSR: true,
2666
- },
2667
- },
2668
- `${generatedContractLabel} apps.${vertical.id}`,
2669
- 'regenerate the generated MicroVertical contract entry',
2670
- );
2671
- }
2672
- };
2673
- const toPosixPath = value => value.split(path.sep).join('/');
2674
- const referenceFrom = (fromPath, toPath) => ({
2675
- path: toPosixPath(path.relative(fromPath, toPath)),
2676
- });
2677
- const sharedPackagePaths = [
2678
- 'packages/shared-contracts',
2679
- 'packages/shared-design-tokens',
2680
- ];
2681
- const tsgoCacheKey = packagePath => packagePath.replace(/[^a-zA-Z0-9._-]+/gu, '__');
2682
- const assertProjectReferenceEmitConfig = (tsConfig, packagePath) => {
2683
- const compilerOptions = tsConfig.compilerOptions ?? {};
2684
- const relativeRoot = toPosixPath(path.relative(packagePath, '.')) || '.';
2685
- assert(compilerOptions.composite === true, `${packagePath} must stay a composite TS-Go project`);
2686
- assert(compilerOptions.declaration === true, `${packagePath} must emit declarations for TS-Go build mode`);
2687
- assert(compilerOptions.declarationMap === false, `${packagePath} must not emit declaration maps during checks`);
2688
- assert(compilerOptions.emitDeclarationOnly === true, `${packagePath} must only emit declarations during checks`);
2689
- assert(compilerOptions.noEmit === false, `${packagePath} must override root noEmit for TS-Go build mode`);
2690
- assert(
2691
- compilerOptions.outDir === `${relativeRoot}/node_modules/.cache/tsgo/declarations/${tsgoCacheKey(packagePath)}`,
2692
- `${packagePath} must emit TS-Go declarations into the generated cache`,
2693
- );
2694
- assert(
2695
- compilerOptions.tsBuildInfoFile === `${relativeRoot}/node_modules/.cache/tsgo/${tsgoCacheKey(packagePath)}.tsbuildinfo`,
2696
- `${packagePath} must keep TS-Go build info in the generated cache`,
2697
- );
2698
- };
2699
- const assertTsConfigReferenceGraph = () => {
2700
- const baseTsConfig = readJson('tsconfig.base.json');
2701
- const rootTsConfig = readJson('tsconfig.json');
2702
- const shellTsConfig = readJson('apps/shell-super-app/tsconfig.json');
2703
- const shellMfTypesTsConfig = readJson(
2704
- 'apps/shell-super-app/tsconfig.mf-types.json',
2705
- );
2706
- const additionalShellPaths = (
2707
- workspaceValidationContract.structuralShellPolicy?.shells ?? []
2708
- )
2709
- .filter(shell => shell.id !== 'shell-super-app')
2710
- .map(shell => shell.packageDir);
2711
- const expectedRootReferences = [
2712
- ...sharedPackagePaths,
2713
- 'apps/shell-super-app',
2714
- ...fullStackVerticals.map(vertical => vertical.path),
2715
- ...additionalShellPaths,
2716
- ].map(referencePath => ({ path: referencePath }));
2717
- const expectedShellReferences = sharedPackagePaths.map(referencePath =>
2718
- referenceFrom('apps/shell-super-app', referencePath),
2719
- );
2720
-
2721
- assertSameJson(
2722
- rootTsConfig.files,
2723
- [],
2724
- 'tsconfig.json files',
2725
- 'restore the generated root project-reference tsconfig',
2726
- );
2727
- assertSameJson(
2728
- rootTsConfig.references ?? [],
2729
- expectedRootReferences,
2730
- 'tsconfig.json references',
2731
- 'restore the generated root project-reference graph',
2732
- );
2733
- assertSameJson(
2734
- shellTsConfig.references ?? [],
2735
- expectedShellReferences,
2736
- 'apps/shell-super-app/tsconfig.json references',
2737
- 'restore the generated shell project-reference graph',
2738
- );
2739
- assert(baseTsConfig.compilerOptions?.skipLibCheck !== true, 'tsconfig.base.json must not use skipLibCheck');
2740
- assertSameJson(
2741
- shellTsConfig.include ?? [],
2742
- ['src', 'locales/**/*.json', 'package.json', 'shared', 'server'],
2743
- 'apps/shell-super-app/tsconfig.json include',
2744
- 'restore the generated shell typecheck boundary',
2745
- );
2746
- assertProjectReferenceEmitConfig(
2747
- shellTsConfig,
2748
- 'apps/shell-super-app',
2749
- );
2750
- assertSameJson(
2751
- shellMfTypesTsConfig,
2752
- {
2753
- extends: '../../tsconfig.base.json',
2754
- include: ['src/modern-app-env.d.ts'],
2755
- compilerOptions: {
2756
- skipLibCheck: true,
2757
- },
2758
- },
2759
- 'apps/shell-super-app/tsconfig.mf-types.json',
2760
- 'restore the generated shell Module Federation DTS boundary',
2761
- );
2762
- for (const sharedPackagePath of sharedPackagePaths) {
2763
- assertProjectReferenceEmitConfig(
2764
- readJson(`${sharedPackagePath}/tsconfig.json`),
2765
- sharedPackagePath,
2766
- );
2767
- }
2768
-
2769
- for (const vertical of fullStackVerticals) {
2770
- const verticalTsConfig = readJson(`${vertical.path}/tsconfig.json`);
2771
- const verticalMfTypesTsConfig = readJson(
2772
- `${vertical.path}/tsconfig.mf-types.json`,
2773
- );
2774
- const expectedVerticalReferences = [
2775
- ...sharedPackagePaths,
2776
- ...(vertical.verticalRefs ?? [])
2777
- .map(verticalRef =>
2778
- fullStackVerticals.find(candidate => candidate.id === verticalRef),
2779
- )
2780
- .filter(Boolean)
2781
- .map(referencedVertical => referencedVertical.path),
2782
- ].map(referencePath => referenceFrom(vertical.path, referencePath));
2783
- assertSameJson(
2784
- verticalTsConfig.references ?? [],
2785
- expectedVerticalReferences,
2786
- `${vertical.path}/tsconfig.json references`,
2787
- 'restore the generated MicroVertical project-reference graph',
2788
- );
2789
- assertSameJson(
2790
- verticalTsConfig.include ?? [],
2791
- [
2792
- 'src',
2793
- 'locales/**/*.json',
2794
- 'package.json',
2795
- 'shared',
2796
- 'server',
2797
- ...(vertical.emitsApi ? ['api'] : []),
2798
- ],
2799
- `${vertical.path}/tsconfig.json include`,
2800
- 'restore the generated MicroVertical typecheck boundary',
2801
- );
2802
- assertProjectReferenceEmitConfig(verticalTsConfig, vertical.path);
2803
- assert(
2804
- (vertical.verticalRefs ?? []).length > 0
2805
- ? verticalTsConfig.compilerOptions?.skipLibCheck === true
2806
- : verticalTsConfig.compilerOptions?.skipLibCheck !== true,
2807
- `${vertical.path}/tsconfig.json must scope skipLibCheck to composed remotes`,
2808
- );
2809
- assertSameJson(
2810
- verticalMfTypesTsConfig,
2811
- {
2812
- extends: '../../tsconfig.base.json',
2813
- // A headless (api-only) unit exposes no Module Federation surface, so
2814
- // its DTS boundary only covers the ambient env declarations (G2a).
2815
- include: vertical.emitsUi
2816
- ? [
2817
- 'src/federation-entry.tsx',
2818
- ...vertical.componentPaths.map(componentPath =>
2819
- componentPath.replace(`${vertical.path}/`, ''),
2820
- ),
2821
- 'src/modern-app-env.d.ts',
2822
- ]
2823
- : ['src/modern-app-env.d.ts'],
2824
- compilerOptions: {
2825
- skipLibCheck: true,
2826
- },
2827
- },
2828
- `${vertical.path}/tsconfig.mf-types.json`,
2829
- 'restore the generated MicroVertical Module Federation DTS boundary',
2830
- );
2831
- }
2832
-
2833
- for (const shell of expectedAdditionalShells) {
2834
- const shellTsConfig = readJson(`${shell.path}/tsconfig.json`);
2835
- const shellMfTypesTsConfig = readJson(
2836
- `${shell.path}/tsconfig.mf-types.json`,
2837
- );
2838
- const expectedShellReferences = sharedPackagePaths.map(referencePath =>
2839
- referenceFrom(shell.path, referencePath),
2840
- );
2841
- assertSameJson(
2842
- shellTsConfig.references ?? [],
2843
- expectedShellReferences,
2844
- `${shell.path}/tsconfig.json references`,
2845
- 'restore the generated additional-shell project-reference graph',
2846
- );
2847
- assertSameJson(
2848
- shellTsConfig.include ?? [],
2849
- ['src', 'locales/**/*.json', 'package.json', 'shared', 'server'],
2850
- `${shell.path}/tsconfig.json include`,
2851
- 'restore the generated additional-shell typecheck boundary',
2852
- );
2853
- assertProjectReferenceEmitConfig(shellTsConfig, shell.path);
2854
- assertSameJson(
2855
- shellMfTypesTsConfig,
2856
- {
2857
- extends: '../../tsconfig.base.json',
2858
- include: ['src/modern-app-env.d.ts'],
2859
- compilerOptions: {
2860
- skipLibCheck: true,
2861
- },
2862
- },
2863
- `${shell.path}/tsconfig.mf-types.json`,
2864
- 'restore the generated additional-shell Module Federation DTS boundary',
2865
- );
2866
- }
2867
- };
2868
- const packageJsonFiles = startDir => {
2869
- const files = [];
2870
- const queue = [startDir];
2871
- while (queue.length > 0) {
2872
- const current = queue.shift();
2873
- for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
2874
- if (
2875
- ['.git', '.output', '.zerops', 'dist', 'node_modules', 'repos'].includes(
2876
- entry.name,
2877
- )
2878
- ) {
2879
- continue;
2880
- }
2881
- const absolute = path.join(current, entry.name);
2882
- if (entry.isDirectory()) {
2883
- queue.push(absolute);
2884
- } else if (entry.name === 'package.json') {
2885
- files.push(absolute);
2886
- }
2887
- }
2888
- }
2889
- return files.sort();
2890
- };
2891
- const modernDependencyNames = packageJson =>
2892
- [
2893
- ...new Set(
2894
- [
2895
- 'dependencies',
2896
- 'devDependencies',
2897
- 'optionalDependencies',
2898
- 'peerDependencies',
2899
- ]
2900
- .flatMap(section => Object.keys(packageJson[section] ?? {}))
2901
- .filter(packageName => packageName.startsWith('@modern-js/')),
2902
- ),
2903
- ];
2904
- const assertModernPackageCohort = () => {
2905
- const modernPackageNames = workspaceValidationContract.cohort.modernPackages;
2906
- const modernPackageNameSet = new Set(modernPackageNames);
2907
- const observedModernPackageNames = new Set();
2908
- const observedAppIds = [];
2909
- // Additional shells (G28) are their own Delivery Units registered in the
2910
- // additive `config.shells` collection and gated by the structural thin-shell
2911
- // policy; they are deliberately kept out of the strict topology.apps cohort,
2912
- // so their package manifests are excluded from the app-id cohort walk here.
2913
- const additionalShellAppIds = new Set(
2914
- (workspaceValidationContract.structuralShellPolicy?.shells ?? [])
2915
- .map(shell => shell.id)
2916
- .filter(id => id !== 'shell-super-app'),
2917
- );
2918
- for (const packageJsonPath of packageJsonFiles(root)) {
2919
- const relativePath = path.relative(root, packageJsonPath).split(path.sep).join('/');
2920
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
2921
- if (
2922
- typeof packageJson.modernjs?.appId === 'string' &&
2923
- !additionalShellAppIds.has(packageJson.modernjs.appId)
2924
- ) {
2925
- observedAppIds.push(packageJson.modernjs.appId);
2926
- }
2927
- for (const packageName of modernDependencyNames(packageJson)) {
2928
- observedModernPackageNames.add(packageName);
2929
- assert(
2930
- modernPackageNameSet.has(packageName),
2931
- `${relativePath} declares ${packageName} outside package source metadata`,
2932
- );
2933
- }
2934
- for (const section of [
2935
- 'dependencies',
2936
- 'devDependencies',
2937
- 'optionalDependencies',
2938
- 'peerDependencies',
2939
- ]) {
2940
- for (const packageName of modernPackageNames) {
2941
- const actual = packageJson[section]?.[packageName];
2942
- if (actual !== undefined) {
2943
- assert(
2944
- actual === expectedModernPackageSpecifier(packageName),
2945
- `${relativePath} ${section}.${packageName} must match package source metadata`,
2946
- );
2947
- }
2948
- }
2949
- }
2950
- }
2951
-
2952
- for (const packageName of modernPackageNames) {
2953
- assert(
2954
- observedModernPackageNames.has(packageName),
2955
- `Modern package cohort is missing ${packageName}`,
2956
- );
2957
- }
2958
- assertUniqueStrings(observedAppIds, 'generated app package manifests');
2959
- // The app-id cohort is a SET: observedAppIds is discovered by a
2960
- // filesystem-ordered package.json walk, while cohort.appIds is in generation
2961
- // insertion order. Compare order-insensitively (every other cohort check uses
2962
- // the same sorted comparison via assertSameIdCohort) so a multi-vertical
2963
- // workspace whose insertion order differs from filesystem order still passes.
2964
- assertSameJson(
2965
- [...observedAppIds].toSorted(),
2966
- [...workspaceValidationContract.cohort.appIds].toSorted(),
2967
- 'generated app package manifest cohort',
2968
- 'restore every generated app package manifest',
2969
- );
2970
- };
2971
- const assertPublicSurfaceAssets = (appPath, publicRoutes) => {
2972
- for (const relativePath of publicSurfaceManagedSourceAssetPaths) {
2973
- assertNotExists(`${appPath}/${relativePath}`);
2974
- }
2975
- void publicRoutes;
2976
- };
2977
- const assertPublicSurfaceContract = (appId, publicSurface) => {
2978
- assert(publicSurface?.artifactLifecycle === 'build-and-deploy-output', `${appId} public surface artifacts must be build/deploy outputs`);
2979
- assert(publicSurface?.generator === 'scripts/generate-public-surface-assets.mts', `${appId} public surface generator script is incorrect`);
2980
- assert(publicSurface?.outputRoot === 'dist/public', `${appId} public surface dist outputRoot is incorrect`);
2981
- assert(publicSurface?.cloudflareBuildOutputRoot === 'dist-cloudflare/public', `${appId} public surface Cloudflare build outputRoot is incorrect`);
2982
- assert(!('cloudflareOutputRoot' in (publicSurface ?? {})), `${appId} public surface must not target final .output directly`);
2983
- assert(!('staticRoot' in (publicSurface ?? {})), `${appId} public surface must not point at source config/public`);
2984
- assert((publicSurface?.files ?? []).includes('robots.txt'), `${appId} public surface must always emit robots.txt`);
2985
- assert(publicSurface?.contentExpansion?.authoring === 'route-owned-esm-provider', `${appId} public content expansion authoring is incorrect`);
2986
- assert(publicSurface?.contentExpansion?.defaultProviderFile === 'route.sitemap.mjs', `${appId} public content expansion provider file is incorrect`);
2987
- assert(publicSurface?.contentExpansion?.draftPolicy === 'omit-draft-by-default', `${appId} public content expansion draft policy is incorrect`);
2988
- assert(publicSurface?.contentExpansion?.indexablePolicy === 'omit-indexable-false', `${appId} public content expansion indexable policy is incorrect`);
2989
- assert(Array.isArray(publicSurface?.contentSources), `${appId} public content sources must be an array`);
2990
- if ((publicSurface?.publicRoutes ?? []).length === 0) {
2991
- assert(!(publicSurface?.files ?? []).includes('sitemap.xml'), `${appId} private public surface must omit sitemap.xml`);
2992
- assert(!(publicSurface?.files ?? []).includes('site.webmanifest'), `${appId} private public surface must omit site.webmanifest`);
2993
- } else {
2994
- assert((publicSurface?.files ?? []).includes('sitemap.xml'), `${appId} public surface must emit sitemap.xml when public routes exist`);
2995
- assert((publicSurface?.files ?? []).includes('site.webmanifest'), `${appId} public surface must emit site.webmanifest when public routes exist`);
2996
- }
2997
- };
2998
- const assertPublicHeadContract = (appId, publicHead) => {
2999
- assert(publicHead?.generator === './src/routes/ultramodern-route-head', `${appId} public head generator is incorrect`);
3000
- assert(publicHead?.renderer === '@modern-js/runtime/head Helmet', `${appId} public head renderer is incorrect`);
3001
- assert(publicHead?.ssr === true, `${appId} public head must be SSR-rendered`);
3002
- assert(publicHead?.title?.source === 'route.titleKey', `${appId} public head title must come from route metadata`);
3003
- assert(publicHead?.description?.source === 'route.descriptionKey', `${appId} public head description must come from route metadata`);
3004
- assert(publicHead?.canonical?.publicIndexableOnly === true, `${appId} canonical links must be public/indexable only`);
3005
- assert(publicHead?.structuredData?.optional === true, `${appId} structured data must be optional`);
3006
- assert(publicHead?.structuredData?.source === 'route.jsonLd', `${appId} structured data must come from explicit route metadata`);
3007
- assert(publicHead?.structuredData?.inference === false, `${appId} structured data inference must stay disabled`);
3008
- assert(publicHead?.structuredData?.helperModule === './src/routes/ultramodern-jsonld', `${appId} structured data helper module is incorrect`);
3009
- assert(publicHead?.structuredData?.sanitizesHtmlOpenBracket === true, `${appId} structured data must sanitize HTML open brackets`);
3010
- assert(publicHead?.privateRouteRobots === 'noindex, nofollow', `${appId} private route robots policy is incorrect`);
3011
- };
3012
- const assertCloudflareQualityGates = (appId, qualityGates) => {
3013
- assert(qualityGates?.publicRoutes?.requireSitemapWhenPresent === true, `${appId} quality gates must require sitemap for public routes`);
3014
- assert(qualityGates?.publicRoutes?.requireRobotsSitemapConsistency === true, `${appId} quality gates must require robots/sitemap consistency`);
3015
- assert(qualityGates?.statusCodes?.unknownRouteStatus === 404, `${appId} quality gates must require 404 unknown routes`);
3016
- assert(qualityGates?.indexing?.previewNoindex === true, `${appId} quality gates must require preview noindex`);
3017
- assert(qualityGates?.indexing?.productionPublicRoutesIndexable === true, `${appId} quality gates must require production public routes to be indexable`);
3018
- assert(qualityGates?.assets?.cssPreloadRequired === true, `${appId} quality gates must require CSS preload evidence`);
3019
- assert(qualityGates?.assets?.sourcemapsPubliclyReferenced === false, `${appId} quality gates must reject public sourcemap references`);
3020
- assert(typeof qualityGates?.budgets?.ssrHtmlMaxBytes === 'number', `${appId} quality gates must define SSR HTML byte budget`);
3021
- assert(typeof qualityGates?.budgets?.mfManifestMaxBytes === 'number', `${appId} quality gates must define MF manifest byte budget`);
3022
- assert(qualityGates?.csp?.finalMode === 'report-only-dogfood', `${appId} CSP final mode decision is missing`);
3023
- };
3024
- const expectedWorkerName = packageSuffix => `${packageScope}-${packageSuffix}`.slice(0, 63);
3025
- const expectedChunkLoadingGlobal = mfName =>
3026
- `__ULTRAMODERN_${mfName
3027
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
3028
- .replace(/[^a-zA-Z0-9]+/g, '_')
3029
- .replace(/^_+|_+$/g, '')
3030
- .toUpperCase()}_LOADED_CHUNKS__`;
3031
- const parseSemver = version => {
3032
- const match = /^(\d+)\.(\d+)\.(\d+)/u.exec(version);
3033
- assert(match, `Unable to parse pnpm version: ${version}`);
3034
- return {
3035
- major: Number(match[1]),
3036
- minor: Number(match[2]),
3037
- patch: Number(match[3]),
3038
- };
3039
- };
3040
- const compareSemver = (left, right) =>
3041
- left.major - right.major || left.minor - right.minor || left.patch - right.patch;
3042
-
3043
- const activePnpmVersion = execFileSync('pnpm', ['--pm-on-fail=ignore', '--version'], {
3044
- cwd: root,
3045
- encoding: 'utf-8',
3046
- stdio: ['ignore', 'pipe', 'pipe'],
3047
- }).trim();
3048
- const activeNodeVersion = process.versions.node;
3049
- const minimumPnpmVersion = { major: 11, minor: 0, patch: 0 };
3050
- const currentPnpmVersion = parseSemver(activePnpmVersion);
3051
- const minimumNodeVersion = { major: 26, minor: 0, patch: 0 };
3052
- const currentNodeVersion = parseSemver(activeNodeVersion);
3053
-
3054
- assert(
3055
- compareSemver(currentPnpmVersion, minimumPnpmVersion) >= 0,
3056
- `Generated workspace requires pnpm >=11; active pnpm is ${activePnpmVersion}. Run mise install, then rerun pnpm from the activated shell.`,
3057
- );
3058
- assert(
3059
- compareSemver(currentNodeVersion, minimumNodeVersion) >= 0,
3060
- `Generated workspace requires Node >=26; active Node is ${activeNodeVersion}. Run mise install, then rerun node from the activated shell.`,
3061
- );
3062
-
3063
- const agentInstructionPaths = ['AGENTS.md', 'CLAUDE.md'];
3064
- const presentAgentInstructionPaths = agentInstructionPaths.filter(
3065
- relativePath => fs.existsSync(path.join(root, relativePath)),
3066
- );
3067
- assert(
3068
- presentAgentInstructionPaths.length === 0 ||
3069
- presentAgentInstructionPaths.length === agentInstructionPaths.length,
3070
- 'Agent instruction files must be either fully enabled (AGENTS.md and CLAUDE.md) or fully disabled',
3071
- );
3072
-
3073
- const requiredPaths = [
3074
- '.gitignore',
3075
- 'package.json',
3076
- 'pnpm-workspace.yaml',
3077
- `patches/@module-federation__modern-js-v3@${expectedModuleFederationVersion}.patch`,
3078
- `patches/@module-federation__bridge-react@${expectedModuleFederationVersion}.patch`,
3079
- `patches/@module-federation__runtime-core@${expectedModuleFederationVersion}.patch`,
3080
- 'tsconfig.json',
3081
- 'tsconfig.base.json',
3082
- 'oxlint.config.ts',
3083
- 'oxfmt.config.ts',
3084
- '.github/renovate.json',
3085
- '.github/workflows/ultramodern-workspace-gates.yml',
3086
- '.agents/agent-reference-repos.json',
3087
- 'topology/reference-topology.json',
3088
- 'topology/ownership.json',
3089
- 'topology/local-overlays/development.json',
3090
- 'scripts/assert-mf-types.mts',
3091
- 'scripts/bootstrap-agent-skills.mts',
3092
- 'scripts/check-ultramodern-api-boundaries.mts',
3093
- 'scripts/check-ultramodern-i18n-boundaries.mts',
3094
- ...(hasBackendSurfaces ? ['scripts/generate-node-backend-federation.mts'] : []),
3095
- 'scripts/generate-public-surface-assets.mts',
3096
- 'scripts/generate-tanstack-routes.mts',
3097
- 'scripts/proof-cloudflare-version.mts',
3098
- ...(hasDeliveryUnits ? ['scripts/proof-workerd-ssr.mts'] : []),
3099
- ...(hasBackendSurfaces ? ['scripts/proof-node-backend-federation.mts'] : []),
3100
- 'scripts/setup-agent-reference-repos.mts',
3101
- 'scripts/ultramodern-performance-readiness.config.mjs',
3102
- 'scripts/ultramodern-performance-readiness.mts',
3103
- 'scripts/ultramodern-typecheck.mts',
3104
- 'scripts/validate-ultramodern-workspace.mts',
3105
- 'scripts/verify-cloudflare-output.mts',
3106
- 'apps/shell-super-app/package.json',
3107
- 'apps/shell-super-app/tsconfig.json',
3108
- 'apps/shell-super-app/tsconfig.mf-types.json',
3109
- 'apps/shell-super-app/modern.config.ts',
3110
- 'apps/shell-super-app/module-federation.config.ts',
3111
- 'apps/shell-super-app/src/modern-app-env.d.ts',
3112
- 'apps/shell-super-app/src/modern.runtime.ts',
3113
- 'apps/shell-super-app/src/api/vertical-clients.ts',
3114
- 'apps/shell-super-app/locales/en/translation.json',
3115
- `apps/shell-super-app/locales/en/${shellNamespace}.json`,
3116
- 'apps/shell-super-app/locales/cs/translation.json',
3117
- `apps/shell-super-app/locales/cs/${shellNamespace}.json`,
3118
- 'apps/shell-super-app/src/routes/index.css',
3119
- 'apps/shell-super-app/src/routes/layout.tsx',
3120
- 'apps/shell-super-app/src/routes/shell-frame.tsx',
3121
- 'apps/shell-super-app/src/routes/ultramodern-route-head.tsx',
3122
- 'apps/shell-super-app/src/routes/ultramodern-route-metadata.ts',
3123
- 'apps/shell-super-app/src/routes/vertical-components.tsx',
3124
- requiredShellWorkerCompositionPath('apps/shell-super-app'),
3125
- 'apps/shell-super-app/src/routes/[lang]/page.tsx',
3126
- ...shellRouteMetaPaths,
3127
- 'packages/shared-contracts/package.json',
3128
- 'packages/shared-contracts/src/index.ts',
3129
- 'packages/shared-contracts/tsconfig.json',
3130
- 'packages/shared-design-tokens/package.json',
3131
- 'packages/shared-design-tokens/src/index.ts',
3132
- 'packages/shared-design-tokens/src/tokens.css',
3133
- 'packages/shared-design-tokens/tsconfig.json',
3134
- ];
3135
-
3136
- for (const vertical of fullStackVerticals) {
3137
- requiredPaths.push(...requiredMicroVerticalPaths(vertical));
3138
- }
3139
-
3140
- if (tailwindEnabled) {
3141
- requiredPaths.push(
3142
- 'apps/shell-super-app/tailwind.config.ts',
3143
- ...fullStackVerticals
3144
- .filter(vertical => vertical.emitsUi)
3145
- .flatMap(vertical => [`${vertical.path}/tailwind.config.ts`]),
3146
- );
3147
- }
3148
-
3149
- requiredPaths.push(compactConfigPath);
3150
-
3151
- for (const vertical of fullStackVerticals) {
3152
- requiredMicroVerticalPaths(vertical).forEach(
3153
- assertRequiredVerticalFile(vertical),
3154
- );
3155
- // Reject profile-foreign surfaces planted into a unit (e.g. a browser MF
3156
- // config in a headless api-only unit, or an API contract in a UI-only unit).
3157
- forbiddenMicroVerticalPaths(vertical).forEach(
3158
- assertForbiddenVerticalFile(vertical),
3159
- );
3160
- }
3161
- for (const shell of expectedAdditionalShells) {
3162
- requiredPaths.push(
3163
- `${shell.path}/package.json`,
3164
- `${shell.path}/tsconfig.json`,
3165
- `${shell.path}/tsconfig.mf-types.json`,
3166
- `${shell.path}/modern.config.ts`,
3167
- `${shell.path}/module-federation.config.ts`,
3168
- `${shell.path}/src/modern-app-env.d.ts`,
3169
- `${shell.path}/src/modern.runtime.ts`,
3170
- `${shell.path}/src/api/vertical-clients.ts`,
3171
- `${shell.path}/locales/en/translation.json`,
3172
- `${shell.path}/locales/en/${shellNamespace}.json`,
3173
- `${shell.path}/locales/cs/translation.json`,
3174
- `${shell.path}/locales/cs/${shellNamespace}.json`,
3175
- `${shell.path}/src/routes/index.css`,
3176
- `${shell.path}/src/routes/layout.tsx`,
3177
- `${shell.path}/src/routes/shell-frame.tsx`,
3178
- `${shell.path}/src/routes/vertical-components.tsx`,
3179
- requiredShellWorkerCompositionPath(shell.path),
3180
- `${shell.path}/src/routes/ultramodern-route-head.tsx`,
3181
- `${shell.path}/src/routes/ultramodern-route-metadata.ts`,
3182
- `${shell.path}/src/routes/[lang]/page.tsx`,
3183
- ...shellRouteMetaPaths.map(relativePath =>
3184
- relativePath.replace(/^apps\/shell-super-app/u, shell.path),
3185
- ),
3186
- );
3187
- if (tailwindEnabled) {
3188
- requiredPaths.push(`${shell.path}/tailwind.config.ts`);
3189
- }
3190
- }
3191
- for (const requiredPath of requiredPaths) {
3192
- assertExists(requiredPath);
3193
- }
3194
- // Agent skills metadata may live under .agents/ (agents-standard layout) or
3195
- // .codex/ (legacy scaffold default).
3196
- assertAnyOf(['.agents/skills-lock.json', '.codex/skills-lock.json']);
3197
- assertAnyOf([
3198
- '.agents/rstackjs-agent-skills-LICENSE',
3199
- '.codex/rstackjs-agent-skills-LICENSE',
3200
- ]);
3201
- const readPnpmWorkspaceConfig = key =>
3202
- JSON.parse(
3203
- execFileSync('pnpm', ['config', 'get', key, '--json'], {
3204
- cwd: root,
3205
- encoding: 'utf-8',
3206
- }),
3207
- );
3208
- const pnpmOverrides = readPnpmWorkspaceConfig('overrides');
3209
- const pnpmPatches = readPnpmWorkspaceConfig('patchedDependencies');
3210
- // pnpm reports patch paths realpathed against the workspace dir; on Windows
3211
- // that can be the long-name form while `root` (process.cwd()) is the 8.3
3212
- // short form, so compare realpaths instead of raw string equality.
3213
- const canonicalFsPath = target => {
3214
- try {
3215
- return fs.realpathSync.native(target);
3216
- } catch {
3217
- return path.resolve(target);
3218
- }
3219
- };
3220
- const patchPathMatches = (key, expectedRelativePath) =>
3221
- typeof pnpmPatches[key] === 'string' &&
3222
- canonicalFsPath(pnpmPatches[key]) ===
3223
- canonicalFsPath(path.join(root, expectedRelativePath));
3224
- assert(
3225
- pnpmOverrides['@effect/opentelemetry'] === expectedEffectVersion,
3226
- 'pnpm-workspace.yaml must override @effect/opentelemetry to the generated Effect cohort',
3227
- );
3228
- assert(
3229
- pnpmOverrides.effect === expectedEffectVersion,
3230
- 'pnpm-workspace.yaml must override effect to the generated Effect cohort',
3231
- );
3232
- assert(
3233
- pnpmOverrides['@effect/vitest'] === expectedEffectVitestVersion,
3234
- 'pnpm-workspace.yaml must override @effect/vitest to the generated Effect test cohort',
3235
- );
3236
- assert(
3237
- pnpmOverrides['@tanstack/history'] === expectedTanstackHistoryVersion,
3238
- 'pnpm-workspace.yaml must override @tanstack/history to the generated TanStack cohort',
3239
- );
3240
- assert(
3241
- patchPathMatches(
3242
- `@module-federation/modern-js-v3@${expectedModuleFederationVersion}`,
3243
- `patches/@module-federation__modern-js-v3@${expectedModuleFederationVersion}.patch`,
3244
- ),
3245
- 'pnpm-workspace.yaml must patch the generated Module Federation Modern.js integration cohort',
3246
- );
3247
- assert(
3248
- patchPathMatches(
3249
- `@module-federation/bridge-react@${expectedModuleFederationVersion}`,
3250
- `patches/@module-federation__bridge-react@${expectedModuleFederationVersion}.patch`,
3251
- ),
3252
- 'pnpm-workspace.yaml must patch the generated Module Federation React bridge cohort',
3253
- );
3254
- assert(
3255
- patchPathMatches(
3256
- `@module-federation/runtime-core@${expectedModuleFederationVersion}`,
3257
- `patches/@module-federation__runtime-core@${expectedModuleFederationVersion}.patch`,
3258
- ),
3259
- 'pnpm-workspace.yaml must patch the generated Module Federation runtime-core cohort',
3260
- );
3261
- assertWorkspaceValidationContract(workspaceValidationContract);
3262
- assertAdditionalShellContractShape();
3263
- assertCompilerArchitecture();
3264
- for (const oldRemotePath of oldRemotePaths) {
3265
- assertNotExists(oldRemotePath);
3266
- }
3267
- for (const retiredMetadataPath of retiredMetadataPaths) {
3268
- assertNotExists(retiredMetadataPath);
3269
- }
3270
- const rootPackage = readJson('package.json');
3271
- const ultramodernConfig = readJson(compactConfigPath);
3272
- const topology = readJson(
3273
- workspaceValidationContract.metadata.referenceTopology.path,
3274
- );
3275
- const ownership = readJson(workspaceValidationContract.metadata.ownership.path);
3276
- const overlay = readJson(
3277
- workspaceValidationContract.metadata.developmentOverlay.path,
3278
- );
3279
- assertStructuredWorkspaceMetadata({
3280
- ultramodernConfig,
3281
- topology,
3282
- ownership,
3283
- overlay,
3284
- });
3285
- const bridgeConfig =
3286
- ultramodernConfig?.bridge?.enabled === true ? ultramodernConfig.bridge : undefined;
3287
- const packageSource = createPackageSourceView(ultramodernConfig);
3288
- const generatedContract = readGeneratedContractView(ultramodernConfig);
3289
- const shellPackage = readJson('apps/shell-super-app/package.json');
3290
-
3291
- assertMicroVerticalContractGraph({
3292
- generatedContract,
3293
- topology,
3294
- ownership,
3295
- overlay,
3296
- shellPackage,
3297
- });
3298
- assertStructuredWorkspaceMetadataSemantics({
3299
- ultramodernConfig,
3300
- topology,
3301
- ownership,
3302
- overlay,
3303
- });
3304
- assertTsConfigReferenceGraph();
3305
-
3306
- assert(rootPackage.private === true, 'Root package must be private');
3307
- assert(typeof rootPackage.packageManager === 'string', 'Root must declare packageManager');
3308
- const packageManagerPnpmVersionMatch = /^pnpm@(\d+\.\d+\.\d+)$/u.exec(rootPackage.packageManager);
3309
- assert(packageManagerPnpmVersionMatch, 'Root packageManager must pin pnpm with a semver version');
3310
- const packageManagerPnpmVersion = packageManagerPnpmVersionMatch[1];
3311
- assert(
3312
- compareSemver(parseSemver(packageManagerPnpmVersion), minimumPnpmVersion) >= 0,
3313
- 'Root packageManager must use pnpm >=11',
3314
- );
3315
- assert(rootPackage.engines?.node === '>=26', 'Root must require Node >=26');
3316
- assert(rootPackage.engines?.pnpm === '>=11', 'Root must require pnpm >=11');
3317
- assert(generatedContract.node?.version === expectedNodeVersion, 'Generated contract must record the Node toolchain version');
3318
- assert(generatedContract.node?.engineRange === '>=26', 'Generated contract must record the Node engine range');
3319
- const activeMiseTools = JSON.parse(
3320
- execFileSync('mise', ['ls', '--current', '--json'], {
3321
- cwd: root,
3322
- encoding: 'utf-8',
3323
- }),
3324
- );
3325
- assert(
3326
- activeMiseTools.node?.some(tool => tool.requested_version === expectedNodeVersion),
3327
- 'mise must activate the generated Node version',
3328
- );
3329
- assert(
3330
- activeMiseTools.pnpm?.some(
3331
- tool => tool.requested_version === packageManagerPnpmVersion,
3332
- ),
3333
- 'mise must activate the generated pnpm version',
3334
- );
3335
- assertExists('.github/workflows/ultramodern-workspace-gates.yml');
3336
- assert(rootPackage.modernjs?.preset === 'presetUltramodern', 'Root must declare presetUltramodern');
3337
- assert(
3338
- rootPackage.modernjs?.packageSource?.config === './.modernjs/ultramodern.json',
3339
- 'Root must point at compact UltraModern config',
3340
- );
3341
- assert(rootPackage.modernjs?.packageSource?.strategy === packageSource.strategy, 'Root package source strategy must match metadata');
3342
- assert(packageSource.strategy === 'workspace' || packageSource.strategy === 'install', 'Package source strategy must be workspace or install');
3343
- assert(packageSource.strategy === 'install' || packageSource.modernPackages?.specifier === 'workspace:*', 'Workspace package source must be explicitly backed by workspace:*');
3344
- assertModernPackageCohort();
3345
- const assertAdditionalShellCohort = () => {
3346
- const primaryShellConfig = findById(
3347
- ultramodernConfig.topology?.apps,
3348
- 'shell-super-app',
3349
- );
3350
- const overlayPorts = overlay.ports ?? {};
3351
- const configuredPorts = [
3352
- ...Object.entries(overlayPorts).map(([id, port]) => ({ id, port })),
3353
- ...(!Object.hasOwn(overlayPorts, 'shell-super-app')
3354
- ? [{ id: 'shell-super-app', port: primaryShellConfig?.port }]
3355
- : []),
3356
- ...expectedAdditionalShells.map(shell => ({
3357
- id: shell.id,
3358
- port: shell.port,
3359
- })),
3360
- ];
3361
- const portsByValue = new Map();
3362
- for (const { id, port } of configuredPorts) {
3363
- assert(
3364
- typeof port === 'number' && Number.isFinite(port),
3365
- `Configured development port for ${id} must be finite`,
3366
- );
3367
- const previous = portsByValue.get(port);
3368
- assert(
3369
- previous === undefined,
3370
- `Duplicate configured development port ${port} for ${previous} and ${id}`,
3371
- );
3372
- portsByValue.set(port, id);
3373
- }
3374
-
3375
- if (expectedAdditionalShellIds.length === 0) {
3376
- assert(
3377
- ultramodernConfig.shells === undefined,
3378
- 'Single-shell workspace must not declare config.shells',
3379
- );
3380
- return;
3381
- }
3382
-
3383
- assertSameIdCohort(
3384
- ultramodernConfig.shells,
3385
- expectedAdditionalShellIds,
3386
- `${compactConfigPath} shells`,
3387
- 'restore every configured additional shell',
3388
- );
3389
- const configuredShellRecords = ultramodernConfig.shells;
3390
- const configuredShellById = new Map(
3391
- configuredShellRecords.map(shell => [shell.id, shell]),
3392
- );
3393
- assertUniqueStrings(
3394
- [
3395
- primaryShellConfig?.moduleFederation?.name,
3396
- ...expectedAdditionalShells.map(shell => shell.mfName),
3397
- ],
3398
- 'configured shell Module Federation identities',
3399
- );
3400
- assertUniqueStrings(
3401
- expectedAdditionalShells.map(
3402
- shell => shell.deliveryUnit?.buildMarker,
3403
- ),
3404
- 'configured shell build markers',
3405
- );
3406
-
3407
- for (const shell of expectedAdditionalShells) {
3408
- const configShell = configuredShellById.get(shell.id);
3409
- assertObject(
3410
- configShell,
3411
- `${compactConfigPath} shells.${shell.id}`,
3412
- 'restore the generated additional-shell config record',
3413
- );
3414
- assertSameJson(
3415
- {
3416
- id: configShell.id,
3417
- name: configShell.name,
3418
- kind: configShell.kind,
3419
- package: configShell.package,
3420
- path: configShell.path,
3421
- port: configShell.port,
3422
- portEnv: configShell.portEnv,
3423
- mfName: configShell.mfName,
3424
- verticalRefs: configShell.verticalRefs,
3425
- owner: configShell.owner,
3426
- deliveryUnit: configShell.deliveryUnit,
3427
- moduleFederation: configShell.moduleFederation,
3428
- },
3429
- {
3430
- id: shell.id,
3431
- name: shell.id.replace(/^shell-/u, ''),
3432
- kind: 'shell',
3433
- package: shell.packageName,
3434
- path: shell.path,
3435
- port: shell.port,
3436
- portEnv: shell.portEnv,
3437
- mfName: shell.mfName,
3438
- verticalRefs: shell.verticalRefs,
3439
- owner: shell.owner,
3440
- deliveryUnit: shell.deliveryUnit,
3441
- moduleFederation: shell.moduleFederation,
3442
- },
3443
- `${compactConfigPath} shells.${shell.id}`,
3444
- 'restore the complete additional-shell config record',
3445
- );
3446
- assert(
3447
- !Object.hasOwn(overlay.ports ?? {}, shell.id),
3448
- `${shell.id} port must stay in config.shells, not the development overlay`,
3449
- );
3450
- const owner = configShell.owner;
3451
- assertObject(
3452
- owner,
3453
- `${compactConfigPath} shells.${shell.id}.owner`,
3454
- 'record exactly one owner for every configured Delivery Unit',
3455
- );
3456
- assert(
3457
- ['team', 'agent', 'agent-team'].includes(owner.kind) &&
3458
- typeof owner.id === 'string' &&
3459
- owner.id.length > 0,
3460
- `${compactConfigPath} shells.${shell.id}.owner must identify one accountable owner`,
3461
- );
3462
- assertSameJson(
3463
- owner,
3464
- shell.owner,
3465
- `${compactConfigPath} shells.${shell.id}.owner`,
3466
- 'restore the generated additional-shell owner attribution',
3467
- );
3468
- assertObject(
3469
- configShell.deliveryUnit,
3470
- `${compactConfigPath} shells.${shell.id}.deliveryUnit`,
3471
- 'restore the generated additional-shell Delivery Unit identity',
3472
- );
3473
- assert(
3474
- typeof configShell.deliveryUnit.unitId === 'string' &&
3475
- configShell.deliveryUnit.unitId.length > 0 &&
3476
- typeof configShell.deliveryUnit.buildMarker === 'string' &&
3477
- configShell.deliveryUnit.buildMarker.length > 0,
3478
- `${compactConfigPath} shells.${shell.id}.deliveryUnit must carry unitId and buildMarker`,
3479
- );
3480
-
3481
- const packagePath = `${shell.path}/package.json`;
3482
- const packageJson = readJson(packagePath);
3483
- assert(packageJson.name === shell.packageName, `${shell.id} package name is incorrect`);
3484
- assert(packageJson.modernjs?.appId === shell.id, `${shell.id} package modernjs.appId is incorrect`);
3485
- assert(packageJson.modernjs?.role === 'shell', `${shell.id} package modernjs.role must be shell`);
3486
- assert(packageJson.scripts?.['cloudflare:deploy'] === 'cross-env ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json', `${shell.id} must expose a portable cloudflare:deploy`);
3487
- assert(packageJson.devDependencies?.['cross-env'] === expectedCrossEnvVersion, `${shell.id} cross-env dependency must match the portable generated-script cohort`);
3488
- assertPortableGeneratedEnvironmentScripts(packageJson, shell.id);
3489
-
3490
- const buildArtifact = readJson(`${shell.path}/shared/ultramodern-build.json`);
3491
- assert(buildArtifact.deliveryUnit?.appId === shell.id, `${shell.id} build artifact appId is incorrect`);
3492
- assert(buildArtifact.deliveryUnit?.buildMarker === shell.deliveryUnit?.buildMarker, `${shell.id} build marker is not participating in the build artifact`);
3493
- assertSameJson(
3494
- deliveryUnitBlock(configShell.deliveryUnit),
3495
- deliveryUnitBlock(shell.deliveryUnit),
3496
- `${compactConfigPath} shells.${shell.id}.deliveryUnit`,
3497
- deliveryUnitIdentityFixArea,
3498
- );
3499
- assertSameJson(
3500
- deliveryUnitBlock(buildArtifact.deliveryUnit),
3501
- deliveryUnitBlock(shell.deliveryUnit),
3502
- `${shell.path}/shared/ultramodern-build.json deliveryUnit`,
3503
- deliveryUnitIdentityFixArea,
3504
- );
3505
- assert(
3506
- shell.degradedState?.appId === shell.id &&
3507
- shell.degradedState?.status === 'degraded',
3508
- `${shell.id} degraded-state contract must identify its own shell`,
3509
- );
3510
-
3511
- if ((shell.verticalRefs ?? []).length > 0) {
3512
- assert(
3513
- shell.degradedState.required === true,
3514
- `${shell.id} degraded-state contract must be required for remote consumption`,
3515
- );
3516
- }
3517
- }
3518
-
3519
-
3520
- // Zerops artifacts exist whenever the workspace has delivery units at all
3521
- // (ui-only and horizontal-remote units deploy too); a shell-only workspace
3522
- // must not carry one.
3523
- if (!hasDeliveryUnits) {
3524
- assertNotExists('zerops.yaml');
3525
- } else {
3526
- assertExists('zerops.yaml');
3527
- const zeropsYaml = readText('zerops.yaml');
3528
- assert(
3529
- zeropsYaml.includes(`setup: ${quoteYamlString('shell-super-app')}`),
3530
- 'shell-super-app must have a Zerops service',
3531
- );
3532
- for (const shell of expectedAdditionalShells) {
3533
- assert(zeropsYaml.includes(`setup: ${quoteYamlString(shell.id)}`), `${shell.id} must have a Zerops service`);
3534
- assert(zeropsYaml.includes(`start: cd ${quoteShellValue(`.zerops/runtime/${shell.id}`)} && npm run serve`), `${shell.id} Zerops service start command is missing`);
3535
- assert(zeropsYaml.includes(` ${shell.portEnv}: ${quoteYamlString(String(shell.port))}`), `${shell.id} Zerops service port environment is missing`);
3536
- }
3537
- }
3538
- };
3539
- assertAdditionalShellCohort();
3540
- assert(
3541
- rootPackage.devDependencies?.['@modern-js/ultramodern-create'] ===
3542
- expectedModernPackageSpecifier('@modern-js/ultramodern-create'),
3543
- 'Root must depend on @modern-js/ultramodern-create through package source metadata',
3544
- );
3545
- assert(
3546
- rootPackage.devDependencies?.['@modern-js/code-tools'] ===
3547
- expectedModernPackageSpecifier('@modern-js/code-tools'),
3548
- 'Root must depend on @modern-js/code-tools through package source metadata',
3549
- );
3550
- assert(
3551
- rootPackage.devDependencies?.['@modern-js/plugin-bff'] ===
3552
- expectedModernPackageSpecifier('@modern-js/plugin-bff'),
3553
- 'Root must depend on @modern-js/plugin-bff for Node backend federation proof',
3554
- );
3555
- assert(rootPackage.devDependencies?.['cross-env'] === expectedCrossEnvVersion, 'Root cross-env dependency must match the portable generated-script cohort');
3556
- assert(rootPackage.scripts?.format === 'oxfmt .', 'Root format script must rely on the cross-platform Oxfmt ignore configuration');
3557
- assert(rootPackage.scripts?.['format:check'] === 'oxfmt --check .', 'Root format:check script must rely on the cross-platform Oxfmt ignore configuration');
3558
- if (packageSource.strategy === 'install') {
3559
- const installSpecifier = packageSource.modernPackages?.specifier;
3560
- assert(
3561
- typeof installSpecifier === 'string' &&
3562
- /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(installSpecifier) &&
3563
- installSpecifier.includes('ultramodern'),
3564
- 'Install package source must use a semver UltraModern published cohort',
3565
- );
3566
- const modernAliases = packageSource.modernPackages?.aliases ?? {};
3567
- if (Object.keys(modernAliases).length > 0) {
3568
- for (const modernPackageName of [
3569
- '@modern-js/app-tools',
3570
- '@modern-js/code-tools',
3571
- '@modern-js/plugin-bff',
3572
- '@modern-js/plugin-i18n',
3573
- '@modern-js/plugin-tanstack',
3574
- '@modern-js/runtime',
3575
- '@modern-js/ultramodern-create',
3576
- ]) {
3577
- assert(
3578
- /^@[^/]+\/.+/.test(modernAliases[modernPackageName] ?? ''),
3579
- `Install package source alias for ${modernPackageName} must be a scoped npm package`,
3580
- );
3581
- }
3582
- }
3583
- }
3584
- assert(packageSource.generatedWorkspacePackages?.specifier === 'workspace:*', 'Generated workspace packages must keep workspace:* links');
3585
- assert(
3586
- rootPackage.scripts?.build === expectedBuildScript,
3587
- 'Root build script must build verticals before shell',
3588
- );
3589
- assert(rootPackage.scripts?.['cloudflare:build'] === expectedCloudflareBuildScript, 'Root cloudflare:build script is incorrect');
3590
- assert(!('ultramodern:check' in (rootPackage.scripts ?? {})), 'Root must not expose ultramodern:check');
3591
- if (bridgeConfig) {
3592
- assert(
3593
- rootPackage.scripts?.typecheck ===
3594
- 'pnpm -r --filter "./apps/*" --filter "./verticals/*" --filter "./packages/*" run typecheck',
3595
- 'Bridge root typecheck must check generated package boundaries without building parent implementation sources',
3596
- );
3597
- assert(Array.isArray(bridgeConfig.workspacePackages), 'Bridge config must record workspace package patterns');
3598
- for (const workspacePackage of bridgeConfig.workspacePackages) {
3599
- assert(
3600
- rootPackage.workspaces?.includes(workspacePackage.pattern),
3601
- `Root workspaces must include bridge package pattern ${workspacePackage.pattern}`,
3602
- );
3603
- assert(
3604
- readPnpmWorkspaceConfig('packages').includes(workspacePackage.pattern),
3605
- `pnpm-workspace.yaml must include bridge package pattern ${workspacePackage.pattern}`,
3606
- );
3607
- }
3608
- for (const gate of bridgeConfig.gates ?? []) {
3609
- const expectedGateScript = `${gate.cwd ? `cd ${gate.cwd} && ` : ''}${gate.command}`;
3610
- assert(
3611
- rootPackage.scripts?.[`bridge:${gate.name}`] === expectedGateScript,
3612
- `Bridge gate script bridge:${gate.name} is incorrect`,
3613
- );
3614
- }
3615
- assert(rootPackage.scripts?.['bridge:check'], 'Bridge workspaces must expose bridge:check');
3616
- } else {
3617
- assert(rootPackage.scripts?.typecheck === 'node ./scripts/ultramodern-typecheck.mts --project tsconfig.json', 'Root typecheck must run TS-Go across the root project reference graph');
3618
- }
3619
- assert(rootPackage.scripts?.['contract:check'] === 'node ./scripts/validate-ultramodern-workspace.mts', 'Root must expose contract:check');
3620
- assert(rootPackage.scripts?.['api:check'] === 'node ./scripts/check-ultramodern-api-boundaries.mts', 'Root must expose api:check');
3621
- assert(rootPackage.scripts?.['i18n:boundaries'] === 'node ./scripts/check-ultramodern-i18n-boundaries.mts', 'Root must expose i18n:boundaries');
3622
- assert(rootPackage.scripts?.['performance:readiness'] === 'node ./scripts/ultramodern-performance-readiness.mts', 'Root must expose default-on performance readiness diagnostics');
3623
- if (hasBackendSurfaces) {
3624
- assert(rootPackage.scripts?.['node:backend-federation:generate'] === 'node ./scripts/generate-node-backend-federation.mts', 'Root must expose local Node backend federation artifact generation');
3625
- assert(
3626
- rootPackage.scripts?.['node:proof'] ===
3627
- 'node ./scripts/proof-node-backend-federation.mts',
3628
- 'Root must expose read-only node:proof for already-built backend federation Effect modules',
3629
- );
3630
- } else {
3631
- assert(rootPackage.scripts?.['node:backend-federation:generate'] === undefined, 'Root must not expose backend federation generation without an API surface');
3632
- assert(rootPackage.scripts?.['node:proof'] === undefined, 'Root must not expose node:proof without an API surface');
3633
- }
3634
- if (hasDeliveryUnits) {
3635
- assert(rootPackage.scripts?.['zerops:materialize'] === 'node ./scripts/materialize-zerops-runtime.mjs', 'Root must expose Zerops runtime materialization script');
3636
- assert(rootPackage.scripts?.['cloudflare:ssr-proof'] === 'node ./scripts/proof-workerd-ssr.mts', 'Root must expose workerd distributed SSR composition proof');
3637
- assert(rootPackage.scripts?.['cloudflare:build']?.endsWith('&& pnpm cloudflare:ssr-proof'), 'Root Cloudflare build must finish with workerd distributed SSR composition proof');
3638
- } else {
3639
- assert(rootPackage.scripts?.['zerops:materialize'] === undefined, 'Root must not expose Zerops materialization in a shell-only workspace');
3640
- assert(rootPackage.scripts?.['cloudflare:ssr-proof'] === undefined, 'Root must not expose workerd SSR proof in a shell-only workspace');
3641
- }
3642
- assertNotExists('scripts/generate-node-backend-federation.mjs');
3643
- assertNotExists('scripts/proof-node-backend-federation.mjs');
3644
- assertNotExists('scripts/verify-cloudflare-output.mjs');
3645
- assertNotExists('scripts/generate-tanstack-routes.mjs');
3646
- assert(
3647
- typeof rootPackage.scripts?.check === 'string',
3648
- 'Root check must expose the generated quality gate',
3649
- );
3650
- if (hasDeliveryUnits) {
3651
- const zeropsYaml = readText('zerops.yaml');
3652
- assert(zeropsYaml.includes('zerops:'), 'Zerops manifest must include zerops services');
3653
- assert(zeropsYaml.includes(`setup: ${quoteYamlString('shell-super-app')}`), 'Zerops manifest must include shell service');
3654
- assert(zeropsYaml.includes(`base: ${quoteYamlString('nodejs@26')}`), 'Zerops manifest must use generated Node runtime major');
3655
- assert(zeropsYaml.includes('deployFiles:'), 'Zerops manifest must deploy package-pruned runtime directories');
3656
- assert(
3657
- zeropsYaml.includes(`start: cd ${quoteShellValue('.zerops/runtime/shell-super-app')} && npm run serve`),
3658
- 'Zerops shell service must start from materialized runtime package',
3659
- );
3660
- assert(
3661
- zeropsYaml.includes('SHELL_SUPER_APP_PORT:') &&
3662
- zeropsYaml.includes('ULTRAMODERN_ZEROPS_SERVICE:'),
3663
- 'Zerops manifest must expose service identity and ports',
3664
- );
3665
- for (const vertical of fullStackVerticals) {
3666
- assert(zeropsYaml.includes(`setup: ${quoteYamlString(vertical.id)}`), `${vertical.id} must have a Zerops service`);
3667
- assert(
3668
- zeropsYaml.includes(`~/.local/bin/mise exec -- pnpm run zerops:materialize -- --app ${quoteShellValue(vertical.id)} --package ${quoteShellValue(vertical.packageName)} --package-dir ${quoteShellValue(vertical.path)}`),
3669
- `${vertical.id} Zerops service must materialize its runtime package`,
3670
- );
3671
- // Only REST-backed (or headless-less UI) units expose an HTTP readiness
3672
- // probe; the RPC surface has no REST readiness endpoint (G7a).
3673
- if (!vertical.emitsApi || vertical.apiProtocol === 'rest') {
3674
- const zeropsReadinessPath = vertical.emitsApi
3675
- ? `${vertical.apiPrefix}/${vertical.stem}/readiness`
3676
- : '/';
3677
- assert(
3678
- zeropsYaml.includes(`path: ${quoteYamlString(zeropsReadinessPath)}`),
3679
- `${vertical.id} BFF readiness path must use generated API prefix`,
3680
- );
3681
- }
3682
- }
3683
- }
3684
- assert(rootPackage.scripts?.['mf:types'] === 'node ./scripts/assert-mf-types.mts', 'Root must expose mf:types');
3685
- assert(rootPackage.scripts?.['cloudflare:deploy'] === expectedCloudflareDeployScript, 'Root must expose cloudflare:deploy');
3686
- assert(rootPackage.scripts?.['cloudflare:proof'] === 'node ./scripts/proof-cloudflare-version.mts --out .codex/reports/cloudflare-version-proof/public-url-proof.json', 'Root must expose cloudflare:proof');
3687
- assert(rootPackage.scripts?.['migrate:strict-effect'] === 'node ./scripts/migrate-strict-effect.mts', 'Root must expose migrate:strict-effect');
3688
- assert(rootPackage.scripts?.['skills:install'] === 'node ./scripts/bootstrap-agent-skills.mts', 'Root must expose skills:install');
3689
- assert(rootPackage.scripts?.['skills:check'] === 'node ./scripts/bootstrap-agent-skills.mts --check', 'Root must expose skills:check');
3690
- assert(rootPackage.scripts?.postinstall === 'node ./scripts/bootstrap-agent-skills.mts --postinstall && oxfmt .', 'Root postinstall must run the default-on Codex skills bootstrap, format installed skills through the cross-platform ignore configuration, and leave reference repository installs explicit');
3691
- assert(rootPackage.scripts?.['agents:refs:install'] === 'node ./scripts/setup-agent-reference-repos.mts', 'Root must expose agents:refs:install as the explicit reference repo installer');
3692
-
3693
- const expectedAppIds = ['shell-super-app', ...fullStackVerticals.map(vertical => vertical.id)];
3694
- const expectedCloudflareCompatibilityFlags = ['nodejs_compat', 'global_fetch_strictly_public'];
3695
- assert(
3696
- sameJson(generatedContract.apps?.map(app => app.id), expectedAppIds),
3697
- 'Generated contract must contain shell plus the full-stack verticals',
3698
- );
3699
- assert(generatedContract.cssFederation?.sharedDesignTokens?.owner?.id === 'shared-design-tokens', 'CSS federation must declare shared design token ownership');
3700
- assert(generatedContract.cssFederation?.sharedDesignTokens?.role === 'shared-design-tokens', 'CSS federation must mark shared-design-tokens as token owner');
3701
- assert(generatedContract.cssFederation?.sharedDesignTokens?.rootSelector === ':root', 'Shared design tokens must declare their root selector');
3702
- assert(generatedContract.cssFederation?.sharedDesignTokens?.classPrefix === '--um-', 'Shared design tokens must declare their CSS custom property prefix');
3703
- assert(generatedContract.cssFederation?.sharedDesignTokens?.layers?.owned?.includes('ultramodern-shared-tokens'), 'Shared design tokens must own the shared token CSS layer');
3704
- assert(generatedContract.cssFederation?.sharedDesignTokens?.entrypoints?.css?.includes('packages/shared-design-tokens/src/tokens.css'), 'Shared design tokens must declare their CSS entrypoint');
3705
- assert(generatedContract.cssFederation?.sharedDesignTokens?.assets?.exports?.includes('./tokens.css'), 'Shared design tokens must export their CSS asset');
3706
- assert(generatedContract.cssFederation?.sharedDesignTokens?.dedupe?.duplicateBaseStylesAllowed === false, 'Shared design token CSS must be deduplicated');
3707
- assert(generatedContract.cssFederation?.sharedDesignTokens?.ssr?.firstPaintRequired === true, 'Shared design token CSS must be required for SSR first paint');
3708
- const expectedPerformanceReadinessSignals = [
3709
- 'bfcache',
3710
- 'core-web-vitals-rum',
3711
- 'duplicate-prefetch-warmup',
3712
- 'cache-policy-sanity',
3713
- 'save-data-behavior',
3714
- 'cloudflare-ssr-cache-hints',
3715
- ];
3716
- assert(generatedContract.performanceReadiness?.default === 'enabled', 'Performance readiness diagnostics must be default-on in the generated contract');
3717
- assert(generatedContract.performanceReadiness?.mode === 'diagnostic', 'Performance readiness must remain diagnostic-only');
3718
- assert(generatedContract.performanceReadiness?.report?.script === 'scripts/ultramodern-performance-readiness.mts', 'Performance readiness contract must point at the generated script');
3719
- assert(generatedContract.performanceReadiness?.report?.deterministic === true, 'Performance readiness reports must be deterministic');
3720
- assert(generatedContract.performanceReadiness?.optOut?.env === 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false', 'Performance readiness env opt-out is incorrect');
3721
- assert(sameJson(generatedContract.performanceReadiness?.signals?.map(signal => signal.id), expectedPerformanceReadinessSignals), 'Performance readiness signal ids are incorrect');
3722
-
3723
- const expectedZephyrDependencies = Object.fromEntries(
3724
- expectedPrimaryShellVerticalIds.map(verticalId => {
3725
- const vertical = fullStackVerticals.find(
3726
- candidate => candidate.id === verticalId,
3727
- );
3728
- assert(vertical, `Missing primary-shell vertical ${verticalId}`);
3729
- return [
3730
- vertical.zephyrAlias,
3731
- `${vertical.packageName}@workspace:*`,
3732
- ];
3733
- }),
3734
- );
3735
- assert(
3736
- sameJson(shellPackage['zephyr:dependencies'], expectedZephyrDependencies),
3737
- 'Shell Zephyr dependencies must reference every primary-shell vertical package',
3738
- );
3739
- assert(shellPackage.devDependencies?.['@modern-js/app-tools'] === expectedModernPackageSpecifier('@modern-js/app-tools'), 'Shell app-tools dependency must match package source metadata');
3740
- assert(shellPackage.dependencies?.['@modern-js/plugin-bff'] === expectedModernPackageSpecifier('@modern-js/plugin-bff'), 'Shell plugin-bff dependency must match package source metadata');
3741
- assert(shellPackage.dependencies?.['@modern-js/plugin-i18n'] === expectedModernPackageSpecifier('@modern-js/plugin-i18n'), 'Shell plugin-i18n dependency must match package source metadata');
3742
- assert(shellPackage.dependencies?.['@modern-js/plugin-tanstack'] === expectedModernPackageSpecifier('@modern-js/plugin-tanstack'), 'Shell plugin-tanstack dependency must match package source metadata');
3743
- assert(shellPackage.dependencies?.['@modern-js/runtime'] === expectedModernPackageSpecifier('@modern-js/runtime'), 'Shell runtime dependency must match package source metadata');
3744
- assert(shellPackage.scripts?.['cloudflare:deploy'] === 'cross-env ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json', 'Shell must expose a portable cloudflare:deploy');
3745
- assert(shellPackage.devDependencies?.['cross-env'] === expectedCrossEnvVersion, 'Shell cross-env dependency must match the portable generated-script cohort');
3746
- assertPortableGeneratedEnvironmentScripts(shellPackage, 'Shell');
3747
- const shellContract = generatedContract.apps?.find(app => app.id === 'shell-super-app');
3748
- assert(shellContract?.deploy?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell Cloudflare workerName is incorrect');
3749
- assert(shellContract?.deploy?.cloudflare?.publicUrlEnv === 'ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP', 'Shell Cloudflare public URL env is incorrect');
3750
- assert(shellContract?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell Cloudflare compatibilityDate is incorrect');
3751
- assert(sameJson(shellContract?.deploy?.cloudflare?.compatibilityFlags, expectedCloudflareCompatibilityFlags), 'Shell Cloudflare compatibility flags are incorrect');
3752
- assert(sameJson(shellContract?.deploy?.cloudflare?.security, expectedCloudflareSecurity), 'Shell Cloudflare security contract is incorrect');
3753
- assertCloudflareQualityGates('shell-super-app', shellContract?.deploy?.cloudflare?.qualityGates);
3754
- assert(shellContract?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, 'Shell worker compatibilityDate is incorrect');
3755
- assert(shellContract?.deploy?.worker?.name === expectedWorkerName('shell-super-app'), 'Shell worker name is incorrect');
3756
- assert(shellContract?.config?.dev?.assetPrefix === '/', 'Shell dev asset prefix must stay origin-relative');
3757
- assert(shellContract?.config?.output?.assetPrefix?.default === '/', 'Shell asset prefix must default to origin-relative paths');
3758
- assert(sameJson(shellContract?.config?.output?.assetPrefix?.envFallbackOrder, ['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), 'Shell asset prefix env fallback order is incorrect');
3759
- assert(shellContract?.config?.output?.disableTsChecker === false, 'Shell must keep the framework TypeScript checker enabled');
3760
- assert(shellContract?.config?.performance?.readinessDiagnostics?.default === 'enabled', 'Shell performance readiness diagnostics must be default-on');
3761
- assert(shellContract?.config?.performance?.readinessDiagnostics?.failOn === 'framework-invariant', 'Shell performance readiness diagnostics must only fail framework invariants by default');
3762
- assert(shellContract?.config?.performance?.readinessDiagnostics?.optOut?.env === 'ULTRAMODERN_PERFORMANCE_READINESS_DIAGNOSTICS=false', 'Shell performance readiness env opt-out is incorrect');
3763
- assert(sameJson(shellContract?.config?.source?.siteUrl?.envFallbackOrder, ['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');
3764
- assert(shellContract?.config?.rspack?.output?.uniqueName === 'shellSuperApp', 'Shell Rspack uniqueName is incorrect');
3765
- assert(shellContract?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal('shellSuperApp'), 'Shell Rspack chunkLoadingGlobal is incorrect');
3766
- assert(shellContract?.moduleFederation?.dts?.compilerInstance === 'effect-tsgo', 'Shell must keep mandatory DTS compiler');
3767
- assert(shellContract?.moduleFederation?.dts?.tsConfigPath === './tsconfig.mf-types.json', 'Shell must keep dedicated Module Federation DTS tsconfig');
3768
- assert(topology.shell?.cloudflare?.workerName === expectedWorkerName('shell-super-app'), 'Shell topology Cloudflare workerName is incorrect');
3769
- assert(shellContract?.styling?.federation?.owner?.id === 'shell-super-app', 'Shell CSS federation owner is missing');
3770
- assert(shellContract?.styling?.federation?.role === 'shell-base-overlay', 'Shell must own base and overlay CSS');
3771
- assert(shellContract?.styling?.federation?.rootSelector === '[data-app-id="shell-super-app"]', 'Shell CSS root selector is incorrect');
3772
- assert(shellContract?.styling?.federation?.classPrefix === 'shell:', 'Shell CSS class prefix is incorrect');
3773
- assert(shellContract?.styling?.federation?.layers?.owned?.includes('ultramodern-shell-base'), 'Shell must own the base CSS layer');
3774
- assert(shellContract?.styling?.federation?.layers?.owned?.includes('ultramodern-shell-overlay'), 'Shell must own the overlay CSS layer');
3775
- assert(shellContract?.styling?.federation?.entrypoints?.css?.includes('src/routes/index.css'), 'Shell CSS entrypoint is missing');
3776
- assert(shellContract?.styling?.federation?.assets?.shared?.some(asset => asset.endsWith('/shared-design-tokens/tokens.css')), 'Shell must import the shared design token CSS asset');
3777
- assert(shellContract?.styling?.federation?.dedupe?.duplicateBaseStylesAllowed === false, 'Shell CSS contract must forbid duplicated base styles');
3778
- assert(shellContract?.styling?.federation?.ssr?.firstPaintRequired === true, 'Shell CSS must be required for SSR first paint');
3779
- assert(shellContract?.routes?.privateByDefault === true, 'Shell routes must be private by default');
3780
- assert(shellContract?.routes?.metadataAuthoring === 'colocated-route-meta', 'Shell route metadata authoring mode is incorrect');
3781
- assert(shellContract?.routes?.generatedManifest === true, 'Shell route metadata manifest must be generated');
3782
- assert(shellContract?.routes?.publicnessDefault === 'private-app-screen', 'Shell route publicness default is incorrect');
3783
- assert(sameJson(shellContract?.routes?.publicRoutes ?? [], []), 'Shell must not expose generated public routes by default');
3784
- assertPublicHeadContract('shell-super-app', shellContract?.routes?.publicHead);
3785
- assertPublicSurfaceContract('shell-super-app', shellContract?.routes?.publicSurface);
3786
- assert(
3787
- (shellContract?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen' && typeof route.descriptionKey === 'string'),
3788
- 'Shell owned routes must be non-indexable private app screens by default and include description keys',
3789
- );
3790
- assertPublicSurfaceAssets('apps/shell-super-app', shellContract?.routes?.publicRoutes ?? []);
3791
- assert(
3792
- topology.shell?.verticalRefs?.join(',') === expectedPrimaryShellVerticalIds.join(','),
3793
- 'Topology shell verticalRefs must match generated verticals',
3794
- );
3795
- assert(topology.verticals?.length === fullStackVerticals.length, 'Topology must contain only generated verticals');
3796
- assert(!('remotes' in topology), 'Topology must not expose legacy remotes; use verticals');
3797
- assert(!('effectServices' in topology), 'Default APIs must be vertical-owned, not effectServices');
3798
-
3799
- for (const vertical of fullStackVerticals) {
3800
- const packageJson = readJson(`${vertical.path}/package.json`);
3801
- const ultramodernBuildArtifact = readJson(`${vertical.path}/shared/ultramodern-build.json`);
3802
- if (vertical.deliveryUnit) {
3803
- const topologyEntry = findById(topology.verticals, vertical.id);
3804
- const expectedDeliveryUnit = deliveryUnitBlock(
3805
- expectedDeliveryUnitFor(vertical, topologyEntry),
3806
- );
3807
- const buildLabel = `${vertical.path}/shared/ultramodern-build.json deliveryUnit`;
3808
- const buildIdentity = ultramodernBuildArtifact.deliveryUnit ?? {};
3809
- assertSelfCheck(
3810
- buildIdentity.buildMarker === expectedDeliveryUnit.buildMarker,
3811
- buildLabel,
3812
- `Expected build "${expectedDeliveryUnit.buildMarker}", found ${formatJson(buildIdentity.buildMarker)}`,
3813
- deliveryUnitIdentityFixArea,
3814
- );
3815
- assertSelfCheck(
3816
- buildIdentity.unitId === expectedDeliveryUnit.unitId,
3817
- buildLabel,
3818
- `Expected unitId "${expectedDeliveryUnit.unitId}", found ${formatJson(buildIdentity.unitId)}`,
3819
- deliveryUnitIdentityFixArea,
3820
- );
3821
- assertSelfCheck(
3822
- buildIdentity.packageName === expectedDeliveryUnit.packageName,
3823
- buildLabel,
3824
- `Expected packageName "${expectedDeliveryUnit.packageName}", found ${formatJson(buildIdentity.packageName)}`,
3825
- deliveryUnitIdentityFixArea,
3826
- );
3827
- assertSelfCheck(
3828
- buildIdentity.version === expectedDeliveryUnit.version,
3829
- buildLabel,
3830
- `Expected version "${expectedDeliveryUnit.version}", found ${formatJson(buildIdentity.version)}`,
3831
- deliveryUnitIdentityFixArea,
3832
- );
3833
- }
3834
- assert(packageJson.name === vertical.packageName, `${vertical.id} package name is incorrect`);
3835
- assert(packageJson.scripts?.['cloudflare:deploy'] === 'cross-env ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json', `${vertical.id} must expose a portable cloudflare:deploy`);
3836
- assert(packageJson.devDependencies?.['cross-env'] === expectedCrossEnvVersion, `${vertical.id} cross-env dependency must match the portable generated-script cohort`);
3837
- assertPortableGeneratedEnvironmentScripts(packageJson, vertical.id);
3838
- assert(packageJson.devDependencies?.['@modern-js/app-tools'] === expectedModernPackageSpecifier('@modern-js/app-tools'), `${vertical.id} app-tools dependency must match package source metadata`);
3839
- if (vertical.emitsApi) {
3840
- assert(packageJson.dependencies?.['@modern-js/plugin-bff'] === expectedModernPackageSpecifier('@modern-js/plugin-bff'), `${vertical.id} plugin-bff dependency must match package source metadata`);
3841
- }
3842
- assert(packageJson.dependencies?.['@modern-js/plugin-i18n'] === expectedModernPackageSpecifier('@modern-js/plugin-i18n'), `${vertical.id} plugin-i18n dependency must match package source metadata`);
3843
- assert(packageJson.dependencies?.['@modern-js/plugin-tanstack'] === expectedModernPackageSpecifier('@modern-js/plugin-tanstack'), `${vertical.id} plugin-tanstack dependency must match package source metadata`);
3844
- assert(packageJson.dependencies?.['@modern-js/runtime'] === expectedModernPackageSpecifier('@modern-js/runtime'), `${vertical.id} runtime dependency must match package source metadata`);
3845
- if (vertical.emitsApi) {
3846
- assert(packageJson.exports?.[vertical.apiClientExport] === `./${vertical.apiClientPath}`, `${vertical.id} must export its API client`);
3847
- assert(packageJson.exports?.['./api'] === `./${vertical.apiContractPath}`, `${vertical.id} must export its API contract`);
3848
- // API protocol exclusivity (G7a): an RPC unit ships only the RPC contract
3849
- // and `${stem}-rpc-client`; a REST unit ships only the REST contract and
3850
- // `${stem}-client`. Neither may carry the other protocol's surface.
3851
- if (vertical.apiProtocol === 'rpc') {
3852
- assert(!fs.existsSync(path.join(root, `${vertical.path}/src/api/${vertical.stem}-client.ts`)), `${vertical.id} RPC unit must not emit the REST API client`);
3853
- assert(!fs.existsSync(path.join(root, `${vertical.path}/shared/api.ts`)), `${vertical.id} RPC unit must not emit the REST API contract`);
3854
- } else {
3855
- assert(!fs.existsSync(path.join(root, `${vertical.path}/src/api/${vertical.stem}-rpc-client.ts`)), `${vertical.id} REST unit must not emit the RPC API client`);
3856
- assert(!fs.existsSync(path.join(root, `${vertical.path}/shared/rpc.ts`)), `${vertical.id} REST unit must not emit the RPC API contract`);
3857
- }
3858
- }
3859
- const expectedVerticalZephyrDependencies = Object.fromEntries(
3860
- fullStackVerticals
3861
- .filter(candidate => vertical.verticalRefs.includes(candidate.id))
3862
- .map(candidate => [
3863
- candidate.zephyrAlias,
3864
- `${candidate.packageName}@workspace:*`,
3865
- ]),
3866
- );
3867
- assert(
3868
- sameJson(packageJson['zephyr:dependencies'], expectedVerticalZephyrDependencies),
3869
- `${vertical.id} Zephyr dependencies must match declared vertical refs`,
3870
- );
3871
-
3872
- const contractEntry = generatedContract.apps?.find(app => app.id === vertical.id);
3873
- assert(contractEntry?.path === vertical.path, `${vertical.id} generated contract path is incorrect`);
3874
- assert(contractEntry?.kind === 'vertical', `${vertical.id} generated contract kind is incorrect`);
3875
- assert(contractEntry?.deploy?.cloudflare?.workerName === expectedWorkerName(vertical.id), `${vertical.id} Cloudflare workerName is incorrect`);
3876
- assert(contractEntry?.deploy?.cloudflare?.publicUrlEnv === `ULTRAMODERN_PUBLIC_URL_${vertical.id.replace(/-/g, '_').toUpperCase()}`, `${vertical.id} Cloudflare public URL env is incorrect`);
3877
- assert(contractEntry?.deploy?.cloudflare?.compatibilityDate === expectedCloudflareCompatibilityDate, `${vertical.id} Cloudflare compatibilityDate is incorrect`);
3878
- assert(sameJson(contractEntry?.deploy?.cloudflare?.compatibilityFlags, expectedCloudflareCompatibilityFlags), `${vertical.id} Cloudflare compatibility flags are incorrect`);
3879
- assert(sameJson(contractEntry?.deploy?.cloudflare?.security, expectedCloudflareSecurity), `${vertical.id} Cloudflare security contract is incorrect`);
3880
- assertCloudflareQualityGates(vertical.id, contractEntry?.deploy?.cloudflare?.qualityGates);
3881
- assert(contractEntry?.deploy?.worker?.compatibilityDate === expectedCloudflareCompatibilityDate, `${vertical.id} worker compatibilityDate is incorrect`);
3882
- assert(contractEntry?.deploy?.worker?.name === expectedWorkerName(vertical.id), `${vertical.id} worker name is incorrect`);
3883
- assert(contractEntry?.config?.dev?.assetPrefix === 'app-public-origin', `${vertical.id} dev asset prefix must default to its app public origin`);
3884
- assert(contractEntry?.config?.output?.assetPrefix?.default === 'app-public-origin', `${vertical.id} asset prefix must default to its app public origin`);
3885
- assert(sameJson(contractEntry?.config?.output?.assetPrefix?.envFallbackOrder, ['MODERN_ASSET_PREFIX', 'ULTRAMODERN_ASSET_PREFIX']), `${vertical.id} asset prefix env fallback order is incorrect`);
3886
- assert(contractEntry?.config?.output?.disableTsChecker === false, `${vertical.id} must keep the framework TypeScript checker enabled`);
3887
- assert(contractEntry?.config?.performance?.readinessDiagnostics?.default === 'enabled', `${vertical.id} performance readiness diagnostics must be default-on`);
3888
- assert(contractEntry?.config?.performance?.readinessDiagnostics?.failOn === 'framework-invariant', `${vertical.id} performance readiness diagnostics must only fail framework invariants by default`);
3889
- assert(contractEntry?.config?.performance?.readinessDiagnostics?.optOut?.config === 'scripts/ultramodern-performance-readiness.config.mjs', `${vertical.id} performance readiness opt-out config is incorrect`);
3890
- if (vertical.emitsApi) {
3891
- // Per policy.ts:76 the Cloudflare proof advertises a REST readiness route
3892
- // only for `rest` units; an `rpc` unit must not carry one.
3893
- if (vertical.apiProtocol === 'rpc') {
3894
- assert(contractEntry?.deploy?.cloudflare?.routes?.apiReadiness === undefined, `${vertical.id} rpc unit must not carry a REST Cloudflare readiness route`);
3895
- } else {
3896
- assert(contractEntry?.deploy?.cloudflare?.routes?.apiReadiness === `${vertical.apiPrefix}/${vertical.stem}/readiness`, `${vertical.id} Cloudflare proof readiness route is incorrect`);
3897
- }
3898
- }
3899
- assert(contractEntry?.config?.rspack?.output?.uniqueName === vertical.mfName, `${vertical.id} Rspack uniqueName is incorrect`);
3900
- assert(contractEntry?.config?.rspack?.output?.chunkLoadingGlobal === expectedChunkLoadingGlobal(vertical.mfName), `${vertical.id} Rspack chunkLoadingGlobal is incorrect`);
3901
- assert(contractEntry?.moduleFederation?.name === vertical.mfName, `${vertical.id} MF name is incorrect`);
3902
- assert(sameJson(contractEntry?.moduleFederation?.exposes, vertical.exposes), `${vertical.id} MF exposes are incorrect`);
3903
- // The browser Module Federation DTS surface and its config file only exist
3904
- // for UI-emitting units; a headless api-only unit federates no browser types.
3905
- if (vertical.emitsUi) {
3906
- assert(contractEntry?.moduleFederation?.dts?.compilerInstance === 'effect-tsgo', `${vertical.id} must keep mandatory DTS compiler`);
3907
- assert(contractEntry?.moduleFederation?.dts?.tsConfigPath === './tsconfig.mf-types.json', `${vertical.id} must keep dedicated Module Federation DTS tsconfig`);
3908
- }
3909
- assert(sameJson(contractEntry?.moduleFederation?.verticalRefs ?? [], vertical.verticalRefs), `${vertical.id} MF verticalRefs are incorrect`);
3910
- assert(
3911
- sameJson(
3912
- (contractEntry?.moduleFederation?.remotes ?? []).map(remote => remote.id),
3913
- vertical.verticalRefs,
3914
- ),
3915
- `${vertical.id} MF consumed verticals are incorrect`,
3916
- );
3917
- // API contract surface is only present for API-bearing units.
3918
- if (vertical.emitsApi) {
3919
- assert(contractEntry?.api?.prefix === vertical.apiPrefix, `${vertical.id} API prefix is incorrect`);
3920
- assert(contractEntry?.api?.group === vertical.group, `${vertical.id} API group is incorrect`);
3921
- assert(contractEntry?.api?.runtime === 'effect', `${vertical.id} API runtime must be Effect`);
3922
- assert(contractEntry?.api?.strictEffectApproach === true, `${vertical.id} strictEffectApproach must be enabled`);
3923
- assert(contractEntry?.api?.contract === vertical.apiContractExport, `${vertical.id} API contract export is incorrect`);
3924
- assert(contractEntry?.api?.client === vertical.apiClientExport, `${vertical.id} API client export is incorrect`);
3925
- if (vertical.apiProtocol === 'rpc') {
3926
- // An `rpc` unit records the `/rpc` route/serialization; it must not carry
3927
- // REST readiness or domain-operation semantics.
3928
- assert(contractEntry?.api?.protocol === 'rpc', `${vertical.id} generated contract API protocol must be rpc`);
3929
- assert(contractEntry?.api?.rpc?.path === '/rpc', `${vertical.id} generated contract RPC route path is incorrect`);
3930
- assert(contractEntry?.api?.rpcPath === `${vertical.apiPrefix}/rpc`, `${vertical.id} generated contract RPC path is incorrect`);
3931
- } else {
3932
- assert(contractEntry?.api?.readiness?.endpoint === `/${vertical.stem}/readiness`, `${vertical.id} readiness endpoint is incorrect`);
3933
- assert(contractEntry?.api?.operations?.readiness?.path === `/${vertical.stem}/readiness`, `${vertical.id} readiness operation is missing`);
3934
- assert(contractEntry?.api?.requestContext?.propagatedHeaders?.includes('traceparent'), `${vertical.id} trace context propagation is missing`);
3935
- assert(Object.keys(contractEntry?.api?.domainOperations ?? {}).length >= 3, `${vertical.id} domain operations are missing`);
3936
- }
3937
- }
3938
- assert(contractEntry?.i18n?.languages?.includes('en') && contractEntry?.i18n?.languages?.includes('cs'), `${vertical.id} must declare i18n languages`);
3939
- assert(contractEntry?.i18n?.namespace === vertical.namespace, `${vertical.id} i18n namespace is incorrect`);
3940
- assert(
3941
- JSON.stringify(contractEntry?.i18n?.localisedUrls) === JSON.stringify(vertical.localisedUrls),
3942
- `${vertical.id} localisedUrls must come from route metadata`,
3943
- );
3944
- assert(contractEntry?.routes?.source === 'route-owned', `${vertical.id} routes must be route-owned`);
3945
- assert(contractEntry?.routes?.metadataAuthoring === 'colocated-route-meta', `${vertical.id} route metadata authoring mode is incorrect`);
3946
- assert(contractEntry?.routes?.generatedManifest === true, `${vertical.id} route metadata manifest must be generated`);
3947
- assert(contractEntry?.routes?.metadataExport === './src/routes/ultramodern-route-metadata', `${vertical.id} route metadata export is incorrect`);
3948
- assert(contractEntry?.routes?.privateByDefault === true, `${vertical.id} routes must be private by default`);
3949
- assert(contractEntry?.routes?.publicnessDefault === 'private-app-screen', `${vertical.id} route publicness default is incorrect`);
3950
- assert(JSON.stringify(contractEntry?.routes?.publicRoutes ?? []) === '[]', `${vertical.id} must not expose generated public routes by default`);
3951
- // Public head/surface and owned browser routes only exist for UI-emitting
3952
- // units; a headless api-only unit renders no route head or public surface.
3953
- if (vertical.emitsUi) {
3954
- assertPublicHeadContract(vertical.id, contractEntry?.routes?.publicHead);
3955
- assertPublicSurfaceContract(vertical.id, contractEntry?.routes?.publicSurface);
3956
- assert(
3957
- (contractEntry?.routes?.owned ?? []).every(route => route.public === false && route.indexable === false && route.publicSurface === 'private-app-screen' && typeof route.descriptionKey === 'string'),
3958
- `${vertical.id} owned routes must be non-indexable private app screens by default and include description keys`,
3959
- );
3960
- assertPublicSurfaceAssets(vertical.path, contractEntry?.routes?.publicRoutes ?? []);
3961
- }
3962
- // CSS federation (a federated browser stylesheet keyed off the federation
3963
- // entry) only applies to UI-emitting units; a headless api-only unit owns no
3964
- // vertical CSS surface.
3965
- if (vertical.emitsUi) {
3966
- assert(contractEntry?.styling?.federation?.owner?.id === vertical.id, `${vertical.id} CSS federation owner is missing`);
3967
- assert(contractEntry?.styling?.federation?.role === 'vertical-css', `${vertical.id} must own only vertical CSS`);
3968
- assert(contractEntry?.styling?.federation?.rootSelector === `[data-app-id="${vertical.id}"]`, `${vertical.id} CSS root selector is incorrect`);
3969
- assert(contractEntry?.styling?.federation?.classPrefix === `${vertical.tailwindPrefix}:`, `${vertical.id} CSS class prefix is incorrect`);
3970
- assert(contractEntry?.styling?.federation?.layers?.owned?.includes(`ultramodern-vertical-${vertical.domain}`), `${vertical.id} vertical CSS layer is missing`);
3971
- assert(!contractEntry?.styling?.federation?.layers?.owned?.includes('ultramodern-shell-base'), `${vertical.id} must not own shell base CSS`);
3972
- assert(contractEntry?.styling?.federation?.entrypoints?.federationEntry === 'src/federation-entry.tsx', `${vertical.id} CSS contract must include federation entry`);
3973
- assert(contractEntry?.styling?.federation?.assets?.shared?.some(asset => asset.endsWith('/shared-design-tokens/tokens.css')), `${vertical.id} must import shared design token CSS`);
3974
- assert(contractEntry?.styling?.federation?.dedupe?.runtimeLoad === 'once-per-content-hash', `${vertical.id} CSS dedupe strategy is incorrect`);
3975
- assert(contractEntry?.styling?.federation?.ssr?.verticalCss === 'federated-manifest-owned-css', `${vertical.id} SSR CSS loading contract is incorrect`);
3976
- }
3977
-
3978
- const topologyEntry = topology.verticals?.find(verticalEntry => verticalEntry.id === vertical.id);
3979
- assert(topologyEntry?.kind === 'vertical', `${vertical.id} topology kind is incorrect`);
3980
- assert(topologyEntry?.package === vertical.packageName, `${vertical.id} topology package is incorrect`);
3981
- assert(topologyEntry?.cloudflare?.workerName === expectedWorkerName(vertical.id), `${vertical.id} topology Cloudflare workerName is incorrect`);
3982
- assert(topologyEntry?.moduleFederation?.name === vertical.mfName, `${vertical.id} topology MF name is incorrect`);
3983
- assert(JSON.stringify(topologyEntry?.moduleFederation?.exposes) === JSON.stringify(vertical.exposes), `${vertical.id} topology exposes are incorrect`);
3984
- assert(JSON.stringify(topologyEntry?.moduleFederation?.verticalRefs ?? []) === JSON.stringify(vertical.verticalRefs), `${vertical.id} topology verticalRefs are incorrect`);
3985
- // API/BFF topology metadata only exists for API-bearing units; and the REST
3986
- // readiness/domain-operation surface is absent for the RPC protocol (G7a).
3987
- if (vertical.emitsApi) {
3988
- assert(topologyEntry?.api?.bff?.prefix === vertical.apiPrefix, `${vertical.id} topology API prefix is incorrect`);
3989
- assert(topologyEntry?.api?.bff?.strictEffectApproach === true, `${vertical.id} topology strictEffectApproach is incorrect`);
3990
- assert(topologyEntry?.api?.serverEntry === `${vertical.path}/api/index.ts`, `${vertical.id} topology server entry is incorrect`);
3991
- if (vertical.apiProtocol !== 'rpc') {
3992
- assert(topologyEntry?.api?.readiness?.endpoint === `/${vertical.stem}/readiness`, `${vertical.id} topology readiness endpoint is incorrect`);
3993
- assert(Object.keys(topologyEntry?.api?.domainOperations ?? {}).length >= 3, `${vertical.id} topology domain operations are missing`);
3994
- }
3995
- }
3996
-
3997
- if (vertical.deliveryUnit) {
3998
- const expectedDeliveryUnit = deliveryUnitBlock(
3999
- expectedDeliveryUnitFor(vertical, topologyEntry),
4000
- );
4001
- const compactAppEntry = ultramodernConfig.topology?.apps?.find(
4002
- entry => entry?.id === vertical.id,
4003
- );
4004
- // The backend-federation delivery-unit mirror only exists for API-bearing
4005
- // units; a UI-only vertical carries just the app-level delivery unit.
4006
- if (vertical.emitsApi) {
4007
- const compactBackendDeliveryUnit = deliveryUnitBlock(
4008
- compactAppEntry?.backendFederation?.deliveryUnit,
4009
- );
4010
- assertSameJson(
4011
- deliveryUnitBlock(compactAppEntry?.deliveryUnit),
4012
- compactBackendDeliveryUnit,
4013
- `${compactConfigPath} topology.apps.${vertical.id}.deliveryUnit`,
4014
- deliveryUnitIdentityFixArea,
4015
- );
4016
- assertSameJson(
4017
- compactBackendDeliveryUnit,
4018
- expectedDeliveryUnit,
4019
- `${compactConfigPath} topology.apps.${vertical.id}.backendFederation.deliveryUnit`,
4020
- deliveryUnitIdentityFixArea,
4021
- );
4022
- assertSelfCheck(
4023
- compactAppEntry?.backendFederation?.versionBoundary?.identityRoot === 'deliveryUnit',
4024
- `${compactConfigPath} topology.apps.${vertical.id}.backendFederation.versionBoundary.identityRoot`,
4025
- `Expected "deliveryUnit", found ${formatJson(compactAppEntry?.backendFederation?.versionBoundary?.identityRoot)}`,
4026
- deliveryUnitIdentityFixArea,
4027
- );
4028
- }
4029
- assertSameJson(
4030
- deliveryUnitBlock(compactAppEntry?.deliveryUnit),
4031
- expectedDeliveryUnit,
4032
- `${compactConfigPath} topology.apps.${vertical.id}.deliveryUnit`,
4033
- deliveryUnitIdentityFixArea,
4034
- );
4035
- assertSameJson(
4036
- deliveryUnitBlock(compactAppEntry?.deliveryUnit),
4037
- deliveryUnitBlock(topologyEntry?.deliveryUnit),
4038
- `${compactConfigPath} vs topology/reference-topology.json verticals.${vertical.id}.deliveryUnit`,
4039
- deliveryUnitIdentityFixArea,
4040
- );
4041
- }
4042
-
4043
- assert(ownership.owners?.some(owner => owner.id === vertical.id && owner.path === vertical.path), `${vertical.id} ownership entry is missing`);
4044
- assert(overlay.ports?.[vertical.id], `${vertical.id} development port is missing`);
4045
- if (vertical.emitsUi) {
4046
- assert(overlay.manifests?.[vertical.id]?.includes('/mf-manifest.json'), `${vertical.id} development manifest is missing`);
4047
- }
4048
- if (vertical.emitsApi) {
4049
- assert(overlay.apis?.[vertical.id]?.endsWith(vertical.apiProtocol === 'rpc' ? `${vertical.apiPrefix}/rpc` : vertical.apiPrefix), `${vertical.id} development API URL is missing`);
4050
- }
4051
- }
4052
-
4053
- // Delivery-unit identity for ALL unit kinds (G29). Every workspace app —
4054
- // shell, UI-only vertical, and API-bearing vertical — is an indivisible
4055
- // delivery unit and must carry one consistent identity record across the
4056
- // compact config, the reference topology, and its generated build artifact
4057
- // (ADR-0019: one delivery-unit record, one build marker).
4058
- for (const expectedApp of workspaceValidationContract.topology.compactConfig?.apps ?? []) {
4059
- const unitLabel = `delivery-unit identity for ${expectedApp.id}`;
4060
- const expectedDeliveryUnit = deliveryUnitBlock(expectedApp.deliveryUnit);
4061
- assertSelfCheck(
4062
- typeof expectedDeliveryUnit.unitId === 'string' && expectedDeliveryUnit.unitId.length > 0 &&
4063
- typeof expectedDeliveryUnit.buildMarker === 'string' && expectedDeliveryUnit.buildMarker.length > 0,
4064
- unitLabel,
4065
- `Every unit kind must declare a delivery-unit record; found ${formatJson(expectedApp.deliveryUnit)}`,
4066
- deliveryUnitIdentityFixArea,
4067
- );
4068
-
4069
- const compactAppEntry = ultramodernConfig.topology?.apps?.find(
4070
- entry => entry?.id === expectedApp.id,
4071
- );
4072
- assertSameJson(
4073
- compactAppEntry?.deploy?.cloudflare,
4074
- expectedApp.deploy?.cloudflare,
4075
- `${compactConfigPath} topology.apps.${expectedApp.id}.deploy.cloudflare`,
4076
- 'regenerate the app Cloudflare deployment contract; do not add local proof-only smoke checks',
4077
- );
4078
- assertSameJson(
4079
- deliveryUnitBlock(compactAppEntry?.deliveryUnit),
4080
- expectedDeliveryUnit,
4081
- `${compactConfigPath} topology.apps.${expectedApp.id}.deliveryUnit`,
4082
- deliveryUnitIdentityFixArea,
4083
- );
4084
-
4085
- const topologyUnitEntry =
4086
- expectedApp.kind === 'shell'
4087
- ? topology.shell
4088
- : topology.verticals?.find(entry => entry?.id === expectedApp.id);
4089
- assertSameJson(
4090
- deliveryUnitBlock(topologyUnitEntry?.deliveryUnit),
4091
- expectedDeliveryUnit,
4092
- `topology/reference-topology.json ${expectedApp.kind === 'shell' ? 'shell' : `verticals.${expectedApp.id}`}.deliveryUnit`,
4093
- deliveryUnitIdentityFixArea,
4094
- );
4095
-
4096
- const appPath = expectedApp.path;
4097
- const buildArtifactPath = `${appPath}/shared/ultramodern-build.json`;
4098
- assertExists(buildArtifactPath);
4099
- const buildIdentity = readJson(buildArtifactPath).deliveryUnit ?? {};
4100
- assertSelfCheck(
4101
- buildIdentity.unitId === expectedDeliveryUnit.unitId &&
4102
- buildIdentity.buildMarker === expectedDeliveryUnit.buildMarker,
4103
- `${buildArtifactPath} deliveryUnit`,
4104
- `Expected ${formatJson({ unitId: expectedDeliveryUnit.unitId, buildMarker: expectedDeliveryUnit.buildMarker })}, found ${formatJson({ unitId: buildIdentity.unitId, buildMarker: buildIdentity.buildMarker })}`,
4105
- deliveryUnitIdentityFixArea,
4106
- );
4107
- }
4108
-
4109
- console.log('UltraModern workspace scaffold validated');