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