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

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,147 +1,28 @@
1
- import { execFileSync } from "node:child_process";
2
1
  import { createHash } from "node:crypto";
3
2
  import { accessSync, chmodSync, constants, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
- import { createRequire } from "node:module";
3
+ import { createRequire, findPackageJSON } from "node:module";
5
4
  import { tmpdir } from "node:os";
6
- import { basename, dirname, join, resolve } from "node:path";
7
- const EFFECT_TSGO_PACKAGE = '@effect/tsgo';
8
- const EFFECT_TSGO_BIN = 'effect-tsgo';
5
+ import { basename, join } from "node:path";
6
+ import "node:url";
7
+ const owningModuleUrl = import.meta.url;
8
+ const owningManifest = findPackageJSON(owningModuleUrl, owningModuleUrl);
9
+ if (!owningManifest) throw new Error('Cannot find owning build-config package manifest');
10
+ const owningName = JSON.parse(readFileSync(owningManifest, 'utf8')).name;
11
+ if ('string' != typeof owningName || 0 === owningName.length) throw new Error(`Invalid owning build-config package name: ${owningManifest}`);
12
+ const { effectCompilerDiscoveryFailureStage: effectCompilerDiscoveryFailureStage, resolveInstalledEffectCompiler: resolveInstalledEffectCompiler } = createRequire(owningModuleUrl)(`${owningName}/internal-effect-discovery`);
9
13
  const EFFECT_TSGO_RESOLUTION_ERROR = 'Unable to resolve the Effect TS-Go compiler. Install "@effect/tsgo" and a native TypeScript backend for this build config, or set EFFECT_TSGO_BIN.';
10
14
  const executableEffectTsgoCompilers = new Map();
11
- const BUILD_CONFIG_ENVIRONMENT_PLUGIN = 'ModernJsBuildConfigEnvironmentLeasePlugin';
12
- const ENVIRONMENT_LEASE_REGISTRY_KEY = Symbol.for('@modern-js/app-tools/build-config-environment-lease-registry/v1');
13
- const ENVIRONMENT_LEASE_REGISTRY_BRAND = Symbol.for('@modern-js/app-tools/build-config-environment-lease-registry-brand/v1');
14
- const LIFECYCLE_HOOK_NAMES = [
15
- 'run',
16
- 'watchRun',
17
- 'afterDone',
18
- 'failed',
19
- 'shutdown',
20
- 'watchClose'
21
- ];
22
- function restoreEnvironment(name, value) {
23
- if (void 0 === value) delete process.env[name];
24
- else process.env[name] = value;
25
- }
26
- function createEnvironmentLeaseRegistry() {
27
- const environmentLeases = new Map();
28
- return Object.freeze({
29
- acquire (name, value) {
30
- let lease = environmentLeases.get(name);
31
- if (lease) {
32
- if (lease.value !== value) throw new Error(`Build config environment "${name}" already has an active lease for a different value.`);
33
- if (process.env[name] !== lease.value) throw new Error(`Build config environment lease for "${name}" lost ownership before another lease was acquired.`);
34
- } else {
35
- lease = {
36
- originalValue: process.env[name],
37
- owners: new Set(),
38
- value
39
- };
40
- process.env[name] = value;
41
- environmentLeases.set(name, lease);
42
- }
43
- const owner = Symbol(name);
44
- lease.owners.add(owner);
45
- let released = false;
46
- return ()=>{
47
- if (released) return;
48
- if (environmentLeases.get(name) !== lease || !lease.owners.has(owner)) throw new Error(`Build config environment lease for "${name}" cannot be restored because its ownership record was lost.`);
49
- released = true;
50
- const lostOwnership = process.env[name] !== lease.value;
51
- lease.owners.delete(owner);
52
- if (0 === lease.owners.size) {
53
- environmentLeases.delete(name);
54
- restoreEnvironment(name, lease.originalValue);
55
- } else if (lostOwnership) process.env[name] = lease.value;
56
- if (lostOwnership) throw new Error(`Build config environment lease for "${name}" lost ownership before restoration.`);
57
- };
58
- },
59
- brand: ENVIRONMENT_LEASE_REGISTRY_BRAND
60
- });
61
- }
62
- function getEnvironmentLeaseRegistry() {
63
- const descriptor = Object.getOwnPropertyDescriptor(process, ENVIRONMENT_LEASE_REGISTRY_KEY);
64
- if (descriptor) {
65
- const registry = descriptor.value;
66
- if (descriptor.configurable || descriptor.enumerable || descriptor.writable || registry?.brand !== ENVIRONMENT_LEASE_REGISTRY_BRAND || 'function' != typeof registry.acquire) throw new Error('The process-global build config environment lease registry is occupied by an incompatible value.');
67
- return registry;
68
- }
69
- const registry = createEnvironmentLeaseRegistry();
70
- Object.defineProperty(process, ENVIRONMENT_LEASE_REGISTRY_KEY, {
71
- configurable: false,
72
- enumerable: false,
73
- value: registry,
74
- writable: false
15
+ function effectTsgoResolutionError(cause, stage, from) {
16
+ const detail = cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
17
+ return new Error(`${EFFECT_TSGO_RESOLUTION_ERROR}\n${stage} failed for ${String(from)}: ${detail}`, {
18
+ cause
75
19
  });
76
- return registry;
77
- }
78
- function acquireEnvironmentLease(name, value) {
79
- return getEnvironmentLeaseRegistry().acquire(name, value);
80
- }
81
- function releaseAfterFailure(release, originalError) {
82
- try {
83
- release();
84
- } catch (restorationError) {
85
- throw new AggregateError([
86
- originalError,
87
- restorationError
88
- ], 'Build config environment setup and restoration both failed.');
89
- }
90
- throw originalError;
91
- }
92
- class BuildConfigEnvironmentLeasePlugin {
93
- apply(compiler) {
94
- const hooks = compiler.hooks;
95
- try {
96
- const lifecycleHooks = {};
97
- for (const hookName of LIFECYCLE_HOOK_NAMES){
98
- const hook = hooks[hookName];
99
- if (!hook || 'function' != typeof hook.tap) throw new Error(`Rspack does not expose the "${hookName}" lifecycle hook required to restore build config environment leases.`);
100
- lifecycleHooks[hookName] = hook;
101
- }
102
- let mode = 'pending';
103
- const modeTap = {
104
- name: BUILD_CONFIG_ENVIRONMENT_PLUGIN,
105
- stage: Number.MIN_SAFE_INTEGER
106
- };
107
- const releaseTap = {
108
- name: BUILD_CONFIG_ENVIRONMENT_PLUGIN,
109
- stage: Number.MAX_SAFE_INTEGER
110
- };
111
- const releaseAfterOneShot = ()=>{
112
- if ('watch' !== mode && true !== compiler.watchMode) this.release();
113
- };
114
- lifecycleHooks.run.tap(modeTap, ()=>{
115
- mode = 'run';
116
- });
117
- lifecycleHooks.watchRun.tap(modeTap, ()=>{
118
- mode = 'watch';
119
- });
120
- lifecycleHooks.afterDone.tap(releaseTap, releaseAfterOneShot);
121
- lifecycleHooks.failed.tap(releaseTap, releaseAfterOneShot);
122
- lifecycleHooks.shutdown.tap(releaseTap, this.release);
123
- lifecycleHooks.watchClose.tap(releaseTap, this.release);
124
- } catch (error) {
125
- releaseAfterFailure(this.release, error);
126
- }
127
- }
128
- constructor(release){
129
- this.release = release;
130
- }
131
- }
132
- function resolveEffectTsgoCli(from) {
133
- const projectRequire = createRequire(from);
134
- const packageJsonPath = projectRequire.resolve(`${EFFECT_TSGO_PACKAGE}/package.json`);
135
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
136
- const bin = 'string' == typeof packageJson.bin ? packageJson.bin : packageJson.bin?.[EFFECT_TSGO_BIN];
137
- if (!bin) throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
138
- return resolve(dirname(packageJsonPath), bin);
139
20
  }
140
21
  function resolveExecutableEffectTsgoCompiler(compilerPath) {
141
22
  if ('win32' === process.platform) return compilerPath;
142
23
  const sourcePath = realpathSync(compilerPath);
143
24
  const source = lstatSync(sourcePath);
144
- if (!source.isFile()) throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
25
+ if (!source.isFile()) throw new Error(`Effect TS-Go compiler path is not a regular file: ${sourcePath}`);
145
26
  try {
146
27
  accessSync(sourcePath, constants.X_OK);
147
28
  return compilerPath;
@@ -188,37 +69,22 @@ function resolveExecutableEffectTsgoCompiler(compilerPath) {
188
69
  function getBuildConfigEnvironment(name) {
189
70
  return process.env[name];
190
71
  }
191
- function withBuildConfigEnvironment(name, value, setup) {
192
- return async (config, ...args)=>{
193
- const release = acquireEnvironmentLease(name, value);
194
- try {
195
- const configured = await setup(config, ...args) ?? config;
196
- configured.plugins = [
197
- ...configured.plugins ?? [],
198
- new BuildConfigEnvironmentLeasePlugin(release)
199
- ];
200
- return configured;
201
- } catch (error) {
202
- releaseAfterFailure(release, error);
203
- }
204
- };
205
- }
206
72
  function resolveEffectTsgoCompiler(options) {
207
73
  const configuredCompiler = getBuildConfigEnvironment('EFFECT_TSGO_BIN')?.trim();
208
74
  if (configuredCompiler) try {
209
75
  return resolveExecutableEffectTsgoCompiler(configuredCompiler);
210
- } catch {
211
- throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
76
+ } catch (cause) {
77
+ throw effectTsgoResolutionError(cause, 'Configured compiler validation', options.from);
212
78
  }
79
+ let stage = 'Package CLI resolution';
213
80
  try {
214
- const compiler = execFileSync(process.execPath, [
215
- resolveEffectTsgoCli(options.from),
216
- 'get-exe-path'
217
- ], {
218
- encoding: 'utf-8'
219
- }).trim();
220
- if (compiler) return resolveExecutableEffectTsgoCompiler(compiler);
221
- } catch {}
222
- throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
81
+ const compiler = resolveInstalledEffectCompiler(options.from).trim();
82
+ stage = 'Compiler backend lookup';
83
+ if (!compiler) throw new Error('Effect TS-Go discovery returned an empty compiler path');
84
+ stage = 'Compiler executable validation';
85
+ return resolveExecutableEffectTsgoCompiler(compiler);
86
+ } catch (cause) {
87
+ throw effectTsgoResolutionError(cause, effectCompilerDiscoveryFailureStage(cause) ?? stage, options.from);
88
+ }
223
89
  }
224
- export { getBuildConfigEnvironment, resolveEffectTsgoCompiler, withBuildConfigEnvironment };
90
+ export { getBuildConfigEnvironment, resolveEffectTsgoCompiler };
@@ -0,0 +1,102 @@
1
+ import { lstatSync, readFileSync, realpathSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const EFFECT_TSGO_PACKAGE = '@effect/tsgo';
6
+ const EFFECT_TSGO_BIN = 'effect-tsgo';
7
+ const NATIVE_TYPESCRIPT_PACKAGES = [
8
+ "typescript",
9
+ "@typescript/native"
10
+ ];
11
+ const failureStages = new WeakMap();
12
+ function effectCompilerDiscoveryFailureStage(error) {
13
+ return error && 'object' == typeof error ? failureStages.get(error) : void 0;
14
+ }
15
+ function resolveEffectTsgoPackage(from) {
16
+ const projectRequire = createRequire(from);
17
+ const packageJsonPath = projectRequire.resolve(`${EFFECT_TSGO_PACKAGE}/package.json`);
18
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
19
+ const bin = 'string' == typeof packageJson.bin ? packageJson.bin : packageJson.bin?.[EFFECT_TSGO_BIN];
20
+ if (!bin) throw new Error(`Resolved ${EFFECT_TSGO_PACKAGE} package has no ${EFFECT_TSGO_BIN} bin: ${packageJsonPath}`);
21
+ const cliPath = realpathSync(resolve(dirname(packageJsonPath), bin));
22
+ if (!lstatSync(cliPath).isFile()) throw new Error(`Effect TS-Go CLI is not a regular file: ${cliPath}`);
23
+ return {
24
+ packageJsonPath,
25
+ packageJson,
26
+ cliPath
27
+ };
28
+ }
29
+ function isAbsentNativeTypeScriptPackage(projectRequire, from, packageName, error) {
30
+ const filename = 'string' != typeof from || from.startsWith('file:') ? fileURLToPath(from) : from;
31
+ if (!(error instanceof Error) || !('code' in error) || 'MODULE_NOT_FOUND' !== error.code || 'path' in error && void 0 !== error.path || error.message.split('\n')[0] !== `Cannot find module '${packageName}/package.json'` || !('requireStack' in error) || !Array.isArray(error.requireStack) || error.requireStack[0] !== filename) return false;
32
+ for (const searchDirectory of projectRequire.resolve.paths(packageName) ?? [])try {
33
+ lstatSync(join(searchDirectory, packageName));
34
+ return false;
35
+ } catch (presenceError) {
36
+ if (!(presenceError instanceof Error) || !('code' in presenceError) || ![
37
+ 'ENOENT',
38
+ 'ENOTDIR'
39
+ ].includes(String(presenceError.code))) throw presenceError;
40
+ }
41
+ return true;
42
+ }
43
+ function resolveNativeTypeScriptPackage(from) {
44
+ const projectRequire = createRequire(from);
45
+ for (const packageName of NATIVE_TYPESCRIPT_PACKAGES){
46
+ let packageJsonPath;
47
+ try {
48
+ packageJsonPath = projectRequire.resolve(`${packageName}/package.json`);
49
+ } catch (error) {
50
+ if (isAbsentNativeTypeScriptPackage(projectRequire, from, packageName, error)) continue;
51
+ throw error;
52
+ }
53
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
54
+ const major = packageJson.version?.match(/^(\d+)\.\d+\.\d+(?:[-+].*)?$/u)?.[1];
55
+ if (!major) throw new Error(`Resolved TypeScript package has no valid version: ${packageJsonPath}`);
56
+ if (Number(major) >= 7) return packageJsonPath;
57
+ }
58
+ throw new Error(`No native TypeScript backend is installed for ${String(from)}. Install typescript >=7 or @typescript/native.`);
59
+ }
60
+ function resolveNativeTypeScriptManifest(from) {
61
+ const packageJsonPath = resolveNativeTypeScriptPackage(from);
62
+ const directory = dirname(packageJsonPath);
63
+ const discoveryPackage = resolveNativeTypeScriptPackage(join(directory, '__ultramodern_effect_backend__.cjs'));
64
+ if (realpathSync(discoveryPackage) !== realpathSync(packageJsonPath)) throw new Error(`Effect discovery would select a different native TypeScript installation from ${directory}: ${discoveryPackage}`);
65
+ return packageJsonPath;
66
+ }
67
+ function readCompilerArtifact(filename) {
68
+ const canonicalPath = realpathSync(filename);
69
+ const stats = lstatSync(canonicalPath);
70
+ if (!stats.isFile()) throw new Error(`Compiler artifact is not a regular file: ${filename}`);
71
+ if (0 === stats.size) throw new Error(`Compiler artifact is empty: ${filename}`);
72
+ return canonicalPath;
73
+ }
74
+ function resolveInstalledEffectCompiler(from) {
75
+ let stage = 'Package CLI resolution';
76
+ try {
77
+ const effect = resolveEffectTsgoPackage(from);
78
+ stage = 'Native TypeScript package resolution';
79
+ const typeScriptManifest = resolveNativeTypeScriptManifest(from);
80
+ const typeScript = JSON.parse(readFileSync(typeScriptManifest, 'utf8'));
81
+ const nativeName = `@typescript/typescript-${process.platform}-${process.arch}`;
82
+ const nativeManifest = createRequire(typeScriptManifest).resolve(`${nativeName}/package.json`);
83
+ const native = JSON.parse(readFileSync(nativeManifest, 'utf8'));
84
+ if (native.name !== nativeName || native.version !== typeScript.version || 'string' != typeof native.version || !/^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/u.test(native.version) || 'string' != typeof native.gitHead || !/^[a-f\d]{40}$/u.test(native.gitHead) || native.gitHead !== typeScript.gitHead) throw new Error(`Native TypeScript platform does not match its selected package: ${nativeManifest}`);
85
+ const binaryName = 'win32' === process.platform ? 'tsc.exe' : 'tsc';
86
+ readCompilerArtifact(join(dirname(nativeManifest), 'lib', binaryName));
87
+ stage = 'Compiler backend lookup';
88
+ const effectName = `@effect/tsgo-${process.platform}-${process.arch}`;
89
+ const effectManifest = createRequire(effect.cliPath).resolve(`${effectName}/package.json`);
90
+ const platform = JSON.parse(readFileSync(effectManifest, 'utf8'));
91
+ if (platform.name !== effectName || 'string' != typeof effect.packageJson.version || platform.version !== effect.packageJson.version) throw new Error(`Effect platform does not match its selected provider: ${effectManifest}`);
92
+ const metadataPath = join(dirname(effectManifest), 'lib', 'upstream.json');
93
+ const metadata = JSON.parse(readFileSync(metadataPath, 'utf8'));
94
+ const component = metadata.components?.typescript?.[native.version];
95
+ if (5 !== metadata.schemaVersion || component?.provider !== "typescript-go" || component.gitHead !== native.gitHead) throw new Error(`Effect replacement metadata does not match the selected native TypeScript backend: ${metadataPath}`);
96
+ return readCompilerArtifact(join(dirname(effectManifest), 'artifacts', "typescript", native.version, binaryName));
97
+ } catch (error) {
98
+ if (error && 'object' == typeof error && !failureStages.has(error)) failureStages.set(error, stage);
99
+ throw error;
100
+ }
101
+ }
102
+ export { effectCompilerDiscoveryFailureStage, resolveInstalledEffectCompiler };
@@ -1 +1 @@
1
- export { getBuildConfigEnvironment, resolveEffectTsgoCompiler, withBuildConfigEnvironment } from "./build-environment.mjs";
1
+ export { getBuildConfigEnvironment, resolveEffectTsgoCompiler } from "./build-environment.mjs";
@@ -0,0 +1,20 @@
1
+ import { resolveBrowserManifestAddress } from "@modern-js/surface-resolution";
2
+ import { resolveDeployTarget } from "../deploy-output/target.mjs";
3
+ import { getBuildConfigEnvironment } from "./build-environment.mjs";
4
+ function createRemoteManifestUrl(options) {
5
+ const env = {
6
+ ...Object.fromEntries([
7
+ options.manifestEnv,
8
+ options.publicUrlEnv,
9
+ 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN'
10
+ ].map((name)=>[
11
+ name,
12
+ getBuildConfigEnvironment(name)
13
+ ])),
14
+ MODERNJS_DEPLOY: resolveDeployTarget().target
15
+ };
16
+ const address = resolveBrowserManifestAddress(env, options, getBuildConfigEnvironment('NODE_ENV') ?? 'production');
17
+ if (!address.ok) throw new Error(`Remote ${options.mfName}: ${address.reason}`);
18
+ return env[options.manifestEnv]?.trim() || `${options.mfName}@${address.manifestUrl}`;
19
+ }
20
+ export { createRemoteManifestUrl };
@@ -1,5 +1,6 @@
1
1
  import node_path from "node:path";
2
2
  import { fs } from "@modern-js/utils";
3
+ import { CLOUDFLARE_PUBLIC_ASSET_SCOPE, normalizeDeclaredPublicAssets, stageDeclaredPublicAssets } from "../deploy-output/public-assets.mjs";
3
4
  import { RESERVED_ARTIFACT_DESTINATION_DIRECTORIES, RESERVED_ARTIFACT_DESTINATION_FILES, ROUTE_SPEC_OUTPUT } from "./constants.mjs";
4
5
  import { normalizeRelativePath } from "./utils.mjs";
5
6
  const normalizeCloudflareArtifact = (artifact, index)=>{
@@ -15,18 +16,7 @@ const normalizeCloudflareArtifact = (artifact, index)=>{
15
16
  };
16
17
  };
17
18
  const getCloudflareArtifacts = (modernConfig)=>(modernConfig.deploy?.worker?.artifacts ?? []).map(normalizeCloudflareArtifact);
18
- const normalizeCloudflarePublicAsset = (asset, index)=>{
19
- const from = normalizeRelativePath(asset.from, `deploy.worker.publicAssets[${index}].from`, 'app root');
20
- const to = normalizeRelativePath(asset.to, `deploy.worker.publicAssets[${index}].to`, 'Cloudflare public output', {
21
- allowRoot: true
22
- });
23
- return {
24
- from,
25
- to,
26
- index
27
- };
28
- };
29
- const getCloudflarePublicAssets = (modernConfig)=>(modernConfig.deploy?.worker?.publicAssets ?? []).map(normalizeCloudflarePublicAsset);
19
+ const getCloudflarePublicAssets = (modernConfig)=>normalizeDeclaredPublicAssets(modernConfig.deploy?.worker?.publicAssets, CLOUDFLARE_PUBLIC_ASSET_SCOPE);
30
20
  const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)=>{
31
21
  for (const artifact of artifacts){
32
22
  const sourcePath = node_path.join(appDirectory, artifact.from);
@@ -34,13 +24,12 @@ const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)
34
24
  await fs.copy(sourcePath, node_path.join(outputDirectory, artifact.to));
35
25
  }
36
26
  };
37
- const copyCloudflarePublicAssets = async (appDirectory, publicDirectory, publicAssets)=>{
38
- for (const asset of publicAssets){
39
- const sourcePath = node_path.join(appDirectory, asset.from);
40
- if (!await fs.pathExists(sourcePath)) throw new Error(`deploy.worker.publicAssets[${asset.index}].from does not exist: ${asset.from}`);
41
- await fs.copy(sourcePath, node_path.join(publicDirectory, asset.to));
42
- }
43
- };
27
+ const copyCloudflarePublicAssets = async (appDirectory, outputDirectory, publicAssets)=>stageDeclaredPublicAssets({
28
+ appDirectory,
29
+ outputDirectory,
30
+ assets: publicAssets,
31
+ scope: CLOUDFLARE_PUBLIC_ASSET_SCOPE
32
+ });
44
33
  const copyCloudflareD1Migrations = async (appDirectory, outputDirectory, modernConfig)=>{
45
34
  for (const [index, database] of (modernConfig.deploy?.worker?.d1Databases ?? []).entries()){
46
35
  if (!database.migrationsDir) continue;
@@ -1,13 +1,21 @@
1
1
  import node_path from "node:path";
2
- import { ULTRAMODERN_BUILD_ARTIFACT_PATH, isUltramodernBuildArtifact, nonEmptyString, toDeliveryUnitIdentity } from "@modern-js/backend-federation-contracts";
2
+ import { isDeepStrictEqual } from "node:util";
3
+ import { ULTRAMODERN_BUILD_ARTIFACT_FILE, ULTRAMODERN_BUILD_ARTIFACT_PATH, assertRendererProfileCompatibility, assertUltramodernBuildArtifact, formatBackendFederationValidationErrors, immutableRendererRouterBindings, nonEmptyString, toDeliveryUnitIdentity, validateRendererIdentity, validateRendererProfile, validateRendererRouterBindings } from "@modern-js/backend-federation-contracts";
3
4
  import { fs } from "@modern-js/utils";
4
5
  import { resolveUltramodernReleaseIdentity } from "../release-identity.mjs";
5
6
  import { isRecord } from "./utils.mjs";
6
- const COMPACT_CONFIG_PATH = '.modernjs/ultramodern.json';
7
+ const TOPOLOGY_PATH = 'topology/reference-topology.json';
8
+ const RENDERER_PROJECTION_FIELDS = [
9
+ 'renderer',
10
+ 'rendererIdentity',
11
+ 'rendererIdentities',
12
+ 'rendererProfile',
13
+ 'routerBindings'
14
+ ];
7
15
  const findWorkspaceRoot = async (appDirectory)=>{
8
16
  let current = node_path.resolve(appDirectory);
9
17
  for(;;){
10
- if (await fs.pathExists(node_path.join(current, COMPACT_CONFIG_PATH))) return current;
18
+ if (await fs.pathExists(node_path.join(current, TOPOLOGY_PATH))) return current;
11
19
  const parent = node_path.dirname(current);
12
20
  if (parent === current) return;
13
21
  current = parent;
@@ -24,51 +32,83 @@ const stampReleaseIdentity = (identity, workspaceRoot)=>{
24
32
  })
25
33
  };
26
34
  };
27
- const resolveCompactApp = async (appDirectory)=>{
35
+ const resolveTopologyApp = async (appDirectory)=>{
28
36
  const workspaceRoot = await findWorkspaceRoot(appDirectory);
29
37
  if (!workspaceRoot) return;
30
- let compactConfig;
31
- try {
32
- compactConfig = await fs.readJSON(node_path.join(workspaceRoot, COMPACT_CONFIG_PATH));
33
- } catch {
34
- return {
35
- workspaceRoot
36
- };
37
- }
38
- if (!isRecord(compactConfig)) return {
39
- workspaceRoot
40
- };
41
- const topology = isRecord(compactConfig.topology) ? compactConfig.topology : void 0;
42
- const apps = Array.isArray(topology?.apps) ? topology.apps : void 0;
43
- if (!apps) return {
44
- app: compactConfig,
45
- workspaceRoot
46
- };
38
+ const topology = await fs.readJSON(node_path.join(workspaceRoot, TOPOLOGY_PATH));
39
+ if (!isRecord(topology) || !isRecord(topology.shell) || !Array.isArray(topology.verticals) || void 0 !== topology.shells && !Array.isArray(topology.shells)) throw new Error('[cloudflare-delivery-unit] Invalid declared reference topology.');
40
+ const apps = [
41
+ topology.shell,
42
+ ...topology.verticals,
43
+ ...Array.isArray(topology.shells) ? topology.shells : []
44
+ ];
47
45
  const resolvedAppDirectory = node_path.resolve(appDirectory);
48
46
  const app = apps.find((candidate)=>{
49
47
  if (!isRecord(candidate)) return false;
50
48
  const appPath = nonEmptyString(candidate.path);
51
49
  return void 0 !== appPath && node_path.resolve(workspaceRoot, appPath.replace(/^\.\/+/u, '')) === resolvedAppDirectory;
52
50
  });
51
+ if (!isRecord(app)) throw new Error(`[cloudflare-delivery-unit] ${appDirectory} is missing from the declared reference topology.`);
53
52
  return {
54
- ...isRecord(app) ? {
55
- app
56
- } : {},
53
+ app,
54
+ role: app === topology.shell || Array.isArray(topology.shells) && topology.shells.includes(app) ? 'shell' : 'microvertical',
57
55
  workspaceRoot
58
56
  };
59
57
  };
60
- const createDeliveryUnitStamp = (identity, app)=>{
61
- const surfaceProfile = nonEmptyString(app?.surfaceProfile);
58
+ const resolveTopologyReleaseRole = async (appDirectory)=>{
59
+ const resolved = await resolveTopologyApp(appDirectory);
60
+ if (!resolved?.app) return;
61
+ const kind = 'shell' === resolved.role ? 'shell' : 'vertical';
62
+ if (resolved.app.kind !== kind) throw new Error('[cloudflare-delivery-unit] Declared app kind disagrees with its topology membership.');
63
+ return resolved.role;
64
+ };
65
+ const createTopologyDeliveryUnitStamp = (identity, app)=>{
66
+ const surfaceProfile = nonEmptyString(app.surfaceProfile);
67
+ if (void 0 !== surfaceProfile && ![
68
+ 'api-only',
69
+ 'ui-only',
70
+ 'full-stack'
71
+ ].includes(surfaceProfile)) throw new Error('[cloudflare-delivery-unit] Declared app has an invalid surface profile.');
62
72
  const emitsUi = 'api-only' !== surfaceProfile;
63
73
  const emitsApi = 'ui-only' !== surfaceProfile;
74
+ const appId = nonEmptyString(app.id);
75
+ if (!appId) throw new Error('[cloudflare-delivery-unit] Declared app requires an id.');
76
+ let ui;
77
+ if (emitsUi) if (RENDERER_PROJECTION_FIELDS.every((field)=>!Object.hasOwn(app, field))) ui = {
78
+ ...identity,
79
+ surface: 'ui'
80
+ };
81
+ else {
82
+ const routerBindings = isRecord(app.routerBindings) ? app.routerBindings : {};
83
+ const errors = [
84
+ ...validateRendererIdentity(app.rendererIdentity).errors,
85
+ ...validateRendererProfile(app.rendererProfile).errors,
86
+ ...validateRendererRouterBindings(app.routerBindings, Object.keys(routerBindings), 'routerBindings').errors
87
+ ];
88
+ if (errors.length) throw new Error(`[cloudflare-delivery-unit] ${formatBackendFederationValidationErrors(errors)}`);
89
+ const rendererIdentity = app.rendererIdentity;
90
+ const rendererProfile = app.rendererProfile;
91
+ if (!Object.hasOwn(routerBindings, rendererIdentity.entryName)) throw new Error('[cloudflare-delivery-unit] Topology routerBindings must include the primary renderer identity entry.');
92
+ const declaredIdentity = toDeliveryUnitIdentity(app.deliveryUnit);
93
+ if (rendererIdentity.renderer !== app.renderer || rendererIdentity.renderer !== rendererProfile.renderer || rendererIdentity.appId !== app.id || rendererIdentity.buildId !== declaredIdentity?.buildMarker) throw new Error('[cloudflare-delivery-unit] Topology UI renderer metadata must match its app and delivery-unit identity.');
94
+ ui = {
95
+ ...identity,
96
+ surface: 'ui',
97
+ rendererIdentity: {
98
+ ...rendererIdentity,
99
+ buildId: identity.buildMarker
100
+ },
101
+ rendererProfile,
102
+ routerBindings: immutableRendererRouterBindings(routerBindings)
103
+ };
104
+ }
105
+ else if (void 0 !== app.rendererIdentity || void 0 !== app.rendererProfile || void 0 !== app.routerBindings) throw new Error('[cloudflare-delivery-unit] API-only topology must not declare a UI renderer identity or profile.');
64
106
  return {
65
107
  ...identity,
108
+ appId,
66
109
  surfaces: {
67
- ...emitsUi ? {
68
- ui: {
69
- ...identity,
70
- surface: 'ui'
71
- }
110
+ ...ui ? {
111
+ ui
72
112
  } : {},
73
113
  ...emitsApi ? {
74
114
  api: {
@@ -80,22 +120,61 @@ const createDeliveryUnitStamp = (identity, app)=>{
80
120
  };
81
121
  };
82
122
  const resolveTopologyDeliveryUnit = async (appDirectory)=>{
83
- const resolved = await resolveCompactApp(appDirectory);
123
+ const resolved = await resolveTopologyApp(appDirectory);
84
124
  if (!resolved?.app) return;
85
125
  const identity = toDeliveryUnitIdentity(resolved.app.deliveryUnit);
86
- return identity ? createDeliveryUnitStamp(stampReleaseIdentity(identity, resolved.workspaceRoot), resolved.app) : void 0;
126
+ if (!identity) throw new Error('[cloudflare-delivery-unit] Declared app is missing a valid delivery-unit identity.');
127
+ return createTopologyDeliveryUnitStamp(stampReleaseIdentity(identity, resolved.workspaceRoot), resolved.app);
87
128
  };
88
- const resolveWorkerDeliveryUnitStamp = async (appDirectory)=>{
89
- const buildArtifactPath = node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH);
90
- let identity;
91
- if (await fs.pathExists(buildArtifactPath)) {
92
- const artifact = await fs.readJSON(buildArtifactPath);
93
- if (!isUltramodernBuildArtifact(artifact)) return;
94
- identity = toDeliveryUnitIdentity(artifact.deliveryUnit);
129
+ const resolveWorkerDeliveryUnitStamp = async (appDirectory, distDirectory)=>{
130
+ const buildArtifactPath = node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE);
131
+ if (!await fs.pathExists(buildArtifactPath)) {
132
+ if (await fs.pathExists(node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH))) throw new Error('[cloudflare-delivery-unit] Finalized build artifact is required before worker output stamping.');
133
+ return;
134
+ }
135
+ const artifact = await fs.readJSON(buildArtifactPath);
136
+ assertUltramodernBuildArtifact(artifact);
137
+ const identity = {
138
+ unitId: artifact.deliveryUnit.unitId,
139
+ buildMarker: artifact.deliveryUnit.buildMarker,
140
+ sourceRevision: artifact.deliveryUnit.sourceRevision
141
+ };
142
+ const resolved = await resolveTopologyApp(appDirectory);
143
+ const stampedArtifact = artifact;
144
+ let emitsApi = true;
145
+ if (resolved?.app) {
146
+ if (stampedArtifact.deliveryUnit.appId !== resolved.app.id) throw new Error('[cloudflare-delivery-unit] Build artifact appId must match the declared topology app.');
147
+ const declaredIdentity = toDeliveryUnitIdentity(resolved.app.deliveryUnit);
148
+ if (!declaredIdentity) throw new Error('[cloudflare-delivery-unit] Declared app is missing a valid delivery-unit identity.');
149
+ const expected = createTopologyDeliveryUnitStamp(identity, resolved.app);
150
+ for (const field of [
151
+ 'unitId'
152
+ ])if (declaredIdentity[field] !== identity[field]) throw new Error(`[cloudflare-delivery-unit] Build artifact ${field} must match the declared topology delivery-unit identity.`);
153
+ const ui = stampedArtifact.surfaces.ui;
154
+ if (Boolean(expected.surfaces.ui) !== Boolean(ui)) throw new Error('[cloudflare-delivery-unit] Build artifact UI surface must match the declared topology surface profile.');
155
+ if (expected.surfaces.ui?.rendererIdentity && ui) {
156
+ if (!isDeepStrictEqual(expected.surfaces.ui.routerBindings, ui.routerBindings)) throw new Error('[cloudflare-delivery-unit] Finalized build routerBindings must match the declared topology router bindings.');
157
+ for (const field of [
158
+ 'renderer',
159
+ 'appId',
160
+ 'entryName',
161
+ 'protocolVersion',
162
+ 'buildId'
163
+ ])if (expected.surfaces.ui.rendererIdentity[field] !== ui.rendererIdentity[field]) throw new Error(`[cloudflare-delivery-unit] Build artifact UI rendererIdentity.${field} must match the declared topology renderer identity.`);
164
+ assertRendererProfileCompatibility(expected.surfaces.ui.rendererProfile, ui.rendererProfile);
165
+ }
166
+ emitsApi = void 0 !== expected.surfaces.api;
95
167
  }
96
- if (!identity) return;
97
- const resolved = await resolveCompactApp(appDirectory);
98
- if (resolved) identity = stampReleaseIdentity(identity, resolved.workspaceRoot);
99
- return createDeliveryUnitStamp(identity, resolved?.app);
168
+ return {
169
+ ...stampedArtifact.deliveryUnit,
170
+ surfaces: {
171
+ ...emitsApi ? {
172
+ api: stampedArtifact.surfaces.api
173
+ } : {},
174
+ ...stampedArtifact.surfaces.ui ? {
175
+ ui: stampedArtifact.surfaces.ui
176
+ } : {}
177
+ }
178
+ };
100
179
  };
101
- export { resolveTopologyDeliveryUnit, resolveWorkerDeliveryUnitStamp };
180
+ export { resolveTopologyDeliveryUnit, resolveTopologyReleaseRole, resolveWorkerDeliveryUnitStamp };