@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.9 → 3.9.3-ultramodern.10

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 (275) 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 +30 -165
  6. package/dist/cjs/build-config/internal-effect-discovery.js +231 -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 +180 -17
  37. package/dist/cjs/policy-defaults.js +38 -29
  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 +9 -6
  46. package/dist/cjs/renderer-build-identity.js +144 -0
  47. package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  48. package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  49. package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  50. package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +547 -103
  51. package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  52. package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
  53. package/dist/cjs/workspace-topology.js +84 -0
  54. package/dist/esm/backend-federation-build/codegen.mjs +29 -18
  55. package/dist/esm/backend-federation-build/config.mjs +74 -38
  56. package/dist/esm/backend-federation-build/emit.mjs +64 -23
  57. package/dist/esm/build-config/build-environment.mjs +31 -163
  58. package/dist/esm/build-config/internal-effect-discovery.mjs +187 -0
  59. package/dist/esm/build-config/public.mjs +1 -1
  60. package/dist/esm/build-config/remote-address.mjs +20 -0
  61. package/dist/esm/cloudflare/artifacts.mjs +8 -19
  62. package/dist/esm/cloudflare/delivery-unit.mjs +124 -45
  63. package/dist/esm/cloudflare/index.mjs +15 -5
  64. package/dist/esm/cloudflare/native-renderer.mjs +48 -0
  65. package/dist/esm/cloudflare/renderer-identity.mjs +57 -0
  66. package/dist/esm/cloudflare/worker-manifest.mjs +55 -14
  67. package/dist/esm/cloudflare/worker-options.mjs +10 -0
  68. package/dist/esm/cloudflare/wrangler-config.mjs +203 -12
  69. package/dist/esm/cloudflare-builder.mjs +301 -89
  70. package/dist/esm/cloudflare-output-contract.mjs +29 -3
  71. package/dist/esm/cloudflare-output-verifier/identity.mjs +113 -24
  72. package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
  73. package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
  74. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  75. package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  76. package/dist/esm/config.mjs +3 -1
  77. package/dist/esm/deploy-output/npmAliases.mjs +92 -6
  78. package/dist/esm/deploy-output/plugin.mjs +24 -6
  79. package/dist/esm/deploy-output/public-assets.mjs +82 -0
  80. package/dist/esm/deploy-output/target.mjs +50 -3
  81. package/dist/esm/deploy-output/trace-files.mjs +47 -0
  82. package/dist/esm/module-federation-config/exposes.mjs +18 -0
  83. package/dist/esm/module-federation-config/index.mjs +4 -0
  84. package/dist/esm/module-federation-config/inspect.mjs +65 -0
  85. package/dist/esm/module-federation-config/object-literal.mjs +43 -0
  86. package/dist/esm/module-federation-config/syntax.mjs +101 -0
  87. package/dist/esm/module-federation-config/types.mjs +0 -0
  88. package/dist/esm/native-type-checker.mjs +175 -18
  89. package/dist/esm/policy-defaults.mjs +36 -30
  90. package/dist/esm/release-envelope/canonical.mjs +2 -9
  91. package/dist/esm/release-envelope/contract.mjs +21 -0
  92. package/dist/esm/release-envelope/framework-output.mjs +297 -106
  93. package/dist/esm/release-envelope/index.mjs +149 -38
  94. package/dist/esm/release-envelope/plugin.mjs +24 -15
  95. package/dist/esm/release-envelope/renderer-output-stamp.mjs +112 -0
  96. package/dist/esm/release-envelope/types.mjs +2 -3
  97. package/dist/esm/release-identity.mjs +9 -6
  98. package/dist/esm/renderer-build-identity.mjs +92 -0
  99. package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  100. package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  101. package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  102. package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +547 -103
  103. package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  104. package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
  105. package/dist/esm/workspace-topology.mjs +21 -0
  106. package/dist/esm-node/backend-federation-build/codegen.mjs +29 -18
  107. package/dist/esm-node/backend-federation-build/config.mjs +74 -38
  108. package/dist/esm-node/backend-federation-build/emit.mjs +64 -23
  109. package/dist/esm-node/build-config/build-environment.mjs +31 -163
  110. package/dist/esm-node/build-config/internal-effect-discovery.mjs +188 -0
  111. package/dist/esm-node/build-config/public.mjs +1 -1
  112. package/dist/esm-node/build-config/remote-address.mjs +21 -0
  113. package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
  114. package/dist/esm-node/cloudflare/delivery-unit.mjs +124 -45
  115. package/dist/esm-node/cloudflare/index.mjs +15 -5
  116. package/dist/esm-node/cloudflare/native-renderer.mjs +49 -0
  117. package/dist/esm-node/cloudflare/renderer-identity.mjs +58 -0
  118. package/dist/esm-node/cloudflare/worker-manifest.mjs +55 -14
  119. package/dist/esm-node/cloudflare/worker-options.mjs +11 -0
  120. package/dist/esm-node/cloudflare/wrangler-config.mjs +203 -12
  121. package/dist/esm-node/cloudflare-builder.mjs +301 -89
  122. package/dist/esm-node/cloudflare-output-contract.mjs +29 -3
  123. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +113 -24
  124. package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
  125. package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
  126. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  127. package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  128. package/dist/esm-node/config.mjs +3 -1
  129. package/dist/esm-node/deploy-output/npmAliases.mjs +92 -6
  130. package/dist/esm-node/deploy-output/plugin.mjs +24 -6
  131. package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
  132. package/dist/esm-node/deploy-output/target.mjs +50 -3
  133. package/dist/esm-node/deploy-output/trace-files.mjs +48 -0
  134. package/dist/esm-node/module-federation-config/exposes.mjs +19 -0
  135. package/dist/esm-node/module-federation-config/index.mjs +5 -0
  136. package/dist/esm-node/module-federation-config/inspect.mjs +66 -0
  137. package/dist/esm-node/module-federation-config/object-literal.mjs +44 -0
  138. package/dist/esm-node/module-federation-config/syntax.mjs +102 -0
  139. package/dist/esm-node/module-federation-config/types.mjs +1 -0
  140. package/dist/esm-node/native-type-checker.mjs +175 -18
  141. package/dist/esm-node/policy-defaults.mjs +36 -30
  142. package/dist/esm-node/release-envelope/canonical.mjs +2 -9
  143. package/dist/esm-node/release-envelope/contract.mjs +22 -0
  144. package/dist/esm-node/release-envelope/framework-output.mjs +297 -106
  145. package/dist/esm-node/release-envelope/index.mjs +149 -38
  146. package/dist/esm-node/release-envelope/plugin.mjs +24 -15
  147. package/dist/esm-node/release-envelope/renderer-output-stamp.mjs +113 -0
  148. package/dist/esm-node/release-envelope/types.mjs +2 -3
  149. package/dist/esm-node/release-identity.mjs +9 -6
  150. package/dist/esm-node/renderer-build-identity.mjs +93 -0
  151. package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  152. package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  153. package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  154. package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +547 -103
  155. package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  156. package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
  157. package/dist/esm-node/workspace-topology.mjs +22 -0
  158. package/dist/types/backend-federation-build/codegen.d.ts +30 -0
  159. package/dist/types/backend-federation-build/config.d.ts +10 -57
  160. package/dist/types/backend-federation-build/emit.d.ts +12 -2
  161. package/dist/types/backend-federation-build/index.d.ts +1 -1
  162. package/dist/types/build-config/build-environment.d.ts +4 -10
  163. package/dist/types/build-config/internal-effect-discovery.d.ts +6 -0
  164. package/dist/types/build-config/public.d.ts +1 -1
  165. package/dist/types/build-config/remote-address.d.ts +3 -0
  166. package/dist/types/cloudflare/artifacts.d.ts +7 -6
  167. package/dist/types/cloudflare/constants.d.ts +1 -1
  168. package/dist/types/cloudflare/delivery-unit.d.ts +38 -7
  169. package/dist/types/cloudflare/native-renderer.d.ts +30 -0
  170. package/dist/types/cloudflare/renderer-identity.d.ts +25 -0
  171. package/dist/types/cloudflare/worker-manifest.d.ts +20 -4
  172. package/dist/types/cloudflare/worker-options.d.ts +20 -0
  173. package/dist/types/cloudflare/wrangler-config.d.ts +55 -2
  174. package/dist/types/cloudflare-builder.d.ts +51 -8
  175. package/dist/types/cloudflare-output-contract.d.ts +5 -3
  176. package/dist/types/cloudflare-output-verifier/identity.d.ts +7 -3
  177. package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
  178. package/dist/types/config.d.ts +102 -13
  179. package/dist/types/deploy-output/plugin.d.ts +29 -9
  180. package/dist/types/deploy-output/public-assets.d.ts +51 -0
  181. package/dist/types/deploy-output/target.d.ts +28 -5
  182. package/dist/types/deploy-output/trace-files.d.ts +17 -0
  183. package/dist/types/module-federation-config/exposes.d.ts +11 -0
  184. package/dist/types/module-federation-config/index.d.ts +5 -0
  185. package/dist/types/module-federation-config/inspect.d.ts +2 -0
  186. package/dist/types/module-federation-config/object-literal.d.ts +4 -0
  187. package/dist/types/module-federation-config/syntax.d.ts +7 -0
  188. package/dist/types/module-federation-config/types.d.ts +20 -0
  189. package/dist/types/native-type-checker.d.ts +28 -3
  190. package/dist/types/policy-defaults.d.ts +12 -2
  191. package/dist/types/release-envelope/canonical.d.ts +5 -5
  192. package/dist/types/release-envelope/contract.d.mts +8 -0
  193. package/dist/types/release-envelope/framework-output.d.ts +23 -13
  194. package/dist/types/release-envelope/index.d.ts +17 -5
  195. package/dist/types/release-envelope/plugin.d.ts +10 -6
  196. package/dist/types/release-envelope/renderer-output-stamp.d.ts +34 -0
  197. package/dist/types/release-envelope/types.d.ts +42 -4
  198. package/dist/types/release-identity.d.ts +13 -2
  199. package/dist/types/renderer-build-identity.d.ts +41 -0
  200. package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
  201. package/dist/types/workspace-topology.d.ts +91 -0
  202. package/package.json +87 -27
  203. package/src/backend-federation-build/codegen.ts +28 -19
  204. package/src/backend-federation-build/config.ts +189 -123
  205. package/src/backend-federation-build/emit.ts +119 -38
  206. package/src/backend-federation-build/index.ts +4 -1
  207. package/src/build-config/build-environment.ts +69 -293
  208. package/src/build-config/internal-effect-discovery.ts +458 -0
  209. package/src/build-config/public.ts +0 -1
  210. package/src/build-config/remote-address.ts +33 -0
  211. package/src/cloudflare/artifacts.ts +22 -43
  212. package/src/cloudflare/delivery-unit.ts +283 -60
  213. package/src/cloudflare/index.ts +35 -19
  214. package/src/cloudflare/native-renderer.ts +117 -0
  215. package/src/cloudflare/renderer-identity.ts +171 -0
  216. package/src/cloudflare/worker-manifest.ts +115 -13
  217. package/src/cloudflare/worker-options.ts +60 -0
  218. package/src/cloudflare/wrangler-config.ts +390 -14
  219. package/src/cloudflare-builder.ts +543 -170
  220. package/src/cloudflare-output-contract.ts +45 -2
  221. package/src/cloudflare-output-verifier/identity.ts +138 -7
  222. package/src/cloudflare-output-verifier/index.ts +20 -0
  223. package/src/cloudflare-output-verifier/manifest.ts +77 -0
  224. package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
  225. package/src/cloudflare-output-verifier/worker-bundles.ts +27 -11
  226. package/src/config.ts +105 -19
  227. package/src/deploy-output/npmAliases.ts +141 -4
  228. package/src/deploy-output/plugin.ts +55 -18
  229. package/src/deploy-output/public-assets.ts +224 -0
  230. package/src/deploy-output/target.ts +97 -14
  231. package/src/deploy-output/trace-files.ts +80 -0
  232. package/src/module-federation-config/exposes.ts +46 -0
  233. package/src/module-federation-config/index.ts +22 -0
  234. package/src/module-federation-config/inspect.ts +157 -0
  235. package/src/module-federation-config/object-literal.ts +76 -0
  236. package/src/module-federation-config/syntax.ts +212 -0
  237. package/src/module-federation-config/types.ts +22 -0
  238. package/src/native-type-checker.ts +284 -27
  239. package/src/policy-defaults.ts +47 -17
  240. package/src/release-envelope/canonical.ts +6 -18
  241. package/src/release-envelope/contract.mjs +28 -0
  242. package/src/release-envelope/framework-output.ts +681 -156
  243. package/src/release-envelope/index.ts +315 -60
  244. package/src/release-envelope/plugin.ts +54 -38
  245. package/src/release-envelope/renderer-output-stamp.ts +210 -0
  246. package/src/release-envelope/types.ts +75 -6
  247. package/src/release-identity.ts +30 -4
  248. package/src/renderer-build-identity.ts +208 -0
  249. package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  250. package/src/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  251. package/src/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  252. package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +547 -103
  253. package/src/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  254. package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
  255. package/src/workspace-topology.ts +125 -0
  256. package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +0 -11
  257. package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +0 -281
  258. package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  259. package/dist/cjs/templates/cloudflare-worker-path.mjs +0 -98
  260. package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +0 -11
  261. package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +0 -281
  262. package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  263. package/dist/esm/templates/cloudflare-worker-path.mjs +0 -98
  264. package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +0 -11
  265. package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +0 -281
  266. package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  267. package/dist/esm-node/templates/cloudflare-worker-path.mjs +0 -98
  268. package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +0 -5
  269. package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +0 -48
  270. package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +0 -13
  271. package/dist/types/templates/cloudflare-worker-path.d.mts +0 -21
  272. package/src/templates/cloudflare-worker-fs-promises.mjs +0 -11
  273. package/src/templates/cloudflare-worker-loadable-server.mjs +0 -281
  274. package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  275. package/src/templates/cloudflare-worker-path.mjs +0 -98
@@ -1,13 +1,21 @@
1
1
  import node_path from "node:path";
2
- import { ULTRAMODERN_BUILD_ARTIFACT_PATH, isUltramodernBuildArtifact, nonEmptyString, toDeliveryUnitIdentity } from "@modern-js/backend-federation-contracts";
2
+ import { isDeepStrictEqual } from "node:util";
3
+ import { ULTRAMODERN_BUILD_ARTIFACT_FILE, ULTRAMODERN_BUILD_ARTIFACT_PATH, assertRendererProfileCompatibility, assertUltramodernBuildArtifact, formatBackendFederationValidationErrors, immutableRendererRouterBindings, nonEmptyString, toDeliveryUnitIdentity, validateRendererIdentity, validateRendererProfile, validateRendererRouterBindings } from "@modern-js/backend-federation-contracts";
3
4
  import { fs } from "@modern-js/utils";
4
5
  import { resolveUltramodernReleaseIdentity } from "../release-identity.mjs";
5
6
  import { isRecord } from "./utils.mjs";
6
- const COMPACT_CONFIG_PATH = '.modernjs/ultramodern.json';
7
+ const TOPOLOGY_PATH = 'topology/reference-topology.json';
8
+ const RENDERER_PROJECTION_FIELDS = [
9
+ 'renderer',
10
+ 'rendererIdentity',
11
+ 'rendererIdentities',
12
+ 'rendererProfile',
13
+ 'routerBindings'
14
+ ];
7
15
  const findWorkspaceRoot = async (appDirectory)=>{
8
16
  let current = node_path.resolve(appDirectory);
9
17
  for(;;){
10
- if (await fs.pathExists(node_path.join(current, COMPACT_CONFIG_PATH))) return current;
18
+ if (await fs.pathExists(node_path.join(current, TOPOLOGY_PATH))) return current;
11
19
  const parent = node_path.dirname(current);
12
20
  if (parent === current) return;
13
21
  current = parent;
@@ -24,51 +32,83 @@ const stampReleaseIdentity = (identity, workspaceRoot)=>{
24
32
  })
25
33
  };
26
34
  };
27
- const resolveCompactApp = async (appDirectory)=>{
35
+ const resolveTopologyApp = async (appDirectory)=>{
28
36
  const workspaceRoot = await findWorkspaceRoot(appDirectory);
29
37
  if (!workspaceRoot) return;
30
- let compactConfig;
31
- try {
32
- compactConfig = await fs.readJSON(node_path.join(workspaceRoot, COMPACT_CONFIG_PATH));
33
- } catch {
34
- return {
35
- workspaceRoot
36
- };
37
- }
38
- if (!isRecord(compactConfig)) return {
39
- workspaceRoot
40
- };
41
- const topology = isRecord(compactConfig.topology) ? compactConfig.topology : void 0;
42
- const apps = Array.isArray(topology?.apps) ? topology.apps : void 0;
43
- if (!apps) return {
44
- app: compactConfig,
45
- workspaceRoot
46
- };
38
+ const topology = await fs.readJSON(node_path.join(workspaceRoot, TOPOLOGY_PATH));
39
+ if (!isRecord(topology) || !isRecord(topology.shell) || !Array.isArray(topology.verticals) || void 0 !== topology.shells && !Array.isArray(topology.shells)) throw new Error('[cloudflare-delivery-unit] Invalid declared reference topology.');
40
+ const apps = [
41
+ topology.shell,
42
+ ...topology.verticals,
43
+ ...Array.isArray(topology.shells) ? topology.shells : []
44
+ ];
47
45
  const resolvedAppDirectory = node_path.resolve(appDirectory);
48
46
  const app = apps.find((candidate)=>{
49
47
  if (!isRecord(candidate)) return false;
50
48
  const appPath = nonEmptyString(candidate.path);
51
49
  return void 0 !== appPath && node_path.resolve(workspaceRoot, appPath.replace(/^\.\/+/u, '')) === resolvedAppDirectory;
52
50
  });
51
+ if (!isRecord(app)) throw new Error(`[cloudflare-delivery-unit] ${appDirectory} is missing from the declared reference topology.`);
53
52
  return {
54
- ...isRecord(app) ? {
55
- app
56
- } : {},
53
+ app,
54
+ role: app === topology.shell || Array.isArray(topology.shells) && topology.shells.includes(app) ? 'shell' : 'microvertical',
57
55
  workspaceRoot
58
56
  };
59
57
  };
60
- const createDeliveryUnitStamp = (identity, app)=>{
61
- const surfaceProfile = nonEmptyString(app?.surfaceProfile);
58
+ const resolveTopologyReleaseRole = async (appDirectory)=>{
59
+ const resolved = await resolveTopologyApp(appDirectory);
60
+ if (!resolved?.app) return;
61
+ const kind = 'shell' === resolved.role ? 'shell' : 'vertical';
62
+ if (resolved.app.kind !== kind) throw new Error('[cloudflare-delivery-unit] Declared app kind disagrees with its topology membership.');
63
+ return resolved.role;
64
+ };
65
+ const createTopologyDeliveryUnitStamp = (identity, app)=>{
66
+ const surfaceProfile = nonEmptyString(app.surfaceProfile);
67
+ if (void 0 !== surfaceProfile && ![
68
+ 'api-only',
69
+ 'ui-only',
70
+ 'full-stack'
71
+ ].includes(surfaceProfile)) throw new Error('[cloudflare-delivery-unit] Declared app has an invalid surface profile.');
62
72
  const emitsUi = 'api-only' !== surfaceProfile;
63
73
  const emitsApi = 'ui-only' !== surfaceProfile;
74
+ const appId = nonEmptyString(app.id);
75
+ if (!appId) throw new Error('[cloudflare-delivery-unit] Declared app requires an id.');
76
+ let ui;
77
+ if (emitsUi) if (RENDERER_PROJECTION_FIELDS.every((field)=>!Object.hasOwn(app, field))) ui = {
78
+ ...identity,
79
+ surface: 'ui'
80
+ };
81
+ else {
82
+ const routerBindings = isRecord(app.routerBindings) ? app.routerBindings : {};
83
+ const errors = [
84
+ ...validateRendererIdentity(app.rendererIdentity).errors,
85
+ ...validateRendererProfile(app.rendererProfile).errors,
86
+ ...validateRendererRouterBindings(app.routerBindings, Object.keys(routerBindings), 'routerBindings').errors
87
+ ];
88
+ if (errors.length) throw new Error(`[cloudflare-delivery-unit] ${formatBackendFederationValidationErrors(errors)}`);
89
+ const rendererIdentity = app.rendererIdentity;
90
+ const rendererProfile = app.rendererProfile;
91
+ if (!Object.hasOwn(routerBindings, rendererIdentity.entryName)) throw new Error('[cloudflare-delivery-unit] Topology routerBindings must include the primary renderer identity entry.');
92
+ const declaredIdentity = toDeliveryUnitIdentity(app.deliveryUnit);
93
+ if (rendererIdentity.renderer !== app.renderer || rendererIdentity.renderer !== rendererProfile.renderer || rendererIdentity.appId !== app.id || rendererIdentity.buildId !== declaredIdentity?.buildMarker) throw new Error('[cloudflare-delivery-unit] Topology UI renderer metadata must match its app and delivery-unit identity.');
94
+ ui = {
95
+ ...identity,
96
+ surface: 'ui',
97
+ rendererIdentity: {
98
+ ...rendererIdentity,
99
+ buildId: identity.buildMarker
100
+ },
101
+ rendererProfile,
102
+ routerBindings: immutableRendererRouterBindings(routerBindings)
103
+ };
104
+ }
105
+ else if (void 0 !== app.rendererIdentity || void 0 !== app.rendererProfile || void 0 !== app.routerBindings) throw new Error('[cloudflare-delivery-unit] API-only topology must not declare a UI renderer identity or profile.');
64
106
  return {
65
107
  ...identity,
108
+ appId,
66
109
  surfaces: {
67
- ...emitsUi ? {
68
- ui: {
69
- ...identity,
70
- surface: 'ui'
71
- }
110
+ ...ui ? {
111
+ ui
72
112
  } : {},
73
113
  ...emitsApi ? {
74
114
  api: {
@@ -80,22 +120,61 @@ const createDeliveryUnitStamp = (identity, app)=>{
80
120
  };
81
121
  };
82
122
  const resolveTopologyDeliveryUnit = async (appDirectory)=>{
83
- const resolved = await resolveCompactApp(appDirectory);
123
+ const resolved = await resolveTopologyApp(appDirectory);
84
124
  if (!resolved?.app) return;
85
125
  const identity = toDeliveryUnitIdentity(resolved.app.deliveryUnit);
86
- return identity ? createDeliveryUnitStamp(stampReleaseIdentity(identity, resolved.workspaceRoot), resolved.app) : void 0;
126
+ if (!identity) throw new Error('[cloudflare-delivery-unit] Declared app is missing a valid delivery-unit identity.');
127
+ return createTopologyDeliveryUnitStamp(stampReleaseIdentity(identity, resolved.workspaceRoot), resolved.app);
87
128
  };
88
- const resolveWorkerDeliveryUnitStamp = async (appDirectory)=>{
89
- const buildArtifactPath = node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH);
90
- let identity;
91
- if (await fs.pathExists(buildArtifactPath)) {
92
- const artifact = await fs.readJSON(buildArtifactPath);
93
- if (!isUltramodernBuildArtifact(artifact)) return;
94
- identity = toDeliveryUnitIdentity(artifact.deliveryUnit);
129
+ const resolveWorkerDeliveryUnitStamp = async (appDirectory, distDirectory)=>{
130
+ const buildArtifactPath = node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE);
131
+ if (!await fs.pathExists(buildArtifactPath)) {
132
+ if (await fs.pathExists(node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH))) throw new Error('[cloudflare-delivery-unit] Finalized build artifact is required before worker output stamping.');
133
+ return;
134
+ }
135
+ const artifact = await fs.readJSON(buildArtifactPath);
136
+ assertUltramodernBuildArtifact(artifact);
137
+ const identity = {
138
+ unitId: artifact.deliveryUnit.unitId,
139
+ buildMarker: artifact.deliveryUnit.buildMarker,
140
+ sourceRevision: artifact.deliveryUnit.sourceRevision
141
+ };
142
+ const resolved = await resolveTopologyApp(appDirectory);
143
+ const stampedArtifact = artifact;
144
+ let emitsApi = true;
145
+ if (resolved?.app) {
146
+ if (stampedArtifact.deliveryUnit.appId !== resolved.app.id) throw new Error('[cloudflare-delivery-unit] Build artifact appId must match the declared topology app.');
147
+ const declaredIdentity = toDeliveryUnitIdentity(resolved.app.deliveryUnit);
148
+ if (!declaredIdentity) throw new Error('[cloudflare-delivery-unit] Declared app is missing a valid delivery-unit identity.');
149
+ const expected = createTopologyDeliveryUnitStamp(identity, resolved.app);
150
+ for (const field of [
151
+ 'unitId'
152
+ ])if (declaredIdentity[field] !== identity[field]) throw new Error(`[cloudflare-delivery-unit] Build artifact ${field} must match the declared topology delivery-unit identity.`);
153
+ const ui = stampedArtifact.surfaces.ui;
154
+ if (Boolean(expected.surfaces.ui) !== Boolean(ui)) throw new Error('[cloudflare-delivery-unit] Build artifact UI surface must match the declared topology surface profile.');
155
+ if (expected.surfaces.ui?.rendererIdentity && ui) {
156
+ if (!isDeepStrictEqual(expected.surfaces.ui.routerBindings, ui.routerBindings)) throw new Error('[cloudflare-delivery-unit] Finalized build routerBindings must match the declared topology router bindings.');
157
+ for (const field of [
158
+ 'renderer',
159
+ 'appId',
160
+ 'entryName',
161
+ 'protocolVersion',
162
+ 'buildId'
163
+ ])if (expected.surfaces.ui.rendererIdentity[field] !== ui.rendererIdentity[field]) throw new Error(`[cloudflare-delivery-unit] Build artifact UI rendererIdentity.${field} must match the declared topology renderer identity.`);
164
+ assertRendererProfileCompatibility(expected.surfaces.ui.rendererProfile, ui.rendererProfile);
165
+ }
166
+ emitsApi = void 0 !== expected.surfaces.api;
95
167
  }
96
- if (!identity) return;
97
- const resolved = await resolveCompactApp(appDirectory);
98
- if (resolved) identity = stampReleaseIdentity(identity, resolved.workspaceRoot);
99
- return createDeliveryUnitStamp(identity, resolved?.app);
168
+ return {
169
+ ...stampedArtifact.deliveryUnit,
170
+ surfaces: {
171
+ ...emitsApi ? {
172
+ api: stampedArtifact.surfaces.api
173
+ } : {},
174
+ ...stampedArtifact.surfaces.ui ? {
175
+ ui: stampedArtifact.surfaces.ui
176
+ } : {}
177
+ }
178
+ };
100
179
  };
101
- export { resolveTopologyDeliveryUnit, resolveWorkerDeliveryUnitStamp };
180
+ export { resolveTopologyDeliveryUnit, resolveTopologyReleaseRole, resolveWorkerDeliveryUnitStamp };
@@ -1,4 +1,5 @@
1
1
  import node_path from "node:path";
2
+ import { BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, ULTRAMODERN_BUILD_ARTIFACT_FILE } from "@modern-js/backend-federation-contracts";
2
3
  import { fs } from "@modern-js/utils";
3
4
  import { createCloudflareOutputPlan } from "../cloudflare-output-plan.mjs";
4
5
  import { assertCloudflareOutput } from "../cloudflare-output-verifier/index.mjs";
@@ -6,7 +7,7 @@ import { readTemplate } from "../read-template.mjs";
6
7
  import { MICROVERTICAL_RELEASE_ENVELOPE_PATH, emitCloudflareStagedReleaseEnvelope, emitFrameworkMicroVerticalReleaseEnvelope, stageCloudflareReleaseEnvelope, verifyBuildOutputReleaseEnvelope, verifyCloudflareReleaseEnvelopeStaging } from "../release-envelope/framework-output.mjs";
7
8
  import { copyCloudflareArtifacts, copyCloudflareD1Migrations, copyCloudflarePublicAssets, getCloudflareArtifacts, getCloudflarePublicAssets } from "./artifacts.mjs";
8
9
  import { ROUTE_SPEC_FILE, ROUTE_SPEC_OUTPUT, WORKER_BUNDLE_DIRECTORY } from "./constants.mjs";
9
- import { resolveTopologyDeliveryUnit, resolveWorkerDeliveryUnitStamp } from "./delivery-unit.mjs";
10
+ import { resolveWorkerDeliveryUnitStamp } from "./delivery-unit.mjs";
10
11
  import { createWorkerManifest, createWorkerModuleLoaders, getPublicAssetExcludes, shouldCopyToPublicAssets, shouldCopyToWorkerBundle } from "./worker-manifest.mjs";
11
12
  import { createWranglerConfig } from "./wrangler-config.mjs";
12
13
  const CLOUDFLARE_ENTRY_TEMPLATE_FRAGMENTS = [
@@ -31,20 +32,28 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
31
32
  const publicAssetExcludes = getPublicAssetExcludes(appDirectory, modernConfig);
32
33
  const releaseEnvelopeEnabled = api.isPluginExists('@modern-js/ultramodern-release-envelope');
33
34
  let hasReleaseEnvelope = false;
35
+ let declaredPublicAssets = [];
34
36
  return {
35
37
  async prepare () {
36
38
  await fs.remove(outputDirectory);
37
39
  },
38
40
  async writeOutput () {
39
- if (releaseEnvelopeEnabled && await fs.pathExists(node_path.join(distDirectory, MICROVERTICAL_RELEASE_ENVELOPE_PATH))) await emitFrameworkMicroVerticalReleaseEnvelope({
41
+ if (releaseEnvelopeEnabled) await emitFrameworkMicroVerticalReleaseEnvelope({
40
42
  apiOnly,
43
+ appDirectory,
41
44
  distDirectory,
45
+ role: modernConfig.deploy?.releaseEnvelopeRole,
42
46
  target: 'cloudflare'
43
47
  });
44
48
  if (releaseEnvelopeEnabled) await verifyBuildOutputReleaseEnvelope(distDirectory, 'cloudflare');
45
49
  await fs.copy(distDirectory, publicDirectory, {
46
50
  filter: (src)=>{
47
51
  const relativePath = node_path.relative(distDirectory, src).replace(/\\/gu, '/');
52
+ if (apiOnly && '' !== relativePath && ![
53
+ BACKEND_FEDERATION_MANIFEST_FILE,
54
+ BACKEND_FEDERATION_REMOTE_ENTRY_FILE,
55
+ ULTRAMODERN_BUILD_ARTIFACT_FILE
56
+ ].includes(relativePath)) return false;
48
57
  return 'release' !== relativePath && !relativePath.startsWith('release/') && 'public' !== relativePath && !relativePath.startsWith('public/') && shouldCopyToPublicAssets(src, distDirectory, publicAssetExcludes);
49
58
  }
50
59
  });
@@ -68,12 +77,12 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
68
77
  await fs.writeJSON(outputPlan.paths.workerPackage, outputPlan.packages.worker);
69
78
  }
70
79
  await copyCloudflareArtifacts(appDirectory, outputDirectory, cloudflareArtifacts);
71
- await copyCloudflarePublicAssets(appDirectory, publicDirectory, getCloudflarePublicAssets(modernConfig));
80
+ declaredPublicAssets = await copyCloudflarePublicAssets(appDirectory, outputDirectory, getCloudflarePublicAssets(modernConfig));
72
81
  await copyCloudflareD1Migrations(appDirectory, outputDirectory, modernConfig);
73
82
  await fs.writeJSON(wranglerConfigPath, createWranglerConfig(appDirectory, modernConfig), {
74
83
  spaces: 2
75
84
  });
76
- const deliveryUnitStamp = await resolveWorkerDeliveryUnitStamp(appDirectory);
85
+ const deliveryUnitStamp = await resolveWorkerDeliveryUnitStamp(appDirectory, distDirectory);
77
86
  await fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig, appContext, deliveryUnitStamp), {
78
87
  spaces: 2
79
88
  });
@@ -87,7 +96,7 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
87
96
  const template = await readCloudflareEntryTemplate();
88
97
  const manifest = await fs.readJSON(workerManifestPath);
89
98
  await fs.writeFile(workerEntryPath, template.replace('p_workerManifest', JSON.stringify(manifest, null, 2)).replace('p_workerModuleLoaders', createWorkerModuleLoaders(manifest)));
90
- const topologyDeliveryUnit = await resolveTopologyDeliveryUnit(appDirectory);
99
+ const topologyDeliveryUnit = await resolveWorkerDeliveryUnitStamp(appDirectory, distDirectory);
91
100
  await assertCloudflareOutput({
92
101
  outputDirectory,
93
102
  importWorker: false,
@@ -97,6 +106,7 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
97
106
  });
98
107
  if (hasReleaseEnvelope) {
99
108
  await emitCloudflareStagedReleaseEnvelope({
109
+ declaredPublicAssets,
100
110
  distDirectory,
101
111
  outputDirectory
102
112
  });
@@ -0,0 +1,48 @@
1
+ import promises from "node:fs/promises";
2
+ import node_path from "node:path";
3
+ import { WORKER_BUNDLE_DIRECTORY } from "./constants.mjs";
4
+ import { isRecord } from "./utils.mjs";
5
+ const NATIVE_RENDERER_WORKER_RESOURCES_FILE = `${WORKER_BUNDLE_DIRECTORY}/native-renderer.json`;
6
+ const invalid = (detail)=>new Error(`Invalid native renderer worker resources (${NATIVE_RENDERER_WORKER_RESOURCES_FILE}): ${detail}. Rebuild the application with deploy.worker.ssr before deploying.`);
7
+ function validateEntry(entryName, value) {
8
+ if (!isRecord(value)) throw invalid(`entry ${entryName} is not an object`);
9
+ const { assets, hydrationBuildId, serverConfig, csrFallbackHeader, nonce } = value;
10
+ if (!Array.isArray(assets) || !assets.length) throw invalid(`entry ${entryName} has no document assets`);
11
+ if (void 0 !== hydrationBuildId && ('string' != typeof hydrationBuildId || !hydrationBuildId.trim())) throw invalid(`entry ${entryName} has an empty hydration build ID`);
12
+ if (!isRecord(serverConfig) || ![
13
+ true,
14
+ false,
15
+ 'stream'
16
+ ].includes(serverConfig.ssr) || void 0 !== serverConfig.ssrByRouteIds && (!Array.isArray(serverConfig.ssrByRouteIds) || serverConfig.ssrByRouteIds.some((id)=>'string' != typeof id))) throw invalid(`entry ${entryName} has an invalid server config`);
17
+ for (const [field, text] of [
18
+ [
19
+ 'csrFallbackHeader',
20
+ csrFallbackHeader
21
+ ],
22
+ [
23
+ 'nonce',
24
+ nonce
25
+ ]
26
+ ])if (void 0 !== text && ('string' != typeof text || !text)) throw invalid(`entry ${entryName} has an invalid ${field}`);
27
+ }
28
+ async function readNativeRendererWorkerResources(distDirectory, build) {
29
+ let bytes;
30
+ try {
31
+ bytes = await promises.readFile(node_path.join(distDirectory, NATIVE_RENDERER_WORKER_RESOURCES_FILE), 'utf8');
32
+ } catch (error) {
33
+ if (error instanceof Error && 'code' in error && 'ENOENT' === error.code) return;
34
+ throw error;
35
+ }
36
+ const value = JSON.parse(bytes);
37
+ if (!isRecord(value) || 'ultramodern-native-worker-resources' !== value.schema || 1 !== value.version || 'string' != typeof value.renderer || !value.renderer || !isRecord(value.entries)) throw invalid('unknown schema');
38
+ if (value.renderer !== build?.renderer.name || !build.renderer.nativeDocuments) throw invalid(`renderer ${value.renderer} is not the built native-document renderer`);
39
+ const entries = value.entries;
40
+ const nativeEntries = Object.entries(build.identities).filter(([, identity])=>identity.renderer === value.renderer);
41
+ for (const [entryName] of nativeEntries)if (!Object.hasOwn(entries, entryName)) throw invalid(`entry ${entryName} is missing`);
42
+ for (const [entryName, entry] of Object.entries(entries)){
43
+ if (!nativeEntries.some(([name])=>name === entryName)) throw invalid(`entry ${entryName} has no built ${value.renderer} renderer identity`);
44
+ validateEntry(entryName, entry);
45
+ }
46
+ return value;
47
+ }
48
+ export { NATIVE_RENDERER_WORKER_RESOURCES_FILE, readNativeRendererWorkerResources };
@@ -0,0 +1,57 @@
1
+ import promises from "node:fs/promises";
2
+ import node_path from "node:path";
3
+ import { isDeepStrictEqual } from "node:util";
4
+ import { formatBackendFederationValidationErrors, validateRendererIdentity, validateRendererProfile } from "@modern-js/backend-federation-contracts";
5
+ import { isRecord } from "./utils.mjs";
6
+ async function readWorkerRendererBuild(distDirectory, routes, deliveryUnit) {
7
+ let bytes;
8
+ try {
9
+ bytes = await promises.readFile(node_path.join(distDirectory, 'renderer-build.json'), 'utf8');
10
+ } catch (error) {
11
+ if (error instanceof Error && 'code' in error && 'ENOENT' === error.code) return;
12
+ throw error;
13
+ }
14
+ const build = JSON.parse(bytes);
15
+ if (!isRecord(build) || 'ultramodern-renderer-build' !== build.schema || 2 !== build.version || 'string' != typeof build.buildId || !/^[a-f0-9]{64}$/u.test(build.buildId) || !isRecord(build.profile) || build.renderer !== build.profile.renderer || !isRecord(build.worker) || 'boolean' != typeof build.worker.nativeDocuments || 'boolean' != typeof build.worker.rsc || !isRecord(build.entries) || !Object.keys(build.entries).length) throw new Error('Invalid or outdated renderer-build.json for the Cloudflare worker; rebuild the application.');
16
+ const { renderer, protocolVersion, compiler, hydration, router } = build.profile;
17
+ const validation = validateRendererProfile({
18
+ renderer,
19
+ protocolVersion,
20
+ compiler,
21
+ hydration,
22
+ router
23
+ });
24
+ if (!validation.ok) throw new Error(formatBackendFederationValidationErrors(validation.errors));
25
+ const identities = {};
26
+ let appId;
27
+ for (const [entryName, value] of Object.entries(build.entries)){
28
+ const validation = validateRendererIdentity(value);
29
+ if (!validation.ok) throw new Error(formatBackendFederationValidationErrors(validation.errors));
30
+ const identity = value;
31
+ if ('__proto__' === entryName || identity.entryName !== entryName || identity.renderer !== renderer || identity.protocolVersion !== protocolVersion || identity.buildId !== build.buildId || void 0 !== appId && identity.appId !== appId) throw new Error('Cloudflare renderer entry identity conflicts with its built manifest');
32
+ appId = identity.appId;
33
+ identities[entryName] = Object.freeze({
34
+ ...identity
35
+ });
36
+ }
37
+ for (const route of routes){
38
+ if (false === route.isSSR && void 0 === route.entryName && void 0 === route.worker && void 0 === route.bundle && (void 0 === route.isRSC || false === route.isRSC) && (void 0 === route.isStream || false === route.isStream) && 'string' == typeof route.entryPath && route.entryPath.startsWith('public/') && !route.entryPath.includes('\\') && node_path.posix.normalize(route.entryPath) === route.entryPath) {
39
+ const asset = await promises.stat(node_path.join(distDirectory, route.entryPath));
40
+ if (asset.isFile()) continue;
41
+ }
42
+ if (true !== route.isApi || false !== route.isSSR || void 0 !== route.entryName || '' !== route.entryPath || void 0 !== route.worker || void 0 !== route.bundle || void 0 !== route.isRSC && false !== route.isRSC || void 0 !== route.isStream && false !== route.isStream) {
43
+ if ('string' != typeof route.entryName || !Object.hasOwn(identities, route.entryName)) throw new Error(`Cloudflare generated route has no built renderer identity for ${String(route.entryName)}`);
44
+ }
45
+ }
46
+ const ui = deliveryUnit?.surfaces.ui;
47
+ if (ui && !isDeepStrictEqual(identities[ui.rendererIdentity.entryName], ui.rendererIdentity)) throw new Error('Cloudflare built renderer identity conflicts with its finalized delivery unit');
48
+ return Object.freeze({
49
+ renderer: Object.freeze({
50
+ name: renderer,
51
+ nativeDocuments: build.worker.nativeDocuments,
52
+ rsc: build.worker.rsc
53
+ }),
54
+ identities: Object.freeze(identities)
55
+ });
56
+ }
57
+ export { readWorkerRendererBuild };
@@ -1,11 +1,14 @@
1
1
  import node_path from "node:path";
2
2
  import { fs } from "@modern-js/utils";
3
+ import { getCloudflareWorkerRouteDataEntryName } from "../cloudflare-output-contract.mjs";
3
4
  import { readRouteSpec } from "./artifacts.mjs";
4
5
  import { ASSETS_BINDING, BFF_EFFECT_WORKER_DISPATCHER_EXPORT, BFF_EFFECT_WORKER_ENTRY, CLOUDFLARE_RUNTIME_TYPE, CLOUDFLARE_WORKER_BUNDLE_FORMAT, DEFAULT_SERVER_ONLY_PUBLIC_ASSET_EXCLUDES, EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE, LOADABLE_STATS_FILE, PUBLIC_ASSETS_DIRECTORY, ROUTE_MANIFEST_FILE, ROUTE_SPEC_FILE, ROUTE_SPEC_OUTPUT, SERVER_BUNDLE_DIRECTORY, WORKER_BUNDLE_DIRECTORY, WORKER_ENTRY } from "./constants.mjs";
5
6
  import { createI18nWorkerManifest } from "./i18n-worker.mjs";
7
+ import { readNativeRendererWorkerResources } from "./native-renderer.mjs";
8
+ import { readWorkerRendererBuild } from "./renderer-identity.mjs";
6
9
  import { createCloudflareWorkerSecurityPolicy } from "./security-policies.mjs";
7
10
  import { isRecord, normalizeRelativePath } from "./utils.mjs";
8
- import { createWorkerManifestServiceBindings, createWorkerServiceBindings } from "./wrangler-config.mjs";
11
+ import { createWorkerManifestServiceBindings, createWorkerServiceBindings, createWorkerVpcServiceBindings, getWorkerEffectBffPrefix } from "./wrangler-config.mjs";
9
12
  const createMissingEffectBffWorkerError = (outputDirectory, worker)=>new Error(`Cloudflare Effect API runtime is configured, but the BFF worker bundle is missing: ${node_path.join(outputDirectory, worker)}. ${EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE}`);
10
13
  const createEffectBffWorkerManifest = (modernConfig)=>{
11
14
  const bff = modernConfig.bff;
@@ -56,31 +59,59 @@ const createModuleFederationWorkerManifest = async (outputDirectory)=>{
56
59
  }
57
60
  ];
58
61
  });
59
- return name && exposes.length > 0 ? {
62
+ if (!name || 0 === exposes.length) return;
63
+ const routeManifestPath = node_path.join(outputDirectory, PUBLIC_ASSETS_DIRECTORY, ROUTE_MANIFEST_FILE);
64
+ const routeManifest = await fs.pathExists(routeManifestPath) ? await fs.readJson(routeManifestPath) : void 0;
65
+ const routeAssets = isRecord(routeManifest) && isRecord(routeManifest.routeAssets) ? Object.values(routeManifest.routeAssets).filter(isRecord) : [];
66
+ const routeCss = [
67
+ ...new Set(routeAssets.flatMap((routeAsset)=>[
68
+ routeAsset.referenceCssAssets,
69
+ routeAsset.assets
70
+ ].flatMap((value)=>Array.isArray(value) ? value : []).filter((asset)=>'string' == typeof asset && asset.endsWith('.css'))))
71
+ ];
72
+ return {
60
73
  name,
61
- exposes
62
- } : void 0;
74
+ exposes,
75
+ routeCss
76
+ };
63
77
  };
64
78
  const createWorkerManifest = async (outputDirectory, modernConfig, appContext, deliveryUnitStamp)=>{
65
79
  const routeSpec = await readRouteSpec(outputDirectory);
66
80
  const routes = await Promise.all(routeSpec.routes.map(async (route)=>{
67
81
  const worker = 'string' == typeof route.worker ? route.worker : void 0;
82
+ const routeDataWorker = route.isSSR && 'string' == typeof route.entryName ? `${WORKER_BUNDLE_DIRECTORY}/${getCloudflareWorkerRouteDataEntryName(route.entryName)}.js` : void 0;
83
+ const hasRouteDataWorker = routeDataWorker ? await fs.pathExists(node_path.join(outputDirectory, routeDataWorker)) : false;
68
84
  return {
69
85
  urlPath: route.urlPath,
70
86
  entryName: route.entryName,
71
87
  entryPath: route.entryPath,
72
88
  isSSR: Boolean(route.isSSR),
73
89
  worker,
74
- workerExists: worker ? await fs.pathExists(node_path.join(outputDirectory, worker)) : false
90
+ workerExists: worker ? await fs.pathExists(node_path.join(outputDirectory, worker)) : false,
91
+ ...hasRouteDataWorker ? {
92
+ routeDataWorker
93
+ } : {},
94
+ ...route.responseHeaders && 'object' == typeof route.responseHeaders ? {
95
+ responseHeaders: route.responseHeaders
96
+ } : {}
75
97
  };
76
98
  }));
77
- const bffPrefix = modernConfig.bff?.prefix;
78
- const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
99
+ const rendererBuild = await readWorkerRendererBuild(appContext.distDirectory, routeSpec.routes, deliveryUnitStamp);
100
+ const rendererIdentities = rendererBuild?.identities;
101
+ const nativeRenderer = await readNativeRendererWorkerResources(appContext.distDirectory, rendererBuild);
102
+ for (const route of routes){
103
+ const renderer = 'string' == typeof route.entryName && rendererIdentities && Object.hasOwn(rendererIdentities, route.entryName) ? rendererIdentities[route.entryName].renderer : void 0;
104
+ if (void 0 !== renderer && rendererBuild?.renderer.nativeDocuments && (nativeRenderer?.renderer !== renderer || !route.workerExists)) throw new Error(`Cloudflare worker deploy of the ${renderer} renderer requires its native worker build for entry ${route.entryName}. Set deploy.worker.ssr: true and rebuild with the Cloudflare deploy target.`);
105
+ }
79
106
  const isEffectApi = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
107
+ const effectBffPrefix = getWorkerEffectBffPrefix(modernConfig);
80
108
  const effectApiWorkerExists = await fs.pathExists(node_path.join(outputDirectory, BFF_EFFECT_WORKER_ENTRY));
81
- const serviceBindings = createWorkerManifestServiceBindings(createWorkerServiceBindings(modernConfig, void 0));
109
+ const typedServiceBindings = createWorkerServiceBindings(modernConfig, void 0);
110
+ const serviceBindings = createWorkerManifestServiceBindings(typedServiceBindings, createWorkerVpcServiceBindings(modernConfig, void 0, typedServiceBindings, new Set([
111
+ ASSETS_BINDING
112
+ ])));
82
113
  const moduleFederation = await createModuleFederationWorkerManifest(outputDirectory);
83
- if (isEffectApi && primaryBffPrefix && !effectApiWorkerExists) throw createMissingEffectBffWorkerError(outputDirectory, BFF_EFFECT_WORKER_ENTRY);
114
+ if (void 0 !== effectBffPrefix && !effectApiWorkerExists) throw createMissingEffectBffWorkerError(outputDirectory, BFF_EFFECT_WORKER_ENTRY);
84
115
  const effectBffManifest = isEffectApi ? createEffectBffWorkerManifest(modernConfig) : void 0;
85
116
  return {
86
117
  version: 1,
@@ -116,11 +147,18 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, d
116
147
  ...deliveryUnitStamp ? {
117
148
  deliveryUnit: deliveryUnitStamp
118
149
  } : {},
150
+ ...rendererBuild ? {
151
+ renderer: rendererBuild.renderer,
152
+ rendererIdentities
153
+ } : {},
154
+ ...nativeRenderer ? {
155
+ nativeRenderer
156
+ } : {},
119
157
  i18n: createI18nWorkerManifest(routeSpec, appContext),
120
- bff: isEffectApi && primaryBffPrefix && effectApiWorkerExists ? {
158
+ bff: void 0 !== effectBffPrefix && effectApiWorkerExists ? {
121
159
  dispatcherExport: BFF_EFFECT_WORKER_DISPATCHER_EXPORT,
122
160
  runtimeFramework: 'effect',
123
- prefix: primaryBffPrefix,
161
+ prefix: effectBffPrefix,
124
162
  worker: BFF_EFFECT_WORKER_ENTRY,
125
163
  effect: effectBffManifest
126
164
  } : void 0,
@@ -131,9 +169,12 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, d
131
169
  };
132
170
  const createWorkerModuleLoaders = (manifest)=>{
133
171
  const imports = new Map();
134
- for (const route of manifest.routeSpec.routes)if (route.worker && route.workerExists) {
135
- const importPath = `../${String(route.worker).replace(/^\/+/u, '')}`;
136
- imports.set(route.worker, `() => import(${JSON.stringify(importPath)})`);
172
+ for (const route of manifest.routeSpec.routes)for (const worker of [
173
+ route.workerExists ? route.worker : void 0,
174
+ route.routeDataWorker
175
+ ])if (worker) {
176
+ const importPath = `../${String(worker).replace(/^\/+/u, '')}`;
177
+ imports.set(worker, `() => import(${JSON.stringify(importPath)})`);
137
178
  }
138
179
  if (manifest.bff?.worker) {
139
180
  const importPath = `../${String(manifest.bff.worker).replace(/^\/+/u, '')}`;
@@ -0,0 +1,10 @@
1
+ function isCloudflareWorkerRequestHandlerOptions(options) {
2
+ if (!('platform' in options) || !('executionContext' in options)) return false;
3
+ const platform = options.platform;
4
+ if (null === platform || 'object' != typeof platform || !('kind' in platform) || 'worker' !== platform.kind || !('bindings' in platform)) return false;
5
+ const bindings = platform.bindings;
6
+ if (void 0 !== bindings && (null === bindings || 'object' != typeof bindings || Array.isArray(bindings))) return false;
7
+ const context = options.executionContext;
8
+ return void 0 === context || null !== context && 'object' == typeof context && 'waitUntil' in context && 'function' == typeof context.waitUntil && 'passThroughOnException' in context && 'function' == typeof context.passThroughOnException;
9
+ }
10
+ export { isCloudflareWorkerRequestHandlerOptions };