@bleedingdev/modern-js-create 3.5.0-ultramodern.29 → 3.5.0-ultramodern.30

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.
@@ -312,19 +312,71 @@ const cloudflareWranglerDeployInvalidSkipBuildCommand = `${cloudflareWranglerDep
312
312
  function removeStaleBackendFederationCommandSegments(command) {
313
313
  return command.replace(/\s+&&\s+node\s+\S*scripts\/generate-node-backend-federation\.m[ct]s(?:\s+--app\s+\S+)?(?:\s+--target\s+\S+)?(?=\s+&&|$)/gu, '');
314
314
  }
315
+ const SHELL_ONLY_OMITTED_ROOT_SCRIPTS = [
316
+ 'node:backend-federation:generate',
317
+ 'node:proof',
318
+ 'zerops:materialize'
319
+ ];
320
+ const splitScriptSegments = (command)=>command.split('&&').map((segment)=>segment.trim()).filter((segment)=>segment.length > 0);
321
+ const scriptSegmentTarget = (segment)=>segment.replace(/^pnpm\s+/u, '').split(/\s+/u)[0] ?? segment;
322
+ const FRAMEWORK_CHECK_TARGETS = new Set([
323
+ 'format:check',
324
+ 'lint',
325
+ 'typecheck',
326
+ 'skills:check',
327
+ 'i18n:boundaries',
328
+ 'api:check',
329
+ 'contract:check',
330
+ 'node:backend-federation:generate',
331
+ 'node:proof',
332
+ 'performance:readiness',
333
+ 'bridge:check'
334
+ ]);
335
+ function mergeAggregateCheckScript(consumer, framework) {
336
+ const frameworkSegments = splitScriptSegments(framework);
337
+ const consumerExtras = splitScriptSegments(consumer).filter((segment)=>!FRAMEWORK_CHECK_TARGETS.has(scriptSegmentTarget(segment)));
338
+ return [
339
+ ...consumerExtras,
340
+ ...frameworkSegments
341
+ ].join(' && ');
342
+ }
343
+ function rewriteMigratedScriptReferences(scripts) {
344
+ let changed = false;
345
+ const pattern = new RegExp(`(scripts/(?:${workspace_scripts_cjs_namespaceObject.migratedWorkspaceScriptBasenames.join('|')}))\\.mjs`, 'gu');
346
+ for (const [name, value] of Object.entries(scripts)){
347
+ if ('string' != typeof value) continue;
348
+ const next = value.replace(pattern, '$1.mts');
349
+ if (next !== value) {
350
+ scripts[name] = next;
351
+ changed = true;
352
+ }
353
+ }
354
+ return changed;
355
+ }
315
356
  function updateGeneratedPackageScripts(packageJson, options = {}) {
316
357
  const scripts = packageJson.scripts;
317
358
  if (!scripts || 'object' != typeof scripts || Array.isArray(scripts)) return false;
318
359
  let changed = false;
319
360
  const apps = options.apps ?? [];
361
+ const shellOnly = options.shellOnly ?? false;
320
362
  const app = apps.find((candidate)=>`${candidate.directory}/package.json` === options.relativePackageFile);
321
- const expectedScripts = 'package.json' === options.relativePackageFile ? (0, workspace_script_plan_cjs_namespaceObject.createWorkspaceRootPackageScripts)(apps.filter((candidate)=>'shell' !== candidate.kind)) : app ? (0, workspace_script_plan_cjs_namespaceObject.createWorkspaceAppPackageScripts)(app) : void 0;
322
- if (expectedScripts) {
323
- for (const [name, value] of Object.entries(expectedScripts))if (scripts[name] !== value) {
324
- scripts[name] = value;
363
+ const isRootPackage = 'package.json' === options.relativePackageFile;
364
+ const expectedScripts = isRootPackage ? (0, workspace_script_plan_cjs_namespaceObject.createWorkspaceRootPackageScripts)(apps.filter((candidate)=>'shell' !== candidate.kind)) : app ? (0, workspace_script_plan_cjs_namespaceObject.createWorkspaceAppPackageScripts)(app) : void 0;
365
+ if (expectedScripts) for (const [name, value] of Object.entries(expectedScripts)){
366
+ if (isRootPackage && shellOnly && SHELL_ONLY_OMITTED_ROOT_SCRIPTS.includes(name)) {
367
+ if (name in scripts) {
368
+ delete scripts[name];
369
+ changed = true;
370
+ }
371
+ continue;
372
+ }
373
+ const nextValue = 'check' === name && 'string' == typeof scripts[name] ? mergeAggregateCheckScript(scripts[name], value) : value;
374
+ if (scripts[name] !== nextValue) {
375
+ scripts[name] = nextValue;
325
376
  changed = true;
326
377
  }
327
378
  }
379
+ if (rewriteMigratedScriptReferences(scripts)) changed = true;
328
380
  const build = scripts.build;
329
381
  if ('string' == typeof build) {
330
382
  const nextBuild = removeStaleBackendFederationCommandSegments(build);
@@ -699,6 +751,7 @@ function updateUltramodernConfig(io, config, packageSource) {
699
751
  aliasPackageNamePrefix: packageSource.aliasPackageNamePrefix
700
752
  } : {}
701
753
  };
754
+ if (config.generator && 'object' == typeof config.generator && !Array.isArray(config.generator) && 'string' == typeof config.generator.version) config.generator.version = packageSource.modernPackageVersion;
702
755
  for (const app of config.topology?.apps ?? [])if (app && 'object' == typeof app && !Array.isArray(app)) normalizeStrictEffectApiMetadata(app);
703
756
  writeJsonFile(io, configPath, config);
704
757
  }
@@ -928,14 +981,27 @@ workspaces skip the backend-federation and Zerops runtime stages. Pass
928
981
  const migrated = (0, external_config_cjs_namespaceObject.normalizeCompactUltramodernConfig)(io.workspaceRoot, raw);
929
982
  const migratedApps = (0, external_config_cjs_namespaceObject.workspaceAppsFromToolingConfig)(migrated);
930
983
  const shellOnly = !migrated.topology.apps.some((app)=>app.api);
931
- if (shellOnly) io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
932
- else {
984
+ if (shellOnly) {
985
+ io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
986
+ for (const relativePath of [
987
+ "scripts/generate-node-backend-federation.mts",
988
+ "scripts/generate-node-backend-federation.mjs",
989
+ "scripts/proof-node-backend-federation.mts",
990
+ "scripts/proof-node-backend-federation.mjs",
991
+ "scripts/materialize-zerops-runtime.mjs",
992
+ 'zerops.yaml'
993
+ ])removeGeneratedFileIfExists(io, relativePath);
994
+ } else {
933
995
  removeStaleBackendFederationArtifacts(io, migrated);
934
996
  updateGeneratedZeropsArtifacts(io, migrated);
935
997
  updateGeneratedBackendFederationContractFiles(io, migrated);
936
998
  }
937
- if (dryRun) io.log("would refresh generated tool wrapper scripts under scripts/");
938
- else (0, workspace_scripts_cjs_namespaceObject.writeGeneratedToolWrapperScripts)(io.workspaceRoot);
999
+ for (const artifact of (0, workspace_scripts_cjs_namespaceObject.migratedWorkspaceScriptArtifacts)({
1000
+ shellOnly
1001
+ })){
1002
+ if (artifact.legacyPath) io.remove(external_node_path_default().join(io.workspaceRoot, artifact.legacyPath));
1003
+ io.write(external_node_path_default().join(io.workspaceRoot, artifact.relativePath), artifact.content);
1004
+ }
939
1005
  updateGeneratedBuildIdentityModules(io, migrated);
940
1006
  updateGeneratedTypeScriptSurfaces(io, migrated);
941
1007
  updateGeneratedModernConfigs(io, migrated);
@@ -953,7 +1019,8 @@ workspaces skip the backend-federation and Zerops runtime stages. Pass
953
1019
  updateGeneratedToolingDependencies(packageJson);
954
1020
  updateGeneratedPackageScripts(packageJson, {
955
1021
  relativePackageFile,
956
- apps: migratedApps
1022
+ apps: migratedApps,
1023
+ shellOnly
957
1024
  });
958
1025
  writeJsonFile(io, packageFile, packageJson);
959
1026
  }
@@ -1092,9 +1159,23 @@ for (const target of targets) {
1092
1159
  console.log(\`[ultramodern] TanStack route artifacts generated: \${target.label}\`);
1093
1160
  } catch (error) {
1094
1161
  failed = true;
1095
- const message = error instanceof Error ? error.message : String(error);
1096
1162
  console.error(\`[ultramodern] TanStack route generation failed: \${target.label}\`);
1097
- console.error(\`- \${message}\`);
1163
+ // Print the full underlying failure, including the cause chain. The
1164
+ // route-generate crash is often an opaque node error thrown deep inside
1165
+ // app-tools/plugin (e.g. a path TypeError), so surfacing only
1166
+ // \`error.message\` swallows the stack that points at the real culprit.
1167
+ let current = error;
1168
+ let depth = 0;
1169
+ while (current) {
1170
+ const label = depth === 0 ? '-' : ' caused by:';
1171
+ const detail =
1172
+ current instanceof Error
1173
+ ? current.stack ?? \`\${current.name}: \${current.message}\`
1174
+ : String(current);
1175
+ console.error(\`\${label} \${detail}\`);
1176
+ current = current instanceof Error ? current.cause : undefined;
1177
+ depth += 1;
1178
+ }
1098
1179
  }
1099
1180
  }
1100
1181
 
@@ -104,6 +104,15 @@ const zephyrEnabled = process.env['ULTRAMODERN_ZEPHYR'] !== 'false';
104
104
  const cloudflareDeployEnabled =
105
105
  process.env['MODERNJS_DEPLOY'] === 'cloudflare';
106
106
 
107
+ const parsedZephyrTimeoutMs = Number.parseInt(
108
+ process.env['ULTRAMODERN_ZEPHYR_TIMEOUT_MS'] ?? '',
109
+ 10,
110
+ );
111
+ const zephyrTimeoutMs =
112
+ Number.isFinite(parsedZephyrTimeoutMs) && parsedZephyrTimeoutMs > 0
113
+ ? parsedZephyrTimeoutMs
114
+ : 45000;
115
+
107
116
  const zephyrWarn = (error: unknown) => {
108
117
  const message = error instanceof Error ? error.message : String(error);
109
118
  console.warn(
@@ -126,10 +135,27 @@ const zephyrRspackPlugin = () => ({
126
135
  }
127
136
  api.modifyRspackConfig(config => {
128
137
  try {
129
- return Promise.resolve(withZephyrRspack()(config)).catch(error => {
130
- zephyrWarn(error);
131
- return config;
138
+ // Zephyr can not only throw/reject but also hang on a stalled network
139
+ // call, wedging the whole build. Race it against a watchdog so a hang
140
+ // degrades to an unmodified config instead of blocking indefinitely.
141
+ const zephyrConfig = Promise.resolve(withZephyrRspack()(config)).catch(
142
+ error => {
143
+ zephyrWarn(error);
144
+ return config;
145
+ },
146
+ );
147
+ const watchdog = new Promise<ZephyrRspackConfig>(resolve => {
148
+ const timer = setTimeout(() => {
149
+ zephyrWarn(
150
+ \`timed out after \${zephyrTimeoutMs}ms (override with ULTRAMODERN_ZEPHYR_TIMEOUT_MS)\`,
151
+ );
152
+ resolve(config);
153
+ }, zephyrTimeoutMs);
154
+ if (typeof timer.unref === 'function') {
155
+ timer.unref();
156
+ }
132
157
  });
158
+ return Promise.race([zephyrConfig, watchdog]);
133
159
  } catch (error) {
134
160
  zephyrWarn(error);
135
161
  return config;
@@ -138,7 +138,7 @@ function createWorkspaceRootScriptPlan(remotes = [], options = {}) {
138
138
  zeropsMaterialize: "node ./scripts/materialize-zerops-runtime.mjs",
139
139
  contractCheck: rootToolingWrapperCommand('validate'),
140
140
  typecheck: options.typecheck ?? `${rootToolingWrapperCommand('typecheck')} --project tsconfig.json`,
141
- check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm api:check && pnpm contract:check && pnpm ${backendFederationGenerateScript} && pnpm ${nodeProofScript} && pnpm performance:readiness${bridgeCheck}`
141
+ check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm api:check && pnpm contract:check${hasRemotes ? ` && pnpm ${backendFederationGenerateScript} && pnpm ${nodeProofScript}` : ''} && pnpm performance:readiness${bridgeCheck}`
142
142
  };
143
143
  }
144
144
  function createWorkspaceRootPackageScripts(remotes = [], options = {}) {
@@ -37,12 +37,15 @@ var __webpack_require__ = {};
37
37
  var __webpack_exports__ = {};
38
38
  __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
+ createAgentReferenceReposSetupScript: ()=>createAgentReferenceReposSetupScript,
40
41
  createNodeBackendFederationProofScript: ()=>createNodeBackendFederationProofScript,
41
42
  createPerformanceReadinessConfigScript: ()=>createPerformanceReadinessConfigScript,
42
43
  createWorkspaceApiBoundaryValidationScript: ()=>createWorkspaceApiBoundaryValidationScript,
43
44
  createWorkspaceI18nBoundaryValidationScript: ()=>createWorkspaceI18nBoundaryValidationScript,
44
45
  createWorkspaceValidationScript: ()=>createWorkspaceValidationScript,
45
46
  createZeropsRuntimeMaterializationScript: ()=>createZeropsRuntimeMaterializationScript,
47
+ migratedWorkspaceScriptArtifacts: ()=>migratedWorkspaceScriptArtifacts,
48
+ migratedWorkspaceScriptBasenames: ()=>migratedWorkspaceScriptBasenames,
46
49
  writeGeneratedToolWrapperScripts: ()=>writeGeneratedToolWrapperScripts,
47
50
  writeGeneratedWorkspaceScripts: ()=>writeGeneratedWorkspaceScripts
48
51
  });
@@ -53,9 +56,10 @@ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_no
53
56
  const external_fs_io_cjs_namespaceObject = require("./fs-io.cjs");
54
57
  const external_tooling_command_catalog_cjs_namespaceObject = require("./tooling-command-catalog.cjs");
55
58
  const external_workspace_validation_contract_cjs_namespaceObject = require("./workspace-validation-contract.cjs");
59
+ const singleQuoted = (value)=>`'${value.replace(/'/gu, "\\'")}'`;
56
60
  function createToolWrapperScript(command, extraArgs = []) {
57
- const commandJson = JSON.stringify(command);
58
- const extraArgsJson = JSON.stringify(extraArgs);
61
+ const commandLiteral = singleQuoted(command);
62
+ const extraArgsLiteral = `[${extraArgs.map(singleQuoted).join(', ')}]`;
59
63
  return `#!/usr/bin/env node
60
64
  import { spawnSync } from 'node:child_process';
61
65
  import path from 'node:path';
@@ -66,7 +70,7 @@ const forwardedArgs = process.argv.slice(2);
66
70
  const workspaceRoot =
67
71
  process.env.ULTRAMODERN_WORKSPACE_ROOT ??
68
72
  path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
69
- const ultramodernArgs = ['ultramodern', ${commandJson}, ...${extraArgsJson}, ...forwardedArgs];
73
+ const ultramodernArgs = ['ultramodern', ${commandLiteral}, ...${extraArgsLiteral}, ...forwardedArgs];
70
74
  const result = createBin
71
75
  ? spawnSync(process.execPath, [createBin, ...ultramodernArgs], {
72
76
  env: { ...process.env, ULTRAMODERN_WORKSPACE_ROOT: workspaceRoot },
@@ -191,21 +195,75 @@ function writeGeneratedWorkspaceScripts(targetDir, _scope, _enableTailwind, _rem
191
195
  writeWorkspaceOwnedMtsScript(targetDir, 'bootstrap-agent-skills', createSkillsToolWrapperScript());
192
196
  migrateCopiedWorkspaceScriptToMts(targetDir, 'setup-agent-reference-repos');
193
197
  }
198
+ function createAgentReferenceReposSetupScript() {
199
+ return external_node_fs_default().readFileSync(external_node_path_default().join(external_fs_io_cjs_namespaceObject.workspaceTemplateDir, "scripts/setup-agent-reference-repos.mjs"), 'utf-8');
200
+ }
201
+ const BACKEND_FEDERATION_WRAPPER_IDS = new Set([
202
+ 'backendFederationGenerate',
203
+ 'backendFederationProof'
204
+ ]);
205
+ function migratedWorkspaceScriptArtifacts(options) {
206
+ const artifacts = [
207
+ {
208
+ relativePath: "scripts/check-ultramodern-i18n-boundaries.mts",
209
+ content: createWorkspaceI18nBoundaryValidationScript(),
210
+ legacyPath: "scripts/check-ultramodern-i18n-boundaries.mjs"
211
+ },
212
+ {
213
+ relativePath: "scripts/check-ultramodern-api-boundaries.mts",
214
+ content: createWorkspaceApiBoundaryValidationScript(),
215
+ legacyPath: "scripts/check-ultramodern-api-boundaries.mjs"
216
+ },
217
+ {
218
+ relativePath: "scripts/ultramodern-performance-readiness.config.mjs",
219
+ content: createPerformanceReadinessConfigScript()
220
+ },
221
+ {
222
+ relativePath: "scripts/bootstrap-agent-skills.mts",
223
+ content: createSkillsToolWrapperScript(),
224
+ legacyPath: "scripts/bootstrap-agent-skills.mjs"
225
+ },
226
+ {
227
+ relativePath: "scripts/setup-agent-reference-repos.mts",
228
+ content: createAgentReferenceReposSetupScript(),
229
+ legacyPath: "scripts/setup-agent-reference-repos.mjs"
230
+ }
231
+ ];
232
+ for (const command of external_tooling_command_catalog_cjs_namespaceObject.generatedToolingCommands)if (!(options.shellOnly && BACKEND_FEDERATION_WRAPPER_IDS.has(command.id))) artifacts.push({
233
+ relativePath: command.wrapperPath,
234
+ content: createToolWrapperScript(command.command),
235
+ legacyPath: command.wrapperPath.replace(/\.mts$/u, '.mjs')
236
+ });
237
+ return artifacts;
238
+ }
239
+ const migratedWorkspaceScriptBasenames = [
240
+ 'check-ultramodern-i18n-boundaries',
241
+ 'check-ultramodern-api-boundaries',
242
+ 'bootstrap-agent-skills',
243
+ 'setup-agent-reference-repos',
244
+ ...external_tooling_command_catalog_cjs_namespaceObject.generatedToolingCommands.map((command)=>command.wrapperName)
245
+ ];
246
+ exports.createAgentReferenceReposSetupScript = __webpack_exports__.createAgentReferenceReposSetupScript;
194
247
  exports.createNodeBackendFederationProofScript = __webpack_exports__.createNodeBackendFederationProofScript;
195
248
  exports.createPerformanceReadinessConfigScript = __webpack_exports__.createPerformanceReadinessConfigScript;
196
249
  exports.createWorkspaceApiBoundaryValidationScript = __webpack_exports__.createWorkspaceApiBoundaryValidationScript;
197
250
  exports.createWorkspaceI18nBoundaryValidationScript = __webpack_exports__.createWorkspaceI18nBoundaryValidationScript;
198
251
  exports.createWorkspaceValidationScript = __webpack_exports__.createWorkspaceValidationScript;
199
252
  exports.createZeropsRuntimeMaterializationScript = __webpack_exports__.createZeropsRuntimeMaterializationScript;
253
+ exports.migratedWorkspaceScriptArtifacts = __webpack_exports__.migratedWorkspaceScriptArtifacts;
254
+ exports.migratedWorkspaceScriptBasenames = __webpack_exports__.migratedWorkspaceScriptBasenames;
200
255
  exports.writeGeneratedToolWrapperScripts = __webpack_exports__.writeGeneratedToolWrapperScripts;
201
256
  exports.writeGeneratedWorkspaceScripts = __webpack_exports__.writeGeneratedWorkspaceScripts;
202
257
  for(var __rspack_i in __webpack_exports__)if (-1 === [
258
+ "createAgentReferenceReposSetupScript",
203
259
  "createNodeBackendFederationProofScript",
204
260
  "createPerformanceReadinessConfigScript",
205
261
  "createWorkspaceApiBoundaryValidationScript",
206
262
  "createWorkspaceI18nBoundaryValidationScript",
207
263
  "createWorkspaceValidationScript",
208
264
  "createZeropsRuntimeMaterializationScript",
265
+ "migratedWorkspaceScriptArtifacts",
266
+ "migratedWorkspaceScriptBasenames",
209
267
  "writeGeneratedToolWrapperScripts",
210
268
  "writeGeneratedWorkspaceScripts"
211
269
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
@@ -14,7 +14,7 @@ import { createAppMfTypesTsConfig, createAppTsConfig, createSharedPackageTsConfi
14
14
  import { GENERATED_TOOLING_COMMANDS, generatedToolingCommandList, generatedToolingCommands } from "../ultramodern-workspace/tooling-command-catalog.js";
15
15
  import { DRIZZLE_ORM_VERSION, EFFECT_TSGO_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, MODULE_FEDERATION_VERSION, OXFMT_VERSION, OXLINT_VERSION, TYPESCRIPT_NATIVE_PREVIEW_VERSION, WRANGLER_VERSION, ZEPHYR_AGENT_VERSION, ZEPHYR_RSPACK_PLUGIN_VERSION } from "../ultramodern-workspace/versions.js";
16
16
  import { createWorkspaceAppPackageScripts, createWorkspaceRootPackageScripts } from "../ultramodern-workspace/workspace-script-plan.js";
17
- import { createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, writeGeneratedToolWrapperScripts } from "../ultramodern-workspace/workspace-scripts.js";
17
+ import { createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, migratedWorkspaceScriptArtifacts, migratedWorkspaceScriptBasenames } from "../ultramodern-workspace/workspace-scripts.js";
18
18
  import { createZeropsYaml } from "../ultramodern-workspace/zerops.js";
19
19
  import { normalizeCompactUltramodernConfig, readUltramodernConfig, synthesizeCompactUltramodernConfig, workspaceAppsFromToolingConfig } from "./config.js";
20
20
  const commands_dirname = node_path.dirname(fileURLToPath(import.meta.url));
@@ -265,19 +265,71 @@ const cloudflareWranglerDeployInvalidSkipBuildCommand = `${cloudflareWranglerDep
265
265
  function removeStaleBackendFederationCommandSegments(command) {
266
266
  return command.replace(/\s+&&\s+node\s+\S*scripts\/generate-node-backend-federation\.m[ct]s(?:\s+--app\s+\S+)?(?:\s+--target\s+\S+)?(?=\s+&&|$)/gu, '');
267
267
  }
268
+ const SHELL_ONLY_OMITTED_ROOT_SCRIPTS = [
269
+ 'node:backend-federation:generate',
270
+ 'node:proof',
271
+ 'zerops:materialize'
272
+ ];
273
+ const splitScriptSegments = (command)=>command.split('&&').map((segment)=>segment.trim()).filter((segment)=>segment.length > 0);
274
+ const scriptSegmentTarget = (segment)=>segment.replace(/^pnpm\s+/u, '').split(/\s+/u)[0] ?? segment;
275
+ const FRAMEWORK_CHECK_TARGETS = new Set([
276
+ 'format:check',
277
+ 'lint',
278
+ 'typecheck',
279
+ 'skills:check',
280
+ 'i18n:boundaries',
281
+ 'api:check',
282
+ 'contract:check',
283
+ 'node:backend-federation:generate',
284
+ 'node:proof',
285
+ 'performance:readiness',
286
+ 'bridge:check'
287
+ ]);
288
+ function mergeAggregateCheckScript(consumer, framework) {
289
+ const frameworkSegments = splitScriptSegments(framework);
290
+ const consumerExtras = splitScriptSegments(consumer).filter((segment)=>!FRAMEWORK_CHECK_TARGETS.has(scriptSegmentTarget(segment)));
291
+ return [
292
+ ...consumerExtras,
293
+ ...frameworkSegments
294
+ ].join(' && ');
295
+ }
296
+ function rewriteMigratedScriptReferences(scripts) {
297
+ let changed = false;
298
+ const pattern = new RegExp(`(scripts/(?:${migratedWorkspaceScriptBasenames.join('|')}))\\.mjs`, 'gu');
299
+ for (const [name, value] of Object.entries(scripts)){
300
+ if ('string' != typeof value) continue;
301
+ const next = value.replace(pattern, '$1.mts');
302
+ if (next !== value) {
303
+ scripts[name] = next;
304
+ changed = true;
305
+ }
306
+ }
307
+ return changed;
308
+ }
268
309
  function updateGeneratedPackageScripts(packageJson, options = {}) {
269
310
  const scripts = packageJson.scripts;
270
311
  if (!scripts || 'object' != typeof scripts || Array.isArray(scripts)) return false;
271
312
  let changed = false;
272
313
  const apps = options.apps ?? [];
314
+ const shellOnly = options.shellOnly ?? false;
273
315
  const app = apps.find((candidate)=>`${candidate.directory}/package.json` === options.relativePackageFile);
274
- const expectedScripts = 'package.json' === options.relativePackageFile ? createWorkspaceRootPackageScripts(apps.filter((candidate)=>'shell' !== candidate.kind)) : app ? createWorkspaceAppPackageScripts(app) : void 0;
275
- if (expectedScripts) {
276
- for (const [name, value] of Object.entries(expectedScripts))if (scripts[name] !== value) {
277
- scripts[name] = value;
316
+ const isRootPackage = 'package.json' === options.relativePackageFile;
317
+ const expectedScripts = isRootPackage ? createWorkspaceRootPackageScripts(apps.filter((candidate)=>'shell' !== candidate.kind)) : app ? createWorkspaceAppPackageScripts(app) : void 0;
318
+ if (expectedScripts) for (const [name, value] of Object.entries(expectedScripts)){
319
+ if (isRootPackage && shellOnly && SHELL_ONLY_OMITTED_ROOT_SCRIPTS.includes(name)) {
320
+ if (name in scripts) {
321
+ delete scripts[name];
322
+ changed = true;
323
+ }
324
+ continue;
325
+ }
326
+ const nextValue = 'check' === name && 'string' == typeof scripts[name] ? mergeAggregateCheckScript(scripts[name], value) : value;
327
+ if (scripts[name] !== nextValue) {
328
+ scripts[name] = nextValue;
278
329
  changed = true;
279
330
  }
280
331
  }
332
+ if (rewriteMigratedScriptReferences(scripts)) changed = true;
281
333
  const build = scripts.build;
282
334
  if ('string' == typeof build) {
283
335
  const nextBuild = removeStaleBackendFederationCommandSegments(build);
@@ -652,6 +704,7 @@ function updateUltramodernConfig(io, config, packageSource) {
652
704
  aliasPackageNamePrefix: packageSource.aliasPackageNamePrefix
653
705
  } : {}
654
706
  };
707
+ if (config.generator && 'object' == typeof config.generator && !Array.isArray(config.generator) && 'string' == typeof config.generator.version) config.generator.version = packageSource.modernPackageVersion;
655
708
  for (const app of config.topology?.apps ?? [])if (app && 'object' == typeof app && !Array.isArray(app)) normalizeStrictEffectApiMetadata(app);
656
709
  writeJsonFile(io, configPath, config);
657
710
  }
@@ -881,14 +934,27 @@ workspaces skip the backend-federation and Zerops runtime stages. Pass
881
934
  const migrated = normalizeCompactUltramodernConfig(io.workspaceRoot, raw);
882
935
  const migratedApps = workspaceAppsFromToolingConfig(migrated);
883
936
  const shellOnly = !migrated.topology.apps.some((app)=>app.api);
884
- if (shellOnly) io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
885
- else {
937
+ if (shellOnly) {
938
+ io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
939
+ for (const relativePath of [
940
+ "scripts/generate-node-backend-federation.mts",
941
+ "scripts/generate-node-backend-federation.mjs",
942
+ "scripts/proof-node-backend-federation.mts",
943
+ "scripts/proof-node-backend-federation.mjs",
944
+ "scripts/materialize-zerops-runtime.mjs",
945
+ 'zerops.yaml'
946
+ ])removeGeneratedFileIfExists(io, relativePath);
947
+ } else {
886
948
  removeStaleBackendFederationArtifacts(io, migrated);
887
949
  updateGeneratedZeropsArtifacts(io, migrated);
888
950
  updateGeneratedBackendFederationContractFiles(io, migrated);
889
951
  }
890
- if (dryRun) io.log("would refresh generated tool wrapper scripts under scripts/");
891
- else writeGeneratedToolWrapperScripts(io.workspaceRoot);
952
+ for (const artifact of migratedWorkspaceScriptArtifacts({
953
+ shellOnly
954
+ })){
955
+ if (artifact.legacyPath) io.remove(node_path.join(io.workspaceRoot, artifact.legacyPath));
956
+ io.write(node_path.join(io.workspaceRoot, artifact.relativePath), artifact.content);
957
+ }
892
958
  updateGeneratedBuildIdentityModules(io, migrated);
893
959
  updateGeneratedTypeScriptSurfaces(io, migrated);
894
960
  updateGeneratedModernConfigs(io, migrated);
@@ -906,7 +972,8 @@ workspaces skip the backend-federation and Zerops runtime stages. Pass
906
972
  updateGeneratedToolingDependencies(packageJson);
907
973
  updateGeneratedPackageScripts(packageJson, {
908
974
  relativePackageFile,
909
- apps: migratedApps
975
+ apps: migratedApps,
976
+ shellOnly
910
977
  });
911
978
  writeJsonFile(io, packageFile, packageJson);
912
979
  }
@@ -1045,9 +1112,23 @@ for (const target of targets) {
1045
1112
  console.log(\`[ultramodern] TanStack route artifacts generated: \${target.label}\`);
1046
1113
  } catch (error) {
1047
1114
  failed = true;
1048
- const message = error instanceof Error ? error.message : String(error);
1049
1115
  console.error(\`[ultramodern] TanStack route generation failed: \${target.label}\`);
1050
- console.error(\`- \${message}\`);
1116
+ // Print the full underlying failure, including the cause chain. The
1117
+ // route-generate crash is often an opaque node error thrown deep inside
1118
+ // app-tools/plugin (e.g. a path TypeError), so surfacing only
1119
+ // \`error.message\` swallows the stack that points at the real culprit.
1120
+ let current = error;
1121
+ let depth = 0;
1122
+ while (current) {
1123
+ const label = depth === 0 ? '-' : ' caused by:';
1124
+ const detail =
1125
+ current instanceof Error
1126
+ ? current.stack ?? \`\${current.name}: \${current.message}\`
1127
+ : String(current);
1128
+ console.error(\`\${label} \${detail}\`);
1129
+ current = current instanceof Error ? current.cause : undefined;
1130
+ depth += 1;
1131
+ }
1051
1132
  }
1052
1133
  }
1053
1134
 
@@ -62,6 +62,15 @@ const zephyrEnabled = process.env['ULTRAMODERN_ZEPHYR'] !== 'false';
62
62
  const cloudflareDeployEnabled =
63
63
  process.env['MODERNJS_DEPLOY'] === 'cloudflare';
64
64
 
65
+ const parsedZephyrTimeoutMs = Number.parseInt(
66
+ process.env['ULTRAMODERN_ZEPHYR_TIMEOUT_MS'] ?? '',
67
+ 10,
68
+ );
69
+ const zephyrTimeoutMs =
70
+ Number.isFinite(parsedZephyrTimeoutMs) && parsedZephyrTimeoutMs > 0
71
+ ? parsedZephyrTimeoutMs
72
+ : 45000;
73
+
65
74
  const zephyrWarn = (error: unknown) => {
66
75
  const message = error instanceof Error ? error.message : String(error);
67
76
  console.warn(
@@ -84,10 +93,27 @@ const zephyrRspackPlugin = () => ({
84
93
  }
85
94
  api.modifyRspackConfig(config => {
86
95
  try {
87
- return Promise.resolve(withZephyrRspack()(config)).catch(error => {
88
- zephyrWarn(error);
89
- return config;
96
+ // Zephyr can not only throw/reject but also hang on a stalled network
97
+ // call, wedging the whole build. Race it against a watchdog so a hang
98
+ // degrades to an unmodified config instead of blocking indefinitely.
99
+ const zephyrConfig = Promise.resolve(withZephyrRspack()(config)).catch(
100
+ error => {
101
+ zephyrWarn(error);
102
+ return config;
103
+ },
104
+ );
105
+ const watchdog = new Promise<ZephyrRspackConfig>(resolve => {
106
+ const timer = setTimeout(() => {
107
+ zephyrWarn(
108
+ \`timed out after \${zephyrTimeoutMs}ms (override with ULTRAMODERN_ZEPHYR_TIMEOUT_MS)\`,
109
+ );
110
+ resolve(config);
111
+ }, zephyrTimeoutMs);
112
+ if (typeof timer.unref === 'function') {
113
+ timer.unref();
114
+ }
90
115
  });
116
+ return Promise.race([zephyrConfig, watchdog]);
91
117
  } catch (error) {
92
118
  zephyrWarn(error);
93
119
  return config;
@@ -97,7 +97,7 @@ function createWorkspaceRootScriptPlan(remotes = [], options = {}) {
97
97
  zeropsMaterialize: "node ./scripts/materialize-zerops-runtime.mjs",
98
98
  contractCheck: rootToolingWrapperCommand('validate'),
99
99
  typecheck: options.typecheck ?? `${rootToolingWrapperCommand('typecheck')} --project tsconfig.json`,
100
- check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm api:check && pnpm contract:check && pnpm ${backendFederationGenerateScript} && pnpm ${nodeProofScript} && pnpm performance:readiness${bridgeCheck}`
100
+ check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm api:check && pnpm contract:check${hasRemotes ? ` && pnpm ${backendFederationGenerateScript} && pnpm ${nodeProofScript}` : ''} && pnpm performance:readiness${bridgeCheck}`
101
101
  };
102
102
  }
103
103
  function createWorkspaceRootPackageScripts(remotes = [], options = {}) {
@@ -1,11 +1,12 @@
1
1
  import node_fs from "node:fs";
2
2
  import node_path from "node:path";
3
- import { readFileTemplate, renderFileTemplate, writeFileReplacing } from "./fs-io.js";
3
+ import { readFileTemplate, renderFileTemplate, workspaceTemplateDir, writeFileReplacing } from "./fs-io.js";
4
4
  import { GENERATED_TOOLING_COMMANDS, generatedToolingCommands } from "./tooling-command-catalog.js";
5
5
  import { createWorkspaceValidationContract } from "./workspace-validation-contract.js";
6
+ const singleQuoted = (value)=>`'${value.replace(/'/gu, "\\'")}'`;
6
7
  function createToolWrapperScript(command, extraArgs = []) {
7
- const commandJson = JSON.stringify(command);
8
- const extraArgsJson = JSON.stringify(extraArgs);
8
+ const commandLiteral = singleQuoted(command);
9
+ const extraArgsLiteral = `[${extraArgs.map(singleQuoted).join(', ')}]`;
9
10
  return `#!/usr/bin/env node
10
11
  import { spawnSync } from 'node:child_process';
11
12
  import path from 'node:path';
@@ -16,7 +17,7 @@ const forwardedArgs = process.argv.slice(2);
16
17
  const workspaceRoot =
17
18
  process.env.ULTRAMODERN_WORKSPACE_ROOT ??
18
19
  path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
19
- const ultramodernArgs = ['ultramodern', ${commandJson}, ...${extraArgsJson}, ...forwardedArgs];
20
+ const ultramodernArgs = ['ultramodern', ${commandLiteral}, ...${extraArgsLiteral}, ...forwardedArgs];
20
21
  const result = createBin
21
22
  ? spawnSync(process.execPath, [createBin, ...ultramodernArgs], {
22
23
  env: { ...process.env, ULTRAMODERN_WORKSPACE_ROOT: workspaceRoot },
@@ -141,4 +142,52 @@ function writeGeneratedWorkspaceScripts(targetDir, _scope, _enableTailwind, _rem
141
142
  writeWorkspaceOwnedMtsScript(targetDir, 'bootstrap-agent-skills', createSkillsToolWrapperScript());
142
143
  migrateCopiedWorkspaceScriptToMts(targetDir, 'setup-agent-reference-repos');
143
144
  }
144
- export { createNodeBackendFederationProofScript, createPerformanceReadinessConfigScript, createWorkspaceApiBoundaryValidationScript, createWorkspaceI18nBoundaryValidationScript, createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, writeGeneratedToolWrapperScripts, writeGeneratedWorkspaceScripts };
145
+ function createAgentReferenceReposSetupScript() {
146
+ return node_fs.readFileSync(node_path.join(workspaceTemplateDir, "scripts/setup-agent-reference-repos.mjs"), 'utf-8');
147
+ }
148
+ const BACKEND_FEDERATION_WRAPPER_IDS = new Set([
149
+ 'backendFederationGenerate',
150
+ 'backendFederationProof'
151
+ ]);
152
+ function migratedWorkspaceScriptArtifacts(options) {
153
+ const artifacts = [
154
+ {
155
+ relativePath: "scripts/check-ultramodern-i18n-boundaries.mts",
156
+ content: createWorkspaceI18nBoundaryValidationScript(),
157
+ legacyPath: "scripts/check-ultramodern-i18n-boundaries.mjs"
158
+ },
159
+ {
160
+ relativePath: "scripts/check-ultramodern-api-boundaries.mts",
161
+ content: createWorkspaceApiBoundaryValidationScript(),
162
+ legacyPath: "scripts/check-ultramodern-api-boundaries.mjs"
163
+ },
164
+ {
165
+ relativePath: "scripts/ultramodern-performance-readiness.config.mjs",
166
+ content: createPerformanceReadinessConfigScript()
167
+ },
168
+ {
169
+ relativePath: "scripts/bootstrap-agent-skills.mts",
170
+ content: createSkillsToolWrapperScript(),
171
+ legacyPath: "scripts/bootstrap-agent-skills.mjs"
172
+ },
173
+ {
174
+ relativePath: "scripts/setup-agent-reference-repos.mts",
175
+ content: createAgentReferenceReposSetupScript(),
176
+ legacyPath: "scripts/setup-agent-reference-repos.mjs"
177
+ }
178
+ ];
179
+ for (const command of generatedToolingCommands)if (!(options.shellOnly && BACKEND_FEDERATION_WRAPPER_IDS.has(command.id))) artifacts.push({
180
+ relativePath: command.wrapperPath,
181
+ content: createToolWrapperScript(command.command),
182
+ legacyPath: command.wrapperPath.replace(/\.mts$/u, '.mjs')
183
+ });
184
+ return artifacts;
185
+ }
186
+ const migratedWorkspaceScriptBasenames = [
187
+ 'check-ultramodern-i18n-boundaries',
188
+ 'check-ultramodern-api-boundaries',
189
+ 'bootstrap-agent-skills',
190
+ 'setup-agent-reference-repos',
191
+ ...generatedToolingCommands.map((command)=>command.wrapperName)
192
+ ];
193
+ export { createAgentReferenceReposSetupScript, createNodeBackendFederationProofScript, createPerformanceReadinessConfigScript, createWorkspaceApiBoundaryValidationScript, createWorkspaceI18nBoundaryValidationScript, createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, migratedWorkspaceScriptArtifacts, migratedWorkspaceScriptBasenames, writeGeneratedToolWrapperScripts, writeGeneratedWorkspaceScripts };
@@ -15,7 +15,7 @@ import { createAppMfTypesTsConfig, createAppTsConfig, createSharedPackageTsConfi
15
15
  import { GENERATED_TOOLING_COMMANDS, generatedToolingCommandList, generatedToolingCommands } from "../ultramodern-workspace/tooling-command-catalog.js";
16
16
  import { DRIZZLE_ORM_VERSION, EFFECT_TSGO_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, MODULE_FEDERATION_VERSION, OXFMT_VERSION, OXLINT_VERSION, TYPESCRIPT_NATIVE_PREVIEW_VERSION, WRANGLER_VERSION, ZEPHYR_AGENT_VERSION, ZEPHYR_RSPACK_PLUGIN_VERSION } from "../ultramodern-workspace/versions.js";
17
17
  import { createWorkspaceAppPackageScripts, createWorkspaceRootPackageScripts } from "../ultramodern-workspace/workspace-script-plan.js";
18
- import { createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, writeGeneratedToolWrapperScripts } from "../ultramodern-workspace/workspace-scripts.js";
18
+ import { createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, migratedWorkspaceScriptArtifacts, migratedWorkspaceScriptBasenames } from "../ultramodern-workspace/workspace-scripts.js";
19
19
  import { createZeropsYaml } from "../ultramodern-workspace/zerops.js";
20
20
  import { normalizeCompactUltramodernConfig, readUltramodernConfig, synthesizeCompactUltramodernConfig, workspaceAppsFromToolingConfig } from "./config.js";
21
21
  const commands_dirname = node_path.dirname(fileURLToPath(import.meta.url));
@@ -266,19 +266,71 @@ const cloudflareWranglerDeployInvalidSkipBuildCommand = `${cloudflareWranglerDep
266
266
  function removeStaleBackendFederationCommandSegments(command) {
267
267
  return command.replace(/\s+&&\s+node\s+\S*scripts\/generate-node-backend-federation\.m[ct]s(?:\s+--app\s+\S+)?(?:\s+--target\s+\S+)?(?=\s+&&|$)/gu, '');
268
268
  }
269
+ const SHELL_ONLY_OMITTED_ROOT_SCRIPTS = [
270
+ 'node:backend-federation:generate',
271
+ 'node:proof',
272
+ 'zerops:materialize'
273
+ ];
274
+ const splitScriptSegments = (command)=>command.split('&&').map((segment)=>segment.trim()).filter((segment)=>segment.length > 0);
275
+ const scriptSegmentTarget = (segment)=>segment.replace(/^pnpm\s+/u, '').split(/\s+/u)[0] ?? segment;
276
+ const FRAMEWORK_CHECK_TARGETS = new Set([
277
+ 'format:check',
278
+ 'lint',
279
+ 'typecheck',
280
+ 'skills:check',
281
+ 'i18n:boundaries',
282
+ 'api:check',
283
+ 'contract:check',
284
+ 'node:backend-federation:generate',
285
+ 'node:proof',
286
+ 'performance:readiness',
287
+ 'bridge:check'
288
+ ]);
289
+ function mergeAggregateCheckScript(consumer, framework) {
290
+ const frameworkSegments = splitScriptSegments(framework);
291
+ const consumerExtras = splitScriptSegments(consumer).filter((segment)=>!FRAMEWORK_CHECK_TARGETS.has(scriptSegmentTarget(segment)));
292
+ return [
293
+ ...consumerExtras,
294
+ ...frameworkSegments
295
+ ].join(' && ');
296
+ }
297
+ function rewriteMigratedScriptReferences(scripts) {
298
+ let changed = false;
299
+ const pattern = new RegExp(`(scripts/(?:${migratedWorkspaceScriptBasenames.join('|')}))\\.mjs`, 'gu');
300
+ for (const [name, value] of Object.entries(scripts)){
301
+ if ('string' != typeof value) continue;
302
+ const next = value.replace(pattern, '$1.mts');
303
+ if (next !== value) {
304
+ scripts[name] = next;
305
+ changed = true;
306
+ }
307
+ }
308
+ return changed;
309
+ }
269
310
  function updateGeneratedPackageScripts(packageJson, options = {}) {
270
311
  const scripts = packageJson.scripts;
271
312
  if (!scripts || 'object' != typeof scripts || Array.isArray(scripts)) return false;
272
313
  let changed = false;
273
314
  const apps = options.apps ?? [];
315
+ const shellOnly = options.shellOnly ?? false;
274
316
  const app = apps.find((candidate)=>`${candidate.directory}/package.json` === options.relativePackageFile);
275
- const expectedScripts = 'package.json' === options.relativePackageFile ? createWorkspaceRootPackageScripts(apps.filter((candidate)=>'shell' !== candidate.kind)) : app ? createWorkspaceAppPackageScripts(app) : void 0;
276
- if (expectedScripts) {
277
- for (const [name, value] of Object.entries(expectedScripts))if (scripts[name] !== value) {
278
- scripts[name] = value;
317
+ const isRootPackage = 'package.json' === options.relativePackageFile;
318
+ const expectedScripts = isRootPackage ? createWorkspaceRootPackageScripts(apps.filter((candidate)=>'shell' !== candidate.kind)) : app ? createWorkspaceAppPackageScripts(app) : void 0;
319
+ if (expectedScripts) for (const [name, value] of Object.entries(expectedScripts)){
320
+ if (isRootPackage && shellOnly && SHELL_ONLY_OMITTED_ROOT_SCRIPTS.includes(name)) {
321
+ if (name in scripts) {
322
+ delete scripts[name];
323
+ changed = true;
324
+ }
325
+ continue;
326
+ }
327
+ const nextValue = 'check' === name && 'string' == typeof scripts[name] ? mergeAggregateCheckScript(scripts[name], value) : value;
328
+ if (scripts[name] !== nextValue) {
329
+ scripts[name] = nextValue;
279
330
  changed = true;
280
331
  }
281
332
  }
333
+ if (rewriteMigratedScriptReferences(scripts)) changed = true;
282
334
  const build = scripts.build;
283
335
  if ('string' == typeof build) {
284
336
  const nextBuild = removeStaleBackendFederationCommandSegments(build);
@@ -653,6 +705,7 @@ function updateUltramodernConfig(io, config, packageSource) {
653
705
  aliasPackageNamePrefix: packageSource.aliasPackageNamePrefix
654
706
  } : {}
655
707
  };
708
+ if (config.generator && 'object' == typeof config.generator && !Array.isArray(config.generator) && 'string' == typeof config.generator.version) config.generator.version = packageSource.modernPackageVersion;
656
709
  for (const app of config.topology?.apps ?? [])if (app && 'object' == typeof app && !Array.isArray(app)) normalizeStrictEffectApiMetadata(app);
657
710
  writeJsonFile(io, configPath, config);
658
711
  }
@@ -882,14 +935,27 @@ workspaces skip the backend-federation and Zerops runtime stages. Pass
882
935
  const migrated = normalizeCompactUltramodernConfig(io.workspaceRoot, raw);
883
936
  const migratedApps = workspaceAppsFromToolingConfig(migrated);
884
937
  const shellOnly = !migrated.topology.apps.some((app)=>app.api);
885
- if (shellOnly) io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
886
- else {
938
+ if (shellOnly) {
939
+ io.log('Shell-only workspace: skipping backend-federation and Zerops runtime stages.');
940
+ for (const relativePath of [
941
+ "scripts/generate-node-backend-federation.mts",
942
+ "scripts/generate-node-backend-federation.mjs",
943
+ "scripts/proof-node-backend-federation.mts",
944
+ "scripts/proof-node-backend-federation.mjs",
945
+ "scripts/materialize-zerops-runtime.mjs",
946
+ 'zerops.yaml'
947
+ ])removeGeneratedFileIfExists(io, relativePath);
948
+ } else {
887
949
  removeStaleBackendFederationArtifacts(io, migrated);
888
950
  updateGeneratedZeropsArtifacts(io, migrated);
889
951
  updateGeneratedBackendFederationContractFiles(io, migrated);
890
952
  }
891
- if (dryRun) io.log("would refresh generated tool wrapper scripts under scripts/");
892
- else writeGeneratedToolWrapperScripts(io.workspaceRoot);
953
+ for (const artifact of migratedWorkspaceScriptArtifacts({
954
+ shellOnly
955
+ })){
956
+ if (artifact.legacyPath) io.remove(node_path.join(io.workspaceRoot, artifact.legacyPath));
957
+ io.write(node_path.join(io.workspaceRoot, artifact.relativePath), artifact.content);
958
+ }
893
959
  updateGeneratedBuildIdentityModules(io, migrated);
894
960
  updateGeneratedTypeScriptSurfaces(io, migrated);
895
961
  updateGeneratedModernConfigs(io, migrated);
@@ -907,7 +973,8 @@ workspaces skip the backend-federation and Zerops runtime stages. Pass
907
973
  updateGeneratedToolingDependencies(packageJson);
908
974
  updateGeneratedPackageScripts(packageJson, {
909
975
  relativePackageFile,
910
- apps: migratedApps
976
+ apps: migratedApps,
977
+ shellOnly
911
978
  });
912
979
  writeJsonFile(io, packageFile, packageJson);
913
980
  }
@@ -1046,9 +1113,23 @@ for (const target of targets) {
1046
1113
  console.log(\`[ultramodern] TanStack route artifacts generated: \${target.label}\`);
1047
1114
  } catch (error) {
1048
1115
  failed = true;
1049
- const message = error instanceof Error ? error.message : String(error);
1050
1116
  console.error(\`[ultramodern] TanStack route generation failed: \${target.label}\`);
1051
- console.error(\`- \${message}\`);
1117
+ // Print the full underlying failure, including the cause chain. The
1118
+ // route-generate crash is often an opaque node error thrown deep inside
1119
+ // app-tools/plugin (e.g. a path TypeError), so surfacing only
1120
+ // \`error.message\` swallows the stack that points at the real culprit.
1121
+ let current = error;
1122
+ let depth = 0;
1123
+ while (current) {
1124
+ const label = depth === 0 ? '-' : ' caused by:';
1125
+ const detail =
1126
+ current instanceof Error
1127
+ ? current.stack ?? \`\${current.name}: \${current.message}\`
1128
+ : String(current);
1129
+ console.error(\`\${label} \${detail}\`);
1130
+ current = current instanceof Error ? current.cause : undefined;
1131
+ depth += 1;
1132
+ }
1052
1133
  }
1053
1134
  }
1054
1135
 
@@ -63,6 +63,15 @@ const zephyrEnabled = process.env['ULTRAMODERN_ZEPHYR'] !== 'false';
63
63
  const cloudflareDeployEnabled =
64
64
  process.env['MODERNJS_DEPLOY'] === 'cloudflare';
65
65
 
66
+ const parsedZephyrTimeoutMs = Number.parseInt(
67
+ process.env['ULTRAMODERN_ZEPHYR_TIMEOUT_MS'] ?? '',
68
+ 10,
69
+ );
70
+ const zephyrTimeoutMs =
71
+ Number.isFinite(parsedZephyrTimeoutMs) && parsedZephyrTimeoutMs > 0
72
+ ? parsedZephyrTimeoutMs
73
+ : 45000;
74
+
66
75
  const zephyrWarn = (error: unknown) => {
67
76
  const message = error instanceof Error ? error.message : String(error);
68
77
  console.warn(
@@ -85,10 +94,27 @@ const zephyrRspackPlugin = () => ({
85
94
  }
86
95
  api.modifyRspackConfig(config => {
87
96
  try {
88
- return Promise.resolve(withZephyrRspack()(config)).catch(error => {
89
- zephyrWarn(error);
90
- return config;
97
+ // Zephyr can not only throw/reject but also hang on a stalled network
98
+ // call, wedging the whole build. Race it against a watchdog so a hang
99
+ // degrades to an unmodified config instead of blocking indefinitely.
100
+ const zephyrConfig = Promise.resolve(withZephyrRspack()(config)).catch(
101
+ error => {
102
+ zephyrWarn(error);
103
+ return config;
104
+ },
105
+ );
106
+ const watchdog = new Promise<ZephyrRspackConfig>(resolve => {
107
+ const timer = setTimeout(() => {
108
+ zephyrWarn(
109
+ \`timed out after \${zephyrTimeoutMs}ms (override with ULTRAMODERN_ZEPHYR_TIMEOUT_MS)\`,
110
+ );
111
+ resolve(config);
112
+ }, zephyrTimeoutMs);
113
+ if (typeof timer.unref === 'function') {
114
+ timer.unref();
115
+ }
91
116
  });
117
+ return Promise.race([zephyrConfig, watchdog]);
92
118
  } catch (error) {
93
119
  zephyrWarn(error);
94
120
  return config;
@@ -98,7 +98,7 @@ function createWorkspaceRootScriptPlan(remotes = [], options = {}) {
98
98
  zeropsMaterialize: "node ./scripts/materialize-zerops-runtime.mjs",
99
99
  contractCheck: rootToolingWrapperCommand('validate'),
100
100
  typecheck: options.typecheck ?? `${rootToolingWrapperCommand('typecheck')} --project tsconfig.json`,
101
- check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm api:check && pnpm contract:check && pnpm ${backendFederationGenerateScript} && pnpm ${nodeProofScript} && pnpm performance:readiness${bridgeCheck}`
101
+ check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm api:check && pnpm contract:check${hasRemotes ? ` && pnpm ${backendFederationGenerateScript} && pnpm ${nodeProofScript}` : ''} && pnpm performance:readiness${bridgeCheck}`
102
102
  };
103
103
  }
104
104
  function createWorkspaceRootPackageScripts(remotes = [], options = {}) {
@@ -1,12 +1,13 @@
1
1
  import "node:module";
2
2
  import node_fs from "node:fs";
3
3
  import node_path from "node:path";
4
- import { readFileTemplate, renderFileTemplate, writeFileReplacing } from "./fs-io.js";
4
+ import { readFileTemplate, renderFileTemplate, workspaceTemplateDir, writeFileReplacing } from "./fs-io.js";
5
5
  import { GENERATED_TOOLING_COMMANDS, generatedToolingCommands } from "./tooling-command-catalog.js";
6
6
  import { createWorkspaceValidationContract } from "./workspace-validation-contract.js";
7
+ const singleQuoted = (value)=>`'${value.replace(/'/gu, "\\'")}'`;
7
8
  function createToolWrapperScript(command, extraArgs = []) {
8
- const commandJson = JSON.stringify(command);
9
- const extraArgsJson = JSON.stringify(extraArgs);
9
+ const commandLiteral = singleQuoted(command);
10
+ const extraArgsLiteral = `[${extraArgs.map(singleQuoted).join(', ')}]`;
10
11
  return `#!/usr/bin/env node
11
12
  import { spawnSync } from 'node:child_process';
12
13
  import path from 'node:path';
@@ -17,7 +18,7 @@ const forwardedArgs = process.argv.slice(2);
17
18
  const workspaceRoot =
18
19
  process.env.ULTRAMODERN_WORKSPACE_ROOT ??
19
20
  path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
20
- const ultramodernArgs = ['ultramodern', ${commandJson}, ...${extraArgsJson}, ...forwardedArgs];
21
+ const ultramodernArgs = ['ultramodern', ${commandLiteral}, ...${extraArgsLiteral}, ...forwardedArgs];
21
22
  const result = createBin
22
23
  ? spawnSync(process.execPath, [createBin, ...ultramodernArgs], {
23
24
  env: { ...process.env, ULTRAMODERN_WORKSPACE_ROOT: workspaceRoot },
@@ -142,4 +143,52 @@ function writeGeneratedWorkspaceScripts(targetDir, _scope, _enableTailwind, _rem
142
143
  writeWorkspaceOwnedMtsScript(targetDir, 'bootstrap-agent-skills', createSkillsToolWrapperScript());
143
144
  migrateCopiedWorkspaceScriptToMts(targetDir, 'setup-agent-reference-repos');
144
145
  }
145
- export { createNodeBackendFederationProofScript, createPerformanceReadinessConfigScript, createWorkspaceApiBoundaryValidationScript, createWorkspaceI18nBoundaryValidationScript, createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, writeGeneratedToolWrapperScripts, writeGeneratedWorkspaceScripts };
146
+ function createAgentReferenceReposSetupScript() {
147
+ return node_fs.readFileSync(node_path.join(workspaceTemplateDir, "scripts/setup-agent-reference-repos.mjs"), 'utf-8');
148
+ }
149
+ const BACKEND_FEDERATION_WRAPPER_IDS = new Set([
150
+ 'backendFederationGenerate',
151
+ 'backendFederationProof'
152
+ ]);
153
+ function migratedWorkspaceScriptArtifacts(options) {
154
+ const artifacts = [
155
+ {
156
+ relativePath: "scripts/check-ultramodern-i18n-boundaries.mts",
157
+ content: createWorkspaceI18nBoundaryValidationScript(),
158
+ legacyPath: "scripts/check-ultramodern-i18n-boundaries.mjs"
159
+ },
160
+ {
161
+ relativePath: "scripts/check-ultramodern-api-boundaries.mts",
162
+ content: createWorkspaceApiBoundaryValidationScript(),
163
+ legacyPath: "scripts/check-ultramodern-api-boundaries.mjs"
164
+ },
165
+ {
166
+ relativePath: "scripts/ultramodern-performance-readiness.config.mjs",
167
+ content: createPerformanceReadinessConfigScript()
168
+ },
169
+ {
170
+ relativePath: "scripts/bootstrap-agent-skills.mts",
171
+ content: createSkillsToolWrapperScript(),
172
+ legacyPath: "scripts/bootstrap-agent-skills.mjs"
173
+ },
174
+ {
175
+ relativePath: "scripts/setup-agent-reference-repos.mts",
176
+ content: createAgentReferenceReposSetupScript(),
177
+ legacyPath: "scripts/setup-agent-reference-repos.mjs"
178
+ }
179
+ ];
180
+ for (const command of generatedToolingCommands)if (!(options.shellOnly && BACKEND_FEDERATION_WRAPPER_IDS.has(command.id))) artifacts.push({
181
+ relativePath: command.wrapperPath,
182
+ content: createToolWrapperScript(command.command),
183
+ legacyPath: command.wrapperPath.replace(/\.mts$/u, '.mjs')
184
+ });
185
+ return artifacts;
186
+ }
187
+ const migratedWorkspaceScriptBasenames = [
188
+ 'check-ultramodern-i18n-boundaries',
189
+ 'check-ultramodern-api-boundaries',
190
+ 'bootstrap-agent-skills',
191
+ 'setup-agent-reference-repos',
192
+ ...generatedToolingCommands.map((command)=>command.wrapperName)
193
+ ];
194
+ export { createAgentReferenceReposSetupScript, createNodeBackendFederationProofScript, createPerformanceReadinessConfigScript, createWorkspaceApiBoundaryValidationScript, createWorkspaceI18nBoundaryValidationScript, createWorkspaceValidationScript, createZeropsRuntimeMaterializationScript, migratedWorkspaceScriptArtifacts, migratedWorkspaceScriptBasenames, writeGeneratedToolWrapperScripts, writeGeneratedWorkspaceScripts };
@@ -7,3 +7,13 @@ export declare function createPerformanceReadinessConfigScript(): string;
7
7
  export declare function createNodeBackendFederationProofScript(): string;
8
8
  export declare function createZeropsRuntimeMaterializationScript(): string;
9
9
  export declare function writeGeneratedWorkspaceScripts(targetDir: string, _scope: string, _enableTailwind: boolean, _remotes?: WorkspaceApp[]): void;
10
+ export declare function createAgentReferenceReposSetupScript(): string;
11
+ export interface MigratedWorkspaceScriptArtifact {
12
+ relativePath: string;
13
+ content: string;
14
+ legacyPath?: string;
15
+ }
16
+ export declare function migratedWorkspaceScriptArtifacts(options: {
17
+ shellOnly: boolean;
18
+ }): MigratedWorkspaceScriptArtifact[];
19
+ export declare const migratedWorkspaceScriptBasenames: readonly string[];
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.5.0-ultramodern.29",
24
+ "version": "3.5.0-ultramodern.30",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/esm-node/index.js",
27
27
  "bin": {
@@ -77,7 +77,7 @@
77
77
  "@modern-js/codesmith": "2.6.9",
78
78
  "oxfmt": "0.56.0",
79
79
  "ultracite": "7.8.3",
80
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.5.0-ultramodern.29"
80
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.5.0-ultramodern.30"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@rslib/core": "0.23.1",
@@ -99,6 +99,6 @@
99
99
  "test": "rm -rf dist && rslib build -c rslibconfig.mts && rstest --passWithNoTests"
100
100
  },
101
101
  "ultramodern": {
102
- "frameworkVersion": "3.5.0-ultramodern.29"
102
+ "frameworkVersion": "3.5.0-ultramodern.30"
103
103
  }
104
104
  }
@@ -8,6 +8,10 @@ const packageScope = '{{packageScope}}';
8
8
  const expectedNodeVersion = '{{nodeVersion}}';
9
9
  const tailwindEnabled = {{tailwindEnabledJson}};
10
10
  const fullStackVerticals = {{fullStackVerticalsJson}};
11
+ // Backend-federation and Zerops runtime surfaces only exist when the workspace
12
+ // exposes API-bearing verticals. Shell-only workspaces skip their
13
+ // materialization during migrate, so the contract must not require them.
14
+ const hasBackendSurfaces = fullStackVerticals.length > 0;
11
15
  const shellNamespace = {{shellNamespaceJson}};
12
16
  const oldRemotePaths = {{oldRemotePathsJson}};
13
17
  const expectedBuildScript = {{expectedBuildScriptJson}};
@@ -1688,11 +1692,11 @@ const requiredPaths = [
1688
1692
  'scripts/bootstrap-agent-skills.mts',
1689
1693
  'scripts/check-ultramodern-api-boundaries.mts',
1690
1694
  'scripts/check-ultramodern-i18n-boundaries.mts',
1691
- 'scripts/generate-node-backend-federation.mts',
1695
+ ...(hasBackendSurfaces ? ['scripts/generate-node-backend-federation.mts'] : []),
1692
1696
  'scripts/generate-public-surface-assets.mts',
1693
1697
  'scripts/generate-tanstack-routes.mts',
1694
1698
  'scripts/proof-cloudflare-version.mts',
1695
- 'scripts/proof-node-backend-federation.mts',
1699
+ ...(hasBackendSurfaces ? ['scripts/proof-node-backend-federation.mts'] : []),
1696
1700
  'scripts/setup-agent-reference-repos.mts',
1697
1701
  'scripts/ultramodern-performance-readiness.config.mjs',
1698
1702
  'scripts/ultramodern-performance-readiness.mts',
@@ -1923,54 +1927,70 @@ assert(rootPackage.scripts?.['contract:check'] === 'node ./scripts/validate-ultr
1923
1927
  assert(rootPackage.scripts?.['api:check'] === 'node ./scripts/check-ultramodern-api-boundaries.mts', 'Root must expose api:check');
1924
1928
  assert(rootPackage.scripts?.['i18n:boundaries'] === 'node ./scripts/check-ultramodern-i18n-boundaries.mts', 'Root must expose i18n:boundaries');
1925
1929
  assert(rootPackage.scripts?.['performance:readiness'] === 'node ./scripts/ultramodern-performance-readiness.mts', 'Root must expose default-on performance readiness diagnostics');
1926
- assert(rootPackage.scripts?.['node:backend-federation:generate'] === 'node ./scripts/generate-node-backend-federation.mts', 'Root must expose local Node backend federation artifact generation');
1927
- assert(rootPackage.scripts?.['zerops:materialize'] === 'node ./scripts/materialize-zerops-runtime.mjs', 'Root must expose Zerops runtime materialization script');
1930
+ if (hasBackendSurfaces) {
1931
+ assert(rootPackage.scripts?.['node:backend-federation:generate'] === 'node ./scripts/generate-node-backend-federation.mts', 'Root must expose local Node backend federation artifact generation');
1932
+ assert(rootPackage.scripts?.['zerops:materialize'] === 'node ./scripts/materialize-zerops-runtime.mjs', 'Root must expose Zerops runtime materialization script');
1933
+ assert(
1934
+ rootPackage.scripts?.['node:proof'] ===
1935
+ 'node ./scripts/proof-node-backend-federation.mts',
1936
+ 'Root must expose node:proof for backend federation Effect modules',
1937
+ );
1938
+ }
1928
1939
  assertNotExists('scripts/generate-node-backend-federation.mjs');
1929
1940
  assertNotExists('scripts/proof-node-backend-federation.mjs');
1930
1941
  assertNotExists('scripts/verify-cloudflare-output.mjs');
1931
1942
  assertNotExists('scripts/generate-tanstack-routes.mjs');
1932
1943
  assert(
1933
- rootPackage.scripts?.['node:proof'] ===
1934
- 'node ./scripts/proof-node-backend-federation.mts',
1935
- 'Root must expose node:proof for backend federation Effect modules',
1936
- );
1937
- assert(
1938
- bridgeConfig
1939
- ? rootPackage.scripts?.check?.includes('pnpm api:check') &&
1940
- rootPackage.scripts.check.includes('pnpm node:proof') &&
1941
- rootPackage.scripts.check.endsWith('&& pnpm performance:readiness && pnpm bridge:check')
1942
- : rootPackage.scripts?.check?.includes('pnpm api:check') &&
1943
- rootPackage.scripts.check.includes('pnpm node:proof') &&
1944
- rootPackage.scripts.check.endsWith('&& pnpm performance:readiness'),
1945
- 'Root check must run Node backend federation proof, default-on performance readiness diagnostics, and bridge gates when configured',
1946
- );
1947
- const zeropsYaml = readText('zerops.yaml');
1948
- assert(zeropsYaml.includes('zerops:'), 'Zerops manifest must include zerops services');
1949
- assert(zeropsYaml.includes('setup: shell-super-app'), 'Zerops manifest must include shell service');
1950
- assert(zeropsYaml.includes('base: nodejs@26'), 'Zerops manifest must use generated Node runtime major');
1951
- assert(zeropsYaml.includes('deployFiles:'), 'Zerops manifest must deploy package-pruned runtime directories');
1952
- assert(
1953
- zeropsYaml.includes('start: cd .zerops/runtime/shell-super-app && npm run serve'),
1954
- 'Zerops shell service must start from materialized runtime package',
1955
- );
1956
- assert(
1957
- zeropsYaml.includes('SHELL_SUPER_APP_PORT:') &&
1958
- zeropsYaml.includes('ULTRAMODERN_ZEROPS_SERVICE:'),
1959
- 'Zerops manifest must expose service identity and ports',
1944
+ rootPackage.scripts?.check?.includes('pnpm api:check') &&
1945
+ (hasBackendSurfaces
1946
+ ? rootPackage.scripts.check.includes('pnpm node:proof')
1947
+ : !rootPackage.scripts.check.includes('pnpm node:proof')) &&
1948
+ rootPackage.scripts.check.endsWith(
1949
+ bridgeConfig
1950
+ ? '&& pnpm performance:readiness && pnpm bridge:check'
1951
+ : '&& pnpm performance:readiness',
1952
+ ),
1953
+ 'Root check must run Node backend federation proof when backend surfaces exist, default-on performance readiness diagnostics, and bridge gates when configured',
1960
1954
  );
1961
- for (const vertical of fullStackVerticals) {
1962
- assert(zeropsYaml.includes(`setup: ${vertical.id}`), `${vertical.id} must have a Zerops service`);
1955
+ if (hasBackendSurfaces) {
1956
+ const zeropsYaml = readText('zerops.yaml');
1957
+ assert(zeropsYaml.includes('zerops:'), 'Zerops manifest must include zerops services');
1958
+ assert(zeropsYaml.includes('setup: shell-super-app'), 'Zerops manifest must include shell service');
1959
+ assert(zeropsYaml.includes('base: nodejs@26'), 'Zerops manifest must use generated Node runtime major');
1960
+ assert(zeropsYaml.includes('deployFiles:'), 'Zerops manifest must deploy package-pruned runtime directories');
1963
1961
  assert(
1964
- zeropsYaml.includes(`~/.local/bin/mise exec -- pnpm run zerops:materialize -- --app ${vertical.id} --package ${vertical.packageName} --package-dir ${vertical.path}`),
1965
- `${vertical.id} Zerops service must materialize its runtime package`,
1962
+ zeropsYaml.includes('start: cd .zerops/runtime/shell-super-app && npm run serve'),
1963
+ 'Zerops shell service must start from materialized runtime package',
1966
1964
  );
1967
1965
  assert(
1968
- zeropsYaml.includes(`path: ${vertical.apiPrefix}/${vertical.stem}/readiness`),
1969
- `${vertical.id} BFF readiness path must use generated API prefix`,
1966
+ zeropsYaml.includes('SHELL_SUPER_APP_PORT:') &&
1967
+ zeropsYaml.includes('ULTRAMODERN_ZEROPS_SERVICE:'),
1968
+ 'Zerops manifest must expose service identity and ports',
1970
1969
  );
1970
+ for (const vertical of fullStackVerticals) {
1971
+ assert(zeropsYaml.includes(`setup: ${vertical.id}`), `${vertical.id} must have a Zerops service`);
1972
+ assert(
1973
+ zeropsYaml.includes(`~/.local/bin/mise exec -- pnpm run zerops:materialize -- --app ${vertical.id} --package ${vertical.packageName} --package-dir ${vertical.path}`),
1974
+ `${vertical.id} Zerops service must materialize its runtime package`,
1975
+ );
1976
+ assert(
1977
+ zeropsYaml.includes(`path: ${vertical.apiPrefix}/${vertical.stem}/readiness`),
1978
+ `${vertical.id} BFF readiness path must use generated API prefix`,
1979
+ );
1980
+ }
1981
+ const zeropsMaterializer = readText('scripts/materialize-zerops-runtime.mjs');
1982
+ assert(zeropsMaterializer.includes("MODERNJS_DEPLOY: 'node'"), 'Zerops materializer must use Modern.js Node deploy target');
1983
+ assert(/'deploy',\s*'--skip-build'/.test(zeropsMaterializer), 'Zerops materializer must pass --skip-build directly to Modern deploy');
1984
+ assert(zeropsMaterializer.includes('normalizeRuntimePackageDependencies'), 'Zerops materializer must normalize generated Modern package aliases before installing dependencies');
1985
+ assert(zeropsMaterializer.includes('officialPackageName'), 'Zerops materializer must add official Modern.js npm aliases for generated runtime imports');
1986
+ assert(zeropsMaterializer.includes('installRuntimeDependencies'), 'Zerops materializer must install dependencies outside the workspace copy runtime node_modules');
1987
+ assert(zeropsMaterializer.includes('copyWorkspacePackage'), 'Zerops materializer must preserve generated local workspace package dependencies');
1988
+ assert(zeropsMaterializer.includes('makeWorkspacePackageRuntimeSafe'), 'Zerops materializer must rewrite copied workspace packages to runtime-safe JavaScript exports');
1989
+ assert(zeropsMaterializer.includes("serve: runtimePackage.scripts?.serve ?? 'node index.js'"), 'Zerops materializer must preserve runtime serve script fallback');
1990
+ assert(zeropsMaterializer.includes("'install', '--omit=dev'"), 'Zerops materializer must omit dev dependencies from runtime installs');
1991
+ assert(zeropsMaterializer.includes("'--legacy-peer-deps'"), 'Zerops materializer must tolerate generated-workspace peer dependency ranges in npm runtime install');
1971
1992
  }
1972
1993
  const performanceReadinessConfig = readText('scripts/ultramodern-performance-readiness.config.mjs');
1973
- const zeropsMaterializer = readText('scripts/materialize-zerops-runtime.mjs');
1974
1994
  const assertToolWrapper = (scriptPath, command) => {
1975
1995
  const source = readText(scriptPath);
1976
1996
  assert(source.includes('modern-js-create'), `${scriptPath} must delegate to modern-js-create`);
@@ -1981,16 +2001,6 @@ const assertToolWrapper = (scriptPath, command) => {
1981
2001
  assert(performanceReadinessConfig.includes('UltramodernPerformanceReadinessDiagnosticsConfig'), 'Performance readiness config must carry the typed opt-out surface');
1982
2002
  assert(performanceReadinessConfig.includes('enabled: true'), 'Performance readiness diagnostics must be default-on');
1983
2003
  assert(performanceReadinessConfig.includes("failOn: 'framework-invariant'"), 'Performance readiness diagnostics must only fail framework invariants by default');
1984
- assert(zeropsMaterializer.includes("MODERNJS_DEPLOY: 'node'"), 'Zerops materializer must use Modern.js Node deploy target');
1985
- assert(/'deploy',\s*'--skip-build'/.test(zeropsMaterializer), 'Zerops materializer must pass --skip-build directly to Modern deploy');
1986
- assert(zeropsMaterializer.includes('normalizeRuntimePackageDependencies'), 'Zerops materializer must normalize generated Modern package aliases before installing dependencies');
1987
- assert(zeropsMaterializer.includes('officialPackageName'), 'Zerops materializer must add official Modern.js npm aliases for generated runtime imports');
1988
- assert(zeropsMaterializer.includes('installRuntimeDependencies'), 'Zerops materializer must install dependencies outside the workspace copy runtime node_modules');
1989
- assert(zeropsMaterializer.includes('copyWorkspacePackage'), 'Zerops materializer must preserve generated local workspace package dependencies');
1990
- assert(zeropsMaterializer.includes('makeWorkspacePackageRuntimeSafe'), 'Zerops materializer must rewrite copied workspace packages to runtime-safe JavaScript exports');
1991
- assert(zeropsMaterializer.includes("serve: runtimePackage.scripts?.serve ?? 'node index.js'"), 'Zerops materializer must preserve runtime serve script fallback');
1992
- assert(zeropsMaterializer.includes("'install', '--omit=dev'"), 'Zerops materializer must omit dev dependencies from runtime installs');
1993
- assert(zeropsMaterializer.includes("'--legacy-peer-deps'"), 'Zerops materializer must tolerate generated-workspace peer dependency ranges in npm runtime install');
1994
2004
  assertToolWrapper('scripts/validate-ultramodern-workspace.mts', 'validate');
1995
2005
  assertToolWrapper('scripts/ultramodern-performance-readiness.mts', 'performance-readiness');
1996
2006
  const i18nBoundaryScript = readText('scripts/check-ultramodern-i18n-boundaries.mts');
@@ -2010,10 +2020,12 @@ assert(rootPackage.scripts?.postinstall === "oxfmt . '!repos/**' && node ./scrip
2010
2020
  assert(rootPackage.scripts?.['agents:refs:install'] === 'node ./scripts/setup-agent-reference-repos.mts', 'Root must expose agents:refs:install as the explicit reference repo installer');
2011
2021
  const agentSkillsBootstrap = readText('scripts/bootstrap-agent-skills.mts');
2012
2022
  assertToolWrapper('scripts/assert-mf-types.mts', 'mf-types');
2013
- assertToolWrapper('scripts/generate-node-backend-federation.mts', 'backend-federation-generate');
2023
+ if (hasBackendSurfaces) {
2024
+ assertToolWrapper('scripts/generate-node-backend-federation.mts', 'backend-federation-generate');
2025
+ assertToolWrapper('scripts/proof-node-backend-federation.mts', 'backend-federation-proof');
2026
+ }
2014
2027
  assertToolWrapper('scripts/generate-public-surface-assets.mts', 'public-surface');
2015
2028
  assertToolWrapper('scripts/generate-tanstack-routes.mts', 'routes-generate');
2016
- assertToolWrapper('scripts/proof-node-backend-federation.mts', 'backend-federation-proof');
2017
2029
  assertToolWrapper('scripts/proof-cloudflare-version.mts', 'cloudflare-proof');
2018
2030
  assertToolWrapper('scripts/verify-cloudflare-output.mts', 'cloudflare-output-verify');
2019
2031
  assertToolWrapper('scripts/migrate-strict-effect.mts', 'migrate-strict-effect');