@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
@@ -1,6 +1,14 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import fs from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import {
5
+ formatBackendFederationValidationErrors,
6
+ immutableRendererRouterBindings,
7
+ validateRendererIdentity,
8
+ validateRendererProfile,
9
+ validateRendererProfileCompatibility,
10
+ validateRendererRouterBindings,
11
+ } from '@modern-js/backend-federation-contracts';
4
12
  import {
5
13
  canonicalSerializeMicroVerticalReleaseEnvelope,
6
14
  digestMicroVerticalReleaseEnvelopePayload,
@@ -16,8 +24,15 @@ import {
16
24
  type MicroVerticalReleaseEnvelope,
17
25
  type MicroVerticalReleaseEnvelopePayload,
18
26
  type MicroVerticalReleaseIdentity,
19
- type MicroVerticalReleaseSurfaces,
20
27
  type MicroVerticalReleaseTarget,
28
+ type MicroVerticalReleaseUi,
29
+ type ReleaseEnvelope,
30
+ type ReleaseEnvelopeKind,
31
+ type ReleaseEnvelopePayload,
32
+ type ReleaseSurfaces,
33
+ SHELL_RELEASE_ENVELOPE_KIND,
34
+ type ShellReleaseEnvelope,
35
+ type ShellReleaseEnvelopePayload,
21
36
  type VerifyMicroVerticalReleaseEnvelopeOptions,
22
37
  } from './types';
23
38
 
@@ -33,6 +48,14 @@ export type {
33
48
  MicroVerticalReleaseSurfaces,
34
49
  MicroVerticalReleaseSymbolicLinkArtifact,
35
50
  MicroVerticalReleaseTarget,
51
+ MicroVerticalReleaseUi,
52
+ ReleaseEnvelope,
53
+ ReleaseEnvelopeKind,
54
+ ReleaseEnvelopePayload,
55
+ ReleaseSurfaces,
56
+ ShellReleaseEnvelope,
57
+ ShellReleaseEnvelopePayload,
58
+ ShellReleaseSurfaces,
36
59
  VerifyMicroVerticalReleaseEnvelopeOptions,
37
60
  } from './types';
38
61
  export {
@@ -40,6 +63,7 @@ export {
40
63
  MICROVERTICAL_RELEASE_ENVELOPE_KIND,
41
64
  MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION,
42
65
  MICROVERTICAL_RELEASE_TARGETS,
66
+ SHELL_RELEASE_ENVELOPE_KIND,
43
67
  };
44
68
 
45
69
  const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
@@ -100,6 +124,18 @@ const assertTarget = (
100
124
  return target as MicroVerticalReleaseTarget;
101
125
  };
102
126
 
127
+ const assertEnvelopeKind = (value: unknown): ReleaseEnvelopeKind => {
128
+ if (
129
+ value !== MICROVERTICAL_RELEASE_ENVELOPE_KIND &&
130
+ value !== SHELL_RELEASE_ENVELOPE_KIND
131
+ ) {
132
+ throw new Error(
133
+ `envelope.kind must be "${MICROVERTICAL_RELEASE_ENVELOPE_KIND}" or "${SHELL_RELEASE_ENVELOPE_KIND}".`,
134
+ );
135
+ }
136
+ return value;
137
+ };
138
+
103
139
  const assertReleaseIdentity = (
104
140
  value: unknown,
105
141
  location = 'identity',
@@ -381,8 +417,8 @@ const assertUniqueSortedArtifacts = (
381
417
  };
382
418
 
383
419
  const assertSurfacePaths = (value: unknown, location: string): string[] => {
384
- if (!Array.isArray(value) || value.length === 0) {
385
- throw new Error(`${location} must contain at least one artifact path.`);
420
+ if (!Array.isArray(value)) {
421
+ throw new Error(`${location} must be an array of artifact paths.`);
386
422
  }
387
423
  const paths = value.map((item, index) =>
388
424
  assertNormalizedLogicalPath(item, `${location}[${index}]`),
@@ -397,42 +433,97 @@ const assertSurfacePaths = (value: unknown, location: string): string[] => {
397
433
  return paths;
398
434
  };
399
435
 
400
- const assertSurfaces = (value: unknown): MicroVerticalReleaseSurfaces => {
436
+ const assertSurfaces = (
437
+ value: unknown,
438
+ kind: ReleaseEnvelopeKind,
439
+ ):
440
+ | Pick<MicroVerticalReleaseEnvelopePayload, 'kind' | 'surfaces'>
441
+ | Pick<ShellReleaseEnvelopePayload, 'kind' | 'surfaces'> => {
401
442
  const surfaces = assertRecord(value, 'surfaces');
402
443
  assertExactKeys(
403
444
  surfaces,
404
- ['uiClient', 'ssr', 'apiBackend', 'backendFederation'],
445
+ [
446
+ 'uiClient',
447
+ 'ssr',
448
+ 'apiBackend',
449
+ // A Shell consumes remotes and never publishes a backend federation pair.
450
+ // A UI-only MicroVertical (cold deploy) has no backend surface to pair.
451
+ ...(kind !== SHELL_RELEASE_ENVELOPE_KIND &&
452
+ Object.hasOwn(surfaces, 'backendFederation')
453
+ ? ['backendFederation']
454
+ : []),
455
+ ],
405
456
  'surfaces',
406
457
  );
407
- const backendFederation = assertRecord(
408
- surfaces.backendFederation,
409
- 'surfaces.backendFederation',
410
- );
411
- assertExactKeys(
412
- backendFederation,
413
- ['manifest', 'container'],
414
- 'surfaces.backendFederation',
458
+ const backendFederation = Object.hasOwn(surfaces, 'backendFederation')
459
+ ? assertRecord(surfaces.backendFederation, 'surfaces.backendFederation')
460
+ : undefined;
461
+ if (backendFederation)
462
+ assertExactKeys(
463
+ backendFederation,
464
+ ['manifest', 'container'],
465
+ 'surfaces.backendFederation',
466
+ );
467
+ const uiClient = assertSurfacePaths(surfaces.uiClient, 'surfaces.uiClient');
468
+ const ssr = assertSurfacePaths(surfaces.ssr, 'surfaces.ssr');
469
+ const apiBackend = assertSurfacePaths(
470
+ surfaces.apiBackend,
471
+ 'surfaces.apiBackend',
415
472
  );
473
+ if (uiClient.length > 0 && ssr.length === 0) {
474
+ throw new Error(
475
+ 'surfaces.ssr must contain at least one artifact path when UI/client is declared.',
476
+ );
477
+ }
478
+ if (ssr.length > 0 && uiClient.length === 0) {
479
+ throw new Error(
480
+ 'surfaces.uiClient must contain at least one artifact path when SSR is declared.',
481
+ );
482
+ }
483
+ if (apiBackend.length === 0 && (backendFederation || uiClient.length === 0)) {
484
+ throw new Error(
485
+ 'surfaces.apiBackend must contain at least one artifact path.',
486
+ );
487
+ }
488
+ if (kind === SHELL_RELEASE_ENVELOPE_KIND) {
489
+ if (uiClient.length === 0 || ssr.length === 0) {
490
+ throw new Error(
491
+ 'Shell surfaces.uiClient and surfaces.ssr must each contain at least one artifact path.',
492
+ );
493
+ }
494
+ return { kind, surfaces: { uiClient, ssr, apiBackend } };
495
+ }
496
+ if (apiBackend.length > 0 && !backendFederation)
497
+ throw new Error(
498
+ 'surfaces.backendFederation is required when API/backend is declared.',
499
+ );
416
500
  return {
417
- uiClient: assertSurfacePaths(surfaces.uiClient, 'surfaces.uiClient'),
418
- ssr: assertSurfacePaths(surfaces.ssr, 'surfaces.ssr'),
419
- apiBackend: assertSurfacePaths(surfaces.apiBackend, 'surfaces.apiBackend'),
420
- backendFederation: {
421
- manifest: assertNormalizedLogicalPath(
422
- backendFederation.manifest,
423
- 'surfaces.backendFederation.manifest',
424
- ),
425
- container: assertNormalizedLogicalPath(
426
- backendFederation.container,
427
- 'surfaces.backendFederation.container',
428
- ),
501
+ kind,
502
+ surfaces: {
503
+ uiClient,
504
+ ssr,
505
+ apiBackend,
506
+ ...(backendFederation
507
+ ? {
508
+ backendFederation: {
509
+ manifest: assertNormalizedLogicalPath(
510
+ backendFederation.manifest,
511
+ 'surfaces.backendFederation.manifest',
512
+ ),
513
+ container: assertNormalizedLogicalPath(
514
+ backendFederation.container,
515
+ 'surfaces.backendFederation.container',
516
+ ),
517
+ },
518
+ }
519
+ : {}),
429
520
  },
430
521
  };
431
522
  };
432
523
 
433
524
  const assertSurfaceReferences = (
434
525
  artifacts: readonly MicroVerticalReleaseArtifactInput[],
435
- surfaces: MicroVerticalReleaseSurfaces,
526
+ surfaces: ReleaseSurfaces,
436
527
  ) => {
437
528
  const artifactPaths = new Set(
438
529
  artifacts.map(artifact => artifact.logicalPath),
@@ -441,8 +532,12 @@ const assertSurfaceReferences = (
441
532
  uiClient: surfaces.uiClient,
442
533
  ssr: surfaces.ssr,
443
534
  apiBackend: surfaces.apiBackend,
444
- 'backendFederation.manifest': [surfaces.backendFederation.manifest],
445
- 'backendFederation.container': [surfaces.backendFederation.container],
535
+ ...('backendFederation' in surfaces && surfaces.backendFederation
536
+ ? {
537
+ 'backendFederation.manifest': [surfaces.backendFederation.manifest],
538
+ 'backendFederation.container': [surfaces.backendFederation.container],
539
+ }
540
+ : {}),
446
541
  })) {
447
542
  for (const logicalPath of paths) {
448
543
  if (!artifactPaths.has(logicalPath)) {
@@ -457,7 +552,7 @@ const assertSurfaceReferences = (
457
552
  const assertTargetSurfaceContract = (
458
553
  target: MicroVerticalReleaseTarget,
459
554
  artifacts: readonly MicroVerticalReleaseArtifactInput[],
460
- surfaces: MicroVerticalReleaseSurfaces,
555
+ surfaces: ReleaseSurfaces,
461
556
  ) => {
462
557
  const byPath = new Map(
463
558
  artifacts.map(artifact => [artifact.logicalPath, artifact]),
@@ -489,16 +584,18 @@ const assertTargetSurfaceContract = (
489
584
  target === 'node' ? 'nodejs' : 'workerd-effect',
490
585
  'API/backend',
491
586
  );
492
- assertRuntime(
493
- [surfaces.backendFederation.manifest],
494
- 'module-federation-manifest',
495
- 'backend federation manifest',
496
- );
497
- assertRuntime(
498
- [surfaces.backendFederation.container],
499
- target === 'node' ? 'nodejs' : 'commonjs-module',
500
- 'backend federation container',
501
- );
587
+ if ('backendFederation' in surfaces && surfaces.backendFederation) {
588
+ assertRuntime(
589
+ [surfaces.backendFederation.manifest],
590
+ 'module-federation-manifest',
591
+ 'backend federation manifest',
592
+ );
593
+ assertRuntime(
594
+ [surfaces.backendFederation.container],
595
+ target === 'node' ? 'nodejs' : 'commonjs-module',
596
+ 'backend federation container',
597
+ );
598
+ }
502
599
  };
503
600
 
504
601
  const deepFreeze = <T>(value: T): T => {
@@ -511,7 +608,81 @@ const deepFreeze = <T>(value: T): T => {
511
608
  return value;
512
609
  };
513
610
 
514
- const assertEnvelope = (value: unknown): MicroVerticalReleaseEnvelope => {
611
+ const assertReleaseUi = (
612
+ value: unknown,
613
+ identity: MicroVerticalReleaseIdentity,
614
+ surfaces: ReleaseSurfaces,
615
+ ): MicroVerticalReleaseUi | undefined => {
616
+ if (surfaces.uiClient.length === 0) {
617
+ if (value !== undefined) {
618
+ throw new Error('envelope.ui is forbidden for an API-only release.');
619
+ }
620
+ return undefined;
621
+ }
622
+ const ui = assertRecord(value, 'envelope.ui');
623
+ assertExactKeys(
624
+ ui,
625
+ ['rendererIdentity', 'rendererProfile', 'routerBindings'],
626
+ 'envelope.ui',
627
+ );
628
+ const routerBindings = assertRecord(
629
+ ui.routerBindings,
630
+ 'envelope.ui.routerBindings',
631
+ );
632
+ const errors = [
633
+ ...validateRendererIdentity(
634
+ ui.rendererIdentity,
635
+ 'envelope.ui.rendererIdentity',
636
+ ).errors,
637
+ ...validateRendererProfile(
638
+ ui.rendererProfile,
639
+ 'envelope.ui.rendererProfile',
640
+ ).errors,
641
+ ...validateRendererRouterBindings(
642
+ routerBindings,
643
+ Object.keys(routerBindings),
644
+ 'envelope.ui.routerBindings',
645
+ ).errors,
646
+ ];
647
+ if (errors.length > 0) {
648
+ throw new Error(formatBackendFederationValidationErrors(errors));
649
+ }
650
+ const rendererIdentity =
651
+ ui.rendererIdentity as MicroVerticalReleaseUi['rendererIdentity'];
652
+ const rendererProfile =
653
+ ui.rendererProfile as MicroVerticalReleaseUi['rendererProfile'];
654
+ if (!Object.hasOwn(routerBindings, rendererIdentity.entryName)) {
655
+ throw new Error(
656
+ 'envelope.ui.routerBindings must include the primary rendererIdentity.entryName.',
657
+ );
658
+ }
659
+ if (rendererIdentity.buildId !== identity.buildMarker) {
660
+ throw new Error(
661
+ 'envelope.ui.rendererIdentity.buildId must match identity.buildMarker.',
662
+ );
663
+ }
664
+ for (const field of ['renderer', 'protocolVersion'] as const) {
665
+ if (rendererIdentity[field] !== rendererProfile[field]) {
666
+ throw new Error(
667
+ `envelope.ui.rendererProfile.${field} must match rendererIdentity.${field}.`,
668
+ );
669
+ }
670
+ }
671
+ return {
672
+ rendererIdentity: { ...rendererIdentity },
673
+ rendererProfile: {
674
+ ...rendererProfile,
675
+ compiler: { ...rendererProfile.compiler },
676
+ hydration: { ...rendererProfile.hydration },
677
+ router: { ...rendererProfile.router },
678
+ },
679
+ routerBindings: immutableRendererRouterBindings(
680
+ routerBindings as MicroVerticalReleaseUi['routerBindings'],
681
+ ),
682
+ };
683
+ };
684
+
685
+ const assertEnvelope = (value: unknown): ReleaseEnvelope => {
515
686
  const envelope = assertRecord(value, 'envelope');
516
687
  assertExactKeys(
517
688
  envelope,
@@ -520,6 +691,7 @@ const assertEnvelope = (value: unknown): MicroVerticalReleaseEnvelope => {
520
691
  'kind',
521
692
  'target',
522
693
  'identity',
694
+ ...(Object.hasOwn(envelope, 'ui') ? ['ui'] : []),
523
695
  'artifacts',
524
696
  'surfaces',
525
697
  'envelopeDigest',
@@ -533,11 +705,7 @@ const assertEnvelope = (value: unknown): MicroVerticalReleaseEnvelope => {
533
705
  `envelope.schemaVersion must be ${MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION}.`,
534
706
  );
535
707
  }
536
- if (envelope.kind !== MICROVERTICAL_RELEASE_ENVELOPE_KIND) {
537
- throw new Error(
538
- `envelope.kind must be "${MICROVERTICAL_RELEASE_ENVELOPE_KIND}".`,
539
- );
540
- }
708
+ const kind = assertEnvelopeKind(envelope.kind);
541
709
  if (!Array.isArray(envelope.artifacts) || envelope.artifacts.length === 0) {
542
710
  throw new Error('envelope.artifacts must contain at least one artifact.');
543
711
  }
@@ -546,16 +714,25 @@ const assertEnvelope = (value: unknown): MicroVerticalReleaseEnvelope => {
546
714
  );
547
715
  assertUniqueSortedArtifacts(artifacts, 'envelope.artifacts');
548
716
  const target = assertTarget(envelope.target, 'envelope.target');
549
- const surfaces = assertSurfaces(envelope.surfaces);
717
+ const releaseSurfaces = assertSurfaces(envelope.surfaces, kind);
718
+ const { surfaces } = releaseSurfaces;
719
+ if (surfaces.uiClient.length === 0 && Object.hasOwn(envelope, 'ui')) {
720
+ throw new Error('envelope.ui is forbidden for an API-only release.');
721
+ }
550
722
  assertSurfaceReferences(artifacts, surfaces);
551
723
  assertTargetSurfaceContract(target, artifacts, surfaces);
552
- const parsed: MicroVerticalReleaseEnvelope = {
724
+ const identity = assertReleaseIdentity(
725
+ envelope.identity,
726
+ 'envelope.identity',
727
+ );
728
+ const ui = assertReleaseUi(envelope.ui, identity, surfaces);
729
+ const parsed: ReleaseEnvelope = {
730
+ ...releaseSurfaces,
553
731
  schemaVersion: MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION,
554
- kind: MICROVERTICAL_RELEASE_ENVELOPE_KIND,
555
732
  target,
556
- identity: assertReleaseIdentity(envelope.identity, 'envelope.identity'),
733
+ identity,
734
+ ...(ui ? { ui } : {}),
557
735
  artifacts,
558
- surfaces,
559
736
  envelopeDigest: assertNonEmptyString(
560
737
  envelope.envelopeDigest,
561
738
  'envelope.envelopeDigest',
@@ -577,12 +754,28 @@ const assertEnvelope = (value: unknown): MicroVerticalReleaseEnvelope => {
577
754
  return parsed;
578
755
  };
579
756
 
580
- export const createMicroVerticalReleaseEnvelope = async (
757
+ export function createMicroVerticalReleaseEnvelope(
758
+ input: CreateMicroVerticalReleaseEnvelopeInput & {
759
+ kind: typeof SHELL_RELEASE_ENVELOPE_KIND;
760
+ },
761
+ ): Promise<ShellReleaseEnvelope>;
762
+ export function createMicroVerticalReleaseEnvelope(
763
+ input: CreateMicroVerticalReleaseEnvelopeInput & {
764
+ kind?: typeof MICROVERTICAL_RELEASE_ENVELOPE_KIND;
765
+ },
766
+ ): Promise<MicroVerticalReleaseEnvelope>;
767
+ export function createMicroVerticalReleaseEnvelope(
768
+ input: CreateMicroVerticalReleaseEnvelopeInput,
769
+ ): Promise<ReleaseEnvelope>;
770
+ export async function createMicroVerticalReleaseEnvelope(
581
771
  input: CreateMicroVerticalReleaseEnvelopeInput,
582
- ): Promise<MicroVerticalReleaseEnvelope> => {
772
+ ): Promise<ReleaseEnvelope> {
583
773
  const artifactRoot = await fs.realpath(path.resolve(input.artifactRoot));
584
774
  const target = assertTarget(input.target);
585
775
  const identity = assertReleaseIdentity(input.identity);
776
+ const kind = assertEnvelopeKind(
777
+ input.kind === undefined ? MICROVERTICAL_RELEASE_ENVELOPE_KIND : input.kind,
778
+ );
586
779
  if (!Array.isArray(input.artifacts) || input.artifacts.length === 0) {
587
780
  throw new Error('artifacts must contain at least one artifact.');
588
781
  }
@@ -593,32 +786,94 @@ export const createMicroVerticalReleaseEnvelope = async (
593
786
  left.logicalPath.localeCompare(right.logicalPath),
594
787
  );
595
788
  assertUniqueSortedArtifacts(sortedInputs, 'artifacts');
596
- const surfaces = assertSurfaces(input.surfaces);
789
+ const releaseSurfaces = assertSurfaces(input.surfaces, kind);
790
+ const { surfaces } = releaseSurfaces;
791
+ if (surfaces.uiClient.length === 0 && Object.hasOwn(input, 'ui')) {
792
+ throw new Error('envelope.ui is forbidden for an API-only release.');
793
+ }
597
794
  assertSurfaceReferences(sortedInputs, surfaces);
598
795
  assertTargetSurfaceContract(target, sortedInputs, surfaces);
796
+ const ui = assertReleaseUi(input.ui, identity, surfaces);
599
797
  const artifacts = await Promise.all(
600
798
  sortedInputs.map(artifact => readFinalArtifact(artifactRoot, artifact)),
601
799
  );
602
800
  assertTargetSurfaceContract(target, artifacts, surfaces);
603
- const payload: MicroVerticalReleaseEnvelopePayload = {
801
+ const payload: ReleaseEnvelopePayload = {
802
+ ...releaseSurfaces,
604
803
  schemaVersion: MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION,
605
- kind: MICROVERTICAL_RELEASE_ENVELOPE_KIND,
606
804
  target,
607
805
  identity,
806
+ ...(ui ? { ui } : {}),
608
807
  artifacts,
609
- surfaces,
610
808
  };
611
809
  return deepFreeze({
612
810
  ...payload,
613
811
  envelopeDigest: digestMicroVerticalReleaseEnvelopePayload(payload),
614
812
  });
615
- };
813
+ }
616
814
 
617
- export const verifyMicroVerticalReleaseEnvelope = async (
815
+ export function verifyMicroVerticalReleaseEnvelope(
816
+ value: unknown,
817
+ options: VerifyMicroVerticalReleaseEnvelopeOptions & {
818
+ expectedKind: typeof SHELL_RELEASE_ENVELOPE_KIND;
819
+ },
820
+ ): Promise<ShellReleaseEnvelope>;
821
+ export function verifyMicroVerticalReleaseEnvelope(
822
+ value: unknown,
823
+ options: VerifyMicroVerticalReleaseEnvelopeOptions & {
824
+ expectedKind: typeof MICROVERTICAL_RELEASE_ENVELOPE_KIND;
825
+ },
826
+ ): Promise<MicroVerticalReleaseEnvelope>;
827
+ export function verifyMicroVerticalReleaseEnvelope(
618
828
  value: unknown,
619
829
  options: VerifyMicroVerticalReleaseEnvelopeOptions,
620
- ): Promise<MicroVerticalReleaseEnvelope> => {
830
+ ): Promise<ReleaseEnvelope>;
831
+ export async function verifyMicroVerticalReleaseEnvelope(
832
+ value: unknown,
833
+ options: VerifyMicroVerticalReleaseEnvelopeOptions,
834
+ ): Promise<ReleaseEnvelope> {
621
835
  const envelope = assertEnvelope(value);
836
+ if (options.expectedRendererProfile !== undefined) {
837
+ const result = validateRendererProfileCompatibility(
838
+ options.expectedRendererProfile,
839
+ envelope.ui?.rendererProfile,
840
+ 'envelope.ui.rendererProfile',
841
+ );
842
+ if (!result.ok) {
843
+ throw new Error(formatBackendFederationValidationErrors(result.errors));
844
+ }
845
+ }
846
+ if (options.expectedRendererIdentity !== undefined) {
847
+ const expected = options.expectedRendererIdentity;
848
+ const result = validateRendererIdentity(
849
+ expected,
850
+ 'expectedRendererIdentity',
851
+ );
852
+ if (!result.ok) {
853
+ throw new Error(formatBackendFederationValidationErrors(result.errors));
854
+ }
855
+ for (const field of [
856
+ 'renderer',
857
+ 'protocolVersion',
858
+ 'appId',
859
+ 'entryName',
860
+ 'buildId',
861
+ ] as const) {
862
+ if (envelope.ui?.rendererIdentity[field] !== expected[field]) {
863
+ throw new Error(
864
+ `envelope.ui.rendererIdentity.${field} must match the consuming renderer identity.`,
865
+ );
866
+ }
867
+ }
868
+ }
869
+ if (
870
+ options.expectedKind !== undefined &&
871
+ envelope.kind !== options.expectedKind
872
+ ) {
873
+ throw new Error(
874
+ `envelope.kind must be "${options.expectedKind}" for this release owner; received "${envelope.kind}".`,
875
+ );
876
+ }
622
877
  if (
623
878
  options.expectedTarget !== undefined &&
624
879
  envelope.target !== options.expectedTarget
@@ -653,4 +908,4 @@ export const verifyMicroVerticalReleaseEnvelope = async (
653
908
  }
654
909
  }
655
910
  return deepFreeze(envelope);
656
- };
911
+ }
@@ -1,4 +1,15 @@
1
1
  import path from 'node:path';
2
+ import type { RendererProfile } from '@modern-js/backend-federation-contracts';
3
+ import type { NodePublicAssetConfig } from '../config';
4
+ import {
5
+ NODE_PUBLIC_ASSET_SCOPE,
6
+ normalizeDeclaredPublicAssets,
7
+ resolveAddedDeclaredPublicAssetPaths,
8
+ } from '../deploy-output/public-assets';
9
+ import {
10
+ getDeployingTarget,
11
+ type ResolvedDeployTarget,
12
+ } from '../deploy-output/target';
2
13
  import {
3
14
  emitFrameworkMicroVerticalReleaseEnvelope,
4
15
  emitNodeStagedReleaseEnvelope,
@@ -6,10 +17,11 @@ import {
6
17
  verifyNodeReleaseEnvelopeStaging,
7
18
  } from './framework-output';
8
19
 
9
- type ReleaseEnvelopeTarget = 'cloudflare' | 'node' | string;
10
-
11
20
  export interface ReleaseEnvelopeConfig {
12
- deploy?: { target?: string };
21
+ deploy?: {
22
+ releaseEnvelopeRole?: 'microvertical' | 'shell';
23
+ node?: { publicAssets?: NodePublicAssetConfig[] };
24
+ };
13
25
  }
14
26
 
15
27
  export interface ReleaseEnvelopeAppContext {
@@ -17,6 +29,7 @@ export interface ReleaseEnvelopeAppContext {
17
29
  appDirectory: string;
18
30
  distDirectory: string;
19
31
  metaName: string;
32
+ deployTarget?: ResolvedDeployTarget;
20
33
  }
21
34
 
22
35
  export interface ReleaseEnvelopePluginApi<
@@ -38,64 +51,59 @@ export interface ReleaseEnvelopePlugin<
38
51
  setup(api: ReleaseEnvelopePluginApi<Config>): void;
39
52
  }
40
53
 
41
- export type ResolveDeployTarget<
42
- Config extends ReleaseEnvelopeConfig = ReleaseEnvelopeConfig,
43
- > = (config: Config) => ReleaseEnvelopeTarget;
44
-
45
- const resolveActiveDeployTarget = <Config extends ReleaseEnvelopeConfig>(
46
- api: ReleaseEnvelopePluginApi<Config>,
47
- resolveDeployTarget: ResolveDeployTarget<Config>,
48
- ) => {
49
- const { metaName } = api.getAppContext();
50
- const config = api.getNormalizedConfig();
51
- if (
52
- metaName !== 'modern-js' &&
53
- !config.deploy?.target &&
54
- !process.env.MODERNJS_DEPLOY
55
- ) {
56
- return undefined;
57
- }
58
- return resolveDeployTarget(config);
59
- };
60
-
61
54
  export const createUltramodernReleaseEnvelopePlugin = <
62
55
  Config extends ReleaseEnvelopeConfig,
63
56
  >({
64
- resolveDeployTarget,
57
+ resolveRendererProfile,
65
58
  }: {
66
- resolveDeployTarget: ResolveDeployTarget<Config>;
67
- }): ReleaseEnvelopePlugin<Config> => {
59
+ resolveRendererProfile?: (config: Config) => RendererProfile;
60
+ } = {}): ReleaseEnvelopePlugin<Config> => {
68
61
  return {
69
62
  name: '@modern-js/ultramodern-release-envelope',
70
63
  pre: [
71
64
  '@modern-js/backend-federation-build',
72
65
  '@modern-js/plugin-bff',
73
66
  '@modern-js/deploy-output-aliases',
67
+ '@modern-js/deploy-output-public-assets',
74
68
  ],
75
69
  post: ['@modern-js/plugin-deploy'],
76
70
  setup(api) {
77
- const emitBuildEnvelope = async (target: 'node' | 'cloudflare') => {
78
- const { apiOnly, distDirectory } = api.getAppContext();
71
+ const emitBuildEnvelope = async (
72
+ target: 'node' | 'cloudflare',
73
+ requirePromotable = true,
74
+ ) => {
75
+ const { apiOnly, appDirectory, distDirectory } = api.getAppContext();
79
76
  await emitFrameworkMicroVerticalReleaseEnvelope({
80
77
  apiOnly,
78
+ appDirectory,
81
79
  distDirectory,
80
+ role: api.getNormalizedConfig().deploy?.releaseEnvelopeRole,
81
+ requirePromotable,
82
82
  target,
83
+ ...(!apiOnly && resolveRendererProfile
84
+ ? {
85
+ expectedRendererProfile: resolveRendererProfile(
86
+ api.getNormalizedConfig(),
87
+ ),
88
+ }
89
+ : {}),
83
90
  });
84
91
  };
85
92
 
86
93
  api.onAfterBuild(async () => {
87
- const configuredTarget = resolveDeployTarget(api.getNormalizedConfig());
94
+ const configuredTarget = api.getAppContext().deployTarget?.target;
88
95
  if (configuredTarget !== 'node' && configuredTarget !== 'cloudflare') {
89
96
  return;
90
97
  }
91
- await emitBuildEnvelope(configuredTarget);
98
+ // A plain `modern build` is the development path: a dirty or non-Git
99
+ // checkout resolves to the `workspace` source revision, which is not
100
+ // promotable but must still build. Deploy re-emits the envelope and
101
+ // verifies it, so the release gate keeps its teeth there.
102
+ await emitBuildEnvelope(configuredTarget, false);
92
103
  });
93
104
 
94
105
  api.onBeforeDeploy(async () => {
95
- const configuredTarget = resolveActiveDeployTarget(
96
- api,
97
- resolveDeployTarget,
98
- );
106
+ const configuredTarget = getDeployingTarget(api.getAppContext());
99
107
  if (configuredTarget !== 'node') {
100
108
  return;
101
109
  }
@@ -105,16 +113,24 @@ export const createUltramodernReleaseEnvelopePlugin = <
105
113
  });
106
114
 
107
115
  api.onAfterDeploy(async () => {
108
- const configuredTarget = resolveActiveDeployTarget(
109
- api,
110
- resolveDeployTarget,
111
- );
116
+ const configuredTarget = getDeployingTarget(api.getAppContext());
112
117
  if (configuredTarget !== 'node') {
113
118
  return;
114
119
  }
115
120
  const { appDirectory, distDirectory } = api.getAppContext();
116
121
  const outputDirectory = path.join(appDirectory, '.output');
117
122
  const releaseEnvelope = await emitNodeStagedReleaseEnvelope({
123
+ // The Node deploy output is a copy of dist, so a declared file
124
+ // added output exactly when dist has no file at its path.
125
+ declaredPublicAssets: await resolveAddedDeclaredPublicAssetPaths({
126
+ appDirectory,
127
+ assets: normalizeDeclaredPublicAssets(
128
+ api.getNormalizedConfig().deploy?.node?.publicAssets,
129
+ NODE_PUBLIC_ASSET_SCOPE,
130
+ ),
131
+ generatedRoot: distDirectory,
132
+ scope: NODE_PUBLIC_ASSET_SCOPE,
133
+ }),
118
134
  distDirectory,
119
135
  outputDirectory,
120
136
  });