@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.9 → 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 (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 +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 +130 -13
  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 +8 -5
  46. package/dist/cjs/renderer-build-identity.js +134 -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 +530 -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 +26 -160
  58. package/dist/esm/build-config/internal-effect-discovery.mjs +102 -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 +124 -13
  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 +8 -5
  98. package/dist/esm/renderer-build-identity.mjs +82 -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 +530 -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 +26 -160
  110. package/dist/esm-node/build-config/internal-effect-discovery.mjs +103 -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 +124 -13
  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 +8 -5
  150. package/dist/esm-node/renderer-build-identity.mjs +83 -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 +530 -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 +1 -9
  163. package/dist/types/build-config/internal-effect-discovery.d.ts +4 -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 +17 -2
  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 +98 -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 +26 -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 +3 -2
  199. package/dist/types/renderer-build-identity.d.ts +30 -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 +59 -285
  208. package/src/build-config/internal-effect-discovery.ts +239 -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 +108 -10
  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 +101 -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 +233 -20
  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 +19 -2
  248. package/src/renderer-build-identity.ts +168 -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 +530 -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,9 +1,10 @@
1
1
  import "node:module";
2
- import { DELIVERY_UNIT_IDENTITY_FIELDS, deliveryUnitIdentityFieldValue } from "@modern-js/backend-federation-contracts";
2
+ import { DELIVERY_UNIT_IDENTITY_FIELDS, deliveryUnitIdentityFieldValue, validateRendererIdentity, validateRendererProfile, validateRendererProfileCompatibility } from "@modern-js/backend-federation-contracts";
3
+ import { isRecord } from "../cloudflare/utils.mjs";
3
4
  import { addIssue, assertEqual } from "./issues.mjs";
4
5
  const verifyDeliveryUnitIdentity = (issues, manifest, manifestPath, declared)=>{
5
6
  const stamped = manifest?.deliveryUnit;
6
- const hasStamp = Boolean(stamped) && 'object' == typeof stamped;
7
+ const hasStamp = isRecord(stamped);
7
8
  if (declared) {
8
9
  if (!hasStamp) return void addIssue(issues, {
9
10
  code: 'missing-delivery-unit',
@@ -19,36 +20,124 @@ const verifyDeliveryUnitIdentity = (issues, manifest, manifestPath, declared)=>{
19
20
  });
20
21
  }
21
22
  }
22
- if (hasStamp && stamped.surfaces && 'object' == typeof stamped.surfaces) for (const surface of [
23
- 'ui',
24
- 'api'
25
- ]){
26
- const marker = stamped.surfaces[surface];
27
- const expected = declared?.surfaces?.[surface];
28
- if (declared?.surfaces && !expected) {
29
- if (marker) addIssue(issues, {
23
+ if (hasStamp) {
24
+ if (!isRecord(stamped.surfaces)) return void addIssue(issues, {
25
+ code: 'missing-delivery-unit',
26
+ message: 'Cloudflare worker manifest is missing delivery-unit surface markers.',
27
+ path: manifestPath
28
+ });
29
+ for (const surface of Object.keys(stamped.surfaces))if ('ui' !== surface && 'api' !== surface) addIssue(issues, {
30
+ code: 'delivery-unit-drift',
31
+ message: `Cloudflare worker manifest declares an unsupported ${surface} delivery-unit surface.`,
32
+ path: manifestPath
33
+ });
34
+ if (!declared?.surfaces && !Object.hasOwn(stamped.surfaces, 'ui') && !Object.hasOwn(stamped.surfaces, 'api')) return void addIssue(issues, {
35
+ code: 'missing-delivery-unit',
36
+ message: 'Cloudflare worker manifest is missing delivery-unit surface markers.',
37
+ path: manifestPath
38
+ });
39
+ for (const surface of [
40
+ 'ui',
41
+ 'api'
42
+ ]){
43
+ const marker = stamped.surfaces[surface];
44
+ const expected = declared?.surfaces?.[surface];
45
+ if (!expected && !marker) continue;
46
+ if (declared?.surfaces && !expected) {
47
+ if (marker) addIssue(issues, {
48
+ code: 'delivery-unit-drift',
49
+ message: `Cloudflare worker manifest declares an unexpected ${surface} delivery-unit surface for this topology profile.`,
50
+ path: manifestPath
51
+ });
52
+ continue;
53
+ }
54
+ if (!isRecord(marker)) {
55
+ addIssue(issues, {
56
+ code: 'missing-delivery-unit',
57
+ message: `Cloudflare worker manifest is missing the ${surface} delivery-unit surface marker.`,
58
+ path: manifestPath
59
+ });
60
+ continue;
61
+ }
62
+ assertEqual(issues, marker.surface, surface, {
30
63
  code: 'delivery-unit-drift',
31
- message: `Cloudflare worker manifest declares an unexpected ${surface} delivery-unit surface for this topology profile.`,
64
+ message: `Cloudflare worker manifest ${surface} surface must declare surface "${surface}".`,
32
65
  path: manifestPath
33
66
  });
34
- continue;
35
- }
36
- if (!marker || 'object' != typeof marker) {
37
- addIssue(issues, {
38
- code: 'missing-delivery-unit',
39
- message: `Cloudflare worker manifest is missing the ${surface} delivery-unit surface marker.`,
67
+ for (const field of DELIVERY_UNIT_IDENTITY_FIELDS){
68
+ const markerValue = deliveryUnitIdentityFieldValue(marker, field);
69
+ const stampedValue = deliveryUnitIdentityFieldValue(stamped, field);
70
+ assertEqual(issues, markerValue, stampedValue, {
71
+ code: 'delivery-unit-drift',
72
+ message: `Cloudflare worker manifest ${surface} surface deliveryUnit.${field} must derive from one delivery-unit record (expected ${stampedValue ?? 'undefined'}, received ${markerValue ?? 'undefined'}).`,
73
+ path: manifestPath
74
+ });
75
+ }
76
+ if ('api' === surface) {
77
+ for (const field of [
78
+ 'rendererIdentity',
79
+ 'rendererProfile'
80
+ ])if (Object.hasOwn(marker, field)) addIssue(issues, {
81
+ code: 'delivery-unit-drift',
82
+ message: `Cloudflare worker manifest API surface must not declare ${field}.`,
83
+ path: manifestPath
84
+ });
85
+ continue;
86
+ }
87
+ const identity = marker.rendererIdentity;
88
+ const profile = marker.rendererProfile;
89
+ const errors = [
90
+ ...validateRendererIdentity(identity).errors,
91
+ ...validateRendererProfile(profile).errors
92
+ ];
93
+ for (const error of errors)addIssue(issues, {
94
+ code: 'delivery-unit-drift',
95
+ message: `Cloudflare worker manifest UI ${error.path} ${error.message}`,
40
96
  path: manifestPath
41
97
  });
42
- continue;
43
- }
44
- for (const field of DELIVERY_UNIT_IDENTITY_FIELDS){
45
- const markerValue = deliveryUnitIdentityFieldValue(marker, field);
46
- const stampedValue = deliveryUnitIdentityFieldValue(stamped, field);
47
- assertEqual(issues, markerValue, stampedValue, {
98
+ if (errors.length || !isRecord(identity) || !isRecord(profile)) continue;
99
+ assertEqual(issues, identity.buildId, marker.buildMarker, {
100
+ code: 'delivery-unit-drift',
101
+ message: 'Cloudflare worker manifest UI rendererIdentity.buildId must match its surface buildMarker.',
102
+ path: manifestPath
103
+ });
104
+ assertEqual(issues, identity.appId, marker.appId, {
105
+ code: 'delivery-unit-drift',
106
+ message: 'Cloudflare worker manifest UI rendererIdentity.appId must match its surface appId.',
107
+ path: manifestPath
108
+ });
109
+ assertEqual(issues, marker.appId, stamped.appId, {
110
+ code: 'delivery-unit-drift',
111
+ message: 'Cloudflare worker manifest UI surface appId must match its delivery-unit appId.',
112
+ path: manifestPath
113
+ });
114
+ for (const field of [
115
+ 'renderer',
116
+ 'protocolVersion'
117
+ ])assertEqual(issues, identity[field], profile[field], {
48
118
  code: 'delivery-unit-drift',
49
- message: `Cloudflare worker manifest ${surface} surface deliveryUnit.${field} must derive from one delivery-unit record (expected ${stampedValue ?? 'undefined'}, received ${markerValue ?? 'undefined'}).`,
119
+ message: `Cloudflare worker manifest UI rendererProfile.${field} must match its renderer identity.`,
50
120
  path: manifestPath
51
121
  });
122
+ const expectedUi = declared?.surfaces?.ui;
123
+ if (expectedUi?.rendererIdentity) {
124
+ for (const field of [
125
+ 'renderer',
126
+ 'appId',
127
+ 'entryName',
128
+ 'protocolVersion',
129
+ 'buildId'
130
+ ])assertEqual(issues, identity[field], expectedUi.rendererIdentity[field], {
131
+ code: 'delivery-unit-drift',
132
+ message: `Cloudflare worker manifest UI rendererIdentity.${field} must match the topology renderer identity.`,
133
+ path: manifestPath
134
+ });
135
+ for (const error of validateRendererProfileCompatibility(expectedUi.rendererProfile, profile).errors)addIssue(issues, {
136
+ code: 'delivery-unit-drift',
137
+ message: `Cloudflare worker manifest UI ${error.path} ${error.message}`,
138
+ path: manifestPath
139
+ });
140
+ }
52
141
  }
53
142
  }
54
143
  };
@@ -73,6 +73,16 @@ const verifyCloudflareOutput = async (options)=>{
73
73
  }
74
74
  }
75
75
  if (wrangler) {
76
+ if ('string' != typeof wrangler.name || 0 === wrangler.name.trim().length) addIssue(issues, {
77
+ code: 'invalid-wrangler',
78
+ message: 'wrangler.json name must be a non-empty worker name.',
79
+ path: wranglerPath
80
+ });
81
+ if ('string' != typeof wrangler.compatibility_date || !/^\d{4}-\d{2}-\d{2}$/u.test(wrangler.compatibility_date)) addIssue(issues, {
82
+ code: 'invalid-wrangler',
83
+ message: 'wrangler.json compatibility_date must use YYYY-MM-DD.',
84
+ path: wranglerPath
85
+ });
76
86
  assertEqual(issues, wrangler.main, outputPlan.wrangler.main, {
77
87
  code: 'invalid-wrangler',
78
88
  message: `wrangler.json main must be ${outputPlan.wrangler.main}.`,
@@ -43,6 +43,52 @@ const verifyManifestShape = (issues, manifest, manifestPath)=>{
43
43
  message: 'Cloudflare output manifest routeSpec.routes must be array.',
44
44
  path: manifestPath
45
45
  });
46
+ const security = manifest.security;
47
+ if (security && 'object' == typeof security && 'boolean' == typeof security.enabled) {
48
+ const cors = security.cors;
49
+ if (!cors || 'object' != typeof cors || 'boolean' != typeof cors.assets || !Array.isArray(cors.allowedOrigins) || !Array.isArray(cors.allowedMethods) || !Array.isArray(cors.allowedHeaders)) addIssue(issues, {
50
+ code: 'invalid-manifest',
51
+ message: 'Cloudflare output manifest security.cors is incomplete.',
52
+ path: manifestPath
53
+ });
54
+ if (security.enabled) {
55
+ const headers = security.headers;
56
+ const csp = security.contentSecurityPolicy;
57
+ const noindex = security.noindex;
58
+ if (!headers || 'object' != typeof headers || ![
59
+ 'string',
60
+ 'boolean'
61
+ ].includes(typeof headers.referrerPolicy) || ![
62
+ 'string',
63
+ 'boolean'
64
+ ].includes(typeof headers.contentTypeOptions) || ![
65
+ 'string',
66
+ 'boolean'
67
+ ].includes(typeof headers.permissionsPolicy)) addIssue(issues, {
68
+ code: 'invalid-manifest',
69
+ message: 'Cloudflare output manifest security.headers is incomplete.',
70
+ path: manifestPath
71
+ });
72
+ if (!csp || ![
73
+ 'off',
74
+ 'report-only',
75
+ 'enforce'
76
+ ].includes(csp.mode) || !csp.directives || 'object' != typeof csp.directives || Array.isArray(csp.directives) || Object.values(csp.directives).some((value)=>!Array.isArray(value) || value.some((entry)=>'string' != typeof entry))) addIssue(issues, {
77
+ code: 'invalid-manifest',
78
+ message: 'Cloudflare output manifest security.contentSecurityPolicy is invalid.',
79
+ path: manifestPath
80
+ });
81
+ if (!noindex || 'boolean' != typeof noindex.workersDev || 'boolean' != typeof noindex.localhost || !Array.isArray(noindex.previewHostnames)) addIssue(issues, {
82
+ code: 'invalid-manifest',
83
+ message: 'Cloudflare output manifest security.noindex is incomplete.',
84
+ path: manifestPath
85
+ });
86
+ }
87
+ } else addIssue(issues, {
88
+ code: 'invalid-manifest',
89
+ message: 'Cloudflare output manifest security.enabled must be a boolean.',
90
+ path: manifestPath
91
+ });
46
92
  if (manifest.bff?.runtimeFramework === 'effect') assertEqual(issues, manifest.bff.dispatcherExport, BFF_EFFECT_WORKER_DISPATCHER_EXPORT, {
47
93
  code: 'invalid-manifest',
48
94
  message: `Cloudflare Effect BFF manifest dispatcherExport must be ${BFF_EFFECT_WORKER_DISPATCHER_EXPORT}.`,
@@ -85,7 +85,13 @@ const verifyCloudflareOutputMutationPolicy = async (options)=>{
85
85
  const files = (await Promise.all(options.scanRoots.map((scanRoot)=>walkFiles(node_path.resolve(scanRoot))))).flat();
86
86
  for (const file of files){
87
87
  if (excludedFiles.has(file)) continue;
88
- const source = await promises.readFile(file, 'utf-8');
88
+ let source;
89
+ try {
90
+ source = await promises.readFile(file, 'utf-8');
91
+ } catch (error) {
92
+ if ('ENOENT' === error.code) continue;
93
+ throw error;
94
+ }
89
95
  for (const forbidden of FORBIDDEN_MUTATION_PATTERNS)if (forbidden.pattern.test(source)) addIssue(issues, {
90
96
  code: forbidden.code,
91
97
  message: forbidden.message,
@@ -5,9 +5,9 @@ import { pathToFileURL } from "node:url";
5
5
  import { isDeepStrictEqual } from "node:util";
6
6
  import { parse } from "@babel/parser";
7
7
  import traverse from "@babel/traverse";
8
- import { CLOUDFLARE_WORKER_BUNDLE_DIRECTORY, CLOUDFLARE_WORKER_NODE_BUILTINS, CLOUDFLARE_WORKER_PLATFORM_MODULES } from "../cloudflare-output-contract.mjs";
8
+ import { CLOUDFLARE_ROUTE_DATA_HANDLER_EXPORT, CLOUDFLARE_WORKER_BUNDLE_DIRECTORY, CLOUDFLARE_WORKER_NODE_BUILTINS, CLOUDFLARE_WORKER_PLATFORM_MODULES } from "../cloudflare-output-contract.mjs";
9
9
  import { addIssue } from "./issues.mjs";
10
- const getReferencedRouteWorkers = (manifest)=>Array.isArray(manifest?.routeSpec?.routes) ? manifest.routeSpec.routes.map((route)=>'string' == typeof route?.worker && route.worker.length > 0 ? route.worker : void 0).filter((worker)=>'string' == typeof worker) : [];
10
+ const getReferencedRouteWorkers = (manifest, field)=>Array.isArray(manifest?.routeSpec?.routes) ? manifest.routeSpec.routes.map((route)=>'string' == typeof route?.[field] && route[field].length > 0 ? route[field] : void 0).filter((worker)=>'string' == typeof worker) : [];
11
11
  const getEffectBffWorker = (manifest)=>manifest?.bff?.runtimeFramework === 'effect' && 'string' == typeof manifest.bff.worker ? manifest.bff.worker : void 0;
12
12
  const getWorkerBundleReferences = (manifest)=>{
13
13
  const effectBffWorker = getEffectBffWorker(manifest);
@@ -19,9 +19,14 @@ const getWorkerBundleReferences = (manifest)=>{
19
19
  reference: effectBffWorker
20
20
  }
21
21
  ] : [],
22
- ...getReferencedRouteWorkers(manifest).map((reference)=>({
22
+ ...getReferencedRouteWorkers(manifest, 'worker').map((reference)=>({
23
23
  kind: 'route',
24
24
  reference
25
+ })),
26
+ ...getReferencedRouteWorkers(manifest, 'routeDataWorker').map((reference)=>({
27
+ dispatcherExport: CLOUDFLARE_ROUTE_DATA_HANDLER_EXPORT,
28
+ kind: 'route-data',
29
+ reference
25
30
  }))
26
31
  ];
27
32
  };
@@ -42,7 +47,7 @@ const resolveWorkerBundleReference = (issues, outputDirectory, reference, manife
42
47
  path: workerPath
43
48
  };
44
49
  };
45
- const missingWorkerBundleMessage = (reference)=>'effect-bff' === reference.kind ? 'Cloudflare Effect BFF manifest points to a missing worker bundle.' : 'Cloudflare route worker manifest points to a missing worker bundle.';
50
+ const missingWorkerBundleMessage = (reference)=>'effect-bff' === reference.kind ? 'Cloudflare Effect BFF manifest points to a missing worker bundle.' : 'route-data' === reference.kind ? 'Cloudflare route data worker manifest points to a missing worker bundle.' : 'Cloudflare route worker manifest points to a missing worker bundle.';
46
51
  const isAstNode = (value)=>null !== value && 'object' == typeof value && 'type' in value && 'string' == typeof value.type;
47
52
  const getNodeName = (node)=>{
48
53
  if (!isAstNode(node)) return;
@@ -286,9 +291,9 @@ const verifyWorkerModuleClosure = async (issues, entryPath, entrySource, contrac
286
291
  };
287
292
  const verifyWorkerBundleReferences = async (issues, worker, source, contract)=>{
288
293
  const entryAnalysis = await verifyWorkerModuleClosure(issues, worker.path, source, contract);
289
- if ('effect-bff' === worker.kind && 'string' == typeof worker.dispatcherExport && !entryAnalysis?.exports.has(worker.dispatcherExport)) addIssue(issues, {
294
+ if ('string' == typeof worker.dispatcherExport && !entryAnalysis?.exports.has(worker.dispatcherExport)) addIssue(issues, {
290
295
  code: 'invalid-worker-bundle',
291
- message: 'Cloudflare Effect BFF worker bundle must expose its manifest dispatcherExport.',
296
+ message: 'route-data' === worker.kind ? `Cloudflare route data worker bundle must expose ${worker.dispatcherExport}.` : 'Cloudflare Effect BFF worker bundle must expose its manifest dispatcherExport.',
292
297
  path: worker.path
293
298
  });
294
299
  };
@@ -1,2 +1,4 @@
1
1
  import "node:module";
2
- export { getBuildConfigEnvironment, resolveEffectTsgoCompiler, withBuildConfigEnvironment } from "./build-config/public.mjs";
2
+ export { getBuildConfigEnvironment, resolveEffectTsgoCompiler } from "./build-config/public.mjs";
3
+ export { createRemoteManifestUrl } from "./build-config/remote-address.mjs";
4
+ export { resolveDeployTarget } from "./deploy-output/target.mjs";
@@ -5,6 +5,10 @@ const dependencyKeys = [
5
5
  'dependencies',
6
6
  'optionalDependencies'
7
7
  ];
8
+ const rootDependencyKeys = [
9
+ ...dependencyKeys,
10
+ 'devDependencies'
11
+ ];
8
12
  const exactVersionPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u;
9
13
  async function readPackageJson(filePath) {
10
14
  return JSON.parse(await promises.readFile(filePath, 'utf8'));
@@ -20,14 +24,87 @@ function parseNpmAlias(aliasName, specifier) {
20
24
  targetVersion: target.slice(versionSeparator + 1)
21
25
  };
22
26
  }
23
- function aliasesFromPackageJson(packageJson) {
27
+ function aliasesFromPackageJson(packageJson, keys = dependencyKeys) {
24
28
  const aliases = [];
25
- for (const dependencyKey of dependencyKeys)for (const [aliasName, specifier] of Object.entries(packageJson[dependencyKey] ?? {})){
29
+ for (const dependencyKey of keys)for (const [aliasName, specifier] of Object.entries(packageJson[dependencyKey] ?? {})){
26
30
  const alias = parseNpmAlias(aliasName, specifier);
27
31
  if (alias) aliases.push(alias);
28
32
  }
29
33
  return aliases;
30
34
  }
35
+ async function catalogAliasesFromInstalledPackages(appDirectory, packageJson) {
36
+ const aliases = [];
37
+ for (const dependencyKey of rootDependencyKeys)for (const [aliasName, specifier] of Object.entries(packageJson[dependencyKey] ?? {})){
38
+ if (!specifier.startsWith('catalog:')) continue;
39
+ let installedPackage;
40
+ try {
41
+ installedPackage = await readPackageJson(node_path.join(appDirectory, 'node_modules', aliasName, 'package.json'));
42
+ } catch (error) {
43
+ if ('ENOENT' === error.code) continue;
44
+ throw error;
45
+ }
46
+ if (!installedPackage.name || !installedPackage.version) throw new Error(`Installed catalog package ${aliasName} has no identity`);
47
+ if (installedPackage.name !== aliasName) aliases.push({
48
+ aliasName,
49
+ targetName: installedPackage.name,
50
+ targetVersion: installedPackage.version
51
+ });
52
+ }
53
+ return aliases;
54
+ }
55
+ async function installedAliasesByOwner(appDirectory, emittedOwners) {
56
+ const aliasesByOwner = new Map();
57
+ let rootAliases = [];
58
+ const visited = new Set();
59
+ const rootFile = await promises.realpath(node_path.join(appDirectory, 'package.json'));
60
+ const pending = [
61
+ rootFile
62
+ ];
63
+ while(pending.length){
64
+ const file = await promises.realpath(pending.pop());
65
+ if (visited.has(file)) continue;
66
+ visited.add(file);
67
+ const owner = await readPackageJson(file);
68
+ const aliases = [];
69
+ const keys = file === rootFile ? rootDependencyKeys : dependencyKeys;
70
+ for (const key of keys)for (const name of Object.keys(owner[key] ?? {})){
71
+ let directory = node_path.dirname(file);
72
+ while(true){
73
+ const packageFile = node_path.join(directory, 'node_modules', name, 'package.json');
74
+ let target;
75
+ try {
76
+ target = await readPackageJson(packageFile);
77
+ } catch (error) {
78
+ if ('ENOENT' !== error.code) throw error;
79
+ const parent = node_path.dirname(directory);
80
+ if (parent === directory) break;
81
+ directory = parent;
82
+ continue;
83
+ }
84
+ if (!target.name || !target.version) throw new Error(`Installed package ${name} has no identity`);
85
+ pending.push(packageFile);
86
+ if (target.name !== name) aliases.push({
87
+ aliasName: name,
88
+ targetName: target.name,
89
+ targetVersion: target.version
90
+ });
91
+ break;
92
+ }
93
+ }
94
+ if (file === rootFile) rootAliases = aliases;
95
+ const identity = `${owner.name}@${owner.version}`;
96
+ if (emittedOwners.has(identity)) {
97
+ const existing = aliasesByOwner.get(identity);
98
+ const normalized = (items)=>items.map((item)=>`${item.aliasName}=${aliasSpecifier(item)}`).sort().join('\n');
99
+ if (existing && normalized(existing) !== normalized(aliases)) throw new Error(`Cannot preserve npm aliases for ${identity}: installed contexts resolve different dependency identities`);
100
+ aliasesByOwner.set(identity, aliases);
101
+ }
102
+ }
103
+ return {
104
+ aliasesByOwner,
105
+ rootAliases
106
+ };
107
+ }
31
108
  async function collectPackageRecords(nodeModulesDirectory) {
32
109
  const records = [];
33
110
  const visitedNodeModules = new Set();
@@ -148,8 +225,11 @@ async function readPackageIdentity(entryPath) {
148
225
  async function preserveNpmAliases({ appDirectory, outputDirectory, implicitAliases = [] }) {
149
226
  const records = await collectPackageRecords(node_path.join(outputDirectory, 'node_modules'));
150
227
  const appPackageJson = await readPackageJson(node_path.join(appDirectory, 'package.json'));
228
+ const installedAliases = await installedAliasesByOwner(appDirectory, new Set(records.map(({ packageJson })=>`${packageJson.name}@${packageJson.version}`)));
151
229
  const rootAliases = [
152
- ...aliasesFromPackageJson(appPackageJson),
230
+ ...aliasesFromPackageJson(appPackageJson, rootDependencyKeys),
231
+ ...installedAliases.rootAliases,
232
+ ...await catalogAliasesFromInstalledPackages(appDirectory, appPackageJson),
153
233
  ...implicitAliases
154
234
  ].filter((alias)=>alias.aliasName !== alias.targetName);
155
235
  const emittedRootAliases = [];
@@ -162,9 +242,15 @@ async function preserveNpmAliases({ appDirectory, outputDirectory, implicitAlias
162
242
  await ensureAliasLink(outputDirectory, alias, target);
163
243
  emittedRootAliases.push(alias);
164
244
  }
165
- for (const record of records)for (const alias of aliasesFromPackageJson(record.packageJson).filter((candidate)=>candidate.aliasName !== candidate.targetName)){
166
- const target = await resolveAliasTarget(record.directory, alias, outputDirectory);
167
- if (target) await ensureAliasLink(record.directory, alias, target);
245
+ for (const record of records){
246
+ const ownerAliases = [
247
+ ...aliasesFromPackageJson(record.packageJson),
248
+ ...installedAliases.aliasesByOwner.get(`${record.packageJson.name}@${record.packageJson.version}`) ?? []
249
+ ];
250
+ for (const alias of ownerAliases.filter((candidate)=>candidate.aliasName !== candidate.targetName)){
251
+ const target = await resolveAliasTarget(record.directory, alias, outputDirectory);
252
+ if (target) await ensureAliasLink(record.directory, alias, target);
253
+ }
168
254
  }
169
255
  const outputPackageJsonPath = node_path.join(outputDirectory, 'package.json');
170
256
  const outputPackageJson = await readPackageJson(outputPackageJsonPath);
@@ -2,16 +2,17 @@ import "node:module";
2
2
  import { createRequire } from "node:module";
3
3
  import node_path from "node:path";
4
4
  import { preserveNpmAliases, readPackageIdentity } from "./npmAliases.mjs";
5
- import { resolveDeployTarget as external_target_mjs_resolveDeployTarget } from "./target.mjs";
5
+ import { NODE_PUBLIC_ASSET_SCOPE, normalizeDeclaredPublicAssets, stageDeclaredPublicAssets } from "./public-assets.mjs";
6
+ import { getDeployingTarget } from "./target.mjs";
6
7
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
7
8
  var __rspack_import_meta_filename__ = __rspack_fileURLToPath(import.meta.url);
8
- const createDeployOutputAliasesPlugin = ({ resolveDeployTarget = external_target_mjs_resolveDeployTarget } = {})=>({
9
+ const createDeployOutputAliasesPlugin = ()=>({
9
10
  name: '@modern-js/deploy-output-aliases',
10
11
  setup (api) {
11
12
  api.onAfterDeploy(async ()=>{
12
- const { appDirectory, metaName } = api.getAppContext();
13
- const config = api.getNormalizedConfig();
14
- if ('modern-js' !== metaName && !config.deploy?.target && !process.env.MODERNJS_DEPLOY || 'node' !== resolveDeployTarget(config)) return;
13
+ const appContext = api.getAppContext();
14
+ if ('node' !== getDeployingTarget(appContext)) return;
15
+ const { appDirectory } = appContext;
15
16
  const entry = createRequire(__rspack_import_meta_filename__).resolve('@modern-js/prod-server');
16
17
  const identity = await readPackageIdentity(entry);
17
18
  await preserveNpmAliases({
@@ -28,4 +29,21 @@ const createDeployOutputAliasesPlugin = ({ resolveDeployTarget = external_target
28
29
  });
29
30
  }
30
31
  });
31
- export { createDeployOutputAliasesPlugin };
32
+ const createDeployOutputPublicAssetsPlugin = ()=>({
33
+ name: '@modern-js/deploy-output-public-assets',
34
+ setup (api) {
35
+ api.onAfterDeploy(async ()=>{
36
+ const appContext = api.getAppContext();
37
+ if ('node' !== getDeployingTarget(appContext)) return;
38
+ const { appDirectory } = appContext;
39
+ const config = api.getNormalizedConfig();
40
+ await stageDeclaredPublicAssets({
41
+ appDirectory,
42
+ outputDirectory: node_path.join(appDirectory, '.output'),
43
+ assets: normalizeDeclaredPublicAssets(config.deploy?.node?.publicAssets, NODE_PUBLIC_ASSET_SCOPE),
44
+ scope: NODE_PUBLIC_ASSET_SCOPE
45
+ });
46
+ });
47
+ }
48
+ });
49
+ export { createDeployOutputAliasesPlugin, createDeployOutputPublicAssetsPlugin };
@@ -0,0 +1,83 @@
1
+ import "node:module";
2
+ import promises from "node:fs/promises";
3
+ import node_path from "node:path";
4
+ import { normalizeRelativePath } from "../cloudflare/utils.mjs";
5
+ const DECLARED_PUBLIC_ASSET_DIRECTORY = 'public';
6
+ const CLOUDFLARE_PUBLIC_ASSET_SCOPE = {
7
+ label: 'deploy.worker.publicAssets',
8
+ output: 'Cloudflare public output'
9
+ };
10
+ const NODE_PUBLIC_ASSET_SCOPE = {
11
+ label: 'deploy.node.publicAssets',
12
+ output: 'Node public output'
13
+ };
14
+ const normalizeDeclaredPublicAssets = (assets, scope)=>(assets ?? []).map((asset, index)=>({
15
+ from: normalizeRelativePath(asset?.from, `${scope.label}[${index}].from`, 'app root'),
16
+ to: normalizeRelativePath(asset?.to, `${scope.label}[${index}].to`, scope.output, {
17
+ allowRoot: true
18
+ }),
19
+ index
20
+ }));
21
+ const isMissingPathError = (error)=>error instanceof Error && 'code' in error && 'ENOENT' === error.code;
22
+ const pathExists = async (filePath)=>{
23
+ try {
24
+ await promises.lstat(filePath);
25
+ return true;
26
+ } catch (error) {
27
+ if (isMissingPathError(error)) return false;
28
+ throw error;
29
+ }
30
+ };
31
+ const listDeclaredPublicAssetFiles = async (appDirectory, assets, scope)=>{
32
+ const files = [];
33
+ const owners = new Map();
34
+ const add = (index, logicalPath)=>{
35
+ const owner = owners.get(logicalPath);
36
+ if (void 0 !== owner) throw new Error(`${scope.label}[${index}] and ${scope.label}[${owner}] both stage ${JSON.stringify(logicalPath)}.`);
37
+ owners.set(logicalPath, index);
38
+ files.push({
39
+ index,
40
+ logicalPath
41
+ });
42
+ };
43
+ const visit = async (asset, sourcePath, destination)=>{
44
+ const stat = await promises.lstat(sourcePath);
45
+ if (stat.isSymbolicLink()) throw new Error(`${scope.label}[${asset.index}].from must not contain symbolic links: ${node_path.relative(appDirectory, sourcePath).replace(/\\/gu, '/')}`);
46
+ if (stat.isFile()) {
47
+ if (destination === DECLARED_PUBLIC_ASSET_DIRECTORY) throw new Error(`${scope.label}[${asset.index}].to must name a file when ${scope.label}[${asset.index}].from is a file.`);
48
+ add(asset.index, destination);
49
+ return;
50
+ }
51
+ if (!stat.isDirectory()) throw new Error(`${scope.label}[${asset.index}].from must contain only files and directories: ${node_path.relative(appDirectory, sourcePath).replace(/\\/gu, '/')}`);
52
+ const entries = (await promises.readdir(sourcePath)).sort();
53
+ for (const entry of entries)await visit(asset, node_path.join(sourcePath, entry), node_path.posix.join(destination, entry));
54
+ };
55
+ for (const asset of assets){
56
+ const sourcePath = node_path.join(appDirectory, asset.from);
57
+ if (!await pathExists(sourcePath)) throw new Error(`${scope.label}[${asset.index}].from does not exist: ${asset.from}`);
58
+ await visit(asset, sourcePath, node_path.posix.join(DECLARED_PUBLIC_ASSET_DIRECTORY, asset.to));
59
+ }
60
+ return files;
61
+ };
62
+ const resolveAddedDeclaredPublicAssetPaths = async ({ appDirectory, assets, generatedRoot, scope })=>{
63
+ const added = [];
64
+ for (const file of (await listDeclaredPublicAssetFiles(appDirectory, assets, scope)))if (!await pathExists(node_path.join(generatedRoot, file.logicalPath))) added.push(file.logicalPath);
65
+ return added.sort();
66
+ };
67
+ const stageDeclaredPublicAssets = async ({ appDirectory, outputDirectory, assets, scope })=>{
68
+ const files = await listDeclaredPublicAssetFiles(appDirectory, assets, scope);
69
+ const added = [];
70
+ for (const file of files){
71
+ const asset = assets.find((candidate)=>candidate.index === file.index);
72
+ const relativePath = node_path.posix.relative(node_path.posix.join(DECLARED_PUBLIC_ASSET_DIRECTORY, asset.to), file.logicalPath);
73
+ const destination = node_path.join(outputDirectory, file.logicalPath);
74
+ const replacesGeneratedFile = await pathExists(destination);
75
+ await promises.mkdir(node_path.dirname(destination), {
76
+ recursive: true
77
+ });
78
+ await promises.copyFile(node_path.join(appDirectory, asset.from, relativePath), destination);
79
+ if (!replacesGeneratedFile) added.push(file.logicalPath);
80
+ }
81
+ return added.sort();
82
+ };
83
+ export { CLOUDFLARE_PUBLIC_ASSET_SCOPE, DECLARED_PUBLIC_ASSET_DIRECTORY, NODE_PUBLIC_ASSET_SCOPE, normalizeDeclaredPublicAssets, resolveAddedDeclaredPublicAssetPaths, stageDeclaredPublicAssets };
@@ -1,5 +1,14 @@
1
1
  import "node:module";
2
- import { provider } from "std-env";
2
+ import { getArgv } from "@modern-js/utils";
3
+ import { provider as external_std_env_provider } from "std-env";
4
+ const DEPLOY_TARGETS = [
5
+ 'node',
6
+ 'vercel',
7
+ 'netlify',
8
+ 'ghPages',
9
+ 'cloudflare'
10
+ ];
11
+ const DEPLOY_TARGET_FLAG = '--deploy-target';
3
12
  const providerDeployTargets = {
4
13
  vercel: 'vercel',
5
14
  netlify: 'netlify',
@@ -7,5 +16,43 @@ const providerDeployTargets = {
7
16
  cloudflare_pages: 'cloudflare',
8
17
  cloudflare_workers: 'cloudflare'
9
18
  };
10
- const resolveDeployTarget = (config, envDeployTarget = process.env.MODERNJS_DEPLOY, detectedProvider = provider)=>config.deploy?.target || envDeployTarget || (detectedProvider ? providerDeployTargets[detectedProvider] : void 0) || 'node';
11
- export { resolveDeployTarget };
19
+ const readDeployTargetFlag = (argv)=>{
20
+ for (const [index, arg] of argv.entries()){
21
+ if (arg === DEPLOY_TARGET_FLAG) {
22
+ const value = argv[index + 1];
23
+ if (void 0 === value || value.startsWith('-')) throw new Error(`${DEPLOY_TARGET_FLAG} needs a value.`);
24
+ return value;
25
+ }
26
+ if (arg.startsWith(`${DEPLOY_TARGET_FLAG}=`)) return arg.slice(DEPLOY_TARGET_FLAG.length + 1);
27
+ }
28
+ };
29
+ const assertDeployTarget = (target, source)=>{
30
+ if (!DEPLOY_TARGETS.includes(target)) throw new Error(`Unknown deploy target '${target}' from ${source}. Use one of: ${DEPLOY_TARGETS.join(', ')}.`);
31
+ return target;
32
+ };
33
+ const resolveDeployTarget = ({ argv = getArgv(), configTarget, env = process.env.MODERNJS_DEPLOY, provider = external_std_env_provider } = {})=>{
34
+ const explicit = [
35
+ [
36
+ readDeployTargetFlag(argv),
37
+ DEPLOY_TARGET_FLAG
38
+ ],
39
+ [
40
+ configTarget,
41
+ 'deploy.target'
42
+ ],
43
+ [
44
+ env,
45
+ 'MODERNJS_DEPLOY'
46
+ ]
47
+ ].find(([target])=>target);
48
+ if (explicit?.[0]) return {
49
+ target: assertDeployTarget(explicit[0], explicit[1]),
50
+ explicit: true
51
+ };
52
+ return {
53
+ target: provider && providerDeployTargets[provider] || 'node',
54
+ explicit: false
55
+ };
56
+ };
57
+ const getDeployingTarget = ({ deployTarget, metaName })=>deployTarget && ('modern-js' === metaName || deployTarget.explicit) ? deployTarget.target : void 0;
58
+ export { DEPLOY_TARGETS, DEPLOY_TARGET_FLAG, getDeployingTarget, resolveDeployTarget };