@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.
- package/dist/cjs/ultramodern-tooling/commands.cjs +92 -11
- package/dist/cjs/ultramodern-workspace/module-federation.cjs +29 -3
- package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +1 -1
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +61 -3
- package/dist/esm/ultramodern-tooling/commands.js +93 -12
- package/dist/esm/ultramodern-workspace/module-federation.js +29 -3
- package/dist/esm/ultramodern-workspace/workspace-script-plan.js +1 -1
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +54 -5
- package/dist/esm-node/ultramodern-tooling/commands.js +93 -12
- package/dist/esm-node/ultramodern-workspace/module-federation.js +29 -3
- package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +1 -1
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +54 -5
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
- package/package.json +3 -3
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +62 -50
|
@@ -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
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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)
|
|
932
|
-
|
|
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
|
-
|
|
938
|
-
|
|
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
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
58
|
-
const
|
|
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', ${
|
|
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,
|
|
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
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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)
|
|
885
|
-
|
|
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
|
-
|
|
891
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
8
|
-
const
|
|
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', ${
|
|
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
|
-
|
|
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,
|
|
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
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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)
|
|
886
|
-
|
|
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
|
-
|
|
892
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
9
|
-
const
|
|
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', ${
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1927
|
-
assert(rootPackage.scripts?.['
|
|
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?.
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
)
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
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
|
-
|
|
1962
|
-
|
|
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(
|
|
1965
|
-
|
|
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(
|
|
1969
|
-
|
|
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
|
-
|
|
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');
|