@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.8 → 3.9.3-ultramodern.1

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 (280) hide show
  1. package/README.md +23 -0
  2. package/dist/cjs/backend-federation-build/codegen.js +29 -18
  3. package/dist/cjs/backend-federation-build/config.js +84 -45
  4. package/dist/cjs/backend-federation-build/emit.js +63 -22
  5. package/dist/cjs/build-config/build-environment.js +25 -162
  6. package/dist/cjs/build-config/internal-effect-discovery.js +143 -0
  7. package/dist/cjs/build-config/public.js +2 -5
  8. package/dist/cjs/build-config/remote-address.js +58 -0
  9. package/dist/cjs/cloudflare/artifacts.js +8 -19
  10. package/dist/cjs/cloudflare/delivery-unit.js +125 -43
  11. package/dist/cjs/cloudflare/index.js +14 -4
  12. package/dist/cjs/cloudflare/native-renderer.js +100 -0
  13. package/dist/cjs/cloudflare/renderer-identity.js +106 -0
  14. package/dist/cjs/cloudflare/worker-manifest.js +54 -13
  15. package/dist/cjs/cloudflare/worker-options.js +48 -0
  16. package/dist/cjs/cloudflare/wrangler-config.js +210 -13
  17. package/dist/cjs/cloudflare-builder.js +307 -89
  18. package/dist/cjs/cloudflare-output-contract.js +36 -4
  19. package/dist/cjs/cloudflare-output-verifier/identity.js +112 -23
  20. package/dist/cjs/cloudflare-output-verifier/index.js +10 -0
  21. package/dist/cjs/cloudflare-output-verifier/manifest.js +46 -0
  22. package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +7 -1
  23. package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +10 -5
  24. package/dist/cjs/config.js +10 -5
  25. package/dist/cjs/deploy-output/npmAliases.js +92 -6
  26. package/dist/cjs/deploy-output/plugin.js +27 -6
  27. package/dist/cjs/deploy-output/public-assets.js +146 -0
  28. package/dist/cjs/deploy-output/target.js +57 -1
  29. package/dist/cjs/deploy-output/trace-files.js +99 -0
  30. package/dist/cjs/module-federation-config/exposes.js +70 -0
  31. package/dist/cjs/module-federation-config/index.js +73 -0
  32. package/dist/cjs/module-federation-config/inspect.js +103 -0
  33. package/dist/cjs/module-federation-config/object-literal.js +87 -0
  34. package/dist/cjs/module-federation-config/syntax.js +151 -0
  35. package/dist/cjs/module-federation-config/types.js +18 -0
  36. package/dist/cjs/native-type-checker.js +164 -18
  37. package/dist/cjs/policy-defaults.js +110 -0
  38. package/dist/cjs/release-envelope/canonical.js +2 -9
  39. package/dist/cjs/release-envelope/contract.js +68 -0
  40. package/dist/cjs/release-envelope/framework-output.js +296 -105
  41. package/dist/cjs/release-envelope/index.js +150 -36
  42. package/dist/cjs/release-envelope/plugin.js +24 -15
  43. package/dist/cjs/release-envelope/renderer-output-stamp.js +170 -0
  44. package/dist/cjs/release-envelope/types.js +10 -8
  45. package/dist/cjs/release-identity.js +8 -5
  46. package/dist/cjs/renderer-build-identity.js +134 -0
  47. package/dist/cjs/runtime-package-resolution.js +118 -0
  48. package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  49. package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  50. package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  51. package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  52. package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  53. package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
  54. package/dist/cjs/workspace-topology.js +84 -0
  55. package/dist/esm/backend-federation-build/codegen.mjs +29 -18
  56. package/dist/esm/backend-federation-build/config.mjs +74 -38
  57. package/dist/esm/backend-federation-build/emit.mjs +64 -23
  58. package/dist/esm/build-config/build-environment.mjs +26 -160
  59. package/dist/esm/build-config/internal-effect-discovery.mjs +102 -0
  60. package/dist/esm/build-config/public.mjs +1 -1
  61. package/dist/esm/build-config/remote-address.mjs +20 -0
  62. package/dist/esm/cloudflare/artifacts.mjs +8 -19
  63. package/dist/esm/cloudflare/delivery-unit.mjs +124 -45
  64. package/dist/esm/cloudflare/index.mjs +15 -5
  65. package/dist/esm/cloudflare/native-renderer.mjs +48 -0
  66. package/dist/esm/cloudflare/renderer-identity.mjs +57 -0
  67. package/dist/esm/cloudflare/worker-manifest.mjs +55 -14
  68. package/dist/esm/cloudflare/worker-options.mjs +10 -0
  69. package/dist/esm/cloudflare/wrangler-config.mjs +203 -12
  70. package/dist/esm/cloudflare-builder.mjs +301 -89
  71. package/dist/esm/cloudflare-output-contract.mjs +29 -3
  72. package/dist/esm/cloudflare-output-verifier/identity.mjs +113 -24
  73. package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
  74. package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
  75. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  76. package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  77. package/dist/esm/config.mjs +3 -1
  78. package/dist/esm/deploy-output/npmAliases.mjs +92 -6
  79. package/dist/esm/deploy-output/plugin.mjs +24 -6
  80. package/dist/esm/deploy-output/public-assets.mjs +82 -0
  81. package/dist/esm/deploy-output/target.mjs +50 -3
  82. package/dist/esm/deploy-output/trace-files.mjs +47 -0
  83. package/dist/esm/module-federation-config/exposes.mjs +18 -0
  84. package/dist/esm/module-federation-config/index.mjs +4 -0
  85. package/dist/esm/module-federation-config/inspect.mjs +65 -0
  86. package/dist/esm/module-federation-config/object-literal.mjs +43 -0
  87. package/dist/esm/module-federation-config/syntax.mjs +101 -0
  88. package/dist/esm/module-federation-config/types.mjs +0 -0
  89. package/dist/esm/native-type-checker.mjs +158 -18
  90. package/dist/esm/policy-defaults.mjs +51 -0
  91. package/dist/esm/release-envelope/canonical.mjs +2 -9
  92. package/dist/esm/release-envelope/contract.mjs +21 -0
  93. package/dist/esm/release-envelope/framework-output.mjs +297 -106
  94. package/dist/esm/release-envelope/index.mjs +149 -38
  95. package/dist/esm/release-envelope/plugin.mjs +24 -15
  96. package/dist/esm/release-envelope/renderer-output-stamp.mjs +112 -0
  97. package/dist/esm/release-envelope/types.mjs +2 -3
  98. package/dist/esm/release-identity.mjs +8 -5
  99. package/dist/esm/renderer-build-identity.mjs +82 -0
  100. package/dist/esm/runtime-package-resolution.mjs +60 -0
  101. package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  102. package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  103. package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  104. package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  105. package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  106. package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
  107. package/dist/esm/workspace-topology.mjs +21 -0
  108. package/dist/esm-node/backend-federation-build/codegen.mjs +29 -18
  109. package/dist/esm-node/backend-federation-build/config.mjs +74 -38
  110. package/dist/esm-node/backend-federation-build/emit.mjs +64 -23
  111. package/dist/esm-node/build-config/build-environment.mjs +26 -160
  112. package/dist/esm-node/build-config/internal-effect-discovery.mjs +103 -0
  113. package/dist/esm-node/build-config/public.mjs +1 -1
  114. package/dist/esm-node/build-config/remote-address.mjs +21 -0
  115. package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
  116. package/dist/esm-node/cloudflare/delivery-unit.mjs +124 -45
  117. package/dist/esm-node/cloudflare/index.mjs +15 -5
  118. package/dist/esm-node/cloudflare/native-renderer.mjs +49 -0
  119. package/dist/esm-node/cloudflare/renderer-identity.mjs +58 -0
  120. package/dist/esm-node/cloudflare/worker-manifest.mjs +55 -14
  121. package/dist/esm-node/cloudflare/worker-options.mjs +11 -0
  122. package/dist/esm-node/cloudflare/wrangler-config.mjs +203 -12
  123. package/dist/esm-node/cloudflare-builder.mjs +301 -89
  124. package/dist/esm-node/cloudflare-output-contract.mjs +29 -3
  125. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +113 -24
  126. package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
  127. package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
  128. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  129. package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  130. package/dist/esm-node/config.mjs +3 -1
  131. package/dist/esm-node/deploy-output/npmAliases.mjs +92 -6
  132. package/dist/esm-node/deploy-output/plugin.mjs +24 -6
  133. package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
  134. package/dist/esm-node/deploy-output/target.mjs +50 -3
  135. package/dist/esm-node/deploy-output/trace-files.mjs +48 -0
  136. package/dist/esm-node/module-federation-config/exposes.mjs +19 -0
  137. package/dist/esm-node/module-federation-config/index.mjs +5 -0
  138. package/dist/esm-node/module-federation-config/inspect.mjs +66 -0
  139. package/dist/esm-node/module-federation-config/object-literal.mjs +44 -0
  140. package/dist/esm-node/module-federation-config/syntax.mjs +102 -0
  141. package/dist/esm-node/module-federation-config/types.mjs +1 -0
  142. package/dist/esm-node/native-type-checker.mjs +158 -18
  143. package/dist/esm-node/policy-defaults.mjs +52 -0
  144. package/dist/esm-node/release-envelope/canonical.mjs +2 -9
  145. package/dist/esm-node/release-envelope/contract.mjs +22 -0
  146. package/dist/esm-node/release-envelope/framework-output.mjs +297 -106
  147. package/dist/esm-node/release-envelope/index.mjs +149 -38
  148. package/dist/esm-node/release-envelope/plugin.mjs +24 -15
  149. package/dist/esm-node/release-envelope/renderer-output-stamp.mjs +113 -0
  150. package/dist/esm-node/release-envelope/types.mjs +2 -3
  151. package/dist/esm-node/release-identity.mjs +8 -5
  152. package/dist/esm-node/renderer-build-identity.mjs +83 -0
  153. package/dist/esm-node/runtime-package-resolution.mjs +61 -0
  154. package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  155. package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  156. package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  157. package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  158. package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  159. package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
  160. package/dist/esm-node/workspace-topology.mjs +22 -0
  161. package/dist/types/backend-federation-build/codegen.d.ts +30 -0
  162. package/dist/types/backend-federation-build/config.d.ts +10 -57
  163. package/dist/types/backend-federation-build/emit.d.ts +12 -2
  164. package/dist/types/backend-federation-build/index.d.ts +1 -1
  165. package/dist/types/build-config/build-environment.d.ts +1 -9
  166. package/dist/types/build-config/internal-effect-discovery.d.ts +4 -0
  167. package/dist/types/build-config/public.d.ts +1 -1
  168. package/dist/types/build-config/remote-address.d.ts +3 -0
  169. package/dist/types/cloudflare/artifacts.d.ts +7 -6
  170. package/dist/types/cloudflare/constants.d.ts +1 -1
  171. package/dist/types/cloudflare/delivery-unit.d.ts +38 -7
  172. package/dist/types/cloudflare/native-renderer.d.ts +30 -0
  173. package/dist/types/cloudflare/renderer-identity.d.ts +25 -0
  174. package/dist/types/cloudflare/worker-manifest.d.ts +17 -2
  175. package/dist/types/cloudflare/worker-options.d.ts +20 -0
  176. package/dist/types/cloudflare/wrangler-config.d.ts +55 -2
  177. package/dist/types/cloudflare-builder.d.ts +51 -8
  178. package/dist/types/cloudflare-output-contract.d.ts +5 -3
  179. package/dist/types/cloudflare-output-verifier/identity.d.ts +7 -3
  180. package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
  181. package/dist/types/config.d.ts +98 -13
  182. package/dist/types/deploy-output/plugin.d.ts +29 -9
  183. package/dist/types/deploy-output/public-assets.d.ts +51 -0
  184. package/dist/types/deploy-output/target.d.ts +28 -5
  185. package/dist/types/deploy-output/trace-files.d.ts +17 -0
  186. package/dist/types/module-federation-config/exposes.d.ts +11 -0
  187. package/dist/types/module-federation-config/index.d.ts +5 -0
  188. package/dist/types/module-federation-config/inspect.d.ts +2 -0
  189. package/dist/types/module-federation-config/object-literal.d.ts +4 -0
  190. package/dist/types/module-federation-config/syntax.d.ts +7 -0
  191. package/dist/types/module-federation-config/types.d.ts +20 -0
  192. package/dist/types/native-type-checker.d.ts +26 -3
  193. package/dist/types/policy-defaults.d.ts +56 -0
  194. package/dist/types/release-envelope/canonical.d.ts +5 -5
  195. package/dist/types/release-envelope/contract.d.mts +8 -0
  196. package/dist/types/release-envelope/framework-output.d.ts +23 -13
  197. package/dist/types/release-envelope/index.d.ts +17 -5
  198. package/dist/types/release-envelope/plugin.d.ts +10 -6
  199. package/dist/types/release-envelope/renderer-output-stamp.d.ts +34 -0
  200. package/dist/types/release-envelope/types.d.ts +42 -4
  201. package/dist/types/release-identity.d.ts +3 -2
  202. package/dist/types/renderer-build-identity.d.ts +30 -0
  203. package/dist/types/runtime-package-resolution.d.ts +28 -0
  204. package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
  205. package/dist/types/workspace-topology.d.ts +91 -0
  206. package/package.json +97 -26
  207. package/src/backend-federation-build/codegen.ts +28 -19
  208. package/src/backend-federation-build/config.ts +189 -123
  209. package/src/backend-federation-build/emit.ts +119 -38
  210. package/src/backend-federation-build/index.ts +4 -1
  211. package/src/build-config/build-environment.ts +59 -285
  212. package/src/build-config/internal-effect-discovery.ts +239 -0
  213. package/src/build-config/public.ts +0 -1
  214. package/src/build-config/remote-address.ts +33 -0
  215. package/src/cloudflare/artifacts.ts +22 -43
  216. package/src/cloudflare/delivery-unit.ts +283 -60
  217. package/src/cloudflare/index.ts +35 -19
  218. package/src/cloudflare/native-renderer.ts +117 -0
  219. package/src/cloudflare/renderer-identity.ts +171 -0
  220. package/src/cloudflare/worker-manifest.ts +108 -10
  221. package/src/cloudflare/worker-options.ts +60 -0
  222. package/src/cloudflare/wrangler-config.ts +390 -14
  223. package/src/cloudflare-builder.ts +543 -170
  224. package/src/cloudflare-output-contract.ts +45 -2
  225. package/src/cloudflare-output-verifier/identity.ts +138 -7
  226. package/src/cloudflare-output-verifier/index.ts +20 -0
  227. package/src/cloudflare-output-verifier/manifest.ts +77 -0
  228. package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
  229. package/src/cloudflare-output-verifier/worker-bundles.ts +27 -11
  230. package/src/config.ts +101 -19
  231. package/src/deploy-output/npmAliases.ts +141 -4
  232. package/src/deploy-output/plugin.ts +55 -18
  233. package/src/deploy-output/public-assets.ts +224 -0
  234. package/src/deploy-output/target.ts +97 -14
  235. package/src/deploy-output/trace-files.ts +80 -0
  236. package/src/module-federation-config/exposes.ts +46 -0
  237. package/src/module-federation-config/index.ts +22 -0
  238. package/src/module-federation-config/inspect.ts +157 -0
  239. package/src/module-federation-config/object-literal.ts +76 -0
  240. package/src/module-federation-config/syntax.ts +212 -0
  241. package/src/module-federation-config/types.ts +22 -0
  242. package/src/native-type-checker.ts +281 -26
  243. package/src/policy-defaults.ts +157 -0
  244. package/src/release-envelope/canonical.ts +6 -18
  245. package/src/release-envelope/contract.mjs +28 -0
  246. package/src/release-envelope/framework-output.ts +681 -156
  247. package/src/release-envelope/index.ts +315 -60
  248. package/src/release-envelope/plugin.ts +54 -38
  249. package/src/release-envelope/renderer-output-stamp.ts +210 -0
  250. package/src/release-envelope/types.ts +75 -6
  251. package/src/release-identity.ts +19 -2
  252. package/src/renderer-build-identity.ts +168 -0
  253. package/src/runtime-package-resolution.ts +115 -0
  254. package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  255. package/src/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  256. package/src/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  257. package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  258. package/src/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  259. package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
  260. package/src/workspace-topology.ts +125 -0
  261. package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +0 -11
  262. package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +0 -281
  263. package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  264. package/dist/cjs/templates/cloudflare-worker-path.mjs +0 -98
  265. package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +0 -11
  266. package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +0 -281
  267. package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  268. package/dist/esm/templates/cloudflare-worker-path.mjs +0 -98
  269. package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +0 -11
  270. package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +0 -281
  271. package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  272. package/dist/esm-node/templates/cloudflare-worker-path.mjs +0 -98
  273. package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +0 -5
  274. package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +0 -48
  275. package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +0 -13
  276. package/dist/types/templates/cloudflare-worker-path.d.mts +0 -21
  277. package/src/templates/cloudflare-worker-fs-promises.mjs +0 -11
  278. package/src/templates/cloudflare-worker-loadable-server.mjs +0 -281
  279. package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  280. package/src/templates/cloudflare-worker-path.mjs +0 -98
@@ -2,14 +2,34 @@ import "node:module";
2
2
  import { createHash } from "node:crypto";
3
3
  import promises from "node:fs/promises";
4
4
  import node_path from "node:path";
5
- import { BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, ULTRAMODERN_BUILD_ARTIFACT_FILE, isUltramodernBuildArtifact } from "@modern-js/backend-federation-contracts";
5
+ import { isDeepStrictEqual } from "node:util";
6
+ import { BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, ULTRAMODERN_BUILD_ARTIFACT_FILE, ULTRAMODERN_BUILD_ARTIFACT_PATH, assertRendererProfileCompatibility, isUltramodernBuildArtifact } from "@modern-js/backend-federation-contracts";
7
+ import { readBuildIdentity } from "../backend-federation-build/config.mjs";
8
+ import { resolveTopologyDeliveryUnit, resolveTopologyReleaseRole, resolveWorkerDeliveryUnitStamp } from "../cloudflare/delivery-unit.mjs";
6
9
  import { canonicalSerializeMicroVerticalReleaseEnvelope, createMicroVerticalReleaseEnvelope, verifyMicroVerticalReleaseEnvelope } from "./index.mjs";
10
+ import { SHELL_RELEASE_ENVELOPE_KIND } from "./types.mjs";
7
11
  const MICROVERTICAL_RELEASE_ENVELOPE_PATH = 'release/microvertical-release-envelope.json';
8
12
  const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH = 'release/microvertical-release-identity-carriers.json';
9
13
  const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_KIND = 'ultramodern-release-identity-carriers';
10
14
  const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_SCHEMA_VERSION = 1;
11
15
  const COMPILED_MODULE_PATTERN = /\.(?:c|m)?js$/u;
12
16
  const EFFECT_BFF_WORKER_PATTERN = /^worker\/__modern_bff_effect\.(?:c|m)?js$/u;
17
+ const EFFECT_BFF_WORKER_SUPPORT_PATTERN = /^worker\/__modern_worker_(?:runtime|shared)_*\.(?:c|m)?js$/u;
18
+ const isEffectBffWorkerArtifact = (logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath) || EFFECT_BFF_WORKER_SUPPORT_PATTERN.test(logicalPath);
19
+ const effectBffWorkerArtifacts = (files)=>files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath)) ? files.filter(isEffectBffWorkerArtifact) : [];
20
+ const CRAWLER_POLICY_PATH = 'public/robots.txt';
21
+ const isPublicMetadataPath = (logicalPath)=>'public/_headers' === logicalPath || /^public\/\.well-known\/[^/]+\.json$/u.test(logicalPath);
22
+ const PUBLIC_BUILD_ARTIFACT_PATH = `public/${ULTRAMODERN_BUILD_ARTIFACT_FILE}`;
23
+ const DECLARED_PUBLIC_ASSET_RUNTIME = 'public-asset';
24
+ const resolveDeclaredPublicAssets = (files, declaredPublicAssets, target)=>{
25
+ const staged = new Set(files);
26
+ const declared = new Set();
27
+ for (const logicalPath of declaredPublicAssets){
28
+ if (!logicalPath.startsWith('public/') || !staged.has(logicalPath)) throw new Error(`[ultramodern-release-envelope] final ${target} staging has no declared public asset file ${JSON.stringify(logicalPath)}.`);
29
+ declared.add(logicalPath);
30
+ }
31
+ return declared;
32
+ };
13
33
  const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
14
34
  const flattenStrings = (value)=>'string' == typeof value ? [
15
35
  value
@@ -23,7 +43,12 @@ const collectNestedStrings = (value, key)=>{
23
43
  ]);
24
44
  };
25
45
  const emptyProducerRouteModules = async (distDirectory, files, manifest, manifestLogicalPath)=>{
26
- if (!isRecord(manifest) || !Array.isArray(manifest.exposes) || 0 !== manifest.exposes.length || !Array.isArray(manifest.remotes) || 0 !== manifest.remotes.length || !isRecord(manifest.metaData) || !isRecord(manifest.metaData.remoteEntry) || '' !== manifest.metaData.remoteEntry.name || '' !== manifest.metaData.remoteEntry.path || 'global' !== manifest.metaData.remoteEntry.type) return [];
46
+ if (!isRecord(manifest) || !Array.isArray(manifest.exposes) || 0 !== manifest.exposes.length || !Array.isArray(manifest.remotes) || manifest.remotes.some((remote)=>!isRecord(remote) || [
47
+ 'federationContainerName',
48
+ 'moduleName',
49
+ 'alias',
50
+ 'entry'
51
+ ].some((key)=>'string' != typeof remote[key] || 0 === remote[key].trim().length)) || !isRecord(manifest.metaData) || !isRecord(manifest.metaData.remoteEntry) || '' !== manifest.metaData.remoteEntry.name || '' !== manifest.metaData.remoteEntry.path || 'global' !== manifest.metaData.remoteEntry.type) return [];
27
52
  const publicPath = manifest.metaData.publicPath;
28
53
  const safeReference = (value)=>'string' == typeof value && value.length > 0 && !/[\\\\%?#\s]/u.test(value) && !value.split('/').some((segment)=>'.' === segment || '..' === segment);
29
54
  if (!safeReference(publicPath)) return [];
@@ -124,11 +149,14 @@ const writeReleaseEnvelope = async (artifactRoot, envelope)=>{
124
149
  if (!envelopePath) throw new Error('[ultramodern-release-envelope] could not resolve release envelope path.');
125
150
  await promises.writeFile(envelopePath, `${canonicalSerializeMicroVerticalReleaseEnvelope(envelope)}\n`);
126
151
  };
127
- const readBuildArtifact = async (distDirectory)=>{
152
+ const readBuildArtifact = async (distDirectory, requirePromotable = true)=>{
128
153
  const artifactPath = node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE);
129
154
  const artifact = await readJson(artifactPath);
130
155
  if (!isUltramodernBuildArtifact(artifact)) throw new Error(`[ultramodern-release-envelope] ${ULTRAMODERN_BUILD_ARTIFACT_FILE} is missing or invalid.`);
131
- if ('workspace' === artifact.deliveryUnit.sourceRevision) throw new Error('[ultramodern-release-envelope] sourceRevision "workspace" cannot produce a promotable full-stack envelope.');
156
+ if ('workspace' === artifact.deliveryUnit.sourceRevision) {
157
+ if (!requirePromotable) return;
158
+ throw new Error('[ultramodern-release-envelope] sourceRevision "workspace" cannot produce a promotable envelope.');
159
+ }
132
160
  return artifact;
133
161
  };
134
162
  const assertIdentityBlock = (value, identity, location, includeReleaseVersion)=>{
@@ -197,7 +225,7 @@ const releaseArtifact = (logicalPath, runtime)=>({
197
225
  const writeReleaseIdentityCarrierMetadata = async (artifactRoot, identity, carrierPaths)=>{
198
226
  const surfacesByPath = new Map();
199
227
  for (const [surface, logicalPaths] of Object.entries(carrierPaths)){
200
- if (0 === logicalPaths.length) throw new Error(`[ultramodern-release-envelope] ${surface} has no declared release-identity carrier artifact.`);
228
+ if (0 === logicalPaths.length && 'ssr' !== surface && 'uiClient' !== surface) throw new Error(`[ultramodern-release-envelope] ${surface} has no declared release-identity carrier artifact.`);
201
229
  for (const logicalPath of logicalPaths){
202
230
  const surfaces = surfacesByPath.get(logicalPath) ?? new Set();
203
231
  surfaces.add(surface);
@@ -237,12 +265,14 @@ const writeReleaseIdentityCarrierMetadata = async (artifactRoot, identity, carri
237
265
  }, null, 2)}\n`);
238
266
  return releaseArtifact(MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH, 'release-identity-metadata');
239
267
  };
240
- const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
268
+ const createReleaseArtifactInputs = async (distDirectory, identity, target, apiOnly, uiOnly, shellConsumer)=>{
269
+ const backendProducer = !uiOnly && !shellConsumer;
241
270
  const files = await collectFiles(distDirectory);
242
- if ('node' === target && files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath))) throw new Error('[ultramodern-release-envelope] Node target conflicts with a Cloudflare Effect API/BFF worker artifact; resolve the canonical deploy target instead of emitting a Node envelope for Cloudflare output.');
271
+ const effectWorkerPaths = effectBffWorkerArtifacts(files);
272
+ if ('node' === target && files.some(isEffectBffWorkerArtifact)) throw new Error('[ultramodern-release-envelope] Node target conflicts with a Cloudflare Effect API/BFF worker artifact; resolve the canonical deploy target instead of emitting a Node envelope for Cloudflare output.');
243
273
  const uiClientPaths = files.filter((logicalPath)=>{
244
274
  const topLevel = logicalPath.split('/')[0];
245
- return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel || [
275
+ return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel && logicalPath !== CRAWLER_POLICY_PATH && logicalPath !== PUBLIC_BUILD_ARTIFACT_PATH && !isPublicMetadataPath(logicalPath) || [
246
276
  'index.html',
247
277
  'mf-manifest.json',
248
278
  'mf-stats.json',
@@ -251,16 +281,19 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
251
281
  'loadable-stats.json'
252
282
  ].includes(logicalPath);
253
283
  });
254
- const ssrPaths = 'node' === target ? files.filter((logicalPath)=>logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter((logicalPath)=>logicalPath.startsWith('worker/') && COMPILED_MODULE_PATTERN.test(logicalPath) && !EFFECT_BFF_WORKER_PATTERN.test(logicalPath));
284
+ const ssrPaths = 'node' === target ? files.filter((logicalPath)=>logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter((logicalPath)=>logicalPath.startsWith('worker/') && COMPILED_MODULE_PATTERN.test(logicalPath) && !effectWorkerPaths.includes(logicalPath));
255
285
  const nodeApiEntryPaths = files.filter((logicalPath)=>logicalPath.startsWith('api/') && COMPILED_MODULE_PATTERN.test(logicalPath));
256
- const apiBackendPaths = 'node' === target ? files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath));
257
- if (0 === uiClientPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no UI/client artifacts.`);
258
- if (0 === ssrPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no ${'node' === target ? 'Node' : 'workerd'} SSR artifacts.`);
259
- if ('node' === target && 0 === nodeApiEntryPaths.length || 0 === apiBackendPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no actual ${'node' === target ? 'compiled Node Effect API artifact' : 'Effect API/BFF worker artifact'}.`);
260
- const clientExecutionPaths = await manifestReferencedClientModules(distDirectory, files);
261
- if (0 === clientExecutionPaths.length) throw new Error('[ultramodern-release-envelope] UI/client manifest references no compiled execution module.');
262
- const ssrExecutionPaths = await routeReferencedSsrModules(distDirectory, files, target);
263
- if (0 === ssrExecutionPaths.length) throw new Error(`[ultramodern-release-envelope] route manifest references no emitted ${'node' === target ? 'Node' : 'Cloudflare'} SSR execution module.`);
286
+ const apiBackendPaths = 'node' === target ? files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath)) : effectWorkerPaths;
287
+ if (backendProducer && 'cloudflare' === target && 0 === effectWorkerPaths.length) throw new Error('[ultramodern-release-envelope] cloudflare MicroVertical has no actual Effect API/BFF worker artifact.');
288
+ if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.length > 0)) throw new Error('[ultramodern-release-envelope] API-only MicroVertical emitted an undeclared UI/client or SSR surface.');
289
+ if (!apiOnly && 0 === uiClientPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no UI/client artifacts.`);
290
+ if (!apiOnly && 0 === ssrPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no ${'node' === target ? 'Node' : 'workerd'} SSR artifacts.`);
291
+ if (uiOnly && (apiBackendPaths.length > 0 || nodeApiEntryPaths.length > 0)) throw new Error('[ultramodern-release-envelope] UI-only application emitted an undeclared API/backend artifact.');
292
+ if (backendProducer && ('node' === target && 0 === nodeApiEntryPaths.length || 0 === apiBackendPaths.length)) throw new Error(`[ultramodern-release-envelope] ${target} MicroVertical has no actual ${'node' === target ? 'compiled Node Effect API artifact' : 'Effect API/BFF worker artifact'}.`);
293
+ const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(distDirectory, files);
294
+ if (!apiOnly && 0 === clientExecutionPaths.length) throw new Error('[ultramodern-release-envelope] UI/client manifest references no compiled execution module.');
295
+ const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(distDirectory, files, target);
296
+ if (!apiOnly && 0 === ssrExecutionPaths.length) throw new Error(`[ultramodern-release-envelope] route manifest references no emitted ${'node' === target ? 'Node' : 'Cloudflare'} SSR execution module.`);
264
297
  const runtimeByPath = new Map();
265
298
  const add = (paths, runtime)=>{
266
299
  for (const logicalPath of paths){
@@ -270,14 +303,25 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
270
303
  }
271
304
  };
272
305
  add(uiClientPaths, 'browser');
306
+ add(files.filter((logicalPath)=>logicalPath === CRAWLER_POLICY_PATH), 'crawler-policy');
307
+ add(files.filter(isPublicMetadataPath), 'public-metadata');
273
308
  add(ssrPaths, 'node' === target ? 'nodejs' : 'workerd');
274
309
  add(apiBackendPaths, 'node' === target ? 'nodejs' : 'workerd-effect');
275
310
  add([
276
- BACKEND_FEDERATION_MANIFEST_FILE
277
- ], 'module-federation-manifest');
278
- add([
279
- BACKEND_FEDERATION_REMOTE_ENTRY_FILE
280
- ], 'node' === target ? 'nodejs' : 'commonjs-module');
311
+ ULTRAMODERN_BUILD_ARTIFACT_FILE
312
+ ], 'release-identity-metadata');
313
+ add(files.filter((logicalPath)=>logicalPath === PUBLIC_BUILD_ARTIFACT_PATH), 'release-identity-metadata');
314
+ if (backendProducer) {
315
+ add([
316
+ BACKEND_FEDERATION_MANIFEST_FILE
317
+ ], 'module-federation-manifest');
318
+ add([
319
+ BACKEND_FEDERATION_REMOTE_ENTRY_FILE
320
+ ], 'node' === target ? 'nodejs' : 'commonjs-module');
321
+ }
322
+ if (shellConsumer) {
323
+ for (const logicalPath of files)if (!runtimeByPath.has(logicalPath)) runtimeByPath.set(logicalPath, `${target}-deployment`);
324
+ }
281
325
  const surfaces = {
282
326
  uiClient: [
283
327
  ...uiClientPaths
@@ -288,20 +332,26 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
288
332
  apiBackend: [
289
333
  ...apiBackendPaths
290
334
  ].sort((left, right)=>left.localeCompare(right)),
291
- backendFederation: {
292
- manifest: BACKEND_FEDERATION_MANIFEST_FILE,
293
- container: BACKEND_FEDERATION_REMOTE_ENTRY_FILE
294
- }
335
+ ...backendProducer ? {
336
+ backendFederation: {
337
+ manifest: BACKEND_FEDERATION_MANIFEST_FILE,
338
+ container: BACKEND_FEDERATION_REMOTE_ENTRY_FILE
339
+ }
340
+ } : {}
295
341
  };
296
342
  const artifacts = [
297
343
  ...runtimeByPath
298
344
  ].map(([logicalPath, runtime])=>releaseArtifact(logicalPath, runtime)).sort((left, right)=>left.logicalPath.localeCompare(right.logicalPath));
299
345
  const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(distDirectory, identity, {
300
- apiBackend: apiBackendPaths,
301
- backendFederation: [
302
- BACKEND_FEDERATION_MANIFEST_FILE,
303
- BACKEND_FEDERATION_REMOTE_ENTRY_FILE
304
- ],
346
+ ...apiBackendPaths.length > 0 ? {
347
+ apiBackend: apiBackendPaths
348
+ } : {},
349
+ ...backendProducer ? {
350
+ backendFederation: [
351
+ BACKEND_FEDERATION_MANIFEST_FILE,
352
+ BACKEND_FEDERATION_REMOTE_ENTRY_FILE
353
+ ]
354
+ } : {},
305
355
  ssr: [
306
356
  ...new Set([
307
357
  ...ssrExecutionPaths,
@@ -318,6 +368,25 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
318
368
  surfaces
319
369
  };
320
370
  };
371
+ const releaseEnvelopeSurfaces = (surfaces, shellConsumer)=>{
372
+ if (!shellConsumer) return {
373
+ surfaces
374
+ };
375
+ const { uiClient, ssr, apiBackend } = surfaces;
376
+ return {
377
+ kind: SHELL_RELEASE_ENVELOPE_KIND,
378
+ surfaces: {
379
+ uiClient,
380
+ ssr,
381
+ apiBackend
382
+ }
383
+ };
384
+ };
385
+ const assertShellBuildIdentity = async (appDirectory, distDirectory)=>{
386
+ const source = await readBuildIdentity(appDirectory);
387
+ const finalized = await pathExists(node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE)) ? await resolveWorkerDeliveryUnitStamp(appDirectory, distDirectory) : void 0;
388
+ if (!source.artifact || !finalized || finalized.unitId !== source.unitId) throw new Error('[ultramodern-release-envelope] Shell is missing its native compiled delivery-unit identity.');
389
+ };
321
390
  const pathExists = async (filePath)=>{
322
391
  try {
323
392
  await promises.access(filePath);
@@ -326,36 +395,79 @@ const pathExists = async (filePath)=>{
326
395
  return false;
327
396
  }
328
397
  };
329
- const emitFrameworkMicroVerticalReleaseEnvelope = async ({ apiOnly, distDirectory, target })=>{
330
- if (apiOnly) return;
398
+ const emitFrameworkMicroVerticalReleaseEnvelope = async ({ apiOnly, appDirectory, distDirectory, requirePromotable = true, expectedRendererProfile, role, target })=>{
399
+ const declaredRole = appDirectory ? await resolveTopologyReleaseRole(appDirectory) : void 0;
400
+ if (void 0 !== role && void 0 !== declaredRole && role !== declaredRole) throw new Error('[ultramodern-release-envelope] Configured release role disagrees with the declared topology app.');
401
+ const shellConsumer = (declaredRole ?? role ?? 'microvertical') === 'shell';
331
402
  const backendManifestPath = node_path.join(distDirectory, BACKEND_FEDERATION_MANIFEST_FILE);
332
403
  const backendContainerPath = node_path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE);
333
404
  const [hasBackendManifest, hasBackendContainer] = await Promise.all([
334
405
  pathExists(backendManifestPath),
335
406
  pathExists(backendContainerPath)
336
407
  ]);
337
- if (!hasBackendManifest && !hasBackendContainer) return;
338
- if (!hasBackendManifest || !hasBackendContainer) throw new Error('[ultramodern-release-envelope] full-stack MicroVertical backend federation manifest and container must be emitted together.');
339
- const buildArtifact = await readBuildArtifact(distDirectory);
408
+ if (shellConsumer) {
409
+ if (apiOnly || hasBackendManifest || hasBackendContainer || !appDirectory) throw new Error('[ultramodern-release-envelope] Shell requires a full-stack application source and cannot declare a backend federation producer.');
410
+ await assertShellBuildIdentity(appDirectory, distDirectory);
411
+ } else if (!apiOnly && !hasBackendManifest && !hasBackendContainer) {
412
+ const source = appDirectory ? node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH) : void 0;
413
+ const participationPaths = [
414
+ node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE),
415
+ backendManifestPath,
416
+ backendContainerPath,
417
+ ...source ? [
418
+ source,
419
+ source.replace(/\.json$/u, '.ts')
420
+ ] : []
421
+ ];
422
+ const hasParticipationEntry = (await Promise.all(participationPaths.map(async (filePath)=>{
423
+ try {
424
+ await promises.lstat(filePath);
425
+ return true;
426
+ } catch (error) {
427
+ if (isMissingPathError(error)) return false;
428
+ throw error;
429
+ }
430
+ }))).some(Boolean);
431
+ if (!hasParticipationEntry && (!appDirectory || !await resolveTopologyDeliveryUnit(appDirectory))) return;
432
+ }
433
+ const buildArtifact = await readBuildArtifact(distDirectory, requirePromotable);
434
+ const declared = appDirectory && buildArtifact ? await resolveWorkerDeliveryUnitStamp(appDirectory, distDirectory) : void 0;
435
+ const uiOnly = Boolean(declared?.surfaces.ui && !declared.surfaces.api);
436
+ if (uiOnly && (apiOnly || hasBackendManifest || hasBackendContainer)) throw new Error('[ultramodern-release-envelope] UI-only application cannot declare API-only configuration or backend federation output.');
437
+ if (!hasBackendManifest && !hasBackendContainer && !apiOnly && !buildArtifact) return;
438
+ if (!shellConsumer && !uiOnly && (!hasBackendManifest || !hasBackendContainer)) throw new Error('[ultramodern-release-envelope] MicroVertical backend federation manifest and container must be emitted together.');
439
+ if (!buildArtifact) return;
440
+ const builtUi = buildArtifact.surfaces.ui;
441
+ if (Boolean(builtUi) === apiOnly) throw new Error('[ultramodern-release-envelope] build artifact UI surface must match the configured application surface.');
442
+ if (void 0 !== expectedRendererProfile) assertRendererProfileCompatibility(expectedRendererProfile, builtUi?.rendererProfile);
443
+ const ui = builtUi ? {
444
+ rendererIdentity: builtUi.rendererIdentity,
445
+ rendererProfile: builtUi.rendererProfile,
446
+ routerBindings: builtUi.routerBindings
447
+ } : void 0;
340
448
  const identity = {
341
449
  unitId: buildArtifact.deliveryUnit.unitId,
342
450
  buildMarker: buildArtifact.deliveryUnit.buildMarker,
343
451
  sourceRevision: buildArtifact.deliveryUnit.sourceRevision,
344
452
  releaseVersion: buildArtifact.deliveryUnit.version
345
453
  };
346
- await readBackendManifest(distDirectory, identity);
347
- const { artifacts, surfaces } = await createReleaseArtifactInputs(distDirectory, identity, target);
454
+ if (!shellConsumer && !uiOnly) await readBackendManifest(distDirectory, identity);
455
+ const { artifacts, surfaces } = await createReleaseArtifactInputs(distDirectory, identity, target, apiOnly, uiOnly, shellConsumer);
348
456
  const envelope = await createMicroVerticalReleaseEnvelope({
349
457
  artifactRoot: distDirectory,
350
458
  target,
351
459
  identity,
460
+ ...ui ? {
461
+ ui
462
+ } : {},
352
463
  artifacts,
353
- surfaces
464
+ ...releaseEnvelopeSurfaces(surfaces, shellConsumer)
354
465
  });
466
+ await verifyBuildArtifactCarriers(distDirectory, envelope);
355
467
  await writeReleaseEnvelope(distDirectory, envelope);
356
468
  return envelope;
357
469
  };
358
- const readFrameworkMicroVerticalReleaseEnvelope = async ({ artifactRoot, expectedTarget, logicalPathForArtifact, required = false })=>{
470
+ const readFrameworkMicroVerticalReleaseEnvelope = async ({ artifactRoot, expectedTarget, logicalPathForArtifact, expectedRendererProfile, required = false })=>{
359
471
  const envelopePath = await resolveSafeReleaseEnvelopePath({
360
472
  artifactRoot,
361
473
  createDirectory: false
@@ -364,30 +476,55 @@ const readFrameworkMicroVerticalReleaseEnvelope = async ({ artifactRoot, expecte
364
476
  if (required) throw new Error(`[ultramodern-release-envelope] required envelope is missing at ${MICROVERTICAL_RELEASE_ENVELOPE_PATH}.`);
365
477
  return;
366
478
  }
367
- return verifyMicroVerticalReleaseEnvelope(await readJson(envelopePath), {
479
+ const envelope = await verifyMicroVerticalReleaseEnvelope(await readJson(envelopePath), {
368
480
  artifactRoot,
369
481
  ...expectedTarget ? {
370
482
  expectedTarget
371
483
  } : {},
372
484
  ...logicalPathForArtifact ? {
373
485
  logicalPathForArtifact
486
+ } : {},
487
+ ...expectedRendererProfile ? {
488
+ expectedRendererProfile
374
489
  } : {}
375
490
  });
491
+ await verifyBuildArtifactCarriers(artifactRoot, envelope, logicalPathForArtifact);
492
+ return envelope;
493
+ };
494
+ const verifyBuildArtifactCarriers = async (artifactRoot, envelope, logicalPathForArtifact)=>{
495
+ const carriers = envelope.artifacts.filter((artifact)=>artifact.logicalPath === ULTRAMODERN_BUILD_ARTIFACT_FILE || artifact.logicalPath === PUBLIC_BUILD_ARTIFACT_PATH);
496
+ if (0 === carriers.length) throw new Error('[ultramodern-release-envelope] immutable build artifact must be bound to the release envelope.');
497
+ for (const artifact of carriers){
498
+ if ('file' !== artifact.kind || 'release-identity-metadata' !== artifact.runtime) throw new Error('[ultramodern-release-envelope] immutable build artifact must be a real file with runtime "release-identity-metadata".');
499
+ const logicalPath = logicalPathForArtifact?.(artifact) ?? artifact.logicalPath;
500
+ const built = await readJson(node_path.join(artifactRoot, logicalPath));
501
+ if (!isUltramodernBuildArtifact(built)) throw new Error('[ultramodern-release-envelope] bound build artifact is invalid.');
502
+ assertIdentityBlock(built.deliveryUnit, envelope.identity, 'buildArtifact.deliveryUnit', true);
503
+ if (Boolean(built.surfaces.ui) !== Boolean(envelope.ui)) throw new Error('[ultramodern-release-envelope] bound build artifact UI surface differs from the release envelope.');
504
+ if (envelope.ui && built.surfaces.ui) {
505
+ if (!isDeepStrictEqual(envelope.ui.routerBindings, built.surfaces.ui.routerBindings)) throw new Error('[ultramodern-release-envelope] buildArtifact.ui.routerBindings differ from the release envelope.');
506
+ assertRendererProfileCompatibility(envelope.ui.rendererProfile, built.surfaces.ui.rendererProfile);
507
+ for (const field of [
508
+ 'renderer',
509
+ 'protocolVersion',
510
+ 'appId',
511
+ 'entryName',
512
+ 'buildId'
513
+ ])if (envelope.ui.rendererIdentity[field] !== built.surfaces.ui.rendererIdentity[field]) throw new Error(`[ultramodern-release-envelope] buildArtifact.ui.rendererIdentity.${field} differs from the release envelope.`);
514
+ }
515
+ }
376
516
  };
377
517
  const verifyBuildOutputReleaseEnvelope = async (distDirectory, expectedTarget)=>{
378
- const [hasEnvelope, hasBuildArtifact, hasBackendManifest, hasBackendEntry] = await Promise.all([
518
+ const [hasEnvelope, hasBuildArtifact] = await Promise.all([
379
519
  pathExists(node_path.join(distDirectory, MICROVERTICAL_RELEASE_ENVELOPE_PATH)),
380
- pathExists(node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE)),
381
- pathExists(node_path.join(distDirectory, BACKEND_FEDERATION_MANIFEST_FILE)),
382
- pathExists(node_path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE))
520
+ pathExists(node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE))
383
521
  ]);
384
- const isFullStackMicroVertical = hasBuildArtifact && (hasBackendManifest || hasBackendEntry);
385
522
  return readFrameworkMicroVerticalReleaseEnvelope({
386
523
  artifactRoot: distDirectory,
387
524
  ...expectedTarget ? {
388
525
  expectedTarget
389
526
  } : {},
390
- required: hasEnvelope || isFullStackMicroVertical
527
+ required: hasEnvelope || hasBuildArtifact
391
528
  });
392
529
  };
393
530
  const verifyNodeReleaseEnvelopeStaging = async ({ outputDirectory })=>readFrameworkMicroVerticalReleaseEnvelope({
@@ -395,21 +532,26 @@ const verifyNodeReleaseEnvelopeStaging = async ({ outputDirectory })=>readFramew
395
532
  expectedTarget: 'node',
396
533
  required: true
397
534
  });
398
- const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=>{
535
+ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity, apiOnly, uiOnly, declaredPublicAssets, shellConsumer)=>{
536
+ const backendProducer = !uiOnly && !shellConsumer;
399
537
  const files = await collectFiles(outputDirectory);
538
+ const declaredPublicAssetPaths = resolveDeclaredPublicAssets(files, declaredPublicAssets, 'Node');
400
539
  for (const requiredPath of [
401
540
  'index.js',
402
541
  'package.json',
403
- BACKEND_FEDERATION_MANIFEST_FILE,
404
- BACKEND_FEDERATION_REMOTE_ENTRY_FILE
542
+ ...backendProducer ? [
543
+ BACKEND_FEDERATION_MANIFEST_FILE,
544
+ BACKEND_FEDERATION_REMOTE_ENTRY_FILE
545
+ ] : []
405
546
  ])if (!files.includes(requiredPath)) throw new Error(`[ultramodern-release-envelope] final Node staging is missing ${requiredPath}.`);
406
547
  const backendFederationPaths = new Set([
407
548
  BACKEND_FEDERATION_MANIFEST_FILE,
408
549
  BACKEND_FEDERATION_REMOTE_ENTRY_FILE
409
550
  ]);
551
+ if (shellConsumer && files.some((logicalPath)=>backendFederationPaths.has(logicalPath))) throw new Error('[ultramodern-release-envelope] final Shell Node staging cannot contain a backend federation producer artifact.');
410
552
  const uiClientPaths = files.filter((logicalPath)=>{
411
553
  const topLevel = logicalPath.split('/')[0];
412
- return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel || [
554
+ return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel && logicalPath !== CRAWLER_POLICY_PATH && logicalPath !== PUBLIC_BUILD_ARTIFACT_PATH && !isPublicMetadataPath(logicalPath) && !declaredPublicAssetPaths.has(logicalPath) || [
413
555
  'index.html',
414
556
  'mf-manifest.json',
415
557
  'mf-stats.json',
@@ -420,6 +562,8 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
420
562
  });
421
563
  const ssrPaths = files.filter((logicalPath)=>'index.js' === logicalPath || logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath));
422
564
  const apiBackendPaths = files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath));
565
+ if (uiOnly && (apiBackendPaths.length > 0 || files.some((logicalPath)=>backendFederationPaths.has(logicalPath)))) throw new Error('[ultramodern-release-envelope] final UI-only Node staging contains an undeclared API/backend or backend federation artifact.');
566
+ if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.some((path)=>'index.js' !== path))) throw new Error('[ultramodern-release-envelope] final API-only Node staging contains an undeclared UI/client or SSR surface.');
423
567
  for (const [surface, paths] of [
424
568
  [
425
569
  'UI/client',
@@ -427,32 +571,45 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
427
571
  ],
428
572
  [
429
573
  'Node SSR',
430
- ssrPaths
574
+ apiOnly ? [] : ssrPaths
431
575
  ],
432
576
  [
433
577
  'Node API/backend',
434
578
  apiBackendPaths
435
579
  ]
436
- ])if (0 === paths.length) throw new Error(`[ultramodern-release-envelope] final Node staging has no ${surface} artifacts.`);
437
- const clientExecutionPaths = await manifestReferencedClientModules(outputDirectory, files);
438
- const ssrExecutionPaths = await routeReferencedSsrModules(outputDirectory, files, 'node');
439
- if (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length) throw new Error('[ultramodern-release-envelope] final Node manifests must reference emitted UI/client and SSR execution modules.');
580
+ ])if (0 === paths.length && ('Node API/backend' === surface ? backendProducer : !apiOnly)) throw new Error(`[ultramodern-release-envelope] final Node staging has no ${surface} artifacts.`);
581
+ const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(outputDirectory, files);
582
+ const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(outputDirectory, files, 'node');
583
+ if (!apiOnly && (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length)) throw new Error('[ultramodern-release-envelope] final Node manifests must reference emitted UI/client and SSR execution modules.');
440
584
  const runtimeByPath = new Map(files.filter((logicalPath)=>!backendFederationPaths.has(logicalPath)).map((logicalPath)=>[
441
585
  logicalPath,
442
586
  'nodejs-deployment'
443
587
  ]));
444
588
  for (const logicalPath of uiClientPaths)runtimeByPath.set(logicalPath, 'browser');
445
- for (const logicalPath of ssrPaths)runtimeByPath.set(logicalPath, 'nodejs');
589
+ for (const logicalPath of declaredPublicAssetPaths)runtimeByPath.set(logicalPath, DECLARED_PUBLIC_ASSET_RUNTIME);
590
+ if (files.includes(CRAWLER_POLICY_PATH)) runtimeByPath.set(CRAWLER_POLICY_PATH, 'crawler-policy');
591
+ for (const logicalPath of files.filter(isPublicMetadataPath))runtimeByPath.set(logicalPath, 'public-metadata');
592
+ for (const logicalPath of apiOnly ? [] : ssrPaths)runtimeByPath.set(logicalPath, 'nodejs');
446
593
  for (const logicalPath of apiBackendPaths)runtimeByPath.set(logicalPath, 'nodejs');
447
- runtimeByPath.set(BACKEND_FEDERATION_MANIFEST_FILE, 'module-federation-manifest');
448
- runtimeByPath.set(BACKEND_FEDERATION_REMOTE_ENTRY_FILE, 'nodejs');
594
+ if (backendProducer) {
595
+ runtimeByPath.set(BACKEND_FEDERATION_MANIFEST_FILE, 'module-federation-manifest');
596
+ runtimeByPath.set(BACKEND_FEDERATION_REMOTE_ENTRY_FILE, 'nodejs');
597
+ }
598
+ for (const logicalPath of [
599
+ ULTRAMODERN_BUILD_ARTIFACT_FILE,
600
+ PUBLIC_BUILD_ARTIFACT_PATH
601
+ ])if (files.includes(logicalPath)) runtimeByPath.set(logicalPath, 'release-identity-metadata');
449
602
  const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(outputDirectory, identity, {
450
- apiBackend: apiBackendPaths,
451
- backendFederation: [
452
- BACKEND_FEDERATION_MANIFEST_FILE,
453
- BACKEND_FEDERATION_REMOTE_ENTRY_FILE
454
- ],
455
- ssr: [
603
+ ...apiBackendPaths.length > 0 ? {
604
+ apiBackend: apiBackendPaths
605
+ } : {},
606
+ ...backendProducer ? {
607
+ backendFederation: [
608
+ BACKEND_FEDERATION_MANIFEST_FILE,
609
+ BACKEND_FEDERATION_REMOTE_ENTRY_FILE
610
+ ]
611
+ } : {},
612
+ ssr: apiOnly ? [] : [
456
613
  ...new Set([
457
614
  ...ssrExecutionPaths,
458
615
  ...ssrPaths.filter((logicalPath)=>'index.js' !== logicalPath)
@@ -471,30 +628,37 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
471
628
  uiClient: [
472
629
  ...uiClientPaths
473
630
  ].sort((left, right)=>left.localeCompare(right)),
474
- ssr: [
631
+ ssr: apiOnly ? [] : [
475
632
  ...ssrPaths
476
633
  ].sort((left, right)=>left.localeCompare(right)),
477
634
  apiBackend: [
478
635
  ...apiBackendPaths
479
636
  ].sort((left, right)=>left.localeCompare(right)),
480
- backendFederation: {
481
- manifest: BACKEND_FEDERATION_MANIFEST_FILE,
482
- container: BACKEND_FEDERATION_REMOTE_ENTRY_FILE
483
- }
637
+ ...backendProducer ? {
638
+ backendFederation: {
639
+ manifest: BACKEND_FEDERATION_MANIFEST_FILE,
640
+ container: BACKEND_FEDERATION_REMOTE_ENTRY_FILE
641
+ }
642
+ } : {}
484
643
  }
485
644
  };
486
645
  };
487
- const emitNodeStagedReleaseEnvelope = async ({ distDirectory, outputDirectory })=>{
646
+ const emitNodeStagedReleaseEnvelope = async ({ declaredPublicAssets = [], distDirectory, outputDirectory })=>{
488
647
  const source = await verifyBuildOutputReleaseEnvelope(distDirectory, 'node');
489
648
  if (!source) return;
490
- const { artifacts, surfaces } = await createNodeStagedReleaseArtifactInputs(outputDirectory, source.identity);
649
+ const shellConsumer = source.kind === SHELL_RELEASE_ENVELOPE_KIND;
650
+ const { artifacts, surfaces } = await createNodeStagedReleaseArtifactInputs(outputDirectory, source.identity, 0 === source.surfaces.uiClient.length, 0 === source.surfaces.apiBackend.length, declaredPublicAssets, shellConsumer);
491
651
  const staged = await createMicroVerticalReleaseEnvelope({
492
652
  artifactRoot: outputDirectory,
493
653
  target: 'node',
494
654
  identity: source.identity,
655
+ ...source.ui ? {
656
+ ui: source.ui
657
+ } : {},
495
658
  artifacts,
496
- surfaces
659
+ ...releaseEnvelopeSurfaces(surfaces, shellConsumer)
497
660
  });
661
+ await verifyBuildArtifactCarriers(outputDirectory, staged);
498
662
  await writeReleaseEnvelope(outputDirectory, staged);
499
663
  return staged;
500
664
  };
@@ -505,21 +669,29 @@ const stageCloudflareReleaseEnvelope = async ({ distDirectory, outputDirectory }
505
669
  if (await pathExists(stagedEnvelopePath)) throw new Error('[ultramodern-release-envelope] Cloudflare staging contains a stale envelope before final output generation.');
506
670
  return source;
507
671
  };
508
- const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, identity)=>{
672
+ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, identity, apiOnly, uiOnly, declaredPublicAssets, shellConsumer)=>{
673
+ const backendProducer = !uiOnly && !shellConsumer;
509
674
  const files = await collectFiles(outputDirectory);
675
+ const declaredPublicAssetPaths = resolveDeclaredPublicAssets(files, declaredPublicAssets, 'Cloudflare');
510
676
  for (const requiredPath of [
511
677
  'server/index.mjs',
512
678
  'server/modern-worker-manifest.json',
513
679
  'wrangler.json',
514
680
  'package.json',
515
- 'worker/package.json'
681
+ ...apiOnly ? [] : [
682
+ 'worker/package.json'
683
+ ]
516
684
  ])if (!files.includes(requiredPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${requiredPath}.`);
517
685
  if (files.some((logicalPath)=>'public/release' === logicalPath || logicalPath.startsWith('public/release/'))) throw new Error('[ultramodern-release-envelope] final Cloudflare envelope must remain private and cannot be staged under public/release.');
518
686
  const backendManifestPath = `public/${BACKEND_FEDERATION_MANIFEST_FILE}`;
519
687
  const backendContainerPath = `public/${BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`;
520
- const apiBackendPaths = files.filter((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath));
521
- const ssrPaths = files.filter((logicalPath)=>(logicalPath.startsWith('server/') || logicalPath.startsWith('worker/')) && COMPILED_MODULE_PATTERN.test(logicalPath) && !EFFECT_BFF_WORKER_PATTERN.test(logicalPath));
522
- const uiClientPaths = files.filter((logicalPath)=>logicalPath.startsWith('public/') && logicalPath !== backendManifestPath && logicalPath !== backendContainerPath);
688
+ const apiBackendPaths = effectBffWorkerArtifacts(files);
689
+ if (backendProducer && !files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath))) throw new Error('[ultramodern-release-envelope] final Cloudflare staging has no actual Effect API/BFF worker artifact.');
690
+ const ssrPaths = files.filter((logicalPath)=>(logicalPath.startsWith('server/') || logicalPath.startsWith('worker/')) && COMPILED_MODULE_PATTERN.test(logicalPath) && !apiBackendPaths.includes(logicalPath));
691
+ const uiClientPaths = files.filter((logicalPath)=>logicalPath.startsWith('public/') && logicalPath !== CRAWLER_POLICY_PATH && !isPublicMetadataPath(logicalPath) && !declaredPublicAssetPaths.has(logicalPath) && logicalPath !== PUBLIC_BUILD_ARTIFACT_PATH && logicalPath !== backendManifestPath && logicalPath !== backendContainerPath);
692
+ if (shellConsumer && files.some((logicalPath)=>logicalPath === backendManifestPath || logicalPath === backendContainerPath)) throw new Error('[ultramodern-release-envelope] final Shell Cloudflare staging cannot contain a backend federation producer artifact.');
693
+ if (uiOnly && (apiBackendPaths.length > 0 || files.some((logicalPath)=>logicalPath === backendManifestPath || logicalPath === backendContainerPath || logicalPath.startsWith('api/') && COMPILED_MODULE_PATTERN.test(logicalPath)))) throw new Error('[ultramodern-release-envelope] final UI-only Cloudflare staging contains an undeclared API/backend or backend federation artifact.');
694
+ if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.some((path)=>'server/index.mjs' !== path))) throw new Error('[ultramodern-release-envelope] final API-only Cloudflare staging contains an undeclared UI/client or SSR surface.');
523
695
  for (const [surface, paths] of [
524
696
  [
525
697
  'UI/client',
@@ -527,41 +699,53 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
527
699
  ],
528
700
  [
529
701
  'Cloudflare SSR',
530
- ssrPaths
702
+ apiOnly ? [] : ssrPaths
531
703
  ],
532
704
  [
533
705
  'Cloudflare API/backend',
534
706
  apiBackendPaths
535
707
  ]
536
- ])if (0 === paths.length) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging has no ${surface} artifacts.`);
537
- for (const logicalPath of [
708
+ ])if (0 === paths.length && ('Cloudflare API/backend' === surface ? backendProducer : !apiOnly)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging has no ${surface} artifacts.`);
709
+ for (const logicalPath of backendProducer ? [
538
710
  backendManifestPath,
539
711
  backendContainerPath
540
- ])if (!files.includes(logicalPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${logicalPath}.`);
541
- const clientExecutionPaths = await manifestReferencedClientModules(outputDirectory, files, 'public/mf-manifest.json');
542
- const ssrExecutionPaths = await routeReferencedSsrModules(outputDirectory, files, 'cloudflare', 'server/route.json');
543
- if (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length) throw new Error('[ultramodern-release-envelope] final Cloudflare manifests must reference emitted UI/client and SSR execution modules.');
712
+ ] : [])if (!files.includes(logicalPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${logicalPath}.`);
713
+ const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(outputDirectory, files, 'public/mf-manifest.json');
714
+ const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(outputDirectory, files, 'cloudflare', 'server/route.json');
715
+ if (!apiOnly && (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length)) throw new Error('[ultramodern-release-envelope] final Cloudflare manifests must reference emitted UI/client and SSR execution modules.');
544
716
  const runtimeByPath = new Map();
545
717
  for (const logicalPath of files)runtimeByPath.set(logicalPath, 'cloudflare-deployment');
546
718
  const add = (paths, runtime)=>{
547
719
  for (const logicalPath of paths)runtimeByPath.set(logicalPath, runtime);
548
720
  };
549
721
  add(uiClientPaths, 'browser');
550
- add(ssrPaths, 'workerd');
551
- add(apiBackendPaths, 'workerd-effect');
552
722
  add([
553
- backendManifestPath
554
- ], 'module-federation-manifest');
555
- add([
556
- backendContainerPath
557
- ], 'commonjs-module');
558
- const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(outputDirectory, identity, {
559
- apiBackend: apiBackendPaths,
560
- backendFederation: [
561
- backendManifestPath,
723
+ ...declaredPublicAssetPaths
724
+ ], DECLARED_PUBLIC_ASSET_RUNTIME);
725
+ if (files.includes(CRAWLER_POLICY_PATH)) runtimeByPath.set(CRAWLER_POLICY_PATH, 'crawler-policy');
726
+ add(files.filter(isPublicMetadataPath), 'public-metadata');
727
+ add(apiOnly ? [] : ssrPaths, 'workerd');
728
+ add(apiBackendPaths, 'workerd-effect');
729
+ if (backendProducer) {
730
+ add([
731
+ backendManifestPath
732
+ ], 'module-federation-manifest');
733
+ add([
562
734
  backendContainerPath
563
- ],
564
- ssr: [
735
+ ], 'commonjs-module');
736
+ }
737
+ add(files.filter((logicalPath)=>logicalPath === ULTRAMODERN_BUILD_ARTIFACT_FILE || logicalPath === PUBLIC_BUILD_ARTIFACT_PATH), 'release-identity-metadata');
738
+ const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(outputDirectory, identity, {
739
+ ...apiBackendPaths.length > 0 ? {
740
+ apiBackend: apiBackendPaths
741
+ } : {},
742
+ ...backendProducer ? {
743
+ backendFederation: [
744
+ backendManifestPath,
745
+ backendContainerPath
746
+ ]
747
+ } : {},
748
+ ssr: apiOnly ? [] : [
565
749
  ...new Set([
566
750
  ...ssrExecutionPaths,
567
751
  ...ssrPaths.filter((logicalPath)=>logicalPath.startsWith('worker/'))
@@ -580,30 +764,37 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
580
764
  uiClient: [
581
765
  ...uiClientPaths
582
766
  ].sort((left, right)=>left.localeCompare(right)),
583
- ssr: [
767
+ ssr: apiOnly ? [] : [
584
768
  ...ssrPaths
585
769
  ].sort((left, right)=>left.localeCompare(right)),
586
770
  apiBackend: [
587
771
  ...apiBackendPaths
588
772
  ].sort((left, right)=>left.localeCompare(right)),
589
- backendFederation: {
590
- manifest: backendManifestPath,
591
- container: backendContainerPath
592
- }
773
+ ...backendProducer ? {
774
+ backendFederation: {
775
+ manifest: backendManifestPath,
776
+ container: backendContainerPath
777
+ }
778
+ } : {}
593
779
  }
594
780
  };
595
781
  };
596
- const emitCloudflareStagedReleaseEnvelope = async ({ distDirectory, outputDirectory })=>{
782
+ const emitCloudflareStagedReleaseEnvelope = async ({ declaredPublicAssets = [], distDirectory, outputDirectory })=>{
597
783
  const source = await verifyBuildOutputReleaseEnvelope(distDirectory, 'cloudflare');
598
784
  if (!source) return;
599
- const { artifacts, surfaces } = await createCloudflareStagedReleaseArtifactInputs(outputDirectory, source.identity);
785
+ const shellConsumer = source.kind === SHELL_RELEASE_ENVELOPE_KIND;
786
+ const { artifacts, surfaces } = await createCloudflareStagedReleaseArtifactInputs(outputDirectory, source.identity, 0 === source.surfaces.uiClient.length, 0 === source.surfaces.apiBackend.length, declaredPublicAssets, shellConsumer);
600
787
  const staged = await createMicroVerticalReleaseEnvelope({
601
788
  artifactRoot: outputDirectory,
602
789
  target: 'cloudflare',
603
790
  identity: source.identity,
791
+ ...source.ui ? {
792
+ ui: source.ui
793
+ } : {},
604
794
  artifacts,
605
- surfaces
795
+ ...releaseEnvelopeSurfaces(surfaces, shellConsumer)
606
796
  });
797
+ await verifyBuildArtifactCarriers(outputDirectory, staged);
607
798
  await writeReleaseEnvelope(outputDirectory, staged);
608
799
  return staged;
609
800
  };