@bleedingdev/modern-js-app-tools 3.5.0-ultramodern.101 → 3.5.0-ultramodern.103

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 (31) hide show
  1. package/dist/cjs/builder/generator/getBuilderEnvironments.js +73 -11
  2. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/index.js +1 -1
  3. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/worker-bundles.js +94 -18
  4. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.001-bootstrap-security.mjs +1 -0
  5. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
  6. package/dist/cjs/ultramodern-release-envelope/framework-output.js +109 -57
  7. package/dist/esm/builder/generator/getBuilderEnvironments.mjs +63 -10
  8. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +1 -1
  9. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/worker-bundles.mjs +93 -18
  10. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.001-bootstrap-security.mjs +1 -0
  11. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
  12. package/dist/esm/ultramodern-release-envelope/framework-output.mjs +107 -58
  13. package/dist/esm-node/builder/generator/getBuilderEnvironments.mjs +63 -10
  14. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +1 -1
  15. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/worker-bundles.mjs +93 -18
  16. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.001-bootstrap-security.mjs +1 -0
  17. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
  18. package/dist/esm-node/ultramodern-release-envelope/framework-output.mjs +107 -58
  19. package/dist/types/builder/generator/getBuilderEnvironments.d.ts +29 -0
  20. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/worker-bundles.d.ts +1 -1
  21. package/dist/types/plugins/deploy/platforms/templates/cloudflare-entry.001-bootstrap-security.d.mts +1 -1
  22. package/dist/types/plugins/deploy/platforms/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +13 -0
  23. package/dist/types/ultramodern-release-envelope/framework-output.d.ts +1 -0
  24. package/docs/configure/app/bff/effect.md +13 -15
  25. package/docs/configure/app/server/rsc.md +8 -12
  26. package/docs/guides/basic-features/render/rsc.md +5 -14
  27. package/docs/guides/get-started/quick-start.md +1 -1
  28. package/docs/guides/get-started/ultramodern.md +19 -7
  29. package/docs/guides/get-started/upgrade.md +5 -10
  30. package/docs/llms.txt +1 -1
  31. package/package.json +12 -12
@@ -37,7 +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
- getBuilderEnvironments: ()=>getBuilderEnvironments
40
+ applyCloudflareWorkerMfRuntimeBoundary: ()=>applyCloudflareWorkerMfRuntimeBoundary,
41
+ applyCloudflareWorkerRspackConfig: ()=>applyCloudflareWorkerRspackConfig,
42
+ getBuilderEnvironments: ()=>getBuilderEnvironments,
43
+ getCloudflareWorkerRspackConfig: ()=>getCloudflareWorkerRspackConfig
41
44
  });
42
45
  const external_node_fs_namespaceObject = require("node:fs");
43
46
  var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
@@ -48,6 +51,8 @@ const utils_namespaceObject = require("@modern-js/utils");
48
51
  const cloudflare_output_contract_js_namespaceObject = require("../../plugins/deploy/platforms/cloudflare-output-contract.js");
49
52
  const BFF_EFFECT_WORKER_ENTRY_NAME = '__modern_bff_effect';
50
53
  const BFF_EFFECT_WORKER_RUNTIME_QUERY = 'modern-bff-runtime';
54
+ const MF_SSR_DATA_FETCH_RUNTIME_PLUGIN = '@module-federation/modern-js-v3/ssr-inject-data-fetch-function-plugin';
55
+ const MF_SSR_DEV_RUNTIME_PLUGIN = '@module-federation/modern-js-v3/ssr-dev-plugin';
51
56
  const JS_OR_TS_EXTS = [
52
57
  '.js',
53
58
  '.jsx',
@@ -128,6 +133,64 @@ function getCloudflareWorkerNodeExternals() {
128
133
  ];
129
134
  }));
130
135
  }
136
+ const reserveWorkerChunkName = (baseName, reservedOutputNames)=>{
137
+ let name = baseName;
138
+ while(reservedOutputNames.has(normalizeWorkerOutputName(name)))name = `${name}_`;
139
+ return name;
140
+ };
141
+ const normalizeWorkerOutputName = (name)=>external_node_path_default().posix.normalize(name.replace(/\\/gu, '/')).toLowerCase();
142
+ function getCloudflareWorkerRspackConfig(workerEntryNames) {
143
+ const entryNames = [
144
+ ...workerEntryNames
145
+ ];
146
+ for (const entryName of entryNames){
147
+ const slashName = entryName.replace(/\\/gu, '/');
148
+ const normalizedName = external_node_path_default().posix.normalize(slashName);
149
+ if (entryName !== slashName || normalizedName !== slashName || '.' === normalizedName || normalizedName.startsWith('../') || external_node_path_default().posix.isAbsolute(normalizedName) || external_node_path_default().win32.isAbsolute(slashName) || /^[A-Za-z]:/u.test(slashName)) throw new Error(`Cloudflare worker entry name "${entryName}" must be a canonical relative output path without dot segments or backslashes.`);
150
+ }
151
+ const reservedNames = new Set(entryNames.map(normalizeWorkerOutputName));
152
+ const runtimeName = reserveWorkerChunkName('__modern_worker_runtime', reservedNames);
153
+ reservedNames.add(normalizeWorkerOutputName(runtimeName));
154
+ const sharedName = reserveWorkerChunkName('__modern_worker_shared', reservedNames);
155
+ return {
156
+ experiments: {
157
+ outputModule: true
158
+ },
159
+ externals: getCloudflareWorkerNodeExternals(),
160
+ externalsType: 'module-import',
161
+ module: {
162
+ parser: {
163
+ javascript: {
164
+ dynamicImportMode: 'eager'
165
+ }
166
+ }
167
+ },
168
+ optimization: {
169
+ runtimeChunk: {
170
+ name: runtimeName
171
+ },
172
+ splitChunks: {
173
+ chunks: 'all',
174
+ minSize: 0,
175
+ name: sharedName
176
+ }
177
+ }
178
+ };
179
+ }
180
+ function applyCloudflareWorkerRspackConfig(chain, workerEntryNames) {
181
+ const config = getCloudflareWorkerRspackConfig(workerEntryNames);
182
+ chain.experiments(config.experiments);
183
+ chain.externals(config.externals);
184
+ chain.externalsType(config.externalsType);
185
+ chain.module.parser.merge(config.module.parser);
186
+ chain.optimization.runtimeChunk(config.optimization.runtimeChunk);
187
+ chain.optimization.splitChunks(config.optimization.splitChunks);
188
+ }
189
+ function applyCloudflareWorkerMfRuntimeBoundary(chain) {
190
+ chain.plugins.delete('plugin-module-federation');
191
+ chain.resolve.alias.set(`${MF_SSR_DATA_FETCH_RUNTIME_PLUGIN}$`, getCloudflareWorkerCompatFile('cloudflare-worker-mf-ssr-runtime-plugin.mjs'));
192
+ chain.resolve.alias.set(`${MF_SSR_DEV_RUNTIME_PLUGIN}$`, getCloudflareWorkerCompatFile('cloudflare-worker-mf-ssr-runtime-plugin.mjs'));
193
+ }
131
194
  function getEffectBffEntry(normalizedConfig, appContext) {
132
195
  if (!normalizedConfig.bff || 'hono' === normalizedConfig.bff.runtimeFramework) return;
133
196
  const configuredEntry = normalizedConfig.bff.effect?.entry;
@@ -286,17 +349,10 @@ function getBuilderEnvironments(normalizedConfig, appContext, tempBuilderConfig)
286
349
  htmlPlugin: false,
287
350
  ...useCloudflareModuleWorker ? {
288
351
  bundlerChain: (chain)=>{
289
- chain.merge({
290
- experiments: {
291
- outputModule: true
292
- },
293
- externals: getCloudflareWorkerNodeExternals(),
294
- externalsType: 'module-import'
295
- });
352
+ applyCloudflareWorkerRspackConfig(chain, Object.keys(workerEntries));
296
353
  chain.output.module(true).library({
297
354
  type: 'module'
298
355
  }).publicPath('/').chunkFormat('module').chunkLoading('import').workerChunkLoading('import');
299
- chain.optimization.usedExports(false).providedExports(true).innerGraph(false).sideEffects(false);
300
356
  for (const condition of [
301
357
  'workerd',
302
358
  'worker',
@@ -306,7 +362,7 @@ function getBuilderEnvironments(normalizedConfig, appContext, tempBuilderConfig)
306
362
  'require',
307
363
  'module'
308
364
  ])chain.resolve.conditionNames.add(condition);
309
- chain.plugins.delete('plugin-module-federation');
365
+ applyCloudflareWorkerMfRuntimeBoundary(chain);
310
366
  if (tanstackRouterSsrServerFile) {
311
367
  chain.resolve.alias.set('@tanstack/router-core/ssr/server$', tanstackRouterSsrServerFile);
312
368
  chain.resolve.alias.set('@tanstack/router-core/ssr/server', tanstackRouterSsrServerFile);
@@ -347,9 +403,15 @@ function getBuilderEnvironments(normalizedConfig, appContext, tempBuilderConfig)
347
403
  builderConfig: tempBuilderConfig
348
404
  };
349
405
  }
406
+ exports.applyCloudflareWorkerMfRuntimeBoundary = __webpack_exports__.applyCloudflareWorkerMfRuntimeBoundary;
407
+ exports.applyCloudflareWorkerRspackConfig = __webpack_exports__.applyCloudflareWorkerRspackConfig;
350
408
  exports.getBuilderEnvironments = __webpack_exports__.getBuilderEnvironments;
409
+ exports.getCloudflareWorkerRspackConfig = __webpack_exports__.getCloudflareWorkerRspackConfig;
351
410
  for(var __rspack_i in __webpack_exports__)if (-1 === [
352
- "getBuilderEnvironments"
411
+ "applyCloudflareWorkerMfRuntimeBoundary",
412
+ "applyCloudflareWorkerRspackConfig",
413
+ "getBuilderEnvironments",
414
+ "getCloudflareWorkerRspackConfig"
353
415
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
354
416
  Object.defineProperty(exports, '__esModule', {
355
417
  value: true
@@ -162,7 +162,7 @@ const verifyCloudflareOutput = async (options)=>{
162
162
  path: publicPath
163
163
  });
164
164
  }
165
- if (false !== options.importWorker && await pathExists(outputPlan.paths.workerEntry)) await (0, external_worker_bundles_js_namespaceObject.verifyWorkerImport)(issues, outputPlan.paths.workerEntry);
165
+ if (false !== options.importWorker && await pathExists(outputPlan.paths.workerEntry)) await (0, external_worker_bundles_js_namespaceObject.verifyWorkerImport)(issues, outputPlan.paths.workerEntry, manifest);
166
166
  return {
167
167
  ok: 0 === issues.length,
168
168
  issues
@@ -49,7 +49,10 @@ var promises_default = /*#__PURE__*/ __webpack_require__.n(promises_namespaceObj
49
49
  const external_node_path_namespaceObject = require("node:path");
50
50
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
51
51
  const external_node_url_namespaceObject = require("node:url");
52
+ const external_node_util_namespaceObject = require("node:util");
52
53
  const parser_namespaceObject = require("@babel/parser");
54
+ const traverse_namespaceObject = require("@babel/traverse");
55
+ var traverse_default = /*#__PURE__*/ __webpack_require__.n(traverse_namespaceObject);
53
56
  const external_cloudflare_output_contract_js_namespaceObject = require("../cloudflare-output-contract.js");
54
57
  const external_issues_js_namespaceObject = require("./issues.js");
55
58
  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) : [];
@@ -93,6 +96,7 @@ const getNodeName = (node)=>{
93
96
  if (!isAstNode(node)) return;
94
97
  return 'Identifier' === node.type && 'string' == typeof node.name ? node.name : 'StringLiteral' === node.type && 'string' == typeof node.value ? node.value : void 0;
95
98
  };
99
+ const getStringLiteralValue = (node)=>isAstNode(node) && 'StringLiteral' === node.type && 'string' == typeof node.value ? node.value : void 0;
96
100
  const collectBindingNames = (node, names)=>{
97
101
  if (!isAstNode(node)) return;
98
102
  const name = getNodeName(node);
@@ -102,7 +106,11 @@ const collectBindingNames = (node, names)=>{
102
106
  } else if ('ArrayPattern' === node.type && Array.isArray(node.elements)) for (const element of node.elements)collectBindingNames(element, names);
103
107
  else if ('AssignmentPattern' === node.type || 'RestElement' === node.type) collectBindingNames('AssignmentPattern' === node.type ? node.left : node.argument, names);
104
108
  };
105
- const getMemberPropertyName = (node)=>'MemberExpression' === node.type ? getNodeName(node.property) : void 0;
109
+ const getMemberPropertyName = (node)=>{
110
+ if ('MemberExpression' !== node.type) return;
111
+ return true === node.computed ? getStringLiteralValue(node.property) : getNodeName(node.property);
112
+ };
113
+ const isAmbientModuleRequire = (node, modulePath)=>isAstNode(node) && 'MemberExpression' === node.type && isAstNode(node.object) && 'Identifier' === node.object.type && 'module' === node.object.name && void 0 === modulePath.scope.getBinding('module') && 'require' === getMemberPropertyName(node);
106
114
  const isModuleExports = (node)=>isAstNode(node) && 'MemberExpression' === node.type && isAstNode(node.object) && 'Identifier' === node.object.type && 'module' === node.object.name && 'exports' === getMemberPropertyName(node);
107
115
  const collectCommonJsExports = (statement, exports1)=>{
108
116
  if ('ExpressionStatement' !== statement.type || !isAstNode(statement.expression) || 'AssignmentExpression' !== statement.expression.type) return;
@@ -137,19 +145,31 @@ const collectEsmExports = (statement, exports1)=>{
137
145
  }
138
146
  }
139
147
  };
140
- const visitAst = (node, visitor)=>{
141
- visitor(node);
142
- for (const [key, value] of Object.entries(node))if ('loc' !== key && 'start' !== key && 'end' !== key && 'leadingComments' !== key && 'trailingComments' !== key && 'innerComments' !== key) {
143
- if (isAstNode(value)) visitAst(value, visitor);
144
- else if (Array.isArray(value)) {
145
- for (const item of value)if (isAstNode(item)) visitAst(item, visitor);
148
+ const getStaticImport = (modulePath)=>{
149
+ const node = modulePath.node;
150
+ if (('ImportDeclaration' === node.type || 'ExportNamedDeclaration' === node.type || 'ExportAllDeclaration' === node.type) && isAstNode(node.source)) return getStringLiteralValue(node.source);
151
+ if ('ImportExpression' === node.type && isAstNode(node.source)) return getStringLiteralValue(node.source);
152
+ if ('CallExpression' === node.type && Array.isArray(node.arguments) && 1 === node.arguments.length && isAstNode(node.callee) && ('Identifier' === node.callee.type && 'require' === node.callee.name && void 0 === modulePath.scope.getBinding('require') || isAmbientModuleRequire(node.callee, modulePath) || 'Import' === node.callee.type)) return getStringLiteralValue(node.arguments[0]);
153
+ };
154
+ const isAmbientLoaderWrite = (modulePath)=>{
155
+ let targetPath = modulePath;
156
+ while(targetPath.parentPath){
157
+ const parentPath = targetPath.parentPath;
158
+ if (parentPath.isAssignmentExpression()) return 'left' === targetPath.key;
159
+ if (parentPath.isUpdateExpression()) return 'argument' === targetPath.key;
160
+ if (parentPath.isForInStatement() || parentPath.isForOfStatement()) return 'left' === targetPath.key;
161
+ if (parentPath.isObjectProperty()) {
162
+ if ('value' !== targetPath.key) return false;
163
+ targetPath = parentPath;
164
+ continue;
165
+ }
166
+ if (parentPath.isObjectPattern() || parentPath.isArrayPattern() || parentPath.isRestElement() || parentPath.isAssignmentPattern()) {
167
+ targetPath = parentPath;
168
+ continue;
146
169
  }
170
+ break;
147
171
  }
148
- };
149
- const getStaticImport = (node)=>{
150
- if (('ImportDeclaration' === node.type || 'ExportNamedDeclaration' === node.type || 'ExportAllDeclaration' === node.type) && isAstNode(node.source)) return getNodeName(node.source);
151
- if ('ImportExpression' === node.type && isAstNode(node.source)) return getNodeName(node.source);
152
- if ('CallExpression' === node.type && Array.isArray(node.arguments) && 1 === node.arguments.length && isAstNode(node.callee) && ('Identifier' === node.callee.type && 'require' === node.callee.name || 'Import' === node.callee.type)) return getNodeName(node.arguments[0]);
172
+ return false;
153
173
  };
154
174
  const analyzeWorkerModule = (source)=>{
155
175
  const ast = (0, parser_namespaceObject.parse)(source, {
@@ -157,17 +177,49 @@ const analyzeWorkerModule = (source)=>{
157
177
  });
158
178
  const exports1 = new Set();
159
179
  const imports = new Set();
180
+ let unsafeCommonJsLoaderUsage = false;
181
+ let unsafeDynamicImport = false;
160
182
  for (const statement of ast.program.body){
161
183
  collectEsmExports(statement, exports1);
162
184
  collectCommonJsExports(statement, exports1);
163
185
  }
164
- visitAst(ast.program, (node)=>{
165
- const specifier = getStaticImport(node);
166
- if (specifier) imports.add(specifier);
186
+ traverse_default()(ast, {
187
+ enter (modulePath) {
188
+ const node = modulePath.node;
189
+ if (('ImportExpression' === node.type || 'CallExpression' === node.type && isAstNode(node.callee) && 'Import' === node.callee.type) && void 0 === getStaticImport(modulePath)) unsafeDynamicImport = true;
190
+ if (modulePath.isReferencedIdentifier({
191
+ name: 'require'
192
+ }) && void 0 === modulePath.scope.getBinding('require')) {
193
+ const parentPath = modulePath.parentPath;
194
+ const isStaticRequireCall = parentPath?.isCallExpression() && 'callee' === modulePath.key && void 0 !== getStaticImport(parentPath);
195
+ const isAvailabilityCheck = parentPath?.isUnaryExpression({
196
+ operator: 'typeof'
197
+ });
198
+ if (!isStaticRequireCall && !isAvailabilityCheck) unsafeCommonJsLoaderUsage = true;
199
+ }
200
+ if ('Identifier' === node.type && isAmbientLoaderWrite(modulePath) && ('require' === node.name || 'module' === node.name) && void 0 === modulePath.scope.getBinding(node.name)) unsafeCommonJsLoaderUsage = true;
201
+ if (modulePath.isReferencedIdentifier({
202
+ name: 'module'
203
+ }) && void 0 === modulePath.scope.getBinding('module')) {
204
+ const parentPath = modulePath.parentPath;
205
+ const isExportsAccess = null !== parentPath && isModuleExports(parentPath.node);
206
+ const isStaticModuleRequireCall = null !== parentPath && isAmbientModuleRequire(parentPath.node, parentPath) && parentPath.parentPath?.isCallExpression() === true && 'callee' === parentPath.key && void 0 !== getStaticImport(parentPath.parentPath);
207
+ if (!isExportsAccess && !isStaticModuleRequireCall) unsafeCommonJsLoaderUsage = true;
208
+ }
209
+ if (isAmbientModuleRequire(node, modulePath)) {
210
+ const parentPath = modulePath.parentPath;
211
+ const isStaticRequireCall = parentPath?.isCallExpression() && 'callee' === modulePath.key && void 0 !== getStaticImport(parentPath);
212
+ if (!isStaticRequireCall) unsafeCommonJsLoaderUsage = true;
213
+ }
214
+ const specifier = getStaticImport(modulePath);
215
+ if (void 0 !== specifier) imports.add(specifier);
216
+ }
167
217
  });
168
218
  return {
169
219
  exports: exports1,
170
- imports
220
+ imports,
221
+ unsafeCommonJsLoaderUsage,
222
+ unsafeDynamicImport
171
223
  };
172
224
  };
173
225
  const getPackageName = (specifier)=>specifier.startsWith('@') ? specifier.split('/').slice(0, 2).join('/') : specifier.split('/')[0];
@@ -218,7 +270,25 @@ const verifyWorkerModuleClosure = async (issues, entryPath, entrySource, contrac
218
270
  return;
219
271
  }
220
272
  if (isEntry) entryAnalysis = analysis;
273
+ if (analysis.unsafeCommonJsLoaderUsage) (0, external_issues_js_namespaceObject.addIssue)(issues, {
274
+ code: 'invalid-worker-bundle',
275
+ message: 'Cloudflare worker bundles must use exactly one string-literal specifier in ambient CommonJS loader calls and must not pass or alias the loader.',
276
+ path: modulePath
277
+ });
278
+ if (analysis.unsafeDynamicImport) (0, external_issues_js_namespaceObject.addIssue)(issues, {
279
+ code: 'invalid-worker-bundle',
280
+ message: 'Cloudflare worker bundles must not contain non-static dynamic module imports.',
281
+ path: modulePath
282
+ });
221
283
  for (const specifier of analysis.imports){
284
+ if (0 === specifier.length) {
285
+ (0, external_issues_js_namespaceObject.addIssue)(issues, {
286
+ code: 'invalid-worker-bundle',
287
+ message: 'Cloudflare worker bundle module specifiers must not be empty.',
288
+ path: modulePath
289
+ });
290
+ continue;
291
+ }
222
292
  if (!specifier.startsWith('.') && !external_node_path_default().isAbsolute(specifier)) {
223
293
  if (CLOUDFLARE_WORKER_NODE_BUILTIN_SPECIFIERS.has(specifier)) continue;
224
294
  if (specifier.startsWith('node:')) {
@@ -267,14 +337,20 @@ const verifyWorkerBundleReferences = async (issues, worker, source, contract)=>{
267
337
  path: worker.path
268
338
  });
269
339
  };
270
- const verifyWorkerImport = async (issues, entryPath)=>{
340
+ const verifyWorkerImport = async (issues, entryPath, expectedManifest)=>{
271
341
  try {
272
- const worker = (await import(`${(0, external_node_url_namespaceObject.pathToFileURL)(entryPath).href}?t=${Date.now()}`)).default;
342
+ const workerModule = await import(`${(0, external_node_url_namespaceObject.pathToFileURL)(entryPath).href}?t=${Date.now()}`);
343
+ const worker = workerModule.default;
273
344
  if (!worker || 'function' != typeof worker.fetch) (0, external_issues_js_namespaceObject.addIssue)(issues, {
274
345
  code: 'worker-import-failed',
275
346
  message: 'Cloudflare server entry must default-export a Worker with fetch.',
276
347
  path: entryPath
277
348
  });
349
+ if (void 0 !== expectedManifest && !(0, external_node_util_namespaceObject.isDeepStrictEqual)(workerModule.modernWorkerManifest, expectedManifest)) (0, external_issues_js_namespaceObject.addIssue)(issues, {
350
+ code: 'worker-import-failed',
351
+ message: 'Cloudflare server entry runtime manifest must exactly match modern-worker-manifest.json.',
352
+ path: entryPath
353
+ });
278
354
  } catch (error) {
279
355
  (0, external_issues_js_namespaceObject.addIssue)(issues, {
280
356
  code: 'worker-import-failed',
@@ -1,5 +1,6 @@
1
1
  const ASSETS_BINDING = 'ASSETS';
2
2
  const MODERN_WORKER_MANIFEST = p_workerManifest;
3
+ export const modernWorkerManifest = MODERN_WORKER_MANIFEST;
3
4
  const WORKER_MODULE_LOADERS = p_workerModuleLoaders;
4
5
  const workerModulePromises = new Map();
5
6
  const effectBffDispatcherPromises = new Map();
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Workerd cannot initialize Module Federation's Node-oriented SSR data-fetch
3
+ * runtime. Cloudflare workers do not load native MF remotes, so retain the
4
+ * runtime-plugin contract without installing its server data-fetch hooks.
5
+ */
6
+ export const injectDataFetchFunctionPlugin = () => ({
7
+ name: '@module-federation/inject-data-fetch-function-plugin',
8
+ setup() {},
9
+ });
10
+
11
+ export const mfSSRDevPlugin = () => ({
12
+ name: '@module-federation/modern-js-v3',
13
+ setup() {},
14
+ });
@@ -38,6 +38,7 @@ var __webpack_exports__ = {};
38
38
  __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
40
  MICROVERTICAL_RELEASE_ENVELOPE_PATH: ()=>MICROVERTICAL_RELEASE_ENVELOPE_PATH,
41
+ MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH: ()=>MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH,
41
42
  emitCloudflareStagedReleaseEnvelope: ()=>emitCloudflareStagedReleaseEnvelope,
42
43
  emitFrameworkMicroVerticalReleaseEnvelope: ()=>emitFrameworkMicroVerticalReleaseEnvelope,
43
44
  emitNodeStagedReleaseEnvelope: ()=>emitNodeStagedReleaseEnvelope,
@@ -47,47 +48,20 @@ __webpack_require__.d(__webpack_exports__, {
47
48
  verifyCloudflareReleaseEnvelopeStaging: ()=>verifyCloudflareReleaseEnvelopeStaging,
48
49
  verifyNodeReleaseEnvelopeStaging: ()=>verifyNodeReleaseEnvelopeStaging
49
50
  });
51
+ const external_node_crypto_namespaceObject = require("node:crypto");
50
52
  const promises_namespaceObject = require("node:fs/promises");
51
53
  var promises_default = /*#__PURE__*/ __webpack_require__.n(promises_namespaceObject);
52
54
  const external_node_path_namespaceObject = require("node:path");
53
55
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
54
- const parser_namespaceObject = require("@babel/parser");
55
56
  const universal_namespaceObject = require("@modern-js/utils/universal");
56
57
  const external_index_js_namespaceObject = require("./index.js");
57
58
  const MICROVERTICAL_RELEASE_ENVELOPE_PATH = 'release/microvertical-release-envelope.json';
59
+ const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH = 'release/microvertical-release-identity-carriers.json';
60
+ const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_KIND = 'ultramodern-release-identity-carriers';
61
+ const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_SCHEMA_VERSION = 1;
58
62
  const COMPILED_MODULE_PATTERN = /\.(?:c|m)?js$/u;
59
63
  const EFFECT_BFF_WORKER_PATTERN = /^worker\/__modern_bff_effect\.(?:c|m)?js$/u;
60
64
  const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
61
- const isAstNode = (value)=>isRecord(value) && 'string' == typeof value.type;
62
- const visitAst = (node, visitor)=>{
63
- visitor(node);
64
- for (const value of Object.values(node))if (isAstNode(value)) visitAst(value, visitor);
65
- else if (Array.isArray(value)) {
66
- for (const item of value)if (isAstNode(item)) visitAst(item, visitor);
67
- }
68
- };
69
- const compiledModuleIdentityValues = (source)=>{
70
- const values = new Set();
71
- const ast = (0, parser_namespaceObject.parse)(source, {
72
- sourceType: 'unambiguous'
73
- });
74
- visitAst(ast.program, (node)=>{
75
- if ('StringLiteral' === node.type && 'string' == typeof node.value) values.add(node.value);
76
- else if ('TemplateElement' === node.type && isRecord(node.value) && 'string' == typeof node.value.cooked) values.add(node.value.cooked);
77
- });
78
- return values;
79
- };
80
- const assertEveryCompiledIdentityCarrier = async (distDirectory, logicalPaths, identity, surface)=>{
81
- const compiledLogicalPaths = logicalPaths.filter((pathname)=>COMPILED_MODULE_PATTERN.test(pathname));
82
- if (0 === compiledLogicalPaths.length) throw new Error(`[ultramodern-release-envelope] ${surface} has no compiled execution module carrying release identity.`);
83
- for (const logicalPath of compiledLogicalPaths){
84
- const source = await promises_default().readFile(external_node_path_default().join(distDirectory, logicalPath), {
85
- encoding: 'utf8'
86
- });
87
- const values = compiledModuleIdentityValues(source);
88
- if (!values.has(identity.buildMarker) || !values.has(identity.sourceRevision) || !values.has(identity.releaseVersion)) throw new Error(`[ultramodern-release-envelope] ${surface} execution module ${logicalPath} does not carry exact buildMarker ${identity.buildMarker}, sourceRevision ${identity.sourceRevision}, and releaseVersion ${identity.releaseVersion}.`);
89
- }
90
- };
91
65
  const flattenStrings = (value)=>'string' == typeof value ? [
92
66
  value
93
67
  ] : Array.isArray(value) ? value.flatMap(flattenStrings) : [];
@@ -230,6 +204,49 @@ const releaseArtifact = (logicalPath, runtime)=>({
230
204
  logicalPath,
231
205
  runtime
232
206
  });
207
+ const writeReleaseIdentityCarrierMetadata = async (artifactRoot, identity, carrierPaths)=>{
208
+ const surfacesByPath = new Map();
209
+ for (const [surface, logicalPaths] of Object.entries(carrierPaths)){
210
+ if (0 === logicalPaths.length) throw new Error(`[ultramodern-release-envelope] ${surface} has no declared release-identity carrier artifact.`);
211
+ for (const logicalPath of logicalPaths){
212
+ const surfaces = surfacesByPath.get(logicalPath) ?? new Set();
213
+ surfaces.add(surface);
214
+ surfacesByPath.set(logicalPath, surfaces);
215
+ }
216
+ }
217
+ const carriers = await Promise.all([
218
+ ...surfacesByPath
219
+ ].sort(([left], [right])=>left.localeCompare(right)).map(async ([logicalPath, surfaces])=>{
220
+ const bytes = await promises_default().readFile(external_node_path_default().join(artifactRoot, logicalPath));
221
+ return {
222
+ logicalPath,
223
+ byteLength: bytes.byteLength,
224
+ sha256: (0, external_node_crypto_namespaceObject.createHash)('sha256').update(bytes).digest('hex'),
225
+ surfaces: [
226
+ ...surfaces
227
+ ].sort((left, right)=>left.localeCompare(right))
228
+ };
229
+ }));
230
+ const releaseEnvelopePath = await resolveSafeReleaseEnvelopePath({
231
+ artifactRoot,
232
+ createDirectory: true
233
+ });
234
+ if (!releaseEnvelopePath) throw new Error('[ultramodern-release-envelope] could not resolve private release metadata directory.');
235
+ const metadataPath = external_node_path_default().join(external_node_path_default().dirname(releaseEnvelopePath), external_node_path_default().basename(MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH));
236
+ try {
237
+ const metadataStat = await promises_default().lstat(metadataPath);
238
+ if (metadataStat.isSymbolicLink() || !metadataStat.isFile()) throw new Error('[ultramodern-release-envelope] release identity carrier metadata must be a real file inside artifactRoot.');
239
+ } catch (error) {
240
+ if (!isMissingPathError(error)) throw error;
241
+ }
242
+ await promises_default().writeFile(metadataPath, `${JSON.stringify({
243
+ schemaVersion: MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_SCHEMA_VERSION,
244
+ kind: MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_KIND,
245
+ identity,
246
+ carriers
247
+ }, null, 2)}\n`);
248
+ return releaseArtifact(MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH, 'release-identity-metadata');
249
+ };
233
250
  const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
234
251
  const files = await collectFiles(distDirectory);
235
252
  if ('node' === target && files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath))) throw new Error('[ultramodern-release-envelope] Node target conflicts with a Cloudflare Effect API/BFF worker artifact; resolve the canonical deploy target instead of emitting a Node envelope for Cloudflare output.');
@@ -253,13 +270,7 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
253
270
  const clientExecutionPaths = await manifestReferencedClientModules(distDirectory, files);
254
271
  if (0 === clientExecutionPaths.length) throw new Error('[ultramodern-release-envelope] UI/client manifest references no compiled execution module.');
255
272
  const ssrExecutionPaths = await routeReferencedSsrModules(distDirectory, files, target);
256
- await assertEveryCompiledIdentityCarrier(distDirectory, clientExecutionPaths, identity, 'manifest-referenced UI/client');
257
- await assertEveryCompiledIdentityCarrier(distDirectory, ssrExecutionPaths, identity, 'node' === target ? 'route-referenced Node SSR' : 'route-referenced Cloudflare SSR');
258
- await assertEveryCompiledIdentityCarrier(distDirectory, ssrPaths, identity, 'node' === target ? 'compiled Node SSR closure' : 'compiled Cloudflare SSR/workerd closure');
259
- await assertEveryCompiledIdentityCarrier(distDirectory, 'node' === target ? nodeApiEntryPaths : apiBackendPaths, identity, 'node' === target ? 'Node API/backend' : 'Cloudflare API/backend');
260
- await assertEveryCompiledIdentityCarrier(distDirectory, [
261
- universal_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE
262
- ], identity, 'backend Module Federation container');
273
+ if (0 === ssrExecutionPaths.length) throw new Error(`[ultramodern-release-envelope] route manifest references no emitted ${'node' === target ? 'Node' : 'Cloudflare'} SSR execution module.`);
263
274
  const runtimeByPath = new Map();
264
275
  const add = (paths, runtime)=>{
265
276
  for (const logicalPath of paths){
@@ -295,8 +306,25 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
295
306
  const artifacts = [
296
307
  ...runtimeByPath
297
308
  ].map(([logicalPath, runtime])=>releaseArtifact(logicalPath, runtime)).sort((left, right)=>left.logicalPath.localeCompare(right.logicalPath));
309
+ const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(distDirectory, identity, {
310
+ apiBackend: apiBackendPaths,
311
+ backendFederation: [
312
+ universal_namespaceObject.BACKEND_FEDERATION_MANIFEST_FILE,
313
+ universal_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE
314
+ ],
315
+ ssr: [
316
+ ...new Set([
317
+ ...ssrExecutionPaths,
318
+ ...ssrPaths
319
+ ])
320
+ ],
321
+ uiClient: uiClientPaths.filter((logicalPath)=>COMPILED_MODULE_PATTERN.test(logicalPath))
322
+ });
298
323
  return {
299
- artifacts,
324
+ artifacts: [
325
+ ...artifacts,
326
+ identityCarrierMetadata
327
+ ].sort((left, right)=>left.logicalPath.localeCompare(right.logicalPath)),
300
328
  surfaces
301
329
  };
302
330
  };
@@ -418,13 +446,7 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
418
446
  ])if (0 === paths.length) throw new Error(`[ultramodern-release-envelope] final Node staging has no ${surface} artifacts.`);
419
447
  const clientExecutionPaths = await manifestReferencedClientModules(outputDirectory, files);
420
448
  const ssrExecutionPaths = await routeReferencedSsrModules(outputDirectory, files, 'node');
421
- await assertEveryCompiledIdentityCarrier(outputDirectory, clientExecutionPaths, identity, 'final manifest-referenced Node UI/client');
422
- await assertEveryCompiledIdentityCarrier(outputDirectory, ssrExecutionPaths, identity, 'final route-referenced Node SSR');
423
- await assertEveryCompiledIdentityCarrier(outputDirectory, ssrPaths.filter((logicalPath)=>'index.js' !== logicalPath), identity, 'final compiled Node SSR closure');
424
- await assertEveryCompiledIdentityCarrier(outputDirectory, apiBackendPaths.filter((logicalPath)=>logicalPath.startsWith('api/')), identity, 'final Node API/backend');
425
- await assertEveryCompiledIdentityCarrier(outputDirectory, [
426
- universal_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE
427
- ], identity, 'final Node backend Module Federation container');
449
+ if (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length) throw new Error('[ultramodern-release-envelope] final Node manifests must reference emitted UI/client and SSR execution modules.');
428
450
  const runtimeByPath = new Map(files.filter((logicalPath)=>!backendFederationPaths.has(logicalPath)).map((logicalPath)=>[
429
451
  logicalPath,
430
452
  'nodejs-deployment'
@@ -434,10 +456,27 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
434
456
  for (const logicalPath of apiBackendPaths)runtimeByPath.set(logicalPath, 'nodejs');
435
457
  runtimeByPath.set(universal_namespaceObject.BACKEND_FEDERATION_MANIFEST_FILE, 'module-federation-manifest');
436
458
  runtimeByPath.set(universal_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE, 'nodejs');
459
+ const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(outputDirectory, identity, {
460
+ apiBackend: apiBackendPaths,
461
+ backendFederation: [
462
+ universal_namespaceObject.BACKEND_FEDERATION_MANIFEST_FILE,
463
+ universal_namespaceObject.BACKEND_FEDERATION_REMOTE_ENTRY_FILE
464
+ ],
465
+ ssr: [
466
+ ...new Set([
467
+ ...ssrExecutionPaths,
468
+ ...ssrPaths.filter((logicalPath)=>'index.js' !== logicalPath)
469
+ ])
470
+ ],
471
+ uiClient: uiClientPaths.filter((logicalPath)=>COMPILED_MODULE_PATTERN.test(logicalPath))
472
+ });
437
473
  return {
438
474
  artifacts: [
439
- ...runtimeByPath
440
- ].map(([logicalPath, runtime])=>releaseArtifact(logicalPath, runtime)).sort((left, right)=>left.logicalPath.localeCompare(right.logicalPath)),
475
+ ...[
476
+ ...runtimeByPath
477
+ ].map(([logicalPath, runtime])=>releaseArtifact(logicalPath, runtime)),
478
+ identityCarrierMetadata
479
+ ].sort((left, right)=>left.logicalPath.localeCompare(right.logicalPath)),
441
480
  surfaces: {
442
481
  uiClient: [
443
482
  ...uiClientPaths
@@ -511,13 +550,7 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
511
550
  ])if (!files.includes(logicalPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${logicalPath}.`);
512
551
  const clientExecutionPaths = await manifestReferencedClientModules(outputDirectory, files, 'public/mf-manifest.json');
513
552
  const ssrExecutionPaths = await routeReferencedSsrModules(outputDirectory, files, 'cloudflare', 'server/route.json');
514
- await assertEveryCompiledIdentityCarrier(outputDirectory, clientExecutionPaths, identity, 'final manifest-referenced Cloudflare UI/client');
515
- await assertEveryCompiledIdentityCarrier(outputDirectory, ssrExecutionPaths, identity, 'final route-referenced Cloudflare SSR');
516
- await assertEveryCompiledIdentityCarrier(outputDirectory, ssrPaths.filter((logicalPath)=>logicalPath.startsWith('worker/')), identity, 'final compiled Cloudflare SSR/workerd closure');
517
- await assertEveryCompiledIdentityCarrier(outputDirectory, apiBackendPaths, identity, 'final Cloudflare API/backend');
518
- await assertEveryCompiledIdentityCarrier(outputDirectory, [
519
- backendContainerPath
520
- ], identity, 'final Cloudflare backend Module Federation container');
553
+ if (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length) throw new Error('[ultramodern-release-envelope] final Cloudflare manifests must reference emitted UI/client and SSR execution modules.');
521
554
  const runtimeByPath = new Map();
522
555
  for (const logicalPath of files)runtimeByPath.set(logicalPath, 'cloudflare-deployment');
523
556
  const add = (paths, runtime)=>{
@@ -532,10 +565,27 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
532
565
  add([
533
566
  backendContainerPath
534
567
  ], 'commonjs-module');
568
+ const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(outputDirectory, identity, {
569
+ apiBackend: apiBackendPaths,
570
+ backendFederation: [
571
+ backendManifestPath,
572
+ backendContainerPath
573
+ ],
574
+ ssr: [
575
+ ...new Set([
576
+ ...ssrExecutionPaths,
577
+ ...ssrPaths.filter((logicalPath)=>logicalPath.startsWith('worker/'))
578
+ ])
579
+ ],
580
+ uiClient: uiClientPaths.filter((logicalPath)=>COMPILED_MODULE_PATTERN.test(logicalPath))
581
+ });
535
582
  return {
536
583
  artifacts: [
537
- ...runtimeByPath
538
- ].map(([logicalPath, runtime])=>releaseArtifact(logicalPath, runtime)).sort((left, right)=>left.logicalPath.localeCompare(right.logicalPath)),
584
+ ...[
585
+ ...runtimeByPath
586
+ ].map(([logicalPath, runtime])=>releaseArtifact(logicalPath, runtime)),
587
+ identityCarrierMetadata
588
+ ].sort((left, right)=>left.logicalPath.localeCompare(right.logicalPath)),
539
589
  surfaces: {
540
590
  uiClient: [
541
591
  ...uiClientPaths
@@ -573,6 +623,7 @@ const verifyCloudflareReleaseEnvelopeStaging = async (outputDirectory)=>readFram
573
623
  required: true
574
624
  });
575
625
  exports.MICROVERTICAL_RELEASE_ENVELOPE_PATH = __webpack_exports__.MICROVERTICAL_RELEASE_ENVELOPE_PATH;
626
+ exports.MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH = __webpack_exports__.MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH;
576
627
  exports.emitCloudflareStagedReleaseEnvelope = __webpack_exports__.emitCloudflareStagedReleaseEnvelope;
577
628
  exports.emitFrameworkMicroVerticalReleaseEnvelope = __webpack_exports__.emitFrameworkMicroVerticalReleaseEnvelope;
578
629
  exports.emitNodeStagedReleaseEnvelope = __webpack_exports__.emitNodeStagedReleaseEnvelope;
@@ -583,6 +634,7 @@ exports.verifyCloudflareReleaseEnvelopeStaging = __webpack_exports__.verifyCloud
583
634
  exports.verifyNodeReleaseEnvelopeStaging = __webpack_exports__.verifyNodeReleaseEnvelopeStaging;
584
635
  for(var __rspack_i in __webpack_exports__)if (-1 === [
585
636
  "MICROVERTICAL_RELEASE_ENVELOPE_PATH",
637
+ "MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH",
586
638
  "emitCloudflareStagedReleaseEnvelope",
587
639
  "emitFrameworkMicroVerticalReleaseEnvelope",
588
640
  "emitNodeStagedReleaseEnvelope",