@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,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 };
@@ -1,12 +1,16 @@
1
1
  import node_path from "node:path";
2
2
  import { ASSETS_BINDING, COMPATIBILITY_DATE_PATTERN, DEFAULT_COMPATIBILITY_DATE, PUBLIC_ASSETS_DIRECTORY, REQUIRED_COMPATIBILITY_FLAGS, WORKER_ENTRY } from "./constants.mjs";
3
3
  import { isJsonRecord, normalizeRelativePath } from "./utils.mjs";
4
- const getCompatibilityDate = (modernConfig)=>{
5
- const configuredDate = modernConfig.deploy?.worker?.compatibilityDate?.trim();
6
- const compatibilityDate = configuredDate || DEFAULT_COMPATIBILITY_DATE;
7
- if (!COMPATIBILITY_DATE_PATTERN.test(compatibilityDate)) throw new Error(`deploy.worker.compatibilityDate must use YYYY-MM-DD, received ${JSON.stringify(compatibilityDate)}.`);
4
+ const assertSupportedCompatibilityDate = (compatibilityDate, label)=>{
5
+ if ('string' != typeof compatibilityDate) throw new Error(`${label} must be a YYYY-MM-DD string.`);
6
+ if (!COMPATIBILITY_DATE_PATTERN.test(compatibilityDate)) throw new Error(`${label} must use YYYY-MM-DD, received ${JSON.stringify(compatibilityDate)}.`);
7
+ if (compatibilityDate < DEFAULT_COMPATIBILITY_DATE) throw new Error(`${label} must be ${DEFAULT_COMPATIBILITY_DATE} or later, the date the Cloudflare worker Node.js built-in contract is verified against; received ${JSON.stringify(compatibilityDate)}.`);
8
8
  return compatibilityDate;
9
9
  };
10
+ const getCompatibilityDate = (modernConfig, wranglerCompatibilityDate)=>{
11
+ if (void 0 !== wranglerCompatibilityDate) return assertSupportedCompatibilityDate(wranglerCompatibilityDate, 'deploy.worker.wrangler.compatibility_date');
12
+ return assertSupportedCompatibilityDate(modernConfig.deploy?.worker?.compatibilityDate?.trim() || DEFAULT_COMPATIBILITY_DATE, 'deploy.worker.compatibilityDate');
13
+ };
10
14
  const getWorkerName = (appDirectory)=>{
11
15
  const basename = node_path.basename(appDirectory);
12
16
  return basename.replace(/[^a-zA-Z0-9-_]/g, '-') || 'modern-cloudflare-worker';
@@ -21,11 +25,11 @@ const getConfiguredWrangler = (modernConfig)=>{
21
25
  if (!isJsonRecord(wrangler)) throw new Error('deploy.worker.wrangler must be a JSON object.');
22
26
  return wrangler;
23
27
  };
24
- const createWranglerCompatibilityFlags = (configuredFlags)=>{
28
+ const createWranglerCompatibilityFlags = (configuredFlags, label = 'deploy.worker.wrangler.compatibility_flags')=>{
25
29
  if (void 0 === configuredFlags) return [
26
30
  ...REQUIRED_COMPATIBILITY_FLAGS
27
31
  ];
28
- if (!Array.isArray(configuredFlags) || configuredFlags.some((flag)=>'string' != typeof flag)) throw new Error('deploy.worker.wrangler.compatibility_flags must be an array of strings.');
32
+ if (!Array.isArray(configuredFlags) || configuredFlags.some((flag)=>'string' != typeof flag)) throw new Error(`${label} must be an array of strings.`);
29
33
  return [
30
34
  ...new Set([
31
35
  ...configuredFlags,
@@ -33,6 +37,26 @@ const createWranglerCompatibilityFlags = (configuredFlags)=>{
33
37
  ])
34
38
  ];
35
39
  };
40
+ const createWranglerEnvironments = (wranglerEnv)=>{
41
+ if (void 0 === wranglerEnv) return;
42
+ if (!isJsonRecord(wranglerEnv)) throw new Error('deploy.worker.wrangler.env must be an object.');
43
+ return Object.fromEntries(Object.entries(wranglerEnv).map(([name, environment])=>{
44
+ const label = `deploy.worker.wrangler.env.${name}`;
45
+ if (!isJsonRecord(environment)) throw new Error(`${label} must be an object.`);
46
+ return [
47
+ name,
48
+ {
49
+ ...environment,
50
+ ...void 0 === environment.compatibility_date ? {} : {
51
+ compatibility_date: assertSupportedCompatibilityDate(environment.compatibility_date, `${label}.compatibility_date`)
52
+ },
53
+ ...void 0 === environment.compatibility_flags ? {} : {
54
+ compatibility_flags: createWranglerCompatibilityFlags(environment.compatibility_flags, `${label}.compatibility_flags`)
55
+ }
56
+ }
57
+ ];
58
+ }));
59
+ };
36
60
  const createWranglerAssetsConfig = (configuredAssets)=>{
37
61
  if (void 0 !== configuredAssets && !isJsonRecord(configuredAssets)) throw new Error('deploy.worker.wrangler.assets must be an object.');
38
62
  return {
@@ -103,6 +127,129 @@ const createWorkerServiceBindings = (modernConfig, configuredWranglerServices)=>
103
127
  if (!Array.isArray(services)) throw new Error('deploy.worker.services must be an array.');
104
128
  return services.map(normalizeServiceBinding);
105
129
  };
130
+ const normalizeVpcService = (vpcService, index)=>{
131
+ const binding = assertNonEmptyString(vpcService.binding, `deploy.worker.vpcServices[${index}].binding`);
132
+ const serviceId = assertNonEmptyString(vpcService.serviceId, `deploy.worker.vpcServices[${index}].serviceId`);
133
+ const prefix = void 0 === vpcService.prefix ? void 0 : assertNonEmptyString(vpcService.prefix, `deploy.worker.vpcServices[${index}].prefix`);
134
+ return {
135
+ binding,
136
+ serviceId,
137
+ ...void 0 === prefix ? {} : {
138
+ prefix
139
+ }
140
+ };
141
+ };
142
+ const decodeRoutePrefix = (prefix)=>{
143
+ let decoded = prefix;
144
+ for(let round = 0; round < 4; round += 1){
145
+ let next;
146
+ try {
147
+ next = decodeURIComponent(decoded);
148
+ } catch {
149
+ break;
150
+ }
151
+ if (next === decoded) break;
152
+ decoded = next;
153
+ }
154
+ return decoded;
155
+ };
156
+ const normalizeRoutePrefix = (prefix)=>{
157
+ const decoded = decodeRoutePrefix(prefix);
158
+ return '/' === decoded ? decoded : decoded.replace(/\/+$/u, '');
159
+ };
160
+ const isCanonicalRoutePrefix = (prefix)=>prefix.startsWith('/') && !prefix.includes('\\') && decodeRoutePrefix(prefix) === prefix && new URL(prefix, 'https://route.invalid').pathname === prefix;
161
+ const collectWorkerBindingNames = (config)=>{
162
+ const names = new Set();
163
+ const visit = (value, inArray)=>{
164
+ if (Array.isArray(value)) {
165
+ for (const item of value)visit(item, true);
166
+ return;
167
+ }
168
+ if (!isJsonRecord(value)) return;
169
+ for (const field of inArray ? [
170
+ 'binding',
171
+ 'name'
172
+ ] : [
173
+ 'binding'
174
+ ]){
175
+ const name = value[field];
176
+ if ('string' == typeof name) names.add(name.trim());
177
+ }
178
+ for (const child of Object.values(value))visit(child, false);
179
+ };
180
+ for (const [key, value] of Object.entries(config))if ('env' !== key && 'services' !== key && 'vpc_services' !== key) {
181
+ if ('vars' === key && isJsonRecord(value)) {
182
+ for (const name of Object.keys(value))names.add(name);
183
+ continue;
184
+ }
185
+ visit(value, false);
186
+ }
187
+ return names;
188
+ };
189
+ const prefixesOverlap = (left, right)=>{
190
+ const [first, second] = [
191
+ normalizeRoutePrefix(left),
192
+ normalizeRoutePrefix(right)
193
+ ];
194
+ return '/' === first || '/' === second || first === second || first.startsWith(`${second}/`) || second.startsWith(`${first}/`);
195
+ };
196
+ const getWorkerEffectBffPrefix = (modernConfig)=>{
197
+ const bffPrefix = modernConfig.bff?.prefix;
198
+ const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
199
+ const isEffectApi = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
200
+ return isEffectApi && primaryBffPrefix ? primaryBffPrefix : void 0;
201
+ };
202
+ const effectiveServiceBindings = (serviceBindings)=>{
203
+ const bindings = Array.isArray(serviceBindings) ? serviceBindings : [];
204
+ return bindings.flatMap((service)=>isJsonRecord(service) && 'string' == typeof service.binding ? [
205
+ {
206
+ binding: service.binding.trim(),
207
+ prefix: 'string' == typeof service.prefix ? service.prefix : void 0
208
+ }
209
+ ] : []);
210
+ };
211
+ const createWorkerVpcServiceBindings = (modernConfig, configuredWranglerVpcServices, serviceBindings, workerBindingNames)=>{
212
+ const vpcServices = modernConfig.deploy?.worker?.vpcServices;
213
+ if (void 0 === vpcServices) return;
214
+ if (void 0 !== configuredWranglerVpcServices) throw new Error('Use deploy.worker.vpcServices or deploy.worker.wrangler.vpc_services, not both.');
215
+ if (!Array.isArray(vpcServices)) throw new Error('deploy.worker.vpcServices must be an array.');
216
+ const services = effectiveServiceBindings(serviceBindings);
217
+ const serviceBindingNames = new Set(services.map(({ binding })=>binding));
218
+ const effectBffPrefix = getWorkerEffectBffPrefix(modernConfig);
219
+ const routes = [
220
+ ...void 0 === effectBffPrefix ? [] : [
221
+ {
222
+ owner: 'the Effect BFF',
223
+ prefix: effectBffPrefix
224
+ }
225
+ ],
226
+ ...services.flatMap(({ binding, prefix })=>void 0 === prefix ? [] : [
227
+ {
228
+ owner: `binding "${binding}"`,
229
+ prefix
230
+ }
231
+ ])
232
+ ];
233
+ const vpcBindingNames = new Set();
234
+ return vpcServices.map((vpcService, index)=>{
235
+ const normalized = normalizeVpcService(vpcService, index);
236
+ if (serviceBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is already declared by deploy.worker.services.`);
237
+ if (workerBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is already a binding of this Worker.`);
238
+ if (vpcBindingNames.has(normalized.binding)) throw new Error(`deploy.worker.vpcServices[${index}].binding "${normalized.binding}" is declared more than once.`);
239
+ vpcBindingNames.add(normalized.binding);
240
+ if (void 0 !== normalized.prefix) {
241
+ const { prefix } = normalized;
242
+ if (!isCanonicalRoutePrefix(prefix)) throw new Error(`deploy.worker.vpcServices[${index}].prefix "${prefix}" must be a decoded URL path that starts with "/" (no query, fragment, "." or ".." segments).`);
243
+ const shadow = routes.find((route)=>prefixesOverlap(route.prefix, prefix));
244
+ if (void 0 !== shadow) throw new Error(`deploy.worker.vpcServices[${index}].prefix "${prefix}" overlaps the prefix "${shadow.prefix}" of ${shadow.owner}; the worker dispatcher would route one of them ambiguously.`);
245
+ routes.push({
246
+ owner: `binding "${normalized.binding}"`,
247
+ prefix
248
+ });
249
+ }
250
+ return normalized;
251
+ });
252
+ };
106
253
  const createWranglerServices = (serviceBindings)=>{
107
254
  if (!Array.isArray(serviceBindings)) return serviceBindings;
108
255
  if (!serviceBindings.every(isJsonRecord)) return serviceBindings;
@@ -111,8 +258,16 @@ const createWranglerServices = (serviceBindings)=>{
111
258
  return wranglerService;
112
259
  });
113
260
  };
114
- const createWorkerManifestServiceBindings = (serviceBindings)=>{
115
- if (!Array.isArray(serviceBindings)) return;
261
+ const createWorkerManifestServiceBindings = (serviceBindings, vpcServiceBindings)=>{
262
+ const vpcManifestBindings = (vpcServiceBindings ?? []).flatMap(({ binding, prefix, serviceId })=>void 0 === prefix ? [] : [
263
+ {
264
+ binding,
265
+ interface: 'fetch',
266
+ prefix,
267
+ vpcServiceId: serviceId
268
+ }
269
+ ]);
270
+ if (!Array.isArray(serviceBindings)) return vpcManifestBindings.length > 0 ? vpcManifestBindings : void 0;
116
271
  const bindings = serviceBindings;
117
272
  const manifestBindings = bindings.filter((service)=>isJsonRecord(service) && 'string' == typeof service.binding && 'string' == typeof service.service && ('string' == typeof service.prefix || Array.isArray(service.fragments) && service.fragments.length > 0)).map((service)=>({
118
273
  binding: service.binding,
@@ -125,21 +280,29 @@ const createWorkerManifestServiceBindings = (serviceBindings)=>{
125
280
  fragments: service.fragments
126
281
  }
127
282
  }));
128
- return manifestBindings.length > 0 ? manifestBindings : void 0;
283
+ const allBindings = [
284
+ ...manifestBindings,
285
+ ...vpcManifestBindings
286
+ ];
287
+ return allBindings.length > 0 ? allBindings : void 0;
129
288
  };
130
289
  const createWranglerConfig = (appDirectory, modernConfig)=>{
131
290
  const wrangler = getConfiguredWrangler(modernConfig);
291
+ const environments = createWranglerEnvironments(wrangler.env);
132
292
  const d1Databases = createWranglerD1Databases(modernConfig, wrangler.d1_databases);
133
293
  const serviceBindings = createWorkerServiceBindings(modernConfig, wrangler.services);
134
294
  const wranglerServices = createWranglerServices(serviceBindings);
135
- return {
295
+ const workerConfig = {
136
296
  $schema: 'node_modules/wrangler/config-schema.json',
137
297
  name: getConfiguredWorkerName(appDirectory, modernConfig),
138
- compatibility_date: getCompatibilityDate(modernConfig),
139
298
  ...wrangler,
299
+ compatibility_date: getCompatibilityDate(modernConfig, wrangler.compatibility_date),
140
300
  main: WORKER_ENTRY,
141
301
  compatibility_flags: createWranglerCompatibilityFlags(wrangler.compatibility_flags),
142
302
  assets: createWranglerAssetsConfig(wrangler.assets),
303
+ ...void 0 === environments ? {} : {
304
+ env: environments
305
+ },
143
306
  ...void 0 === d1Databases ? {} : {
144
307
  d1_databases: d1Databases
145
308
  },
@@ -147,5 +310,33 @@ const createWranglerConfig = (appDirectory, modernConfig)=>{
147
310
  services: wranglerServices
148
311
  }
149
312
  };
313
+ const vpcServiceBindings = createWorkerVpcServiceBindings(modernConfig, wrangler.vpc_services, serviceBindings, collectWorkerBindingNames(workerConfig));
314
+ if (void 0 === vpcServiceBindings) return workerConfig;
315
+ const vpcServicesConfig = vpcServiceBindings.map(({ binding, serviceId })=>({
316
+ binding,
317
+ service_id: serviceId
318
+ }));
319
+ return {
320
+ ...workerConfig,
321
+ ...void 0 === environments ? {} : {
322
+ env: Object.fromEntries(Object.entries(environments).map(([name, environment])=>{
323
+ if (void 0 !== environment.vpc_services) throw new Error(`Use deploy.worker.vpcServices or deploy.worker.wrangler.env.${name}.vpc_services, not both.`);
324
+ const environmentBindingNames = new Set([
325
+ ...collectWorkerBindingNames(environment),
326
+ ...effectiveServiceBindings(environment.services).map(({ binding })=>binding)
327
+ ]);
328
+ const collision = vpcServiceBindings.find(({ binding })=>environmentBindingNames.has(binding));
329
+ if (void 0 !== collision) throw new Error(`deploy.worker.vpcServices binding "${collision.binding}" is already a binding of deploy.worker.wrangler.env.${name}.`);
330
+ return [
331
+ name,
332
+ {
333
+ ...environment,
334
+ vpc_services: vpcServicesConfig
335
+ }
336
+ ];
337
+ }))
338
+ },
339
+ vpc_services: vpcServicesConfig
340
+ };
150
341
  };
151
- export { createWorkerManifestServiceBindings, createWorkerServiceBindings, createWranglerConfig };
342
+ export { createWorkerManifestServiceBindings, createWorkerServiceBindings, createWorkerVpcServiceBindings, createWranglerConfig, getWorkerEffectBffPrefix };