@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.12 → 3.9.0-ultramodern.14

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 (93) hide show
  1. package/dist/cjs/backend-federation-build/codegen.js +7 -2
  2. package/dist/cjs/backend-federation-build/config.js +86 -36
  3. package/dist/cjs/backend-federation-build/emit.js +17 -13
  4. package/dist/cjs/cloudflare/delivery-unit.js +16 -26
  5. package/dist/cjs/cloudflare/index.js +7 -1
  6. package/dist/cjs/cloudflare/worker-manifest.js +13 -4
  7. package/dist/cjs/cloudflare/wrangler-config.js +35 -7
  8. package/dist/cjs/cloudflare-builder.js +247 -16
  9. package/dist/cjs/cloudflare-output-contract.js +32 -2
  10. package/dist/cjs/cloudflare-output-verifier/identity.js +26 -24
  11. package/dist/cjs/cloudflare-output-verifier/index.js +10 -0
  12. package/dist/cjs/cloudflare-output-verifier/manifest.js +46 -0
  13. package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +7 -1
  14. package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +10 -5
  15. package/dist/cjs/deploy-output/npmAliases.js +28 -3
  16. package/dist/cjs/release-envelope/framework-output.js +62 -49
  17. package/dist/cjs/release-envelope/index.js +10 -4
  18. package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +4 -0
  19. package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +46 -0
  20. package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
  21. package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
  22. package/dist/esm/backend-federation-build/codegen.mjs +7 -2
  23. package/dist/esm/backend-federation-build/config.mjs +78 -31
  24. package/dist/esm/backend-federation-build/emit.mjs +18 -14
  25. package/dist/esm/cloudflare/delivery-unit.mjs +16 -26
  26. package/dist/esm/cloudflare/index.mjs +7 -1
  27. package/dist/esm/cloudflare/worker-manifest.mjs +13 -4
  28. package/dist/esm/cloudflare/wrangler-config.mjs +35 -7
  29. package/dist/esm/cloudflare-builder.mjs +241 -19
  30. package/dist/esm/cloudflare-output-contract.mjs +25 -1
  31. package/dist/esm/cloudflare-output-verifier/identity.mjs +26 -24
  32. package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
  33. package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
  34. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  35. package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  36. package/dist/esm/deploy-output/npmAliases.mjs +28 -3
  37. package/dist/esm/release-envelope/framework-output.mjs +62 -49
  38. package/dist/esm/release-envelope/index.mjs +10 -4
  39. package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +4 -0
  40. package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +46 -0
  41. package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
  42. package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
  43. package/dist/esm-node/backend-federation-build/codegen.mjs +7 -2
  44. package/dist/esm-node/backend-federation-build/config.mjs +78 -31
  45. package/dist/esm-node/backend-federation-build/emit.mjs +18 -14
  46. package/dist/esm-node/cloudflare/delivery-unit.mjs +16 -26
  47. package/dist/esm-node/cloudflare/index.mjs +7 -1
  48. package/dist/esm-node/cloudflare/worker-manifest.mjs +13 -4
  49. package/dist/esm-node/cloudflare/wrangler-config.mjs +35 -7
  50. package/dist/esm-node/cloudflare-builder.mjs +241 -19
  51. package/dist/esm-node/cloudflare-output-contract.mjs +25 -1
  52. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +26 -24
  53. package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
  54. package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
  55. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  56. package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  57. package/dist/esm-node/deploy-output/npmAliases.mjs +28 -3
  58. package/dist/esm-node/release-envelope/framework-output.mjs +62 -49
  59. package/dist/esm-node/release-envelope/index.mjs +10 -4
  60. package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +4 -0
  61. package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +46 -0
  62. package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
  63. package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
  64. package/dist/types/backend-federation-build/codegen.d.ts +30 -0
  65. package/dist/types/backend-federation-build/config.d.ts +32 -13
  66. package/dist/types/cloudflare/delivery-unit.d.ts +1 -1
  67. package/dist/types/cloudflare/wrangler-config.d.ts +6 -0
  68. package/dist/types/cloudflare-builder.d.ts +45 -1
  69. package/dist/types/cloudflare-output-contract.d.ts +3 -1
  70. package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
  71. package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
  72. package/package.json +25 -21
  73. package/src/backend-federation-build/codegen.ts +6 -2
  74. package/src/backend-federation-build/config.ts +207 -71
  75. package/src/backend-federation-build/emit.ts +33 -22
  76. package/src/cloudflare/delivery-unit.ts +39 -33
  77. package/src/cloudflare/index.ts +20 -6
  78. package/src/cloudflare/worker-manifest.ts +19 -3
  79. package/src/cloudflare/wrangler-config.ts +86 -10
  80. package/src/cloudflare-builder.ts +464 -15
  81. package/src/cloudflare-output-contract.ts +35 -2
  82. package/src/cloudflare-output-verifier/identity.ts +4 -0
  83. package/src/cloudflare-output-verifier/index.ts +20 -0
  84. package/src/cloudflare-output-verifier/manifest.ts +77 -0
  85. package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
  86. package/src/cloudflare-output-verifier/worker-bundles.ts +27 -11
  87. package/src/deploy-output/npmAliases.ts +52 -3
  88. package/src/release-envelope/framework-output.ts +163 -90
  89. package/src/release-envelope/index.ts +26 -5
  90. package/src/templates/cloudflare-entry.004-rendering-css.mjs +4 -0
  91. package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +46 -0
  92. package/src/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
  93. package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
@@ -213,8 +213,13 @@ const createBackendManifest = (workspaceRoot, distDirectory, app, entryIntegrity
213
213
  source: {
214
214
  module: sourceModule
215
215
  },
216
- readinessPath: `${app.apiPrefix}/${app.apiStem}/readiness`,
217
- openapiPath: `${app.apiPrefix}/openapi.json`,
216
+ ...'rpc' === app.apiProtocol ? {
217
+ rpcPath: app.rpcPath,
218
+ rpcSerialization: 'json'
219
+ } : {
220
+ readinessPath: `${app.apiPrefix}/${app.apiStem}/readiness`,
221
+ openapiPath: `${app.apiPrefix}/openapi.json`
222
+ },
218
223
  ...app.deliveryUnit ? {
219
224
  deliveryUnit: app.deliveryUnit
220
225
  } : {},
@@ -37,9 +37,10 @@ var __webpack_require__ = {};
37
37
  var __webpack_exports__ = {};
38
38
  __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
- COMPACT_CONFIG_PATH: ()=>COMPACT_CONFIG_PATH,
40
+ DEVELOPMENT_OVERLAY_PATH: ()=>DEVELOPMENT_OVERLAY_PATH,
41
+ REFERENCE_TOPOLOGY_PATH: ()=>REFERENCE_TOPOLOGY_PATH,
41
42
  buildArtifactPathFor: ()=>buildArtifactPathFor,
42
- createAppFromCompactMetadata: ()=>createAppFromCompactMetadata,
43
+ createAppFromTopology: ()=>createAppFromTopology,
43
44
  createStampedDeliveryUnit: ()=>createStampedDeliveryUnit,
44
45
  findBackendFederationApp: ()=>findBackendFederationApp,
45
46
  findWorkspaceRoot: ()=>findWorkspaceRoot,
@@ -54,7 +55,8 @@ const external_node_path_namespaceObject = require("node:path");
54
55
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
55
56
  const release_identity_namespaceObject = require("@modern-js/app-tools-extensions/release-identity");
56
57
  const backend_federation_contracts_namespaceObject = require("@modern-js/backend-federation-contracts");
57
- const COMPACT_CONFIG_PATH = '.modernjs/ultramodern.json';
58
+ const REFERENCE_TOPOLOGY_PATH = 'topology/reference-topology.json';
59
+ const DEVELOPMENT_OVERLAY_PATH = 'topology/local-overlays/development.json';
58
60
  const normalizeRelativePath = (value)=>value.replace(/\\/gu, '/').replace(/^\.\/+/u, '');
59
61
  const isRecord = (value)=>'object' == typeof value && null !== value;
60
62
  const readJsonFile = async (filePath)=>JSON.parse(await promises_default().readFile(filePath, 'utf8'));
@@ -82,15 +84,25 @@ const readBuildIdentity = async (appDirectory)=>{
82
84
  const toPascalCase = (value)=>value.split(/[^a-zA-Z0-9]+/u).filter(Boolean).map((part)=>part.charAt(0).toUpperCase() + part.slice(1)).join('');
83
85
  const stringValue = (value)=>'string' == typeof value && value.length > 0 ? value : void 0;
84
86
  const createBackendName = (app, id)=>{
85
- const configuredName = stringValue(app.backendFederation?.name) ?? stringValue(app.backendFederation?.executionSurfaces?.node?.remoteName) ?? stringValue(app.serverExecution?.node?.remoteName);
87
+ const configuredName = stringValue(app.backendFederation?.name) ?? stringValue(app.backendFederation?.executionSurfaces?.node?.remoteName);
86
88
  if (configuredName) return configuredName;
87
89
  const mfName = stringValue(app.moduleFederation?.name);
88
90
  return mfName ? `${mfName}Backend` : `vertical${toPascalCase(id)}Backend`;
89
91
  };
92
+ const rebaseDefaultLocalUrl = (value, defaultPort, publicOrigin)=>{
93
+ let url;
94
+ try {
95
+ url = new URL(value);
96
+ } catch {
97
+ return value;
98
+ }
99
+ if ('http:' !== url.protocol || 'localhost' !== url.hostname || Number(url.port) !== defaultPort || url.username || url.password) return value;
100
+ return `${publicOrigin}${url.pathname}${url.search}${url.hash}`;
101
+ };
90
102
  const findWorkspaceRoot = (appDirectory)=>{
91
103
  let current = appDirectory;
92
104
  while(true){
93
- if ((0, external_node_fs_namespaceObject.existsSync)(external_node_path_default().join(current, COMPACT_CONFIG_PATH))) return current;
105
+ if ((0, external_node_fs_namespaceObject.existsSync)(external_node_path_default().join(current, REFERENCE_TOPOLOGY_PATH))) return current;
94
106
  const parent = external_node_path_default().dirname(current);
95
107
  if (parent === current) return;
96
108
  current = parent;
@@ -114,59 +126,96 @@ const createStampedDeliveryUnit = (input)=>{
114
126
  if (!validation.ok) return;
115
127
  return (0, backend_federation_contracts_namespaceObject.deliveryUnitContractBlock)(record);
116
128
  };
117
- const createAppFromCompactMetadata = (workspaceRoot, appDirectory, compactApp)=>{
118
- const id = stringValue(compactApp.id);
119
- const appPath = stringValue(compactApp.path);
120
- if (!id || 'vertical' !== compactApp.kind || !isRecord(compactApp.api)) return;
121
- const directory = appPath ? normalizeRelativePath(appPath) : `verticals/${id}`;
129
+ const createAppFromTopology = (workspaceRoot, appDirectory, topologyApp, overlay, packageManifest)=>{
130
+ const id = stringValue(topologyApp.id);
131
+ const appPath = stringValue(topologyApp.path);
132
+ if (!id || !appPath || 'vertical' !== topologyApp.kind || !isRecord(topologyApp.api) || !isRecord(topologyApp.backendFederation)) return;
133
+ const directory = normalizeRelativePath(appPath);
122
134
  if (external_node_path_default().resolve(workspaceRoot, directory) !== external_node_path_default().resolve(appDirectory)) return;
123
- const port = 'number' == typeof compactApp.port ? compactApp.port : void 0;
124
- if (void 0 === port) return;
125
- const apiPrefix = stringValue(compactApp.api.prefix) ?? `/${id}-api`;
126
- const apiStem = stringValue(compactApp.api.stem) ?? id;
127
- const backendName = createBackendName(compactApp, id);
128
- const manifestUrl = stringValue(compactApp.backendFederation?.executionSurfaces?.node?.manifestUrl) ?? stringValue(compactApp.serverExecution?.node?.manifestUrl) ?? `http://localhost:${port}/${backend_federation_contracts_namespaceObject.BACKEND_FEDERATION_MANIFEST_FILE}`;
129
- const containerEntry = stringValue(compactApp.backendFederation?.executionSurfaces?.node?.containerEntry) ?? stringValue(compactApp.serverExecution?.node?.containerEntry) ?? `http://localhost:${port}/${backend_federation_contracts_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`;
130
- const configuredRemoteType = stringValue(compactApp.backendFederation?.executionSurfaces?.node?.remoteType) ?? stringValue(compactApp.serverExecution?.node?.remoteType);
135
+ const defaultPort = overlay.ports?.[id];
136
+ if ('number' != typeof defaultPort || !Number.isInteger(defaultPort) || defaultPort < 1 || defaultPort > 65535) throw new Error(`[backend-federation-build] Invalid development port for ${id}.`);
137
+ const portEnv = stringValue(topologyApp.portEnv) ?? `VERTICAL_${(stringValue(topologyApp.domain) ?? id).replace(/[^a-zA-Z0-9]/gu, '_').toUpperCase()}_PORT`;
138
+ const port = Number(process.env[portEnv] ?? defaultPort);
139
+ if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`[backend-federation-build] Invalid ${portEnv} development port for ${id}.`);
140
+ const publicUrlEnv = stringValue(topologyApp.cloudflare?.publicUrlEnv);
141
+ const configuredPublicUrl = publicUrlEnv ? process.env[publicUrlEnv]?.trim() : void 0;
142
+ let publicOrigin = `http://localhost:${port}`;
143
+ if (configuredPublicUrl) {
144
+ let url;
145
+ try {
146
+ url = new URL(configuredPublicUrl);
147
+ } catch {
148
+ throw new Error(`[backend-federation-build] Invalid ${publicUrlEnv} public URL for ${id}.`);
149
+ }
150
+ if (![
151
+ 'http:',
152
+ 'https:'
153
+ ].includes(url.protocol) || url.username || url.password) throw new Error(`[backend-federation-build] Invalid ${publicUrlEnv} public URL for ${id}.`);
154
+ publicOrigin = url.origin;
155
+ }
156
+ const apiPrefix = stringValue(topologyApp.api.bff?.prefix) ?? `/${id}-api`;
157
+ const apiProtocol = topologyApp.api.protocol ?? 'rest';
158
+ if ('rest' !== apiProtocol && 'rpc' !== apiProtocol) throw new Error(`[backend-federation-build] Unsupported API protocol for ${id}.`);
159
+ const rpcPath = 'rpc' === apiProtocol ? stringValue(topologyApp.api.rpcPath) : void 0;
160
+ if ('rpc' === apiProtocol && (!rpcPath || 'json' !== topologyApp.api.rpcSerialization)) throw new Error(`[backend-federation-build] Missing JSON RPC route metadata for ${id}.`);
161
+ const apiStem = stringValue(topologyApp.api.stem) ?? id;
162
+ const backendName = createBackendName(topologyApp, id);
163
+ const nodeOverlay = overlay.serverExecution?.[id]?.node;
164
+ if (!isRecord(nodeOverlay)) throw new Error(`[backend-federation-build] Missing declared Node server execution for ${id}.`);
165
+ const manifestUrl = rebaseDefaultLocalUrl(stringValue(nodeOverlay?.manifestUrl) ?? `http://localhost:${defaultPort}/${backend_federation_contracts_namespaceObject.BACKEND_FEDERATION_MANIFEST_FILE}`, defaultPort, publicOrigin);
166
+ const containerEntry = rebaseDefaultLocalUrl(stringValue(nodeOverlay?.containerEntry) ?? `http://localhost:${defaultPort}/${backend_federation_contracts_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`, defaultPort, publicOrigin);
167
+ const configuredRemoteType = stringValue(nodeOverlay?.remoteType) ?? stringValue(topologyApp.backendFederation?.executionSurfaces?.node?.remoteType);
131
168
  if (void 0 !== configuredRemoteType && 'commonjs-module' !== configuredRemoteType) throw new Error(`[backend-federation-build] Node backend federation remoteType must be "commonjs-module"; received "${configuredRemoteType}" for ${id}.`);
132
169
  if (!new URL(containerEntry).pathname.endsWith(`/${backend_federation_contracts_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`)) throw new Error(`[backend-federation-build] Node backend federation containerEntry must end with "/${backend_federation_contracts_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE}"; received "${containerEntry}" for ${id}.`);
133
170
  const remoteType = 'commonjs-module';
134
- const packageName = stringValue(compactApp.package);
135
- const uiManifestUrl = stringValue(compactApp.backendFederation?.versionBoundary?.ui?.manifestUrl) ?? stringValue(compactApp.moduleFederation?.manifestUrl);
136
- const compactDeliveryUnit = isRecord(compactApp.deliveryUnit) ? {
137
- unitId: stringValue(compactApp.deliveryUnit.unitId),
138
- buildMarker: stringValue(compactApp.deliveryUnit.buildMarker),
139
- sourceRevision: stringValue(compactApp.deliveryUnit.sourceRevision),
140
- packageName: stringValue(compactApp.deliveryUnit.packageName),
141
- version: stringValue(compactApp.deliveryUnit.version)
171
+ const packageName = stringValue(packageManifest.name);
172
+ const version = stringValue(packageManifest.version);
173
+ if (stringValue(topologyApp.package) !== packageName || !version) throw new Error(`[backend-federation-build] Topology package identity must match package.json for ${id}.`);
174
+ const declaredUiManifestUrl = stringValue(overlay.manifests?.[id]);
175
+ const uiManifestUrl = declaredUiManifestUrl ? rebaseDefaultLocalUrl(declaredUiManifestUrl, defaultPort, publicOrigin) : void 0;
176
+ const topologyDeliveryUnit = isRecord(topologyApp.deliveryUnit) ? {
177
+ unitId: stringValue(topologyApp.deliveryUnit.unitId),
178
+ buildMarker: stringValue(topologyApp.deliveryUnit.buildMarker),
179
+ sourceRevision: stringValue(topologyApp.deliveryUnit.sourceRevision),
180
+ packageName: stringValue(topologyApp.deliveryUnit.packageName),
181
+ version: stringValue(topologyApp.deliveryUnit.version)
142
182
  } : void 0;
143
183
  return {
144
184
  id,
145
185
  directory,
146
186
  packageName,
187
+ version,
147
188
  port,
148
189
  apiPrefix,
190
+ apiProtocol,
191
+ rpcPath,
149
192
  apiStem,
150
193
  backendName,
151
194
  manifestUrl,
152
195
  containerEntry,
153
196
  remoteType,
154
197
  uiManifestUrl,
155
- compactDeliveryUnit
198
+ topologyDeliveryUnit
156
199
  };
157
200
  };
158
201
  const findBackendFederationApp = async (workspaceRoot, appDirectory)=>{
159
- const compactConfigPath = external_node_path_default().join(workspaceRoot, COMPACT_CONFIG_PATH);
160
- const compactConfig = await readJsonFile(compactConfigPath);
161
- const apps = Array.isArray(compactConfig.topology?.apps) ? compactConfig.topology.apps : [];
162
- for (const app of apps){
163
- const resolved = createAppFromCompactMetadata(workspaceRoot, appDirectory, app);
202
+ const topologyPath = external_node_path_default().join(workspaceRoot, REFERENCE_TOPOLOGY_PATH);
203
+ const topology = await readJsonFile(topologyPath);
204
+ if (!isRecord(topology) || !Array.isArray(topology.verticals)) throw new Error(`[backend-federation-build] Invalid declared topology at ${topologyPath}.`);
205
+ const overlayPath = external_node_path_default().join(workspaceRoot, DEVELOPMENT_OVERLAY_PATH);
206
+ const overlay = await readJsonFile(overlayPath);
207
+ if (!isRecord(overlay) || !isRecord(overlay.ports)) throw new Error(`[backend-federation-build] Invalid development overlay at ${overlayPath}.`);
208
+ const packageManifest = await readJsonFile(external_node_path_default().join(appDirectory, 'package.json'));
209
+ for (const app of topology.verticals){
210
+ const resolved = createAppFromTopology(workspaceRoot, appDirectory, app, overlay, packageManifest);
164
211
  if (resolved) return resolved;
165
212
  }
213
+ throw new Error(`[backend-federation-build] ${appDirectory} is missing from the declared reference topology.`);
166
214
  };
167
- exports.COMPACT_CONFIG_PATH = __webpack_exports__.COMPACT_CONFIG_PATH;
215
+ exports.DEVELOPMENT_OVERLAY_PATH = __webpack_exports__.DEVELOPMENT_OVERLAY_PATH;
216
+ exports.REFERENCE_TOPOLOGY_PATH = __webpack_exports__.REFERENCE_TOPOLOGY_PATH;
168
217
  exports.buildArtifactPathFor = __webpack_exports__.buildArtifactPathFor;
169
- exports.createAppFromCompactMetadata = __webpack_exports__.createAppFromCompactMetadata;
218
+ exports.createAppFromTopology = __webpack_exports__.createAppFromTopology;
170
219
  exports.createStampedDeliveryUnit = __webpack_exports__.createStampedDeliveryUnit;
171
220
  exports.findBackendFederationApp = __webpack_exports__.findBackendFederationApp;
172
221
  exports.findWorkspaceRoot = __webpack_exports__.findWorkspaceRoot;
@@ -174,9 +223,10 @@ exports.normalizeRelativePath = __webpack_exports__.normalizeRelativePath;
174
223
  exports.readBuildIdentity = __webpack_exports__.readBuildIdentity;
175
224
  exports.resolveWorkspaceSourceRevision = __webpack_exports__.resolveWorkspaceSourceRevision;
176
225
  for(var __rspack_i in __webpack_exports__)if (-1 === [
177
- "COMPACT_CONFIG_PATH",
226
+ "DEVELOPMENT_OVERLAY_PATH",
227
+ "REFERENCE_TOPOLOGY_PATH",
178
228
  "buildArtifactPathFor",
179
- "createAppFromCompactMetadata",
229
+ "createAppFromTopology",
180
230
  "createStampedDeliveryUnit",
181
231
  "findBackendFederationApp",
182
232
  "findWorkspaceRoot",
@@ -59,35 +59,39 @@ const emitBackendFederationArtifacts = async (appDirectory, distDirectory)=>{
59
59
  const app = await (0, external_config_js_namespaceObject.findBackendFederationApp)(workspaceRoot, appDirectory);
60
60
  if (!app) return;
61
61
  const buildIdentity = await (0, external_config_js_namespaceObject.readBuildIdentity)(appDirectory);
62
- const compactDeliveryUnit = app.compactDeliveryUnit;
63
- const hasCompactDeliveryUnit = void 0 !== compactDeliveryUnit && (void 0 !== compactDeliveryUnit.unitId || void 0 !== compactDeliveryUnit.buildMarker || void 0 !== compactDeliveryUnit.packageName || void 0 !== compactDeliveryUnit.version);
62
+ const topologyDeliveryUnit = app.topologyDeliveryUnit;
63
+ const hasTopologyDeliveryUnit = void 0 !== topologyDeliveryUnit && (void 0 !== topologyDeliveryUnit.unitId || void 0 !== topologyDeliveryUnit.buildMarker || void 0 !== topologyDeliveryUnit.packageName || void 0 !== topologyDeliveryUnit.version);
64
64
  const hasBuildIdentity = void 0 !== buildIdentity.unitId || void 0 !== buildIdentity.buildVersion || void 0 !== buildIdentity.packageName || void 0 !== buildIdentity.version;
65
65
  if (hasBuildIdentity) {
66
- const compactConfigPath = external_node_path_default().join(workspaceRoot, external_config_js_namespaceObject.COMPACT_CONFIG_PATH);
66
+ const topologyPath = external_node_path_default().join(workspaceRoot, external_config_js_namespaceObject.REFERENCE_TOPOLOGY_PATH);
67
67
  const buildIdentityPath = (0, external_config_js_namespaceObject.buildArtifactPathFor)(appDirectory);
68
68
  const mismatches = [];
69
69
  const compare = (label, a, b, leftLabel = 'deliveryUnit')=>{
70
70
  if (void 0 !== a && void 0 !== b && a !== b) mismatches.push(`${label}: ${leftLabel}=${a} vs ultramodern-build=${b}`);
71
71
  };
72
72
  compare('appId', app.id, buildIdentity.appId, 'topology');
73
- if (hasCompactDeliveryUnit) {
74
- compare('unitId', compactDeliveryUnit?.unitId, buildIdentity.unitId);
75
- compare('buildMarker/build', compactDeliveryUnit?.buildMarker, buildIdentity.buildVersion);
76
- compare('packageName', compactDeliveryUnit?.packageName, buildIdentity.packageName);
77
- compare('version', compactDeliveryUnit?.version, buildIdentity.version);
73
+ if (hasTopologyDeliveryUnit) {
74
+ compare('unitId', topologyDeliveryUnit?.unitId, buildIdentity.unitId);
75
+ compare('buildMarker/build', topologyDeliveryUnit?.buildMarker, buildIdentity.buildVersion);
76
+ compare('packageName', topologyDeliveryUnit?.packageName, buildIdentity.packageName);
77
+ compare('version', topologyDeliveryUnit?.version, buildIdentity.version);
78
78
  }
79
- if (mismatches.length > 0) throw new Error(`[backend-federation-build] Delivery-unit identity drift between ${compactConfigPath} (topology) and ${buildIdentityPath}: ${mismatches.join('; ')}`);
79
+ compare('packageName', app.packageName, buildIdentity.packageName, 'package.json');
80
+ compare('packageName', topologyDeliveryUnit?.packageName, app.packageName, 'topology');
81
+ compare('version', app.version, buildIdentity.version, 'package.json');
82
+ compare('version', topologyDeliveryUnit?.version, app.version, 'topology');
83
+ if (mismatches.length > 0) throw new Error(`[backend-federation-build] Delivery-unit identity drift between ${topologyPath}, package.json and ${buildIdentityPath}: ${mismatches.join('; ')}`);
80
84
  }
81
- const unitId = compactDeliveryUnit?.unitId ?? buildIdentity.unitId;
82
- const generationBuildMarker = compactDeliveryUnit?.buildMarker ?? buildIdentity.buildVersion;
85
+ const unitId = topologyDeliveryUnit?.unitId ?? buildIdentity.unitId;
86
+ const generationBuildMarker = topologyDeliveryUnit?.buildMarker ?? buildIdentity.buildVersion;
83
87
  const sourceRevision = await (0, external_config_js_namespaceObject.resolveWorkspaceSourceRevision)(workspaceRoot);
84
88
  const buildVersion = generationBuildMarker && unitId ? (0, release_identity_namespaceObject.resolveUltramodernReleaseIdentity)({
85
89
  generationBuildMarker,
86
90
  unitId,
87
91
  workspaceRoot
88
92
  }).buildMarker : void 0;
89
- const packageName = compactDeliveryUnit?.packageName ?? buildIdentity.packageName ?? app.packageName;
90
- const version = compactDeliveryUnit?.version ?? buildIdentity.version;
93
+ const packageName = app.packageName;
94
+ const version = app.version;
91
95
  const deliveryUnit = (0, external_config_js_namespaceObject.createStampedDeliveryUnit)({
92
96
  appId: app.id,
93
97
  unitId,
@@ -46,11 +46,11 @@ const backend_federation_contracts_namespaceObject = require("@modern-js/backend
46
46
  const utils_namespaceObject = require("@modern-js/utils");
47
47
  const external_release_identity_js_namespaceObject = require("../release-identity.js");
48
48
  const external_utils_js_namespaceObject = require("./utils.js");
49
- const COMPACT_CONFIG_PATH = '.modernjs/ultramodern.json';
49
+ const TOPOLOGY_PATH = 'topology/reference-topology.json';
50
50
  const findWorkspaceRoot = async (appDirectory)=>{
51
51
  let current = external_node_path_default().resolve(appDirectory);
52
52
  for(;;){
53
- if (await utils_namespaceObject.fs.pathExists(external_node_path_default().join(current, COMPACT_CONFIG_PATH))) return current;
53
+ if (await utils_namespaceObject.fs.pathExists(external_node_path_default().join(current, TOPOLOGY_PATH))) return current;
54
54
  const parent = external_node_path_default().dirname(current);
55
55
  if (parent === current) return;
56
56
  current = parent;
@@ -67,36 +67,25 @@ const stampReleaseIdentity = (identity, workspaceRoot)=>{
67
67
  })
68
68
  };
69
69
  };
70
- const resolveCompactApp = async (appDirectory)=>{
70
+ const resolveTopologyApp = async (appDirectory)=>{
71
71
  const workspaceRoot = await findWorkspaceRoot(appDirectory);
72
72
  if (!workspaceRoot) return;
73
- let compactConfig;
74
- try {
75
- compactConfig = await utils_namespaceObject.fs.readJSON(external_node_path_default().join(workspaceRoot, COMPACT_CONFIG_PATH));
76
- } catch {
77
- return {
78
- workspaceRoot
79
- };
80
- }
81
- if (!(0, external_utils_js_namespaceObject.isRecord)(compactConfig)) return {
82
- workspaceRoot
83
- };
84
- const topology = (0, external_utils_js_namespaceObject.isRecord)(compactConfig.topology) ? compactConfig.topology : void 0;
85
- const apps = Array.isArray(topology?.apps) ? topology.apps : void 0;
86
- if (!apps) return {
87
- app: compactConfig,
88
- workspaceRoot
89
- };
73
+ const topology = await utils_namespaceObject.fs.readJSON(external_node_path_default().join(workspaceRoot, TOPOLOGY_PATH));
74
+ if (!(0, external_utils_js_namespaceObject.isRecord)(topology) || !(0, external_utils_js_namespaceObject.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.');
75
+ const apps = [
76
+ topology.shell,
77
+ ...topology.verticals,
78
+ ...Array.isArray(topology.shells) ? topology.shells : []
79
+ ];
90
80
  const resolvedAppDirectory = external_node_path_default().resolve(appDirectory);
91
81
  const app = apps.find((candidate)=>{
92
82
  if (!(0, external_utils_js_namespaceObject.isRecord)(candidate)) return false;
93
83
  const appPath = (0, backend_federation_contracts_namespaceObject.nonEmptyString)(candidate.path);
94
84
  return void 0 !== appPath && external_node_path_default().resolve(workspaceRoot, appPath.replace(/^\.\/+/u, '')) === resolvedAppDirectory;
95
85
  });
86
+ if (!(0, external_utils_js_namespaceObject.isRecord)(app)) throw new Error(`[cloudflare-delivery-unit] ${appDirectory} is missing from the declared reference topology.`);
96
87
  return {
97
- ...(0, external_utils_js_namespaceObject.isRecord)(app) ? {
98
- app
99
- } : {},
88
+ app,
100
89
  workspaceRoot
101
90
  };
102
91
  };
@@ -123,10 +112,11 @@ const createDeliveryUnitStamp = (identity, app)=>{
123
112
  };
124
113
  };
125
114
  const resolveTopologyDeliveryUnit = async (appDirectory)=>{
126
- const resolved = await resolveCompactApp(appDirectory);
115
+ const resolved = await resolveTopologyApp(appDirectory);
127
116
  if (!resolved?.app) return;
128
117
  const identity = (0, backend_federation_contracts_namespaceObject.toDeliveryUnitIdentity)(resolved.app.deliveryUnit);
129
- return identity ? createDeliveryUnitStamp(stampReleaseIdentity(identity, resolved.workspaceRoot), resolved.app) : void 0;
118
+ if (!identity) throw new Error('[cloudflare-delivery-unit] Declared app is missing a valid delivery-unit identity.');
119
+ return createDeliveryUnitStamp(stampReleaseIdentity(identity, resolved.workspaceRoot), resolved.app);
130
120
  };
131
121
  const resolveWorkerDeliveryUnitStamp = async (appDirectory)=>{
132
122
  const buildArtifactPath = external_node_path_default().join(appDirectory, backend_federation_contracts_namespaceObject.ULTRAMODERN_BUILD_ARTIFACT_PATH);
@@ -137,7 +127,7 @@ const resolveWorkerDeliveryUnitStamp = async (appDirectory)=>{
137
127
  identity = (0, backend_federation_contracts_namespaceObject.toDeliveryUnitIdentity)(artifact.deliveryUnit);
138
128
  }
139
129
  if (!identity) return;
140
- const resolved = await resolveCompactApp(appDirectory);
130
+ const resolved = await resolveTopologyApp(appDirectory);
141
131
  if (resolved) identity = stampReleaseIdentity(identity, resolved.workspaceRoot);
142
132
  return createDeliveryUnitStamp(identity, resolved?.app);
143
133
  };
@@ -41,6 +41,7 @@ __webpack_require__.d(__webpack_exports__, {
41
41
  });
42
42
  const external_node_path_namespaceObject = require("node:path");
43
43
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
44
+ const backend_federation_contracts_namespaceObject = require("@modern-js/backend-federation-contracts");
44
45
  const utils_namespaceObject = require("@modern-js/utils");
45
46
  const external_cloudflare_output_plan_js_namespaceObject = require("../cloudflare-output-plan.js");
46
47
  const index_js_namespaceObject = require("../cloudflare-output-verifier/index.js");
@@ -78,7 +79,7 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
78
79
  await utils_namespaceObject.fs.remove(outputDirectory);
79
80
  },
80
81
  async writeOutput () {
81
- if (releaseEnvelopeEnabled && await utils_namespaceObject.fs.pathExists(external_node_path_default().join(distDirectory, framework_output_js_namespaceObject.MICROVERTICAL_RELEASE_ENVELOPE_PATH))) await (0, framework_output_js_namespaceObject.emitFrameworkMicroVerticalReleaseEnvelope)({
82
+ if (releaseEnvelopeEnabled) await (0, framework_output_js_namespaceObject.emitFrameworkMicroVerticalReleaseEnvelope)({
82
83
  apiOnly,
83
84
  distDirectory,
84
85
  target: 'cloudflare'
@@ -87,6 +88,11 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
87
88
  await utils_namespaceObject.fs.copy(distDirectory, publicDirectory, {
88
89
  filter: (src)=>{
89
90
  const relativePath = external_node_path_default().relative(distDirectory, src).replace(/\\/gu, '/');
91
+ if (apiOnly && '' !== relativePath && ![
92
+ backend_federation_contracts_namespaceObject.BACKEND_FEDERATION_MANIFEST_FILE,
93
+ backend_federation_contracts_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE,
94
+ backend_federation_contracts_namespaceObject.ULTRAMODERN_BUILD_ARTIFACT_FILE
95
+ ].includes(relativePath)) return false;
90
96
  return 'release' !== relativePath && !relativePath.startsWith('release/') && 'public' !== relativePath && !relativePath.startsWith('public/') && (0, external_worker_manifest_js_namespaceObject.shouldCopyToPublicAssets)(src, distDirectory, publicAssetExcludes);
91
97
  }
92
98
  });
@@ -46,6 +46,7 @@ __webpack_require__.d(__webpack_exports__, {
46
46
  const external_node_path_namespaceObject = require("node:path");
47
47
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
48
48
  const utils_namespaceObject = require("@modern-js/utils");
49
+ const external_cloudflare_output_contract_js_namespaceObject = require("../cloudflare-output-contract.js");
49
50
  const external_artifacts_js_namespaceObject = require("./artifacts.js");
50
51
  const external_constants_js_namespaceObject = require("./constants.js");
51
52
  const external_i18n_worker_js_namespaceObject = require("./i18n-worker.js");
@@ -111,13 +112,18 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, d
111
112
  const routeSpec = await (0, external_artifacts_js_namespaceObject.readRouteSpec)(outputDirectory);
112
113
  const routes = await Promise.all(routeSpec.routes.map(async (route)=>{
113
114
  const worker = 'string' == typeof route.worker ? route.worker : void 0;
115
+ const routeDataWorker = route.isSSR && 'string' == typeof route.entryName ? `${external_constants_js_namespaceObject.WORKER_BUNDLE_DIRECTORY}/${(0, external_cloudflare_output_contract_js_namespaceObject.getCloudflareWorkerRouteDataEntryName)(route.entryName)}.js` : void 0;
116
+ const hasRouteDataWorker = routeDataWorker ? await utils_namespaceObject.fs.pathExists(external_node_path_default().join(outputDirectory, routeDataWorker)) : false;
114
117
  return {
115
118
  urlPath: route.urlPath,
116
119
  entryName: route.entryName,
117
120
  entryPath: route.entryPath,
118
121
  isSSR: Boolean(route.isSSR),
119
122
  worker,
120
- workerExists: worker ? await utils_namespaceObject.fs.pathExists(external_node_path_default().join(outputDirectory, worker)) : false
123
+ workerExists: worker ? await utils_namespaceObject.fs.pathExists(external_node_path_default().join(outputDirectory, worker)) : false,
124
+ ...hasRouteDataWorker ? {
125
+ routeDataWorker
126
+ } : {}
121
127
  };
122
128
  }));
123
129
  const bffPrefix = modernConfig.bff?.prefix;
@@ -177,9 +183,12 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, d
177
183
  };
178
184
  const createWorkerModuleLoaders = (manifest)=>{
179
185
  const imports = new Map();
180
- for (const route of manifest.routeSpec.routes)if (route.worker && route.workerExists) {
181
- const importPath = `../${String(route.worker).replace(/^\/+/u, '')}`;
182
- imports.set(route.worker, `() => import(${JSON.stringify(importPath)})`);
186
+ for (const route of manifest.routeSpec.routes)for (const worker of [
187
+ route.workerExists ? route.worker : void 0,
188
+ route.routeDataWorker
189
+ ])if (worker) {
190
+ const importPath = `../${String(worker).replace(/^\/+/u, '')}`;
191
+ imports.set(worker, `() => import(${JSON.stringify(importPath)})`);
183
192
  }
184
193
  if (manifest.bff?.worker) {
185
194
  const importPath = `../${String(manifest.bff.worker).replace(/^\/+/u, '')}`;
@@ -45,12 +45,16 @@ const external_node_path_namespaceObject = require("node:path");
45
45
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
46
46
  const external_constants_js_namespaceObject = require("./constants.js");
47
47
  const external_utils_js_namespaceObject = require("./utils.js");
48
- const getCompatibilityDate = (modernConfig)=>{
49
- const configuredDate = modernConfig.deploy?.worker?.compatibilityDate?.trim();
50
- const compatibilityDate = configuredDate || external_constants_js_namespaceObject.DEFAULT_COMPATIBILITY_DATE;
51
- if (!external_constants_js_namespaceObject.COMPATIBILITY_DATE_PATTERN.test(compatibilityDate)) throw new Error(`deploy.worker.compatibilityDate must use YYYY-MM-DD, received ${JSON.stringify(compatibilityDate)}.`);
48
+ const assertSupportedCompatibilityDate = (compatibilityDate, label)=>{
49
+ if ('string' != typeof compatibilityDate) throw new Error(`${label} must be a YYYY-MM-DD string.`);
50
+ if (!external_constants_js_namespaceObject.COMPATIBILITY_DATE_PATTERN.test(compatibilityDate)) throw new Error(`${label} must use YYYY-MM-DD, received ${JSON.stringify(compatibilityDate)}.`);
51
+ if (compatibilityDate < external_constants_js_namespaceObject.DEFAULT_COMPATIBILITY_DATE) throw new Error(`${label} must be ${external_constants_js_namespaceObject.DEFAULT_COMPATIBILITY_DATE} or later, the date the Cloudflare worker Node.js built-in contract is verified against; received ${JSON.stringify(compatibilityDate)}.`);
52
52
  return compatibilityDate;
53
53
  };
54
+ const getCompatibilityDate = (modernConfig, wranglerCompatibilityDate)=>{
55
+ if (void 0 !== wranglerCompatibilityDate) return assertSupportedCompatibilityDate(wranglerCompatibilityDate, 'deploy.worker.wrangler.compatibility_date');
56
+ return assertSupportedCompatibilityDate(modernConfig.deploy?.worker?.compatibilityDate?.trim() || external_constants_js_namespaceObject.DEFAULT_COMPATIBILITY_DATE, 'deploy.worker.compatibilityDate');
57
+ };
54
58
  const getWorkerName = (appDirectory)=>{
55
59
  const basename = external_node_path_default().basename(appDirectory);
56
60
  return basename.replace(/[^a-zA-Z0-9-_]/g, '-') || 'modern-cloudflare-worker';
@@ -65,11 +69,11 @@ const getConfiguredWrangler = (modernConfig)=>{
65
69
  if (!(0, external_utils_js_namespaceObject.isJsonRecord)(wrangler)) throw new Error('deploy.worker.wrangler must be a JSON object.');
66
70
  return wrangler;
67
71
  };
68
- const createWranglerCompatibilityFlags = (configuredFlags)=>{
72
+ const createWranglerCompatibilityFlags = (configuredFlags, label = 'deploy.worker.wrangler.compatibility_flags')=>{
69
73
  if (void 0 === configuredFlags) return [
70
74
  ...external_constants_js_namespaceObject.REQUIRED_COMPATIBILITY_FLAGS
71
75
  ];
72
- if (!Array.isArray(configuredFlags) || configuredFlags.some((flag)=>'string' != typeof flag)) throw new Error('deploy.worker.wrangler.compatibility_flags must be an array of strings.');
76
+ if (!Array.isArray(configuredFlags) || configuredFlags.some((flag)=>'string' != typeof flag)) throw new Error(`${label} must be an array of strings.`);
73
77
  return [
74
78
  ...new Set([
75
79
  ...configuredFlags,
@@ -77,6 +81,26 @@ const createWranglerCompatibilityFlags = (configuredFlags)=>{
77
81
  ])
78
82
  ];
79
83
  };
84
+ const createWranglerEnvironments = (wranglerEnv)=>{
85
+ if (void 0 === wranglerEnv) return;
86
+ if (!(0, external_utils_js_namespaceObject.isJsonRecord)(wranglerEnv)) throw new Error('deploy.worker.wrangler.env must be an object.');
87
+ return Object.fromEntries(Object.entries(wranglerEnv).map(([name, environment])=>{
88
+ const label = `deploy.worker.wrangler.env.${name}`;
89
+ if (!(0, external_utils_js_namespaceObject.isJsonRecord)(environment)) throw new Error(`${label} must be an object.`);
90
+ return [
91
+ name,
92
+ {
93
+ ...environment,
94
+ ...void 0 === environment.compatibility_date ? {} : {
95
+ compatibility_date: assertSupportedCompatibilityDate(environment.compatibility_date, `${label}.compatibility_date`)
96
+ },
97
+ ...void 0 === environment.compatibility_flags ? {} : {
98
+ compatibility_flags: createWranglerCompatibilityFlags(environment.compatibility_flags, `${label}.compatibility_flags`)
99
+ }
100
+ }
101
+ ];
102
+ }));
103
+ };
80
104
  const createWranglerAssetsConfig = (configuredAssets)=>{
81
105
  if (void 0 !== configuredAssets && !(0, external_utils_js_namespaceObject.isJsonRecord)(configuredAssets)) throw new Error('deploy.worker.wrangler.assets must be an object.');
82
106
  return {
@@ -173,17 +197,21 @@ const createWorkerManifestServiceBindings = (serviceBindings)=>{
173
197
  };
174
198
  const createWranglerConfig = (appDirectory, modernConfig)=>{
175
199
  const wrangler = getConfiguredWrangler(modernConfig);
200
+ const environments = createWranglerEnvironments(wrangler.env);
176
201
  const d1Databases = createWranglerD1Databases(modernConfig, wrangler.d1_databases);
177
202
  const serviceBindings = createWorkerServiceBindings(modernConfig, wrangler.services);
178
203
  const wranglerServices = createWranglerServices(serviceBindings);
179
204
  return {
180
205
  $schema: 'node_modules/wrangler/config-schema.json',
181
206
  name: getConfiguredWorkerName(appDirectory, modernConfig),
182
- compatibility_date: getCompatibilityDate(modernConfig),
183
207
  ...wrangler,
208
+ compatibility_date: getCompatibilityDate(modernConfig, wrangler.compatibility_date),
184
209
  main: external_constants_js_namespaceObject.WORKER_ENTRY,
185
210
  compatibility_flags: createWranglerCompatibilityFlags(wrangler.compatibility_flags),
186
211
  assets: createWranglerAssetsConfig(wrangler.assets),
212
+ ...void 0 === environments ? {} : {
213
+ env: environments
214
+ },
187
215
  ...void 0 === d1Databases ? {} : {
188
216
  d1_databases: d1Databases
189
217
  },